From 571811462658b04e8a6ed49dac73b4e0d117b506 Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Mon, 19 Jan 2026 16:05:48 +0000 Subject: [PATCH] appview: handle non-unique publicKey records - upsert public key to handle record update event - don't delete by pair of name and key. delete by name or rkey instead. Signed-off-by: Seongmin Lee --- appview/db/pubkeys.go | 24 +++++++++++++++++------- appview/ingester.go | 16 +++++++++++++--- appview/models/pubkey.go | 38 ++++++++++++++++++++++++++++++++++++++ appview/pages/templates/user/settings/fragments/keyListing.html | 2 +- appview/settings/settings.go | 75 +++++++++++++++++++++++++++++++++++++++------------------------------------ 5 file(s) changed, 108 insertion(s)(+), 47 deletion(s)(-) diff --git a/appview/db/pubkeys.go b/appview/db/pubkeys.go --- a/appview/db/pubkeys.go +++ b/appview/db/pubkeys.go @@ -5,19 +5,29 @@ "tangled.org/core/appview/models" "time" ) -func AddPublicKey(e Execer, did, name, key, rkey string) error { +func UpsertPublicKey(e Execer, pubKey models.PublicKey) error { _, err := e.Exec( - `insert or ignore into public_keys (did, name, key, rkey) - values (?, ?, ?, ?)`, - did, name, key, rkey) + `insert into public_keys (did, rkey, name, key, created) + values (?, ?, ?, ?, ?) + on conflict(did, rkey) do update set + name = excluded.name, + key = excluded.key, + created = excluded.created`, + pubKey.Did, + pubKey.Rkey, + pubKey.Name, + pubKey.Key, + pubKey.Created.Format(time.RFC3339), + ) return err } -func DeletePublicKey(e Execer, did, name, key string) error { +// for public_keys with empty rkey +func DeletePublicKeyLegacy(e Execer, did, name string) error { _, err := e.Exec(` delete from public_keys - where did = ? and name = ? and key = ?`, - did, name, key) + where did = ? and name = ? and rkey = ''`, + did, name) return err } diff --git a/appview/ingester.go b/appview/ingester.go --- a/appview/ingester.go +++ b/appview/ingester.go @@ -131,6 +131,7 @@ if err != nil { return fmt.Errorf("failed to %s star record: %w", e.Commit.Operation, err) } + l.Info("processed star", "operation", e.Commit.Operation, "rkey", e.Commit.RKey) return nil } @@ -164,6 +165,7 @@ if err != nil { return fmt.Errorf("failed to %s follow record: %w", e.Commit.Operation, err) } + l.Info("processed follow", "operation", e.Commit.Operation, "rkey", e.Commit.RKey) return nil } @@ -185,10 +187,17 @@ if err != nil { l.Error("invalid record", "err", err) return err } + pubKey, err := models.PublicKeyFromRecord(syntax.DID(did), syntax.RecordKey(e.Commit.RKey), record) + if err != nil { + l.Error("invalid record", "err", err) + return err + } + if err := pubKey.Validate(); err != nil { + l.Error("invalid record", "err", err) + return err + } - name := record.Name - key := record.Key - err = db.AddPublicKey(i.Db, did, name, key, e.Commit.RKey) + err = db.UpsertPublicKey(i.Db, pubKey) case jmodels.CommitOperationDelete: l.Debug("processing delete of pubkey") err = db.DeletePublicKeyByRkey(i.Db, did, e.Commit.RKey) @@ -197,6 +206,7 @@ if err != nil { return fmt.Errorf("failed to %s pubkey record: %w", e.Commit.Operation, err) } + l.Info("processed pubkey", "operation", e.Commit.Operation, "rkey", e.Commit.RKey) return nil } diff --git a/appview/models/pubkey.go b/appview/models/pubkey.go --- a/appview/models/pubkey.go +++ b/appview/models/pubkey.go @@ -2,7 +2,12 @@ package models import ( "encoding/json" + "fmt" "time" + + "github.com/bluesky-social/indigo/atproto/syntax" + "github.com/gliderlabs/ssh" + "tangled.org/core/api/tangled" ) type PublicKey struct { @@ -23,3 +28,36 @@ Created: p.Created.Format(time.RFC3339), Alias: (*Alias)(&p), }) } + +func (p *PublicKey) AsRecord() tangled.PublicKey { + return tangled.PublicKey{ + Name: p.Name, + Key: p.Key, + CreatedAt: p.Created.Format(time.RFC3339), + } +} + +var _ Validator = new(PublicKey) + +func (p *PublicKey) Validate() error { + if _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(p.Key)); err != nil { + return fmt.Errorf("invalid ssh key format: %w", err) + } + + return nil +} + +func PublicKeyFromRecord(did syntax.DID, rkey syntax.RecordKey, record tangled.PublicKey) (PublicKey, error) { + created, err := time.Parse(time.RFC3339, record.CreatedAt) + if err != nil { + return PublicKey{}, fmt.Errorf("invalid time format '%s'", record.CreatedAt) + } + + return PublicKey{ + Did: did.String(), + Rkey: rkey.String(), + Name: record.Name, + Key: record.Key, + Created: &created, + }, nil +} diff --git a/appview/pages/templates/user/settings/fragments/keyListing.html b/appview/pages/templates/user/settings/fragments/keyListing.html --- a/appview/pages/templates/user/settings/fragments/keyListing.html +++ b/appview/pages/templates/user/settings/fragments/keyListing.html @@ -19,7 +19,7 @@