148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
import { Database } from "@db/sqlite";
|
|
import type { Note } from "./note.ts";
|
|
|
|
export const db = new Database(Deno.env.get("SQLITE_PATH") ?? "notes.db");
|
|
try {
|
|
db.exec("PRAGMA journal_mode=WAL");
|
|
db.exec("PRAGMA busy_timeout=5000");
|
|
const [row] = db.prepare("PRAGMA journal_mode").all<{ journal_mode: string }>();
|
|
console.log(`[db] journal_mode=${row.journal_mode}, busy_timeout=5000`);
|
|
} catch (e) {
|
|
console.error("[db] failed to set PRAGMAs:", e);
|
|
}
|
|
|
|
type NoteRow = {
|
|
did: string;
|
|
rkey: string;
|
|
title: string;
|
|
publishedAt: string;
|
|
createdAt: string;
|
|
};
|
|
|
|
export const getNotes = (cursor?: string, limit = 20) => {
|
|
const notes = cursor
|
|
? db.prepare(
|
|
"SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 AND rkey < ? ORDER BY rkey DESC LIMIT ?",
|
|
).all<NoteRow>(cursor, limit)
|
|
: db.prepare(
|
|
"SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 ORDER BY rkey DESC LIMIT ?",
|
|
).all<NoteRow>(limit);
|
|
|
|
return {
|
|
notes,
|
|
cursor: notes.length === limit ? notes[notes.length - 1].rkey : undefined,
|
|
};
|
|
};
|
|
|
|
export const getNotesByDid = (did: string, cursor?: string, limit = 20) => {
|
|
const notes = cursor
|
|
? db.prepare(
|
|
"SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 AND did = ? AND rkey < ? ORDER BY rkey DESC LIMIT ?",
|
|
).all<NoteRow>(did, cursor, limit)
|
|
: db.prepare(
|
|
"SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 AND did = ? ORDER BY rkey DESC LIMIT ?",
|
|
).all<NoteRow>(did, limit);
|
|
|
|
return {
|
|
notes,
|
|
cursor: notes.length === limit ? notes[notes.length - 1].rkey : undefined,
|
|
};
|
|
};
|
|
|
|
export const getNotesByDids = (dids: string[], cursor?: string, limit = 20) => {
|
|
if (dids.length === 0) return { notes: [] };
|
|
const placeholders = dids.map(() => "?").join(", ");
|
|
const notes = cursor
|
|
? db.prepare(
|
|
`SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 AND did IN (${placeholders}) AND rkey < ? ORDER BY rkey DESC LIMIT ?`,
|
|
).all<NoteRow>(...dids, cursor, limit)
|
|
: db.prepare(
|
|
`SELECT did, rkey, title, publishedAt, createdAt, language FROM note WHERE discoverable = 1 AND did IN (${placeholders}) ORDER BY rkey DESC LIMIT ?`,
|
|
).all<NoteRow>(...dids, limit);
|
|
|
|
return {
|
|
notes,
|
|
cursor: notes.length === limit ? notes[notes.length - 1].rkey : undefined,
|
|
};
|
|
};
|
|
|
|
export const deleteNote = ({ did, rkey }: { did: string; rkey: string }) => {
|
|
db.exec("DELETE FROM note WHERE did = ? AND rkey = ?", did, rkey);
|
|
};
|
|
|
|
export const getCursor = (): string | undefined => {
|
|
const row = db.prepare(
|
|
"SELECT value FROM state WHERE key = 'cursor'",
|
|
).get<{ value: string }>();
|
|
return row?.value;
|
|
};
|
|
|
|
export const saveCursor = (cursor: number) => {
|
|
db.exec(
|
|
"INSERT OR REPLACE INTO state (key, value) VALUES ('cursor', ?)",
|
|
String(cursor),
|
|
);
|
|
};
|
|
|
|
type WebhookSubscriptionRow = {
|
|
id: number;
|
|
did: string;
|
|
method: string;
|
|
url: string;
|
|
};
|
|
|
|
export const addWebhookSubscription = (
|
|
{ did, method, url }: Omit<WebhookSubscriptionRow, "id">,
|
|
): WebhookSubscriptionRow => {
|
|
db.exec(
|
|
"INSERT INTO webhook_subscription (did, method, url) VALUES (?, ?, ?)",
|
|
did,
|
|
method,
|
|
url,
|
|
);
|
|
return db.prepare(
|
|
"SELECT id, did, method, url FROM webhook_subscription WHERE id = last_insert_rowid()",
|
|
).get<WebhookSubscriptionRow>()!;
|
|
};
|
|
|
|
export const deleteWebhooksByDid = (did: string): void => {
|
|
db.exec("DELETE FROM webhook_subscription WHERE did = ?", did);
|
|
};
|
|
|
|
export const getWebhooksByDid = (did: string): WebhookSubscriptionRow[] => {
|
|
return db.prepare(
|
|
"SELECT id, did, method, url FROM webhook_subscription WHERE did = ? ORDER BY id DESC LIMIT 10",
|
|
).all<WebhookSubscriptionRow>(did);
|
|
};
|
|
|
|
export const upsertNote = (note: Note) => {
|
|
const now = new Date().toISOString();
|
|
db.exec(
|
|
`
|
|
INSERT INTO note (
|
|
title,
|
|
publishedAt,
|
|
createdAt,
|
|
did,
|
|
rkey,
|
|
discoverable,
|
|
language
|
|
)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
ON CONFLICT(did, rkey)
|
|
DO UPDATE SET
|
|
title = excluded.title,
|
|
publishedAt = excluded.publishedAt,
|
|
discoverable = excluded.discoverable,
|
|
language = excluded.language
|
|
`,
|
|
note.title,
|
|
note.publishedAt ? new Date(note.publishedAt).toISOString() : now,
|
|
note.createdAt ? new Date(note.createdAt).toISOString() : now,
|
|
note.did,
|
|
note.rkey,
|
|
note.discoverable !== false ? 1 : 0,
|
|
note.language,
|
|
);
|
|
};
|