chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
// The drive extension over a host with spaces on: a drive held in a space
// reads through the space browser and storage the host carries and writes
// through the spaces package, all reached from the account routes.
import { createAccountJwt } from '@pdsjs/core/auth';
import { PersonalDataServer } from '@pdsjs/core/pds';
import { createMemorySpaceStorage } from '@pdsjs/spaces/memory-storage';
import { makeSpaceRow } from '@pdsjs/spaces/space-row';
import { describe, expect, it } from 'vitest';
import { createDriveExtension } from '../src/extension.js';
const DID = 'did:plc:driveextension';
const SPACE = `at://${DID}/space/dev.pdsjs.drive.space/photos`;
const SECRET = 'drive-extension-secret';
const BASE = 'https://pds.example.com';
/** @param {{ownSpace?: boolean}} [opts] */
async function createServer({ ownSpace = true } = {}) {
/** @type {Map<string, Uint8Array>} */
const blobData = new Map();
const actorStorage = /** @type {any} */ ({
getDid: async () => DID,
getRecord: async () => null,
listRecords: async () => ({ records: [], cursor: null }),
putBlock: async () => {},
putRecord: async () => {},
putBlob: async () => {},
linkBlobToRecord: async () => {},
deleteRecord: async () => {},
unlinkBlobsFromRecord: async () => {},
});
const blobs = /** @type {any} */ ({
put: async (
/** @type {string} */ _did,
/** @type {string} */ cid,
/** @type {Uint8Array} */ data,
) => {
blobData.set(cid, data);
},
get: async (/** @type {string} */ _did, /** @type {string} */ cid) => {
const data = blobData.get(cid);
return data ? { data, mimeType: 'application/octet-stream' } : null;
},
});
const spaceStorage = createMemorySpaceStorage();
// The account's own space: a write into one that does not exist is refused.
if (ownSpace) {
await spaceStorage.putSpace(makeSpaceRow(SPACE, { isOwner: true }));
}
return new PersonalDataServer({
actorStorage,
sharedStorage: /** @type {any} */ ({}),
blobs,
jwtSecret: SECRET,
hostname: 'pds.example.com',
blobUploadLimit: 64,
spaceStorage,
extensions: [createDriveExtension()],
});
}
async function cookie() {
return `pdsjs_account=${encodeURIComponent(await createAccountJwt(DID, SECRET))}`;
}
/**
* @param {PersonalDataServer} pds
* @param {string} path
* @param {RequestInit & {json?: unknown}} [init]
*/
async function call(pds, path, init = {}) {
const headers = new Headers(init.headers);
headers.set('cookie', await cookie());
if (init.method === 'POST') headers.set('origin', BASE);
if (init.json !== undefined) headers.set('content-type', 'application/json');
return pds.fetch(
new Request(BASE + path, {
...init,
headers,
body: init.json !== undefined ? JSON.stringify(init.json) : init.body,
}),
);
}
describe('a drive held in a space', () => {
it('makes a drive space through the admin port built over the storage', async () => {
const pds = await createServer({ ownSpace: false });
const made = await call(pds, '/account/api/drive/space', {
method: 'POST',
json: { name: 'work' },
});
expect(made.status).toBe(200);
expect(await made.json()).toMatchObject({ key: 'work', name: 'work' });
const listing = await call(pds, '/account/api/drive/files');
expect(
(await listing.json()).spaces.map(
(/** @type {{name: string}} */ s) => s.name,
),
).toEqual(['work']);
});
it('creates a folder, stores a file and reads it back through the space', async () => {
const pds = await createServer();
const folder = await call(pds, '/account/api/drive/folder', {
method: 'POST',
json: { name: 'trips', space: SPACE },
});
expect(folder.status).toBe(200);
const listing = await call(
pds,
`/account/api/drive/files?space=${encodeURIComponent(SPACE)}`,
);
expect(listing.status).toBe(200);
expect(
(await listing.json()).folders.map(
(/** @type {{name: string}} */ f) => f.name,
),
).toEqual(['trips']);
const upload = await call(
pds,
`/account/api/drive/upload?name=${encodeURIComponent('note.txt')}&space=${encodeURIComponent(SPACE)}`,
{
method: 'POST',
headers: { 'content-type': 'text/plain' },
body: 'hello from a space',
},
);
expect(upload.status).toBe(200);
const { rkey } = await upload.json();
const file = await call(
pds,
`/account/api/drive/file?rkey=${encodeURIComponent(rkey)}&space=${encodeURIComponent(SPACE)}`,
);
expect(file.status).toBe(200);
expect(await file.text()).toBe('hello from a space');
});
});