All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
- Backend: R2StorageService, upload controller (POST /api/upload) - Frontend: CDN url helper, NEXT_PUBLIC_CDN_URL build arg - Deploy: pass R2 secrets from Woodpecker CI to containers via .env - Docs: update CLAUDE.md with CDN and upload conventions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
193 lines
6.9 KiB
Markdown
193 lines
6.9 KiB
Markdown
# GoodBrick Site
|
||
|
||
Сайт производителя фасадной плитки GoodBrick с каталогом продукции.
|
||
|
||
## Stack
|
||
|
||
- **Frontend:** Next.js 16 + shadcn/ui + Tailwind CSS 4 (TypeScript)
|
||
- **Backend:** .NET 9 Web API (C#)
|
||
- **Database:** PostgreSQL 16
|
||
- **Proxy:** Caddy 2 (auto-SSL)
|
||
- **CI/CD:** Woodpecker CI (Gitea OAuth)
|
||
- **Git:** Gitea (self-hosted)
|
||
|
||
## Project Structure
|
||
|
||
```
|
||
GBSite/
|
||
├── frontend/ # Next.js app
|
||
│ ├── src/app/ # App Router pages
|
||
│ ├── src/lib/ # Utilities
|
||
│ ├── Dockerfile # Multi-stage: node:20-alpine
|
||
│ └── package.json
|
||
├── backend/ # .NET 9 Web API
|
||
│ ├── src/GBSite.Api/ # Main API project
|
||
│ │ ├── Controllers/
|
||
│ │ └── Program.cs
|
||
│ ├── GBSite.Api.sln
|
||
│ └── Dockerfile # Multi-stage: dotnet sdk → aspnet runtime
|
||
├── deploy/
|
||
│ ├── docker-compose.prod.yml
|
||
│ ├── docker-compose.dev.yml
|
||
│ └── docker-compose.local.yml # Local backend (Docker mode)
|
||
├── scripts/
|
||
│ ├── local-tunnel.ps1 # SSH tunnel to dev DB
|
||
│ ├── local-backend-docker.ps1 # Start Docker backend
|
||
│ ├── local-backend-dotnet.ps1 # Start native backend
|
||
│ └── local-frontend.ps1 # Start frontend dev server
|
||
├── .woodpecker/
|
||
│ └── deploy.yml # CI/CD pipeline
|
||
└── CLAUDE.md # This file
|
||
```
|
||
|
||
## Environments
|
||
|
||
| Env | Frontend URL | API URL | DB |
|
||
|-----|-------------|---------|-----|
|
||
| **Prod** | https://new.goodbrick.com.ua | https://new.goodbrick.com.ua/api/* | prod_db |
|
||
| **Dev** | https://dev.goodbrick.com.ua | https://dev.goodbrick.com.ua/api/* | dev_db |
|
||
| **Local** | http://localhost:3000 | http://localhost:5000/api/* | dev_db (via SSH tunnel) |
|
||
|
||
## Docker Containers (Server)
|
||
|
||
| Container | Internal Port | Host Port | Network |
|
||
|-----------|--------------|-----------|---------|
|
||
| gb-prod-frontend | 3000 | 127.0.0.1:3100 | app-network |
|
||
| gb-prod-backend | 5000 | 127.0.0.1:5100 | app-network |
|
||
| gb-dev-frontend | 3000 | 127.0.0.1:3200 | app-network |
|
||
| gb-dev-backend | 5000 | 127.0.0.1:5200 | app-network |
|
||
|
||
## Routing (Caddy)
|
||
|
||
Path-based routing — один домен для фронта и API:
|
||
- `domain.com/*` → frontend container (port 3000)
|
||
- `domain.com/api/*` → backend container (port 5000)
|
||
|
||
Backend получает запросы с prefix `/api/` (Caddy использует `handle`, не `handle_path`).
|
||
|
||
## Local Development
|
||
|
||
### Quick Start (3 терминала)
|
||
|
||
**Terminal 1 — SSH Tunnel (всегда нужен):**
|
||
```powershell
|
||
.\scripts\local-tunnel.ps1
|
||
```
|
||
|
||
**Terminal 2 — Backend (выбрать один):**
|
||
```powershell
|
||
# Работа с фронтом — бэкенд в Docker:
|
||
.\scripts\local-backend-docker.ps1
|
||
|
||
# Работа с бэком — dotnet run с hot-reload:
|
||
.\scripts\local-backend-dotnet.ps1
|
||
```
|
||
|
||
**Terminal 3 — Frontend:**
|
||
```powershell
|
||
.\scripts\local-frontend.ps1
|
||
```
|
||
|
||
### Как это работает
|
||
|
||
- **SSH Tunnel:** `localhost:5433` → сервер `31.131.18.254:5432` (dev_db)
|
||
- **Backend:** слушает на `localhost:5000`, подключается к БД через туннель
|
||
- **Frontend:** `npm run dev` на `:3000`, Next.js rewrites проксируют `/api/*` → `localhost:5000`
|
||
- **Переключение режимов:** Ctrl+C в терминале с бэком, запустить другой скрипт. Фронт перезапускать не нужно.
|
||
|
||
### Конфигурация
|
||
|
||
- `backend/src/GBSite.Api/appsettings.Local.json` — connection string к dev_db (gitignored)
|
||
- `frontend/.env.local` — `LOCAL_API_URL` и `INTERNAL_API_URL` для локального проксирования
|
||
- `deploy/docker-compose.local.yml` — Docker backend с `host.docker.internal:5433`
|
||
|
||
### URLs
|
||
- Frontend: http://localhost:3000
|
||
- Backend API: http://localhost:5000/api/health
|
||
- DB: localhost:5433 (через SSH tunnel к dev_db)
|
||
|
||
## Development (manual)
|
||
|
||
### Frontend
|
||
```bash
|
||
cd frontend && npm install && npm run dev
|
||
```
|
||
|
||
### Backend
|
||
```bash
|
||
cd backend && dotnet run --project src/GBSite.Api
|
||
```
|
||
|
||
### Build locally
|
||
```bash
|
||
cd frontend && npm run build
|
||
cd backend && dotnet build
|
||
```
|
||
|
||
## API Conventions
|
||
|
||
- All API routes start with `/api/`
|
||
- Controllers use attribute routing: `[Route("api/[controller]")]`
|
||
- Health check: `GET /api/health` — returns `{ status, database, environment }`
|
||
- Health ping: `GET /api/health/ping` — returns `{ status: "pong" }`
|
||
- Upload: `POST /api/upload` — multipart form, fields: `file` (required), `path` (optional folder prefix)
|
||
|
||
## Frontend Conventions
|
||
|
||
- App Router (not Pages Router)
|
||
- `src/app/` — pages and API routes
|
||
- `src/lib/` — shared utilities
|
||
- `src/components/` — React components (create when needed)
|
||
- shadcn/ui components via `npx shadcn@latest add <component>`
|
||
- Server-side calls to backend use `INTERNAL_API_URL` env var
|
||
- Client-side calls use relative paths (`/api/...`) — Caddy proxies them on server, Next.js rewrites locally
|
||
|
||
## Git & CI/CD
|
||
|
||
- **Repo:** https://git.goodbrick.com.ua/admin/gb-site
|
||
- **Branches:** `main` → prod deploy, `dev` → dev deploy
|
||
- **CI:** Woodpecker auto-deploys on push (SSH to server, git pull, docker compose build & up)
|
||
- **Commit style:** conventional, short descriptions in English
|
||
|
||
## Server
|
||
|
||
- **Host:** 31.131.18.254
|
||
- **SSH:** `ssh deploy@31.131.18.254` (key-based)
|
||
- **App location:** `/srv/apps/gb-site` (main), `/srv/apps/gb-site-dev` (dev worktree)
|
||
- **Caddy config:** `/srv/proxy/Caddyfile`
|
||
- **All services docs:** see SERVER.md
|
||
|
||
## Database
|
||
|
||
PostgreSQL доступен только внутри Docker network (container name: `postgres`).
|
||
|
||
Connection string format:
|
||
```
|
||
Host=postgres;Port=5432;Database={db};Username={user};Password={pass}
|
||
```
|
||
|
||
Configured via env var `ConnectionStrings__Default` in docker-compose files.
|
||
Locally via `appsettings.Local.json` with SSH tunnel (`localhost:5433`).
|
||
|
||
## CDN & Image Storage
|
||
|
||
- **Storage:** Cloudflare R2 (S3-compatible)
|
||
- **CDN domain:** https://cdn.goodbrick.com.ua
|
||
- **Bucket:** goodbrick
|
||
- **Frontend:** `NEXT_PUBLIC_CDN_URL` env var, `cdnUrl()` helper in `src/lib/cdn.ts`
|
||
- **Backend:** `R2__*` env vars, `R2StorageService` for uploads
|
||
- **Upload endpoint:** `POST /api/upload` — multipart/form-data, max 10MB, images only (jpeg/png/webp/avif)
|
||
|
||
### Folder Convention
|
||
- `products/<slug>/main.jpg` — product photos
|
||
- `catalog/<slug>/cover.jpg` — collection covers
|
||
- `site/<name>.jpg` — site-wide images (hero, about, etc.)
|
||
|
||
## Key Rules
|
||
|
||
- Never commit `.env` files or secrets — credentials are in docker-compose env vars on server
|
||
- Frontend standalone output (`output: "standalone"` in next.config.ts) — required for Docker
|
||
- Backend listens on port 5000 (`ASPNETCORE_URLS=http://+:5000`)
|
||
- All containers must be in `app-network` (external Docker network)
|
||
- `appsettings.Local.json` is gitignored — local dev overrides only
|