Monorepo for Tangled tangled.org
6

Configure Feed

Select the types of activity you want to include in your feed.

appview/{repo,sites}: name-keyed sites kv mapping

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

author
Anirudh Oppiliappan
committer
Tangled
date (May 16, 2026, 11:51 AM +0300) commit ccb87cd0 parent 879a5a66 change-id tqpszwmw
+45 -20
+12 -7
appview/repo/repo.go
··· 970 970 rp.notifier.RenameRepo(r.Context(), syntax.DID(user.Did), &oldRepo, &newRepo) 971 971 972 972 if newRkey != f.Rkey { 973 - rp.migrateSiteOnRename(r.Context(), f, newRkey) 973 + rp.migrateSiteOnRename(r.Context(), f, newName, newRkey) 974 974 } 975 975 976 976 rp.pages.HxLocation(w, fmt.Sprintf("/%s", f.RepoDid)) ··· 994 994 return newName, nil 995 995 } 996 996 997 - func (rp *Repo) migrateSiteOnRename(ctx context.Context, oldRepo *models.Repo, newRkey string) { 997 + func (rp *Repo) migrateSiteOnRename(ctx context.Context, oldRepo *models.Repo, newName, newRkey string) { 998 998 l := rp.logger.With("handler", "migrateSiteOnRename", "repo_did", oldRepo.RepoDid) 999 999 1000 1000 siteConfig, err := db.GetRepoSiteConfig(rp.db, oldRepo.RepoDid) ··· 1011 1011 go func() { 1012 1012 bgCtx := context.Background() 1013 1013 oldRkey := oldRepo.Rkey 1014 + oldName := oldRepo.Name 1014 1015 1015 1016 if err := sites.Delete(bgCtx, rp.cfClient, oldRepo.Did, oldRkey); err != nil { 1016 1017 l.Error("sites: failed to delete old R2 prefix", "oldRkey", oldRkey, "err", err) 1017 1018 } 1018 1019 1019 1020 newRepo := *oldRepo 1021 + newRepo.Name = newName 1020 1022 newRepo.Rkey = newRkey 1021 1023 if deployErr := sites.Deploy(bgCtx, rp.cfClient, rp.config, &newRepo, siteConfig.Branch, siteConfig.Dir); deployErr != nil { 1022 1024 l.Error("sites: redeploy after rename failed", "err", deployErr) 1023 1025 } 1024 1026 1025 1027 if ownerClaim != nil { 1026 - if err := sites.DeleteDomainMapping(bgCtx, rp.cfClient, ownerClaim.Domain, oldRkey); err != nil { 1027 - l.Error("sites: failed to remove old KV mapping", "oldRkey", oldRkey, "err", err) 1028 + // drop the old name's entry when the name actually changed. 1029 + if oldName != newName { 1030 + if err := sites.DeleteDomainMapping(bgCtx, rp.cfClient, ownerClaim.Domain, oldName); err != nil { 1031 + l.Error("sites: failed to remove old KV mapping", "oldName", oldName, "err", err) 1032 + } 1028 1033 } 1029 - if err := sites.PutDomainMapping(bgCtx, rp.cfClient, ownerClaim.Domain, oldRepo.Did, newRkey, siteConfig.IsIndex); err != nil { 1030 - l.Error("sites: failed to write new KV mapping", "newRkey", newRkey, "err", err) 1034 + if err := sites.PutDomainMapping(bgCtx, rp.cfClient, ownerClaim.Domain, oldRepo.Did, newName, newRkey, siteConfig.IsIndex); err != nil { 1035 + l.Error("sites: failed to write new KV mapping", "newName", newName, "newRkey", newRkey, "err", err) 1031 1036 } 1032 1037 } 1033 1038 1034 - l.Info("sites: migrated on rename", "oldRkey", oldRkey, "newRkey", newRkey) 1039 + l.Info("sites: migrated on rename", "oldName", oldName, "oldRkey", oldRkey, "newName", newName, "newRkey", newRkey) 1035 1040 }() 1036 1041 } 1037 1042
+3 -4
appview/repo/settings.go
··· 196 196 host := fmt.Sprintf("%s://%s", scheme, f.Knot) 197 197 xrpcc := &indigoxrpc.Client{Host: host} 198 198 199 - repo := fmt.Sprintf("%s/%s", f.Did, f.Rkey) 200 - xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, repo) 199 + xrpcBytes, err := tangled.RepoBranches(r.Context(), xrpcc, "", 0, f.RepoIdentifier()) 201 200 if xrpcerr := xrpcclient.HandleXrpcErr(err); xrpcerr != nil { 202 201 l.Error("failed to call XRPC repo.branches", "xrpcerr", xrpcerr, "err", err) 203 202 rp.pages.Error503(w) ··· 318 317 } 319 318 320 319 if deployErr == nil { 321 - if err := sites.PutDomainMapping(ctx, rp.cfClient, ownerClaim.Domain, f.Did, f.Rkey, isIndex); err != nil { 320 + if err := sites.PutDomainMapping(ctx, rp.cfClient, ownerClaim.Domain, f.Did, f.Name, f.Rkey, isIndex); err != nil { 322 321 l.Error("sites: KV write failed", "domain", ownerClaim.Domain, "err", err) 323 322 } 324 323 rp.logger.Info("site deployed to r2", "repo", f.RepoIdentifier(), "is_index", isIndex) ··· 363 362 l.Error("sites: R2 delete failed", "repo", f.RepoIdentifier(), "err", err) 364 363 } 365 364 if ownerClaim != nil { 366 - if err := sites.DeleteDomainMapping(ctx, rp.cfClient, ownerClaim.Domain, f.Rkey); err != nil { 365 + if err := sites.DeleteDomainMapping(ctx, rp.cfClient, ownerClaim.Domain, f.Name); err != nil { 367 366 l.Error("sites: KV delete failed", "domain", ownerClaim.Domain, "err", err) 368 367 } 369 368 }
+30 -9
appview/sites/sites.go
··· 21 21 ) 22 22 23 23 // DomainMapping is the value stored in Workers KV, keyed by the bare domain. 24 - // Repos maps repo name → is_index; at most one repo may have is_index = true. 24 + // Repos maps repo name → RepoEntry; at most one repo may have IsIndex = true. 25 25 type DomainMapping struct { 26 - Did string `json:"did"` 27 - Repos map[string]bool `json:"repos"` 26 + Did string `json:"did"` 27 + Repos map[string]RepoEntry `json:"repos"` 28 + } 29 + 30 + // RepoEntry is the per-repo value within a DomainMapping. Rkey is the 31 + // repository's atproto record key, which identifies the {did}/{rkey}/ 32 + // prefix in R2 where the site's objects live. 33 + type RepoEntry struct { 34 + Rkey string `json:"rkey"` 35 + IsIndex bool `json:"is_index"` 28 36 } 29 37 30 38 // getOrNewMapping fetches the existing KV entry for domain, or returns a ··· 35 43 return DomainMapping{}, fmt.Errorf("reading domain mapping for %q: %w", domain, err) 36 44 } 37 45 if raw == nil { 38 - return DomainMapping{Did: did, Repos: make(map[string]bool)}, nil 46 + return DomainMapping{Did: did, Repos: make(map[string]RepoEntry)}, nil 39 47 } 40 48 var m DomainMapping 41 49 if err := json.Unmarshal(raw, &m); err != nil { 42 50 return DomainMapping{}, fmt.Errorf("unmarshalling domain mapping for %q: %w", domain, err) 43 51 } 44 52 if m.Repos == nil { 45 - m.Repos = make(map[string]bool) 53 + m.Repos = make(map[string]RepoEntry) 46 54 } 47 55 return m, nil 48 56 } 49 57 50 58 // PutDomainMapping adds or updates a single repo entry within the per-domain 51 59 // KV record. If isIndex is true, any previously indexed repo is demoted first. 52 - func PutDomainMapping(ctx context.Context, cf *cloudflare.Client, domain, did, repo string, isIndex bool) error { 60 + func PutDomainMapping(ctx context.Context, cf *cloudflare.Client, domain, did, repoName, repoRkey string, isIndex bool) error { 53 61 m, err := getOrNewMapping(ctx, cf, domain, did) 54 62 if err != nil { 55 63 return err 56 64 } 57 65 58 66 m.Did = did 59 - m.Repos[repo] = isIndex 67 + 68 + if isIndex { 69 + for name, entry := range m.Repos { 70 + if name == repoName { 71 + continue 72 + } 73 + if entry.IsIndex { 74 + entry.IsIndex = false 75 + m.Repos[name] = entry 76 + } 77 + } 78 + } 79 + 80 + m.Repos[repoName] = RepoEntry{Rkey: repoRkey, IsIndex: isIndex} 60 81 61 82 val, err := json.Marshal(m) 62 83 if err != nil { ··· 70 91 71 92 // DeleteDomainMapping removes a single repo from the per-domain KV record. 72 93 // If it was the last repo, the key is deleted entirely. 73 - func DeleteDomainMapping(ctx context.Context, cf *cloudflare.Client, domain, repo string) error { 94 + func DeleteDomainMapping(ctx context.Context, cf *cloudflare.Client, domain, repoName string) error { 74 95 m, err := getOrNewMapping(ctx, cf, domain, "") 75 96 if err != nil { 76 97 return err 77 98 } 78 99 79 - delete(m.Repos, repo) 100 + delete(m.Repos, repoName) 80 101 81 102 if len(m.Repos) == 0 { 82 103 if err := cf.KVDelete(ctx, domain); err != nil {