Files
remanso-jetstream/src/data/db.ts

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,
);
};