Files
zpan/server/services/purge.ts
T
Jasper VanandBob de4938e75c feat: add shares schema, service layer, and lifecycle integration (#308)
- Add `shares` and `share_recipients` tables to Drizzle schema with indices
- Add migration 0010_shares.sql for shares/share_recipients tables
- Add `server/lib/password.ts` extracting scrypt hash/verify from auth.ts
  to eliminate duplicate crypto params across services
- Add `server/services/share.ts` implementing full CRUD + atomic counters:
  createShare, getShareByToken, incrementViews, incrementDownloadsAtomic
  (atomic SQL UPDATE), listSharesByCreator, revokeShare, cascadeDeleteByMatter
- Add `shared/schemas/share.ts` Zod validation schemas
- Export Share, ShareKind, ShareRecipient from shared/types
- Extend `purge.ts` to cascade-delete shares on matter hard-delete
- Add 38 integration tests and CF Workers atomic counter race tests

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-19 23:23:48 -04:00

48 lines
1.5 KiB
TypeScript

import { DirType } from '../../shared/constants'
import type { Storage as S3Storage } from '../../shared/types'
import type { Database } from '../platform/interface'
import { decrementUsage, type Matter, purgeMatters } from './matter'
import { S3Service } from './s3'
import { cascadeDeleteByMatter } from './share'
import { getStorage } from './storage'
const s3 = new S3Service()
export async function purgeRecursively(db: Database, orgId: string, matters: Matter[]): Promise<number> {
const keysByStorage = new Map<string, { storage: S3Storage | null; keys: string[] }>()
const bytesByStorage = new Map<string, number>()
let totalBytes = 0
for (const m of matters) {
const size = m.size ?? 0
if (m.dirtype === DirType.FILE && size > 0) {
bytesByStorage.set(m.storageId, (bytesByStorage.get(m.storageId) ?? 0) + size)
totalBytes += size
}
if (!m.object) continue
let entry = keysByStorage.get(m.storageId)
if (!entry) {
const storage = (await getStorage(db, m.storageId)) as unknown as S3Storage | null
entry = { storage, keys: [] }
keysByStorage.set(m.storageId, entry)
}
entry.keys.push(m.object)
}
for (const { storage, keys } of keysByStorage.values()) {
if (storage && keys.length > 0) await s3.deleteObjects(storage, keys)
}
for (const m of matters) {
await cascadeDeleteByMatter(db, m.id)
}
await purgeMatters(
db,
orgId,
matters.map((m) => m.id),
)
await decrementUsage(db, orgId, bytesByStorage, totalBytes)
return matters.length
}