Clear puppet phone numbers when phone ID changes

This commit is contained in:
Tulir Asokan 2024-03-18 15:12:56 +02:00
parent e3619dbe7b
commit 0456b8c3de
6 changed files with 76 additions and 9 deletions

View file

@ -18,6 +18,7 @@ package database
import (
"context"
"database/sql"
"time"
"go.mau.fi/util/dbutil"
@ -43,12 +44,22 @@ const (
SET phone=$3, contact_id=$4, name=$5, name_set=$6, avatar_hash=$7, avatar_mxc=$8, avatar_set=$9, avatar_update_ts=$10, contact_info_set=$11
WHERE id=$1 AND receiver=$2
`
resetPuppetMetadataQuery = `
UPDATE puppet SET phone=NULL, contact_info_set=false, avatar_update_ts=0, contact_id='' WHERE receiver=$1
`
)
func (pq *PuppetQuery) Get(ctx context.Context, key Key) (*Puppet, error) {
return pq.QueryOne(ctx, getPuppetQuery, key.ID, key.Receiver)
}
// Reset clears the phone number of all puppets, so that puppets can be upserted safely on relogin
// even if the internal IDs shuffled around. This does *not* delete the puppets, because otherwise
// avatars wouldn't get reset appropriately.
func (pq *PuppetQuery) Reset(ctx context.Context, userID int) error {
return pq.Exec(ctx, resetPuppetMetadataQuery, userID)
}
type Puppet struct {
qh *dbutil.QueryHelper[*Puppet]
@ -67,8 +78,9 @@ type Puppet struct {
func (puppet *Puppet) Scan(row dbutil.Scannable) (*Puppet, error) {
var avatarHash []byte
var avatarUpdateTS int64
var phone sql.NullString
err := row.Scan(
&puppet.ID, &puppet.Receiver, &puppet.Phone, &puppet.ContactID, &puppet.Name, &puppet.NameSet,
&puppet.ID, &puppet.Receiver, &phone, &puppet.ContactID, &puppet.Name, &puppet.NameSet,
&avatarHash, &puppet.AvatarMXC, &puppet.AvatarSet, &avatarUpdateTS, &puppet.ContactInfoSet,
)
if err != nil {
@ -77,14 +89,21 @@ func (puppet *Puppet) Scan(row dbutil.Scannable) (*Puppet, error) {
if len(avatarHash) == 32 {
puppet.AvatarHash = *(*[32]byte)(avatarHash)
}
puppet.AvatarUpdateTS = time.UnixMilli(avatarUpdateTS)
puppet.Phone = phone.String
if avatarUpdateTS > 0 {
puppet.AvatarUpdateTS = time.UnixMilli(avatarUpdateTS)
}
return puppet, nil
}
func (puppet *Puppet) sqlVariables() []any {
var avatarUpdateTS int64
if !puppet.AvatarUpdateTS.IsZero() {
avatarUpdateTS = puppet.AvatarUpdateTS.UnixMilli()
}
return []any{
puppet.ID, puppet.Receiver, puppet.Phone, puppet.ContactID, puppet.Name, puppet.NameSet,
puppet.AvatarHash[:], &puppet.AvatarMXC, puppet.AvatarSet, puppet.AvatarUpdateTS.UnixMilli(), puppet.ContactInfoSet,
puppet.ID, puppet.Receiver, dbutil.StrPtr(puppet.Phone), puppet.ContactID, puppet.Name, puppet.NameSet,
puppet.AvatarHash[:], &puppet.AvatarMXC, puppet.AvatarSet, avatarUpdateTS, puppet.ContactInfoSet,
}
}

View file

@ -1,4 +1,4 @@
-- v0 -> v8: Latest revision
-- v0 -> v9: Latest revision
CREATE TABLE "user" (
-- only: postgres
@ -23,7 +23,7 @@ CREATE TABLE "user" (
CREATE TABLE puppet (
id TEXT NOT NULL,
receiver BIGINT NOT NULL,
phone TEXT NOT NULL,
phone TEXT,
contact_id TEXT NOT NULL,
name TEXT NOT NULL,
name_set BOOLEAN NOT NULL DEFAULT false,

View file

@ -0,0 +1,3 @@
-- v9: Make phone nullable for puppets
-- transaction: off
ALTER TABLE puppet ALTER COLUMN phone DROP NOT NULL;

View file

@ -0,0 +1,32 @@
-- v9: Make phone nullable for puppets
-- transaction: off
PRAGMA foreign_keys = OFF;
BEGIN TRANSACTION;
CREATE TABLE puppet_new (
id TEXT NOT NULL,
receiver BIGINT NOT NULL,
phone TEXT,
contact_id TEXT NOT NULL,
name TEXT NOT NULL,
name_set BOOLEAN NOT NULL DEFAULT false,
avatar_hash bytea CHECK ( LENGTH(avatar_hash) = 32 ),
avatar_update_ts BIGINT NOT NULL,
avatar_mxc TEXT NOT NULL,
avatar_set BOOLEAN NOT NULL DEFAULT false,
contact_info_set BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (id, receiver),
CONSTRAINT puppet_user_fkey FOREIGN KEY (receiver) REFERENCES "user"(rowid) ON DELETE CASCADE,
CONSTRAINT puppet_phone_unique UNIQUE (phone, receiver)
);
INSERT INTO puppet_new (id, receiver, phone, contact_id, name, name_set, avatar_hash, avatar_update_ts, avatar_mxc, avatar_set, contact_info_set)
SELECT id, receiver, phone, contact_id, name, name_set, avatar_hash, avatar_update_ts, avatar_mxc, avatar_set, contact_info_set
FROM puppet;
DROP TABLE puppet;
ALTER TABLE puppet_new RENAME TO puppet;
PRAGMA foreign_key_check;
COMMIT;
PRAGMA FOREIGN_KEYS = ON;

View file

@ -281,8 +281,9 @@ func (puppet *Puppet) UpdateContactInfo(ctx context.Context) bool {
return false
}
idents := []string{
fmt.Sprintf("tel:%s", puppet.Phone),
idents := make([]string, 0, 2)
if puppet.Phone != "" {
idents = append(idents, fmt.Sprintf("tel:%s", puppet.Phone))
}
if puppet.ContactID != "" {
idents = append(idents, fmt.Sprintf("gmsg-contact:%s", puppet.ContactID))
@ -312,8 +313,15 @@ func (puppet *Puppet) Sync(ctx context.Context, source *User, contact *gmproto.P
update := false
if contact.ID.Number != "" && puppet.Phone != contact.ID.Number {
oldPhone := puppet.Phone
puppet.Phone = contact.ID.Number
puppet.ContactInfoSet = false
puppet.log = puppet.bridge.ZLog.With().
Str("phone", puppet.Phone).
Str("puppet_id", puppet.ID).
Int("puppet_receiver", puppet.Receiver).
Logger()
puppet.log.Debug().Str("old_phone", oldPhone).Msg("Phone number changed")
update = true
}
if contact.ContactID != puppet.ContactID {

View file

@ -820,10 +820,15 @@ func (user *User) syncHandleEvent(event any) {
}
func (user *User) ResetState() {
ctx := context.TODO()
portals := user.bridge.GetAllPortalsForUser(user.RowID)
user.zlog.Debug().Int("portal_count", len(portals)).Msg("Deleting portals")
for _, portal := range portals {
portal.Delete(context.TODO())
portal.Delete(ctx)
}
err := user.bridge.DB.Puppet.Reset(ctx, user.RowID)
if err != nil {
user.zlog.Err(err).Msg("Failed to reset puppet state")
}
user.PhoneID = ""
go func() {