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