diff --git a/AGENTS.md b/AGENTS.md index 8bd0e39..364642c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,163 @@ - # This is NOT the Next.js you know This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. - + +# Manga Website Project Requirements + +## Tech Stack + +| Layer | Technology | +|-------|-----------| +| Frontend & Backend | Next.js (App Router, TypeScript, Tailwind CSS) | +| Database | PostgreSQL 16 | +| ORM | Prisma | +| Image Storage | Cloudflare R2 | +| Containerization | Docker + Docker Compose (managed via Portainer) | +| Reverse Proxy | Nginx via aaPanel | +| Domain | `manga.04080616.xyz` | + +## Infrastructure + +- **Server**: Proxmox host, Docker VM/LXC running Portainer +- **Reverse proxy**: aaPanel Nginx routes `manga.04080616.xyz` → `http://127.0.0.1:3001` +- **SSL**: Let's Encrypt via aaPanel +- **DDNS already configured** — no Cloudflare Tunnel needed + +### Port Allocation + +| Port | Service | +|------|---------| +| 3001 | Next.js app (host) → 3000 (container) | +| 5433 | PostgreSQL (host) → 5432 (container) | + +Ports 3000, 8000–8002, 8005, 2283, 5432, 51820–51821, 9443 are already in use. + +## Deployment Workflow + +1. Develop on macOS locally +2. Push code to Gitea (`gitea.04080616.xyz/yiekheng/sunnymh-manga-site`) +3. On Proxmox Docker host: `git pull` → `docker build` → restart container +4. Portainer used for container management (GUI) + +## Image Storage (Cloudflare R2) + +- Images stored in R2 (S3-compatible), **not** on the Proxmox host +- Upload flow: backend generates presigned URL → browser uploads directly to R2 → database records image path +- Format: **WebP** (25–35% smaller than JPEG) +- Multiple resolutions: thumbnail / reading / original + +### R2 Directory Structure + +``` +/manga/{manga_id}/{chapter_id}/{page_number}.webp +/thumbnails/{manga_id}/cover.webp +``` + +## Database Schema (Prisma) + +```prisma +model Manga { + id Int @id @default(autoincrement()) + title String + description String + coverUrl String + slug String @unique + status Status @default(PUBLISHED) + chapters Chapter[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Chapter { + id Int @id @default(autoincrement()) + mangaId Int + number Int + title String + pages Page[] + manga Manga @relation(fields: [mangaId], references: [id]) +} + +model Page { + id Int @id @default(autoincrement()) + chapterId Int + number Int + imageUrl String + chapter Chapter @relation(fields: [chapterId], references: [id]) +} + +enum Status { + PUBLISHED + DRAFT +} +``` + +## Features + +### Core (build first) +- Manga listing page with cover images +- Manga detail page (title, description, chapter list) +- Chapter reader page (page-by-page image display) +- Internal search by manga title (PostgreSQL `contains` query) + +### SEO +- Next.js SSR/SSG for all public pages +- `generateMetadata()` per page (title, description) +- Auto-generated `/sitemap.xml` via `app/sitemap.ts` +- Submit sitemap to Google Search Console + +### Search +- **Phase 1**: PostgreSQL `contains` with `insensitive` mode (sufficient for < 10k titles) +- **Phase 2** (future): Meilisearch for fuzzy/full-text search if needed + +## URL Structure + +``` +/ Homepage (manga grid) +/manga/[slug] Manga detail + chapter list +/manga/[slug]/[chapter] Chapter reader +/api/search?q= Search API endpoint +/api/manga Manga CRUD (admin) +/api/upload R2 presigned URL generation +``` + +## Environment Variables (.env) + +```env +DATABASE_URL=postgresql://manga_user:yourpassword@localhost:5433/manga_db + +R2_ACCOUNT_ID=your_cloudflare_account_id +R2_ACCESS_KEY=your_r2_access_key +R2_SECRET_KEY=your_r2_secret_key +R2_BUCKET=manga-images +R2_PUBLIC_URL=https://your-r2-public-url.r2.dev +``` + +## Nginx Reverse Proxy Config (aaPanel) + +```nginx +location / { + proxy_pass http://127.0.0.1:3001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; +} +``` + +## Content Source + +- **Public domain / free manga only** (no copyright issues) +- Sources: Comic Book Plus, Digital Comic Museum, Internet Archive +- Initial: manual download and upload +- Future: automated scraper (Node.js + cheerio) for allowed sources + +## Recommended Build Order + +1. Set up Prisma schema + connect to PostgreSQL +2. Build manga listing and reader pages (static UI first) +3. Wire up database queries via Prisma +4. Add R2 upload + image serving +5. Add search API +6. Add SEO metadata + sitemap +7. Dockerize and deploy to Proxmox via Gitea diff --git a/CLAUDE.md b/CLAUDE.md index 43c994c..8d039a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1 +1,68 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + @AGENTS.md + +## Build & Development Commands + +```bash +npm run dev # Start dev server (hot reload) +npm run build # Production build +npm start # Start production server +npm run lint # ESLint (flat config, v9+) +npx prisma generate # Regenerate Prisma client after schema changes +npx prisma migrate dev --name # Create and apply a migration +npx prisma db push # Push schema changes without migration (dev only) +``` + +## Architecture + +**Stack**: Next.js 16.2.1 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Prisma · PostgreSQL 16 · Cloudflare R2 + +**Path alias**: `@/*` maps to project root (configured in tsconfig.json). + +### App Router Structure + +``` +app/ +├── page.tsx # Homepage (manga grid) +├── layout.tsx # Root layout (Geist fonts, metadata) +├── manga/[slug]/page.tsx # Manga detail + chapter list +├── manga/[slug]/[chapter]/page.tsx # Chapter reader +├── api/search/route.ts # Search endpoint (?q=) +├── api/manga/route.ts # Manga CRUD (admin) +├── api/upload/route.ts # R2 presigned URL generation +└── sitemap.ts # Auto-generated sitemap +``` + +### Data Flow + +- **Database**: Prisma ORM → PostgreSQL. Models: `Manga`, `Chapter`, `Page` (see `prisma/schema.prisma`). +- **Images**: Stored in Cloudflare R2 (S3-compatible). Upload flow: backend generates presigned URL → browser uploads directly to R2 → database records the image path. +- **R2 bucket layout**: `/manga/{manga_id}/{chapter_id}/{page_number}.webp` and `/thumbnails/{manga_id}/cover.webp`. +- **Search**: PostgreSQL `contains` with `insensitive` mode (phase 1). No external search engine needed until >10k titles. + +### Key Libraries + +- `@aws-sdk/client-s3` + `@aws-sdk/s3-request-presigner` — R2 uploads (S3-compatible API) +- `prisma` / `@prisma/client` — ORM and database client + +## Deployment + +- **Docker**: `docker build` from root Dockerfile → container exposes port 3000, mapped to host port 3001. +- **Compose**: `docker-compose.yml` runs both the app and PostgreSQL 16. +- **Flow**: Push to Gitea → `git pull` on Proxmox Docker host → rebuild → restart via Portainer. +- **Reverse proxy**: aaPanel Nginx routes `manga.04080616.xyz` → `http://127.0.0.1:3001`. +- **Port 3001** (app) and **5433** (Postgres) on the host — many other ports are taken. + +## Environment Variables + +Required in `.env` (see docker-compose.yml for container equivalents): + +- `DATABASE_URL` — PostgreSQL connection string +- `R2_ACCOUNT_ID`, `R2_ACCESS_KEY`, `R2_SECRET_KEY`, `R2_BUCKET`, `R2_PUBLIC_URL` — Cloudflare R2 + +## Content Policy + +All manga content must be **public domain or free** (Comic Book Plus, Digital Comic Museum, Internet Archive). No copyrighted material. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f917a6d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,8 @@ +FROM node:20-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +RUN npm run build +EXPOSE 3000 +CMD ["npm", "start"] diff --git a/app/page.tsx b/app/page.tsx index 3f36f7c..2dd076a 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,14 @@ -import Image from "next/image"; - export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
+
+
+

+ SunnyMH Manga +

+

+ Hello World — Coming Soon +

+
); } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..89a4884 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +services: + manga-app: + build: . + pull_policy: build + ports: + - "3001:3000" + environment: + DATABASE_URL: postgresql://manga_user:${POSTGRES_PASSWORD}@manga-db:5432/manga_db + R2_ACCOUNT_ID: ${R2_ACCOUNT_ID} + R2_ACCESS_KEY: ${R2_ACCESS_KEY} + R2_SECRET_KEY: ${R2_SECRET_KEY} + R2_BUCKET: ${R2_BUCKET} + R2_PUBLIC_URL: ${R2_PUBLIC_URL} + depends_on: + - manga-db + restart: unless-stopped + + manga-db: + image: postgres:16 + volumes: + - manga_pgdata:/var/lib/postgresql/data + environment: + POSTGRES_USER: manga_user + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: manga_db + restart: unless-stopped + +volumes: + manga_pgdata: