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 <noreply@anthropic.com>main
parent
6e512a14fc
commit
248b22dcba
@ -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:<POSTGRES_PASSWORD>@localhost:5432/cleo_auth"
|
||||
|
||||
# MariaDB `cleo` from the legacy app (office LAN only)
|
||||
AMIGO_DATABASE_URL="mysql://<user>:<password>@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
|
||||
@ -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 `<POSTGRES_PASSWORD>` 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 |
|
||||
|
||||
@ -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
|
||||
@ -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());
|
||||
Loading…
Reference in New Issue