···4747 name text not null,4848 knot text not null,4949 rkey text not null,5050+ at_uri text not null unique,5051 created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),5152 unique(did, name, knot, rkey)5253 );···6059 create table if not exists follows (6160 user_did text not null,6261 subject_did text not null,6262+ at_uri text not null unique,6363 rkey text not null,6464 followed_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),6565 primary key (user_did, subject_did),6666 check (user_did <> subject_did)6767 );6868+ create table if not exists issues (6969+ id integer primary key autoincrement,7070+ owner_did text not null,7171+ repo_at text not null,7272+ issue_id integer not null unique,7373+ title text not null,7474+ body text not null,7575+ created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),7676+ unique(repo_at, issue_id),7777+ foreign key (repo_at) references repos(at_uri) on delete cascade7878+ );7979+ create table if not exists comments (8080+ id integer primary key autoincrement,8181+ owner_did text not null,8282+ issue_id integer not null,8383+ repo_at text not null,8484+ comment_id integer not null,8585+ body text not null,8686+ created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),8787+ unique(issue_id, comment_id),8888+ foreign key (issue_id) references issues(issue_id) on delete cascade8989+ );6890 create table if not exists _jetstream (6991 id integer primary key autoincrement,7092 last_time_us integer not null7193 );9494+9595+ create table if not exists repo_issue_seqs (9696+ repo_at text primary key,9797+ next_issue_id integer not null default 19898+ );9999+72100 `)73101 if err != nil {74102 return nil, err
+179
appview/db/issues.go
···11+package db22+33+import "time"44+55+type Issue struct {66+ RepoAt string77+ OwnerDid string88+ IssueId int99+ Created *time.Time1010+ Title string1111+ Body string1212+ Open bool1313+}1414+1515+type Comment struct {1616+ OwnerDid string1717+ RepoAt string1818+ Issue int1919+ CommentId int2020+ Body string2121+ Created *time.Time2222+}2323+2424+func (d *DB) NewIssue(issue *Issue) (int, error) {2525+ tx, err := d.db.Begin()2626+ if err != nil {2727+ return 0, err2828+ }2929+ defer tx.Rollback()3030+3131+ _, err = tx.Exec(`3232+ insert or ignore into repo_issue_seqs (repo_at, next_issue_id)3333+ values (?, 1)3434+ `, issue.RepoAt)3535+ if err != nil {3636+ return 0, err3737+ }3838+3939+ var nextId int4040+ err = tx.QueryRow(`4141+ update repo_issue_seqs4242+ set next_issue_id = next_issue_id + 14343+ where repo_at = ?4444+ returning next_issue_id - 14545+ `, issue.RepoAt).Scan(&nextId)4646+ if err != nil {4747+ return 0, err4848+ }4949+5050+ issue.IssueId = nextId5151+5252+ _, err = tx.Exec(`5353+ insert into issues (repo_at, owner_did, issue_id, title, body)5454+ values (?, ?, ?, ?, ?)5555+ `, issue.RepoAt, issue.OwnerDid, issue.IssueId, issue.Title, issue.Body)5656+ if err != nil {5757+ return 0, err5858+ }5959+6060+ if err := tx.Commit(); err != nil {6161+ return 0, err6262+ }6363+6464+ return nextId, nil6565+}6666+6767+func (d *DB) GetIssues(repoAt string) ([]Issue, error) {6868+ var issues []Issue6969+7070+ rows, err := d.db.Query(`select owner_did, issue_id, created, title, body, open from issues where repo_at = ?`, repoAt)7171+ if err != nil {7272+ return nil, err7373+ }7474+ defer rows.Close()7575+7676+ for rows.Next() {7777+ var issue Issue7878+ var createdAt string7979+ err := rows.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open)8080+ if err != nil {8181+ return nil, err8282+ }8383+8484+ createdTime, err := time.Parse(time.RFC3339, createdAt)8585+ if err != nil {8686+ return nil, err8787+ }8888+ issue.Created = &createdTime8989+9090+ issues = append(issues, issue)9191+ }9292+9393+ if err := rows.Err(); err != nil {9494+ return nil, err9595+ }9696+9797+ return issues, nil9898+}9999+100100+func (d *DB) GetIssueWithComments(repoAt string, issueId int) (*Issue, []Comment, error) {101101+ query := `select owner_did, issue_id, created, title, body, open from issues where repo_at = ? and issue_id = ?`102102+ row := d.db.QueryRow(query, repoAt, issueId)103103+104104+ var issue Issue105105+ var createdAt string106106+ err := row.Scan(&issue.OwnerDid, &issue.IssueId, &createdAt, &issue.Title, &issue.Body, &issue.Open)107107+ if err != nil {108108+ return nil, nil, err109109+ }110110+111111+ createdTime, err := time.Parse(time.RFC3339, createdAt)112112+ if err != nil {113113+ return nil, nil, err114114+ }115115+ issue.Created = &createdTime116116+117117+ comments, err := d.GetComments(repoAt, issueId)118118+ if err != nil {119119+ return nil, nil, err120120+ }121121+122122+ return &issue, comments, nil123123+}124124+125125+func (d *DB) NewComment(comment *Comment) error {126126+ query := `insert into comments (owner_did, repo_at, issue_id, comment_id, body) values (?, ?, ?, ?, ?)`127127+ _, err := d.db.Exec(128128+ query,129129+ comment.OwnerDid,130130+ comment.RepoAt,131131+ comment.Issue,132132+ comment.CommentId,133133+ comment.Body,134134+ )135135+ return err136136+}137137+138138+func (d *DB) GetComments(repoAt string, issueId int) ([]Comment, error) {139139+ var comments []Comment140140+141141+ rows, err := d.db.Query(`select owner_did, issue_id, comment_id, body, created from comments where repo_at = ? and issue_id = ? order by created asc`, repoAt, issueId)142142+ if err != nil {143143+ return nil, err144144+ }145145+ defer rows.Close()146146+147147+ for rows.Next() {148148+ var comment Comment149149+ var createdAt string150150+ err := rows.Scan(&comment.OwnerDid, &comment.Issue, &comment.CommentId, &comment.Body, &createdAt)151151+ if err != nil {152152+ return nil, err153153+ }154154+155155+ createdAtTime, err := time.Parse(time.RFC3339, createdAt)156156+ if err != nil {157157+ return nil, err158158+ }159159+ comment.Created = &createdAtTime160160+161161+ comments = append(comments, comment)162162+ }163163+164164+ if err := rows.Err(); err != nil {165165+ return nil, err166166+ }167167+168168+ return comments, nil169169+}170170+171171+func (d *DB) CloseIssue(repoAt string, issueId int) error {172172+ _, err := d.db.Exec(`update issues set open = 0 where repo_at = ? and issue_id = ?`, repoAt, issueId)173173+ return err174174+}175175+176176+func (d *DB) ReopenIssue(repoAt string, issueId int) error {177177+ _, err := d.db.Exec(`update issues set open = 1 where repo_at = ? and issue_id = ?`, repoAt, issueId)178178+ return err179179+}
+4-3
appview/db/repos.go
···1111 Knot string1212 Rkey string1313 Created time.Time1414+ AtUri string1415}15161617func (d *DB) GetAllRepos() ([]Repo, error) {···6766func (d *DB) GetRepo(did, name string) (*Repo, error) {6867 var repo Repo69687070- row := d.db.QueryRow(`select did, name, knot, created from repos where did = ? and name = ?`, did, name)6969+ row := d.db.QueryRow(`select did, name, knot, created, at_uri from repos where did = ? and name = ?`, did, name)71707271 var createdAt string7373- if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt); err != nil {7272+ if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri); err != nil {7473 return nil, err7574 }7675 createdAtTime, _ := time.Parse(time.RFC3339, createdAt)···8079}81808281func (d *DB) AddRepo(repo *Repo) error {8383- _, err := d.db.Exec(`insert into repos (did, name, knot, rkey) values (?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey)8282+ _, err := d.db.Exec(`insert into repos (did, name, knot, rkey, at_uri) values (?, ?, ?, ?, ?)`, repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri)8483 return err8584}8685