- activity_log table (sqlite + pg, structurally identical; schema-parity covers it). Plain-text columns, no FKs — an append-only trail that must outlive the projects/users it references, so target_label snapshots a human-readable name at write time. - ActivityRepository: record() (fire-and-forget, never breaks the caller) + list() (org-scoped, actor/action filters, keyset pagination) + listActors(). - Recording wired into the mutations worth tracking: project create/archive/restore/domain, audit start, team user create/remove/ password-reset, invitation sent. - getActivityLog / getActivityActors server functions (owner/admin gated) + Settings → Activity tab (ActivityLogView: filter by user & action, load more). - Migration: drizzle/0045_*, drizzle-pg/0023_*. The pipeline does not run migrations — see docs/SELF_HOSTING_TEAM_MODE.md step 5 for the one-time `drizzle-kit migrate` on the server. Writes fail silently until the table exists. tsc / oxlint / knip clean. New ActivityRepository.test.ts (4) + schema-parity picks up the new table; suite otherwise unchanged (pre-existing samSkills CRLF failure only). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
16 lines
617 B
SQL
16 lines
617 B
SQL
CREATE TABLE "activity_log" (
|
|
"id" text PRIMARY KEY NOT NULL,
|
|
"organization_id" text NOT NULL,
|
|
"actor_user_id" text NOT NULL,
|
|
"actor_email" text NOT NULL,
|
|
"action" text NOT NULL,
|
|
"target_type" text,
|
|
"target_id" text,
|
|
"target_label" text,
|
|
"metadata" text,
|
|
"ip_address" text,
|
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE INDEX "activity_log_org_created_at_idx" ON "activity_log" USING btree ("organization_id","created_at");--> statement-breakpoint
|
|
CREATE INDEX "activity_log_org_actor_idx" ON "activity_log" USING btree ("organization_id","actor_user_id"); |