Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ jobs:
working-directory: ./test

- run: ./run-s3-test.sh
working-directory: ./test

- run: ./run-blob-test.sh
working-directory: ./test
138 changes: 67 additions & 71 deletions api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,89 +7,85 @@ const sandbox = require('../util/sandbox.js');
const readOnly = require('../util/readOnly.js');

const pathToWP = '/tmp/wp';
let initSqliteS3 = false;
const wpContentPath = pathToWP + '/wp-content';
const sqlitePluginPath = wpContentPath + '/plugins/sqlite-database-integration';

// Move the /wp directory to /tmp/wp so that it is writeable.
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'] || 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']) {
const branch = sqliteS3.branchNameToS3file(process.env['VERCEL_GIT_COMMIT_REF']);
branchSlug = branch ? '-' + branch : '';
bucketFallback = process.env['VERCEL_PROJECT_ID'];
}
const hasSqliteS3 = !!(process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']);
const readOnlyActive = !!process.env['SERVERLESSWP_READ_ONLY_MODE']
&& !['false', '0', 'no'].includes(process.env['SERVERLESSWP_READ_ONLY_MODE'].toLowerCase());

// Configure the sqliteS3 plugin.
let sqliteS3Config = {
bucket: process.env['SQLITE_S3_BUCKET'] || bucketFallback,
file:`wp-sqlite-s3${branchSlug}.sqlite`,
S3Client: {
credentials: {
"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'],
}
};
let sqliteSelection = null;
let initDone = false;

if (process.env['SQLITE_S3_ENDPOINT']) {
sqliteS3Config.S3Client.endpoint = process.env['SQLITE_S3_ENDPOINT'];
function buildSqliteS3Config(overrides = {}) {
const config = {
bucket: overrides.bucket || process.env['SQLITE_S3_BUCKET'],
file: overrides.file || 'wp-sqlite-s3.sqlite',
S3Client: {
credentials: {
accessKeyId: overrides.accessKeyId || process.env['SQLITE_S3_API_KEY'],
secretAccessKey: process.env['SQLITE_S3_API_SECRET'] || process.env['SERVERLESSWP_DATA_SECRET'],
},
region: process.env['SQLITE_S3_REGION'],
}
};

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 = () => sandbox.register(
sqliteS3Config.bucket,
process.env['SERVERLESSWP_DATA_SECRET']
);
}

sqliteS3.config(sqliteS3Config);
initSqliteS3 = true;
if (process.env['SQLITE_S3_ENDPOINT']) {
config.S3Client.endpoint = process.env['SQLITE_S3_ENDPOINT'];
}

// Send the request (event object) to the serverlesswp library.
// It includes the PHP server that allows WordPress to handle the request.
let response = await serverlesswp({docRoot: pathToWP, event: event});

// Check to see if the database environment variables are in place.
let checkInstall = validate(response);

if (checkInstall) {
return checkInstall;
if (process.env['SQLITE_S3_FORCE_PATH_STYLE'] || process.env['SERVERLESSWP_DATA_SECRET']) {
config.S3Client.forcePathStyle = true;
}
else {
// Return the response for serving.
return response;

if (process.env['SERVERLESSWP_DATA_SECRET']) {
config.S3Client.endpoint = 'https://data.serverlesswp.com';
config.onAuthError = () => sandbox.register(config.bucket, process.env['SERVERLESSWP_DATA_SECRET']);
}
}

if (process.env['SERVERLESSWP_READ_ONLY_MODE'] && !['false', '0', 'no'].includes(process.env['SERVERLESSWP_READ_ONLY_MODE'].toLowerCase())) {
// Register before sqliteS3 so blocked requests force a response before the S3 fetch.
serverlesswp.registerPlugin(readOnly);
return config;
}

if (process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']) {
// Register the sqlite serverlesswp plugin.
serverlesswp.registerPlugin(sqliteS3);
// Platform entry points (api/vercel.js) may call this before the first request
// to override which sqlite plugin gets used or pass a platform-tuned config.
function useSqlitePlugin(plugin, config) {
sqliteSelection = { plugin, config };
}

if (process.env['SERVERLESSWP_DATA_SECRET']) {
// Register the sandbox widget plugin.
serverlesswp.registerPlugin(sandbox);
exports.useSqlitePlugin = useSqlitePlugin;
exports.buildSqliteS3Config = buildSqliteS3Config;

// Default: use sqliteS3 when its env vars are present.
if (hasSqliteS3) {
useSqlitePlugin(sqliteS3, buildSqliteS3Config());
}

// Move the /wp directory to /tmp/wp so that it is writeable.
setup();

// This is where all requests to WordPress are routed through.
// See netlify.toml or serverless.yml for the redirection rules.
// On Vercel, requests come through api/vercel.js which then calls this handler.
exports.handler = async function (event, context, callback) {
if (!initDone) {
// Register readOnly first so blocked mutations short-circuit before the
// sqlite plugin tries to hit storage.
if (readOnlyActive) {
serverlesswp.registerPlugin(readOnly);
}
if (sqliteSelection) {
const { plugin, config } = sqliteSelection;
await plugin.prepPlugin(wpContentPath, sqlitePluginPath);
plugin.config(config);
serverlesswp.registerPlugin(plugin);
}
if (process.env['SERVERLESSWP_DATA_SECRET']) {
serverlesswp.registerPlugin(sandbox);
}
initDone = true;
}

const response = await serverlesswp({ docRoot: pathToWP, event: event });
const checkInstall = validate(response);
return checkInstall || response;
};
117 changes: 28 additions & 89 deletions api/vercel.js
Original file line number Diff line number Diff line change
@@ -1,98 +1,37 @@
const serverlesswp = require('serverlesswp');
// Vercel-specific entry point. Adds Vercel Blob as a storage option and
// layers VERCEL_* env var fallbacks onto the SQLite + S3 config, then delegates
// to the core handler in api/index.js.

const { validate } = require('../util/install.js');
const { setup } = require('../util/directory.js');
const core = require('./index.js');
const sqliteS3 = require('../util/sqliteS3.js');
const sandbox = require('../util/sandbox.js');
const readOnly = require('../util/readOnly.js');
const sqliteVercelBlob = require('../util/sqliteVercelBlob.js');

const pathToWP = '/tmp/wp';
let initSqliteS3 = false;
const hasSqliteS3 = !!(process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']);
const useVercelBlob = !!process.env['BLOB_READ_WRITE_TOKEN'] && !hasSqliteS3;

// Move the /wp directory to /tmp/wp so that it is writeable.
setup();

// This is where all requests to WordPress are routed through.
// See vercel.json or netlify.toml for the redirection rules.
async function handler(event, context, callback) {
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']) {
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'] || bucketFallback,
file:`wp-sqlite-s3${branchSlug}.sqlite`,
S3Client: {
credentials: {
"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'],
}
};

if (process.env['SQLITE_S3_ENDPOINT']) {
sqliteS3Config.S3Client.endpoint = process.env['SQLITE_S3_ENDPOINT'];
}

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 = () => sandbox.register(
sqliteS3Config.bucket,
process.env['SERVERLESSWP_DATA_SECRET']
);
}

sqliteS3.config(sqliteS3Config);
initSqliteS3 = true;
// Encode the current Vercel git branch so each branch gets its own database.
function branchSlug() {
if (process.env['VERCEL_GIT_COMMIT_REF']) {
const branch = encodeURIComponent(process.env['VERCEL_GIT_COMMIT_REF']);
return branch ? '-' + branch : '';
}

// Send the request (event object) to the serverlesswp library.
// It includes the PHP server that allows WordPress to handle the request.
let response = await serverlesswp({docRoot: pathToWP, event: event});

// Check to see if the database environment variables are in place.
let checkInstall = validate(response);

if (checkInstall) {
return checkInstall;
}
else {
// Return the response for serving.
return response;
}
}

if (process.env['SERVERLESSWP_READ_ONLY_MODE'] && !['false', '0', 'no'].includes(process.env['SERVERLESSWP_READ_ONLY_MODE'].toLowerCase())) {
// Register before sqliteS3 so blocked requests force a response before the S3 fetch.
serverlesswp.registerPlugin(readOnly);
}

if (process.env['SQLITE_S3_BUCKET'] || process.env['SERVERLESSWP_DATA_SECRET']) {
// Register the sqlite serverlesswp plugin.
serverlesswp.registerPlugin(sqliteS3);
return '';
}

if (process.env['SERVERLESSWP_DATA_SECRET']) {
// Register the sandbox widget plugin.
serverlesswp.registerPlugin(sandbox);
if (hasSqliteS3) {
// Re-configure the S3 plugin with Vercel-aware overrides: the sandbox flow
// uses VERCEL_PROJECT_ID as the bucket name and the API key fallback, and
// branch-aware filenames keep preview deploys isolated.
core.useSqlitePlugin(sqliteS3, core.buildSqliteS3Config({
bucket: process.env['SQLITE_S3_BUCKET'] || process.env['VERCEL_PROJECT_ID'],
file: `wp-sqlite-s3${branchSlug()}.sqlite`,
accessKeyId: process.env['SQLITE_S3_API_KEY'] || process.env['VERCEL_PROJECT_ID'],
}));
} else if (useVercelBlob) {
core.useSqlitePlugin(sqliteVercelBlob, {
pathname: `wp-sqlite${branchSlug()}.sqlite`,
});
}

module.exports = handler;
module.exports.handler = handler;
module.exports = core.handler;
module.exports.handler = core.handler;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "MIT",
"dependencies": {
"@aws-sdk/client-s3": "^3.772.0",
"@vercel/blob": "^2.3.3",
"serverlesswp": "^0.5.1",
"sqlite3": "^5.1.7"
},
Expand Down
13 changes: 13 additions & 0 deletions test/Dockerfile-blob
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM public.ecr.aws/lambda/nodejs:22

COPY package.json ${LAMBDA_TASK_ROOT}/
RUN npm install --omit=optional --omit=dev

COPY api/ ${LAMBDA_TASK_ROOT}/api/
COPY util/ ${LAMBDA_TASK_ROOT}/util/
COPY wp/ ${LAMBDA_TASK_ROOT}/wp/
COPY test/installer.php ${LAMBDA_TASK_ROOT}/wp/
COPY test/vercel-blob-emulator/undici-patch.js ${LAMBDA_TASK_ROOT}/blob-test/undici-patch.js
COPY test/vercel-blob-emulator/handler.js ${LAMBDA_TASK_ROOT}/blob-test/handler.js

CMD [ "blob-test/handler.handler" ]
Loading
Loading