forked from tangled.org/core
this repo has no description
1package db 2 3import ( 4 "database/sql" 5 "fmt" 6 "time" 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 securejoin "github.com/cyphar/filepath-securejoin" 10 "tangled.sh/tangled.sh/core/api/tangled" 11) 12 13type Repo struct { 14 Did string 15 Name string 16 Knot string 17 Rkey string 18 Created time.Time 19 AtUri string 20 Description string 21 22 // optionally, populate this when querying for reverse mappings 23 RepoStats *RepoStats 24 25 // optional 26 Source string 27} 28 29func (r Repo) RepoAt() syntax.ATURI { 30 return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", r.Did, tangled.RepoNSID, r.Rkey)) 31} 32 33func (r Repo) DidSlashRepo() string { 34 p, _ := securejoin.SecureJoin(r.Did, r.Name) 35 return p 36} 37 38func GetAllRepos(e Execer, limit int) ([]Repo, error) { 39 var repos []Repo 40 41 rows, err := e.Query( 42 `select did, name, knot, rkey, description, created, source 43 from repos 44 order by created desc 45 limit ? 46 `, 47 limit, 48 ) 49 if err != nil { 50 return nil, err 51 } 52 defer rows.Close() 53 54 for rows.Next() { 55 var repo Repo 56 err := scanRepo( 57 rows, &repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &repo.Description, &repo.Created, &repo.Source, 58 ) 59 if err != nil { 60 return nil, err 61 } 62 repos = append(repos, repo) 63 } 64 65 if err := rows.Err(); err != nil { 66 return nil, err 67 } 68 69 return repos, nil 70} 71 72func GetAllReposByDid(e Execer, did string) ([]Repo, error) { 73 var repos []Repo 74 75 rows, err := e.Query( 76 `select 77 r.did, 78 r.name, 79 r.knot, 80 r.rkey, 81 r.description, 82 r.created, 83 count(s.id) as star_count, 84 r.source 85 from 86 repos r 87 left join 88 stars s on r.at_uri = s.repo_at 89 where 90 r.did = ? 91 group by 92 r.at_uri 93 order by r.created desc`, 94 did) 95 if err != nil { 96 return nil, err 97 } 98 defer rows.Close() 99 100 for rows.Next() { 101 var repo Repo 102 var repoStats RepoStats 103 var createdAt string 104 var nullableDescription sql.NullString 105 var nullableSource sql.NullString 106 107 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount, &nullableSource) 108 if err != nil { 109 return nil, err 110 } 111 112 if nullableDescription.Valid { 113 repo.Description = nullableDescription.String 114 } 115 116 if nullableSource.Valid { 117 repo.Source = nullableSource.String 118 } 119 120 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 121 if err != nil { 122 repo.Created = time.Now() 123 } else { 124 repo.Created = createdAtTime 125 } 126 127 repo.RepoStats = &repoStats 128 129 repos = append(repos, repo) 130 } 131 132 if err := rows.Err(); err != nil { 133 return nil, err 134 } 135 136 return repos, nil 137} 138 139func GetRepo(e Execer, did, name string) (*Repo, error) { 140 var repo Repo 141 var nullableDescription sql.NullString 142 143 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where did = ? and name = ?`, did, name) 144 145 var createdAt string 146 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 147 return nil, err 148 } 149 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 150 repo.Created = createdAtTime 151 152 if nullableDescription.Valid { 153 repo.Description = nullableDescription.String 154 } else { 155 repo.Description = "" 156 } 157 158 return &repo, nil 159} 160 161func GetRepoByAtUri(e Execer, atUri string) (*Repo, error) { 162 var repo Repo 163 var nullableDescription sql.NullString 164 165 row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where at_uri = ?`, atUri) 166 167 var createdAt string 168 if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 169 return nil, err 170 } 171 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 172 repo.Created = createdAtTime 173 174 if nullableDescription.Valid { 175 repo.Description = nullableDescription.String 176 } else { 177 repo.Description = "" 178 } 179 180 return &repo, nil 181} 182 183func AddRepo(e Execer, repo *Repo) error { 184 _, err := e.Exec( 185 `insert into repos 186 (did, name, knot, rkey, at_uri, description, source) 187 values (?, ?, ?, ?, ?, ?, ?)`, 188 repo.Did, repo.Name, repo.Knot, repo.Rkey, repo.AtUri, repo.Description, repo.Source, 189 ) 190 return err 191} 192 193func RemoveRepo(e Execer, did, name string) error { 194 _, err := e.Exec(`delete from repos where did = ? and name = ?`, did, name) 195 return err 196} 197 198func GetRepoSource(e Execer, repoAt syntax.ATURI) (string, error) { 199 var nullableSource sql.NullString 200 err := e.QueryRow(`select source from repos where at_uri = ?`, repoAt).Scan(&nullableSource) 201 if err != nil { 202 return "", err 203 } 204 return nullableSource.String, nil 205} 206 207func GetForksByDid(e Execer, did string) ([]Repo, error) { 208 var repos []Repo 209 210 rows, err := e.Query( 211 `select did, name, knot, rkey, description, created, at_uri, source 212 from repos 213 where did = ? and source is not null and source != '' 214 order by created desc`, 215 did, 216 ) 217 if err != nil { 218 return nil, err 219 } 220 defer rows.Close() 221 222 for rows.Next() { 223 var repo Repo 224 var createdAt string 225 var nullableDescription sql.NullString 226 var nullableSource sql.NullString 227 228 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 229 if err != nil { 230 return nil, err 231 } 232 233 if nullableDescription.Valid { 234 repo.Description = nullableDescription.String 235 } 236 237 if nullableSource.Valid { 238 repo.Source = nullableSource.String 239 } 240 241 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 242 if err != nil { 243 repo.Created = time.Now() 244 } else { 245 repo.Created = createdAtTime 246 } 247 248 repos = append(repos, repo) 249 } 250 251 if err := rows.Err(); err != nil { 252 return nil, err 253 } 254 255 return repos, nil 256} 257 258func GetForkByDid(e Execer, did string, name string) (*Repo, error) { 259 var repo Repo 260 var createdAt string 261 var nullableDescription sql.NullString 262 var nullableSource sql.NullString 263 264 row := e.QueryRow( 265 `select did, name, knot, rkey, description, created, at_uri, source 266 from repos 267 where did = ? and name = ? and source is not null and source != ''`, 268 did, name, 269 ) 270 271 err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repo.AtUri, &nullableSource) 272 if err != nil { 273 return nil, err 274 } 275 276 if nullableDescription.Valid { 277 repo.Description = nullableDescription.String 278 } 279 280 if nullableSource.Valid { 281 repo.Source = nullableSource.String 282 } 283 284 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 285 if err != nil { 286 repo.Created = time.Now() 287 } else { 288 repo.Created = createdAtTime 289 } 290 291 return &repo, nil 292} 293 294func AddCollaborator(e Execer, collaborator, repoOwnerDid, repoName, repoKnot string) error { 295 _, err := e.Exec( 296 `insert into collaborators (did, repo) 297 values (?, (select id from repos where did = ? and name = ? and knot = ?));`, 298 collaborator, repoOwnerDid, repoName, repoKnot) 299 return err 300} 301 302func UpdateDescription(e Execer, repoAt, newDescription string) error { 303 _, err := e.Exec( 304 `update repos set description = ? where at_uri = ?`, newDescription, repoAt) 305 return err 306} 307 308func CollaboratingIn(e Execer, collaborator string) ([]Repo, error) { 309 var repos []Repo 310 311 rows, err := e.Query( 312 `select 313 r.did, r.name, r.knot, r.rkey, r.description, r.created, count(s.id) as star_count 314 from 315 repos r 316 join 317 collaborators c on r.id = c.repo 318 left join 319 stars s on r.at_uri = s.repo_at 320 where 321 c.did = ? 322 group by 323 r.id;`, collaborator) 324 if err != nil { 325 return nil, err 326 } 327 defer rows.Close() 328 329 for rows.Next() { 330 var repo Repo 331 var repoStats RepoStats 332 var createdAt string 333 var nullableDescription sql.NullString 334 335 err := rows.Scan(&repo.Did, &repo.Name, &repo.Knot, &repo.Rkey, &nullableDescription, &createdAt, &repoStats.StarCount) 336 if err != nil { 337 return nil, err 338 } 339 340 if nullableDescription.Valid { 341 repo.Description = nullableDescription.String 342 } else { 343 repo.Description = "" 344 } 345 346 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 347 if err != nil { 348 repo.Created = time.Now() 349 } else { 350 repo.Created = createdAtTime 351 } 352 353 repo.RepoStats = &repoStats 354 355 repos = append(repos, repo) 356 } 357 358 if err := rows.Err(); err != nil { 359 return nil, err 360 } 361 362 return repos, nil 363} 364 365type RepoStats struct { 366 StarCount int 367 IssueCount IssueCount 368 PullCount PullCount 369} 370 371func scanRepo(rows *sql.Rows, did, name, knot, rkey, description *string, created *time.Time, source *string) error { 372 var createdAt string 373 var nullableDescription sql.NullString 374 var nullableSource sql.NullString 375 if err := rows.Scan(did, name, knot, rkey, &nullableDescription, &createdAt, &nullableSource); err != nil { 376 return err 377 } 378 379 if nullableDescription.Valid { 380 *description = nullableDescription.String 381 } else { 382 *description = "" 383 } 384 385 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 386 if err != nil { 387 *created = time.Now() 388 } else { 389 *created = createdAtTime 390 } 391 392 if nullableSource.Valid { 393 *source = nullableSource.String 394 } else { 395 *source = "" 396 } 397 398 return nil 399}