From 248b22dcba90bde240a45a97fd0049e0c7a2037c Mon Sep 17 00:00:00 2001 From: Deardo Satria Date: Tue, 20 Oct 2026 16:37:25 +0700 Subject: [PATCH] Add docker compose Postgres, admin seed script, and setup guide - docker-compose.yml runs Postgres 17 and reuses the existing cleo-pgdata volume. - scripts/seed.ts creates the first admin through Better Auth's internal adapter, so it works while public sign-up is disabled; skips if the admin exists. - .env.example documents every variable; README has the new-laptop setup steps. Co-Authored-By: Claude Opus 5.5 --- .env.example | 19 ++++++++ .gitignore | 1 + CLAUDE.md | 5 +- PROGRESS.md | 21 +-------- README.md | 113 ++++++++++++++++++++++++++++++++++++--------- docker-compose.yml | 18 ++++++++ package.json | 4 +- pnpm-lock.yaml | 3 ++ scripts/seed.ts | 53 +++++++++++++++++++++ 9 files changed, 193 insertions(+), 44 deletions(-) create mode 100644 .env.example create mode 100644 docker-compose.yml create mode 100644 scripts/seed.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d8b9186 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +# Copy to .env and fill in the values. + +# Postgres (docker-compose.yml). Password must match the one in DATABASE_URL. +POSTGRES_PASSWORD= +DATABASE_URL="postgresql://postgres:@localhost:5432/cleo_auth" + +# MariaDB `cleo` from the legacy app (office LAN only) +AMIGO_DATABASE_URL="mysql://:@192.168.3.3:3306/cleo" + +# Better Auth. Generate the secret with: +# node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" +BETTER_AUTH_SECRET= +BETTER_AUTH_URL="http://localhost:3000" + +# First admin account, created by `pnpm db:seed` +SEED_ADMIN_USERNAME=admin +SEED_ADMIN_PASSWORD= +SEED_ADMIN_NAME=Administrator +SEED_ADMIN_EMAIL=admin@cleo.local diff --git a/.gitignore b/.gitignore index e6e4404..5cd8a43 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel diff --git a/CLAUDE.md b/CLAUDE.md index 607dddb..aa4ebc4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -122,6 +122,9 @@ pnpm exec tsc --noEmit type-check pnpm db:generate buat migrasi dari db/schema.ts (Postgres) pnpm db:migrate jalankan migrasi ke Postgres pnpm db:studio lihat isi Postgres di https://local.drizzle.studio +pnpm db:seed buat akun admin dari SEED_ADMIN_* di .env (idempotent) +docker compose up -d nyalakan Postgres lokal (docker-compose.yml) ``` -Postgres lokal: container Docker `cleo-postgres` (image `postgres:17`, port 5432). +Postgres lokal: service di `docker-compose.yml`, container `cleo-postgres` (image `postgres:17`, +port 5432, volume `cleo-pgdata`). Setup laptop baru ada di `README.md`. diff --git a/PROGRESS.md b/PROGRESS.md index b8dbb56..682a116 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -52,22 +52,5 @@ Setelah itu: commit, lalu rencana berikutnya (belum dimulai): ## Menyiapkan komputer lain -Yang tidak ada di git dan harus disiapkan ulang: - -1. `.env`: isi `DATABASE_URL`, `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, `AMIGO_DATABASE_URL`. - Buat `BETTER_AUTH_SECRET` baru: - `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"` -2. Postgres lokal: - `docker run -d --name cleo-postgres -e POSTGRES_PASSWORD= -e POSTGRES_DB=cleo_auth -p 5432:5432 -v cleo-pgdata:/var/lib/postgresql/data postgres:17` -3. `pnpm install`, lalu `pnpm db:migrate` untuk membuat tabel auth. -4. Akun admin dibuat ulang. Sign-up sedang ditutup, jadi sementara hapus - `disableSignUp: true` di `lib/auth.ts`, jalankan `pnpm dev`, kirim request sign-up: - - ```powershell - $body = @{ name = "Administrator"; email = "admin@cleo.local"; username = "admin"; password = "" } | ConvertTo-Json - Invoke-RestMethod -Method Post -Uri "http://localhost:3000/api/auth/sign-up/email" -ContentType "application/json" -Headers @{ Origin = "http://localhost:3000" } -Body $body - ``` - - lalu kembalikan `disableSignUp: true`. -5. MariaDB `cleo` di `192.168.3.3` hanya bisa diakses dari jaringan kantor. Halaman login tidak - membutuhkannya. +Ikuti bagian "Setup di laptop baru" di `README.md` (`docker compose up -d`, `pnpm db:migrate`, +`pnpm db:seed`). diff --git a/README.md b/README.md index e215bc4..9504761 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,103 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# Cleo Minimarket -## Getting Started +Back-office minimarket berbasis Next.js 16, pengganti aplikasi PHP lama di `old-source/`. -First, run the development server: +Stack: Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, shadcn/ui, Drizzle ORM, +Better Auth, Postgres (data aplikasi dan login), MariaDB `cleo` (data bisnis lama). -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +## Setup di laptop baru -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +### Yang perlu terpasang -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +- [Node.js](https://nodejs.org) 24 atau lebih baru +- [pnpm](https://pnpm.io/installation) 11 (`npm install -g pnpm`) +- [Docker Desktop](https://www.docker.com/products/docker-desktop/), dalam keadaan menyala +- Git -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +### Langkah -## Learn More +1. **Clone repo dan install dependency** -To learn more about Next.js, take a look at the following resources: + ```bash + git clone https://git.amigogroup.id/deardo/nextjs-cleo-minimarket.git + cd nextjs-cleo-minimarket + pnpm install + ``` -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. +2. **Buat `.env` dari contoh** -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + ```bash + cp .env.example .env + ``` -## Deploy on Vercel + Lalu isi nilainya: -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + | Variabel | Isi | + | --------------------- | --------------------------------------------------------------------- | + | `POSTGRES_PASSWORD` | Password bebas untuk Postgres lokal. | + | `DATABASE_URL` | Ganti `` dengan password yang sama seperti di atas. | + | `AMIGO_DATABASE_URL` | User dan password MariaDB `cleo`. Minta ke admin. | + | `BETTER_AUTH_SECRET` | String acak, buat dengan perintah di bawah. | + | `SEED_ADMIN_PASSWORD` | Password akun admin pertama (minimal 8 karakter). | -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + ```bash + node -e "console.log(require('crypto').randomBytes(32).toString('base64'))" + ``` + + Buat `BETTER_AUTH_SECRET` baru di setiap laptop dan server, jangan disalin dari tempat lain. + +3. **Nyalakan Postgres** + + ```bash + docker compose up -d + ``` + + Container `cleo-postgres` akan muncul di Docker Desktop. Data tersimpan di volume + `cleo-pgdata`, jadi tidak hilang walaupun container dimatikan atau dibuat ulang. + +4. **Buat tabel** + + ```bash + pnpm db:migrate + ``` + +5. **Buat akun admin pertama** + + ```bash + pnpm db:seed + ``` + + Username dan password diambil dari `SEED_ADMIN_*` di `.env`. Aman dijalankan berulang kali: + kalau akun sudah ada, seed dilewati. + +6. **Jalankan aplikasi** + + ```bash + pnpm dev + ``` + + Buka http://localhost:3000. Untuk mengecek koneksi auth, buka + http://localhost:3000/api/auth/get-session, hasilnya `null` kalau belum login. + +### Catatan + +- MariaDB `cleo` (`192.168.3.3`) hanya bisa diakses dari jaringan kantor. Fitur login tidak + membutuhkannya. +- Pendaftaran akun lewat web sengaja ditutup (`disableSignUp` di `lib/auth.ts`). Akun pertama + dibuat lewat `pnpm db:seed`. +- Kalau port 5432 sudah dipakai Postgres lain di laptop itu, matikan dulu Postgres tersebut, atau + ganti port kiri di `docker-compose.yml` (mis. `"5433:5432"`) beserta port di `DATABASE_URL`. + +## Perintah + +| Perintah | Fungsi | +| ---------------------- | -------------------------------------------------------- | +| `pnpm dev` | Dev server di http://localhost:3000 | +| `pnpm build` | Build production | +| `pnpm lint` | ESLint | +| `pnpm exec tsc --noEmit` | Type-check | +| `docker compose up -d` | Nyalakan Postgres lokal | +| `pnpm db:generate` | Buat file migrasi dari `db/schema.ts` | +| `pnpm db:migrate` | Jalankan migrasi ke Postgres | +| `pnpm db:seed` | Buat akun admin pertama | +| `pnpm db:studio` | Lihat isi Postgres di https://local.drizzle.studio | diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..5731ed6 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + postgres: + image: postgres:17 + container_name: cleo-postgres + restart: unless-stopped + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env} + POSTGRES_DB: cleo_auth + ports: + - "5432:5432" + volumes: + - cleo-pgdata:/var/lib/postgresql/data + +volumes: + cleo-pgdata: + # Fixed name so the volume created earlier by `docker run -v cleo-pgdata:...` is reused. + name: cleo-pgdata diff --git a/package.json b/package.json index 19abd76..0c20816 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "lint": "eslint", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", - "db:studio": "drizzle-kit studio" + "db:studio": "drizzle-kit studio", + "db:seed": "tsx scripts/seed.ts" }, "dependencies": { "@base-ui/react": "^1.9.0", @@ -37,6 +38,7 @@ "eslint": "^9", "eslint-config-next": "16.4.2", "tailwindcss": "^4", + "tsx": "4.23.15", "typescript": "^5" }, "packageManager": "pnpm@11.5.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9928f3b..78d2659 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,6 +78,9 @@ importers: tailwindcss: specifier: ^4 version: 4.3.3 + tsx: + specifier: 4.23.15 + version: 4.23.15 typescript: specifier: ^5 version: 5.9.3 diff --git a/scripts/seed.ts b/scripts/seed.ts new file mode 100644 index 0000000..694e4ae --- /dev/null +++ b/scripts/seed.ts @@ -0,0 +1,53 @@ +import "dotenv/config"; +import { eq } from "drizzle-orm"; +import { db } from "../db"; +import { user } from "../db/schema"; +import { auth } from "../lib/auth"; + +function requireEnv(name: string): string { + const value = process.env[name]?.trim(); + if (!value) throw new Error(`${name} is not set in .env`); + return value; +} + +async function seedAdmin() { + // The username plugin stores usernames lowercased, so lookups must match that. + const displayUsername = requireEnv("SEED_ADMIN_USERNAME"); + const username = displayUsername.toLowerCase(); + const password = requireEnv("SEED_ADMIN_PASSWORD"); + const name = process.env.SEED_ADMIN_NAME?.trim() || "Administrator"; + const email = process.env.SEED_ADMIN_EMAIL?.trim() || `${username}@cleo.local`; + + const [existing] = await db + .select({ id: user.id }) + .from(user) + .where(eq(user.username, username)) + .limit(1); + if (existing) { + console.log(`Admin "${username}" already exists, skipped.`); + return; + } + + // Goes through Better Auth's internal adapter instead of the sign-up endpoint, which is + // closed by `disableSignUp`. Hashing with ctx.password keeps the format login expects. + const ctx = await auth.$context; + const created = await ctx.internalAdapter.createUser( + { name, email, username, displayUsername }, + { method: "admin" }, + ); + await ctx.internalAdapter.linkAccount({ + userId: created.id, + providerId: "credential", + accountId: created.id, + password: await ctx.password.hash(password), + }); + + console.log(`Admin "${username}" created.`); +} + +seedAdmin() + .catch((error) => { + console.error(error); + process.exitCode = 1; + }) + .finally(() => db.$client.end());