···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://") {
···295304 )`)
296305 db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_recipient ON notifications(recipient_did)`)
297306 db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at DESC)`)
307307+308308+ db.Exec(`CREATE TABLE IF NOT EXISTS api_keys (
309309+ id TEXT PRIMARY KEY,
310310+ owner_did TEXT NOT NULL,
311311+ name TEXT NOT NULL,
312312+ key_hash TEXT NOT NULL,
313313+ created_at ` + dateType + ` NOT NULL,
314314+ last_used_at ` + dateType + `
315315+ )`)
316316+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`)
317317+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`)
298318299319 db.runMigrations()
300320
+54
backend/internal/db/queries.go
···910910911911 return "", fmt.Errorf("uri not found or no author")
912912}
913913+914914+func (db *DB) CreateAPIKey(key *APIKey) error {
915915+ _, err := db.Exec(db.Rebind(`
916916+ INSERT INTO api_keys (id, owner_did, name, key_hash, created_at)
917917+ VALUES (?, ?, ?, ?, ?)
918918+ `), key.ID, key.OwnerDID, key.Name, key.KeyHash, key.CreatedAt)
919919+ return err
920920+}
921921+922922+func (db *DB) GetAPIKeysByOwner(ownerDID string) ([]APIKey, error) {
923923+ rows, err := db.Query(db.Rebind(`
924924+ SELECT id, owner_did, name, key_hash, created_at, last_used_at
925925+ FROM api_keys
926926+ WHERE owner_did = ?
927927+ ORDER BY created_at DESC
928928+ `), ownerDID)
929929+ if err != nil {
930930+ return nil, err
931931+ }
932932+ defer rows.Close()
933933+934934+ var keys []APIKey
935935+ for rows.Next() {
936936+ var k APIKey
937937+ if err := rows.Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt); err != nil {
938938+ return nil, err
939939+ }
940940+ keys = append(keys, k)
941941+ }
942942+ return keys, nil
943943+}
944944+945945+func (db *DB) GetAPIKeyByHash(keyHash string) (*APIKey, error) {
946946+ var k APIKey
947947+ err := db.QueryRow(db.Rebind(`
948948+ SELECT id, owner_did, name, key_hash, created_at, last_used_at
949949+ FROM api_keys
950950+ WHERE key_hash = ?
951951+ `), keyHash).Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt)
952952+ if err != nil {
953953+ return nil, err
954954+ }
955955+ return &k, nil
956956+}
957957+958958+func (db *DB) DeleteAPIKey(id, ownerDID string) error {
959959+ _, err := db.Exec(db.Rebind(`DELETE FROM api_keys WHERE id = ? AND owner_did = ?`), id, ownerDID)
960960+ return err
961961+}
962962+963963+func (db *DB) UpdateAPIKeyLastUsed(id string) error {
964964+ _, err := db.Exec(db.Rebind(`UPDATE api_keys SET last_used_at = ? WHERE id = ?`), time.Now(), id)
965965+ return err
966966+}
···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() {