feat: add NFT usernames and bot verification (#22)
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>
This commit is contained in:
parent
b0fd3976f1
commit
fff8de783a
169 changed files with 55769 additions and 282 deletions
89
internal/store/verification.go
Normal file
89
internal/store/verification.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue