···120120 ReadAt *time.Time `json:"readAt,omitempty"`
121121}
122122123123+type APIKey struct {
124124+ ID string `json:"id"`
125125+ OwnerDID string `json:"ownerDid"`
126126+ Name string `json:"name"`
127127+ KeyHash string `json:"-"`
128128+ CreatedAt time.Time `json:"createdAt"`
129129+ LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
130130+}
131131+123132func New(dsn string) (*DB, error) {
124133 driver := "sqlite3"
125134 if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
···232241 )`)
233242 db.Exec(`CREATE INDEX IF NOT EXISTS idx_likes_subject_uri ON likes(subject_uri)`)
234243 db.Exec(`CREATE INDEX IF NOT EXISTS idx_likes_author_did ON likes(author_did)`)
244244+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_likes_author_subject ON likes(author_did, subject_uri)`)
235245236246 db.Exec(`CREATE TABLE IF NOT EXISTS collections (
237247 uri TEXT PRIMARY KEY,
···296306 db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_recipient ON notifications(recipient_did)`)
297307 db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at DESC)`)
298308309309+ db.Exec(`CREATE TABLE IF NOT EXISTS api_keys (
310310+ id TEXT PRIMARY KEY,
311311+ owner_did TEXT NOT NULL,
312312+ name TEXT NOT NULL,
313313+ key_hash TEXT NOT NULL,
314314+ created_at ` + dateType + ` NOT NULL,
315315+ last_used_at ` + dateType + `
316316+ )`)
317317+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`)
318318+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`)
319319+299320 db.runMigrations()
300321301322 db.Exec(`CREATE TABLE IF NOT EXISTS cursors (
302323 id TEXT PRIMARY KEY,
303303- last_cursor INTEGER NOT NULL,
324324+ last_cursor BIGINT NOT NULL,
304325 updated_at ` + dateType + ` NOT NULL
305326 )`)
306327···353374 db.Exec(`UPDATE annotations SET body_value = text WHERE body_value IS NULL AND text IS NOT NULL`)
354375 db.Exec(`UPDATE annotations SET target_title = title WHERE target_title IS NULL AND title IS NOT NULL`)
355376 db.Exec(`UPDATE annotations SET motivation = 'commenting' WHERE motivation IS NULL`)
377377+378378+ if db.driver == "postgres" {
379379+ db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`)
380380+ }
356381}
357382358383func (db *DB) Close() error {
···2525 },
2626 "icon": {
2727 "type": "string",
2828- "maxLength": 10,
2929- "maxGraphemes": 2,
3030- "description": "Emoji icon for the collection"
2828+ "maxLength": 100,
2929+ "maxGraphemes": 100,
3030+ "description": "Emoji icon or icon identifier for the collection"
3131 },
3232 "createdAt": {
3333 "type": "string",
-2
web/src/App.jsx
···1515import Collections from "./pages/Collections";
1616import CollectionDetail from "./pages/CollectionDetail";
1717import Privacy from "./pages/Privacy";
1818-1918import Terms from "./pages/Terms";
2020-2119import ScrollToTop from "./components/ScrollToTop";
22202321function AppContent() {