···616616 deleted text -- timestamp when the domain was released/unclaimed; null means actively claimed617617 );618618619619+ create table if not exists repo_sites (620620+ id integer primary key autoincrement,621621+ repo_at text not null unique,622622+ branch text not null,623623+ dir text not null default '/',624624+ is_index integer not null default 0,625625+ created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),626626+ updated text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),627627+ foreign key (repo_at) references repos(at_uri) on delete cascade628628+ );629629+630630+ create table if not exists site_deploys (631631+ id integer primary key autoincrement,632632+ repo_at text not null,633633+ branch text not null,634634+ dir text not null default '/',635635+ commit_sha text not null default '',636636+ status text not null check (status in ('success', 'failure')),637637+ trigger text not null check (trigger in ('config_change', 'push')),638638+ error text not null default '',639639+ created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),640640+ foreign key (repo_at) references repos(at_uri) on delete cascade641641+ );642642+619643 create table if not exists migrations (620644 id integer primary key autoincrement,621645 name text unique···659635 create index if not exists idx_references_to_at on reference_links(to_at);660636 create index if not exists idx_webhooks_repo_at on webhooks(repo_at);661637 create index if not exists idx_webhook_deliveries_webhook_id on webhook_deliveries(webhook_id);638638+ create index if not exists idx_site_deploys_repo_at on site_deploys(repo_at);662639 `)663640 if err != nil {664641 return nil, err···1283125812841259 -- rename new table12851260 alter table profile_stats_new rename to profile_stats;12861286- `)12871287- return err12881288- })12891289-12901290- orm.RunMigration(conn, logger, "add-repo-sites-table", func(tx *sql.Tx) error {12911291- _, err := tx.Exec(`12921292- create table if not exists repo_sites (12931293- id integer primary key autoincrement,12941294- repo_at text not null unique,12951295- branch text not null,12961296- dir text not null default '/',12971297- is_index integer not null default 0,12981298- created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),12991299- updated text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),13001300- foreign key (repo_at) references repos(at_uri) on delete cascade13011301- );13021261 `)13031262 return err13041263 })
+102
appview/db/site_deploys.go
···11+package db22+33+import (44+ "fmt"55+ "time"66+77+ "tangled.org/core/appview/models"88+)99+1010+// AddSiteDeploy records a site deploy attempt.1111+func AddSiteDeploy(e Execer, deploy *models.SiteDeploy) error {1212+ result, err := e.Exec(`1313+ insert into site_deploys (1414+ repo_at,1515+ branch,1616+ dir,1717+ commit_sha,1818+ status,1919+ trigger,2020+ error2121+ ) values (?, ?, ?, ?, ?, ?, ?)2222+ `,2323+ deploy.RepoAt,2424+ deploy.Branch,2525+ deploy.Dir,2626+ deploy.CommitSHA,2727+ string(deploy.Status),2828+ string(deploy.Trigger),2929+ deploy.Error,3030+ )3131+ if err != nil {3232+ return fmt.Errorf("failed to insert site deploy: %w", err)3333+ }3434+3535+ id, err := result.LastInsertId()3636+ if err != nil {3737+ return fmt.Errorf("failed to get site deploy id: %w", err)3838+ }3939+4040+ deploy.Id = id4141+ return nil4242+}4343+4444+// GetSiteDeploys returns recent deploy records for a repository, newest first.4545+func GetSiteDeploys(e Execer, repoAt string, limit int) ([]models.SiteDeploy, error) {4646+ if limit <= 0 {4747+ limit = 204848+ }4949+5050+ rows, err := e.Query(`5151+ select5252+ id,5353+ repo_at,5454+ branch,5555+ dir,5656+ commit_sha,5757+ status,5858+ trigger,5959+ error,6060+ created_at6161+ from site_deploys6262+ where repo_at = ?6363+ order by created_at desc6464+ limit ?6565+ `, repoAt, limit)6666+ if err != nil {6767+ return nil, fmt.Errorf("failed to query site deploys: %w", err)6868+ }6969+ defer rows.Close()7070+7171+ var deploys []models.SiteDeploy7272+ for rows.Next() {7373+ var d models.SiteDeploy7474+ var createdAt string7575+7676+ if err := rows.Scan(7777+ &d.Id,7878+ &d.RepoAt,7979+ &d.Branch,8080+ &d.Dir,8181+ &d.CommitSHA,8282+ &d.Status,8383+ &d.Trigger,8484+ &d.Error,8585+ &createdAt,8686+ ); err != nil {8787+ return nil, fmt.Errorf("failed to scan site deploy: %w", err)8888+ }8989+9090+ if t, err := time.Parse(time.RFC3339, createdAt); err == nil {9191+ d.CreatedAt = t9292+ }9393+9494+ deploys = append(deploys, d)9595+ }9696+9797+ if err := rows.Err(); err != nil {9898+ return nil, fmt.Errorf("failed to iterate site deploys: %w", err)9999+ }100100+101101+ return deploys, nil102102+}
+72
appview/db/sites.go
···196196 _, err := e.Exec(`delete from repo_sites where repo_at = ?`, repoAt)197197 return err198198}199199+200200+// GetRepoSiteConfigsForDid returns all site configurations for repos owned by a DID.201201+// RepoName is populated on each returned RepoSite.202202+func GetRepoSiteConfigsForDid(e Execer, did string) ([]*models.RepoSite, error) {203203+ rows, err := e.Query(`204204+ select rs.id, rs.repo_at, r.name, rs.branch, rs.dir, rs.is_index, rs.created, rs.updated205205+ from repo_sites rs206206+ join repos r on r.at_uri = rs.repo_at207207+ where r.did = ?208208+ `, did)209209+ if err != nil {210210+ return nil, err211211+ }212212+ defer rows.Close()213213+214214+ var sites []*models.RepoSite215215+ for rows.Next() {216216+ var s models.RepoSite217217+ var isIndex int218218+ var createdStr, updatedStr string219219+ if err := rows.Scan(&s.ID, &s.RepoAt, &s.RepoName, &s.Branch, &s.Dir, &isIndex, &createdStr, &updatedStr); err != nil {220220+ return nil, err221221+ }222222+ s.IsIndex = isIndex != 0223223+ s.Created, err = time.Parse(time.RFC3339, createdStr)224224+ if err != nil {225225+ return nil, fmt.Errorf("parsing created timestamp: %w", err)226226+ }227227+ s.Updated, err = time.Parse(time.RFC3339, updatedStr)228228+ if err != nil {229229+ return nil, fmt.Errorf("parsing updated timestamp: %w", err)230230+ }231231+ sites = append(sites, &s)232232+ }233233+ return sites, rows.Err()234234+}235235+236236+// DeleteRepoSiteConfigsForDid removes all site configurations for repos owned by a DID.237237+func DeleteRepoSiteConfigsForDid(e Execer, did string) error {238238+ _, err := e.Exec(`239239+ delete from repo_sites240240+ where repo_at in (241241+ select at_uri from repos where did = ?242242+ )243243+ `, did)244244+ return err245245+}246246+247247+// GetIndexRepoAtForDid returns the repo_at of the repo that currently holds248248+// is_index=1 for the given DID, excluding excludeRepoAt (the current repo).249249+// Returns "", nil if no other repo is the index site.250250+func GetIndexRepoAtForDid(e Execer, did, excludeRepoAt string) (string, error) {251251+ row := e.QueryRow(`252252+ select rs.repo_at253253+ from repo_sites rs254254+ join repos r on r.at_uri = rs.repo_at255255+ where r.did = ?256256+ and rs.is_index = 1257257+ and rs.repo_at != ?258258+ limit 1259259+ `, did, excludeRepoAt)260260+261261+ var repoAt string262262+ err := row.Scan(&repoAt)263263+ if errors.Is(err, sql.ErrNoRows) {264264+ return "", nil265265+ }266266+ if err != nil {267267+ return "", err268268+ }269269+ return repoAt, nil270270+}
···1010}11111212type RepoSite struct {1313- ID int641414- RepoAt string1515- Branch string1616- Dir string1717- IsIndex bool1818- Created time.Time1919- Updated time.Time1313+ ID int641414+ RepoAt string1515+ RepoName string // populated when joined with repos table1616+ Branch string1717+ Dir string1818+ IsIndex bool1919+ Created time.Time2020+ Updated time.Time2021}