Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

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

appview: add spindle to repo settings form

Signed-off-by: oppiliappan <me@oppi.li>

+179 -24
+8
appview/db/db.go
··· 455 455 }) 456 456 db.Exec("pragma foreign_keys = on;") 457 457 458 + // run migrations 459 + runMigration(db, "add-spindle-to-repos", func(tx *sql.Tx) error { 460 + tx.Exec(` 461 + alter table repos add column spindle text; 462 + `) 463 + return nil 464 + }) 465 + 458 466 return &DB{db}, nil 459 467 } 460 468
+23 -7
appview/db/repos.go
··· 18 18 Created time.Time 19 19 AtUri string 20 20 Description string 21 + Spindle string 21 22 22 23 // optionally, populate this when querying for reverse mappings 23 24 RepoStats *RepoStats ··· 139 138 140 139 func GetRepo(e Execer, did, name string) (*Repo, error) { 141 140 var repo Repo 142 - var nullableDescription sql.NullString 141 + var description, spindle sql.NullString 143 142 144 - row := e.QueryRow(`select did, name, knot, created, at_uri, description from repos where did = ? and name = ?`, did, name) 143 + row := e.QueryRow(` 144 + select did, name, knot, created, at_uri, description, spindle 145 + from repos 146 + where did = ? and name = ? 147 + `, 148 + did, 149 + name, 150 + ) 145 151 146 152 var createdAt string 147 - if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &nullableDescription); err != nil { 153 + if err := row.Scan(&repo.Did, &repo.Name, &repo.Knot, &createdAt, &repo.AtUri, &description, &spindle); err != nil { 148 154 return nil, err 149 155 } 150 156 createdAtTime, _ := time.Parse(time.RFC3339, createdAt) 151 157 repo.Created = createdAtTime 152 158 153 - if nullableDescription.Valid { 154 - repo.Description = nullableDescription.String 155 - } else { 156 - repo.Description = "" 159 + if description.Valid { 160 + repo.Description = description.String 161 + } 162 + 163 + if spindle.Valid { 164 + repo.Spindle = spindle.String 157 165 } 158 166 159 167 return &repo, nil ··· 312 302 func UpdateDescription(e Execer, repoAt, newDescription string) error { 313 303 _, err := e.Exec( 314 304 `update repos set description = ? where at_uri = ?`, newDescription, repoAt) 305 + return err 306 + } 307 + 308 + func UpdateSpindle(e Execer, repoAt, spindle string) error { 309 + _, err := e.Exec( 310 + `update repos set spindle = ? where at_uri = ?`, spindle, repoAt) 315 311 return err 316 312 } 317 313
+1
appview/middleware/middleware.go
··· 225 225 ctx := context.WithValue(req.Context(), "knot", repo.Knot) 226 226 ctx = context.WithValue(ctx, "repoAt", repo.AtUri) 227 227 ctx = context.WithValue(ctx, "repoDescription", repo.Description) 228 + ctx = context.WithValue(ctx, "repoSpindle", repo.Spindle) 228 229 ctx = context.WithValue(ctx, "repoAddedAt", repo.Created.Format(time.RFC3339)) 229 230 next.ServeHTTP(w, req.WithContext(ctx)) 230 231 })
+7 -5
appview/pages/pages.go
··· 616 616 } 617 617 618 618 type RepoSettingsParams struct { 619 - LoggedInUser *oauth.User 620 - RepoInfo repoinfo.RepoInfo 621 - Collaborators []Collaborator 622 - Active string 623 - Branches []types.Branch 619 + LoggedInUser *oauth.User 620 + RepoInfo repoinfo.RepoInfo 621 + Collaborators []Collaborator 622 + Active string 623 + Branches []types.Branch 624 + Spindles []string 625 + CurrentSpindle string 624 626 // TODO: use repoinfo.roles 625 627 IsCollaboratorInviteAllowed bool 626 628 }
+37 -2
appview/pages/templates/repo/settings.html
··· 81 81 </div> 82 82 </form> 83 83 84 + {{ if .RepoInfo.Roles.IsOwner }} 85 + <form 86 + hx-put="/{{ $.RepoInfo.FullName }}/settings/spindle" 87 + class="mt-6 group" 88 + > 89 + <label for="spindle">spindle</label> 90 + <div class="flex gap-2 items-center"> 91 + <select id="spindle" name="spindle" required class="p-1 border border-gray-200 bg-white dark:bg-gray-800 dark:text-white dark:border-gray-700"> 92 + <option 93 + value="" 94 + disabled 95 + selected 96 + > 97 + Choose a spindle 98 + </option> 99 + {{ range .Spindles }} 100 + <option 101 + value="{{ . }}" 102 + class="py-1" 103 + {{ if .eq . $.Repo.Spindle }} 104 + selected 105 + {{ end }} 106 + > 107 + {{ . }} 108 + </option> 109 + {{ end }} 110 + </select> 111 + <button class="btn my-2 flex gap-2 items-center" type="submit"> 112 + <span>save</span> 113 + {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 114 + </button> 115 + </div> 116 + </form> 117 + {{ end }} 118 + 84 119 {{ if .RepoInfo.Roles.RepoDeleteAllowed }} 85 120 <form 86 121 hx-confirm="Are you sure you want to delete this repository?" ··· 124 89 hx-indicator="#delete-repo-spinner" 125 90 > 126 91 <label for="branch">delete repository</label> 127 - <button class="btn my-2 flex gap-2 items-center" type="text"> 92 + <button class="btn my-2 flex items-center" type="text"> 128 93 <span>delete</span> 129 94 <span id="delete-repo-spinner" class="group"> 130 - {{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 95 + {{ i "loader-circle" "pl-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 131 96 </span> 132 97 </button> 133 98 <span>
+99 -10
appview/repo/repo.go
··· 367 367 }) 368 368 return 369 369 case http.MethodPut: 370 - user := rp.oauth.GetUser(r) 371 370 newDescription := r.FormValue("description") 372 371 client, err := rp.oauth.AuthorizedClient(r) 373 372 if err != nil { ··· 752 753 return 753 754 } 754 755 756 + // modify the spindle configured for this repo 757 + func (rp *Repo) EditSpindle(w http.ResponseWriter, r *http.Request) { 758 + f, err := rp.repoResolver.Resolve(r) 759 + if err != nil { 760 + log.Println("failed to get repo and knot", err) 761 + w.WriteHeader(http.StatusBadRequest) 762 + return 763 + } 764 + 765 + repoAt := f.RepoAt 766 + rkey := repoAt.RecordKey().String() 767 + if rkey == "" { 768 + log.Println("invalid aturi for repo", err) 769 + w.WriteHeader(http.StatusInternalServerError) 770 + return 771 + } 772 + 773 + user := rp.oauth.GetUser(r) 774 + 775 + newSpindle := r.FormValue("spindle") 776 + client, err := rp.oauth.AuthorizedClient(r) 777 + if err != nil { 778 + log.Println("failed to get client") 779 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, try again later.") 780 + return 781 + } 782 + 783 + // ensure that this is a valid spindle for this user 784 + validSpindles, err := rp.enforcer.GetSpindlesForUser(user.Did) 785 + if err != nil { 786 + log.Println("failed to get valid spindles") 787 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, try again later.") 788 + return 789 + } 790 + 791 + if !slices.Contains(validSpindles, newSpindle) { 792 + log.Println("newSpindle not present in validSpindles", "newSpindle", newSpindle, "validSpindles", validSpindles) 793 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, try again later.") 794 + return 795 + } 796 + 797 + // optimistic update 798 + err = db.UpdateSpindle(rp.db, string(repoAt), newSpindle) 799 + if err != nil { 800 + log.Println("failed to perform update-spindle query", err) 801 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, try again later.") 802 + return 803 + } 804 + 805 + ex, err := client.RepoGetRecord(r.Context(), "", tangled.RepoNSID, user.Did, rkey) 806 + if err != nil { 807 + // failed to get record 808 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, no record found on PDS.") 809 + return 810 + } 811 + _, err = client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 812 + Collection: tangled.RepoNSID, 813 + Repo: user.Did, 814 + Rkey: rkey, 815 + SwapRecord: ex.Cid, 816 + Record: &lexutil.LexiconTypeDecoder{ 817 + Val: &tangled.Repo{ 818 + Knot: f.Knot, 819 + Name: f.RepoName, 820 + Owner: user.Did, 821 + CreatedAt: f.CreatedAt, 822 + Description: &f.Description, 823 + Spindle: &newSpindle, 824 + }, 825 + }, 826 + }) 827 + 828 + if err != nil { 829 + log.Println("failed to perform update-spindle query", err) 830 + // failed to get record 831 + rp.pages.Notice(w, "repo-notice", "Failed to configure spindle, unable to save to PDS.") 832 + return 833 + } 834 + 835 + w.Write(fmt.Append(nil, "spindle set to: ", newSpindle)) 836 + } 837 + 755 838 func (rp *Repo) AddCollaborator(w http.ResponseWriter, r *http.Request) { 756 839 f, err := rp.repoResolver.Resolve(r) 757 840 if err != nil { ··· 875 794 } 876 795 877 796 if ksResp.StatusCode != http.StatusNoContent { 878 - w.Write([]byte(fmt.Sprint("knotserver failed to add collaborator: ", err))) 797 + w.Write(fmt.Append(nil, "knotserver failed to add collaborator: ", err)) 879 798 return 880 799 } 881 800 882 801 tx, err := rp.db.BeginTx(r.Context(), nil) 883 802 if err != nil { 884 803 log.Println("failed to start tx") 885 - w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 804 + w.Write(fmt.Append(nil, "failed to add collaborator: ", err)) 886 805 return 887 806 } 888 807 defer func() { ··· 895 814 896 815 err = rp.enforcer.AddCollaborator(collaboratorIdent.DID.String(), f.Knot, f.DidSlashRepo()) 897 816 if err != nil { 898 - w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 817 + w.Write(fmt.Append(nil, "failed to add collaborator: ", err)) 899 818 return 900 819 } 901 820 902 821 err = db.AddCollaborator(rp.db, collaboratorIdent.DID.String(), f.OwnerDid(), f.RepoName, f.Knot) 903 822 if err != nil { 904 - w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 823 + w.Write(fmt.Append(nil, "failed to add collaborator: ", err)) 905 824 return 906 825 } 907 826 ··· 919 838 return 920 839 } 921 840 922 - w.Write([]byte(fmt.Sprint("added collaborator: ", collaboratorIdent.Handle.String()))) 841 + w.Write(fmt.Append(nil, "added collaborator: ", collaboratorIdent.Handle.String())) 923 842 924 843 } 925 844 ··· 978 897 tx, err := rp.db.BeginTx(r.Context(), nil) 979 898 if err != nil { 980 899 log.Println("failed to start tx") 981 - w.Write([]byte(fmt.Sprint("failed to add collaborator: ", err))) 900 + w.Write(fmt.Append(nil, "failed to add collaborator: ", err)) 982 901 return 983 902 } 984 903 defer func() { ··· 1069 988 return 1070 989 } 1071 990 1072 - w.Write([]byte(fmt.Sprint("default branch set to: ", branch))) 991 + w.Write(fmt.Append(nil, "default branch set to: ", branch)) 1073 992 } 1074 993 1075 994 func (rp *Repo) RepoSettings(w http.ResponseWriter, r *http.Request) { ··· 1108 1027 return 1109 1028 } 1110 1029 1030 + spindles, err := rp.enforcer.GetSpindlesForUser(user.Did) 1031 + if err != nil { 1032 + log.Println("failed to fetch spindles", err) 1033 + return 1034 + } 1035 + 1111 1036 rp.pages.RepoSettings(w, pages.RepoSettingsParams{ 1112 1037 LoggedInUser: user, 1113 1038 RepoInfo: f.RepoInfo(user), 1114 1039 Collaborators: repoCollaborators, 1115 1040 IsCollaboratorInviteAllowed: isCollaboratorInviteAllowed, 1116 1041 Branches: result.Branches, 1042 + Spindles: spindles, 1043 + CurrentSpindle: f.Spindle, 1117 1044 }) 1118 1045 } 1119 1046 } ··· 1138 1049 case http.MethodPost: 1139 1050 secret, err := db.GetRegistrationKey(rp.db, f.Knot) 1140 1051 if err != nil { 1141 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", f.Knot)) 1052 + rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", f.Knot)) 1142 1053 return 1143 1054 } 1144 1055 ··· 1224 1135 } 1225 1136 secret, err := db.GetRegistrationKey(rp.db, knot) 1226 1137 if err != nil { 1227 - rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %rp.", knot)) 1138 + rp.pages.Notice(w, "repo", fmt.Sprintf("No registration key found for knot %s.", knot)) 1228 1139 return 1229 1140 } 1230 1141
+1
appview/repo/router.go
··· 70 70 }) 71 71 r.With(mw.RepoPermissionMiddleware("repo:settings")).Route("/settings", func(r chi.Router) { 72 72 r.Get("/", rp.RepoSettings) 73 + r.With(mw.RepoPermissionMiddleware("repo:owner")).Post("/spindle", rp.EditSpindle) 73 74 r.With(mw.RepoPermissionMiddleware("repo:invite")).Put("/collaborator", rp.AddCollaborator) 74 75 r.With(mw.RepoPermissionMiddleware("repo:delete")).Delete("/delete", rp.DeleteRepo) 75 76 r.Put("/branches/default", rp.SetDefaultBranch)
+3
appview/reporesolver/resolver.go
··· 31 31 RepoName string 32 32 RepoAt syntax.ATURI 33 33 Description string 34 + Spindle string 34 35 CreatedAt string 35 36 Ref string 36 37 CurrentDir string ··· 96 95 // pass through values from the middleware 97 96 description, ok := r.Context().Value("repoDescription").(string) 98 97 addedAt, ok := r.Context().Value("repoAddedAt").(string) 98 + spindle, ok := r.Context().Value("repoSpindle").(string) 99 99 100 100 return &ResolvedRepo{ 101 101 Knot: knot, ··· 107 105 CreatedAt: addedAt, 108 106 Ref: ref, 109 107 CurrentDir: currentDir, 108 + Spindle: spindle, 110 109 111 110 rr: rr, 112 111 }, nil