Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

delete associated subfs records when deleting sites

When a user deletes a site, now fetches the main record first to
extract any subfs references, then deletes all associated subfs
records along with the main record.

Prevents orphaned subfs records from accumulating in the PDS.

Changed files
+55 -1
src
routes
+55 -1
src/routes/site.ts
···
import { Agent } from '@atproto/api'
import { deleteSite } from '../lib/db'
import { logger } from '../lib/logger'
+
import { extractSubfsUris } from '../lib/wisp-utils'
export const siteRoutes = (client: NodeOAuthClient, cookieSecret: string) =>
new Elysia({
···
// Create agent with OAuth session
const agent = new Agent((url, init) => auth.session.fetchHandler(url, init))
-
// Delete the record from AT Protocol
+
// First, fetch the site record to find any subfs references
+
let subfsUris: Array<{ uri: string; path: string }> = [];
+
try {
+
const existingRecord = await agent.com.atproto.repo.getRecord({
+
repo: auth.did,
+
collection: 'place.wisp.fs',
+
rkey: rkey
+
});
+
+
if (existingRecord.data.value && typeof existingRecord.data.value === 'object' && 'root' in existingRecord.data.value) {
+
const manifest = existingRecord.data.value as any;
+
subfsUris = extractSubfsUris(manifest.root);
+
+
if (subfsUris.length > 0) {
+
console.log(`Found ${subfsUris.length} subfs records to delete`);
+
logger.info(`[Site] Found ${subfsUris.length} subfs records associated with ${rkey}`);
+
}
+
}
+
} catch (err) {
+
// Record might not exist, continue with deletion
+
console.log('Could not fetch site record for subfs cleanup, continuing...');
+
}
+
+
// Delete the main record from AT Protocol
try {
await agent.com.atproto.repo.deleteRecord({
repo: auth.did,
···
} catch (err) {
logger.error(`[Site] Failed to delete site ${rkey} from PDS`, err)
throw new Error('Failed to delete site from AT Protocol')
+
}
+
+
// Delete associated subfs records
+
if (subfsUris.length > 0) {
+
console.log(`Deleting ${subfsUris.length} associated subfs records...`);
+
+
await Promise.all(
+
subfsUris.map(async ({ uri }) => {
+
try {
+
// Parse URI: at://did/collection/rkey
+
const parts = uri.replace('at://', '').split('/');
+
const subRkey = parts[2];
+
+
await agent.com.atproto.repo.deleteRecord({
+
repo: auth.did,
+
collection: 'place.wisp.subfs',
+
rkey: subRkey
+
});
+
+
console.log(` 🗑️ Deleted subfs: ${uri}`);
+
logger.info(`[Site] Deleted subfs record: ${uri}`);
+
} catch (err: any) {
+
// Log but don't fail if subfs deletion fails
+
console.warn(`Failed to delete subfs ${uri}:`, err?.message);
+
logger.warn(`[Site] Failed to delete subfs ${uri}`, err);
+
}
+
})
+
);
+
+
logger.info(`[Site] Deleted ${subfsUris.length} subfs records for ${rkey}`);
}
// Delete from database