diff --git a/appview/db/db.go b/appview/db/db.go --- a/appview/db/db.go +++ b/appview/db/db.go @@ -1629,6 +1629,13 @@ `) return err }) + orm.RunMigration(conn, logger, "add-cid-to-strings", func(tx *sql.Tx) error { + _, err := tx.Exec(` + alter table strings add column cid text; + `) + return err + }) + return &DB{ db, logger, diff --git a/appview/db/strings.go b/appview/db/strings.go --- a/appview/db/strings.go +++ b/appview/db/strings.go @@ -7,6 +7,7 @@ "fmt" "strings" "time" + "github.com/bluesky-social/indigo/atproto/syntax" "tangled.org/core/appview/models" "tangled.org/core/orm" ) @@ -16,26 +17,24 @@ _, err := e.Exec( `insert into strings ( did, rkey, + cid, filename, description, content, created, edited ) - values (?, ?, ?, ?, ?, ?, null) + values (?, ?, ?, ?, ?, ?, ?, null) on conflict(did, rkey) do update set - filename = excluded.filename, + cid = excluded.cid, + filename = excluded.filename, description = excluded.description, - content = excluded.content, - edited = case - when - strings.content != excluded.content - or strings.filename != excluded.filename - or strings.description != excluded.description then ? - else strings.edited - end`, + content = excluded.content, + edited = case when strings.cid is not null then ? else strings.edited end + where strings.cid is not excluded.cid`, s.Did, s.Rkey, + s.Cid, s.Filename, s.Description, s.Contents, @@ -68,6 +67,7 @@ query := fmt.Sprintf(`select did, rkey, + cid, filename, description, content, @@ -91,11 +91,12 @@ for rows.Next() { var s models.String var createdAt string - var editedAt sql.NullString + var cid, editedAt sql.Null[string] if err := rows.Scan( &s.Did, &s.Rkey, + &cid, &s.Filename, &s.Description, &s.Contents, @@ -105,13 +106,18 @@ ); err != nil { return nil, err } + if cid.Valid { + s.Cid = new(syntax.CID) + *s.Cid = syntax.CID(cid.V) + } + s.Created, err = time.Parse(time.RFC3339, createdAt) if err != nil { s.Created = time.Now() } if editedAt.Valid { - e, err := time.Parse(time.RFC3339, editedAt.String) + e, err := time.Parse(time.RFC3339, editedAt.V) if err != nil { e = time.Now() } diff --git a/appview/ingester.go b/appview/ingester.go --- a/appview/ingester.go +++ b/appview/ingester.go @@ -800,7 +800,7 @@ rkey := e.Commit.RKey var err error - l := i.Logger.With("handler", "ingestString", "nsid", e.Commit.Collection, "did", did, "rkey", rkey) + l := i.Logger.With("handler", "ingestString", "nsid", e.Commit.Collection, "did", did, "rkey", rkey, "cid", e.Commit.CID) l.Info("ingesting record") ddb, ok := i.Db.Execer.(*db.DB) @@ -818,7 +818,7 @@ l.Error("invalid record", "err", err) return err } - string := models.StringFromRecord(did, rkey, record) + string := models.StringFromRecord(syntax.DID(did), syntax.RecordKey(rkey), syntax.CID(e.Commit.CID), record) if err = i.Validator.ValidateString(&string); err != nil { l.Error("invalid record", "err", err) diff --git a/appview/models/string.go b/appview/models/string.go --- a/appview/models/string.go +++ b/appview/models/string.go @@ -13,7 +13,8 @@ ) type String struct { Did syntax.DID - Rkey string + Rkey syntax.RecordKey + Cid *syntax.CID Filename string Description string @@ -26,8 +27,8 @@ func (s *String) AtUri() syntax.ATURI { return syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", s.Did, tangled.StringNSID, s.Rkey)) } -func (s *String) AsRecord() tangled.String { - return tangled.String{ +func (s *String) AsRecord() *tangled.String { + return &tangled.String{ Filename: s.Filename, Description: s.Description, Contents: s.Contents, @@ -35,14 +36,15 @@ CreatedAt: s.Created.Format(time.RFC3339), } } -func StringFromRecord(did, rkey string, record tangled.String) String { +func StringFromRecord(did syntax.DID, rkey syntax.RecordKey, cid syntax.CID, record tangled.String) String { created, err := time.Parse(record.CreatedAt, time.RFC3339) if err != nil { created = time.Now() } return String{ - Did: syntax.DID(did), + Did: did, Rkey: rkey, + Cid: &cid, Filename: record.Filename, Description: record.Description, Contents: record.Contents, diff --git a/appview/pages/templates/strings/string.html b/appview/pages/templates/strings/string.html --- a/appview/pages/templates/strings/string.html +++ b/appview/pages/templates/strings/string.html @@ -119,6 +119,7 @@ hx-on::after-request="if(event.detail.successful) this.reset()" class="group/form" > +
{{ template "user/fragments/picHandleLink" .LoggedInUser.Did }} diff --git a/appview/strings/strings.go b/appview/strings/strings.go --- a/appview/strings/strings.go +++ b/appview/strings/strings.go @@ -296,8 +296,6 @@ Contents: content, Created: first.Created, } - record := entry.AsRecord() - client, err := s.OAuth.AuthorizedClient(r) if err != nil { fail("Failed to create record.", err) @@ -305,19 +303,27 @@ return } // first replace the existing record in the PDS - ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.StringNSID, entry.Did.String(), entry.Rkey) - if err != nil { - fail("Failed to updated existing record.", err) - return + var exCid string + if entry.Cid != nil { + exCid = entry.Cid.String() + } else { + ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.StringNSID, entry.Did.String(), entry.Rkey.String()) + if err != nil { + fail("Failed to get existing record.", err) + return + } + if ex.Cid == nil { + fail("Failed to get existing record.", err) + return + } + exCid = *ex.Cid } resp, err := comatproto.RepoPutRecord(r.Context(), client, &atproto.RepoPutRecord_Input{ Collection: tangled.StringNSID, Repo: entry.Did.String(), - Rkey: entry.Rkey, - SwapRecord: ex.Cid, - Record: &lexutil.LexiconTypeDecoder{ - Val: &record, - }, + Rkey: entry.Rkey.String(), + SwapRecord: &exCid, + Record: &lexutil.LexiconTypeDecoder{Val: entry.AsRecord()}, }) if err != nil { fail("Failed to updated existing record.", err) @@ -335,7 +341,7 @@ s.Notifier.EditString(r.Context(), &entry) // if that went okay, redir to the string - s.Pages.HxRedirect(w, "/strings/"+user.Did+"/"+entry.Rkey) + s.Pages.HxRedirect(w, fmt.Sprintf("/strings/%s/%s", entry.Did, entry.Rkey)) } } @@ -372,15 +378,13 @@ description := r.FormValue("description") string := models.String{ Did: syntax.DID(user.Did), - Rkey: tid.TID(), + Rkey: syntax.RecordKey(tid.TID()), Filename: filename, Description: description, Contents: content, Created: time.Now(), } - record := string.AsRecord() - client, err := s.OAuth.AuthorizedClient(r) if err != nil { fail("Failed to create record.", err) @@ -389,11 +393,9 @@ } resp, err := comatproto.RepoPutRecord(r.Context(), client, &atproto.RepoPutRecord_Input{ Collection: tangled.StringNSID, - Repo: user.Did, - Rkey: string.Rkey, - Record: &lexutil.LexiconTypeDecoder{ - Val: &record, - }, + Repo: string.Did.String(), + Rkey: string.Rkey.String(), + Record: &lexutil.LexiconTypeDecoder{Val: string.AsRecord()}, }) if err != nil { fail("Failed to create record.", err) @@ -411,7 +413,7 @@ s.Notifier.NewString(r.Context(), &string) // successful - s.Pages.HxRedirect(w, "/strings/"+user.Did+"/"+string.Rkey) + s.Pages.HxRedirect(w, fmt.Sprintf("/strings/%s/%s", string.Did, string.Rkey)) } }