feat(audit): added audit log for billing line items (#2500)

* feat(audit): added audit log for billing line items

* remove migration

* reran migrations after resolving merge conflict

* ack PR comment
This commit is contained in:
Waleed
2025-12-20 14:10:01 -08:00
committed by GitHub
parent f8678b179a
commit 35a57bfad4
9 changed files with 9110 additions and 45 deletions

View File

@@ -0,0 +1,23 @@
CREATE TYPE "public"."usage_log_category" AS ENUM('model', 'fixed');--> statement-breakpoint
CREATE TYPE "public"."usage_log_source" AS ENUM('workflow', 'wand', 'copilot');--> statement-breakpoint
CREATE TABLE "usage_log" (
"id" text PRIMARY KEY NOT NULL,
"user_id" text NOT NULL,
"category" "usage_log_category" NOT NULL,
"source" "usage_log_source" NOT NULL,
"description" text NOT NULL,
"metadata" jsonb,
"cost" numeric NOT NULL,
"workspace_id" text,
"workflow_id" text,
"execution_id" text,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "usage_log" ADD CONSTRAINT "usage_log_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "usage_log" ADD CONSTRAINT "usage_log_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "usage_log" ADD CONSTRAINT "usage_log_workflow_id_workflow_id_fk" FOREIGN KEY ("workflow_id") REFERENCES "public"."workflow"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "usage_log_user_created_at_idx" ON "usage_log" USING btree ("user_id","created_at");--> statement-breakpoint
CREATE INDEX "usage_log_source_idx" ON "usage_log" USING btree ("source");--> statement-breakpoint
CREATE INDEX "usage_log_workspace_id_idx" ON "usage_log" USING btree ("workspace_id");--> statement-breakpoint
CREATE INDEX "usage_log_workflow_id_idx" ON "usage_log" USING btree ("workflow_id");

File diff suppressed because it is too large Load Diff

View File

@@ -890,6 +890,13 @@
"when": 1766209394504,
"tag": "0127_flimsy_sister_grimm",
"breakpoints": true
},
{
"idx": 128,
"version": "7",
"when": 1766266581373,
"tag": "0128_swift_terrax",
"breakpoints": true
}
]
}

View File

@@ -1664,3 +1664,51 @@ export const ssoProvider = pgTable(
organizationIdIdx: index('sso_provider_organization_id_idx').on(table.organizationId),
})
)
// Usage logging for tracking individual billable operations
export const usageLogCategoryEnum = pgEnum('usage_log_category', ['model', 'fixed'])
export const usageLogSourceEnum = pgEnum('usage_log_source', ['workflow', 'wand', 'copilot'])
export const usageLog = pgTable(
'usage_log',
{
id: text('id').primaryKey(),
userId: text('user_id')
.notNull()
.references(() => user.id, { onDelete: 'cascade' }),
// Charge category: 'model' (token-based) or 'fixed' (flat fee)
category: usageLogCategoryEnum('category').notNull(),
// What generated this charge: 'workflow', 'wand', 'copilot'
source: usageLogSourceEnum('source').notNull(),
// For model charges: model name (e.g., 'gpt-4o', 'claude-4.5-opus')
// For fixed charges: charge type (e.g., 'execution_fee', 'search_query')
description: text('description').notNull(),
// Category-specific metadata (e.g., tokens for 'model' category)
metadata: jsonb('metadata'),
// Cost in USD
cost: decimal('cost').notNull(),
// Optional context references
workspaceId: text('workspace_id').references(() => workspace.id, { onDelete: 'set null' }),
workflowId: text('workflow_id').references(() => workflow.id, { onDelete: 'set null' }),
executionId: text('execution_id'),
// Timestamp
createdAt: timestamp('created_at').notNull().defaultNow(),
},
(table) => ({
// Index for querying user's usage history (most common query)
userCreatedAtIdx: index('usage_log_user_created_at_idx').on(table.userId, table.createdAt),
// Index for filtering by source
sourceIdx: index('usage_log_source_idx').on(table.source),
// Index for workspace-specific queries
workspaceIdIdx: index('usage_log_workspace_id_idx').on(table.workspaceId),
// Index for workflow-specific queries
workflowIdIdx: index('usage_log_workflow_id_idx').on(table.workflowId),
})
)