From 71295dc8a1d38095a240223129360aa81c8d13ea Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 19 Jun 2026 09:35:52 +0000 Subject: [PATCH] Containerize application with Docker Compose and add vector database options - Created custom Dockerfile.db with PostgreSQL 16, pgvector, and PostGIS support. - Updated docker-compose.yaml to include qdrant as a dedicated vector database option. - Enhanced database migrations to enable postgis and vector extensions. - Added CLOUD_DEPLOYMENT.md with comprehensive deployment guides for VPS, Render, and GCP. - Improved environment variable handling for production readiness. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com> --- CLOUD_DEPLOYMENT.md | 97 +++ Dockerfile.db | 21 + bun.lock | 4 +- docker-compose.yaml | 36 +- drizzle/migrations/0001_sync_schema_full.sql | 80 +++ drizzle/migrations/meta/0000_snapshot.json | 57 +- drizzle/migrations/meta/0001_snapshot.json | 684 +++++++++++++++++++ drizzle/migrations/meta/_journal.json | 7 + package.json | 2 +- server.log | 30 +- 10 files changed, 974 insertions(+), 44 deletions(-) create mode 100644 CLOUD_DEPLOYMENT.md create mode 100644 Dockerfile.db create mode 100644 drizzle/migrations/0001_sync_schema_full.sql create mode 100644 drizzle/migrations/meta/0001_snapshot.json diff --git a/CLOUD_DEPLOYMENT.md b/CLOUD_DEPLOYMENT.md new file mode 100644 index 00000000..20bee631 --- /dev/null +++ b/CLOUD_DEPLOYMENT.md @@ -0,0 +1,97 @@ +# Cloud Deployment Guide for QCX + +This guide provides instructions for deploying the QCX stack (Next.js, PostgreSQL with pgvector/PostGIS, and Qdrant) to various cloud platforms. + +## Infrastructure Overview + +The application consists of: +1. **QCX Web App**: Next.js application running on Bun. +2. **PostgreSQL**: Database with `pgvector` and `PostGIS` extensions. +3. **Qdrant**: High-performance vector database (optional, for advanced vector search). + +--- + +## 1. Deploying with Docker Compose (VPS / EC2 / Compute Engine) + +The easiest way to deploy the entire stack is using `docker-compose`. + +1. **Clone the repository** on your server. +2. **Create a `.env` file** based on the environment variables in `docker-compose.yaml`. +3. **Run the stack**: + ```bash + docker-compose up -d --build + ``` + +--- + +## 2. Deploying to Render + +Render is a great choice for managed services. + +### PostgreSQL (Managed) +1. Create a **New PostgreSQL** instance on Render. +2. **Note**: Standard Render Postgres does not include `pgvector` or `PostGIS` by default on all plans. You may need to use a Docker-based Postgres on Render or ensure your plan supports these extensions. +3. If using Render's managed Postgres, run the extensions command manually via a SQL client: + ```sql + CREATE EXTENSION IF NOT EXISTS postgis; + CREATE EXTENSION IF NOT EXISTS vector; + ``` + +### Web Service +1. Create a **New Web Service** pointing to your repository. +2. Select **Docker** as the runtime. +3. Specify the **Dockerfile path** as `Dockerfile`. +4. Add environment variables: + * `DATABASE_URL`: Your Render Postgres connection string. + * `EXECUTE_MIGRATIONS`: `true` + * `NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN`: Your Mapbox token. + * (Add other necessary API keys like Google, xAI, etc.) + +### Qdrant (Optional) +1. Create a **New Private Service** or **Web Service**. +2. Select **Docker** as the runtime. +3. Use the image: `qdrant/qdrant:latest`. +4. Set `QDRANT_URL` in your Web Service to point to this service. + +--- + +## 3. Deploying to Google Cloud (GCP) + +### Cloud Run +1. **Build and Push** the image to Google Artifact Registry: + ```bash + docker build -t gcr.io/YOUR_PROJECT/qcx . + docker push gcr.io/YOUR_PROJECT/qcx + ``` +2. **Deploy to Cloud Run**: + ```bash + gcloud run deploy qcx --image gcr.io/YOUR_PROJECT/qcx --platform managed + ``` + +### Cloud SQL +1. Create a **Cloud SQL for PostgreSQL** instance (version 15+). +2. Cloud SQL supports both `pgvector` and `postgis`. Enable them via: + ```sql + CREATE EXTENSION IF NOT EXISTS postgis; + CREATE EXTENSION IF NOT EXISTS vector; + ``` + +--- + +## 4. Vector Database Options + +QCX is configured to support two vector database options: + +1. **PostgreSQL (pgvector)**: Integrated into the main database. Best for smaller datasets and simplicity. +2. **Qdrant**: Dedicated vector database. Recommended for large-scale production use cases requiring high performance and advanced filtering. + +To use Qdrant, ensure the `QDRANT_URL` environment variable is set in your application environment. + +--- + +## 5. Security Checklist + +* [ ] Change default passwords in `docker-compose.yaml`. +* [ ] Use SSL for database connections in production (`ssl=true` in `DATABASE_URL`). +* [ ] Set `NODE_ENV=production`. +* [ ] Ensure all sensitive API keys are stored as secrets, not committed to code. diff --git a/Dockerfile.db b/Dockerfile.db new file mode 100644 index 00000000..ab2a8d37 --- /dev/null +++ b/Dockerfile.db @@ -0,0 +1,21 @@ +FROM postgres:16-alpine + +# Install build dependencies and pgvector +RUN apk add --no-cache --virtual .build-deps \ + git \ + build-base \ + clang15 \ + llvm15-dev \ + postgis-dev \ + postgresql-dev \ + && git clone --branch v0.8.0 https://github.com/pgvector/pgvector.git /tmp/pgvector \ + && cd /tmp/pgvector \ + && make \ + && make install \ + && apk add --no-cache postgis \ + && rm -rf /tmp/pgvector \ + && apk del .build-deps + +# Use the default entrypoint +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["postgres"] diff --git a/bun.lock b/bun.lock index 5e0fc295..8cf00d19 100644 --- a/bun.lock +++ b/bun.lock @@ -50,7 +50,7 @@ "csv-parse": "^6.1.0", "dotenv": "^16.5.0", "drizzle-kit": "^0.31.1", - "drizzle-orm": "^0.29.0", + "drizzle-orm": "^0.45.2", "embla-carousel-react": "^8.6.0", "exa-js": "^1.6.13", "framer-motion": "^12.23.24", @@ -1331,7 +1331,7 @@ "drizzle-kit": ["drizzle-kit@0.31.8", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-O9EC/miwdnRDY10qRxM8P3Pg8hXe3LyU4ZipReKOgTwn4OqANmftj8XJz1UPUAS6NMHf0E2htjsbQujUTkncCg=="], - "drizzle-orm": ["drizzle-orm@0.29.5", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=3", "@libsql/client": "*", "@neondatabase/serverless": ">=0.1", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/react": ">=18", "@types/sql.js": "*", "@vercel/postgres": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=13.2.0", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "react": ">=18", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@libsql/client", "@neondatabase/serverless", "@opentelemetry/api", "@planetscale/database", "@types/better-sqlite3", "@types/pg", "@types/react", "@types/sql.js", "@vercel/postgres", "better-sqlite3", "bun-types", "expo-sqlite", "knex", "kysely", "mysql2", "pg", "postgres", "react", "sql.js", "sqlite3"] }, "sha512-jS3+uyzTz4P0Y2CICx8FmRQ1eplURPaIMWDn/yq6k4ShRFj9V7vlJk67lSf2kyYPzQ60GkkNGXcJcwrxZ6QCRw=="], + "drizzle-orm": ["drizzle-orm@0.45.2", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q=="], "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], diff --git a/docker-compose.yaml b/docker-compose.yaml index 48ffb53f..49e23426 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -2,16 +2,32 @@ name: qcx-stack services: db: - image: postgres:16-alpine + build: + context: . + dockerfile: Dockerfile.db restart: unless-stopped environment: - POSTGRES_USER: qcxuser - POSTGRES_PASSWORD: qcxpassword - POSTGRES_DB: qcxdb + POSTGRES_USER: ${POSTGRES_USER:-qcxuser} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-qcxpassword} + POSTGRES_DB: ${POSTGRES_DB:-qcxdb} volumes: - postgres_data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U qcxuser -d qcxdb"] + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-qcxuser} -d ${POSTGRES_DB:-qcxdb}"] + interval: 10s + timeout: 5s + retries: 5 + + qdrant: + image: qdrant/qdrant:latest + restart: unless-stopped + ports: + - "6333:6333" + - "6334:6334" + volumes: + - qdrant_data:/qdrant/storage + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:6333/health"] interval: 10s timeout: 5s retries: 5 @@ -27,8 +43,10 @@ services: - NODE_ENV=production - PORT=3000 - HOSTNAME=0.0.0.0 - - DATABASE_URL=postgres://qcxuser:qcxpassword@db:5432/qcxdb + - DATABASE_URL=postgres://${POSTGRES_USER:-qcxuser}:${POSTGRES_PASSWORD:-qcxpassword}@db:5432/${POSTGRES_DB:-qcxdb} + - QDRANT_URL=http://qdrant:6333 - EXECUTE_MIGRATIONS=true + # Add other necessary env vars here or use an .env file depends_on: db: condition: service_healthy @@ -52,10 +70,13 @@ services: environment: - NODE_ENV=development - PORT=3000 - - DATABASE_URL=postgres://qcxuser:qcxpassword@db:5432/qcxdb + - DATABASE_URL=postgres://${POSTGRES_USER:-qcxuser}:${POSTGRES_PASSWORD:-qcxpassword}@db:5432/${POSTGRES_DB:-qcxdb} + - QDRANT_URL=http://qdrant:6333 depends_on: db: condition: service_healthy + qdrant: + condition: service_started volumes: - .:/app - node_modules:/app/node_modules @@ -66,5 +87,6 @@ services: volumes: postgres_data: + qdrant_data: node_modules: next_build: diff --git a/drizzle/migrations/0001_sync_schema_full.sql b/drizzle/migrations/0001_sync_schema_full.sql new file mode 100644 index 00000000..11478553 --- /dev/null +++ b/drizzle/migrations/0001_sync_schema_full.sql @@ -0,0 +1,80 @@ +CREATE EXTENSION IF NOT EXISTS postgis; +CREATE EXTENSION IF NOT EXISTS vector; + +CREATE TABLE "calendar_notes" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "date" timestamp with time zone NOT NULL, + "content" text NOT NULL, + "location_tags" jsonb, + "user_tags" text[], + "map_feature_id" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "chat_participants" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "chat_id" uuid NOT NULL, + "user_id" uuid NOT NULL, + "role" text DEFAULT 'collaborator' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "chat_participants_chat_user_unique" UNIQUE("chat_id","user_id") +); +--> statement-breakpoint +CREATE TABLE "locations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "geojson" jsonb NOT NULL, + "geometry" geometry(GEOMETRY, 4326), + "name" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "system_prompts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "prompt" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "visualizations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "type" text DEFAULT 'map_layer' NOT NULL, + "data" jsonb NOT NULL, + "geometry" geometry(GEOMETRY, 4326), + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "title" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "title" SET DEFAULT 'Untitled Chat';--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "visibility" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "visibility" SET DEFAULT 'private';--> statement-breakpoint +ALTER TABLE "messages" ALTER COLUMN "role" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "path" text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "share_path" text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "shareable_link_id" uuid DEFAULT gen_random_uuid();--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "updated_at" timestamp with time zone DEFAULT now() NOT NULL;--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "embedding" vector(1536);--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "location_id" uuid;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "email" text;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "role" text DEFAULT 'viewer';--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "selected_model" text;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "system_prompt" text;--> statement-breakpoint +ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "locations" ADD CONSTRAINT "locations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "locations" ADD CONSTRAINT "locations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "system_prompts" ADD CONSTRAINT "system_prompts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "chats" ADD CONSTRAINT "chats_shareable_link_id_unique" UNIQUE("shareable_link_id");--> statement-breakpoint +ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE("email"); diff --git a/drizzle/migrations/meta/0000_snapshot.json b/drizzle/migrations/meta/0000_snapshot.json index eb62145d..8f31b8c5 100644 --- a/drizzle/migrations/meta/0000_snapshot.json +++ b/drizzle/migrations/meta/0000_snapshot.json @@ -1,10 +1,8 @@ { - "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9", - "prevId": "00000000-0000-0000-0000-000000000000", - "version": "5", - "dialect": "pg", + "version": "7", + "dialect": "postgresql", "tables": { - "chats": { + "public.chats": { "name": "chats", "schema": "", "columns": { @@ -48,21 +46,24 @@ "chats_user_id_users_id_fk": { "name": "chats_user_id_users_id_fk", "tableFrom": "chats", - "tableTo": "users", "columnsFrom": [ "user_id" ], + "tableTo": "users", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} }, - "messages": { + "public.messages": { "name": "messages", "schema": "", "columns": { @@ -110,34 +111,37 @@ "messages_chat_id_chats_id_fk": { "name": "messages_chat_id_chats_id_fk", "tableFrom": "messages", - "tableTo": "chats", "columnsFrom": [ "chat_id" ], + "tableTo": "chats", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" }, "messages_user_id_users_id_fk": { "name": "messages_user_id_users_id_fk", "tableFrom": "messages", - "tableTo": "users", "columnsFrom": [ "user_id" ], + "tableTo": "users", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} }, - "users": { + "public.users": { "name": "users", "schema": "", "columns": { @@ -152,14 +156,23 @@ "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} } }, "enums": {}, "schemas": {}, "_meta": { - "columns": {}, "schemas": {}, - "tables": {} - } + "tables": {}, + "columns": {} + }, + "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9", + "prevId": "00000000-0000-0000-0000-000000000000", + "sequences": {}, + "policies": {}, + "views": {}, + "roles": {} } \ No newline at end of file diff --git a/drizzle/migrations/meta/0001_snapshot.json b/drizzle/migrations/meta/0001_snapshot.json new file mode 100644 index 00000000..c3821a25 --- /dev/null +++ b/drizzle/migrations/meta/0001_snapshot.json @@ -0,0 +1,684 @@ +{ + "id": "4bf9530d-5baf-49a0-aa5d-7997ed7bc44e", + "prevId": "0d46923a-5423-4b73-91cb-5f46741e7ff9", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.calendar_notes": { + "name": "calendar_notes", + "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 + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location_tags": { + "name": "location_tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "user_tags": { + "name": "user_tags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "map_feature_id": { + "name": "map_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "calendar_notes_user_id_users_id_fk": { + "name": "calendar_notes_user_id_users_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "calendar_notes_chat_id_chats_id_fk": { + "name": "calendar_notes_chat_id_chats_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_participants": { + "name": "chat_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'collaborator'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_participants_chat_id_chats_id_fk": { + "name": "chat_participants_chat_id_chats_id_fk", + "tableFrom": "chat_participants", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_participants_user_id_users_id_fk": { + "name": "chat_participants_user_id_users_id_fk", + "tableFrom": "chat_participants", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_participants_chat_user_unique": { + "name": "chat_participants_chat_user_unique", + "nullsNotDistinct": false, + "columns": [ + "chat_id", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chats": { + "name": "chats", + "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 + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Untitled Chat'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'private'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_path": { + "name": "share_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shareable_link_id": { + "name": "shareable_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chats_user_id_users_id_fk": { + "name": "chats_user_id_users_id_fk", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chats_shareable_link_id_unique": { + "name": "chats_shareable_link_id_unique", + "nullsNotDistinct": false, + "columns": [ + "shareable_link_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.locations": { + "name": "locations", + "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 + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "geojson": { + "name": "geojson", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_user_id_users_id_fk": { + "name": "locations_user_id_users_id_fk", + "tableFrom": "locations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "locations_chat_id_chats_id_fk": { + "name": "locations_chat_id_chats_id_fk", + "tableFrom": "locations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "location_id": { + "name": "location_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "messages_chat_id_chats_id_fk": { + "name": "messages_chat_id_chats_id_fk", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_user_id_users_id_fk": { + "name": "messages_user_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_location_id_locations_id_fk": { + "name": "messages_location_id_locations_id_fk", + "tableFrom": "messages", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompts": { + "name": "system_prompts", + "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 + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_prompts_user_id_users_id_fk": { + "name": "system_prompts_user_id_users_id_fk", + "tableFrom": "system_prompts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'viewer'" + }, + "selected_model": { + "name": "selected_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.visualizations": { + "name": "visualizations", + "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 + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'map_layer'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "visualizations_user_id_users_id_fk": { + "name": "visualizations_user_id_users_id_fk", + "tableFrom": "visualizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "visualizations_chat_id_chats_id_fk": { + "name": "visualizations_chat_id_chats_id_fk", + "tableFrom": "visualizations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index 34cd1203..c5c9f55b 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1750358514791, "tag": "0000_sweet_metal_master", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1781859708408, + "tag": "0001_sync_schema_full", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package.json b/package.json index bb49fad9..214ca355 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "csv-parse": "^6.1.0", "dotenv": "^16.5.0", "drizzle-kit": "^0.31.1", - "drizzle-orm": "^0.29.0", + "drizzle-orm": "^0.45.2", "embla-carousel-react": "^8.6.0", "exa-js": "^1.6.13", "framer-motion": "^12.23.24", diff --git a/server.log b/server.log index 41e8e758..9d968b8c 100644 --- a/server.log +++ b/server.log @@ -1,17 +1,23 @@ -$ next dev --turbo - ▲ Next.js 15.3.8 (Turbopack) +$ next start + ▲ Next.js 15.3.8 - Local: http://localhost:3000 - Network: http://192.168.0.2:3000 - - Environments: .env ✓ Starting... - ✓ Compiled middleware in 433ms - ✓ Ready in 1802ms - ○ Compiling / ... - ✓ Compiled / in 27.9s + ⚠ "next start" does not work with "output: standalone" configuration. Use "node .next/standalone/server.js" instead. + ✓ Ready in 504ms Chat DB actions loaded. Ensure getCurrentUserId() is correctly implemented for server-side usage if applicable. - GET / 200 in 30481ms - HEAD / 200 in 1010ms - GET / 200 in 986ms - GET / 200 in 1129ms -[?25h +Chat DB actions loaded. Ensure getCurrentUserId() is correctly implemented for server-side usage if applicable. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth. +[Auth] Supabase URL or Anon Key is not set for server-side auth.