Monorepo for Tangled tangled.org

appview/{db,models}: add repo sites and deploys models/queries

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>

anirudh.fi 70ed337e 7a94ba0c

verified
+247 -23
+25 -16
appview/db/db.go
··· 616 616 deleted text -- timestamp when the domain was released/unclaimed; null means actively claimed 617 617 ); 618 618 619 + create table if not exists repo_sites ( 620 + id integer primary key autoincrement, 621 + repo_at text not null unique, 622 + branch text not null, 623 + dir text not null default '/', 624 + is_index integer not null default 0, 625 + created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 626 + updated text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 627 + foreign key (repo_at) references repos(at_uri) on delete cascade 628 + ); 629 + 630 + create table if not exists site_deploys ( 631 + id integer primary key autoincrement, 632 + repo_at text not null, 633 + branch text not null, 634 + dir text not null default '/', 635 + commit_sha text not null default '', 636 + status text not null check (status in ('success', 'failure')), 637 + trigger text not null check (trigger in ('config_change', 'push')), 638 + error text not null default '', 639 + created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 640 + foreign key (repo_at) references repos(at_uri) on delete cascade 641 + ); 642 + 619 643 create table if not exists migrations ( 620 644 id integer primary key autoincrement, 621 645 name text unique ··· 635 659 create index if not exists idx_references_to_at on reference_links(to_at); 636 660 create index if not exists idx_webhooks_repo_at on webhooks(repo_at); 637 661 create index if not exists idx_webhook_deliveries_webhook_id on webhook_deliveries(webhook_id); 662 + create index if not exists idx_site_deploys_repo_at on site_deploys(repo_at); 638 663 `) 639 664 if err != nil { 640 665 return nil, err ··· 1258 1283 1259 1284 -- rename new table 1260 1285 alter table profile_stats_new rename to profile_stats; 1261 - `) 1262 - return err 1263 - }) 1264 - 1265 - orm.RunMigration(conn, logger, "add-repo-sites-table", func(tx *sql.Tx) error { 1266 - _, err := tx.Exec(` 1267 - create table if not exists repo_sites ( 1268 - id integer primary key autoincrement, 1269 - repo_at text not null unique, 1270 - branch text not null, 1271 - dir text not null default '/', 1272 - is_index integer not null default 0, 1273 - created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1274 - updated text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 1275 - foreign key (repo_at) references repos(at_uri) on delete cascade 1276 - ); 1277 1286 `) 1278 1287 return err 1279 1288 })
+102
appview/db/site_deploys.go
··· 1 + package db 2 + 3 + import ( 4 + "fmt" 5 + "time" 6 + 7 + "tangled.org/core/appview/models" 8 + ) 9 + 10 + // AddSiteDeploy records a site deploy attempt. 11 + func AddSiteDeploy(e Execer, deploy *models.SiteDeploy) error { 12 + result, err := e.Exec(` 13 + insert into site_deploys ( 14 + repo_at, 15 + branch, 16 + dir, 17 + commit_sha, 18 + status, 19 + trigger, 20 + error 21 + ) values (?, ?, ?, ?, ?, ?, ?) 22 + `, 23 + deploy.RepoAt, 24 + deploy.Branch, 25 + deploy.Dir, 26 + deploy.CommitSHA, 27 + string(deploy.Status), 28 + string(deploy.Trigger), 29 + deploy.Error, 30 + ) 31 + if err != nil { 32 + return fmt.Errorf("failed to insert site deploy: %w", err) 33 + } 34 + 35 + id, err := result.LastInsertId() 36 + if err != nil { 37 + return fmt.Errorf("failed to get site deploy id: %w", err) 38 + } 39 + 40 + deploy.Id = id 41 + return nil 42 + } 43 + 44 + // GetSiteDeploys returns recent deploy records for a repository, newest first. 45 + func GetSiteDeploys(e Execer, repoAt string, limit int) ([]models.SiteDeploy, error) { 46 + if limit <= 0 { 47 + limit = 20 48 + } 49 + 50 + rows, err := e.Query(` 51 + select 52 + id, 53 + repo_at, 54 + branch, 55 + dir, 56 + commit_sha, 57 + status, 58 + trigger, 59 + error, 60 + created_at 61 + from site_deploys 62 + where repo_at = ? 63 + order by created_at desc 64 + limit ? 65 + `, repoAt, limit) 66 + if err != nil { 67 + return nil, fmt.Errorf("failed to query site deploys: %w", err) 68 + } 69 + defer rows.Close() 70 + 71 + var deploys []models.SiteDeploy 72 + for rows.Next() { 73 + var d models.SiteDeploy 74 + var createdAt string 75 + 76 + if err := rows.Scan( 77 + &d.Id, 78 + &d.RepoAt, 79 + &d.Branch, 80 + &d.Dir, 81 + &d.CommitSHA, 82 + &d.Status, 83 + &d.Trigger, 84 + &d.Error, 85 + &createdAt, 86 + ); err != nil { 87 + return nil, fmt.Errorf("failed to scan site deploy: %w", err) 88 + } 89 + 90 + if t, err := time.Parse(time.RFC3339, createdAt); err == nil { 91 + d.CreatedAt = t 92 + } 93 + 94 + deploys = append(deploys, d) 95 + } 96 + 97 + if err := rows.Err(); err != nil { 98 + return nil, fmt.Errorf("failed to iterate site deploys: %w", err) 99 + } 100 + 101 + return deploys, nil 102 + }
+72
appview/db/sites.go
··· 196 196 _, err := e.Exec(`delete from repo_sites where repo_at = ?`, repoAt) 197 197 return err 198 198 } 199 + 200 + // GetRepoSiteConfigsForDid returns all site configurations for repos owned by a DID. 201 + // RepoName is populated on each returned RepoSite. 202 + func GetRepoSiteConfigsForDid(e Execer, did string) ([]*models.RepoSite, error) { 203 + rows, err := e.Query(` 204 + select rs.id, rs.repo_at, r.name, rs.branch, rs.dir, rs.is_index, rs.created, rs.updated 205 + from repo_sites rs 206 + join repos r on r.at_uri = rs.repo_at 207 + where r.did = ? 208 + `, did) 209 + if err != nil { 210 + return nil, err 211 + } 212 + defer rows.Close() 213 + 214 + var sites []*models.RepoSite 215 + for rows.Next() { 216 + var s models.RepoSite 217 + var isIndex int 218 + var createdStr, updatedStr string 219 + if err := rows.Scan(&s.ID, &s.RepoAt, &s.RepoName, &s.Branch, &s.Dir, &isIndex, &createdStr, &updatedStr); err != nil { 220 + return nil, err 221 + } 222 + s.IsIndex = isIndex != 0 223 + s.Created, err = time.Parse(time.RFC3339, createdStr) 224 + if err != nil { 225 + return nil, fmt.Errorf("parsing created timestamp: %w", err) 226 + } 227 + s.Updated, err = time.Parse(time.RFC3339, updatedStr) 228 + if err != nil { 229 + return nil, fmt.Errorf("parsing updated timestamp: %w", err) 230 + } 231 + sites = append(sites, &s) 232 + } 233 + return sites, rows.Err() 234 + } 235 + 236 + // DeleteRepoSiteConfigsForDid removes all site configurations for repos owned by a DID. 237 + func DeleteRepoSiteConfigsForDid(e Execer, did string) error { 238 + _, err := e.Exec(` 239 + delete from repo_sites 240 + where repo_at in ( 241 + select at_uri from repos where did = ? 242 + ) 243 + `, did) 244 + return err 245 + } 246 + 247 + // GetIndexRepoAtForDid returns the repo_at of the repo that currently holds 248 + // is_index=1 for the given DID, excluding excludeRepoAt (the current repo). 249 + // Returns "", nil if no other repo is the index site. 250 + func GetIndexRepoAtForDid(e Execer, did, excludeRepoAt string) (string, error) { 251 + row := e.QueryRow(` 252 + select rs.repo_at 253 + from repo_sites rs 254 + join repos r on r.at_uri = rs.repo_at 255 + where r.did = ? 256 + and rs.is_index = 1 257 + and rs.repo_at != ? 258 + limit 1 259 + `, did, excludeRepoAt) 260 + 261 + var repoAt string 262 + err := row.Scan(&repoAt) 263 + if errors.Is(err, sql.ErrNoRows) { 264 + return "", nil 265 + } 266 + if err != nil { 267 + return "", err 268 + } 269 + return repoAt, nil 270 + }
+40
appview/models/site_deploy.go
··· 1 + package models 2 + 3 + import "time" 4 + 5 + type SiteDeployStatus string 6 + 7 + const ( 8 + SiteDeployStatusSuccess SiteDeployStatus = "success" 9 + SiteDeployStatusFailure SiteDeployStatus = "failure" 10 + ) 11 + 12 + type SiteDeployTrigger string 13 + 14 + const ( 15 + SiteDeployTriggerConfigChange SiteDeployTrigger = "config_change" 16 + SiteDeployTriggerPush SiteDeployTrigger = "push" 17 + ) 18 + 19 + func (t SiteDeployTrigger) Label() string { 20 + switch t { 21 + case SiteDeployTriggerConfigChange: 22 + return "config change" 23 + case SiteDeployTriggerPush: 24 + return "push" 25 + default: 26 + return string(t) 27 + } 28 + } 29 + 30 + type SiteDeploy struct { 31 + Id int64 32 + RepoAt string 33 + Branch string 34 + Dir string 35 + CommitSHA string 36 + Status SiteDeployStatus 37 + Trigger SiteDeployTrigger 38 + Error string 39 + CreatedAt time.Time 40 + }
+8 -7
appview/models/sites.go
··· 10 10 } 11 11 12 12 type RepoSite struct { 13 - ID int64 14 - RepoAt string 15 - Branch string 16 - Dir string 17 - IsIndex bool 18 - Created time.Time 19 - Updated time.Time 13 + ID int64 14 + RepoAt string 15 + RepoName string // populated when joined with repos table 16 + Branch string 17 + Dir string 18 + IsIndex bool 19 + Created time.Time 20 + Updated time.Time 20 21 }