diff --git a/api/index.js b/api/index.js index 0d58587cb..4f40e609d 100644 --- a/api/index.js +++ b/api/index.js @@ -4,6 +4,8 @@ const { validate } = require('../util/install.js'); const { setup } = require('../util/directory.js'); const sqliteS3 = require('../util/sqliteS3.js'); +const { register } = require('../util/goldilock.js'); + const pathToWP = '/tmp/wp'; let initSqliteS3 = false; @@ -13,30 +15,29 @@ setup(); // This is where all requests to WordPress are routed through. // See vercel.json or netlify.toml for the redirection rules. exports.handler = async function (event, context, callback) { - if (process.env['SQLITE_S3_BUCKET'] && !initSqliteS3) { + if ((process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']) && !initSqliteS3) { let wpContentPath = pathToWP + '/wp-content'; let sqlitePluginPath = wpContentPath + '/plugins/sqlite-database-integration'; await sqliteS3.prepPlugin(wpContentPath, sqlitePluginPath); let branchSlug = ''; + let bucketFallback = ''; // Vercel if (process.env['VERCEL']) { - branchSlug = sqliteS3.branchNameToS3file(process.env['VERCEL_GIT_COMMIT_REF']); - } - - if (branchSlug) { - branchSlug = '-' + branchSlug; + const branch = sqliteS3.branchNameToS3file(process.env['VERCEL_GIT_COMMIT_REF']); + branchSlug = branch ? '-' + branch : ''; + bucketFallback = process.env['VERCEL_PROJECT_ID']; } // Configure the sqliteS3 plugin. let sqliteS3Config = { - bucket: process.env['SQLITE_S3_BUCKET'], + bucket: process.env['SQLITE_S3_BUCKET'] || bucketFallback, file:`wp-sqlite-s3${branchSlug}.sqlite`, S3Client: { credentials: { - "accessKeyId": process.env['SQLITE_S3_API_KEY'], - "secretAccessKey": process.env['SQLITE_S3_API_SECRET'] + "accessKeyId": process.env['SQLITE_S3_API_KEY'] || process.env['VERCEL_PROJECT_ID'], + "secretAccessKey": process.env['SQLITE_S3_API_SECRET'] || process.env['SERVERLESSWP_DATA_SECRET'] }, region: process.env['SQLITE_S3_REGION'], } @@ -46,10 +47,18 @@ exports.handler = async function (event, context, callback) { sqliteS3Config.S3Client.endpoint = process.env['SQLITE_S3_ENDPOINT']; } - if (process.env['SQLITE_S3_FORCE_PATH_STYLE']) { + if (process.env['SQLITE_S3_FORCE_PATH_STYLE'] || process.env['SERVERLESSWP_DATA_SECRET']) { sqliteS3Config.S3Client.forcePathStyle = true; } + if (process.env['SERVERLESSWP_DATA_SECRET']) { + sqliteS3Config.S3Client.endpoint = 'https://data.serverlesswp.com'; + sqliteS3Config.onAuthError = () => register( + sqliteS3Config.bucket, + process.env['SERVERLESSWP_DATA_SECRET'] + ); + } + sqliteS3.config(sqliteS3Config); initSqliteS3 = true; } @@ -70,7 +79,7 @@ exports.handler = async function (event, context, callback) { } } -if (process.env['SQLITE_S3_BUCKET']) { +if (process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']) { // Register the sqlite serverlesswp plugin. serverlesswp.registerPlugin(sqliteS3); } diff --git a/util/goldilock.js b/util/goldilock.js new file mode 100644 index 000000000..bce5f183c --- /dev/null +++ b/util/goldilock.js @@ -0,0 +1,15 @@ + +exports.register = async function (bucket, secret) { + const response = await fetch('https://data.serverlesswp.com/auto-register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ bucket, secret }), + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Auto registration failed: ${errorText}`); + } +} \ No newline at end of file diff --git a/util/install.js b/util/install.js index 9d78c27a7..8352f41c8 100644 --- a/util/install.js +++ b/util/install.js @@ -4,7 +4,7 @@ exports.validate = function(response) { let platform = 'AWS'; let dashboardLink; - if (process.env['SQLITE_S3_BUCKET']) { + if (process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']) { hasSqliteS3 = true; } diff --git a/util/sqliteS3.js b/util/sqliteS3.js index 67adbeefa..fe6613a42 100644 --- a/util/sqliteS3.js +++ b/util/sqliteS3.js @@ -69,7 +69,16 @@ exports.preRequest = async function(event) { // No need to download, just use existing file db = new sqlite3.Database(sqliteFilePath); dataVersion = await getDataVersion(); - } + } + else if (err.$metadata?.httpStatusCode === 403) { + if (_config.onAuthError) { + try { + await _config.onAuthError(event, _config); + } catch (regErr) { + console.error('Auto-registration failed:', regErr.message); + } + } + } else if (err.name === 'NoSuchKey') { // Handle case where the file doesn't exist on S3 console.log('Database file not found on server'); @@ -83,14 +92,20 @@ exports.preRequest = async function(event) { exports.postRequest = async function(event, response) { try { + // If db wasn't initialized but file exists, this is a new database + const dbExists = await exists(sqliteFilePath); if (!db) { - db = new sqlite3.Database(sqliteFilePath); + if (dbExists) { + db = new sqlite3.Database(sqliteFilePath); + dataVersion = null; + } else { + return; + } } let versionNow = await getDataVersion(); // See if the db has been mutated, if so, send the changes to s3 if (dataVersion !== versionNow) { - const dbExists = await exists(sqliteFilePath); if (dbExists) { try { await dbClose(); @@ -211,7 +226,8 @@ exports.prepPlugin = async function (wpContentPath, sqlitePluginPath) { const content = await fs.readFile(newPath, 'utf8'); const modifiedContent = content.replace(new RegExp(/{SQLITE_IMPLEMENTATION_FOLDER_PATH}/, 'g'), sqlitePluginPath); - await fs.writeFile(newPath, modifiedContent) + await fs.writeFile(newPath, modifiedContent); + init = true; } catch (err) { console.log(err); diff --git a/wp/wp-config.php b/wp/wp-config.php index d78198d54..7afd14e7f 100644 --- a/wp/wp-config.php +++ b/wp/wp-config.php @@ -130,7 +130,7 @@ define('DISALLOW_FILE_MODS', true ); // If using SQLite + S3 instead of MySQL/MariaDB. -if (isset($_ENV['SQLITE_S3_BUCKET'])) { +if (isset($_ENV['SQLITE_S3_BUCKET']) || isset($_ENV['SERVERLESSWP_DATA_SECRET'])) { define('DB_DIR', '/tmp'); define('DB_FILE', 'wp-sqlite-s3.sqlite');