Implements collectible usernames, official verification workflows, and third-party bot verification after maintainer protocol and migration review. The composite activity/moderation rating remains an admin-only read model; Telegram Stars Rating wire fields stay unset pending a dedicated official-semantics implementation. Reviewed-Head: 2796345775ea0f908fb7734601e5e1dee4b653b9 Original-Head: fa082b892fd5180c9c9bc53c81c21cf5d250a75b Co-authored-by: Egor Egorov <business.egor.sg@gmail.com>
89 lines
5.8 KiB
Go
89 lines
5.8 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
// VerificationStore owns official verification applications, their immutable
|
|
// history and the applicant-notification outbox.
|
|
//
|
|
// Every mutation is expected to be atomic with the history row it produces, and
|
|
// every status change is guarded by the caller-supplied version: two reviewers
|
|
// deciding the same application concurrently must produce exactly one decision
|
|
// and one notification.
|
|
type VerificationStore interface {
|
|
// CreateVerificationDraft opens a draft for the applicant/target pair. An
|
|
// active application on the target reports domain.ErrVerificationApplicationExists;
|
|
// an existing draft of the same applicant is returned with created=false so
|
|
// the bot dialog can resume it.
|
|
CreateVerificationDraft(ctx context.Context, req domain.SubmitVerificationApplicationRequest) (app domain.VerificationApplication, created bool, err error)
|
|
// SaveVerificationDraft rewrites the applicant-supplied payload of a draft.
|
|
// The application must be in draft and at the given version.
|
|
SaveVerificationDraft(ctx context.Context, applicationID int64, version int64, draft domain.VerificationDraftInput) (domain.VerificationApplication, error)
|
|
// SubmitVerificationApplication moves a draft to submitted and stamps
|
|
// submitted_at.
|
|
SubmitVerificationApplication(ctx context.Context, applicationID int64, version int64) (domain.VerificationApplication, error)
|
|
// CancelVerificationApplication withdraws an active application on the
|
|
// applicant's behalf.
|
|
CancelVerificationApplication(ctx context.Context, applicationID int64, version int64, reason string) (domain.VerificationApplication, error)
|
|
// ClaimVerificationApplication assigns a reviewer and moves the application to
|
|
// in_review.
|
|
ClaimVerificationApplication(ctx context.Context, decision domain.VerificationDecision) (domain.VerificationApplication, error)
|
|
// DecideVerificationApplication records an approval or a rejection.
|
|
//
|
|
// approve=true is the only path that flips the platform flag, and the store
|
|
// does it in the same transaction as the status change through the supplied
|
|
// applyVerified callback, so the invariant "approved implies target verified"
|
|
// cannot be broken by a crash between two writes. The callback receives the
|
|
// application as it will be stored and must set the flag on the target peer.
|
|
//
|
|
// The notification outbox row is written in the same transaction and is unique
|
|
// per (application, kind), which makes a repeated approve a no-op that returns
|
|
// changed=false instead of notifying twice.
|
|
DecideVerificationApplication(ctx context.Context, decision domain.VerificationDecision, approve bool, applyVerified func(ctx context.Context, app domain.VerificationApplication) error) (app domain.VerificationApplication, changed bool, err error)
|
|
// RevokeVerification clears the platform flag of a previously approved target
|
|
// through the same callback discipline, appends a revoked event to the newest
|
|
// approved application for that target, and enqueues the applicant
|
|
// notification. The application itself stays approved: it is history.
|
|
RevokeVerification(ctx context.Context, req domain.VerificationRevocation, clearVerified func(ctx context.Context, target domain.Peer) error) (app domain.VerificationApplication, changed bool, err error)
|
|
// VerificationApplication reads one application by id.
|
|
VerificationApplication(ctx context.Context, applicationID int64) (domain.VerificationApplication, error)
|
|
// ActiveVerificationApplicationForTarget returns the live application
|
|
// occupying a target, if any.
|
|
ActiveVerificationApplicationForTarget(ctx context.Context, target domain.VerificationTargetType, targetID int64) (domain.VerificationApplication, error)
|
|
// VerificationDraftForApplicant returns the applicant's open draft, if any.
|
|
VerificationDraftForApplicant(ctx context.Context, applicantUserID int64) (domain.VerificationApplication, error)
|
|
// ListVerificationApplications is the review-queue query with keyset paging.
|
|
ListVerificationApplications(ctx context.Context, filter domain.VerificationApplicationFilter) ([]domain.VerificationApplication, error)
|
|
// VerificationApplicationsForApplicant returns the applicant's own history,
|
|
// newest first, for the bot's /status command.
|
|
VerificationApplicationsForApplicant(ctx context.Context, applicantUserID int64, limit int) ([]domain.VerificationApplication, error)
|
|
// VerificationStatusCounts is the queue summary.
|
|
VerificationStatusCounts(ctx context.Context) (domain.VerificationStatusCounts, error)
|
|
// VerificationApplicationEvents returns the immutable history, newest first.
|
|
VerificationApplicationEvents(ctx context.Context, applicationID int64, limit int) ([]domain.VerificationApplicationEvent, error)
|
|
// LastVerificationRejection returns the newest rejected application for the
|
|
// applicant/target pair, which is what the re-application cooldown is measured
|
|
// from.
|
|
LastVerificationRejection(ctx context.Context, applicantUserID int64, target domain.VerificationTargetType, targetID int64) (domain.VerificationApplication, error)
|
|
// PendingVerificationNotifications returns undelivered outbox rows, oldest
|
|
// first, for the delivery worker.
|
|
PendingVerificationNotifications(ctx context.Context, limit int) ([]VerificationNotification, error)
|
|
// MarkVerificationNotificationDelivered closes an outbox row.
|
|
MarkVerificationNotificationDelivered(ctx context.Context, id int64) error
|
|
// MarkVerificationNotificationFailed records a delivery attempt so a poisoned
|
|
// row cannot spin forever without a trace.
|
|
MarkVerificationNotificationFailed(ctx context.Context, id int64, reason string) error
|
|
}
|
|
|
|
// VerificationNotification is one queued applicant notification.
|
|
type VerificationNotification struct {
|
|
ID int64
|
|
ApplicationID int64
|
|
RecipientUserID int64
|
|
Kind string
|
|
Attempts int
|
|
Application domain.VerificationApplication
|
|
}
|