diff --git a/drizzle/0016_normal_basilisk.sql b/drizzle/0016_normal_basilisk.sql new file mode 100644 index 0000000..ba773de --- /dev/null +++ b/drizzle/0016_normal_basilisk.sql @@ -0,0 +1,2 @@ +ALTER TABLE "users" ALTER COLUMN "spending_limit_usd" SET DEFAULT '3';--> statement-breakpoint +UPDATE "users" SET "spending_limit_usd" = '3' WHERE "spending_limit_usd" = '4';--> statement-breakpoint diff --git a/drizzle/meta/0016_snapshot.json b/drizzle/meta/0016_snapshot.json new file mode 100644 index 0000000..4438eaa --- /dev/null +++ b/drizzle/meta/0016_snapshot.json @@ -0,0 +1,709 @@ +{ + "id": "a2ed7df9-ba4f-4872-8bf2-21cacc8e6fee", + "prevId": "f8d248c7-bdbd-465d-bf51-b1c305fe37fb", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.api_keys": { + "name": "api_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "is_unlimited": { + "name": "is_unlimited", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "api_keys_user_id_idx": { + "name": "api_keys_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_keys_key_revoked_idx": { + "name": "api_keys_key_revoked_idx", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "revoked_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_keys_user_id_users_id_fk": { + "name": "api_keys_user_id_users_id_fk", + "tableFrom": "api_keys", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "api_keys_key_unique": { + "name": "api_keys_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pending_charges": { + "name": "pending_charges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "estimated_cost": { + "name": "estimated_cost", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pending_charges_user_created_idx": { + "name": "pending_charges_user_created_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pending_charges_user_id_users_id_fk": { + "name": "pending_charges_user_id_users_id_fk", + "tableFrom": "pending_charges", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.request_logs": { + "name": "request_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "api_key_id": { + "name": "api_key_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "slack_id": { + "name": "slack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prompt_tokens": { + "name": "prompt_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "completion_tokens": { + "name": "completion_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "total_tokens": { + "name": "total_tokens", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "request": { + "name": "request", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "response": { + "name": "response", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "headers": { + "name": "headers", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "ip": { + "name": "ip", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timestamp": { + "name": "timestamp", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "cost": { + "name": "cost", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + } + }, + "indexes": { + "request_logs_user_timestamp_cost_idx": { + "name": "request_logs_user_timestamp_cost_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "timestamp", + "isExpression": false, + "asc": false, + "nulls": "last" + }, + { + "expression": "cost", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_logs_apikey_timestamp_idx": { + "name": "request_logs_apikey_timestamp_idx", + "columns": [ + { + "expression": "api_key_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "timestamp", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_logs_slack_timestamp_idx": { + "name": "request_logs_slack_timestamp_idx", + "columns": [ + { + "expression": "slack_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "timestamp", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_logs_model_idx": { + "name": "request_logs_model_idx", + "columns": [ + { + "expression": "model", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "request_logs_user_id_idx": { + "name": "request_logs_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "request_logs_api_key_id_api_keys_id_fk": { + "name": "request_logs_api_key_id_api_keys_id_fk", + "tableFrom": "request_logs", + "tableTo": "api_keys", + "columnsFrom": [ + "api_key_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "request_logs_user_id_users_id_fk": { + "name": "request_logs_user_id_users_id_fk", + "tableFrom": "request_logs", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.sessions": { + "name": "sessions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "sessions_user_id_idx": { + "name": "sessions_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "sessions_expires_at_idx": { + "name": "sessions_expires_at_idx", + "columns": [ + { + "expression": "expires_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "sessions_user_id_users_id_fk": { + "name": "sessions_user_id_users_id_fk", + "tableFrom": "sessions", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "sessions_token_unique": { + "name": "sessions_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "slack_id": { + "name": "slack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "spending_limit_usd": { + "name": "spending_limit_usd", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": false, + "default": "'3'" + }, + "openrouter_key": { + "name": "openrouter_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "openrouter_key_hash": { + "name": "openrouter_key_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "openrouter_key_limit": { + "name": "openrouter_key_limit", + "type": "numeric(10, 8)", + "primaryKey": false, + "notNull": false + }, + "is_idv_verified": { + "name": "is_idv_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "skip_idv": { + "name": "skip_idv", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_banned": { + "name": "is_banned", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "agent_banner_dismissed_at": { + "name": "agent_banner_dismissed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "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": { + "users_slack_id_idx": { + "name": "users_slack_id_idx", + "columns": [ + { + "expression": "slack_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "users_idv_verified_idx": { + "name": "users_idv_verified_idx", + "columns": [ + { + "expression": "is_idv_verified", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_slack_id_unique": { + "name": "users_slack_id_unique", + "nullsNotDistinct": false, + "columns": [ + "slack_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 7f81517..ff191f2 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -113,6 +113,13 @@ "when": 1778009508013, "tag": "0015_regular_winter_soldier", "breakpoints": true + }, + { + "idx": 16, + "version": "7", + "when": 1784798354917, + "tag": "0016_normal_basilisk", + "breakpoints": true } ] -} +} \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 386684b..0f8a092 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -22,7 +22,7 @@ export const users = pgTable( spendingLimitUsd: numeric("spending_limit_usd", { precision: 10, scale: 8, - }).default("4"), + }).default("3"), openrouterKey: text("openrouter_key"), openrouterKeyHash: text("openrouter_key_hash"), openrouterKeyLimit: numeric("openrouter_key_limit", { diff --git a/src/lib/openrouter-keys.ts b/src/lib/openrouter-keys.ts index 879f371..31bded2 100644 --- a/src/lib/openrouter-keys.ts +++ b/src/lib/openrouter-keys.ts @@ -57,7 +57,7 @@ async function updateOpenrouterKeyLimit(hash: string, limit: number) { */ export async function ensureOpenrouterKey(user: User): Promise { return Sentry.startSpan({ name: "openrouter.ensureKey" }, async () => { - const desiredLimit = parseFloat(user.spendingLimitUsd || "4"); + const desiredLimit = parseFloat(user.spendingLimitUsd || "3"); if (!user.openrouterKey || !user.openrouterKeyHash) { const created = await createOpenrouterKey(user, desiredLimit); diff --git a/src/middleware/limits.ts b/src/middleware/limits.ts index 50ce9f7..3fd2533 100644 --- a/src/middleware/limits.ts +++ b/src/middleware/limits.ts @@ -65,7 +65,7 @@ export async function checkSpendingLimit(c: Ctx, next: Next) { if (apiKey?.isUnlimited) return next(); const user = c.get("user"); - const limit = parseFloat(user.spendingLimitUsd || "4"); + const limit = parseFloat(user.spendingLimitUsd || "3"); const spent = await computeSpent(user.id); if (spent >= limit) { @@ -92,7 +92,7 @@ export async function reserveCharge(c: Ctx, estimatedCost: number) { if (apiKey?.isUnlimited) return; const user = c.get("user"); - const limit = parseFloat(user.spendingLimitUsd || "4"); + const limit = parseFloat(user.spendingLimitUsd || "3"); const estimate = Math.max(0, estimatedCost); const [row] = await db diff --git a/src/views/components/Header.tsx b/src/views/components/Header.tsx index 3699333..9e25ed0 100644 --- a/src/views/components/Header.tsx +++ b/src/views/components/Header.tsx @@ -17,7 +17,7 @@ export const Header = ({ dailySpending, dailyLimit, }: HeaderProps) => { - const limit = dailyLimit ?? parseFloat(user.spendingLimitUsd || "8"); + const limit = dailyLimit ?? parseFloat(user.spendingLimitUsd || "3"); const spent = dailySpending ?? 0; return (