forked from tangled.org/core
Monorepo for Tangled

appview/{pulls,repo,state}: use xrpc + service auth for knot requests

This now covers: Fork, ForkSync, NewHiddenRef, Merge, MergeCheck,
and RepoCreate/Delete.

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

anirudh.fi 36a2f845 2c2b5320

verified
Changed files
+295 -176
appview
pulls
repo
state
+135 -87
appview/pulls/pulls.go
··· 2 2 3 3 import ( 4 4 "database/sql" 5 - "encoding/json" 6 5 "errors" 7 6 "fmt" 8 - "io" 9 7 "log" 10 8 "net/http" 11 9 "sort" ··· 25 23 "tangled.sh/tangled.sh/core/patchutil" 26 24 "tangled.sh/tangled.sh/core/tid" 27 25 "tangled.sh/tangled.sh/core/types" 26 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 28 27 29 28 "github.com/bluekeyes/go-gitdiff/gitdiff" 30 29 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 96 95 return 97 96 } 98 97 99 - mergeCheckResponse := s.mergeCheck(f, pull, stack) 98 + mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 100 99 resubmitResult := pages.Unknown 101 100 if user.Did == pull.OwnerDid { 102 101 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 161 160 } 162 161 } 163 162 164 - mergeCheckResponse := s.mergeCheck(f, pull, stack) 163 + mergeCheckResponse := s.mergeCheck(r, f, pull, stack) 165 164 resubmitResult := pages.Unknown 166 165 if user != nil && user.Did == pull.OwnerDid { 167 166 resubmitResult = s.resubmitCheck(f, pull, stack) ··· 226 225 }) 227 226 } 228 227 229 - func (s *Pulls) mergeCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) types.MergeCheckResponse { 228 + func (s *Pulls) mergeCheck(r *http.Request, f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) types.MergeCheckResponse { 230 229 if pull.State == db.PullMerged { 231 230 return types.MergeCheckResponse{} 232 231 } 233 232 234 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 233 + client, err := s.oauth.ServiceClient( 234 + r, 235 + oauth.WithService(f.Knot), 236 + oauth.WithLxm(tangled.RepoMergeCheckNSID), 237 + oauth.WithDev(s.config.Core.Dev), 238 + ) 235 239 if err != nil { 236 - log.Printf("failed to get registration key: %v", err) 240 + log.Printf("failed to connect to knot server: %v", err) 237 241 return types.MergeCheckResponse{ 238 - Error: "failed to check merge status: this knot is unregistered", 239 - } 240 - } 241 - 242 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 243 - if err != nil { 244 - log.Printf("failed to setup signed client for %s; ignoring: %v", f.Knot, err) 245 - return types.MergeCheckResponse{ 246 - Error: "failed to check merge status", 242 + Error: "failed to check merge status: could not connect to knot server", 247 243 } 248 244 } 249 245 ··· 257 253 patch = mergeable.CombinedPatch() 258 254 } 259 255 260 - resp, err := ksClient.MergeCheck([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch) 256 + resp, err := tangled.RepoMergeCheck( 257 + r.Context(), 258 + client, 259 + &tangled.RepoMergeCheck_Input{ 260 + Did: f.OwnerDid(), 261 + Name: f.RepoName, 262 + Branch: pull.TargetBranch, 263 + Patch: patch, 264 + }, 265 + ) 261 266 if err != nil { 262 - log.Println("failed to check for mergeability:", err) 263 - return types.MergeCheckResponse{ 264 - Error: "failed to check merge status", 265 - } 266 - } 267 - switch resp.StatusCode { 268 - case 404: 269 - return types.MergeCheckResponse{ 270 - Error: "failed to check merge status: this knot does not support PRs", 267 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 268 + if parseErr != nil { 269 + log.Printf("failed to check for mergeability: %v", err) 270 + return types.MergeCheckResponse{ 271 + Error: "failed to check merge status", 272 + } 271 273 } 272 - case 400: 274 + log.Printf("failed to check for mergeability: %s", xe.Error()) 273 275 return types.MergeCheckResponse{ 274 - Error: "failed to check merge status: does this knot support PRs?", 276 + Error: fmt.Sprintf("failed to check merge status: %s", xe.Message), 275 277 } 276 278 } 277 279 278 - respBody, err := io.ReadAll(resp.Body) 279 - if err != nil { 280 - log.Println("failed to read merge check response body") 281 - return types.MergeCheckResponse{ 282 - Error: "failed to check merge status: knot is not speaking the right language", 280 + // convert xrpc response to internal types 281 + conflicts := make([]types.ConflictInfo, len(resp.Conflicts)) 282 + for i, conflict := range resp.Conflicts { 283 + conflicts[i] = types.ConflictInfo{ 284 + Filename: conflict.Filename, 285 + Reason: conflict.Reason, 283 286 } 284 287 } 285 - defer resp.Body.Close() 286 288 287 - var mergeCheckResponse types.MergeCheckResponse 288 - err = json.Unmarshal(respBody, &mergeCheckResponse) 289 - if err != nil { 290 - log.Println("failed to unmarshal merge check response", err) 291 - return types.MergeCheckResponse{ 292 - Error: "failed to check merge status: knot is not speaking the right language", 293 - } 289 + result := types.MergeCheckResponse{ 290 + IsConflicted: resp.Is_conflicted, 291 + Conflicts: conflicts, 294 292 } 295 293 296 - return mergeCheckResponse 294 + if resp.Message != nil { 295 + result.Message = *resp.Message 296 + } 297 + 298 + if resp.Error != nil { 299 + result.Error = *resp.Error 300 + } 301 + 302 + return result 297 303 } 298 304 299 305 func (s *Pulls) resubmitCheck(f *reporesolver.ResolvedRepo, pull *db.Pull, stack db.Stack) pages.ResubmitResult { ··· 923 929 return 924 930 } 925 931 926 - secret, err := db.GetRegistrationKey(s.db, fork.Knot) 932 + client, err := s.oauth.ServiceClient( 933 + r, 934 + oauth.WithService(fork.Knot), 935 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 936 + oauth.WithDev(s.config.Core.Dev), 937 + ) 927 938 if err != nil { 928 - log.Println("failed to fetch registration key:", err) 929 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 930 - return 931 - } 932 - 933 - sc, err := knotclient.NewSignedClient(fork.Knot, secret, s.config.Core.Dev) 934 - if err != nil { 935 - log.Println("failed to create signed client:", err) 939 + log.Printf("failed to connect to knot server: %v", err) 936 940 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 937 941 return 938 942 } ··· 944 948 return 945 949 } 946 950 947 - resp, err := sc.NewHiddenRef(user.Did, fork.Name, sourceBranch, targetBranch) 951 + resp, err := tangled.RepoHiddenRef( 952 + r.Context(), 953 + client, 954 + &tangled.RepoHiddenRef_Input{ 955 + ForkRef: sourceBranch, 956 + RemoteRef: targetBranch, 957 + Repo: fork.AtUri, 958 + }, 959 + ) 948 960 if err != nil { 949 - log.Println("failed to create hidden ref:", err, resp.StatusCode) 950 - s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 961 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 962 + if parseErr != nil { 963 + log.Printf("failed to create hidden ref: %v", err) 964 + s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 965 + } else { 966 + log.Printf("failed to create hidden ref: %s", xe.Error()) 967 + if xe.Tag == "AccessControl" { 968 + s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.") 969 + } else { 970 + s.pages.Notice(w, "pull", fmt.Sprintf("Failed to create pull request: %s", xe.Message)) 971 + } 972 + } 951 973 return 952 974 } 953 975 954 - switch resp.StatusCode { 955 - case 404: 956 - case 400: 957 - s.pages.Notice(w, "pull", "Branch based pull requests are not supported on this knot.") 976 + if !resp.Success { 977 + errorMsg := "Failed to create pull request" 978 + if resp.Error != nil { 979 + errorMsg = fmt.Sprintf("Failed to create pull request: %s", *resp.Error) 980 + } 981 + s.pages.Notice(w, "pull", errorMsg) 958 982 return 959 983 } 960 984 ··· 1524 1548 return 1525 1549 } 1526 1550 1527 - secret, err := db.GetRegistrationKey(s.db, forkRepo.Knot) 1528 - if err != nil { 1529 - log.Printf("failed to get registration key for %s: %s", forkRepo.Knot, err) 1530 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1531 - return 1532 - } 1533 - 1534 1551 // update the hidden tracking branch to latest 1535 - signedClient, err := knotclient.NewSignedClient(forkRepo.Knot, secret, s.config.Core.Dev) 1552 + client, err := s.oauth.ServiceClient( 1553 + r, 1554 + oauth.WithService(forkRepo.Knot), 1555 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 1556 + oauth.WithDev(s.config.Core.Dev), 1557 + ) 1536 1558 if err != nil { 1537 - log.Printf("failed to create signed client for %s: %s", forkRepo.Knot, err) 1538 - s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1559 + log.Printf("failed to connect to knot server: %v", err) 1539 1560 return 1540 1561 } 1541 1562 1542 - resp, err := signedClient.NewHiddenRef(forkRepo.Did, forkRepo.Name, pull.PullSource.Branch, pull.TargetBranch) 1543 - if err != nil || resp.StatusCode != http.StatusNoContent { 1544 - log.Printf("failed to update tracking branch: %s", err) 1563 + resp, err := tangled.RepoHiddenRef( 1564 + r.Context(), 1565 + client, 1566 + &tangled.RepoHiddenRef_Input{ 1567 + ForkRef: pull.PullSource.Branch, 1568 + RemoteRef: pull.TargetBranch, 1569 + Repo: forkRepo.AtUri, 1570 + }, 1571 + ) 1572 + if err != nil || !resp.Success { 1573 + if err != nil { 1574 + log.Printf("failed to update tracking branch: %s", err) 1575 + } else { 1576 + log.Printf("failed to update tracking branch: success=false") 1577 + } 1545 1578 s.pages.Notice(w, "resubmit-error", "Failed to create pull request. Try again later.") 1546 1579 return 1547 1580 } ··· 1958 1991 1959 1992 patch := pullsToMerge.CombinedPatch() 1960 1993 1961 - secret, err := db.GetRegistrationKey(s.db, f.Knot) 1994 + client, err := s.oauth.ServiceClient( 1995 + r, 1996 + oauth.WithService(f.Knot), 1997 + oauth.WithLxm(tangled.RepoMergeNSID), 1998 + oauth.WithDev(s.config.Core.Dev), 1999 + ) 1962 2000 if err != nil { 1963 - log.Printf("no registration key found for domain %s: %s\n", f.Knot, err) 2001 + log.Printf("failed to connect to knot server: %v", err) 1964 2002 s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1965 2003 return 1966 2004 } ··· 1977 2015 log.Printf("failed to get primary email: %s", err) 1978 2016 } 1979 2017 1980 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, s.config.Core.Dev) 1981 - if err != nil { 1982 - log.Printf("failed to create signed client for %s: %s", f.Knot, err) 1983 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1984 - return 2018 + authorName := ident.Handle.String() 2019 + mergeInput := &tangled.RepoMerge_Input{ 2020 + Did: f.OwnerDid(), 2021 + Name: f.RepoName, 2022 + Branch: pull.TargetBranch, 2023 + Patch: patch, 2024 + CommitMessage: &pull.Title, 2025 + AuthorName: &authorName, 2026 + } 2027 + 2028 + if pull.Body != "" { 2029 + mergeInput.CommitBody = &pull.Body 1985 2030 } 1986 2031 1987 - // Merge the pull request 1988 - resp, err := ksClient.Merge([]byte(patch), f.OwnerDid(), f.RepoName, pull.TargetBranch, pull.Title, pull.Body, ident.Handle.String(), email.Address) 1989 - if err != nil { 1990 - log.Printf("failed to merge pull request: %s", err) 1991 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 1992 - return 2032 + if email.Address != "" { 2033 + mergeInput.AuthorEmail = &email.Address 1993 2034 } 1994 2035 1995 - if resp.StatusCode != http.StatusOK { 1996 - log.Printf("knotserver returned non-OK status code for merge: %d", resp.StatusCode) 1997 - s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 2036 + err = tangled.RepoMerge(r.Context(), client, mergeInput) 2037 + if err != nil { 2038 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 2039 + if parseErr != nil { 2040 + log.Printf("failed to merge pull request: %v", err) 2041 + s.pages.Notice(w, "pull-merge-error", "Failed to merge pull request. Try again later.") 2042 + } else { 2043 + log.Printf("failed to merge pull request: %s", xe.Error()) 2044 + s.pages.Notice(w, "pull-merge-error", fmt.Sprintf("Failed to merge pull request: %s", xe.Message)) 2045 + } 1998 2046 return 1999 2047 } 2000 2048
+29 -4
appview/repo/index.go
··· 9 9 "sort" 10 10 "strings" 11 11 12 + "tangled.sh/tangled.sh/core/api/tangled" 12 13 "tangled.sh/tangled.sh/core/appview/commitverify" 13 14 "tangled.sh/tangled.sh/core/appview/db" 14 15 "tangled.sh/tangled.sh/core/appview/oauth" ··· 118 119 119 120 var forkInfo *types.ForkInfo 120 121 if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) { 121 - forkInfo, err = getForkInfo(repoInfo, rp, f, user, signedClient) 122 + forkInfo, err = getForkInfo(r, repoInfo, rp, f, user, signedClient) 122 123 if err != nil { 123 124 log.Printf("Failed to fetch fork information: %v", err) 124 125 return ··· 231 232 } 232 233 233 234 func getForkInfo( 235 + r *http.Request, 234 236 repoInfo repoinfo.RepoInfo, 235 237 rp *Repo, 236 238 f *reporesolver.ResolvedRepo, ··· 270 272 return &forkInfo, nil 271 273 } 272 274 273 - newHiddenRefResp, err := signedClient.NewHiddenRef(user.Did, repoInfo.Name, f.Ref, f.Ref) 274 - if err != nil || newHiddenRefResp.StatusCode != http.StatusNoContent { 275 - log.Printf("failed to update tracking branch: %s", err) 275 + client, err := rp.oauth.ServiceClient( 276 + r, 277 + oauth.WithService(f.Knot), 278 + oauth.WithLxm(tangled.RepoHiddenRefNSID), 279 + oauth.WithDev(rp.config.Core.Dev), 280 + ) 281 + if err != nil { 282 + log.Printf("failed to connect to knot server: %v", err) 276 283 return nil, err 284 + } 285 + 286 + resp, err := tangled.RepoHiddenRef( 287 + r.Context(), 288 + client, 289 + &tangled.RepoHiddenRef_Input{ 290 + ForkRef: f.Ref, 291 + RemoteRef: f.Ref, 292 + Repo: string(f.RepoAt), 293 + }, 294 + ) 295 + if err != nil || !resp.Success { 296 + if err != nil { 297 + log.Printf("failed to update tracking branch: %s", err) 298 + } else { 299 + log.Printf("failed to update tracking branch: success=false") 300 + } 301 + return nil, fmt.Errorf("failed to update tracking branch") 277 302 } 278 303 279 304 hiddenRef := fmt.Sprintf("hidden/%s/%s", f.Ref, f.Ref)
+105 -66
appview/repo/repo.go
··· 17 17 "strings" 18 18 "time" 19 19 20 + comatproto "github.com/bluesky-social/indigo/api/atproto" 21 + lexutil "github.com/bluesky-social/indigo/lex/util" 20 22 "tangled.sh/tangled.sh/core/api/tangled" 21 23 "tangled.sh/tangled.sh/core/appview/commitverify" 22 24 "tangled.sh/tangled.sh/core/appview/config" ··· 33 35 "tangled.sh/tangled.sh/core/rbac" 34 36 "tangled.sh/tangled.sh/core/tid" 35 37 "tangled.sh/tangled.sh/core/types" 38 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 39 + "tangled.sh/tangled.sh/core/xrpc/serviceauth" 36 40 37 41 securejoin "github.com/cyphar/filepath-securejoin" 38 42 "github.com/go-chi/chi/v5" 39 43 "github.com/go-git/go-git/v5/plumbing" 40 44 41 - comatproto "github.com/bluesky-social/indigo/api/atproto" 42 45 "github.com/bluesky-social/indigo/atproto/syntax" 43 - lexutil "github.com/bluesky-social/indigo/lex/util" 44 46 ) 45 47 46 48 type Repo struct { ··· 54 56 enforcer *rbac.Enforcer 55 57 notifier notify.Notifier 56 58 logger *slog.Logger 59 + serviceAuth *serviceauth.ServiceAuth 57 60 } 58 61 59 62 func New( ··· 915 918 } 916 919 log.Println("removed repo record ", f.RepoAt.String()) 917 920 918 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 921 + client, err := rp.oauth.ServiceClient( 922 + r, 923 + oauth.WithService(f.Knot), 924 + oauth.WithLxm(tangled.RepoDeleteNSID), 925 + oauth.WithDev(rp.config.Core.Dev), 926 + ) 919 927 if err != nil { 920 - log.Printf("no key found for domain %s: %s\n", f.Knot, err) 928 + log.Println("failed to connect to knot server:", err) 921 929 return 922 930 } 923 931 924 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 932 + err = tangled.RepoDelete( 933 + r.Context(), 934 + client, 935 + &tangled.RepoDelete_Input{ 936 + Did: f.OwnerDid(), 937 + Name: f.RepoName, 938 + }, 939 + ) 925 940 if err != nil { 926 - log.Println("failed to create client to ", f.Knot) 927 - return 928 - } 929 - 930 - ksResp, err := ksClient.RemoveRepo(f.OwnerDid(), f.RepoName) 931 - if err != nil { 932 - log.Printf("failed to make request to %s: %s", f.Knot, err) 933 - return 934 - } 935 - 936 - if ksResp.StatusCode != http.StatusNoContent { 937 - log.Println("failed to remove repo from knot, continuing anyway ", f.Knot) 941 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 942 + if parseErr != nil { 943 + log.Printf("failed to delete repo from knot %s: %s", f.Knot, err) 944 + } else { 945 + log.Printf("failed to delete repo from knot %s: %s", f.Knot, xe.Error()) 946 + } 947 + // Continue anyway since we want to clean up local state 938 948 } else { 939 949 log.Println("removed repo from knot ", f.Knot) 940 950 } ··· 1010 1020 return 1011 1021 } 1012 1022 1013 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1023 + client, err := rp.oauth.ServiceClient( 1024 + r, 1025 + oauth.WithService(f.Knot), 1026 + oauth.WithLxm(tangled.RepoSetDefaultBranchNSID), 1027 + oauth.WithDev(rp.config.Core.Dev), 1028 + ) 1014 1029 if err != nil { 1015 - log.Printf("no key found for domain %s: %s\n", f.Knot, err) 1030 + log.Println("failed to connect to knot server:", err) 1031 + rp.pages.Notice(w, "repo-settings", "Failed to connect to knot server.") 1016 1032 return 1017 1033 } 1018 1034 1019 - ksClient, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 1035 + err = tangled.RepoSetDefaultBranch( 1036 + r.Context(), 1037 + client, 1038 + &tangled.RepoSetDefaultBranch_Input{ 1039 + Repo: fmt.Sprintf("%s/%s", f.OwnerDid(), f.RepoName), 1040 + DefaultBranch: branch, 1041 + }, 1042 + ) 1020 1043 if err != nil { 1021 - log.Println("failed to create client to ", f.Knot) 1022 - return 1023 - } 1024 - 1025 - ksResp, err := ksClient.SetDefaultBranch(f.OwnerDid(), f.RepoName, branch) 1026 - if err != nil { 1027 - log.Printf("failed to make request to %s: %s", f.Knot, err) 1028 - return 1029 - } 1030 - 1031 - if ksResp.StatusCode != http.StatusNoContent { 1032 - rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 1044 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 1045 + if parseErr != nil { 1046 + log.Printf("failed to set default branch: %s", err) 1047 + rp.pages.Notice(w, "repo-settings", "Failed to set default branch. Try again later.") 1048 + } else { 1049 + log.Printf("failed to set default branch: %s", xe.Error()) 1050 + rp.pages.Notice(w, "repo-settings", fmt.Sprintf("Failed to set default branch: %s", xe.Message)) 1051 + } 1033 1052 return 1034 1053 } 1035 1054 ··· 1323 1342 1324 1343 switch r.Method { 1325 1344 case http.MethodPost: 1326 - secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1345 + client, err := rp.oauth.ServiceClient( 1346 + r, 1347 + oauth.WithService(f.Knot), 1348 + oauth.WithLxm(tangled.RepoForkSyncNSID), 1349 + oauth.WithDev(rp.config.Core.Dev), 1350 + ) 1327 1351 if err != nil { 1328 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot)) 1352 + rp.pages.Notice(w, "repo", "Failed to connect to knot server.") 1329 1353 return 1330 1354 } 1331 1355 1332 - client, err := knotclient.NewSignedClient(f.Knot, secret, rp.config.Core.Dev) 1333 - if err != nil { 1334 - rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1356 + repoInfo := f.RepoInfo(user) 1357 + if repoInfo.Source == nil { 1358 + rp.pages.Notice(w, "repo", "This repository is not a fork.") 1335 1359 return 1336 1360 } 1337 1361 1338 - var uri string 1339 - if rp.config.Core.Dev { 1340 - uri = "http" 1341 - } else { 1342 - uri = "https" 1343 - } 1344 - forkName := fmt.Sprintf("%s", f.RepoName) 1345 - forkSourceUrl := fmt.Sprintf("%s://%s/%s/%s", uri, f.Knot, f.OwnerDid(), f.RepoName) 1346 - 1347 - _, err = client.SyncRepoFork(user.Did, forkSourceUrl, forkName, f.Ref) 1362 + err = tangled.RepoForkSync( 1363 + r.Context(), 1364 + client, 1365 + &tangled.RepoForkSync_Input{ 1366 + Did: user.Did, 1367 + Name: f.RepoName, 1368 + Source: repoInfo.Source.AtUri, 1369 + Branch: f.Ref, 1370 + }, 1371 + ) 1348 1372 if err != nil { 1349 - rp.pages.Notice(w, "repo", "Failed to sync repository fork.") 1373 + xe, parseErr := xrpcerr.Unmarshal(err.Error()) 1374 + if parseErr != nil { 1375 + log.Printf("failed to sync repository fork: %s", err) 1376 + rp.pages.Notice(w, "repo", "Failed to sync repository fork.") 1377 + } else { 1378 + log.Printf("failed to sync repository fork: %s", xe.Error()) 1379 + rp.pages.Notice(w, "repo", fmt.Sprintf("Failed to sync repository fork: %s", xe.Message)) 1380 + } 1350 1381 return 1351 1382 } 1352 1383 ··· 1409 1440 // repo with this name already exists, append random string 1410 1441 forkName = fmt.Sprintf("%s-%s", forkName, randomString(3)) 1411 1442 } 1412 - secret, err := db.GetRegistrationKey(rp.db, knot) 1413 - if err != nil { 1414 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) 1415 - return 1416 - } 1443 + client, err := rp.oauth.ServiceClient( 1444 + r, 1445 + oauth.WithService(knot), 1446 + oauth.WithLxm(tangled.RepoForkNSID), 1447 + oauth.WithDev(rp.config.Core.Dev), 1448 + ) 1417 1449 1418 - client, err := knotclient.NewSignedClient(knot, secret, rp.config.Core.Dev) 1419 1450 if err != nil { 1420 - rp.pages.Notice(w, "repo", "Failed to reach knot server.") 1451 + log.Printf("error creating client for knot server: %v", err) 1452 + rp.pages.Notice(w, "repo", "Failed to connect to knot server.") 1421 1453 return 1422 1454 } 1423 1455 ··· 1453 1485 } 1454 1486 }() 1455 1487 1456 - resp, err := client.ForkRepo(user.Did, forkSourceUrl, forkName) 1488 + err = tangled.RepoFork( 1489 + r.Context(), 1490 + client, 1491 + &tangled.RepoFork_Input{ 1492 + Did: user.Did, 1493 + Name: &forkName, 1494 + Source: forkSourceUrl, 1495 + }, 1496 + ) 1497 + 1457 1498 if err != nil { 1458 - rp.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1459 - return 1460 - } 1499 + xe, err := xrpcerr.Unmarshal(err.Error()) 1500 + if err != nil { 1501 + log.Println(err) 1502 + rp.pages.Notice(w, "repo", "Failed to create repository on knot server.") 1503 + return 1504 + } 1461 1505 1462 - switch resp.StatusCode { 1463 - case http.StatusConflict: 1464 - rp.pages.Notice(w, "repo", "A repository with that name already exists.") 1506 + log.Println(xe.Error()) 1507 + rp.pages.Notice(w, "repo", fmt.Sprintf("Failed to create repository on knot server: %s.", xe.Message)) 1465 1508 return 1466 - case http.StatusInternalServerError: 1467 - rp.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 1468 - case http.StatusNoContent: 1469 - // continue 1470 1509 } 1471 1510 1472 1511 xrpcClient, err := rp.oauth.AuthorizedClient(r)
+26 -19
appview/state/state.go
··· 28 28 "tangled.sh/tangled.sh/core/eventconsumer" 29 29 "tangled.sh/tangled.sh/core/idresolver" 30 30 "tangled.sh/tangled.sh/core/jetstream" 31 - "tangled.sh/tangled.sh/core/knotclient" 32 31 tlog "tangled.sh/tangled.sh/core/log" 33 32 "tangled.sh/tangled.sh/core/rbac" 34 33 "tangled.sh/tangled.sh/core/tid" 34 + xrpcerr "tangled.sh/tangled.sh/core/xrpc/errors" 35 35 ) 36 36 37 37 type State struct { ··· 329 329 330 330 existingRepo, err := db.GetRepo(s.db, user.Did, repoName) 331 331 if err == nil && existingRepo != nil { 332 - s.pages.Notice(w, "repo", fmt.Sprintf("A repo by this name already exists on %s", existingRepo.Knot)) 332 + s.pages.Notice(w, "repo", fmt.Sprintf("You already have a repository by this name on %s", existingRepo.Knot)) 333 333 return 334 334 } 335 335 336 - secret, err := db.GetRegistrationKey(s.db, domain) 337 - if err != nil { 338 - s.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", domain)) 339 - return 340 - } 336 + client, err := s.oauth.ServiceClient( 337 + r, 338 + oauth.WithService(domain), 339 + oauth.WithLxm(tangled.RepoCreateNSID), 340 + oauth.WithDev(s.config.Core.Dev), 341 + ) 341 342 342 - client, err := knotclient.NewSignedClient(domain, secret, s.config.Core.Dev) 343 343 if err != nil { 344 344 s.pages.Notice(w, "repo", "Failed to connect to knot server.") 345 345 return ··· 394 394 } 395 395 }() 396 396 397 - resp, err := client.NewRepo(user.Did, repoName, defaultBranch) 397 + err = tangled.RepoCreate( 398 + r.Context(), 399 + client, 400 + &tangled.RepoCreate_Input{ 401 + Default_branch: &defaultBranch, 402 + Did: user.Did, 403 + Name: repoName, 404 + }, 405 + ) 406 + 398 407 if err != nil { 399 - s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 400 - return 401 - } 408 + xe, err := xrpcerr.Unmarshal(err.Error()) 409 + if err != nil { 410 + log.Println(err) 411 + s.pages.Notice(w, "repo", "Failed to create repository on knot server.") 412 + return 413 + } 402 414 403 - switch resp.StatusCode { 404 - case http.StatusConflict: 405 - s.pages.Notice(w, "repo", "A repository with that name already exists.") 415 + log.Println(xe.Error()) 416 + s.pages.Notice(w, "repo", fmt.Sprintf("Failed to create repository on knot server: %s.", xe.Message)) 406 417 return 407 - case http.StatusInternalServerError: 408 - s.pages.Notice(w, "repo", "Failed to create repository on knot. Try again later.") 409 - case http.StatusNoContent: 410 - // continue 411 418 } 412 419 413 420 repo.AtUri = atresp.Uri