forked from tangled.org/core
Monorepo for Tangled

appview: ingester: process sh.tangled.repo.artifact records

authored by oppi.li and committed by Tangled 84b0871b 7d98bb05

Changed files
+87 -102
appview
+9 -9
appview/db/artifact.go
··· 23 23 BlobCid cid.Cid 24 24 Name string 25 25 Size uint64 26 - Mimetype string 26 + MimeType string 27 27 } 28 28 29 29 func (a *Artifact) ArtifactAt() syntax.ATURI { ··· 52 52 artifact.BlobCid.String(), 53 53 artifact.Name, 54 54 artifact.Size, 55 - artifact.Mimetype, 55 + artifact.MimeType, 56 56 ) 57 57 return err 58 58 } 59 59 60 - type Filter struct { 60 + type filter struct { 61 61 key string 62 62 arg any 63 63 } 64 64 65 - func NewFilter(key string, arg any) Filter { 66 - return Filter{ 65 + func Filter(key string, arg any) filter { 66 + return filter{ 67 67 key: key, 68 68 arg: arg, 69 69 } 70 70 } 71 71 72 - func (f Filter) Condition() string { 72 + func (f filter) Condition() string { 73 73 return fmt.Sprintf("%s = ?", f.key) 74 74 } 75 75 76 - func GetArtifact(e Execer, filters ...Filter) ([]Artifact, error) { 76 + func GetArtifact(e Execer, filters ...filter) ([]Artifact, error) { 77 77 var artifacts []Artifact 78 78 79 79 var conditions []string ··· 124 124 &blobCid, 125 125 &artifact.Name, 126 126 &artifact.Size, 127 - &artifact.Mimetype, 127 + &artifact.MimeType, 128 128 ); err != nil { 129 129 return nil, err 130 130 } ··· 146 146 return artifacts, nil 147 147 } 148 148 149 - func RemoveArtifact(e Execer, filters ...Filter) error { 149 + func DeleteArtifact(e Execer, filters ...filter) error { 150 150 var conditions []string 151 151 var args []any 152 152 for _, filter := range filters {
+56 -1
appview/ingester.go
··· 5 5 "encoding/json" 6 6 "fmt" 7 7 "log" 8 + "time" 8 9 9 10 "github.com/bluesky-social/indigo/atproto/syntax" 10 11 "github.com/bluesky-social/jetstream/pkg/models" 11 - tangled "tangled.sh/tangled.sh/core/api/tangled" 12 + "github.com/go-git/go-git/v5/plumbing" 13 + "github.com/ipfs/go-cid" 14 + "tangled.sh/tangled.sh/core/api/tangled" 12 15 "tangled.sh/tangled.sh/core/appview/db" 13 16 ) 14 17 ··· 36 39 ingestStar(&d, e) 37 40 case tangled.PublicKeyNSID: 38 41 ingestPublicKey(&d, e) 42 + case tangled.RepoArtifactNSID: 43 + ingestArtifact(&d, e) 39 44 } 40 45 41 46 return err ··· 131 136 132 137 return nil 133 138 } 139 + 140 + func ingestArtifact(d *db.DbWrapper, e *models.Event) error { 141 + did := e.Did 142 + var err error 143 + 144 + switch e.Commit.Operation { 145 + case models.CommitOperationCreate, models.CommitOperationUpdate: 146 + log.Println("processing add of artifact") 147 + raw := json.RawMessage(e.Commit.Record) 148 + record := tangled.RepoArtifact{} 149 + err = json.Unmarshal(raw, &record) 150 + if err != nil { 151 + log.Printf("invalid record: %s", err) 152 + return err 153 + } 154 + 155 + repoAt, err := syntax.ParseATURI(record.Repo) 156 + if err != nil { 157 + return err 158 + } 159 + 160 + createdAt, err := time.Parse(time.RFC3339, record.CreatedAt) 161 + if err != nil { 162 + createdAt = time.Now() 163 + } 164 + 165 + artifact := db.Artifact{ 166 + Did: did, 167 + Rkey: e.Commit.RKey, 168 + RepoAt: repoAt, 169 + Tag: plumbing.Hash(record.Tag), 170 + CreatedAt: createdAt, 171 + BlobCid: cid.Cid(record.Artifact.Ref), 172 + Name: record.Name, 173 + Size: uint64(record.Artifact.Size), 174 + MimeType: record.Artifact.MimeType, 175 + } 176 + 177 + err = db.AddArtifact(d, artifact) 178 + case models.CommitOperationDelete: 179 + log.Println("processing delete of artifact") 180 + err = db.DeleteArtifact(d, db.Filter("did", did), db.Filter("rkey", e.Commit.RKey)) 181 + } 182 + 183 + if err != nil { 184 + return fmt.Errorf("failed to %s artifact record: %w", e.Commit.Operation, err) 185 + } 186 + 187 + return nil 188 + }
+1 -1
appview/pages/pages.go
··· 496 496 497 497 type RepoArtifactParams struct { 498 498 LoggedInUser *auth.User 499 - RepoInfo RepoInfo 499 + RepoInfo repoinfo.RepoInfo 500 500 Artifact db.Artifact 501 501 } 502 502
+4 -4
appview/pages/templates/repo/fragments/artifact.html
··· 6 6 <a href="/{{ .RepoInfo.FullName }}/tags/{{ .Artifact.Tag.String }}/download/{{ .Artifact.Name | urlquery }}" class="no-underline hover:no-underline"> 7 7 {{ .Artifact.Name }} 8 8 </a> 9 - <span class="text-gray-500 dark:text-gray-400 pl-2">{{ byteFmt .Artifact.Size }}</span> 9 + <span class="text-gray-500 dark:text-gray-400 pl-2 text-sm">{{ byteFmt .Artifact.Size }}</span> 10 10 </div> 11 11 12 - <div id="right-side" class="text-gray-500 dark:text-gray-400 flex items-center flex-shrink-0 gap-2"> 12 + <div id="right-side" class="text-gray-500 dark:text-gray-400 flex items-center flex-shrink-0 gap-2 text-sm"> 13 13 <span title="{{ longTimeFmt .Artifact.CreatedAt }}" class="hidden md:inline">{{ timeFmt .Artifact.CreatedAt }}</span> 14 14 <span title="{{ longTimeFmt .Artifact.CreatedAt }}" class=" md:hidden">{{ shortTimeFmt .Artifact.CreatedAt }}</span> 15 15 16 16 <span class="select-none after:content-['·'] hidden md:inline"></span> 17 - <span class="truncate max-w-[100px] hidden md:inline">{{ .Artifact.Mimetype }}</span> 17 + <span class="truncate max-w-[100px] hidden md:inline">{{ .Artifact.MimeType }}</span> 18 18 19 - {{ if and (.LoggedInUser) (eq .LoggedInUser.Did .Artifact.Did) }} 19 + {{ if and .LoggedInUser (eq .LoggedInUser.Did .Artifact.Did) }} 20 20 <button 21 21 id="delete-{{ $unique }}" 22 22 class="btn text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 gap-2"
+14 -14
appview/state/artifact.go
··· 23 23 func (s *State) AttachArtifact(w http.ResponseWriter, r *http.Request) { 24 24 user := s.auth.GetUser(r) 25 25 tagParam := chi.URLParam(r, "tag") 26 - f, err := fullyResolvedRepo(r) 26 + f, err := s.fullyResolvedRepo(r) 27 27 if err != nil { 28 28 log.Println("failed to get repo and knot", err) 29 29 s.pages.Notice(w, "upload", "failed to upload artifact, error in repo resolution") ··· 98 98 BlobCid: cid.Cid(uploadBlobResp.Blob.Ref), 99 99 Name: handler.Filename, 100 100 Size: uint64(uploadBlobResp.Blob.Size), 101 - Mimetype: uploadBlobResp.Blob.MimeType, 101 + MimeType: uploadBlobResp.Blob.MimeType, 102 102 } 103 103 104 104 err = db.AddArtifact(tx, artifact) ··· 126 126 func (s *State) DownloadArtifact(w http.ResponseWriter, r *http.Request) { 127 127 tagParam := chi.URLParam(r, "tag") 128 128 filename := chi.URLParam(r, "file") 129 - f, err := fullyResolvedRepo(r) 129 + f, err := s.fullyResolvedRepo(r) 130 130 if err != nil { 131 131 log.Println("failed to get repo and knot", err) 132 132 return ··· 143 143 144 144 artifacts, err := db.GetArtifact( 145 145 s.db, 146 - db.NewFilter("repo_at", f.RepoAt), 147 - db.NewFilter("tag", tag.Tag.Hash[:]), 148 - db.NewFilter("name", filename), 146 + db.Filter("repo_at", f.RepoAt), 147 + db.Filter("tag", tag.Tag.Hash[:]), 148 + db.Filter("name", filename), 149 149 ) 150 150 if err != nil { 151 151 log.Println("failed to get artifacts", err) ··· 173 173 user := s.auth.GetUser(r) 174 174 tagParam := chi.URLParam(r, "tag") 175 175 filename := chi.URLParam(r, "file") 176 - f, err := fullyResolvedRepo(r) 176 + f, err := s.fullyResolvedRepo(r) 177 177 if err != nil { 178 178 log.Println("failed to get repo and knot", err) 179 179 return ··· 185 185 186 186 artifacts, err := db.GetArtifact( 187 187 s.db, 188 - db.NewFilter("repo_at", f.RepoAt), 189 - db.NewFilter("tag", tag[:]), 190 - db.NewFilter("name", filename), 188 + db.Filter("repo_at", f.RepoAt), 189 + db.Filter("tag", tag[:]), 190 + db.Filter("name", filename), 191 191 ) 192 192 if err != nil { 193 193 log.Println("failed to get artifacts", err) ··· 226 226 } 227 227 defer tx.Rollback() 228 228 229 - err = db.RemoveArtifact(tx, 230 - db.NewFilter("repo_at", f.RepoAt), 231 - db.NewFilter("tag", artifact.Tag[:]), 232 - db.NewFilter("name", filename), 229 + err = db.DeleteArtifact(tx, 230 + db.Filter("repo_at", f.RepoAt), 231 + db.Filter("tag", artifact.Tag[:]), 232 + db.Filter("name", filename), 233 233 ) 234 234 if err != nil { 235 235 log.Println("failed to remove artifact record from db", err)
-70
appview/state/jetstream.go
··· 1 - package state 2 - 3 - import ( 4 - "context" 5 - "encoding/json" 6 - "fmt" 7 - "log" 8 - 9 - "github.com/bluesky-social/indigo/atproto/syntax" 10 - "github.com/bluesky-social/jetstream/pkg/models" 11 - "tangled.sh/tangled.sh/core/api/tangled" 12 - "tangled.sh/tangled.sh/core/appview/db" 13 - ) 14 - 15 - type Ingester func(ctx context.Context, e *models.Event) error 16 - 17 - func jetstreamIngester(d db.DbWrapper) Ingester { 18 - return func(ctx context.Context, e *models.Event) error { 19 - var err error 20 - defer func() { 21 - eventTime := e.TimeUS 22 - lastTimeUs := eventTime + 1 23 - if err := d.SaveLastTimeUs(lastTimeUs); err != nil { 24 - err = fmt.Errorf("(deferred) failed to save last time us: %w", err) 25 - } 26 - }() 27 - 28 - if e.Kind != models.EventKindCommit { 29 - return nil 30 - } 31 - 32 - did := e.Did 33 - raw := json.RawMessage(e.Commit.Record) 34 - 35 - switch e.Commit.Collection { 36 - case tangled.GraphFollowNSID: 37 - record := tangled.GraphFollow{} 38 - err := json.Unmarshal(raw, &record) 39 - if err != nil { 40 - log.Println("invalid record") 41 - return err 42 - } 43 - err = db.AddFollow(d, did, record.Subject, e.Commit.RKey) 44 - if err != nil { 45 - return fmt.Errorf("failed to add follow to db: %w", err) 46 - } 47 - case tangled.FeedStarNSID: 48 - record := tangled.FeedStar{} 49 - err := json.Unmarshal(raw, &record) 50 - if err != nil { 51 - log.Println("invalid record") 52 - return err 53 - } 54 - 55 - subjectUri, err := syntax.ParseATURI(record.Subject) 56 - 57 - if err != nil { 58 - log.Println("invalid record") 59 - return err 60 - } 61 - 62 - err = db.AddStar(d, did, subjectUri, e.Commit.RKey) 63 - if err != nil { 64 - return fmt.Errorf("failed to add follow to db: %w", err) 65 - } 66 - } 67 - 68 - return err 69 - } 70 - }
+1 -1
appview/state/repo.go
··· 420 420 return 421 421 } 422 422 423 - artifacts, err := db.GetArtifact(s.db, db.NewFilter("repo_at", f.RepoAt)) 423 + artifacts, err := db.GetArtifact(s.db, db.Filter("repo_at", f.RepoAt)) 424 424 if err != nil { 425 425 log.Println("failed grab artifacts", err) 426 426 return
+1 -1
appview/state/state.go
··· 63 63 jc, err := jetstream.NewJetstreamClient( 64 64 config.JetstreamEndpoint, 65 65 "appview", 66 - []string{tangled.GraphFollowNSID, tangled.FeedStarNSID, tangled.PublicKeyNSID}, 66 + []string{tangled.GraphFollowNSID, tangled.FeedStarNSID, tangled.PublicKeyNSID, tangled.RepoArtifactNSID}, 67 67 nil, 68 68 slog.Default(), 69 69 wrapper,
+1 -1
flake.nix
··· 49 49 inherit (gitignore.lib) gitignoreSource; 50 50 in { 51 51 overlays.default = final: prev: let 52 - goModHash = "sha256-EilWxfqrcKDaSR5zA3ZuDSCq7V+/IfWpKPu8HWhpndA="; 52 + goModHash = "sha256-CmBuvv3duQQoc8iTW4244w1rYLGeqMQS+qQ3wwReZZg="; 53 53 buildCmdPackage = name: 54 54 final.buildGoModule { 55 55 pname = name;