Add local development environment with switchable backend
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
All checks were successful
ci/woodpecker/push/deploy Pipeline was successful
Set up local dev workflow: SSH tunnel to dev DB, Next.js API proxy via rewrites, Docker or native backend on port 5000. Update and restructure project documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -21,6 +21,9 @@ backend/.idea/
|
|||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
|
||||||
|
# Local development
|
||||||
|
backend/src/*/appsettings.Local.json
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
58
CLAUDE.md
58
CLAUDE.md
@@ -28,7 +28,13 @@ GBSite/
|
|||||||
│ └── Dockerfile # Multi-stage: dotnet sdk → aspnet runtime
|
│ └── Dockerfile # Multi-stage: dotnet sdk → aspnet runtime
|
||||||
├── deploy/
|
├── deploy/
|
||||||
│ ├── docker-compose.prod.yml
|
│ ├── docker-compose.prod.yml
|
||||||
│ └── docker-compose.dev.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/
|
├── .woodpecker/
|
||||||
│ └── deploy.yml # CI/CD pipeline
|
│ └── deploy.yml # CI/CD pipeline
|
||||||
└── CLAUDE.md # This file
|
└── CLAUDE.md # This file
|
||||||
@@ -40,8 +46,9 @@ GBSite/
|
|||||||
|-----|-------------|---------|-----|
|
|-----|-------------|---------|-----|
|
||||||
| **Prod** | https://new.goodbrick.com.ua | https://new.goodbrick.com.ua/api/* | prod_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 |
|
| **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
|
## Docker Containers (Server)
|
||||||
|
|
||||||
| Container | Internal Port | Host Port | Network |
|
| Container | Internal Port | Host Port | Network |
|
||||||
|-----------|--------------|-----------|---------|
|
|-----------|--------------|-----------|---------|
|
||||||
@@ -58,7 +65,48 @@ Path-based routing — один домен для фронта и API:
|
|||||||
|
|
||||||
Backend получает запросы с prefix `/api/` (Caddy использует `handle`, не `handle_path`).
|
Backend получает запросы с prefix `/api/` (Caddy использует `handle`, не `handle_path`).
|
||||||
|
|
||||||
## Development
|
## 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
|
### Frontend
|
||||||
```bash
|
```bash
|
||||||
@@ -91,7 +139,7 @@ cd backend && dotnet build
|
|||||||
- `src/components/` — React components (create when needed)
|
- `src/components/` — React components (create when needed)
|
||||||
- shadcn/ui components via `npx shadcn@latest add <component>`
|
- shadcn/ui components via `npx shadcn@latest add <component>`
|
||||||
- Server-side calls to backend use `INTERNAL_API_URL` env var
|
- Server-side calls to backend use `INTERNAL_API_URL` env var
|
||||||
- Client-side calls use relative paths (`/api/...`) — Caddy proxies them
|
- Client-side calls use relative paths (`/api/...`) — Caddy proxies them on server, Next.js rewrites locally
|
||||||
|
|
||||||
## Git & CI/CD
|
## Git & CI/CD
|
||||||
|
|
||||||
@@ -118,6 +166,7 @@ Host=postgres;Port=5432;Database={db};Username={user};Password={pass}
|
|||||||
```
|
```
|
||||||
|
|
||||||
Configured via env var `ConnectionStrings__Default` in docker-compose files.
|
Configured via env var `ConnectionStrings__Default` in docker-compose files.
|
||||||
|
Locally via `appsettings.Local.json` with SSH tunnel (`localhost:5433`).
|
||||||
|
|
||||||
## Key Rules
|
## Key Rules
|
||||||
|
|
||||||
@@ -125,3 +174,4 @@ Configured via env var `ConnectionStrings__Default` in docker-compose files.
|
|||||||
- Frontend standalone output (`output: "standalone"` in next.config.ts) — required for Docker
|
- Frontend standalone output (`output: "standalone"` in next.config.ts) — required for Docker
|
||||||
- Backend listens on port 5000 (`ASPNETCORE_URLS=http://+:5000`)
|
- Backend listens on port 5000 (`ASPNETCORE_URLS=http://+:5000`)
|
||||||
- All containers must be in `app-network` (external Docker network)
|
- All containers must be in `app-network` (external Docker network)
|
||||||
|
- `appsettings.Local.json` is gitignored — local dev overrides only
|
||||||
|
|||||||
129
README.md
129
README.md
@@ -1,94 +1,59 @@
|
|||||||
# GoodBrick Project
|
# GoodBrick Project
|
||||||
|
|
||||||
Корпоративная инфраструктура для компании GoodBrick на базе self-hosted сервисов.
|
Сайт производителя фасадной плитки GoodBrick — каталог продукции, корпоративная информация.
|
||||||
|
|
||||||
## 🚀 Deployed Services
|
## Services
|
||||||
|
|
||||||
| Service | URL | Status | Purpose |
|
| Service | URL | Purpose |
|
||||||
|---------|-----|--------|---------|
|
|---------|-----|---------|
|
||||||
| **Gitea** | https://git.goodbrick.com.ua | ✅ Running | Git сервер |
|
| **Production** | https://new.goodbrick.com.ua | Продакшн приложение |
|
||||||
| **Uptime Kuma** | https://status.goodbrick.com.ua | ✅ Running | Мониторинг |
|
| **Development** | https://dev.goodbrick.com.ua | Dev окружение |
|
||||||
| **Woodpecker CI** | https://ci.goodbrick.com.ua | ✅ Running | CI/CD |
|
| **Gitea** | https://git.goodbrick.com.ua | Git сервер |
|
||||||
| **Production App** | https://new.goodbrick.com.ua | 🔨 TODO | Продакшн приложение |
|
| **Woodpecker CI** | https://ci.goodbrick.com.ua | CI/CD |
|
||||||
| **Development App** | https://dev.goodbrick.com.ua | 🔨 TODO | Dev окружение |
|
| **Uptime Kuma** | https://status.goodbrick.com.ua | Мониторинг |
|
||||||
|
|
||||||
## 📚 Documentation
|
## Stack
|
||||||
|
|
||||||
- **[SERVER.md](./SERVER.md)** - Полная документация по серверу, сервисам, Docker, базам данных и troubleshooting
|
- **Frontend:** Next.js 16 + shadcn/ui + Tailwind CSS 4
|
||||||
|
- **Backend:** .NET 9 Web API + Npgsql
|
||||||
|
- **Database:** PostgreSQL 16
|
||||||
|
- **Proxy:** Caddy 2 (auto-SSL)
|
||||||
|
- **CI/CD:** Woodpecker CI
|
||||||
|
|
||||||
## 🖥️ Server Info
|
## Local Development
|
||||||
|
|
||||||
|
See [CLAUDE.md](./CLAUDE.md#local-development) for full local dev setup.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Terminal 1: SSH tunnel to dev database
|
||||||
|
.\scripts\local-tunnel.ps1
|
||||||
|
|
||||||
|
# Terminal 2: Backend (choose one)
|
||||||
|
.\scripts\local-backend-dotnet.ps1 # native, with hot-reload
|
||||||
|
.\scripts\local-backend-docker.ps1 # Docker container
|
||||||
|
|
||||||
|
# Terminal 3: Frontend
|
||||||
|
.\scripts\local-frontend.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- **[CLAUDE.md](./CLAUDE.md)** — Project conventions, structure, local dev setup
|
||||||
|
- **[SERVER.md](./SERVER.md)** — Server infrastructure, services, troubleshooting
|
||||||
|
|
||||||
|
## Git Workflow
|
||||||
|
|
||||||
|
- `main` branch → auto-deploy to production
|
||||||
|
- `dev` branch → auto-deploy to dev environment
|
||||||
|
- Commit style: conventional, English
|
||||||
|
|
||||||
|
## Server
|
||||||
|
|
||||||
- **Host:** 31.131.18.254
|
- **Host:** 31.131.18.254
|
||||||
- **SSH:** `ssh deploy@31.131.18.254` (key-based auth)
|
- **SSH:** `ssh deploy@31.131.18.254` (key-based)
|
||||||
- **OS:** Linux
|
|
||||||
- **Docker Network:** `app-network`
|
- **Docker Network:** `app-network`
|
||||||
|
|
||||||
## 🗄️ Databases
|
## Next Steps
|
||||||
|
|
||||||
PostgreSQL 16 с отдельными базами для каждого сервиса:
|
- [ ] Настроить email для Gitea
|
||||||
- `gitea_db` - Gitea
|
- [ ] Настроить автобэкапы баз данных
|
||||||
- `prod_db` - Production app
|
|
||||||
- `dev_db` - Development app
|
|
||||||
|
|
||||||
Все credentials в `/srv/postgres/CREDENTIALS.txt` на сервере.
|
|
||||||
|
|
||||||
## 🔧 Stack
|
|
||||||
|
|
||||||
- **Reverse Proxy:** Caddy 2 (автоматический SSL)
|
|
||||||
- **Database:** PostgreSQL 16
|
|
||||||
- **Git:** Gitea
|
|
||||||
- **CI/CD:** Woodpecker CI
|
|
||||||
- **Monitoring:** Uptime Kuma
|
|
||||||
- **Container:** Docker + Docker Compose
|
|
||||||
|
|
||||||
## 📝 Quick Commands
|
|
||||||
|
|
||||||
### SSH в сервер
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254
|
|
||||||
```
|
|
||||||
|
|
||||||
### Просмотр всех контейнеров
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker ps'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Просмотр логов
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker logs <container_name> --tail 50'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Рестарт сервиса
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose restart'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🏗️ Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
C:\Work\goodbrick\GBSite\
|
|
||||||
├── README.md ← Этот файл
|
|
||||||
├── SERVER.md ← Полная документация сервера
|
|
||||||
└── [project files] ← Файлы проектов
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔐 Security
|
|
||||||
|
|
||||||
- SSH: Только key-based аутентификация
|
|
||||||
- PostgreSQL: Изолированные пользователи для каждого приложения
|
|
||||||
- Docker: Все сервисы в изолированной сети `app-network`
|
|
||||||
- SSL: Автоматические сертификаты через Let's Encrypt
|
|
||||||
|
|
||||||
## 🎯 Next Steps
|
|
||||||
|
|
||||||
1. Deploy production app на `new.goodbrick.com.ua`
|
|
||||||
2. Deploy development app на `dev.goodbrick.com.ua`
|
|
||||||
3. Настроить CI/CD пайплайны в Woodpecker
|
|
||||||
4. Настроить email для Gitea
|
|
||||||
5. Настроить автобэкапы баз данных
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Документация актуальна на:** 2026-02-09
|
|
||||||
**Разработчик:** Ivan
|
|
||||||
**AI Assistant:** Claude Sonnet 4.5
|
|
||||||
|
|||||||
323
SERVER.md
323
SERVER.md
@@ -3,7 +3,7 @@
|
|||||||
## SSH Access
|
## SSH Access
|
||||||
- **Host:** 31.131.18.254
|
- **Host:** 31.131.18.254
|
||||||
- **User:** deploy
|
- **User:** deploy
|
||||||
- **Auth:** SSH key (already configured, no password needed)
|
- **Auth:** SSH key (no password)
|
||||||
- **Command:** `ssh deploy@31.131.18.254`
|
- **Command:** `ssh deploy@31.131.18.254`
|
||||||
|
|
||||||
## Server Structure
|
## Server Structure
|
||||||
@@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
/srv/
|
/srv/
|
||||||
├── apps/ # Empty, ready for applications
|
├── apps/
|
||||||
|
│ ├── gb-site/ # Production app (main branch)
|
||||||
|
│ └── gb-site-dev/ # Development app (dev branch, git worktree)
|
||||||
├── gitea/ # Git server
|
├── gitea/ # Git server
|
||||||
├── postgres/ # PostgreSQL database
|
├── postgres/ # PostgreSQL database
|
||||||
├── proxy/ # Caddy reverse proxy
|
├── proxy/ # Caddy reverse proxy
|
||||||
@@ -27,12 +29,13 @@
|
|||||||
|
|
||||||
## Live Services
|
## Live Services
|
||||||
|
|
||||||
### Production URLs
|
| Service | URL |
|
||||||
- **Gitea:** https://git.goodbrick.com.ua
|
|---------|-----|
|
||||||
- **Uptime Kuma:** https://status.goodbrick.com.ua
|
| **Production App** | https://new.goodbrick.com.ua |
|
||||||
- **Woodpecker CI:** https://ci.goodbrick.com.ua ✅ Working!
|
| **Development App** | https://dev.goodbrick.com.ua |
|
||||||
- **Production App:** https://new.goodbrick.com.ua (TODO)
|
| **Gitea** | https://git.goodbrick.com.ua |
|
||||||
- **Development App:** https://dev.goodbrick.com.ua (TODO)
|
| **Woodpecker CI** | https://ci.goodbrick.com.ua |
|
||||||
|
| **Uptime Kuma** | https://status.goodbrick.com.ua |
|
||||||
|
|
||||||
All domains point to `31.131.18.254` via A records.
|
All domains point to `31.131.18.254` via A records.
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ All domains point to `31.131.18.254` via A records.
|
|||||||
- **SSL:** Automatic via Let's Encrypt
|
- **SSL:** Automatic via Let's Encrypt
|
||||||
|
|
||||||
#### Caddyfile Routes
|
#### Caddyfile Routes
|
||||||
|
|
||||||
```caddyfile
|
```caddyfile
|
||||||
git.goodbrick.com.ua {
|
git.goodbrick.com.ua {
|
||||||
reverse_proxy gitea:3000
|
reverse_proxy gitea:3000
|
||||||
@@ -65,17 +69,25 @@ ci.goodbrick.com.ua {
|
|||||||
}
|
}
|
||||||
|
|
||||||
new.goodbrick.com.ua {
|
new.goodbrick.com.ua {
|
||||||
reverse_proxy new:3000
|
handle /api/* {
|
||||||
|
reverse_proxy gb-prod-backend:5000
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
reverse_proxy gb-prod-frontend:3000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev.goodbrick.com.ua {
|
dev.goodbrick.com.ua {
|
||||||
reverse_proxy dev:3000
|
handle /api/* {
|
||||||
|
reverse_proxy gb-dev-backend:5000
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
reverse_proxy gb-dev-frontend:3000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important Notes:**
|
**Important:** `handle` (not `handle_path`) preserves the `/api/` prefix in the request.
|
||||||
- Use container names (not `127.0.0.1` or `localhost`)
|
|
||||||
- `flush_interval -1` required for Woodpecker's Server-Sent Events
|
|
||||||
|
|
||||||
**Restart:**
|
**Restart:**
|
||||||
```bash
|
```bash
|
||||||
@@ -88,7 +100,7 @@ ssh deploy@31.131.18.254 'cd /srv/proxy && docker compose restart'
|
|||||||
- **Location:** `/srv/postgres`
|
- **Location:** `/srv/postgres`
|
||||||
- **Container:** `postgres`
|
- **Container:** `postgres`
|
||||||
- **Image:** `postgres:16`
|
- **Image:** `postgres:16`
|
||||||
- **Port:** 127.0.0.1:5432 (localhost only)
|
- **Port:** 127.0.0.1:5432 (localhost only, not exposed to internet)
|
||||||
- **Data:** `/srv/postgres/data`
|
- **Data:** `/srv/postgres/data`
|
||||||
- **Credentials:** `/srv/postgres/CREDENTIALS.txt` (chmod 600)
|
- **Credentials:** `/srv/postgres/CREDENTIALS.txt` (chmod 600)
|
||||||
|
|
||||||
@@ -102,34 +114,13 @@ Database: app
|
|||||||
|
|
||||||
#### Application Databases
|
#### Application Databases
|
||||||
|
|
||||||
**Gitea:**
|
| App | User | Password | Database |
|
||||||
```
|
|-----|------|----------|----------|
|
||||||
User: gitea_user
|
| Gitea | gitea_user | gitea_pass_zYWT5JWu3iAbbW7m | gitea_db |
|
||||||
Password: gitea_pass_zYWT5JWu3iAbbW7m
|
| Production | prod_user | prod_pass_kL9mN2pQ7xR8sT4v | prod_db |
|
||||||
Database: gitea_db
|
| Development | dev_user | dev_pass_vB6nM3qP8yW2rT9k | dev_db |
|
||||||
Connection String: postgres://gitea_user:gitea_pass_zYWT5JWu3iAbbW7m@postgres:5432/gitea_db
|
|
||||||
```
|
|
||||||
|
|
||||||
**Production App:**
|
Each app has isolated DB and user. Users cannot access other databases.
|
||||||
```
|
|
||||||
User: prod_user
|
|
||||||
Password: prod_pass_kL9mN2pQ7xR8sT4v
|
|
||||||
Database: prod_db
|
|
||||||
Connection String: postgres://prod_user:prod_pass_kL9mN2pQ7xR8sT4v@postgres:5432/prod_db
|
|
||||||
```
|
|
||||||
|
|
||||||
**Development App:**
|
|
||||||
```
|
|
||||||
User: dev_user
|
|
||||||
Password: dev_pass_vB6nM3qP8yW2rT9k
|
|
||||||
Database: dev_db
|
|
||||||
Connection String: postgres://dev_user:dev_pass_vB6nM3qP8yW2rT9k@postgres:5432/dev_db
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important:**
|
|
||||||
- Each app has its own isolated database and user
|
|
||||||
- Users cannot access other databases
|
|
||||||
- PostgreSQL only accessible via Docker network
|
|
||||||
|
|
||||||
**Restart:**
|
**Restart:**
|
||||||
```bash
|
```bash
|
||||||
@@ -143,34 +134,13 @@ ssh deploy@31.131.18.254 'cd /srv/postgres && docker compose restart'
|
|||||||
- **Container:** `gitea`
|
- **Container:** `gitea`
|
||||||
- **Image:** `gitea/gitea:latest`
|
- **Image:** `gitea/gitea:latest`
|
||||||
- **URL:** https://git.goodbrick.com.ua
|
- **URL:** https://git.goodbrick.com.ua
|
||||||
- **Admin:** admin (set during installation)
|
- **Ports:** 127.0.0.1:3002:3000 (HTTP), 127.0.0.1:2222:22 (SSH)
|
||||||
- **Ports:**
|
- **Database:** gitea_db
|
||||||
- 127.0.0.1:3002:3000 (HTTP)
|
|
||||||
- 127.0.0.1:2222:22 (SSH)
|
|
||||||
- **Database:** gitea_db (PostgreSQL)
|
|
||||||
- **Data:** `/srv/gitea/data`
|
|
||||||
|
|
||||||
#### OAuth Application for Woodpecker
|
#### OAuth Application for Woodpecker
|
||||||
- **Client ID:** 157422cf-7391-4fb0-8f2d-27083676dda6
|
- **Client ID:** 157422cf-7391-4fb0-8f2d-27083676dda6
|
||||||
- **Redirect URI:** https://ci.goodbrick.com.ua/authorize
|
- **Redirect URI:** https://ci.goodbrick.com.ua/authorize
|
||||||
|
|
||||||
**Config:**
|
|
||||||
```yaml
|
|
||||||
environment:
|
|
||||||
- GITEA__server__DOMAIN=git.goodbrick.com.ua
|
|
||||||
- GITEA__server__ROOT_URL=https://git.goodbrick.com.ua/
|
|
||||||
- GITEA__webhook__ALLOWED_HOST_LIST=* # Allows webhooks to Woodpecker
|
|
||||||
- GITEA__database__DB_TYPE=postgres
|
|
||||||
- GITEA__database__HOST=postgres:5432
|
|
||||||
- GITEA__database__NAME=gitea_db
|
|
||||||
- GITEA__database__USER=gitea_user
|
|
||||||
```
|
|
||||||
|
|
||||||
**Restart:**
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/gitea && docker compose restart'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 4. Uptime Kuma (Monitoring)
|
### 4. Uptime Kuma (Monitoring)
|
||||||
@@ -179,244 +149,77 @@ ssh deploy@31.131.18.254 'cd /srv/gitea && docker compose restart'
|
|||||||
- **Image:** `louislam/uptime-kuma:2`
|
- **Image:** `louislam/uptime-kuma:2`
|
||||||
- **URL:** https://status.goodbrick.com.ua
|
- **URL:** https://status.goodbrick.com.ua
|
||||||
- **Port:** 127.0.0.1:3001:3001
|
- **Port:** 127.0.0.1:3001:3001
|
||||||
- **Data:** `/srv/uptime-kuma/data`
|
|
||||||
|
|
||||||
**Restart:**
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/uptime-kuma && docker compose restart'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 5. Woodpecker CI ✅
|
### 5. Woodpecker CI
|
||||||
- **Location:** `/srv/woodpecker`
|
- **Location:** `/srv/woodpecker`
|
||||||
- **Containers:** `woodpecker-server`, `woodpecker-agent`
|
- **Containers:** `woodpecker-server`, `woodpecker-agent`
|
||||||
- **Image:** `woodpeckerci/woodpecker-server:latest` (v2.8.3), `woodpeckerci/woodpecker-agent:latest`
|
- **Image:** `woodpeckerci/woodpecker-server:latest` (v2.8.3+)
|
||||||
- **URL:** https://ci.goodbrick.com.ua
|
- **URL:** https://ci.goodbrick.com.ua
|
||||||
- **Port:** 127.0.0.1:8000:8000
|
- **Port:** 127.0.0.1:8000:8000
|
||||||
- **Data:** `/srv/woodpecker/data`
|
|
||||||
|
|
||||||
**Config:**
|
**Notes:**
|
||||||
```yaml
|
- OAuth authentication via Gitea
|
||||||
environment:
|
|
||||||
# Server
|
|
||||||
- WOODPECKER_OPEN=true
|
|
||||||
- WOODPECKER_HOST=https://ci.goodbrick.com.ua
|
|
||||||
- WOODPECKER_GITEA=true
|
|
||||||
- WOODPECKER_GITEA_URL=https://git.goodbrick.com.ua
|
|
||||||
- WOODPECKER_GITEA_CLIENT=157422cf-7391-4fb0-8f2d-27083676dda6
|
|
||||||
- WOODPECKER_GITEA_SECRET=gto_2r3udrnmvtebt37v35bzl375eq5bumxqu2dpwwdfflbumnp5fy7q
|
|
||||||
- WOODPECKER_AGENT_SECRET=supersecrettoken123
|
|
||||||
- WOODPECKER_ADMIN=admin
|
|
||||||
|
|
||||||
# Agent
|
|
||||||
- WOODPECKER_SERVER=woodpecker-server:9000
|
|
||||||
- WOODPECKER_AGENT_SECRET=supersecrettoken123
|
|
||||||
```
|
|
||||||
|
|
||||||
**Status:** ✅ Working! Login with Gitea account.
|
|
||||||
|
|
||||||
**Important Notes:**
|
|
||||||
- Uses OAuth authentication via Gitea
|
|
||||||
- Agent connects to server on port 9000 (gRPC)
|
- Agent connects to server on port 9000 (gRPC)
|
||||||
- Agent has access to Docker socket for running builds
|
- Agent has Docker socket access for builds
|
||||||
- Version 2.8.3 fixes JavaScript security issues (v2.7.2 had SES errors)
|
- v2.8.3+ required (older versions have SES JS errors)
|
||||||
|
|
||||||
**Restart:**
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/woodpecker && docker compose restart'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Common Commands
|
## Common Commands
|
||||||
|
|
||||||
### View all containers
|
|
||||||
```bash
|
```bash
|
||||||
|
# View all containers
|
||||||
ssh deploy@31.131.18.254 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
|
ssh deploy@31.131.18.254 'docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
|
||||||
```
|
|
||||||
|
|
||||||
### View container logs
|
# View container logs
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker logs <container_name> --tail 50'
|
ssh deploy@31.131.18.254 'docker logs <container_name> --tail 50'
|
||||||
ssh deploy@31.131.18.254 'docker logs <container_name> -f' # Follow logs
|
ssh deploy@31.131.18.254 'docker logs <container_name> -f'
|
||||||
```
|
|
||||||
|
|
||||||
### Restart a service
|
# Restart a service
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose restart'
|
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose restart'
|
||||||
```
|
|
||||||
|
|
||||||
### Restart all services
|
# Access PostgreSQL CLI
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/postgres && docker compose restart && \
|
|
||||||
cd /srv/gitea && docker compose restart && \
|
|
||||||
cd /srv/uptime-kuma && docker compose restart && \
|
|
||||||
cd /srv/woodpecker && docker compose restart && \
|
|
||||||
cd /srv/proxy && docker compose restart'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Access PostgreSQL CLI
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker exec -it postgres psql -U app -d app'
|
ssh deploy@31.131.18.254 'docker exec -it postgres psql -U app -d app'
|
||||||
```
|
|
||||||
|
|
||||||
### Check Docker network
|
# Check Docker network
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker network inspect app-network'
|
ssh deploy@31.131.18.254 'docker network inspect app-network'
|
||||||
ssh deploy@31.131.18.254 'docker ps --format "{{.Names}}\t{{.Networks}}"'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test container connectivity
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'docker exec <container1> ping -c 2 <container2>'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### Issue: Container can't reach another container
|
### Container can't reach another container
|
||||||
**Symptoms:** "connection refused", "no such host"
|
**Symptoms:** "connection refused", "no such host"
|
||||||
|
|
||||||
**Check:**
|
|
||||||
```bash
|
```bash
|
||||||
# Verify all containers are in app-network
|
# Check all containers are in app-network
|
||||||
ssh deploy@31.131.18.254 'docker ps --format "{{.Names}}\t{{.Networks}}"'
|
ssh deploy@31.131.18.254 'docker ps --format "{{.Names}}\t{{.Networks}}"'
|
||||||
|
# Fix: ensure docker-compose has networks: app-network: external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
**Solution:**
|
### Caddy "502 Bad Gateway"
|
||||||
1. Ensure all docker-compose.yml files have:
|
1. Target service is down → `docker ps | grep <service>`
|
||||||
```yaml
|
2. Not in app-network → check networks
|
||||||
networks:
|
3. Wrong port/name in Caddyfile → check config
|
||||||
app-network:
|
|
||||||
external: true
|
|
||||||
```
|
|
||||||
2. Restart the affected service
|
|
||||||
|
|
||||||
---
|
### Woodpecker blank page / JS errors
|
||||||
|
1. Update to v2.8.3+
|
||||||
### Issue: "dial tcp: lookup <service> on 127.0.0.11:53: no such host"
|
2. Ensure `flush_interval -1` in Caddy
|
||||||
**Cause:** Container not in app-network
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
```bash
|
|
||||||
ssh deploy@31.131.18.254 'cd /srv/<service> && docker compose down && docker compose up -d'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Issue: Caddy shows "502 Bad Gateway"
|
|
||||||
**Causes:**
|
|
||||||
1. Target service is down
|
|
||||||
2. Target service not in app-network
|
|
||||||
3. Wrong port/container name in Caddyfile
|
|
||||||
|
|
||||||
**Check:**
|
|
||||||
```bash
|
|
||||||
# Check if target is running
|
|
||||||
ssh deploy@31.131.18.254 'docker ps | grep <service>'
|
|
||||||
|
|
||||||
# Check Caddy logs
|
|
||||||
ssh deploy@31.131.18.254 'docker logs caddy --tail 50'
|
|
||||||
|
|
||||||
# Test direct connection
|
|
||||||
ssh deploy@31.131.18.254 'docker exec caddy wget -O- http://<container>:<port>'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Issue: Woodpecker shows blank page or JavaScript errors
|
|
||||||
**Symptoms:** "SES Removing unpermitted intrinsics", SyntaxError
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
1. Update to latest version (2.8.3+)
|
|
||||||
2. Ensure Caddy has `flush_interval -1` for SSE support
|
|
||||||
3. Clear browser cache (Ctrl+Shift+R)
|
3. Clear browser cache (Ctrl+Shift+R)
|
||||||
|
|
||||||
---
|
### SSL certificate issues
|
||||||
|
Usually DNS propagation delay. Check `docker logs caddy | grep certificate` and `nslookup <domain>`.
|
||||||
### Issue: SSL certificate not working
|
|
||||||
**Cause:** Usually DNS propagation or Let's Encrypt rate limits
|
|
||||||
|
|
||||||
**Check:**
|
|
||||||
```bash
|
|
||||||
# Check Caddy logs
|
|
||||||
ssh deploy@31.131.18.254 'docker logs caddy | grep -i certificate'
|
|
||||||
|
|
||||||
# Check if domain resolves
|
|
||||||
nslookup <domain>
|
|
||||||
```
|
|
||||||
|
|
||||||
**Solution:** Wait for DNS propagation (up to 24h) or check DNS A records.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Security Notes
|
## TODO
|
||||||
|
|
||||||
1. **PostgreSQL:**
|
- [ ] Настроить email для Gitea
|
||||||
- Not exposed to internet (127.0.0.1 only)
|
- [ ] Настроить автобэкапы PostgreSQL
|
||||||
- Separate users for each application
|
- [ ] Настроить алерты в Uptime Kuma
|
||||||
- Credentials stored in `/srv/postgres/CREDENTIALS.txt` (chmod 600)
|
|
||||||
|
|
||||||
2. **SSH:**
|
|
||||||
- Key-based authentication only
|
|
||||||
- User `deploy` has Docker permissions
|
|
||||||
|
|
||||||
3. **Docker Network:**
|
|
||||||
- All containers isolated in `app-network`
|
|
||||||
- No direct internet access except through Caddy
|
|
||||||
|
|
||||||
4. **Secrets:**
|
|
||||||
- Never commit credentials to git
|
|
||||||
- Store sensitive data in environment variables
|
|
||||||
- Use `.env` files (gitignored)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Next Steps / TODO
|
**Last Updated:** 2026-02-10
|
||||||
|
|
||||||
- [ ] Deploy production app to `new.goodbrick.com.ua` (port 3100)
|
|
||||||
- [ ] Deploy development app to `dev.goodbrick.com.ua` (port 3200)
|
|
||||||
- [ ] Set up email for Gitea (optional)
|
|
||||||
- [ ] Configure Woodpecker pipelines for repositories
|
|
||||||
- [ ] Set up automatic backups for PostgreSQL databases
|
|
||||||
- [ ] Configure monitoring alerts in Uptime Kuma
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## File Locations Summary
|
|
||||||
|
|
||||||
```
|
|
||||||
Server (31.131.18.254):
|
|
||||||
├── /srv/
|
|
||||||
│ ├── apps/
|
|
||||||
│ ├── gitea/
|
|
||||||
│ │ ├── docker-compose.yml
|
|
||||||
│ │ └── data/
|
|
||||||
│ ├── postgres/
|
|
||||||
│ │ ├── docker-compose.yml
|
|
||||||
│ │ ├── data/
|
|
||||||
│ │ └── CREDENTIALS.txt ← All DB credentials
|
|
||||||
│ ├── proxy/
|
|
||||||
│ │ ├── docker-compose.yml
|
|
||||||
│ │ ├── Caddyfile ← All routes
|
|
||||||
│ │ ├── data/
|
|
||||||
│ │ └── config/
|
|
||||||
│ ├── uptime-kuma/
|
|
||||||
│ │ ├── docker-compose.yml
|
|
||||||
│ │ └── data/
|
|
||||||
│ └── woodpecker/
|
|
||||||
│ ├── docker-compose.yml
|
|
||||||
│ └── data/
|
|
||||||
|
|
||||||
Local:
|
|
||||||
└── C:\Work\goodbrick\GBSite\
|
|
||||||
├── SERVER.md ← This file
|
|
||||||
└── README.md ← Project overview
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated:** 2026-02-09
|
|
||||||
**Maintained by:** Claude AI Assistant
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.Local.json", optional: true, reloadOnChange: true);
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "http://localhost:5224",
|
"applicationUrl": "http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
9
deploy/docker-compose.local.yml
Normal file
9
deploy/docker-compose.local.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
gb-local-backend:
|
||||||
|
build: ../backend
|
||||||
|
container_name: gb-local-backend
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:5000:5000"
|
||||||
|
environment:
|
||||||
|
- ASPNETCORE_ENVIRONMENT=Development
|
||||||
|
- ConnectionStrings__Default=Host=host.docker.internal;Port=5433;Database=dev_db;Username=dev_user;Password=dev_pass_vB6nM3qP8yW2rT9k
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
# URL бэкенда для server-side запросов (внутри Docker network)
|
# URL бэкенда для server-side запросов (внутри Docker network)
|
||||||
INTERNAL_API_URL=http://gb-prod-backend:5000
|
INTERNAL_API_URL=http://gb-prod-backend:5000
|
||||||
|
|
||||||
|
# URL бэкенда для проксирования /api/* в локальной разработке (не задавать на сервере!)
|
||||||
|
# LOCAL_API_URL=http://localhost:5000
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ import type { NextConfig } from "next";
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: "standalone",
|
output: "standalone",
|
||||||
|
async rewrites() {
|
||||||
|
const apiUrl = process.env.LOCAL_API_URL;
|
||||||
|
if (!apiUrl) return [];
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/api/:path*",
|
||||||
|
destination: `${apiUrl}/api/:path*`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
4
scripts/local-backend-docker.ps1
Normal file
4
scripts/local-backend-docker.ps1
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Write-Host "Starting Docker backend on http://localhost:5000" -ForegroundColor Cyan
|
||||||
|
Write-Host "Requires: SSH tunnel running (scripts/local-tunnel.ps1)"
|
||||||
|
Write-Host ""
|
||||||
|
docker compose -f "$PSScriptRoot\..\deploy\docker-compose.local.yml" up --build
|
||||||
6
scripts/local-backend-dotnet.ps1
Normal file
6
scripts/local-backend-dotnet.ps1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Write-Host "Starting backend (dotnet run) on http://localhost:5000" -ForegroundColor Cyan
|
||||||
|
Write-Host "Requires: SSH tunnel running (scripts/local-tunnel.ps1)"
|
||||||
|
Write-Host ""
|
||||||
|
Push-Location "$PSScriptRoot\..\backend"
|
||||||
|
dotnet run --project src/GBSite.Api
|
||||||
|
Pop-Location
|
||||||
6
scripts/local-frontend.ps1
Normal file
6
scripts/local-frontend.ps1
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Write-Host "Starting frontend on http://localhost:3000" -ForegroundColor Cyan
|
||||||
|
Write-Host "API proxy -> http://localhost:5000"
|
||||||
|
Write-Host ""
|
||||||
|
Push-Location "$PSScriptRoot\..\frontend"
|
||||||
|
npm run dev
|
||||||
|
Pop-Location
|
||||||
4
scripts/local-tunnel.ps1
Normal file
4
scripts/local-tunnel.ps1
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Write-Host "SSH Tunnel: localhost:5433 -> server:5432 (dev_db)" -ForegroundColor Cyan
|
||||||
|
Write-Host "Press Ctrl+C to stop"
|
||||||
|
Write-Host ""
|
||||||
|
ssh -N -L 5433:127.0.0.1:5432 -o ServerAliveInterval=60 deploy@31.131.18.254
|
||||||
Reference in New Issue
Block a user