package models import ( "encoding/json" "time" "github.com/google/uuid" ) // Operator-facing read models for the admin panel's operations pages: mailbox // sync, in-flight sends, dead letters, scheduled jobs, fleet placement, // workspace transfers and insight. Every row here is instance-wide and // carries the organization it belongs to, because the operator reads across // workspaces. // ---- mailbox sync ---- // AdminSyncSearch filters the sync governor page. type AdminSyncSearch struct { // State: all | throttled | backfilling | stalled | pending | complete. State string `form:"state"` Q string `form:"q"` Cursor string `form:"cursor"` Limit int `form:"limit"` } // AdminSyncSummary counts every mailbox that has reported sync state once. type AdminSyncSummary struct { Total int `json:"total"` Throttled int `json:"throttled"` Backfilling int `json:"backfilling"` Stalled int `json:"stalled"` Pending int `json:"pending"` Complete int `json:"complete"` Deferred int `json:"deferred"` } // AdminSyncRow is one mailbox's sync state with its owner attached. type AdminSyncRow struct { EmailID uuid.UUID `json:"email_id"` UserID uuid.UUID `json:"user_id"` Email string `json:"email"` Provider string `json:"provider"` AccountStatus string `json:"account_status"` OrganizationID *uuid.UUID `json:"organization_id,omitempty"` OrganizationName string `json:"organization_name"` WorkerID *uuid.UUID `json:"worker_id,omitempty"` BackfillStatus string `json:"backfill_status"` BackfillSynced int `json:"backfill_synced"` BackfillSince *time.Time `json:"backfill_since,omitempty"` BackfillStartedAt *time.Time `json:"backfill_started_at,omitempty"` BackfillCompletedAt *time.Time `json:"backfill_completed_at,omitempty"` ThrottledUntil *time.Time `json:"throttled_until,omitempty"` ThrottleReason string `json:"throttle_reason"` Deferred int `json:"deferred"` // Stalled is a running backfill whose state has not moved for an hour. Stalled bool `json:"stalled"` LastSyncedAt *time.Time `json:"last_synced_at,omitempty"` UpdatedAt time.Time `json:"updated_at"` } type AdminSyncResult struct { Data []AdminSyncRow `json:"data"` Pagination *Pagination `json:"pagination"` Summary AdminSyncSummary `json:"summary"` } // ---- in-flight sends ---- // AdminInFlightSend is a reserved campaign send no worker result has resolved. type AdminInFlightSend struct { CampaignID uuid.UUID `json:"campaign_id"` CampaignName string `json:"campaign_name"` OrganizationID *uuid.UUID `json:"organization_id,omitempty"` OrganizationName string `json:"organization_name"` ContactID uuid.UUID `json:"contact_id"` ContactEmail string `json:"contact_email"` SequenceID uuid.UUID `json:"sequence_id"` TaskID *uuid.UUID `json:"task_id,omitempty"` TaskStatus string `json:"task_status"` // HasMessageID means the worker did put the mail on the wire and only the // stamp was lost; the reclaimer will stamp it rather than retry. HasMessageID bool `json:"has_message_id"` EmailAccountID *uuid.UUID `json:"email_account_id,omitempty"` MailboxEmail string `json:"mailbox_email"` WorkerID *uuid.UUID `json:"worker_id,omitempty"` DispatchedAt time.Time `json:"dispatched_at"` AgeSeconds int64 `json:"age_seconds"` } type AdminInFlightSummary struct { Total int `json:"total"` // Age buckets, in minutes since dispatch. Under5m int `json:"under_5m"` Under30m int `json:"under_30m"` PastReclaimWindow int `json:"past_reclaim_window"` OldestDispatched *time.Time `json:"oldest_dispatched_at,omitempty"` // ReclaimAfterMinutes is config.CampaignSendReclaimAfterMinutes, so the // page can say when the sweep will pick a row up. ReclaimAfterMinutes int `json:"reclaim_after_minutes"` } type AdminInFlightResult struct { Summary AdminInFlightSummary `json:"summary"` Data []AdminInFlightSend `json:"data"` } // AdminDeadLetterRow is a task dead letter with the workspace it belongs to. type AdminDeadLetterRow struct { TaskDeadLetter OrganizationID *uuid.UUID `json:"organization_id,omitempty"` OrganizationName string `json:"organization_name"` } type AdminDeadLettersResult struct { Data []AdminDeadLetterRow `json:"data"` Pagination *Pagination `json:"pagination"` // Counts by status across the instance, independent of the filter. Pending int `json:"pending"` Replayed int `json:"replayed"` Failed int `json:"failed"` } // AdminTaskFailureRow is one row of task_failures with the task it names. type AdminTaskFailureRow struct { TaskID uuid.UUID `json:"task_id"` TaskType string `json:"task_type"` TaskStatus string `json:"task_status"` Title string `json:"title"` Message string `json:"message"` EmailAccountID uuid.UUID `json:"email_account_id"` MailboxEmail string `json:"mailbox_email"` OrganizationID *uuid.UUID `json:"organization_id,omitempty"` OrganizationName string `json:"organization_name"` OccurredAt time.Time `json:"occurred_at"` } // AdminWebhookEndpointRow is a customer webhook endpoint as the operator sees it. type AdminWebhookEndpointRow struct { ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OrganizationName string `json:"organization_name"` URL string `json:"url"` Description string `json:"description"` Enabled bool `json:"enabled"` EventTypes []string `json:"event_types"` ConsecutiveFailures int `json:"consecutive_failures"` LastSuccessAt *time.Time `json:"last_success_at,omitempty"` LastFailureAt *time.Time `json:"last_failure_at,omitempty"` LastFailureReason string `json:"last_failure_reason"` DeliveriesLast7d int64 `json:"deliveries_last_7d"` FailedLast7d int64 `json:"failed_last_7d"` DropsLast7d int64 `json:"drops_last_7d"` } // AdminWebhookHealth is the instance-wide delivery picture. type AdminWebhookHealth struct { // InFlightStale counts deliveries claimed longer ago than the lease. InFlightStale int64 `json:"in_flight_stale"` PendingDue int64 `json:"pending_due"` DeliveredLast24h int64 `json:"delivered_last_24h"` FailedLast24h int64 `json:"failed_last_24h"` AbandonedLast24h int64 `json:"abandoned_last_24h"` DropsLast7d int64 `json:"drops_last_7d"` LeaseMinutes int `json:"lease_minutes"` // FailingEndpoints lists endpoints with consecutive failures, worst first. FailingEndpoints []AdminWebhookEndpointRow `json:"failing_endpoints"` } // ---- scheduled jobs ---- // ScheduledJobRun is the persisted record of one background loop: what it is, // when it last ran and how it went. One row per job name, shared by every // service that runs loops (backend, consumer). type ScheduledJobRun struct { Name string `json:"name"` Service string `json:"service"` IntervalSeconds int `json:"interval_seconds"` LastStartedAt *time.Time `json:"last_started_at,omitempty"` LastFinishedAt *time.Time `json:"last_finished_at,omitempty"` LastDurationMs int64 `json:"last_duration_ms"` // LastStatus: idle | running | ok | error. LastStatus string `json:"last_status"` LastError string `json:"last_error"` RunCount int64 `json:"run_count"` ErrorCount int64 `json:"error_count"` // RunRequestedAt is set by the panel's "run now"; the owning loop clears it // when it picks the request up. RunRequestedAt *time.Time `json:"run_requested_at,omitempty"` NextRunAt *time.Time `json:"next_run_at,omitempty"` UpdatedAt time.Time `json:"updated_at"` } // ---- fleet ---- // AdminFleetWorkerRow is a worker with its capacity-view row attached. type AdminFleetWorkerRow struct { WorkerID uuid.UUID `json:"worker_id"` Name string `json:"name"` IPAddr string `json:"ip_addr"` Active bool `json:"active"` Region string `json:"region"` HealthState WorkerHealthState `json:"health_state"` Version string `json:"version"` LastSeenAt *time.Time `json:"last_seen_at,omitempty"` Live bool `json:"live"` AccountCount int `json:"account_count"` Tags []string `json:"tags"` LoadScore float64 `json:"load_score"` BaseCapacity float64 `json:"base_capacity"` HealthMultiplier float64 `json:"health_multiplier"` AgeMultiplier float64 `json:"age_multiplier"` EffectiveCapacity float64 `json:"effective_capacity"` // Utilization is load over the worker's operational target; the rebalancer calls a // worker hot above 0.8 and cold below 0.5. Utilization float64 `json:"utilization"` SendsAttempted1h int64 `json:"sends_attempted_1h"` SendsSucceeded1h int64 `json:"sends_succeeded_1h"` BouncesHard1h int64 `json:"bounces_hard_1h"` BouncesSoft1h int64 `json:"bounces_soft_1h"` Complaints1h int64 `json:"complaints_1h"` AuthErrors1h int64 `json:"auth_errors_1h"` } // AdminFleetDecision is one decision_log row: what a control loop did and why. type AdminFleetDecision struct { ID int64 `json:"id"` Kind string `json:"kind"` WorkerID *uuid.UUID `json:"worker_id,omitempty"` WorkerName string `json:"worker_name"` MailboxID *uuid.UUID `json:"mailbox_id,omitempty"` Before json.RawMessage `json:"before,omitempty"` After json.RawMessage `json:"after,omitempty"` Reason string `json:"reason"` TriggeredBy string `json:"triggered_by"` CreatedAt time.Time `json:"created_at"` } // AdminDedicatedAssignment is an active worker-to-organization binding. type AdminDedicatedAssignment struct { ID uuid.UUID `json:"id"` WorkerID uuid.UUID `json:"worker_id"` WorkerName string `json:"worker_name"` WorkerLive bool `json:"worker_live"` OrganizationID uuid.UUID `json:"organization_id"` OrganizationName string `json:"organization_name"` SubscriptionID uuid.UUID `json:"subscription_id"` AssignedAt time.Time `json:"assigned_at"` ReleasedAt *time.Time `json:"released_at,omitempty"` AccountCount int `json:"account_count"` } // ---- workspace transfers ---- // AdminTransferJob is an export or import job with its workspace attached. type AdminTransferJob struct { // Kind: export | import. Kind string `json:"kind"` ID uuid.UUID `json:"id"` OrganizationID uuid.UUID `json:"organization_id"` OrganizationName string `json:"organization_name"` RequestedBy *uuid.UUID `json:"requested_by,omitempty"` RequestedByEmail string `json:"requested_by_email"` Status OrgTransferStatus `json:"status"` Groups []OrgDataGroup `json:"groups"` IncludeSecrets bool `json:"include_secrets"` ProgressPercent int `json:"progress_percent"` ProgressStage string `json:"progress_stage"` ArchiveBytes *int64 `json:"archive_bytes,omitempty"` ErrorMessage *string `json:"error_message,omitempty"` StartedAt *time.Time `json:"started_at,omitempty"` CompletedAt *time.Time `json:"completed_at,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` CreatedAt time.Time `json:"created_at"` } // ---- insight ---- // AdminWarmupAction is one warmup_admin_actions row with both parties named. type AdminWarmupAction struct { ID uuid.UUID `json:"id"` AdminUserID uuid.UUID `json:"admin_user_id"` AdminEmail string `json:"admin_email"` EmailAccountID uuid.UUID `json:"email_account_id"` Email string `json:"email"` Action string `json:"action"` Reason *string `json:"reason,omitempty"` CreatedAt time.Time `json:"created_at"` } // AdminOrgAPIKey is an API key as the operator sees it: never the secret. type AdminOrgAPIKey struct { ID uuid.UUID `json:"id"` Name string `json:"name"` KeyPrefix string `json:"key_prefix"` KeySuffix string `json:"key_suffix"` Status string `json:"status"` Permissions int64 `json:"permissions"` UserID uuid.UUID `json:"user_id"` UserEmail string `json:"user_email"` LastUsedAt *time.Time `json:"last_used_at,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` RevokedAt *time.Time `json:"revoked_at,omitempty"` CreatedAt time.Time `json:"created_at"` RequestsLast7d int64 `json:"requests_last_7d"` } // AdminAcquisitionChannel is signups grouped by UTM source and medium. type AdminAcquisitionChannel struct { Source string `json:"source"` Medium string `json:"medium"` Signups int `json:"signups"` Converted int `json:"converted"` } type AdminAcquisitionReferrer struct { Host string `json:"host"` Signups int `json:"signups"` } // AdminAcquisition is signups by channel over a window, with how many of them // went on to a paid subscription and how many trials are about to end. type AdminAcquisition struct { Days int `json:"days"` Signups int `json:"signups"` WithChannel int `json:"with_channel"` Converted int `json:"converted"` TrialsExpiring7d int `json:"trials_expiring_7d"` Channels []AdminAcquisitionChannel `json:"channels"` Referrers []AdminAcquisitionReferrer `json:"referrers"` }