feat: complete v0.6 content and attachment experience

This commit is contained in:
2026-07-19 04:15:28 +08:00
parent d285f3e275
commit ed1a798587
17 changed files with 126 additions and 51 deletions
+8
View File
@@ -88,6 +88,10 @@ CREATE TABLE IF NOT EXISTS error_events (
id INTEGER PRIMARY KEY, scope TEXT NOT NULL, message TEXT NOT NULL, context_json TEXT,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS tag_aliases (
alias TEXT PRIMARY KEY COLLATE NOCASE, canonical TEXT NOT NULL COLLATE NOCASE,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`);
db.exec("INSERT OR IGNORE INTO source_members(source_id,user_id,role) SELECT id,user_id,'owner' FROM sources");
@@ -117,6 +121,10 @@ applyColumnMigration(16, "sources", "last_connection_error", "ALTER TABLE source
applyColumnMigration(17, "posts", "remote_url", "ALTER TABLE posts ADD COLUMN remote_url TEXT");
db.prepare("INSERT OR IGNORE INTO schema_migrations(version) VALUES(18)").run();
db.prepare("INSERT OR IGNORE INTO schema_migrations(version) VALUES(19)").run();
applyColumnMigration(20, "sources", "attachment_storage_mode", "ALTER TABLE sources ADD COLUMN attachment_storage_mode TEXT NOT NULL DEFAULT 'remote'");
applyColumnMigration(21, "sources", "attachment_cache_limit_bytes", "ALTER TABLE sources ADD COLUMN attachment_cache_limit_bytes INTEGER NOT NULL DEFAULT 104857600");
applyColumnMigration(22, "sources", "attachment_cache_error", "ALTER TABLE sources ADD COLUMN attachment_cache_error TEXT");
db.prepare("INSERT OR IGNORE INTO schema_migrations(version) VALUES(23)").run();
const admin = process.env.ADMIN_USERNAME;
const adminPassword = process.env.ADMIN_PASSWORD;
+19
View File
@@ -0,0 +1,19 @@
import { db } from "@/lib/db";
export function canonicalTag(tag: string) {
const clean = tag.trim().replace(/^#/, "");
const alias = db.prepare("SELECT canonical FROM tag_aliases WHERE alias=? COLLATE NOCASE").get(clean) as { canonical: string } | undefined;
return alias?.canonical || clean;
}
export function canonicalTags(tags: string[]) { return [...new Set(tags.map(canonicalTag).filter(Boolean))]; }
/** Removes only known tags from normal Markdown lines; fenced code is always untouched. */
export function withoutInlineTags(content: string, tags: string[]) {
let fenced = false;
return content.split("\n").map((line) => {
if (/^\s*```/.test(line)) { fenced = !fenced; return line; }
if (fenced) return line;
return tags.reduce((text, tag) => text.replace(new RegExp(`(^|\\s)#${tag.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?=\\s|$|[,。!?、,.!?])`, "gu"), "$1").replace(/ {2,}/g, " "), line);
}).join("\n");
}