feat: add listed field to note for public listing visibility

Notes with listed=false are filtered out from all GET /notes queries.
The field defaults to true so existing and new notes without it remain visible.
Migration handles existing databases with ALTER TABLE ADD COLUMN.
This commit is contained in:
Julien Calixte
2026-02-25 23:03:00 +01:00
parent 62f981dd93
commit f39f62f1c7
4 changed files with 24 additions and 7 deletions

View File

@@ -46,6 +46,10 @@
"type": "string", "type": "string",
"description": "Display theme for the note.", "description": "Display theme for the note.",
"knownValues": ["light", "dark"] "knownValues": ["light", "dark"]
},
"listed": {
"type": "boolean",
"description": "Whether the note appears in public listings. Defaults to true."
} }
} }
} }

View File

@@ -22,10 +22,10 @@ type NoteRow = {
export const getNotes = (cursor?: string, limit = 20) => { export const getNotes = (cursor?: string, limit = 20) => {
const notes = cursor const notes = cursor
? db.prepare( ? db.prepare(
"SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE rkey < ? ORDER BY rkey DESC LIMIT ?", "SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE listed = 1 AND rkey < ? ORDER BY rkey DESC LIMIT ?",
).all<NoteRow>(cursor, limit) ).all<NoteRow>(cursor, limit)
: db.prepare( : db.prepare(
"SELECT did, rkey, title, publishedAt, createdAt FROM note ORDER BY rkey DESC LIMIT ?", "SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE listed = 1 ORDER BY rkey DESC LIMIT ?",
).all<NoteRow>(limit); ).all<NoteRow>(limit);
return { return {
@@ -37,10 +37,10 @@ export const getNotes = (cursor?: string, limit = 20) => {
export const getNotesByDid = (did: string, cursor?: string, limit = 20) => { export const getNotesByDid = (did: string, cursor?: string, limit = 20) => {
const notes = cursor const notes = cursor
? db.prepare( ? db.prepare(
"SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE did = ? AND rkey < ? ORDER BY rkey DESC LIMIT ?", "SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE listed = 1 AND did = ? AND rkey < ? ORDER BY rkey DESC LIMIT ?",
).all<NoteRow>(did, cursor, limit) ).all<NoteRow>(did, cursor, limit)
: db.prepare( : db.prepare(
"SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE did = ? ORDER BY rkey DESC LIMIT ?", "SELECT did, rkey, title, publishedAt, createdAt FROM note WHERE listed = 1 AND did = ? ORDER BY rkey DESC LIMIT ?",
).all<NoteRow>(did, limit); ).all<NoteRow>(did, limit);
return { return {
@@ -107,18 +107,21 @@ export const upsertNote = (note: Note) => {
publishedAt, publishedAt,
createdAt, createdAt,
did, did,
rkey rkey,
listed
) )
VALUES (?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(did, rkey) ON CONFLICT(did, rkey)
DO UPDATE SET DO UPDATE SET
title = excluded.title, title = excluded.title,
publishedAt = excluded.publishedAt publishedAt = excluded.publishedAt,
listed = excluded.listed
`, `,
note.title, note.title,
note.publishedAt ? new Date(note.publishedAt).toISOString() : now, note.publishedAt ? new Date(note.publishedAt).toISOString() : now,
note.createdAt ? new Date(note.createdAt).toISOString() : now, note.createdAt ? new Date(note.createdAt).toISOString() : now,
note.did, note.did,
note.rkey, note.rkey,
note.listed !== false ? 1 : 0,
); );
}; };

View File

@@ -4,4 +4,5 @@ export type Note = {
title: string; title: string;
publishedAt: string; publishedAt: string;
createdAt: string; createdAt: string;
listed?: boolean;
}; };

View File

@@ -7,10 +7,19 @@ db.exec(`
createdAt DATETIME NOT NULL, createdAt DATETIME NOT NULL,
did TEXT NOT NULL, did TEXT NOT NULL,
rkey TEXT NOT NULL, rkey TEXT NOT NULL,
listed INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (did, rkey) PRIMARY KEY (did, rkey)
); );
`); `);
try {
db.exec(
`ALTER TABLE note ADD COLUMN listed INTEGER NOT NULL DEFAULT 1;`,
);
} catch {
// Column already exists — no-op
}
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS state ( CREATE TABLE IF NOT EXISTS state (
key TEXT PRIMARY KEY, key TEXT PRIMARY KEY,