Phase two: archive origin facet, redaction-aware reads, share revocation on redaction
- group-files: origin filter facet; GET /files/:id returns 410 for unavailable rows after the ACL recheck; fix latent group_members.status filter and unbound workspace parameter that would 500 listFiles - archive-bot: markRedacted revokes live group_file_shares rows in-transaction - new live-DB integration suite for the archive index surface
This commit is contained in:
parent
7cea73dc0d
commit
7ea0b1e137
6 changed files with 375 additions and 22 deletions
|
|
@ -46,7 +46,11 @@ export interface ArchiveStore {
|
|||
/** Index row + outbox done in one transaction. Returns the archive row id and indexed time. */
|
||||
commitArchived(record: ArchiveRecord, outboxId: string): Promise<{ id: string; indexedAt: Date }>;
|
||||
recordFailure(workspaceId: string, outboxId: string, error: string, terminal: boolean): Promise<OutboxRow>;
|
||||
/** Mark index rows unavailable and stop any not-yet-archived claim. Returns rows affected. */
|
||||
/**
|
||||
* Redaction/membership-removal path (contract point 5): mark index rows
|
||||
* unavailable AND revoke every live share link for them in the same
|
||||
* transaction, so serving stops immediately. Returns rows affected.
|
||||
*/
|
||||
markRedacted(workspaceId: string, roomId: string, eventId: string, claimKey: string): Promise<number>;
|
||||
countPending(workspaceIds: readonly string[]): Promise<number>;
|
||||
}
|
||||
|
|
@ -182,9 +186,19 @@ export class PgArchiveStore implements ArchiveStore {
|
|||
return this.withTenant(workspaceId, async (c) => {
|
||||
const res = await c.query(
|
||||
`UPDATE chat_media_archive SET available = false
|
||||
WHERE workspace_id = $1 AND room_id = $2 AND event_id = $3 AND available`,
|
||||
WHERE workspace_id = $1 AND room_id = $2 AND event_id = $3 AND available
|
||||
RETURNING id`,
|
||||
[workspaceId, roomId, eventId],
|
||||
);
|
||||
// "revoke links and mark the index unavailable; stop serving immediately":
|
||||
// every live share of the now-unavailable rows dies in the same tx.
|
||||
if (res.rows.length > 0) {
|
||||
await c.query(
|
||||
`UPDATE group_file_shares SET revoked_at = now()
|
||||
WHERE item_id = ANY($1::uuid[]) AND workspace_id = $2 AND revoked_at IS NULL`,
|
||||
[res.rows.map((r: { id: string }) => r.id), workspaceId],
|
||||
);
|
||||
}
|
||||
await c.query(
|
||||
`UPDATE archive_sync_outbox SET status = 'failed', last_error = 'redacted', updated_at = now()
|
||||
WHERE workspace_id = $1 AND claim_key = $2 AND status = 'pending'`,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue