Persist event data

This commit is contained in:
cte 2025-05-13 23:42:42 -07:00
parent 1fa656a012
commit 93fc57bb0d
8 changed files with 129 additions and 250 deletions

View file

@ -1,5 +1,5 @@
CREATE TABLE "event" (
"id" text PRIMARY KEY NOT NULL,
CREATE TABLE "events" (
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "events_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
"type" text NOT NULL,
"timestamp" bigint NOT NULL,
"properties" json NOT NULL,

View file

@ -1,12 +0,0 @@
CREATE TABLE "organization" (
"id" text PRIMARY KEY NOT NULL,
"stripe_customer_id" text,
"stripe_subscription_id" text,
"stripe_subscription_price_id" text,
"stripe_subscription_status" text,
"stripe_subscription_current_period_end" bigint,
"updated_at" timestamp DEFAULT now() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX "stripe_customer_id_idx" ON "organization" USING btree ("stripe_customer_id");

View file

@ -1,55 +1,47 @@
{
"id": "013accf9-070a-47cd-9b49-1318a1c8a237",
"id": "5ab333f5-a980-4564-a919-3d15608505d0",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.organization": {
"name": "organization",
"public.events": {
"name": "events",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"type": "integer",
"primaryKey": true,
"notNull": true,
"identity": {
"type": "always",
"name": "events_id_seq",
"schema": "public",
"increment": "1",
"startWith": "1",
"minValue": "1",
"maxValue": "2147483647",
"cache": "1",
"cycle": false
}
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"stripe_customer_id": {
"name": "stripe_customer_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_id": {
"name": "stripe_subscription_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_price_id": {
"name": "stripe_subscription_price_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_status": {
"name": "stripe_subscription_status",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_current_period_end": {
"name": "stripe_subscription_current_period_end",
"timestamp": {
"name": "timestamp",
"type": "bigint",
"primaryKey": false,
"notNull": false
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"properties": {
"name": "properties",
"type": "json",
"primaryKey": false,
"notNull": true,
"default": "now()"
"notNull": true
},
"created_at": {
"name": "created_at",
@ -57,25 +49,16 @@
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"stripe_customer_id_idx": {
"name": "stripe_customer_id_idx",
"columns": [
{
"expression": "stripe_customer_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},

View file

@ -1,149 +0,0 @@
{
"id": "37608d29-a721-43ec-91e6-c4a356d23b3e",
"prevId": "013accf9-070a-47cd-9b49-1318a1c8a237",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.event": {
"name": "event",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": true
},
"timestamp": {
"name": "timestamp",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"properties": {
"name": "properties",
"type": "json",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.organization": {
"name": "organization",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"stripe_customer_id": {
"name": "stripe_customer_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_id": {
"name": "stripe_subscription_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_price_id": {
"name": "stripe_subscription_price_id",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_status": {
"name": "stripe_subscription_status",
"type": "text",
"primaryKey": false,
"notNull": false
},
"stripe_subscription_current_period_end": {
"name": "stripe_subscription_current_period_end",
"type": "bigint",
"primaryKey": false,
"notNull": false
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {
"stripe_customer_id_idx": {
"name": "stripe_customer_id_idx",
"columns": [
{
"expression": "stripe_customer_id",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}

View file

@ -5,15 +5,8 @@
{
"idx": 0,
"version": "7",
"when": 1747031111132,
"tag": "0000_misty_leo",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1747203099634,
"tag": "0001_glamorous_thunderbird",
"when": 1747204609794,
"tag": "0000_hesitant_kylun",
"breakpoints": true
}
]

View file

@ -1,6 +1,10 @@
import { auth } from '@clerk/nextjs/server';
import { NextRequest, NextResponse } from 'next/server';
import { eventSchema } from '@/schemas';
import { db } from '@/db';
import { eventsTable } from '@/db/schema';
export async function POST(request: NextRequest) {
const { userId } = await auth();
@ -11,7 +15,14 @@ export async function POST(request: NextRequest) {
);
}
console.log(await request.json());
const payload = await request.json();
const result = eventSchema.safeParse(payload);
return NextResponse.json({ success: true });
if (!result.success) {
return NextResponse.json({ success: false });
}
const [record] = await db.insert(eventsTable).values(result.data).returning();
return NextResponse.json({ success: true, id: record?.id });
}

View file

@ -3,33 +3,33 @@ import {
pgTable,
text,
timestamp,
uniqueIndex,
json,
integer,
} from 'drizzle-orm/pg-core';
export const organizationSchema = pgTable(
'organization',
{
id: text('id').primaryKey(),
stripeCustomerId: text('stripe_customer_id'),
stripeSubscriptionId: text('stripe_subscription_id'),
stripeSubscriptionPriceId: text('stripe_subscription_price_id'),
stripeSubscriptionStatus: text('stripe_subscription_status'),
stripeSubscriptionCurrentPeriodEnd: bigint(
'stripe_subscription_current_period_end',
{ mode: 'number' },
),
updatedAt: timestamp('updated_at', { mode: 'date' })
.defaultNow()
.$onUpdate(() => new Date())
.notNull(),
createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
},
(table) => [uniqueIndex('stripe_customer_id_idx').on(table.stripeCustomerId)],
);
// export const organizationTable = pgTable(
// 'organizations',
// {
// id: integer().primaryKey().generatedAlwaysAsIdentity(),
// stripeCustomerId: text('stripe_customer_id'),
// stripeSubscriptionId: text('stripe_subscription_id'),
// stripeSubscriptionPriceId: text('stripe_subscription_price_id'),
// stripeSubscriptionStatus: text('stripe_subscription_status'),
// stripeSubscriptionCurrentPeriodEnd: bigint(
// 'stripe_subscription_current_period_end',
// { mode: 'number' },
// ),
// updatedAt: timestamp('updated_at', { mode: 'date' })
// .defaultNow()
// .$onUpdate(() => new Date())
// .notNull(),
// createdAt: timestamp('created_at', { mode: 'date' }).defaultNow().notNull(),
// },
// (table) => [uniqueIndex('stripe_customer_id_idx').on(table.stripeCustomerId)],
// );
export const eventSchema = pgTable('event', {
id: text('id').primaryKey(),
export const eventsTable = pgTable('events', {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
type: text('type').notNull(),
timestamp: bigint('timestamp', { mode: 'number' }).notNull(),
properties: json('properties').notNull(),

53
src/schemas/index.ts Normal file
View file

@ -0,0 +1,53 @@
import { z } from 'zod';
export const providerNames = [
'anthropic',
'glama',
'openrouter',
'bedrock',
'vertex',
'openai',
'ollama',
'vscode-lm',
'lmstudio',
'gemini',
'openai-native',
'mistral',
'deepseek',
'unbound',
'requesty',
'human-relay',
'fake-ai',
'xai',
'groq',
'chutes',
'litellm',
] as const;
export const eventSchema = z.discriminatedUnion('type', [
z.object({
type: z.literal('task_created'),
timestamp: z.number(),
properties: z.object({
taskId: z.string(),
provider: z.enum(providerNames),
modelId: z.string(),
prompt: z.string(),
mode: z.string(),
}),
}),
z.object({
type: z.literal('completion'),
timestamp: z.number(),
properties: z.object({
taskId: z.string(),
provider: z.enum(providerNames),
modelId: z.string(),
inputTokens: z.number(),
outputTokens: z.number(),
cost: z.number(),
}),
}),
]);
export type Event = z.infer<typeof eventSchema>;