···3030 return &repo, nil
3131}
32323333-func (s *Server) getRepoActorByEmail(ctx context.Context, email string) (*models.RepoActor, error) {
3333+func (s *Server) getRepoActor(ctx context.Context, where, arg string) (*models.RepoActor, error) {
3434 var repo models.RepoActor
3535- // Use explicit column selection to ensure proper mapping to embedded structs.
3635 if err := s.db.Raw(ctx, `
3737- SELECT
3838- r.did, r.created_at, r.email, r.email_confirmed_at, r.email_verification_code,
3939- r.email_verification_code_expires_at, r.email_update_code, r.email_update_code_expires_at,
4040- r.password_reset_code, r.password_reset_code_expires_at, r.plc_operation_code,
4141- r.plc_operation_code_expires_at, r.account_delete_code, r.account_delete_code_expires_at,
4242- r.password, r.auth_public_key, r.signing_public_key, r.credential_id, r.compat_mode,
4343- r.rev, r.root, r.preferences, r.deactivated,
4444- a.handle
3636+ SELECT r.*, a.handle
4537 FROM repos r
4638 LEFT JOIN actors a ON r.did = a.did
4747- WHERE r.email = ?
4848- `, nil, email).Scan(&repo).Error; err != nil {
3939+ WHERE `+where, nil, arg).Scan(&repo).Error; err != nil {
4940 return nil, err
5041 }
5142 if repo.Repo.Did == "" {
···5445 return &repo, nil
5546}
56474848+func (s *Server) getRepoActorByEmail(ctx context.Context, email string) (*models.RepoActor, error) {
4949+ return s.getRepoActor(ctx, "r.email = ?", email)
5050+}
5151+5752func (s *Server) getRepoActorByDid(ctx context.Context, did string) (*models.RepoActor, error) {
5858- var repo models.RepoActor
5959- // Use explicit column selection to ensure proper mapping to embedded structs.
6060- // The r.*, a.* pattern can cause issues with GORM's Scan when structs have
6161- // overlapping field names (both Repo and Actor have "Did").
6262- if err := s.db.Raw(ctx, `
6363- SELECT
6464- r.did, r.created_at, r.email, r.email_confirmed_at, r.email_verification_code,
6565- r.email_verification_code_expires_at, r.email_update_code, r.email_update_code_expires_at,
6666- r.password_reset_code, r.password_reset_code_expires_at, r.plc_operation_code,
6767- r.plc_operation_code_expires_at, r.account_delete_code, r.account_delete_code_expires_at,
6868- r.password, r.auth_public_key, r.signing_public_key, r.credential_id, r.compat_mode,
6969- r.rev, r.root, r.preferences, r.deactivated,
7070- a.handle
7171- FROM repos r
7272- LEFT JOIN actors a ON r.did = a.did
7373- WHERE r.did = ?
7474- `, nil, did).Scan(&repo).Error; err != nil {
7575- return nil, err
7676- }
7777- if repo.Repo.Did == "" {
7878- return nil, gorm.ErrRecordNotFound
7979- }
8080- return &repo, nil
5353+ return s.getRepoActor(ctx, "r.did = ?", did)
8154}
82558356func (s *Server) getRepoActorByIdentifier(ctx context.Context, identifier string) (*models.RepoActor, error) {
8484- var repo models.RepoActor
8585- var err error
8686- if strings.HasPrefix(identifier, "did:") {
8787- err = s.db.Raw(ctx, `
8888- SELECT
8989- r.did, r.created_at, r.email, r.email_confirmed_at, r.email_verification_code,
9090- r.email_verification_code_expires_at, r.email_update_code, r.email_update_code_expires_at,
9191- r.password_reset_code, r.password_reset_code_expires_at, r.plc_operation_code,
9292- r.plc_operation_code_expires_at, r.account_delete_code, r.account_delete_code_expires_at,
9393- r.password, r.auth_public_key, r.signing_public_key, r.credential_id, r.compat_mode,
9494- r.rev, r.root, r.preferences, r.deactivated,
9595- a.handle
9696- FROM repos r
9797- LEFT JOIN actors a ON r.did = a.did
9898- WHERE r.did = ?
9999- `, nil, identifier).Scan(&repo).Error
100100- } else if strings.Contains(identifier, "@") {
101101- err = s.db.Raw(ctx, `
102102- SELECT
103103- r.did, r.created_at, r.email, r.email_confirmed_at, r.email_verification_code,
104104- r.email_verification_code_expires_at, r.email_update_code, r.email_update_code_expires_at,
105105- r.password_reset_code, r.password_reset_code_expires_at, r.plc_operation_code,
106106- r.plc_operation_code_expires_at, r.account_delete_code, r.account_delete_code_expires_at,
107107- r.password, r.auth_public_key, r.signing_public_key, r.credential_id, r.compat_mode,
108108- r.rev, r.root, r.preferences, r.deactivated,
109109- a.handle
110110- FROM repos r
111111- LEFT JOIN actors a ON r.did = a.did
112112- WHERE r.email = ?
113113- `, nil, identifier).Scan(&repo).Error
114114- } else {
115115- err = s.db.Raw(ctx, `
116116- SELECT
117117- r.did, r.created_at, r.email, r.email_confirmed_at, r.email_verification_code,
118118- r.email_verification_code_expires_at, r.email_update_code, r.email_update_code_expires_at,
119119- r.password_reset_code, r.password_reset_code_expires_at, r.plc_operation_code,
120120- r.plc_operation_code_expires_at, r.account_delete_code, r.account_delete_code_expires_at,
121121- r.password, r.auth_public_key, r.signing_public_key, r.credential_id, r.compat_mode,
122122- r.rev, r.root, r.preferences, r.deactivated,
123123- a.handle
124124- FROM repos r
125125- LEFT JOIN actors a ON r.did = a.did
126126- WHERE a.handle = ?
127127- `, nil, identifier).Scan(&repo).Error
5757+ switch {
5858+ case strings.HasPrefix(identifier, "did:"):
5959+ return s.getRepoActorByDid(ctx, identifier)
6060+ case strings.Contains(identifier, "@"):
6161+ return s.getRepoActor(ctx, "r.email = ?", identifier)
6262+ default:
6363+ return s.getRepoActor(ctx, "a.handle = ?", identifier)
12864 }
129129- if err != nil {
130130- return nil, err
131131- }
132132- if repo.Repo.Did == "" {
133133- return nil, gorm.ErrRecordNotFound
134134- }
135135- return &repo, nil
13665}
+5-1
server/handle_server_delete_account.go
···7171}
72727373// ---------------------------------------------------------------------------
7474-// com.atproto.server.deleteAccount — unsupported
7474+// com.atproto.server.requestAccountDelete/deleteAccount — unsupported
7575// ---------------------------------------------------------------------------
7676+7777+func (s *Server) handleServerRequestAccountDelete(w http.ResponseWriter, r *http.Request) {
7878+ helpers.InputError(w, new("Account deletion not supported here. Login on Vow and delete the account there."))
7979+}
76807781func (s *Server) handleServerDeleteAccount(w http.ResponseWriter, r *http.Request) {
7882 helpers.InputError(w, new("Account deletion not supported here. Login on Vow and delete the account there."))
-53
server/handle_server_request_account_delete.go
···11-package server
22-33-import (
44- "fmt"
55- "net/http"
66- "time"
77-88- "pkg.rbrt.fr/vow/internal/helpers"
99- "pkg.rbrt.fr/vow/models"
1010-)
1111-1212-func (s *Server) handleServerRequestAccountDelete(w http.ResponseWriter, r *http.Request) {
1313- ctx := r.Context()
1414- logger := s.logger.With("name", "handleServerRequestAccountDelete")
1515-1616- urepo, _ := getContextValue[*models.RepoActor](r, contextKeyRepo)
1717-1818- token := fmt.Sprintf("%s-%s", helpers.RandomVarchar(5), helpers.RandomVarchar(5))
1919- expiresAt := time.Now().UTC().Add(15 * time.Minute)
2020-2121- if err := s.db.Exec(ctx, "UPDATE repos SET account_delete_code = ?, account_delete_code_expires_at = ? WHERE did = ?", nil, token, expiresAt, urepo.Repo.Did).Error; err != nil {
2222- logger.Error("error setting deletion token", "error", err)
2323- helpers.ServerError(w, nil)
2424- return
2525- }
2626-2727- if urepo.Email != "" {
2828- if err := s.sendAccountDeleteEmail(urepo.Email, urepo.Handle, token); err != nil {
2929- logger.Error("error sending account deletion email", "error", err)
3030- }
3131- }
3232-3333- w.WriteHeader(http.StatusOK)
3434-}
3535-3636-func (s *Server) sendAccountDeleteEmail(email, handle, token string) error {
3737- if s.mail == nil {
3838- return nil
3939- }
4040-4141- s.mailLk.Lock()
4242- defer s.mailLk.Unlock()
4343-4444- s.mail.To(email)
4545- s.mail.Subject("Account Deletion Request for " + s.config.Hostname)
4646- s.mail.Plain().Set(fmt.Sprintf("Hello %s. Your account deletion code is %s. This code will expire in fifteen minutes. If you did not request this, please ignore this email.", handle, token))
4747-4848- if err := s.mail.Send(); err != nil {
4949- return err
5050- }
5151-5252- return nil
5353-}