···129 LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
130}
13100000000000132func New(dsn string) (*DB, error) {
133 driver := "sqlite3"
134 if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
···328 db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`)
329 db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`)
330000000000000331 db.runMigrations()
332333 db.Exec(`CREATE TABLE IF NOT EXISTS cursors (
···365 return err
366}
367000000000000000000000000000000000368func (db *DB) runMigrations() {
369370 db.Exec(`ALTER TABLE sessions ADD COLUMN dpop_key TEXT`)
···385 db.Exec(`UPDATE annotations SET body_value = text WHERE body_value IS NULL AND text IS NOT NULL`)
386 db.Exec(`UPDATE annotations SET target_title = title WHERE target_title IS NULL AND title IS NOT NULL`)
387 db.Exec(`UPDATE annotations SET motivation = 'commenting' WHERE motivation IS NULL`)
00388389 if db.driver == "postgres" {
390 db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`)
···129 LastUsedAt *time.Time `json:"lastUsedAt,omitempty"`
130}
131132+type Profile struct {
133+ URI string `json:"uri"`
134+ AuthorDID string `json:"authorDid"`
135+ Bio *string `json:"bio,omitempty"`
136+ Website *string `json:"website,omitempty"`
137+ LinksJSON *string `json:"links,omitempty"`
138+ CreatedAt time.Time `json:"createdAt"`
139+ IndexedAt time.Time `json:"indexedAt"`
140+ CID *string `json:"cid,omitempty"`
141+}
142+143func New(dsn string) (*DB, error) {
144 driver := "sqlite3"
145 if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
···339 db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`)
340 db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`)
341342+ db.Exec(`CREATE TABLE IF NOT EXISTS profiles (
343+ uri TEXT PRIMARY KEY,
344+ author_did TEXT NOT NULL,
345+ bio TEXT,
346+ website TEXT,
347+ links_json TEXT,
348+ created_at ` + dateType + ` NOT NULL,
349+ indexed_at ` + dateType + ` NOT NULL,
350+ cid TEXT
351+ )`)
352+ db.Exec(`CREATE INDEX IF NOT EXISTS idx_profiles_author_did ON profiles(author_did)`)
353+354 db.runMigrations()
355356 db.Exec(`CREATE TABLE IF NOT EXISTS cursors (
···388 return err
389}
390391+func (db *DB) GetProfile(did string) (*Profile, error) {
392+ var p Profile
393+ err := db.QueryRow("SELECT uri, author_did, bio, website, links_json, created_at, indexed_at FROM profiles WHERE author_did = $1", did).Scan(
394+ &p.URI, &p.AuthorDID, &p.Bio, &p.Website, &p.LinksJSON, &p.CreatedAt, &p.IndexedAt,
395+ )
396+ if err == sql.ErrNoRows {
397+ return nil, nil
398+ }
399+ if err != nil {
400+ return nil, err
401+ }
402+ return &p, nil
403+}
404+405+func (db *DB) UpsertProfile(p *Profile) error {
406+ query := `
407+ INSERT INTO profiles (uri, author_did, bio, website, links_json, created_at, indexed_at)
408+ VALUES ($1, $2, $3, $4, $5, $6, $7)
409+ ON CONFLICT(uri) DO UPDATE SET
410+ bio = EXCLUDED.bio,
411+ website = EXCLUDED.website,
412+ links_json = EXCLUDED.links_json,
413+ indexed_at = EXCLUDED.indexed_at
414+ `
415+ _, err := db.Exec(db.Rebind(query), p.URI, p.AuthorDID, p.Bio, p.Website, p.LinksJSON, p.CreatedAt, p.IndexedAt)
416+ return err
417+}
418+419+func (db *DB) DeleteProfile(uri string) error {
420+ _, err := db.Exec("DELETE FROM profiles WHERE uri = $1", uri)
421+ return err
422+}
423+424func (db *DB) runMigrations() {
425426 db.Exec(`ALTER TABLE sessions ADD COLUMN dpop_key TEXT`)
···441 db.Exec(`UPDATE annotations SET body_value = text WHERE body_value IS NULL AND text IS NOT NULL`)
442 db.Exec(`UPDATE annotations SET target_title = title WHERE target_title IS NULL AND title IS NOT NULL`)
443 db.Exec(`UPDATE annotations SET motivation = 'commenting' WHERE motivation IS NULL`)
444+445+ db.Exec(`ALTER TABLE profiles ADD COLUMN website TEXT`)
446447 if db.driver == "postgres" {
448 db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`)