Files
creator-hub/internal/account/deletion.go
T
rogee c567080248 refactor(api): 删除运营链路与 runtime_use_lease
删除任务链(drafts/confirmations/tasks/attempts)、creator 运营链(策略/事件/操作/会话/消息/监听器/素材/关系)、runtime_use_lease 机制及其 API/UI;taskstate 包与对应测试一并移除。
2026-09-28 17:14:00 +08:00

112 lines
3.8 KiB
Go

package account
import (
"context"
"errors"
)
// CheckAccountDeletion verifies that deleting an account will not interrupt an active run.
func (s *Store) CheckAccountDeletion(ctx context.Context, accountID string) error {
if !idPattern.MatchString(accountID) {
return ErrInvalid
}
var exists bool
if err := s.db.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM social_account WHERE id = $1)`, accountID).Scan(&exists); err != nil {
return errors.New("check account deletion state")
}
if !exists {
return ErrNotFound
}
var active bool
if err := s.db.QueryRowContext(ctx, `
SELECT EXISTS (
SELECT 1 FROM runtime_instance
WHERE account_id = $1 AND released_at IS NULL
)`, accountID).Scan(&active); err != nil {
return errors.New("check account deletion state")
}
if active {
return ErrConflict
}
return nil
}
// DeleteAccountData removes Phase A data while keeping the account row for the final deletion step.
func (s *Store) DeleteAccountData(ctx context.Context, accountID string) error {
if !idPattern.MatchString(accountID) {
return ErrInvalid
}
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return errors.New("begin account deletion transaction")
}
defer tx.Rollback()
var lockedID string
if err := tx.QueryRowContext(ctx, `SELECT id FROM social_account WHERE id = $1 FOR UPDATE`, accountID).Scan(&lockedID); err != nil {
return rowError(err)
}
var active bool
if err := tx.QueryRowContext(ctx, `
SELECT EXISTS (
SELECT 1 FROM runtime_instance
WHERE account_id = $1 AND released_at IS NULL
)`, accountID).Scan(&active); err != nil {
return errors.New("check account deletion state")
}
if active {
return ErrConflict
}
if _, err := tx.ExecContext(ctx, `SELECT set_config('creatorhub.account_deletion', 'on', true)`); err != nil {
return errors.New("enable account deletion audit cleanup")
}
if _, err := tx.ExecContext(ctx, `
DELETE FROM audit_event
WHERE account_id = $1
OR browser_env_alias IN (
SELECT browser_env_alias FROM environment_binding WHERE account_id = $1
)
OR runtime_instance_id IN (SELECT id FROM runtime_instance WHERE account_id = $1)`, accountID); err != nil {
return errors.New("delete account audit data")
}
if _, err := tx.ExecContext(ctx, `DELETE FROM runtime_instance WHERE account_id = $1`, accountID); err != nil {
return errors.New("delete account runtime records")
}
return commit(tx)
}
// DeleteAccount removes the account row and its external cookie credential.
// Call DeleteAccountData and delete account-owned creator/environment data first.
func (s *Store) DeleteAccount(ctx context.Context, accountID string, credentials CredentialBridge) error {
if !idPattern.MatchString(accountID) || credentials == nil {
return ErrInvalid
}
tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return errors.New("begin account record deletion")
}
defer tx.Rollback()
var reference CredentialReference
var key string
if err := tx.QueryRowContext(ctx, `
SELECT credential.id, credential.provider, credential.reference_key
FROM social_account account
JOIN credential_reference credential ON credential.id = account.credential_reference_id
WHERE account.id = $1
FOR UPDATE`, accountID).Scan(&reference.ID, &reference.Provider, &key); err != nil {
return rowError(err)
}
if _, err := tx.ExecContext(ctx, `DELETE FROM social_account WHERE id = $1`, accountID); err != nil {
return publicDatabaseError(err)
}
if _, err := tx.ExecContext(ctx, `DELETE FROM credential_reference WHERE id = $1`, reference.ID); err != nil {
return publicDatabaseError(err)
}
if err := tx.Commit(); err != nil {
return errors.New("commit account record deletion")
}
if err := credentials.Delete(context.WithoutCancel(ctx), reference, key); err != nil {
return errors.Join(errors.New("delete account credential"), err)
}
return nil
}