initial commit

This commit is contained in:
Daniel Noulet 2026-10-07 15:21:47 +02:00
commit e7987e88a7
70 changed files with 13661 additions and 0 deletions

405
docs/API.md Normal file
View file

@ -0,0 +1,405 @@
# API Documentation — go-first / URPI
Base URL: `https://www.urpi.be/api/v1`
Local: `http://localhost:8080/api/v1`
All responses follow a consistent envelope format.
---
## Response Format
### Success
```json
{
"success": true,
"data": {},
"meta": {
"page": 1,
"page_size": 20,
"total": 100,
"total_pages": 5
}
}
```
### Error
```json
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human readable message",
"details": "Optional technical details"
}
}
```
---
## Common Error Codes
| Code | HTTP Status | Description |
|------|-------------|-------------|
| `VALIDATION_ERROR` | 400 | Request body validation failed |
| `INVALID_ID` | 400 | UUID format invalid |
| `NOT_FOUND` | 404 | Resource not found |
| `UNAUTHORIZED` | 401 | Authentication required |
| `FORBIDDEN` | 403 | Insufficient permissions |
| `CONFLICT` | 409 | Resource conflict |
| `RATE_LIMITED` | 429 | Too many requests |
| `INTERNAL_ERROR` | 500 | Server error |
| `SERVICE_UNAVAILABLE` | 503 | Service temporarily unavailable |
---
## Endpoints
### Health Check
**GET** `/health`
No authentication required.
#### Response
```json
{
"success": true,
"data": {
"status": "ok",
"service": "go-first",
"version": "1.0.0"
}
}
```
---
### Skills
#### List Skills
**GET** `/skills`
| Query Param | Type | Description |
|-------------|------|-------------|
| `category` | string | Filter by category (Backend, Frontend, Architecture, DevOps, Database, API) |
#### Response
```json
{
"success": true,
"data": [
{
"id": "uuid",
"name": "Go",
"category": "Backend",
"proficiency": 5,
"icon": "go",
"order": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
#### Get Skill
**GET** `/skills/:id`
| Path Param | Type | Description |
|------------|------|-------------|
| `id` | uuid | Skill UUID |
#### Response
```json
{
"success": true,
"data": {
"id": "uuid",
"name": "Go",
"category": "Backend",
"proficiency": 5,
"icon": "go",
"order": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
}
```
---
### Projects
#### List Projects
**GET** `/projects`
| Query Param | Type | Description |
|-------------|------|-------------|
| `category` | string | Filter by category |
| `featured` | boolean | Filter featured projects only |
#### Response
```json
{
"success": true,
"data": [
{
"id": "uuid",
"title": "E-Commerce Platform",
"description": "Full description...",
"short_desc": "Scalable e-commerce platform...",
"tech_stack": ["Go", "React", "PostgreSQL", "Redis", "Kubernetes"],
"image_url": "/images/projects/ecommerce.jpg",
"demo_url": "https://demo.urpi.be/ecommerce",
"repo_url": "https://github.com/urpi/ecommerce-platform",
"category": "Full Stack",
"featured": true,
"order": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
#### Get Featured Projects
**GET** `/projects/featured`
Returns only projects with `featured: true`.
#### Get Project
**GET** `/projects/:id`
| Path Param | Type | Description |
|------------|------|-------------|
| `id` | uuid | Project UUID |
---
### Experience
#### List Experience
**GET** `/experience`
Returns all work experience entries ordered by `order` field.
#### Response
```json
{
"success": true,
"data": [
{
"id": "uuid",
"company": "URPI",
"role": "Founder & Lead Developer",
"location": "Belgium",
"start_date": "2020-01-01T00:00:00Z",
"end_date": null,
"current": true,
"description": "Founded URPI to provide web application...",
"tech_stack": ["Go", "Java", "React", "Kubernetes", "AWS"],
"order": 1,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
---
### Services
#### List Services
**GET** `/services`
Returns all active services offered.
#### Response
```json
{
"success": true,
"data": [
{
"id": "uuid",
"title": "Web Application Development",
"description": "Custom web applications built with modern technologies...",
"icon": "code",
"features": [
"React/Next.js Frontend",
"Go/Java Backend",
"REST/GraphQL APIs"
],
"order": 1,
"active": true,
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
]
}
```
---
### Contact
#### Submit Contact Form
**POST** `/contact`
Content-Type: `application/json`
| Field | Type | Required | Validation |
|-------|------|----------|------------|
| `name` | string | Yes | 2-100 chars |
| `email` | string | Yes | Valid email format |
| `subject` | string | Yes | 5-200 chars |
| `message` | string | Yes | 10-5000 chars |
#### Request
```json
{
"name": "John Doe",
"email": "john@example.com",
"subject": "Project Inquiry",
"message": "Hello, I'd like to discuss a potential project..."
}
```
#### Response (201 Created)
```json
{
"success": true,
"data": {
"id": "uuid",
"name": "John Doe",
"email": "john@example.com",
"subject": "Project Inquiry",
"message": "Hello, I'd like to discuss a potential project...",
"status": "new",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
```
#### Validation Error (400)
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"details": "Key: 'ContactForm.Email' Error:Field validation for 'Email' failed on the 'email' tag"
}
}
```
---
## Rate Limiting
| Endpoint | Limit |
|----------|-------|
| `/api/v1/*` (general) | 10 req/s per IP |
| `/api/v1/contact` | 5 req/min per IP |
| `/api/v1/health` | No limit |
Headers returned:
- `X-RateLimit-Limit`
- `X-RateLimit-Remaining`
- `X-RateLimit-Reset`
---
## CORS
Allowed origins (configured in `config.yaml`):
- `https://www.urpi.be`
- `https://urpi.be`
- `http://localhost:3000`
- `http://localhost:5173`
Allowed methods: `GET, POST, PUT, PATCH, DELETE, OPTIONS`
Allowed headers: `Origin, Content-Type, Accept, Authorization, X-Requested-With`
Credentials: `true`
---
## Versioning
API version in URL path: `/api/v1/`
Breaking changes will result in `/api/v2/` etc.
Non-breaking additions (new fields, new endpoints) may be added to v1.
---
## Authentication
Currently **no authentication required** for public endpoints.
Future plans:
- JWT-based auth for admin endpoints
- API keys for programmatic access
- OAuth2 for third-party integrations
---
## SDKs / Clients
### JavaScript/TypeScript
```typescript
const API_BASE = '/api/v1';
async function fetchAPI<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const res = await fetch(`${API_BASE}${endpoint}`, {
headers: { 'Content-Type': 'application/json', ...options.headers },
...options,
});
const data = await res.json();
if (!res.ok || !data.success) throw new Error(data.error?.message || 'Error');
return data.data;
}
// Usage
const skills = await fetchAPI<Skill[]>('/skills');
const project = await fetchAPI<Project>('/projects/uuid');
await fetchAPI<ContactSubmission>('/contact', {
method: 'POST',
body: JSON.stringify({ name, email, subject, message })
});
```
### Go
```go
type APIResponse struct {
Success bool `json:"success"`
Data interface{} `json:"data,omitempty"`
Error *APIError `json:"error,omitempty"`
}
func GetSkills(ctx context.Context) ([]Skill, error) {
req, _ := http.NewRequestWithContext(ctx, "GET", "https://www.urpi.be/api/v1/skills", nil)
resp, err := http.DefaultClient.Do(req)
// ... parse response
}
```
---
## Changelog
### v1.0.0 (2024-01-15)
- Initial API release
- Skills, Projects, Experience, Services, Contact endpoints
- Rate limiting and CORS configuration
- Health check endpoint

425
docs/DEPLOYMENT.md Normal file
View file

@ -0,0 +1,425 @@
# Deployment Guide — go-first / URPI
This guide covers deploying the go-first application to production at **https://www.urpi.be**.
---
## 🏗️ Architecture Overview
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Users │────▶│ NGINX │────▶│ Go App │
│ (HTTPS) │ │ (SSL/TLS) │ │ (Port 8080)│
└─────────────┘ └─────────────┘ └──────┬──────┘
│
┌─────────────┐ ┌──────▼──────┐
│ PostgreSQL │◀───▶│ Redis │
│ (Port 5432)│ │ (Port 6379)│
└─────────────┘ └─────────────┘
```
---
## 📋 Prerequisites
### Server Requirements
- **OS**: Ubuntu 22.04+ / Debian 12+ / Alpine Linux
- **CPU**: 2+ vCPUs
- **RAM**: 4GB+ (2GB minimum for app + DB)
- **Disk**: 20GB+ SSD
- **Network**: Public IPv4/IPv6, ports 80, 443, 22
### Software
- Docker 24+ & Docker Compose 2+
- Git
- Certbot (Let's Encrypt)
- (Optional) kubectl for Kubernetes
---
## 🔧 Server Setup
### 1. Initial Server Hardening
```bash
# Update system
sudo apt update && sudo apt upgrade -y
# Create deploy user
sudo adduser deploy --gecos "" --disabled-password
sudo usermod -aG docker deploy
sudo usermod -aG sudo deploy
# Setup SSH keys (from your local machine)
ssh-copy-id deploy@your-server-ip
# Configure firewall
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw enable
```
### 2. Install Docker
```bash
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install Docker Compose
sudo apt install -y docker-compose-plugin
# Verify
docker --version
docker compose version
```
### 3. Install Certbot
```bash
sudo apt install -y certbot python3-certbot-nginx
```
---
## 📦 Application Deployment
### 1. Clone Repository
```bash
cd /opt
sudo git clone https://github.com/dnoulet/go-first.git
sudo chown -R deploy:deploy go-first
cd go-first
```
### 2. Configure Environment
```bash
# Copy production template
cp .env.production.example .env.production
# Edit with your values (use strong passwords!)
vim .env.production
# Required changes:
# - DATABASE_PASSWORD: Strong random password
# - JWT_SECRET: 32+ char random string
# - EMAIL_*: Your SMTP credentials
```
### 3. Generate SSL Certificates
```bash
# Stop any service on port 80
sudo systemctl stop nginx 2>/dev/null || true
# Get certificates
sudo certbot certonly --standalone \
-d www.urpi.be \
-d urpi.be \
--email contact@urpi.be \
--agree-tos --non-interactive
# Certificates will be at:
# /etc/letsencrypt/live/www.urpi.be/fullchain.pem
# /etc/letsencrypt/live/www.urpi.be/privkey.pem
```
### 4. Setup NGINX SSL Directory
```bash
sudo mkdir -p /opt/go-first/deploy/ssl
sudo cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
sudo cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
sudo chown -R deploy:deploy /opt/go-first/deploy/ssl
```
### 5. Deploy with Docker Compose
```bash
cd /opt/go-first
# Build and start
docker compose --env-file .env.production up -d --build
# Verify
docker compose --env-file .env.production ps
docker compose --env-file .env.production logs -f app
```
### 6. Verify Deployment
```bash
# Health check
curl -f https://www.urpi.be/api/v1/health
# Check frontend
curl -f https://www.urpi.be/ | grep -c "URPI"
```
---
## 🔐 SSL Certificate Renewal
### Automatic Renewal (systemd timer)
```bash
# Check certbot timer
sudo systemctl status certbot.timer
# Test renewal
sudo certbot renew --dry-run
```
### Manual Renewal Hook
```bash
# Create renewal hook
sudo tee /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh > /dev/null <<'EOF'
#!/bin/bash
cp /etc/letsencrypt/live/www.urpi.be/fullchain.pem /opt/go-first/deploy/ssl/
cp /etc/letsencrypt/live/www.urpi.be/privkey.pem /opt/go-first/deploy/ssl/
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec nginx nginx -s reload
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/nginx-reload.sh
```
---
## 🗄️ Database Management
### Backup
```bash
# Create backup script
cat > /opt/go-first/backup.sh <<'EOF'
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/backups/go-first"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production \
exec -T postgres pg_dump -U postgres go_first | gzip > "$BACKUP_DIR/go_first_$DATE.sql.gz"
# Keep last 30 days
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +30 -delete
echo "Backup saved to $BACKUP_DIR/go_first_$DATE.sql.gz"
EOF
chmod +x /opt/go-first/backup.sh
# Add to crontab (daily at 2 AM)
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/go-first/backup.sh") | crontab -
```
### Restore
```bash
# Stop app
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production stop app
# Restore
gunzip -c /opt/backups/go-first/go_first_20240115_020000.sql.gz | \
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production exec -T postgres psql -U postgres go_first
# Start app
docker compose -f /opt/go-first/docker-compose.yml --env-file /opt/go-first/.env.production start app
```
---
## 📊 Monitoring & Logging
### Log Aggregation (Optional: Loki + Promtail)
```yaml
# Add to docker-compose.yml
services:
promtail:
image: grafana/promtail:2.9
volumes:
- /var/log:/var/log
- /opt/go-first/deploy/promtail.yml:/etc/promtail/config.yml
depends_on:
- loki
loki:
image: grafana/loki:2.9
volumes:
- loki_data:/loki
```
### Health Checks
```bash
# Application health
curl https://www.urpi.be/api/v1/health
# Database
docker compose exec postgres pg_isready -U postgres
# Redis
docker compose exec redis redis-cli ping
# NGINX
docker compose exec nginx nginx -t
```
### Metrics (Optional: Prometheus + Grafana)
Add to `docker-compose.yml`:
```yaml
prometheus:
image: prom/prometheus:v2.47
volumes:
- ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana:10.2
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
```
---
## 🔄 Updates & Rollbacks
### Update Application
```bash
cd /opt/go-first
# Pull latest code
git pull origin main
# Rebuild and deploy
docker compose --env-file .env.production up -d --build --remove-orphans
# Verify
curl -f https://www.urpi.be/api/v1/health
```
### Rollback
```bash
# List images
docker images ghcr.io/dnoulet/go-first
# Rollback to previous tag
docker tag ghcr.io/dnoulet/go-first:previous-tag ghcr.io/dnoulet/go-first:latest
docker compose --env-file .env.production up -d --no-deps app
```
### Zero-Downtime Deploy (with multiple replicas)
```yaml
# In docker-compose.yml, add:
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
order: start-first
restart_policy:
condition: on-failure
```
---
## 🛡️ Security Checklist
- [ ] Strong DATABASE_PASSWORD (32+ chars)
- [ ] Strong JWT_SECRET (32+ chars, random)
- [ ] SMTP credentials secured
- [ ] Firewall: only 22, 80, 443 open
- [ ] SSH key-only authentication
- [ ] Fail2ban configured for SSH
- [ ] Regular security updates (`apt update && apt upgrade`)
- [ ] Docker images scanned for vulnerabilities (`docker scout cves`)
- [ ] NGINX security headers enabled
- [ ] Rate limiting configured
- [ ] CSP headers configured
- [ ] HSTS enabled
- [ ] Database not exposed publicly
- [ ] Redis not exposed publicly
- [ ] Backup encryption at rest
- [ ] Audit logging enabled
---
## 🚨 Troubleshooting
### Application Won't Start
```bash
# Check logs
docker compose --env-file .env.production logs app
# Common issues:
# - Port 8080 already in use
# - Database connection failed
# - Config file missing
# - Permission denied on volumes
```
### 502 Bad Gateway
```bash
# Check if app is healthy
docker compose --env-file .env.production ps app
# Check NGINX config
docker compose --env-file .env.production exec nginx nginx -t
# Check upstream
docker compose --env-file .env.production exec nginx curl -f http://app:8080/api/v1/health
```
### SSL Certificate Issues
```bash
# Check certificate validity
openssl x509 -in /etc/letsencrypt/live/www.urpi.be/fullchain.pem -text -noout
# Check NGINX SSL config
docker compose --env-file .env.production exec nginx nginx -T | grep ssl
```
### Database Connection Issues
```bash
# Test connection
docker compose --env-file .env.production exec postgres psql -U postgres -d go_first -c "SELECT 1;"
# Check credentials in .env.production match docker-compose.yml
```
---
## 📞 Support
For deployment issues:
- Check logs: `docker compose logs -f`
- Review this guide
- Contact: contact@urpi.be
---
## 📝 Maintenance Windows
Recommended: **Sunday 02:00-04:00 UTC**
- System updates
- Docker image updates
- Database maintenance
- Certificate renewal verification

596
docs/DEVELOPMENT.md Normal file
View file

@ -0,0 +1,596 @@
# Development Guide — go-first / URPI
This guide covers local development setup, workflows, and best practices.
---
## 🛠️ Prerequisites
### Required Tools
| Tool | Version | Install |
|------|---------|---------|
| Go | 1.23+ | `brew install go` / `apt install golang` |
| Node.js | 20+ | `nvm install 20` / `fnm install 20` |
| Docker | 24+ | [docker.com](https://docker.com) |
| Git | 2.40+ | `brew install git` / `apt install git` |
### Recommended Tools
- **IDE**: VS Code with Go + TypeScript extensions
- **API Client**: HTTPie, Postman, or VS Code REST Client
- **Database**: TablePlus, DBeaver, or pgAdmin
- **Terminal**: Warp, Ghostty, or Kitty
---
## 🚀 Getting Started
### 1. Clone & Setup
```bash
git clone https://github.com/dnoulet/go-first.git
cd go-first
# Install all dependencies
make install
# This runs:
# - go mod download
# - cd web && npm ci
```
### 2. Start Development Databases
```bash
# Start PostgreSQL + Redis only
docker compose up -d postgres redis
# Verify
docker compose ps
docker compose exec postgres pg_isready -U postgres
docker compose exec redis redis-cli ping
```
### 3. Configure Local Environment
```bash
cp .env.local.example .env.local
# Edit if needed (defaults work for Docker databases)
vim .env.local
```
### 4. Run Development Servers
```bash
# Option A: Both together (recommended)
make dev
# Option B: Separate terminals
# Terminal 1 - Backend
make dev-backend
# Terminal 2 - Frontend
make dev-frontend
```
### 5. Verify
| Service | URL |
|---------|-----|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:8080/api/v1 |
| Health Check | http://localhost:8080/api/v1/health |
| PostgreSQL | localhost:5432 |
| Redis | localhost:6379 |
---
## 🔄 Development Workflow
### Backend Development
```bash
# Hot reload with air (already configured in .air.toml)
make dev-backend
# Or manually:
go run ./cmd/server
```
#### Adding a New Endpoint
1. **Model** — Add types in `internal/model/models.go`
2. **Service** — Create service in `internal/service/`
3. **Handler** — Add handler in `internal/handler/handler.go`
4. **Routes** — Register in `handler.RegisterRoutes()`
5. **Tests** — Add tests in `*_test.go` files
### Frontend Development
```bash
# Hot module replacement (Vite)
make dev-frontend
# Or manually:
cd web && npm run dev
```
#### Adding a New Page/Section
1. **Types** — Add to `web/src/types/index.ts`
2. **API** — Add endpoint to `web/src/services/api.ts`
3. **Hook** — Create custom hook in `web/src/hooks/`
4. **Component** — Create in `web/src/components/sections/`
5. **Register** — Import and add to `web/src/App.tsx`
### Code Generation
```bash
# Generate mocks for testing
make generate
# This runs: go generate ./...
# Uses: github.com/golang/mock/mockgen
```
---
## 🧪 Testing
### Backend Tests
```bash
# Run all tests with coverage
make test-backend
# Run specific package
go test -v ./internal/service/...
# Run with race detector
go test -race ./...
# Generate coverage report
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
```
### Frontend Tests
```bash
# Run tests
make test-frontend
# Or manually:
cd web && npm test
# With UI
cd web && npm test -- --ui
# Coverage
cd web && npm test -- --coverage
```
### E2E Tests (Optional)
```bash
# Install Playwright
cd web && npx playwright install
# Run E2E tests
cd web && npm run test:e2e
```
---
## 🔍 Code Quality
### Linting
```bash
# All linters
make lint
# Backend only
make lint-backend
# Runs: golangci-lint run ./...
# Frontend only
make lint-frontend
# Runs: eslint + prettier check
```
### Formatting
```bash
# Format all code
make fmt
# Backend
go fmt ./...
goimports -w .
# Frontend
cd web && npm run format
```
### Pre-commit Hooks
```bash
# Install pre-commit
pip install pre-commit
pre-commit install
# Run manually
pre-commit run --all-files
```
#### `.pre-commit-config.yaml`
```yaml
repos:
- repo: https://github.com/golangci/golangci-lint
rev: v1.56.0
hooks:
- id: golangci-lint
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
types: [typescript, json, yaml, markdown]
- repo: local
hooks:
- id: go-fmt
name: go fmt
entry: go fmt ./...
language: system
types: [go]
```
---
## 🗃️ Database Migrations
### Creating Migrations
```bash
# Create new migration
make db-migrate-create name=add_users_table
# This creates:
# migrations/000001_add_users_table.up.sql
# migrations/000001_add_users_table.down.sql
```
### Running Migrations
```bash
# Apply all pending
make db-migrate-up
# Rollback last
make db-migrate-down
# Check status
migrate -path ./migrations -database "$DSN" version
```
### Migration Best Practices
- **Up migrations**: Add columns, tables, indexes
- **Down migrations**: Reverse exactly (drop what you added)
- **Never modify** applied migrations
- **Test** both up and down locally
- **Name clearly**: `add_user_email_index`, not `fix_db`
---
## 🐳 Docker Development
### Build Locally
```bash
# Build production image
make docker-build
# Run production image locally
make docker-run
# Or with docker-compose (includes DB)
docker compose up -d --build
```
### Debugging in Container
```bash
# Run with shell
docker run -it --rm -p 8080:8080 ghcr.io/dnoulet/go-first:latest sh
# Or attach to running
docker exec -it go-first-app sh
```
### Development with Docker Compose Override
Create `docker-compose.override.yml`:
```yaml
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
- /app/web/node_modules
command: air -c .air.toml
environment:
- SERVER_MODE=debug
```
---
## 🎨 Frontend Architecture
### Component Structure
```
web/src/components/
├── ui/ # Reusable primitives (Button, Card, Input, Badge)
├── layout/ # Page structure (Header, Footer)
├── sections/ # Page sections (Hero, Services, Projects, etc.)
└── forms/ # Form-specific components
```
### State Management
- **Server state**: `useApi` hook (React Query pattern)
- **Local state**: `useState`, `useReducer`
- **Forms**: Controlled components with validation
- **No global store** (Redux/Zustand) — keep it simple
### Styling
- **Tailwind CSS** utility-first
- **Custom design system** in `tailwind.config.js`
- **CSS variables** for theming (dark mode ready)
- **Component variants** via `clsx` + `tailwind-merge`
### Adding Icons
```bash
# Use lucide-react (already installed)
import { IconName } from 'lucide-react'
# Or add to public/ and use <img>
```
---
## 🔧 Backend Architecture
### Clean Architecture Layers
```
cmd/server/ # Entry point, wiring
internal/
├── config/ # Configuration (YAML + env)
├── handler/ # HTTP layer (Gin)
├── middleware/ # Cross-cutting concerns
├── model/ # Domain models, DTOs
└── service/ # Business logic
```
### Dependency Injection
Services are instantiated in `main.go` and passed to handlers:
```go
skillService := service.NewSkillService()
h := handler.NewHandler(skillService, ...)
```
### Adding Database Support
1. Add DB config to `config.yaml`
2. Create `internal/repository/` package
3. Implement interfaces in `service/`
4. Use `database/sql` or `sqlx` or `gorm`
5. Add migrations
### Middleware Chain
```go
router.Use(
middleware.Recovery(), // Panic recovery
middleware.Logger(), // Request logging
middleware.SecurityHeaders(), // Security headers
middleware.CORS(...), // CORS
)
```
---
## 📝 Git Workflow
### Branch Naming
| Type | Pattern | Example |
|------|---------|---------|
| Feature | `feat/short-description` | `feat/contact-form-validation` |
| Fix | `fix/short-description` | `fix/cors-header-missing` |
| Docs | `docs/short-description` | `docs/update-api-docs` |
| Refactor | `refactor/short-description` | `refactor/service-layer` |
| Chore | `chore/short-description` | `chore/update-dependencies` |
### Commit Messages
Follow [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: add contact form validation
fix: resolve CORS issue on production
docs: update API documentation
refactor: extract service interfaces
chore: update Go dependencies
```
### Pull Requests
1. Create feature branch from `main`
2. Make changes with tests
3. Run `make lint && make test`
4. Push and open PR
5. Require: CI passing, 1 approval
6. Squash merge to `main`
---
## 🐛 Debugging
### Backend Debugging
```bash
# Delve debugger
dlv debug ./cmd/server
# VS Code launch.json
{
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/cmd/server",
"env": {
"SERVER_MODE": "debug"
}
}
```
### Frontend Debugging
```bash
# React DevTools (browser extension)
# Redux DevTools (if using)
# VS Code debug config:
{
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173",
"webRoot": "${workspaceFolder}/web/src"
}
```
### Database Debugging
```bash
# Connect to PostgreSQL
docker compose exec postgres psql -U postgres -d go_first
# Common queries
\dt # List tables
\d users # Describe table
SELECT * FROM skills; # Query data
```
---
## 📦 Dependency Management
### Go Dependencies
```bash
# Add dependency
go get github.com/new/package
# Update all
go get -u ./...
go mod tidy
# Check outdated
make check-deps
```
### Frontend Dependencies
```bash
# Add dependency
cd web && npm install package-name
# Add dev dependency
cd web && npm install -D package-name
# Update
cd web && npm update
# Audit
cd web && npm audit
```
---
## 🚀 Performance Tips
### Backend
- Use connection pooling (configured in config)
- Add Redis caching for expensive queries
- Enable gzip compression (NGINX)
- Use `pprof` for profiling: `import _ "net/http/pprof"`
### Frontend
- Code splitting (Vite does this automatically)
- Lazy load sections: `const Section = lazy(() => import('./Section'))`
- Optimize images (WebP, proper sizing)
- Use `React.memo` for expensive components
---
## 🔐 Environment Variables
### Required for Development
| Variable | Description | Default |
|----------|-------------|---------|
| `SERVER_MODE` | `debug` or `release` | `debug` |
| `DATABASE_HOST` | PostgreSQL host | `localhost` |
| `DATABASE_PASSWORD` | PostgreSQL password | `postgres` |
| `JWT_SECRET` | JWT signing key | `dev-secret` |
| `CORS_ALLOWED_ORIGINS` | Comma-separated origins | `http://localhost:3000,http://localhost:5173` |
### Optional
| Variable | Description |
|----------|-------------|
| `EMAIL_SMTP_*` | SMTP config for contact form emails |
| `LOGGING_LEVEL` | `debug`, `info`, `warn`, `error` |
| `REDIS_*` | Redis connection (for caching/sessions) |
---
## 📚 Resources
- [Go Project Layout](https://github.com/golang-standards/project-layout)
- [Effective Go](https://go.dev/doc/effective_go)
- [React Beta Docs](https://react.dev)
- [Tailwind CSS Docs](https://tailwindcss.com/docs)
- [Gin Framework](https://gin-gonic.com/docs/)
- [Zerolog](https://github.com/rs/zerolog)
- [Vite Guide](https://vitejs.dev/guide/)
---
## ❓ FAQ
**Q: How do I add a new API field?**
A: Update model → service → handler → frontend types → component
**Q: Why is my frontend not updating?**
A: Check Vite HMR, clear browser cache, check console for errors
**Q: How do I test email sending locally?**
A: Use mock email service (default) or configure Mailhog/SMTP
**Q: Can I use a different database?**
A: Yes, implement repository interface in `internal/repository/`
**Q: How do I deploy a preview environment?**
A: Use `make deploy-staging` or create PR preview with GitHub Actions