package models import "time" // Item-level actions (existing) var ValidActions = []string{ "created", "updated", "archived", "restored", "moved", "read", "searched", } // Audit action constants for auth/admin events const ( ActionLogin = "login" ActionLoginFailed = "login_failed" ActionLogout = "logout" ActionBootstrap = "bootstrap" ActionRegister = "register" ActionPasswordChanged = "password_changed" ActionPasswordReset = "password_reset" ActionTokenCreated = "token_created" ActionTokenRevoked = "token_revoked" ActionTokenRotated = "token_rotated" ActionTOTPEnabled = "totp_enabled" ActionTOTPDisabled = "totp_disabled" ActionMemberInvited = "member_invited" ActionMemberRemoved = "member_removed" ActionRoleChanged = "role_changed" ActionSettingsChanged = "settings_changed" ActionOAuthLogin = "oauth_login" ActionOAuthLoginFailed = "oauth_login_failed" ActionPlanChanged = "plan_changed" // ActionPlanOverridesChanged is logged when an admin updates a // user's plan_overrides JSON via the admin user-detail page. // Surfaces per-user storage / workspace / API-token quota // overrides in the audit feed so operators can correlate a // mysteriously-allowed upload with the override that enabled it. ActionPlanOverridesChanged = "plan_overrides_changed" ActionPasswordResetByAdmin = "password_reset_by_admin" ActionUserDisabled = "user_disabled" ActionUserEnabled = "user_enabled" ActionAccountDeleted = "account_deleted" // ActionEmailVerified is logged when a user confirms their email address // via a verification link (POST /auth/verify-email). PLAN-1933 / TASK-1936. ActionEmailVerified = "email_verified" // ActionEmailVerifiedByAdmin is logged when an admin force-verifies a // user's email from the admin console (DR-7). PLAN-1933 / TASK-1936. ActionEmailVerifiedByAdmin = "email_verified_by_admin" // ActionSessionIPChanged is logged when a session presents a different // client IP than the one recorded at creation. We don't strict-check IP // by default (that breaks legitimate geo shifts — VPN toggle, mobile // roaming) but surface the change to the audit log for detection. In // deployments configured with PAD_IP_CHANGE_ENFORCE=strict the middleware // additionally rejects the request. ActionSessionIPChanged = "session_ip_changed" // ActionSessionUAChanged is logged when a session presents a different // User-Agent hash than the one recorded at creation. Like the IP signal // this is surfaced to the audit log for detection; in deployments // configured with PAD_IP_CHANGE_ENFORCE=strict the middleware additionally // revokes the session and rejects the request. The UA hash is stable for // the life of a real session (a browser doesn't rewrite its own UA mid- // session), so a mismatch is a stronger theft signal than an IP change — // which is precisely why UA enforcement carries fewer false positives than // IP enforcement. This audit row is only emitted in strict mode; log-only // mode keeps the historical slog-only behavior to avoid changing the audit // feed for existing self-host users. ActionSessionUAChanged = "session_ua_changed" // ActionStripeEventUnmarked is logged when /admin/stripe-event-unmark // rolls back a row from stripe_processed_events (TASK-736). The // endpoint intentionally reopens Stripe retry windows, so a persisted // audit trail is required — a compromised cloud_secret could otherwise // spam unmarks invisible to the admin /audit-log UI. ActionStripeEventUnmarked = "stripe_event_unmarked" // ActionPaymentFailedEmailSent is logged when the sidecar triggers the // /admin/payment-failed endpoint and pad dispatches a failed-payment // notification to the user. Audit trail exists so operators can prove // a customer was notified before a dunning-related plan change. ActionPaymentFailedEmailSent = "payment_failed_email_sent" ) type Activity struct { ID string `json:"id"` WorkspaceID string `json:"workspace_id,omitempty"` DocumentID string `json:"document_id,omitempty"` Action string `json:"action"` Actor string `json:"actor"` Source string `json:"source"` Metadata string `json:"metadata,omitempty"` // JSON UserID string `json:"user_id,omitempty"` IPAddress string `json:"ip_address,omitempty"` UserAgent string `json:"user_agent,omitempty"` CreatedAt time.Time `json:"created_at"` // Enrichment fields — populated by handlers, not stored in DB ItemTitle string `json:"item_title,omitempty"` ItemSlug string `json:"item_slug,omitempty"` ItemRef string `json:"item_ref,omitempty"` // e.g. "BUG-1748" — computed from the referenced item CollectionSlug string `json:"collection_slug,omitempty"` ActorName string `json:"actor_name,omitempty"` } type ActivityListParams struct { Action string Actor string Source string // Since, when non-zero, restricts results to activity created on or // after this instant (a.created_at >= Since). Applied in the SQL query // so LIMIT counts post-filter rows. Used by `pad project activity // --since` and the pad_project.activity MCP action. Since time.Time Limit int Offset int } // AuditLogParams are query parameters for the audit log endpoint. type AuditLogParams struct { Action string Actor string WorkspaceID string Days int Limit int Offset int } // TimelineEntry represents a single entry in the unified item timeline. // It wraps one of: a comment, an activity, or a version. type TimelineEntry struct { ID string `json:"id"` Kind string `json:"kind"` // "comment", "activity", "version" CreatedAt time.Time `json:"created_at"` Actor string `json:"actor"` ActorName string `json:"actor_name,omitempty"` Source string `json:"source"` Comment *Comment `json:"comment,omitempty"` Activity *Activity `json:"activity,omitempty"` Version *Version `json:"version,omitempty"` } // TimelineResponse is the paginated response from the timeline endpoint. type TimelineResponse struct { Entries []TimelineEntry `json:"entries"` HasMore bool `json:"has_more"` }