···49 created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
50 unique(did, name, knot)
51 );
00000052 create table if not exists follows (
53 user_did text not null,
54 subject_did text not null,
···49 created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
50 unique(did, name, knot)
51 );
52+ create table if not exists collaborators (
53+ id integer primary key autoincrement,
54+ did text not null,
55+ repo integer not null,
56+ foreign key (repo) references repos(id) on delete cascade
57+ );
58 create table if not exists follows (
59 user_did text not null,
60 subject_did text not null,
+57-7
appview/db/repos.go
···1package db
23-import "time"
00045type Repo struct {
6 Did string
···19 defer rows.Close()
2021 for rows.Next() {
22- var repo Repo
23- var createdAt string
24- if err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt); err != nil {
25 return nil, err
26 }
27- createdAtTime, _ := time.Parse(time.RFC3339, createdAt)
28- repo.Created = &createdAtTime
29- repos = append(repos, repo)
30 }
3132 if err := rows.Err(); err != nil {
···60 _, err := d.db.Exec(`delete from repos where did = ? and name = ? and knot = ?`, did, name, knot)
61 return err
62}
00000000000000000000000000000000000000000000000000
···1package db
23+import (
4+ "database/sql"
5+ "time"
6+)
78type Repo struct {
9 Did string
···22 defer rows.Close()
2324 for rows.Next() {
25+ repo, err := scanRepo(rows)
26+ if err != nil {
027 return nil, err
28 }
29+ repos = append(repos, *repo)
0030 }
3132 if err := rows.Err(); err != nil {
···60 _, err := d.db.Exec(`delete from repos where did = ? and name = ? and knot = ?`, did, name, knot)
61 return err
62}
63+64+func (d *DB) AddCollaborator(collaborator, repoOwnerDid, repoName, repoKnot string) error {
65+ _, err := d.db.Exec(
66+ `insert into collaborators (did, repo)
67+ values (?, (select id from repos where did = ? and name = ? and knot = ?));`,
68+ collaborator, repoOwnerDid, repoName, repoKnot)
69+ return err
70+}
71+72+func (d *DB) CollaboratingIn(collaborator string) ([]Repo, error) {
73+ var repos []Repo
74+75+ rows, err := d.db.Query(`select r.* from repos r join collaborators c on r.id = c.repo where c.did = ?;`, collaborator)
76+ if err != nil {
77+ return nil, err
78+ }
79+ defer rows.Close()
80+81+ for rows.Next() {
82+ repo, err := scanRepo(rows)
83+ if err != nil {
84+ return nil, err
85+ }
86+ repos = append(repos, *repo)
87+ }
88+89+ if err := rows.Err(); err != nil {
90+ return nil, err
91+ }
92+93+ return repos, nil
94+}
95+96+func scanRepo(rows *sql.Rows) (*Repo, error) {
97+ var repo Repo
98+ var createdAt string
99+ if err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt); err != nil {
100+ return nil, err
101+ }
102+103+ createdAtTime, err := time.Parse(time.RFC3339, createdAt)
104+ if err != nil {
105+ now := time.Now()
106+ repo.Created = &now
107+ }
108+109+ repo.Created = &createdAtTime
110+111+ return &repo, nil
112+}