mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-20 17:43:26 +00:00
157ca4e88f
* chore: bump Go toolchain to 1.26 (TASK-763) Bump Go from 1.25 to 1.26 across all toolchain pins: - go.mod — go 1.25.0 → go 1.26.0 - Dockerfile — golang:1.25-alpine → golang:1.26-alpine - .github/workflows/ci.yml — three setup-go steps (Go, Go-Postgres, E2E jobs) - .github/workflows/release.yml — release pipeline No `toolchain` directive: the repo is pre-launch with no external contributors yet, so we set the floor where we want it (hard requirement). Verified locally before commit: - golangci-lint v2.11.4 builds and runs under Go 1.26.2 (pinned in CI) - golang:1.26-alpine and 1.26.2-alpine images present on Docker Hub - go build ./... clean - go vet ./... clean - go test ./... all pass Parent: PLAN-644 (OSS Repo Hygiene and Launch Polish). * chore: gofmt -w under Go 1.26 (TASK-763) Apply Go 1.26's gofmt to the codebase. ~41 files reformatted, all struct-tag whitespace realignment — no semantic changes. Verified: - gofmt -l ./cmd ./internal returns empty after - go build ./... still clean - go test ./... still passes (run before commit) Bundling the gofmt diff with the toolchain bump in the same PR because the formatting drift is a direct consequence of moving from 1.25 to 1.26; splitting them creates a mandatory two-PR ordering for no value. Parent: PLAN-644. * docs: bump documented Go floor to 1.26 (TASK-763) Match go.mod's hard 1.26.0 requirement in the source-build instructions. Caught by Codex review round 1 on PR #247. - README.md:158 — "Go 1.25+" → "Go 1.26+" - CONTRIBUTING.md:9 — "Go 1.25+" → "Go 1.26+"
112 lines
4.3 KiB
Go
112 lines
4.3 KiB
Go
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"
|
|
ActionPasswordResetByAdmin = "password_reset_by_admin"
|
|
ActionUserDisabled = "user_disabled"
|
|
ActionUserEnabled = "user_enabled"
|
|
ActionAccountDeleted = "account_deleted"
|
|
// 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"
|
|
// 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"`
|
|
CollectionSlug string `json:"collection_slug,omitempty"`
|
|
ActorName string `json:"actor_name,omitempty"`
|
|
}
|
|
|
|
type ActivityListParams struct {
|
|
Action string
|
|
Actor string
|
|
Source string
|
|
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"`
|
|
}
|