chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// Example Node.js PDS server
import { createLogEmailer, createServer, createSmtpEmailer } from '@pdsjs/node';
/**
* Pick a backup target from the environment. A directory is the simple case
* (`PDS_BACKUP_DIR`, ideally a different disk than the database); an
* S3-compatible bucket — Bunny, Backblaze B2, MinIO, R2, AWS — takes
* `PDS_BACKUP_S3_ENDPOINT`, `PDS_BACKUP_S3_BUCKET`, `PDS_BACKUP_S3_ACCESS_KEY`
* and `PDS_BACKUP_S3_SECRET_KEY` (plus `PDS_BACKUP_S3_REGION` where the
* provider cares), no SDK required. Neither set, the backups page reports
* backups as unavailable.
*/
async function backupTargetFromEnv() {
if (!process.env.PDS_BACKUP_S3_BUCKET) return undefined;
const { createS3FetchBackupTarget } = await import('@pdsjs/blobs-s3');
return createS3FetchBackupTarget({
endpoint: /** @type {string} */ (process.env.PDS_BACKUP_S3_ENDPOINT),
bucket: process.env.PDS_BACKUP_S3_BUCKET,
region: process.env.PDS_BACKUP_S3_REGION || 'us-east-1',
accessKeyId: /** @type {string} */ (process.env.PDS_BACKUP_S3_ACCESS_KEY),
secretAccessKey: /** @type {string} */ (
process.env.PDS_BACKUP_S3_SECRET_KEY
),
});
}
/**
* Pick a mail sender from the environment. `smtp` delivers to a catch-all
* inbox like Mailpit (see docker-compose) so sent mail is visible in a
* browser; `log` prints codes to the terminal; anything else stores addresses
* without being able to confirm them.
*/
function emailerFromEnv() {
if (process.env.PDS_EMAIL_TRANSPORT === 'smtp')
return createSmtpEmailer({
host: process.env.PDS_SMTP_HOST,
port: process.env.PDS_SMTP_PORT
? Number(process.env.PDS_SMTP_PORT)
: undefined,
from: process.env.PDS_EMAIL_FROM,
});
if (process.env.PDS_EMAIL_TRANSPORT === 'log') return createLogEmailer();
return undefined;
}
const { listen } = await createServer({
dbPath: process.env.PDS_DB_PATH || './pds.db',
blobsDir: process.env.PDS_BLOBS_DIR || './blobs',
jwtSecret: process.env.JWT_SECRET || 'development-secret',
blobUploadLimit: process.env.PDS_BLOB_UPLOAD_LIMIT
? Number(process.env.PDS_BLOB_UPLOAD_LIMIT)
: undefined,
port: parseInt(process.env.PORT || '3000', 10),
hostname: process.env.HOSTNAME,
appviewUrl: process.env.APPVIEW_URL,
appviewDid: process.env.APPVIEW_DID,
relayUrl: process.env.RELAY_URL,
plcUrl: process.env.PLC_URL,
password: process.env.PDS_PASSWORD,
// Development only. A deployment supplies a real sender here.
emailer: emailerFromEnv(),
// The React account application. Installed here, so on unless refused.
accountUi: process.env.PDS_ACCOUNT_UI !== 'false',
// Permissioned data (proposal 0016). Off unless explicitly enabled.
spaces: process.env.PDS_ENABLE_SPACES === 'true',
// Semver-exempt experiments; see the experimental option's documentation.
experimental: {
// Git hosting: smart HTTP off unless enabled, browsing on unless
// refused. `external` hands the reads to a separate forge process and
// keeps only the branch-protection write guard here.
git: {
http: process.env.PDS_EXPERIMENTAL_GIT_HTTP === 'true',
browse: process.env.PDS_EXPERIMENTAL_GIT_BROWSE !== 'false',
external: process.env.PDS_EXPERIMENTAL_GIT_EXTERNAL === 'true',
},
// Files stored in the repo, on unless refused.
drive: process.env.PDS_EXPERIMENTAL_DRIVE !== 'false',
// Registries for what this account publishes, both off unless enabled.
// Each needs its own optional peer package installed.
oci: process.env.PDS_EXPERIMENTAL_OCI === 'true',
npm: process.env.PDS_EXPERIMENTAL_NPM === 'true',
// Static sites from the repo (off without a domain) and queries.
sites: {
domain: process.env.PDS_EXPERIMENTAL_SITE_DOMAIN,
apexName: process.env.PDS_EXPERIMENTAL_SITE_APEX_NAME,
queries: process.env.PDS_EXPERIMENTAL_QUERIES === 'true',
},
},
// Scheduled backups: a directory, or an S3-compatible bucket.
backupDir: process.env.PDS_BACKUP_DIR,
backupTarget: await backupTargetFromEnv(),
});
await listen();