{"contents":"package db\n\nimport (\n\t\"database/sql\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n\n\t_ \"github.com/mattn/go-sqlite3\"\n\n\t\"tangled.org/core/notifier\"\n)\n\ntype DB struct {\n\tdb *sql.DB\n}\n\ntype Event struct {\n\tRkey string `json:\"rkey\"`\n\tNsid string `json:\"nsid\"`\n\tEventJson string `json:\"event\"`\n\tCreated int64 `json:\"created\"`\n}\n\nfunc Setup(dbPath string) (*DB, error) {\n\topts := []string{\n\t\t\"_foreign_keys=1\",\n\t\t\"_journal_mode=WAL\",\n\t\t\"_synchronous=NORMAL\",\n\t\t\"_auto_vacuum=incremental\",\n\t}\n\n\tdb, err := sql.Open(\"sqlite3\", dbPath+\"?\"+strings.Join(opts, \"\u0026\"))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_, err = db.Exec(`\n\t\tcreate table if not exists known_dids (\n\t\t\tdid text primary key\n\t\t);\n\n\t\tcreate table if not exists _jetstream (\n\t\t\tid integer primary key autoincrement,\n\t\t\tlast_time_us integer not null\n\t\t);\n\n\t\tcreate table if not exists events (\n\t\t\trkey text not null,\n\t\t\tnsid text not null,\n\t\t\tevent text not null,\n\t\t\tcreated integer not null default (strftime('%s', 'now')),\n\t\t\tprimary key (rkey, nsid)\n\t\t);\n\t`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn \u0026DB{db: db}, nil\n}\n\nfunc (d *DB) InsertEvent(event Event, notifier *notifier.Notifier) error {\n\t_, err := d.db.Exec(\n\t\t`insert into events (rkey, nsid, event, created) values (?, ?, ?, ?)`,\n\t\tevent.Rkey,\n\t\tevent.Nsid,\n\t\tevent.EventJson,\n\t\ttime.Now().UnixNano(),\n\t)\n\tnotifier.NotifyAll()\n\treturn err\n}\n\nfunc (d *DB) GetEvents(cursor int64) ([]Event, error) {\n\twhereClause := \"\"\n\targs := []any{}\n\tif cursor \u003e 0 {\n\t\twhereClause = \"where created \u003e ?\"\n\t\targs = append(args, cursor)\n\t}\n\n\tquery := fmt.Sprintf(`\n\t\tselect rkey, nsid, event, created\n\t\tfrom events\n\t\t%s\n\t\torder by created asc\n\t\tlimit 100\n\t`, whereClause)\n\n\trows, err := d.db.Query(query, args...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar evts []Event\n\tfor rows.Next() {\n\t\tvar ev Event\n\t\tif err := rows.Scan(\u0026ev.Rkey, \u0026ev.Nsid, \u0026ev.EventJson, \u0026ev.Created); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tevts = append(evts, ev)\n\t}\n\treturn evts, rows.Err()\n}\n\nfunc (d *DB) SaveLastTimeUs(lastTimeUs int64) error {\n\t_, err := d.db.Exec(`\n\t\tinsert into _jetstream (id, last_time_us)\n\t\tvalues (1, ?)\n\t\ton conflict(id) do update set last_time_us = excluded.last_time_us\n\t`, lastTimeUs)\n\treturn err\n}\n\nfunc (d *DB) GetLastTimeUs() (int64, error) {\n\tvar lastTimeUs int64\n\trow := d.db.QueryRow(`select last_time_us from _jetstream where id = 1;`)\n\terr := row.Scan(\u0026lastTimeUs)\n\treturn lastTimeUs, err\n}\n\nfunc (d *DB) AddDid(did string) error {\n\t_, err := d.db.Exec(`insert or ignore into known_dids (did) values (?)`, did)\n\treturn err\n}\n\nfunc (d *DB) RemoveDid(did string) error {\n\t_, err := d.db.Exec(`delete from known_dids where did = ?`, did)\n\treturn err\n}\n\nfunc (d *DB) GetAllDids() ([]string, error) {\n\trows, err := d.db.Query(`select did from known_dids`)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rows.Close()\n\n\tvar dids []string\n\tfor rows.Next() {\n\t\tvar did string\n\t\tif err := rows.Scan(\u0026did); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdids = append(dids, did)\n\t}\n\treturn dids, rows.Err()\n}\n\nfunc (d *DB) HasKnownDids() bool {\n\tvar count int\n\terr := d.db.QueryRow(`select count(*) from known_dids`).Scan(\u0026count)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn count \u003e 0\n}\n","path":"db/db.go","ref":"main"}