forked from tangled.org/core
this repo has no description
at opengraph 7.8 kB view raw
1package state 2 3import ( 4 "fmt" 5 "log" 6 "net/http" 7 "net/url" 8 "time" 9 10 comatproto "github.com/bluesky-social/indigo/api/atproto" 11 lexutil "github.com/bluesky-social/indigo/lex/util" 12 "github.com/dustin/go-humanize" 13 "github.com/go-chi/chi/v5" 14 "github.com/go-git/go-git/v5/plumbing" 15 "github.com/ipfs/go-cid" 16 "tangled.sh/tangled.sh/core/api/tangled" 17 "tangled.sh/tangled.sh/core/appview" 18 "tangled.sh/tangled.sh/core/appview/db" 19 "tangled.sh/tangled.sh/core/appview/knotclient" 20 "tangled.sh/tangled.sh/core/appview/pages" 21 "tangled.sh/tangled.sh/core/types" 22) 23 24// TODO: proper statuses here on early exit 25func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { 26 user := s.oauth.GetUser(r) 27 tagParam := chi.URLParam(r, "tag") 28 f, err := s.fullyResolvedRepo(r) 29 if err != nil { 30 log.Println("failed to get repo and knot", err) 31 s.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution") 32 return 33 } 34 35 tag, err := s.resolveTag(f, tagParam) 36 if err != nil { 37 log.Println("failed to resolve tag", err) 38 s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 39 return 40 } 41 42 file, handler, err := r.FormFile("artifact") 43 if err != nil { 44 log.Println("failed to upload artifact", err) 45 s.pages.Notice(w, "upload", "failed to upload artifact") 46 return 47 } 48 defer file.Close() 49 50 client, err := s.oauth.AuthorizedClient(r) 51 if err != nil { 52 log.Println("failed to get authorized client", err) 53 s.pages.Notice(w, "upload", "failed to get authorized client") 54 return 55 } 56 57 uploadBlobResp, err := client.RepoUploadBlob(r.Context(), file) 58 if err != nil { 59 log.Println("failed to upload blob", err) 60 s.pages.Notice(w, "upload", "Failed to upload blob to your PDS. Try again later.") 61 return 62 } 63 64 log.Println("uploaded blob", humanize.Bytes(uint64(uploadBlobResp.Blob.Size)), uploadBlobResp.Blob.Ref.String()) 65 66 rkey := appview.TID() 67 createdAt := time.Now() 68 69 putRecordResp, err := client.RepoPutRecord(r.Context(), &comatproto.RepoPutRecord_Input{ 70 Collection: tangled.RepoArtifactNSID, 71 Repo: user.Did, 72 Rkey: rkey, 73 Record: &lexutil.LexiconTypeDecoder{ 74 Val: &tangled.RepoArtifact{ 75 Artifact: uploadBlobResp.Blob, 76 CreatedAt: createdAt.Format(time.RFC3339), 77 Name: handler.Filename, 78 Repo: f.RepoAt.String(), 79 Tag: tag.Tag.Hash[:], 80 }, 81 }, 82 }) 83 if err != nil { 84 log.Println("failed to create record", err) 85 s.pages.Notice(w, "upload", "Failed to create artifact record. Try again later.") 86 return 87 } 88 89 log.Println(putRecordResp.Uri) 90 91 tx, err := s.db.BeginTx(r.Context(), nil) 92 if err != nil { 93 log.Println("failed to start tx") 94 s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 95 return 96 } 97 defer tx.Rollback() 98 99 artifact := db.Artifact{ 100 Did: user.Did, 101 Rkey: rkey, 102 RepoAt: f.RepoAt, 103 Tag: tag.Tag.Hash, 104 CreatedAt: createdAt, 105 BlobCid: cid.Cid(uploadBlobResp.Blob.Ref), 106 Name: handler.Filename, 107 Size: uint64(uploadBlobResp.Blob.Size), 108 MimeType: uploadBlobResp.Blob.MimeType, 109 } 110 111 err = db.AddArtifact(tx, artifact) 112 if err != nil { 113 log.Println("failed to add artifact record to db", err) 114 s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 115 return 116 } 117 118 err = tx.Commit() 119 if err != nil { 120 log.Println("failed to add artifact record to db") 121 s.pages.Notice(w, "upload", "Failed to create artifact. Try again later.") 122 return 123 } 124 125 s.pages.RepoArtifactFragment(w, pages.RepoArtifactParams{ 126 LoggedInUser: user, 127 RepoInfo: f.RepoInfo(s, user), 128 Artifact: artifact, 129 }) 130} 131 132// TODO: proper statuses here on early exit 133func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { 134 tagParam := chi.URLParam(r, "tag") 135 filename := chi.URLParam(r, "file") 136 f, err := s.fullyResolvedRepo(r) 137 if err != nil { 138 log.Println("failed to get repo and knot", err) 139 return 140 } 141 142 tag, err := s.resolveTag(f, tagParam) 143 if err != nil { 144 log.Println("failed to resolve tag", err) 145 s.pages.Notice(w, "upload", "failed to upload artifact, error in tag resolution") 146 return 147 } 148 149 client, err := s.oauth.AuthorizedClient(r) 150 if err != nil { 151 log.Println("failed to get authorized client", err) 152 return 153 } 154 155 artifacts, err := db.GetArtifact( 156 s.db, 157 db.Filter("repo_at", f.RepoAt), 158 db.Filter("tag", tag.Tag.Hash[:]), 159 db.Filter("name", filename), 160 ) 161 if err != nil { 162 log.Println("failed to get artifacts", err) 163 return 164 } 165 if len(artifacts) != 1 { 166 log.Printf("too many or too little artifacts found") 167 return 168 } 169 170 artifact := artifacts[0] 171 172 getBlobResp, err := client.SyncGetBlob(r.Context(), artifact.BlobCid.String(), artifact.Did) 173 if err != nil { 174 log.Println("failed to get blob from pds", err) 175 return 176 } 177 178 w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename)) 179 w.Write(getBlobResp) 180} 181 182// TODO: proper statuses here on early exit 183func (s *State) DeleteArtifact(w http.ResponseWriter, r *http.Request) { 184 user := s.oauth.GetUser(r) 185 tagParam := chi.URLParam(r, "tag") 186 filename := chi.URLParam(r, "file") 187 f, err := s.fullyResolvedRepo(r) 188 if err != nil { 189 log.Println("failed to get repo and knot", err) 190 return 191 } 192 193 client, _ := s.oauth.AuthorizedClient(r) 194 195 tag := plumbing.NewHash(tagParam) 196 197 artifacts, err := db.GetArtifact( 198 s.db, 199 db.Filter("repo_at", f.RepoAt), 200 db.Filter("tag", tag[:]), 201 db.Filter("name", filename), 202 ) 203 if err != nil { 204 log.Println("failed to get artifacts", err) 205 s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 206 return 207 } 208 if len(artifacts) != 1 { 209 s.pages.Notice(w, "remove", "Unable to find artifact.") 210 return 211 } 212 213 artifact := artifacts[0] 214 215 if user.Did != artifact.Did { 216 log.Println("user not authorized to delete artifact", err) 217 s.pages.Notice(w, "remove", "Unauthorized deletion of artifact.") 218 return 219 } 220 221 _, err = client.RepoDeleteRecord(r.Context(), &comatproto.RepoDeleteRecord_Input{ 222 Collection: tangled.RepoArtifactNSID, 223 Repo: user.Did, 224 Rkey: artifact.Rkey, 225 }) 226 if err != nil { 227 log.Println("failed to get blob from pds", err) 228 s.pages.Notice(w, "remove", "Failed to remove blob from PDS.") 229 return 230 } 231 232 tx, err := s.db.BeginTx(r.Context(), nil) 233 if err != nil { 234 log.Println("failed to start tx") 235 s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 236 return 237 } 238 defer tx.Rollback() 239 240 err = db.DeleteArtifact(tx, 241 db.Filter("repo_at", f.RepoAt), 242 db.Filter("tag", artifact.Tag[:]), 243 db.Filter("name", filename), 244 ) 245 if err != nil { 246 log.Println("failed to remove artifact record from db", err) 247 s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 248 return 249 } 250 251 err = tx.Commit() 252 if err != nil { 253 log.Println("failed to remove artifact record from db") 254 s.pages.Notice(w, "remove", "Failed to delete artifact. Try again later.") 255 return 256 } 257 258 w.Write([]byte{}) 259} 260 261func (s *State) resolveTag(f *FullyResolvedRepo, tagParam string) (*types.TagReference, error) { 262 tagParam, err := url.QueryUnescape(tagParam) 263 if err != nil { 264 return nil, err 265 } 266 267 us, err := knotclient.NewUnsignedClient(f.Knot, s.config.Core.Dev) 268 if err != nil { 269 return nil, err 270 } 271 272 result, err := us.Tags(f.OwnerDid(), f.RepoName) 273 if err != nil { 274 log.Println("failed to reach knotserver", err) 275 return nil, err 276 } 277 278 var tag *types.TagReference 279 for _, t := range result.Tags { 280 if t.Tag != nil { 281 if t.Reference.Name == tagParam || t.Reference.Hash == tagParam { 282 tag = t 283 } 284 } 285 } 286 287 if tag == nil { 288 return nil, fmt.Errorf("invalid tag, only annotated tags are supported for artifacts") 289 } 290 291 if tag.Tag.Target.IsZero() { 292 return nil, fmt.Errorf("invalid tag, only annotated tags are supported for artifacts") 293 } 294 295 return tag, nil 296}