forked from tangled.org/core
Monorepo for Tangled — https://tangled.org
at knot-xrpc 4.9 kB view raw
1package db 2 3import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/bluesky-social/indigo/atproto/syntax" 10) 11 12type Star struct { 13 StarredByDid string 14 RepoAt syntax.ATURI 15 Created time.Time 16 Rkey string 17 18 // optionally, populate this when querying for reverse mappings 19 Repo *Repo 20} 21 22func (star *Star) ResolveRepo(e Execer) error { 23 if star.Repo != nil { 24 return nil 25 } 26 27 repo, err := GetRepoByAtUri(e, star.RepoAt.String()) 28 if err != nil { 29 return err 30 } 31 32 star.Repo = repo 33 return nil 34} 35 36func AddStar(e Execer, star *Star) error { 37 query := `insert or ignore into stars (starred_by_did, repo_at, rkey) values (?, ?, ?)` 38 _, err := e.Exec( 39 query, 40 star.StarredByDid, 41 star.RepoAt.String(), 42 star.Rkey, 43 ) 44 return err 45} 46 47// Get a star record 48func GetStar(e Execer, starredByDid string, repoAt syntax.ATURI) (*Star, error) { 49 query := ` 50 select starred_by_did, repo_at, created, rkey 51 from stars 52 where starred_by_did = ? and repo_at = ?` 53 row := e.QueryRow(query, starredByDid, repoAt) 54 55 var star Star 56 var created string 57 err := row.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) 58 if err != nil { 59 return nil, err 60 } 61 62 createdAtTime, err := time.Parse(time.RFC3339, created) 63 if err != nil { 64 log.Println("unable to determine followed at time") 65 star.Created = time.Now() 66 } else { 67 star.Created = createdAtTime 68 } 69 70 return &star, nil 71} 72 73// Remove a star 74func DeleteStar(e Execer, starredByDid string, repoAt syntax.ATURI) error { 75 _, err := e.Exec(`delete from stars where starred_by_did = ? and repo_at = ?`, starredByDid, repoAt) 76 return err 77} 78 79// Remove a star 80func DeleteStarByRkey(e Execer, starredByDid string, rkey string) error { 81 _, err := e.Exec(`delete from stars where starred_by_did = ? and rkey = ?`, starredByDid, rkey) 82 return err 83} 84 85func GetStarCount(e Execer, repoAt syntax.ATURI) (int, error) { 86 stars := 0 87 err := e.QueryRow( 88 `select count(starred_by_did) from stars where repo_at = ?`, repoAt).Scan(&stars) 89 if err != nil { 90 return 0, err 91 } 92 return stars, nil 93} 94 95func GetStarStatus(e Execer, userDid string, repoAt syntax.ATURI) bool { 96 if _, err := GetStar(e, userDid, repoAt); err != nil { 97 return false 98 } else { 99 return true 100 } 101} 102 103func GetStars(e Execer, limit int, filters ...filter) ([]Star, error) { 104 var conditions []string 105 var args []any 106 for _, filter := range filters { 107 conditions = append(conditions, filter.Condition()) 108 args = append(args, filter.Arg()...) 109 } 110 111 whereClause := "" 112 if conditions != nil { 113 whereClause = " where " + strings.Join(conditions, " and ") 114 } 115 116 limitClause := "" 117 if limit != 0 { 118 limitClause = fmt.Sprintf(" limit %d", limit) 119 } 120 121 repoQuery := fmt.Sprintf( 122 `select starred_by_did, repo_at, created, rkey 123 from stars 124 %s 125 order by created desc 126 %s`, 127 whereClause, 128 limitClause, 129 ) 130 rows, err := e.Query(repoQuery, args...) 131 if err != nil { 132 return nil, err 133 } 134 135 starMap := make(map[string][]Star) 136 for rows.Next() { 137 var star Star 138 var created string 139 err := rows.Scan(&star.StarredByDid, &star.RepoAt, &created, &star.Rkey) 140 if err != nil { 141 return nil, err 142 } 143 144 star.Created = time.Now() 145 if t, err := time.Parse(time.RFC3339, created); err == nil { 146 star.Created = t 147 } 148 149 repoAt := string(star.RepoAt) 150 starMap[repoAt] = append(starMap[repoAt], star) 151 } 152 153 // populate *Repo in each star 154 args = make([]any, len(starMap)) 155 i := 0 156 for r := range starMap { 157 args[i] = r 158 i++ 159 } 160 161 if len(args) == 0 { 162 return nil, nil 163 } 164 165 repos, err := GetRepos(e, 0, FilterIn("at_uri", args)) 166 if err != nil { 167 return nil, err 168 } 169 170 for _, r := range repos { 171 if stars, ok := starMap[string(r.RepoAt())]; ok { 172 for i := range stars { 173 stars[i].Repo = &r 174 } 175 } 176 } 177 178 var stars []Star 179 for _, s := range starMap { 180 stars = append(stars, s...) 181 } 182 183 return stars, nil 184} 185 186func GetAllStars(e Execer, limit int) ([]Star, error) { 187 var stars []Star 188 189 rows, err := e.Query(` 190 select 191 s.starred_by_did, 192 s.repo_at, 193 s.rkey, 194 s.created, 195 r.did, 196 r.name, 197 r.knot, 198 r.rkey, 199 r.created, 200 r.at_uri 201 from stars s 202 join repos r on s.repo_at = r.at_uri 203 `) 204 205 if err != nil { 206 return nil, err 207 } 208 defer rows.Close() 209 210 for rows.Next() { 211 var star Star 212 var repo Repo 213 var starCreatedAt, repoCreatedAt string 214 215 if err := rows.Scan( 216 &star.StarredByDid, 217 &star.RepoAt, 218 &star.Rkey, 219 &starCreatedAt, 220 &repo.Did, 221 &repo.Name, 222 &repo.Knot, 223 &repo.Rkey, 224 &repoCreatedAt, 225 &repo.AtUri, 226 ); err != nil { 227 return nil, err 228 } 229 230 star.Created, err = time.Parse(time.RFC3339, starCreatedAt) 231 if err != nil { 232 star.Created = time.Now() 233 } 234 repo.Created, err = time.Parse(time.RFC3339, repoCreatedAt) 235 if err != nil { 236 repo.Created = time.Now() 237 } 238 star.Repo = &repo 239 240 stars = append(stars, star) 241 } 242 243 if err := rows.Err(); err != nil { 244 return nil, err 245 } 246 247 return stars, nil 248}