v3: added attachments to resources, allow users to paste images into TextEditor, strengthened WS reliability

This commit is contained in:
khannurien
2026-03-27 08:53:32 +00:00
parent ca70bdc14b
commit f0f6472db6
19 changed files with 450 additions and 57 deletions

View File

@@ -0,0 +1,42 @@
import { db } from "../model/db.ts";
import { APIErrorCode, APIException } from "../model/interfaces.ts";
export function createAttachment(id: string, mime: string): void {
db.prepare(
"INSERT INTO attachments (id, mime, created_at) VALUES (?, ?, ?)",
).run(id, mime, new Date().toISOString());
}
export function getAttachment(attachmentId: string): { mime: string } {
const row = db.prepare(
"SELECT mime FROM attachments WHERE id = ?",
).get(attachmentId) as { mime: string } | undefined;
if (!row) {
throw new APIException(
APIErrorCode.NOT_FOUND,
404,
"Attachment not found",
);
}
return { mime: row.mime };
}
// UUID pattern used to extract attachment IDs from markdown/text bodies.
const ATTACHMENT_RE =
/\/api\/attachments\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;
/**
* Parse `text` for attachment URLs and bind any unowned attachments to
* `resourceId`. Only updates rows where resource_id IS NULL so a user cannot
* claim attachments that already belong to another resource.
*/
export function linkAttachments(text: string, resourceId: string): void {
const ids = [...text.matchAll(ATTACHMENT_RE)].map((m) => m[1]);
for (const id of ids) {
db.prepare(
"UPDATE attachments SET resource_id = ? WHERE id = ? AND resource_id IS NULL",
).run(resourceId, id);
}
}