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