From 98af1c7cec1112d0bad2618d5e5a9d08a2709cc3 Mon Sep 17 00:00:00 2001 From: Min Zeya Phyo Date: Thu, 19 Feb 2026 16:51:31 +0800 Subject: [PATCH] Adapt for self-hosted deployment on Coolify - Replace @vercel/postgres with standard pg library - Add Dockerfile for Next.js standalone build - Add tsconfig.json, postcss.config.js - Fix globals.css undefined tailwind utilities - Force dynamic rendering for DB-dependent pages - Add .dockerignore --- .dockerignore | 7 +++++ Dockerfile | 47 ++++++++++++++++++++++++++++ frontend/app/article/[slug]/page.tsx | 4 ++- frontend/app/globals.css | 5 +-- frontend/app/page.tsx | 4 ++- frontend/lib/db.ts | 16 ++++++++++ frontend/next.config.js | 4 +-- frontend/package.json | 2 +- frontend/postcss.config.js | 6 ++++ frontend/public/.gitkeep | 0 frontend/tsconfig.json | 27 ++++++++++++++++ 11 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 frontend/lib/db.ts create mode 100644 frontend/postcss.config.js create mode 100644 frontend/public/.gitkeep create mode 100644 frontend/tsconfig.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f3f6e6c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.next +.git +*.md +backend +database +.github diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..264bede --- /dev/null +++ b/Dockerfile @@ -0,0 +1,47 @@ +FROM node:18-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +RUN apk add --no-cache libc6-compat +WORKDIR /app +COPY frontend/package.json frontend/package-lock.json* ./ +RUN npm ci 2>/dev/null || npm install + +# Build the application +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY frontend/ . + +# Need DATABASE_URL at build time for Next.js static generation (can be dummy) +ARG DATABASE_URL=postgresql://localhost/burmddit +ENV DATABASE_URL=${DATABASE_URL} +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN npm run build + +# Production image +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy public assets +COPY --from=builder /app/public ./public + +# Copy standalone build +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +CMD ["node", "server.js"] diff --git a/frontend/app/article/[slug]/page.tsx b/frontend/app/article/[slug]/page.tsx index f117834..8855915 100644 --- a/frontend/app/article/[slug]/page.tsx +++ b/frontend/app/article/[slug]/page.tsx @@ -1,5 +1,7 @@ -import { sql } from '@vercel/postgres' +import { sql } from '@/lib/db' import { notFound } from 'next/navigation' + +export const dynamic = 'force-dynamic' import Link from 'next/link' import Image from 'next/image' diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 7aa37ae..1b1dace 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -3,11 +3,8 @@ @tailwind utilities; @layer base { - * { - @apply border-border; - } body { - @apply bg-background text-foreground; + @apply bg-gray-50 text-gray-900; } } diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 793ada8..624155a 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,5 +1,7 @@ -import { sql } from '@vercel/postgres' +import { sql } from '@/lib/db' import ArticleCard from '@/components/ArticleCard' + +export const dynamic = 'force-dynamic' import TrendingSection from '@/components/TrendingSection' import CategoryNav from '@/components/CategoryNav' diff --git a/frontend/lib/db.ts b/frontend/lib/db.ts new file mode 100644 index 0000000..8d9035e --- /dev/null +++ b/frontend/lib/db.ts @@ -0,0 +1,16 @@ +import { Pool } from 'pg' + +const pool = new Pool({ + connectionString: process.env.DATABASE_URL, +}) + +// Tagged template literal that mimics @vercel/postgres sql`` syntax +export async function sql(strings: TemplateStringsArray, ...values: any[]) { + // Build parameterized query: replace template expressions with $1, $2, etc. + let text = strings[0] + for (let i = 0; i < values.length; i++) { + text += `$${i + 1}` + strings[i + 1] + } + const result = await pool.query(text, values) + return result +} diff --git a/frontend/next.config.js b/frontend/next.config.js index b328d8b..8d61720 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,5 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { + output: 'standalone', images: { remotePatterns: [ { @@ -8,9 +9,6 @@ const nextConfig = { }, ], }, - experimental: { - serverActions: true, - }, } module.exports = nextConfig diff --git a/frontend/package.json b/frontend/package.json index a3be135..bd18c47 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ "react": "^18", "react-dom": "^18", "pg": "^8.11.3", - "@vercel/postgres": "^0.5.1" + "@types/pg": "^8.10.9" }, "devDependencies": { "@types/node": "^20", diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/public/.gitkeep b/frontend/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..c714696 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +}