-
Notifications
You must be signed in to change notification settings - Fork 1
refactor: Update docker images #8
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| node_modules | ||
| .svelte-kit | ||
| Dockerfile* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,4 @@ node_modules | |
| dist | ||
| *.old | ||
| *.tsbuildinfo | ||
| logs | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,37 @@ | ||
| FROM oven/bun:alpine AS base | ||
| FROM oven/bun:1.1.18-alpine AS base | ||
|
|
||
| # --- Building --- | ||
| FROM base AS builder | ||
|
|
||
| RUN apk update | ||
| # Set working directory | ||
|
|
||
| WORKDIR /app | ||
| RUN bun install -g turbo | ||
|
|
||
| # This assumes its being run in the base spookcord directory | ||
| COPY . . | ||
| RUN turbo prune server --docker | ||
|
|
||
| # Add lockfile and package.json's of isolated subworkspace | ||
| RUN bunx turbo prune server --docker | ||
|
|
||
| # --- Installer --- | ||
| FROM base AS installer | ||
| RUN apk update | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # First install dependencies (as they change less often) | ||
| COPY --from=builder /app/out/json/ . | ||
| RUN bun install | ||
|
|
||
| # Build the project and its dependencies | ||
| COPY --from=builder /app/out/full/ . | ||
|
|
||
| RUN bun turbo compile | ||
| # Install deps & Compile | ||
| RUN bun install --production --ignore-scripts --frozen-lockfile | ||
| RUN bunx turbo compile | ||
|
|
||
|
Comment on lines
+22
to
24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Dev-deps required during Inside the installer stage you first run: Because Two safer options: - RUN bun install --production --ignore-scripts --frozen-lockfile
- RUN bunx turbo compile
+ # Install *all* deps for the build, then prune afterwards
+ RUN bun install --ignore-scripts --frozen-lockfile
+ RUN bunx turbo compile
+ RUN bun prune --production # remove devDeps before hand-offor split compile into the earlier builder stage where full deps already exist. 🤖 Prompt for AI Agents |
||
| # --- Runner --- | ||
| FROM base AS runner | ||
|
|
||
| WORKDIR /app | ||
|
|
||
| # Don't run production as root | ||
| USER bun | ||
| COPY --from=installer /app/apps/server/server ./server | ||
|
|
||
| COPY --from=installer --chown=bun:bun /app/apps/server/server ./server | ||
| COPY --from=installer --chown=bun:bun /app/apps/server/src/db/migrations ./src/db/migrations | ||
|
|
||
| EXPOSE 3000/tcp | ||
| CMD ./server | ||
|
|
||
| CMD ["./server"] | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -16,9 +16,6 @@ | |||||||||||||||||||||||||||||||||||
| "dependencies": { | ||||||||||||||||||||||||||||||||||||
| "@orpc/client": "^1.2.0", | ||||||||||||||||||||||||||||||||||||
| "@orpc/server": "^1.2.0", | ||||||||||||||||||||||||||||||||||||
| "@spookcord/db-schema": "workspace:*", | ||||||||||||||||||||||||||||||||||||
| "@spookcord/types": "workspace:*", | ||||||||||||||||||||||||||||||||||||
| "@types/jsonwebtoken": "^9.0.9", | ||||||||||||||||||||||||||||||||||||
| "better-auth": "^1.2.8", | ||||||||||||||||||||||||||||||||||||
| "dotenv": "^16.5.0", | ||||||||||||||||||||||||||||||||||||
| "drizzle-orm": "^latest", | ||||||||||||||||||||||||||||||||||||
|
|
@@ -28,7 +25,10 @@ | |||||||||||||||||||||||||||||||||||
| "zod": "^3.25.56" | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| "devDependencies": { | ||||||||||||||||||||||||||||||||||||
| "@spookcord/db-schema": "workspace:*", | ||||||||||||||||||||||||||||||||||||
| "@spookcord/types": "workspace:*", | ||||||||||||||||||||||||||||||||||||
| "@types/bun": "^1.2.13", | ||||||||||||||||||||||||||||||||||||
| "@types/jsonwebtoken": "^9.0.9", | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPotential runtime & build-time breakage by moving
Same concern for Please double-check that:
If either statement is false, move the packages back to 🏁 Script executed: #!/bin/bash
# Quick check: look for runtime imports of the schema package
rg --no-heading --trim -N $'import .*["@]spookcord/db-schema' apps/server/srcLength of output: 599 🏁 Script executed: #!/bin/bash
# Locate Dockerfiles
fd Dockerfile
# Search for bun install commands
rg -n "bun install" -C2 .
# Search for bun build commands
rg -n "bun build" -C2 .Length of output: 1586 Restore The server’s Dockerfile does: Since devDependencies aren’t installed in that stage, the bundler and runtime will be missing • apps/server/Dockerfile: line 22 uses --- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -25,7 +25,7 @@
"dependencies": {
- "@spookcord/db-schema": "workspace:*", ← restore here
- "@spookcord/types": "workspace:*", ← if any runtime imports
+ "@spookcord/db-schema": "workspace:*",
+ "@spookcord/types": "workspace:*",
"@types/bun": "^1.2.13",
"@types/jsonwebtoken": "^9.0.9",
},
"devDependencies": {
- "@spookcord/db-schema": "workspace:*",
- "@spookcord/types": "workspace:*",
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| "@types/pg": "^8.15.2", | ||||||||||||||||||||||||||||||||||||
| "drizzle-kit": "^latest", | ||||||||||||||||||||||||||||||||||||
| "tsc-alias": "^1.8.16", | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,5 +3,102 @@ | |||||||||||||||||||||||||||||||||||
| import { drizzle } from 'drizzle-orm/node-postgres'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| import * as schema from '@spookcord/db-schema'; | ||||||||||||||||||||||||||||||||||||
| import { migrate } from 'drizzle-orm/postgres-js/migrator'; | ||||||||||||||||||||||||||||||||||||
| import { sql } from 'drizzle-orm'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export const db = drizzle(process.env.DATABASE_URL || '', { schema }); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| export async function runMigrations() { | ||||||||||||||||||||||||||||||||||||
| console.info('[database] Running migrations'); | ||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| await migrate(db, { migrationsFolder: './src/db/migrations' }); | ||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider using an absolute path for the migrations folder. The relative path Consider using an absolute path: +import { fileURLToPath } from 'url';
+import { dirname, join } from 'path';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
export async function runMigrations() {
console.info('[database] Running migrations');
try {
- await migrate(db, { migrationsFolder: './src/db/migrations' });
+ await migrate(db, { migrationsFolder: join(__dirname, 'migrations') });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| console.log('[database] OK'); | ||||||||||||||||||||||||||||||||||||
| await checkRealtime(); | ||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||
| console.error( | ||||||||||||||||||||||||||||||||||||
| '[database] FAIL | Migrations failed \n', | ||||||||||||||||||||||||||||||||||||
| error, | ||||||||||||||||||||||||||||||||||||
| '\n - This error should be reported. (This is an unrecoverable error)' | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| process.exit(1); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| async function checkRealtime() { | ||||||||||||||||||||||||||||||||||||
| console.log('[database] Checking realtime'); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||
| // Check if we already have the function | ||||||||||||||||||||||||||||||||||||
| const functionExistsResult = await db.execute(sql` | ||||||||||||||||||||||||||||||||||||
| SELECT EXISTS ( | ||||||||||||||||||||||||||||||||||||
| SELECT 1 | ||||||||||||||||||||||||||||||||||||
| FROM pg_proc | ||||||||||||||||||||||||||||||||||||
| WHERE proname = 'add_table_to_realtime' | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| `); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // Do we really need .exists? | ||||||||||||||||||||||||||||||||||||
| const functionExists = functionExistsResult.rows[0]?.exists || false; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (!functionExists) { | ||||||||||||||||||||||||||||||||||||
| // Create the function | ||||||||||||||||||||||||||||||||||||
| console.warn("[database] WARN function doesn't exist, created"); | ||||||||||||||||||||||||||||||||||||
| await db.execute(sql` | ||||||||||||||||||||||||||||||||||||
| CREATE OR REPLACE FUNCTION add_table_to_realtime(_table_name text) | ||||||||||||||||||||||||||||||||||||
| RETURNS text AS | ||||||||||||||||||||||||||||||||||||
| $$ | ||||||||||||||||||||||||||||||||||||
| BEGIN | ||||||||||||||||||||||||||||||||||||
| EXECUTE format('ALTER PUBLICATION supabase_realtime ADD TABLE %I;', _table_name); | ||||||||||||||||||||||||||||||||||||
| RETURN 'success'; | ||||||||||||||||||||||||||||||||||||
| EXCEPTION | ||||||||||||||||||||||||||||||||||||
| WHEN duplicate_object THEN | ||||||||||||||||||||||||||||||||||||
| RETURN 'Table already exists'; | ||||||||||||||||||||||||||||||||||||
| WHEN OTHERS THEN | ||||||||||||||||||||||||||||||||||||
| RAISE; | ||||||||||||||||||||||||||||||||||||
| END | ||||||||||||||||||||||||||||||||||||
| $$ LANGUAGE 'plpgsql'; | ||||||||||||||||||||||||||||||||||||
| `); | ||||||||||||||||||||||||||||||||||||
| console.info('[database] OK, created.'); | ||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||
| console.debug('[database] DBG Function exists'); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const tableExistsResult = await db.execute(sql` | ||||||||||||||||||||||||||||||||||||
| SELECT EXISTS ( | ||||||||||||||||||||||||||||||||||||
| SELECT 1 | ||||||||||||||||||||||||||||||||||||
| FROM pg_tables | ||||||||||||||||||||||||||||||||||||
| WHERE schemaname = 'public' AND tablename = 'message' | ||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||
| `); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const tableExists = tableExistsResult.rows[0].exists || false; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if (tableExists) { | ||||||||||||||||||||||||||||||||||||
| const result = await db.execute(sql` | ||||||||||||||||||||||||||||||||||||
| SELECT add_table_to_realtime('message'); | ||||||||||||||||||||||||||||||||||||
| `); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| const status = result.rows[0].add_table_to_realtime || 'unknown'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| switch (status) { | ||||||||||||||||||||||||||||||||||||
| case 'success': | ||||||||||||||||||||||||||||||||||||
| console.log('[database] OK table successfully added.'); | ||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| case 'Table already exists': | ||||||||||||||||||||||||||||||||||||
| console.debug('[database] OK table aready exists'); | ||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix typo in log message. - console.debug('[database] OK table aready exists');
+ console.debug('[database] OK table already exists');📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||
| console.error('[database] WARN an unexpected error occurred ', status); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| console.error("[database] FAIL no table named 'message' found!"); | ||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||
| console.error( | ||||||||||||||||||||||||||||||||||||
| '[database] FAIL | Realtime table failed\n', | ||||||||||||||||||||||||||||||||||||
| error, | ||||||||||||||||||||||||||||||||||||
| "\n |-> Continuning anyway, your messages won't appear in realtime." | ||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| CREATE TABLE "account" ( | ||
| "id" text PRIMARY KEY NOT NULL, | ||
| "account_id" text NOT NULL, | ||
| "provider_id" text NOT NULL, | ||
| "user_id" text NOT NULL, | ||
| "access_token" text, | ||
| "refresh_token" text, | ||
| "id_token" text, | ||
| "access_token_expires_at" timestamp, | ||
| "refresh_token_expires_at" timestamp, | ||
| "scope" text, | ||
| "password" text, | ||
| "created_at" timestamp NOT NULL, | ||
| "updated_at" timestamp NOT NULL | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "category" ( | ||
| "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, | ||
| "name" text NOT NULL, | ||
| "server_id" uuid NOT NULL, | ||
| "position" serial 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 "channel" ( | ||
| "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, | ||
| "name" text NOT NULL, | ||
| "category_id" uuid NOT NULL, | ||
| "type" text DEFAULT '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 "jwks" ( | ||
| "id" text PRIMARY KEY NOT NULL, | ||
| "public_key" text NOT NULL, | ||
| "private_key" text NOT NULL, | ||
| "created_at" timestamp NOT NULL | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "message" ( | ||
| "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, | ||
| "body" text NOT NULL, | ||
| "sender_id" text NOT NULL, | ||
| "channel_id" uuid NOT NULL, | ||
| "created_at" timestamp with time zone DEFAULT now() NOT NULL, | ||
| "updated_at" timestamp with time zone DEFAULT now() NOT NULL | ||
| ); | ||
| --> statement-breakpoint | ||
| ALTER TABLE "message" ENABLE ROW LEVEL SECURITY;--> statement-breakpoint | ||
| CREATE TABLE "server" ( | ||
| "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, | ||
| "name" text NOT NULL, | ||
| "owner_id" text NOT NULL, | ||
| "image_url" text DEFAULT '', | ||
| "invite_code" text NOT NULL, | ||
| "created_at" timestamp with time zone DEFAULT now() NOT NULL, | ||
| "updated_at" timestamp with time zone DEFAULT now() NOT NULL, | ||
| CONSTRAINT "server_invite_code_unique" UNIQUE("invite_code") | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "server_members" ( | ||
| "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, | ||
| "server_id" uuid NOT NULL, | ||
| "user_id" text NOT NULL, | ||
| "joined_at" timestamp with time zone DEFAULT now() NOT NULL | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "session" ( | ||
| "id" text PRIMARY KEY NOT NULL, | ||
| "expires_at" timestamp NOT NULL, | ||
| "token" text NOT NULL, | ||
| "created_at" timestamp NOT NULL, | ||
| "updated_at" timestamp NOT NULL, | ||
| "ip_address" text, | ||
| "user_agent" text, | ||
| "user_id" text NOT NULL, | ||
| CONSTRAINT "session_token_unique" UNIQUE("token") | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "user" ( | ||
| "id" text PRIMARY KEY NOT NULL, | ||
| "name" text NOT NULL, | ||
| "email" text NOT NULL, | ||
| "email_verified" boolean NOT NULL, | ||
| "image" text, | ||
| "created_at" timestamp NOT NULL, | ||
| "updated_at" timestamp NOT NULL, | ||
| CONSTRAINT "user_email_unique" UNIQUE("email") | ||
| ); | ||
| --> statement-breakpoint | ||
| CREATE TABLE "verification" ( | ||
| "id" text PRIMARY KEY NOT NULL, | ||
| "identifier" text NOT NULL, | ||
| "value" text NOT NULL, | ||
| "expires_at" timestamp NOT NULL, | ||
| "created_at" timestamp, | ||
| "updated_at" timestamp | ||
| ); | ||
| --> statement-breakpoint | ||
| ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "category" ADD CONSTRAINT "category_server_id_server_id_fk" FOREIGN KEY ("server_id") REFERENCES "public"."server"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "channel" ADD CONSTRAINT "channel_category_id_category_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."category"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "message" ADD CONSTRAINT "message_sender_id_user_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."user"("id") ON DELETE restrict ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "message" ADD CONSTRAINT "message_channel_id_channel_id_fk" FOREIGN KEY ("channel_id") REFERENCES "public"."channel"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "server" ADD CONSTRAINT "server_owner_id_user_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "server_members" ADD CONSTRAINT "server_members_server_id_server_id_fk" FOREIGN KEY ("server_id") REFERENCES "public"."server"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "server_members" ADD CONSTRAINT "server_members_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| CREATE UNIQUE INDEX "server_members_unique_idx" ON "server_members" USING btree ("server_id","user_id"); |
Uh oh!
There was an error while loading. Please reload this page.