- Atualiza hash hardcoded em 010_seed_super_admin.sql para hash válido gerado com pepper=gohorse-pepper (o antigo hash estava inválido e causava AUTH_INVALID_CREDENTIALS em qualquer reset do banco) - Corrige valor de PASSWORD_PEPPER e CORS_ORIGINS no DEVOPS.md para refletir os valores reais do Coolify DEV - Adiciona seção de troubleshooting no DEVOPS.md com diagnóstico e fix passo-a-passo para mismatch de pepper - Adiciona seção "Known Gotchas" no AGENTS.md documentando: * Regra do PASSWORD_PEPPER (deve ser gohorse-pepper em todos ambientes) * Campo de login é email no DTO, não identifier * Hashes bcrypt em SQL devem usar arquivo -f, nunca -c ($ é expandido) * Credenciais de teste do ambiente DEV Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 KiB
AGENTS.md - GoHorse Jobs
Last Updated: 2026-02-18 Purpose: Context for AI coding assistants (Claude, Cursor, etc.)
Project Overview
GoHorse Jobs is a B2B SaaS recruitment platform connecting companies with candidates. It is structured as a monorepo with multiple services sharing a single PostgreSQL database.
Business model: Freemium (Free / Pro R$199/month / Enterprise custom pricing).
Repository Structure
gohorsejobs/
├── backend/ # Go 1.24 REST API (Clean Architecture + DDD)
├── frontend/ # Next.js 15 web application (App Router)
├── backoffice/ # NestJS 11 admin/worker API (Fastify adapter)
├── seeder-api/ # Node.js Express database seeder
├── job-scraper-multisite/# Job scraping service
├── k8s/ # Kubernetes manifests (dev, hml, prd)
├── docs/ # Central documentation
├── .forgejo/ # Forgejo CI/CD workflows
├── .drone.yml # Drone CI/CD pipelines
└── start.sh # Interactive dev startup script
Tech Stack
| Service | Language | Framework | Key Libraries |
|---|---|---|---|
| Backend | Go 1.24 | stdlib net/http | JWT v5, lib/pq, GORM, Stripe, AWS SDK v2, RabbitMQ, Firebase Admin, Swaggo |
| Frontend | TypeScript 5 | Next.js 15 (App Router) | React 19, Tailwind CSS 4, shadcn/ui, Zustand, React Hook Form + Zod, Framer Motion |
| Backoffice | TypeScript 5 | NestJS 11 (Fastify) | TypeORM, Passport + JWT, Stripe, AMQP, Nodemailer, Pino, Firebase Admin |
| Seeder | JavaScript (ESM) | Express 5 | pg, bcrypt |
Database: PostgreSQL 16+ with UUID v7 primary keys. 43+ SQL migration files in backend/migrations/.
Quick Start
./start.sh # Interactive menu
| Option | Action |
|---|---|
| 1 | Start Frontend + Backend |
| 2 | Reset DB + Seed + Start |
| 3 | Start all services (Frontend + Backend + Backoffice) |
| 4 | Run migrations only |
| 5 | Seed database (append) |
| 6 | Full DB reset + migrate + seed |
| 7 | Run backend E2E tests |
| 8 | Seed reset LITE (skip 153k cities) |
| 9 | Run all tests (Backend + Frontend) |
Service Ports
| Service | Port |
|---|---|
| Backend | 8521 |
| Frontend | 8963 |
| Backoffice | 3001 |
| Swagger (Backend) | 8521/swagger/index.html |
| Swagger (Backoffice) | 3001/api/docs |
Build & Test Commands
Backend (Go)
cd backend && go run cmd/api/main.go # Run
go test -v ./... -count=1 # All unit tests
go test -tags=e2e -v ./tests/e2e/... # E2E tests
swag init -g cmd/api/main.go --parseDependency --parseInternal # Swagger
go mod tidy # Dependencies
Frontend (Next.js)
cd frontend
npm install # Install deps
npm run dev # Dev server (use -p 8963 for project convention)
npm run build # Production build
npm run lint # ESLint
npm run test # Jest unit tests
Backoffice (NestJS)
cd backoffice
pnpm install # Uses pnpm
npm run start:dev # Dev server with watch
npm run build # Production build
npm run lint # ESLint with auto-fix
npm run test # Jest unit tests
npm run test:e2e # E2E tests
Seeder
cd seeder-api
npm install
npm run migrate # Run migrations
npm run seed # Seed data
npm run seed:reset # Drop all tables
npm run seed:lite # Seed without 153k cities
Architecture & Conventions
Backend (Go) - Clean Architecture + DDD
backend/internal/
├── api/ # (legacy) HTTP handlers
├── handlers/ # HTTP request handlers (current)
├── middleware/ # Auth, CORS, rate limiting, security headers
├── core/
│ ├── domain/entity/ # Business entities (User, Company, Job, etc.)
│ ├── ports/ # Repository interfaces
│ └── usecases/ # Business logic (LoginUseCase, RegisterUseCase, etc.)
├── infrastructure/
│ ├── auth/ # JWT service
│ ├── persistence/ # PostgreSQL repository implementations
│ └── storage/ # S3/R2 storage adapter
├── services/ # Business services (Email, FCM, Storage, Admin)
├── dto/ # Data Transfer Objects
├── router/ # Route definitions
├── models/ # GORM models (legacy, being migrated)
├── database/ # DB connection & migration runner
└── utils/ # Utilities (JWT, sanitizer)
Patterns:
- Constructor injection:
func NewService(db *sql.DB) *Service - All DB operations accept
ctx context.Context - Error handling:
(T, error)return tuples - Repository interfaces in
core/ports/, implementations ininfrastructure/persistence/ - Test files:
*_test.go
Frontend (Next.js) - App Router
frontend/src/
├── app/ # File-based routing (20+ routes)
│ ├── dashboard/ # Protected routes (12+ sub-pages)
│ ├── jobs/ # Job listing & details
│ └── auth/ # Login, register flows
├── components/ # Reusable components (44+)
│ └── ui/ # shadcn/ui primitives (24+)
├── hooks/ # Custom React hooks
├── contexts/ # React contexts (auth, theme)
├── lib/ # Utilities (API calls, validation)
└── i18n/ # Internationalization (PT, EN, ES, JA)
Patterns:
- Server Components by default; use
'use client'for interactivity - Tailwind CSS utility classes
- Path alias:
@/*maps to./src/* - Form validation: React Hook Form + Zod schemas
- Global state: Zustand stores
- Test files:
*.test.tsx
Backoffice (NestJS) - Modular
backoffice/src/
├── admin/ # Dashboard & statistics
├── auth/ # JWT authentication (Passport)
├── email/ # Email worker (AMQP/LavinMQ consumer)
├── external-services/ # Credential management
├── fcm-tokens/ # Firebase push tokens
├── plans/ # Subscription plans
├── stripe/ # Stripe payment integration
└── tickets/ # Support tickets
Patterns:
- NestJS module pattern:
*.module.ts,*.controller.ts,*.service.ts - Guards:
@UseGuards(JwtAuthGuard) - DTOs:
create-*.dto.ts,update-*.dto.tswith class-validator - Prettier: single quotes, trailing commas
Authentication & Authorization
- JWT: HS256 with HttpOnly cookies (web) + Bearer tokens (API/mobile)
- 4 roles:
superadmin>admin>recruiter>candidate - Middleware stack: Auth (JWT+RBAC) -> CORS -> Rate Limiting (100 req/min) -> Security Headers -> XSS Sanitizer
- JWT secret must match between Backend and Backoffice
- Test credentials: See TEST_USERS.md for all test accounts
Database
- PostgreSQL 16+ with UUID v7 for primary keys (SERIAL for reference tables)
- Migrations:
backend/migrations/(43+ SQL files, numbered000_through999_) - Core tables:
users,companies,user_companies,jobs,applications,favorite_jobs,notifications,tickets,activity_logs,job_payments
API Routes
All backend routes under /api/v1:
- Auth:
/auth/login,/auth/register/candidate,/auth/register/company,/auth/forgot-password,/auth/reset-password - Jobs: CRUD at
/jobs, moderation at/jobs/moderation - Companies: CRUD at
/companies, status management - Users:
/users/me(profile), admin CRUD at/users - Applications:
/applicationswith status updates - Storage:
/storage/upload-url(presigned S3/R2 URLs) - Admin:
/admin/companies,/admin/email-templates,/admin/email-settings - Notifications:
/notifications,/tokens(FCM) - Chat:
/conversations,/conversations/{id}/messages
External Services
| Service | Purpose |
|---|---|
| Stripe | Payment processing & subscriptions |
| Firebase (FCM) | Push notifications |
| Appwrite | Real-time chat/messaging |
| LavinMQ (AMQP) | Message queue for background jobs |
| Cloudflare R2 / S3 | File/image storage |
| Resend | Transactional email |
Deployment
- Environments:
dev(branch: dev),hml(branch: hml),prd(branch: main) - CI/CD: Forgejo workflows (
.forgejo/workflows/) + Drone (.drone.yml) - Container runtime: Podman (Apolo), Coolify/Docker (Redbull), Kubernetes (production)
- Registry: Forgejo (
forgejo-gru.rede5.com.br/rede5/) - Coolify UI: https://redbull.rede5.com.br (Redbull VPS DEV environment)
DEV Environments
| Server | Type | URLs |
|---|---|---|
| Redbull (Coolify) | Auto-deploy via Git | local.gohorsejobs.com, api-local.gohorsejobs.com |
| Apolo (Podman/Quadlet) | Manual deploy | dev.gohorsejobs.com, api-tmp.gohorsejobs.com |
Git Workflow
Remotes
| Remote | URL | Function |
|---|---|---|
| origin | git@github.com:rede5/gohorsejobs.git | GitHub - main development |
| pipe | https://pipe.gohorsejobs.com/bohessefm/gohorsejobs.git | Forgejo - mirror/CI |
Branches
| Branch | Environment | Description |
|---|---|---|
| dev | Development | Main working branch |
| hml | Staging | Pre-production testing |
| main | Production | Stable release |
Sync Flow
# 1. Pull from GitHub
git pull origin dev
# 2. Push to Forgejo (pipe)
git push pipe dev
Key Things to Know
- Backend uses Clean Architecture + DDD; respect layer boundaries (handlers -> usecases -> ports/repositories)
- Frontend uses Next.js App Router; pages in
src/app/, components insrc/components/ - Backoffice shares the same PostgreSQL database as the backend
- Migrations are plain SQL files, not managed by an ORM
- Project supports 4 languages (PT, EN, ES, JA) via i18n
- Environment variables in
.envfiles (gitignored) - Use
start.shfor local development
⚠️ Known Gotchas
1. PASSWORD_PEPPER must be gohorse-pepper everywhere
All migration seeds (010_seed_super_admin.sql) and the seeder-api use gohorse-pepper as the bcrypt pepper.
The Coolify/production env var PASSWORD_PEPPER must match or every login returns AUTH_INVALID_CREDENTIALS.
| Location | Value |
|---|---|
backend/.env (local) |
gohorse-pepper |
seeder-api/.env |
gohorse-pepper |
Coolify DEV (iw4sow8s0kkg4cccsk08gsoo) |
gohorse-pepper |
Migration 010_seed_super_admin.sql hash |
computed with gohorse-pepper |
If you suspect a mismatch, see the full fix procedure in DEVOPS.md.
2. Login API field: email, not identifier
The frontend login form sends { "email": "<username_or_email>", "password": "..." }.
The backend core DTO (internal/core/dto/user_auth.go) has field Email, not Identifier.
The repository resolves it via WHERE email = $1 OR identifier = $1, so both username and email work.
Do not send { "identifier": "..." } directly to /api/v1/auth/login — the field will be ignored and login will fail silently.
3. Bcrypt hashes in SQL — always use a file, never -c
Shell interprets $ in bcrypt hashes as variable expansions. Example of broken approach:
# ❌ WRONG — $2b gets expanded by shell, hash gets corrupted
docker exec postgres psql -U user -d db -c "UPDATE users SET password_hash='$2b$10$abc...'"
Correct approach — write to file first, then use -f:
cat > /tmp/fix.sql <<'EOF'
UPDATE users SET password_hash = '$2b$10$4759wJhnXnBpcwSnVZm9Eu.wTqGYVCHkxAU5a2NxhsFHU42nV3tzW' WHERE identifier = 'lol';
EOF
docker cp /tmp/fix.sql <postgres_container>:/tmp/fix.sql
docker exec <postgres_container> psql -U gohorsejobs -d gohorsejobs -f /tmp/fix.sql
4. Test credentials (DEV/local.gohorsejobs.com)
| Role | Identifier | Password | |
|---|---|---|---|
| superadmin | lol |
Admin@2025! |
lol@gohorsejobs.com |
| superadmin | superadmin |
Admin@2025! |
admin@gohorsejobs.com |
Documentation Index
| Document | Location | Description |
|---|---|---|
| API Reference | API.md | Endpoints, contracts, examples |
| API Security | API_SECURITY.md | Auth, RBAC, permissions |
| Database Schema | DATABASE.md | Tables, ERD, migrations |
| DevOps | DEVOPS.md | Infrastructure, deployment, diagrams |
| Test Users | TEST_USERS.md | Credenciais de teste por role |
| Roadmap | ROADMAP.md | Product direction |
| Tasks | TASKS.md | Task tracking |
| Workflows | WORKFLOWS.md | Deployment workflows |
| Backend | ../backend/BACKEND.md | Go API details |
| Frontend | ../frontend/FRONTEND.md | Next.js details |
| Backoffice | ../backoffice/BACKOFFICE.md | NestJS details |
| Seeder | ../seeder-api/SEEDER-API.md | Database seeding & test data |