Live video on the AT Protocol
at next 122 lines 3.5 kB view raw
1import fs from "node:fs/promises"; 2import os from "node:os"; 3import path from "node:path"; 4 5import { Secp256k1Keypair, randomStr } from "@atproto/crypto"; 6import * as pds from "@atproto/pds"; 7 8import getPort from "get-port"; 9import * as ui8 from "uint8arrays"; 10 11import { ADMIN_PASSWORD, JWT_SECRET } from "./constants.js"; 12 13export interface PdsServerOptions extends Partial<pds.ServerEnvironment> { 14 didPlcUrl: string; 15} 16 17export interface AdditionalPdsContext { 18 dataDirectory: string; 19 blobstoreLoc: string; 20} 21 22export class TestPdsServer { 23 constructor( 24 public readonly server: pds.PDS, 25 public readonly url: string, 26 public readonly port: number, 27 public readonly additional: AdditionalPdsContext, 28 ) {} 29 30 static async create(config: PdsServerOptions): Promise<TestPdsServer> { 31 const plcRotationKey = await Secp256k1Keypair.create({ exportable: true }); 32 const plcRotationPriv = ui8.toString(await plcRotationKey.export(), "hex"); 33 const recoveryKey = (await Secp256k1Keypair.create()).did(); 34 35 const port = config.port || (await getPort()); 36 const url = `http://localhost:${port}`; 37 38 const blobstoreLoc = path.join(os.tmpdir(), randomStr(8, "base32")); 39 const dataDirectory = path.join(os.tmpdir(), randomStr(8, "base32")); 40 41 await fs.mkdir(dataDirectory, { recursive: true }); 42 43 const env: pds.ServerEnvironment = { 44 devMode: true, 45 port, 46 dataDirectory: dataDirectory, 47 blobstoreDiskLocation: blobstoreLoc, 48 recoveryDidKey: recoveryKey, 49 adminPassword: ADMIN_PASSWORD, 50 jwtSecret: JWT_SECRET, 51 serviceHandleDomains: [".test"], 52 bskyAppViewUrl: "https://appview.invalid", 53 bskyAppViewDid: "did:example:invalid", 54 bskyAppViewCdnUrlPattern: "http://cdn.appview.com/%s/%s/%s", 55 modServiceUrl: "https://moderator.invalid", 56 modServiceDid: "did:example:invalid", 57 plcRotationKeyK256PrivateKeyHex: plcRotationPriv, 58 inviteRequired: false, 59 disableSsrfProtection: true, 60 serviceName: "Development PDS", 61 // brandColor: "#ffcb1e", 62 errorColor: undefined, 63 logoUrl: 64 "https://uxwing.com/wp-content/themes/uxwing/download/animals-and-birds/bee-icon.png", 65 homeUrl: "https://bsky.social/", 66 termsOfServiceUrl: "https://bsky.social/about/support/tos", 67 privacyPolicyUrl: "https://bsky.social/about/support/privacy-policy", 68 supportUrl: "https://blueskyweb.zendesk.com/hc/en-us", 69 ...config, 70 }; 71 72 const cfg = pds.envToCfg(env); 73 const secrets = pds.envToSecrets(env); 74 75 const server = await pds.PDS.create(cfg, secrets); 76 77 await server.start(); 78 79 return new TestPdsServer(server, url, port, { 80 dataDirectory: dataDirectory, 81 blobstoreLoc: blobstoreLoc, 82 }); 83 } 84 85 get ctx(): pds.AppContext { 86 return this.server.ctx; 87 } 88 89 adminAuth(): string { 90 return ( 91 "Basic " + 92 ui8.toString( 93 ui8.fromString(`admin:${ADMIN_PASSWORD}`, "utf8"), 94 "base64pad", 95 ) 96 ); 97 } 98 99 adminAuthHeaders() { 100 return { 101 authorization: this.adminAuth(), 102 }; 103 } 104 105 jwtSecretKey() { 106 return pds.createSecretKeyObject(JWT_SECRET); 107 } 108 109 async processAll() { 110 await this.ctx.backgroundQueue.processAll(); 111 } 112 113 async close() { 114 await this.server.destroy(); 115 116 await fs.rm(this.additional.dataDirectory, { 117 recursive: true, 118 force: true, 119 }); 120 await fs.rm(this.additional.blobstoreLoc, { force: true }); 121 } 122}