From 028d26d2e69d92294655e6d7109d2cfa657bbc58 Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Mon, 23 Feb 2026 21:16:04 -0600 Subject: [PATCH] docs: overhaul global documentation with diagrams, test scenarios, and agent directives --- docs/AGENTS.md | 409 ++++------------------- docs/API.md | 613 ++++------------------------------ docs/DATABASE.md | 798 +++++---------------------------------------- docs/DEVOPS.md | 706 +++++---------------------------------- docs/README.md | 51 ++- docs/TEST_USERS.md | 59 +++- 6 files changed, 404 insertions(+), 2232 deletions(-) diff --git a/docs/AGENTS.md b/docs/AGENTS.md index c8827fb..e8a99bd 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -1,382 +1,103 @@ # AGENTS.md - GoHorse Jobs -> **Last Updated:** 2026-02-18 -> **Purpose:** Context for AI coding assistants (Claude, Cursor, etc.) +> **Purpose:** Context and Directives for AI coding assistants (Claude, Cursor, Aider, etc.) +> **Branch Context:** `dev` + +--- + +## 🚨 SUPERIOR DIRECTIVES 🚨 + +1. **DO NOT TOUCH KUBERNETES/SECRETS**: You are strictly forbidden from modifying any files in `k8s/` or altering raw RSA/base64 authentication keys. +2. **ENV VARIABLES**: Never hardcode secrets in code. If you define a new environment variable, it must be documented. Next.js variables exposed to the client must use the `NEXT_PUBLIC_` prefix. NEVER expose secrets with `NEXT_PUBLIC_`. +3. **READ FIRST**: Before implementing new features, always read the relevant documentation in this `docs/` folder to understand existing patterns (e.g., Go Clean Architecture, React Server Components). --- ## 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. +GoHorse Jobs is a modern B2B SaaS recruitment platform connecting companies with candidates. It operates across multiple services sharing a single PostgreSQL database. **Business model**: Freemium (Free / Pro R$199/month / Enterprise custom pricing). ---- +### Repository Structure -## Repository Structure - -``` +```text 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) +β”œβ”€β”€ backend/ # Go REST API (Clean Architecture + DDD) +β”œβ”€β”€ frontend/ # Next.js web application (App Router) +β”œβ”€β”€ backoffice/ # NestJS admin and worker API β”œβ”€β”€ seeder-api/ # Node.js Express database seeder β”œβ”€β”€ job-scraper-multisite/# Job scraping service -β”œβ”€β”€ k8s/ # Kubernetes manifests (dev, hml, prd) +β”œβ”€β”€ k8s/ # Kubernetes manifests (FORBIDDEN DOMAIN) β”œβ”€β”€ docs/ # Central documentation -β”œβ”€β”€ .forgejo/ # Forgejo CI/CD workflows -β”œβ”€β”€ .drone.yml # Drone CI/CD pipelines +β”œβ”€β”€ .forgejo/ # Forgejo actions workflows └── 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 - -```bash -./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) - -```bash -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) - -```bash -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) - -```bash -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 - -```bash -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 (Go 1.24) +* **Architecture**: Clean Architecture + Domain Driven Design (DDD) +* **Flow**: Controller (`internal/handlers`) -> UseCase (`internal/core/usecases`) -> Interface Port (`internal/core/ports`) -> Repository (`internal/infrastructure/persistence`). +* **Key Libs**: `net/http` (stdlib routing), `lib/pq` + `gorm` (legacy/transitioning), `swaggo` (Docs). +* **Running**: `cd backend && go run cmd/api/main.go` (Port 8521) -``` -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) -``` +### Frontend (Next.js 15) +* **Architecture**: App Router (`src/app`), heavily favoring React Server Components (RSC). Use `'use client'` strictly at the leaf nodes of the component tree for interactivity. +* **Styling**: Tailwind CSS, generic UI components in `src/components/ui` (shadcn). +* **State & Validation**: Zustand (global state), React Hook Form + Zod (forms). +* **i18n**: Multi-language support (PT, EN, ES, JA). Ensure all text is extracted to translation files. +* **Running**: `cd frontend && npm run dev -p 8963` (Port 8963) -**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 in `infrastructure/persistence/` -- Test files: `*_test.go` +### Backoffice (NestJS 11) +* **Architecture**: Modular NestJS over Fastify. +* **Modules**: Admin, Auth, Email (Worker), Stripe, Tickets. +* **Running**: `cd backoffice && npm run start:dev` (Port 3001) -### 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.ts` with class-validator -- Prettier: single quotes, trailing commas +### Seeder API (Express 5) +* **Purpose**: Manages database migrations and test data populations. +* **Running**: `cd seeder-api && npm run seed` --- -## Authentication & Authorization +## ⚠️ Known Gotchas & Historical Errors -- **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](TEST_USERS.md) for all test accounts +### 1. The `PASSWORD_PEPPER` Trap +**Symptom**: "Invalid credentials" upon login right after seeding the database. +**Cause**: The initial SQL migration creates the superadmin, but lacks the bcrypt pepper hashing. The Node.js `seeder-api` calculates the real hash. +**Fix**: You must run `npm run migrate` **AND THEN** `npm run seed` in the `seeder-api` directory. The seeder will read `PASSWORD_PEPPER` (e.g., `gohorse-pepper`) from your `.env` and fix the hashes. + +### 2. Login Payload +The frontend login form sends `{ "email": "admin", "password": "..." }`. The backend resolves this against the `email` or `identifier` columns. **Do not** change the payload payload to `{ "identifier": "admin" }` because the backend DTO explicitly expects the JSON key `"email"`. + +### 3. Bcrypt in bash strings +**Never** run a raw SQL command via bash `docker exec` containing a bcrypt hash like `$2b$10$...`. The bash shell expands `$2b` as a variable, corrupting the hash. Always write the SQL to a file and execute the file. + +### 4. Contact/Tickets +The contact form uses Next.js internationalization and routes directly to the backoffice `tickets` API. Emails go to `wtf@gohorsejobs.com`. --- -## Database +## Dev Environments & Test Users -- PostgreSQL 16+ with UUID v7 for primary keys (SERIAL for reference tables) -- Migrations: `backend/migrations/` (43+ SQL files, numbered `000_` through `999_`) -- Core tables: `users`, `companies`, `user_companies`, `jobs`, `applications`, `favorite_jobs`, `notifications`, `tickets`, `activity_logs`, `job_payments` +**Environments**: +* **dev**: Current working branch. Deployed to `dev.gohorsejobs.com` or `local.gohorsejobs.com` +* **main**: Production branch. ---- +**Test Users** (Local/Dev): +* Superadmin: `lol` / `Admin@2025!` +* Superadmin: `superadmin` / `Admin@2025!` -## 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**: `/applications` with 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 - -```bash -# 1. Pull from GitHub -git pull origin dev - -# 2. Push to Forgejo (pipe) -git push pipe dev -``` - ---- - -## Key Things to Know - -1. Backend uses Clean Architecture + DDD; respect layer boundaries (handlers -> usecases -> ports/repositories) -2. Frontend uses Next.js App Router; pages in `src/app/`, components in `src/components/` -3. Backoffice shares the same PostgreSQL database as the backend -4. Migrations are plain SQL files, not managed by an ORM -5. Project supports 4 languages (PT, EN, ES, JA) via i18n -6. Environment variables in `.env` files (gitignored) -7. Use `start.sh` for local development - ---- - -## ⚠️ Known Gotchas - -### 1. PASSWORD_PEPPER e o hash do superadmin sΓ£o gerados em runtime - -A migration `010_seed_super_admin.sql` **nΓ£o hardcoda hash**. Ela cria o superadmin com um -placeholder invΓ‘lido e `force_change_password`. O **seeder-api** Γ© quem gera o hash correto -em runtime lendo `process.env.PASSWORD_PEPPER`. - -Fluxo correto apΓ³s reset do banco: -``` -npm run migrate β†’ superadmin criado, hash = placeholder (nΓ£o faz login) -npm run seed β†’ hash recalculado com pepper do ambiente, status = active -``` - -O `start.sh` opΓ§Γ΅es 2, 6 e 8 jΓ‘ fazem isso automaticamente (migrate β†’ seed). - -| Location | `PASSWORD_PEPPER` | -|----------|-------| -| `backend/.env` (local) | `gohorse-pepper` | -| `seeder-api/.env` | `gohorse-pepper` | -| Coolify DEV (`iw4sow8s0kkg4cccsk08gsoo`) | `gohorse-pepper` | - -Se `PASSWORD_PEPPER` mudar, basta rodar `npm run seed` novamente β€” o hash Γ© recalculado -automaticamente. Nenhuma migration precisa ser alterada. - -If you suspect a mismatch, see the full fix procedure in [DEVOPS.md](DEVOPS.md#troubleshooting-login-retorna-invalid-credentials). - -### 2. Login API field: `email`, not `identifier` - -The frontend login form sends `{ "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: -```bash -# ❌ 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`: -```bash -cat > /tmp/fix.sql <<'EOF' -UPDATE users SET password_hash = '$2b$10$4759wJhnXnBpcwSnVZm9Eu.wTqGYVCHkxAU5a2NxhsFHU42nV3tzW' WHERE identifier = 'lol'; -EOF -docker cp /tmp/fix.sql :/tmp/fix.sql -docker exec psql -U gohorsejobs -d gohorsejobs -f /tmp/fix.sql -``` - -### 4. Test credentials (DEV/local.gohorsejobs.com) - -| Role | Identifier | Password | Email | -|------|-----------|----------|-------| -| superadmin | `lol` | `Admin@2025!` | lol@gohorsejobs.com | -| superadmin | `superadmin` | `Admin@2025!` | admin@gohorsejobs.com | - ---- +See [docs/TEST_USERS.md](TEST_USERS.md) for Candidate and Company personas. ## Documentation Index -| Document | Location | Description | -|----------|----------|-------------| -| API Reference | [API.md](API.md) | Endpoints, contracts, examples | -| API Security | [API_SECURITY.md](API_SECURITY.md) | Auth, RBAC, permissions | -| Database Schema | [DATABASE.md](DATABASE.md) | Tables, ERD, migrations | -| DevOps | [DEVOPS.md](DEVOPS.md) | Infrastructure, deployment, diagrams | -| Test Users | [TEST_USERS.md](TEST_USERS.md) | Credenciais de teste por role | -| Roadmap | [ROADMAP.md](ROADMAP.md) | Product direction | -| Tasks | [TASKS.md](TASKS.md) | Task tracking | -| Workflows | [WORKFLOWS.md](WORKFLOWS.md) | Deployment workflows | -| Backend | [../backend/BACKEND.md](../backend/BACKEND.md) | Go API details | -| Frontend | [../frontend/FRONTEND.md](../frontend/FRONTEND.md) | Next.js details | -| Backoffice | [../backoffice/BACKOFFICE.md](../backoffice/BACKOFFICE.md) | NestJS details | -| Seeder | [../seeder-api/SEEDER-API.md](../seeder-api/SEEDER-API.md) | Database seeding & test data | +When asked to learn or modify parts of the system, consult these files first: +* [API.md](API.md) - Endpoint contracts +* [API_SECURITY.md](API_SECURITY.md) - Auth, RBAC +* [APPSEC_STRATEGY.md](APPSEC_STRATEGY.md) - Application Security and Testing +* [DATABASE.md](DATABASE.md) - Schema and ERD +* [DEVOPS.md](DEVOPS.md) - Cloudflare, Traefik, Containers +* [TASKS.md](TASKS.md) / [TEST_USERS.md](TEST_USERS.md) diff --git a/docs/API.md b/docs/API.md index 5fd5bc1..60cf761 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,574 +1,87 @@ -# πŸ“‘ GoHorse Jobs - API Documentation +# πŸ”Œ API Documentation & Routing - GoHorseJobs -Complete API reference with routes, permissions, and modules. - -> **Last Updated:** 2026-02-16 -> **Base URL:** `https://api.gohorsejobs.com/api/v1` -> **Auth:** JWT Bearer Token or HttpOnly Cookie +This document outlines the API landscape across the GoHorseJobs monorepo, detailing the main services and how they communicate. --- -## πŸ” Authentication +## πŸ—οΈ API Architecture & Communication -### Methods -1. **Authorization Header:** `Authorization: Bearer ` -2. **Cookie:** `jwt=` (HttpOnly, Secure) +* **Backend API (Go):** The central source of truth for the platform. Handles all core business logic (Users, Companies, Jobs, Applications). +* **Backoffice API (NestJS):** Dedicated to administrative tasks, workers (email queues), and specialized integrations (Stripe, Firebase Cloud Messaging). Shares the **same PostgreSQL database** as the Backend API. +* **Seeder API (Node.js):** Runs strictly on demand to populate the database with dummy data or run migrations. -### Roles -| Role | Code | Level | Description | -|------|------|-------|-------------| -| **SuperAdmin** | `superadmin` | 0 | Platform administrator | -| **Admin** | `admin` | 1 | Company administrator | -| **Recruiter** | `recruiter` | 2 | Job poster | -| **Candidate** | `candidate` | 3 | Candidate | -| **Guest** | - | - | No authentication | +### Communication Flow (Frontend) +The Next.js Frontend primarily communicates with the **Backend API**. +* **Paradigm:** The frontend uses React Server Components (RSC) to fetch data natively from the Go API. +* **Client Actions:** For forms and interactivity (e.g., login, apply), client components dispatch requests using the wrapped fetch utility located at `src/lib/api.ts`. +* **Auth State:** JWT tokens are stored locally (Cookies/Storage) and appended to the `Authorization: Bearer ` header manually via interceptors or utility functions. --- -## πŸ“‹ Module: Authentication +## πŸ“‘ Backend API Reference (`/api/v1`) +Hosted at `api-local.gohorsejobs.com` (Dev) or `api.gohorsejobs.com` (Prd). +View detailed interactive Swagger Docs by visiting `/swagger/index.html`. -### Login -```http -POST /api/v1/auth/login -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Authenticate user | +### Authentication +* `POST /auth/login`: Accepts `{ "email": "...", "password": "..."}`. Returns JWT token and User DTO. +* `POST /auth/register/candidate`: Creates a standard user. +* `POST /auth/register/company`: Creates a user and a linked `Company` profile awaiting approval. -**Request:** -```json -{ - "identifier": "lol", - "password": "Admin@2025!" -} -``` +### Users & Profiles +* `GET /users/me`: Gets the full profile of the currently authenticated user (derived from JWT context). +* `PUT /users/me`: Updates profile details. +* `GET /admin/users`: (Admin only) Lists all users. -**Response (200):** -```json -{ - "token": "eyJhbGciOiJI...", - "user": { "id": 1, "role": "superadmin", "name": "Dr. Horse Expert" } -} -``` +### Companies +* `GET /companies`: Lists active companies with pagination and filters. +* `GET /companies/{id}`: Detailed company view. +* `PUT /companies/{id}`: Edit company profile (Requires owner/admin permissions via `user_companies` relation). +* `POST /admin/companies/{id}/status`: (Admin only) Approve/Reject pending companies. + +### Jobs & Applications +* `GET /jobs`: Lists published jobs. (Supports full-text search and filters). +* `POST /jobs`: Creates a draft job (Requires Recruiter). +* `PUT /jobs/{id}`: Updates job details. +* `POST /jobs/{id}/apply`: Submits an application for the job. +* `GET /applications`: Lists received applications (for recruiters) or submitted applications (for candidates). +* `POST /applications/{id}/status`: Updates application status (`reviewing`, `accepted`, `rejected`). + +### Miscellaneous +* `GET /health`: Basic ping endpoint. +* `POST /storage/upload-url`: Requests a presigned URL (S3/Cloudflare R2) to upload logos or resumes directly from the browser. --- -### Register Candidate -```http -POST /api/v1/auth/register -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Register new job seeker | +## πŸ› οΈ Backoffice API Reference (`/api`) +Hosted at `b-local.gohorsejobs.com` (Dev) or `b.gohorsejobs.com` (Prd). +View detailed interactive Swagger Docs by visiting `/api/docs`. -**Request:** -```json -{ - "name": "John Doe", - "email": "john@example.com", - "password": "SecurePass123!" -} -``` +### Tickets & Support +* `POST /tickets`: Public endpoint (no auth required) to submit a contact/support ticket. Used directly by the frontend `/contact` page. +* `GET /tickets`: (Admin only) Lists all open tickets. +* `POST /tickets/{id}/reply`: (Admin only) Adds a message to a ticket and triggers an email notification to the user. + +### Emails & Workers +The Backoffice listens to a LavinMQ (AMQP) message queue to dispatch transactional emails (welcome, password reset, application updates) securely without blocking the main Go runtime. + +### Stripe Billing +* `POST /stripe/webhook`: Consumes Stripe events to upgrade company plans from Free to Pro/Enterprise. --- -## 🏒 Module: Companies +## πŸ“š Seeder API Commands -### Create Company -```http -POST /api/v1/companies -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Register new company | +If you need to instantly provision the database for these routes to work: -### List Companies -```http -GET /api/v1/companies -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Smart | βšͺ Optional | Guest/Admin | Public list or admin full list | +```bash +cd seeder-api +npm install -### Update Company Status -```http -PATCH /api/v1/companies/{id}/status -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Activate/deactivate company | +# Drops all tables, runs migrations, inserts core test users (100 jobs, 50 companies) +npm run seed:lite ---- +# Runs migrations ONLY +npm run migrate +``` -## πŸ‘₯ Module: Users - -### Get Current User -```http -GET /api/v1/users/me -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Get current user profile | - -### List Users -```http -GET /api/v1/users -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List all users | - -### Create User -```http -POST /api/v1/users -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Create new user | - -### Update User -```http -PATCH /api/v1/users/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Update user profile | - -### Delete User -```http -DELETE /api/v1/users/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin | Delete user | - -### List User Roles -```http -GET /api/v1/users/roles -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List available roles | - ---- - -## πŸ’Ό Module: Jobs - -### List Jobs (Public) -```http -GET /api/v1/jobs -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Search and filter jobs | - -**Query Parameters:** -| Param | Type | Description | -|-------|------|-------------| -| `q` | string | Search query (title, description) | -| `location` | string | Filter by location | -| `type` | string | Employment type filter | -| `workMode` | string | `onsite`, `hybrid`, `remote` | -| `page` | int | Page number (default: 1) | -| `limit` | int | Items per page (default: 20) | - -### Get Job by ID -```http -GET /api/v1/jobs/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Get job details | - -### Create Job -```http -POST /api/v1/jobs -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public* | ❌ | admin, recruiter | Create new job posting | - -### Update Job -```http -PUT /api/v1/jobs/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public* | ❌ | admin, recruiter | Update job posting | - -### Delete Job -```http -DELETE /api/v1/jobs/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public* | ❌ | admin, recruiter | Delete job posting | - -### List Jobs for Moderation -```http -GET /api/v1/jobs/moderation -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List all jobs for moderation | - -### Update Job Status -```http -PATCH /api/v1/jobs/{id}/status -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Approve/reject/close job | - -### Duplicate Job -```http -POST /api/v1/jobs/{id}/duplicate -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Duplicate job posting | - ---- - -## πŸ“ Module: Applications - -### Create Application -```http -POST /api/v1/applications -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest/JobSeeker | Apply to a job | - -**Request:** -```json -{ - "job_id": 123, - "name": "John Doe", - "email": "john@example.com", - "phone": "+55 11 99999-9999", - "message": "I am interested in this position...", - "resume_url": "https://..." -} -``` - -### List Applications -```http -GET /api/v1/applications -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | admin, recruiter | List applications | - -### Get Application by ID -```http -GET /api/v1/applications/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | admin, recruiter | Get application details | - -### Update Application Status -```http -PUT /api/v1/applications/{id}/status -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | admin, recruiter | Update application status | - -**Request:** -```json -{ - "status": "hired", - "notes": "Approved after final interview" -} -``` - -**Status Values:** -- `pending` - Awaiting review -- `reviewed` - Seen by recruiter -- `shortlisted` - Selected for interview -- `rejected` - Not selected -- `hired` - Offer accepted - ---- - -## πŸ”” Module: Notifications - -### List Notifications -```http -GET /api/v1/notifications -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Get user notifications | - ---- - -## 🏷️ Module: Tags - -### List Tags -```http -GET /api/v1/tags -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List all tags | - -### Create Tag -```http -POST /api/v1/tags -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Create new tag | - -### Update Tag -```http -PATCH /api/v1/tags/{id} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Update tag | - ---- - -## πŸ‘€ Module: Candidates - -### List Candidates -```http -GET /api/v1/candidates -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List all candidates | - ---- - -## πŸ“Š Module: Audit - -### List Login Audits -```http -GET /api/v1/audit/logins -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Login audit trail | - ---- - -## πŸ“¦ Module: Storage - -### Generate Upload URL -```http -POST /api/v1/storage/upload-url -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Get S3 presigned upload URL | - -### Generate Download URL -```http -POST /api/v1/storage/download-url -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Get S3 presigned download URL | - -### Delete File -```http -DELETE /api/v1/storage/files -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | Any authenticated | Delete file from S3 | - ---- - -## πŸ“š Module: Documentation - -### Swagger UI -```http -GET /docs/ -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Public | ❌ | Guest | Interactive API documentation | - ---- - -## πŸ”‘ Module: System Credentials - -Manage encrypted credentials for external services (Stripe, SMTP, Storage, etc.). - -### List Configured Services -```http -GET /api/v1/system/credentials -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | List all configured services (metadata only, no secrets) | - -**Response (200):** -```json -{ - "services": [ - { - "service_name": "stripe", - "updated_at": "2026-02-23T10:30:00Z", - "updated_by": "admin-uuid", - "is_configured": true - }, - { - "service_name": "smtp", - "updated_at": "2026-02-22T14:00:00Z", - "updated_by": "superadmin-uuid", - "is_configured": true - } - ] -} -``` - -### Save Credentials -```http -POST /api/v1/system/credentials -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Create or update service credentials | - -**Request:** -```json -{ - "serviceName": "stripe", - "payload": { - "secretKey": "sk_live_xxx", - "webhookSecret": "whsec_xxx", - "publishableKey": "pk_live_xxx" - } -} -``` - -**Response (200):** -```json -{ - "message": "Credentials saved successfully" -} -``` - -**Supported Services:** -| Service Name | Fields | -|--------------|--------| -| `stripe` | `secretKey`, `webhookSecret`, `publishableKey` | -| `smtp` | `host`, `port`, `username`, `password`, `from_email`, `from_name`, `secure` | -| `storage` | `endpoint`, `region`, `bucket`, `accessKey`, `secretKey` | -| `cloudflare_config` | `apiToken`, `zoneId` | -| `firebase` | `serviceAccountJson` (JSON string) | -| `appwrite` | `endpoint`, `projectId`, `apiKey` | -| `lavinmq` | `amqpUrl` | -| `cpanel` | `host`, `username`, `apiToken` | - -### Delete Credentials -```http -DELETE /api/v1/system/credentials/{serviceName} -``` -| Field | Auth | Roles | Description | -|-------|------|-------|-------------| -| Protected | βœ… | superadmin, admin | Delete service credentials | - -**Response (200):** -```json -{ - "success": true -} -``` - -**Security Notes:** -- Credentials are encrypted with RSA-OAEP-SHA256 before storage -- Only metadata (service name, timestamps) is returned on list operations -- Actual secret values cannot be retrieved after saving -- Updating credentials overwrites the existing configuration - ---- - -## πŸ”‘ Permission Matrix - -| Route | Guest | JobSeeker | Recruiter | CompanyAdmin | Admin | SuperAdmin | -|-------|-------|-----------|-----------|--------------|-------|------------| -| `POST /auth/login` | βœ… | βœ… | βœ… | βœ… | βœ… | βœ… | -| `POST /auth/register` | βœ… | ❌ | ❌ | ❌ | ❌ | ❌ | -| `GET /jobs` | βœ… | βœ… | βœ… | βœ… | βœ… | βœ… | -| `GET /jobs/{id}` | βœ… | βœ… | βœ… | βœ… | βœ… | βœ… | -| `POST /jobs` | ❌ | ❌ | βœ… | βœ… | βœ… | βœ… | -| `POST /applications` | βœ… | βœ… | ❌ | ❌ | ❌ | ❌ | -| `GET /users/me` | ❌ | βœ… | βœ… | βœ… | βœ… | βœ… | -| `GET /users` | ❌ | ❌ | ❌ | ❌ | βœ… | βœ… | -| `DELETE /users/{id}` | ❌ | ❌ | ❌ | ❌ | ❌ | βœ… | -| `GET /notifications` | ❌ | βœ… | βœ… | βœ… | βœ… | βœ… | -| `GET /audit/logins` | ❌ | ❌ | ❌ | ❌ | βœ… | βœ… | -| `GET /jobs/moderation` | ❌ | ❌ | ❌ | ❌ | βœ… | βœ… | - ---- - -## πŸ†” ID Formats - -| Entity | ID Type | Example | -|--------|---------|---------| -| Users | INT (SERIAL) | `1`, `42`, `1337` | -| Companies | INT (SERIAL) | `1`, `15`, `100` | -| Jobs | INT (SERIAL) | `1`, `500`, `2500` | -| Notifications | UUID v7 | `019438a1-2b3c-7abc-8123-4567890abcdef` | -| Tickets | UUID v7 | `019438a2-3c4d-7xyz-9abc-def0123456789` | -| Payments | UUID v7 | `019438a3-4e5f-7mno-pqrs-tuvwxyz012345` | - ---- - -## πŸ“ Error Responses - -### 400 Bad Request -```json -{ - "error": "Invalid request body", - "details": "Field 'email' is required" -} -``` - -### 401 Unauthorized -```json -{ - "error": "Unauthorized", - "message": "Invalid or expired token" -} -``` - -### 403 Forbidden -```json -{ - "error": "Forbidden", - "message": "Insufficient permissions" -} -``` - -### 404 Not Found -```json -{ - "error": "Not Found", - "message": "Resource not found" -} -``` - -### 500 Internal Server Error -```json -{ - "error": "Internal Server Error", - "message": "An unexpected error occurred" -} -``` - ---- - -## πŸ“š Related Documentation - -- [Database Schema](DATABASE.md) -- [Roadmap](ROADMAP.md) -- [Tasks](TASKS.md) +The seeder automatically inserts the `Admin@2025!` superadmin hash required for local testing. diff --git a/docs/DATABASE.md b/docs/DATABASE.md index 5c9dd4a..fa52709 100644 --- a/docs/DATABASE.md +++ b/docs/DATABASE.md @@ -1,751 +1,131 @@ -# πŸ—„οΈ Database Schema Documentation +# πŸ—„οΈ Database Architecture - GoHorseJobs -Complete database documentation for the GoHorseJobs platform. - -> **Last Updated:** 2026-02-16 -> **Database:** PostgreSQL 16+ (Local `postgres-main` container) -> **Connection:** Internal `gohorsejobs_dev` database via `web_proxy` network -> **ID Strategy:** UUID v7 for core tables, SERIAL for reference tables -> **Migrations:** 30 SQL files in `backend/migrations/` +GoHorseJobs uses a single **PostgreSQL 16+** database shared across all services (Backend API, NestJS Backoffice, Node.js Seeder). --- -## �️ Development Environment Structure +## πŸ—οΈ Core Entity-Relationship Diagram (ERD) -The development environment (`apolo` server) uses a **Local Containerized Strategy** to ensure isolation and speed. - -### πŸ—οΈ Topology - -```mermaid -graph TD - subgraph VPS ["Apolo Server (VPS)"] - subgraph Net ["Docker Network: web_proxy"] - PG[("postgres-main")] - BE["Backend API"] - BO["Backoffice"] - SE["Seeder API"] - end - Traefik["Traefik Proxy"] - end - - Traefik --> BE - Traefik --> BO - Traefik --> SE - - BE -- "internal:5432" --> PG - BO -- "internal:5432" --> PG - SE -- "internal:5432" --> PG - - style PG fill:#336791,stroke:#fff,stroke-width:2px,color:#fff -``` - -### πŸ”Œ Connection Details - -All services connect to the database via the internal Docker network. - -| Parameter | Value | Notes | -|-----------|-------|-------| -| **Host** | `postgres-main` | Internal Container Hostname | -| **Port** | `5432` | Internal Port | -| **Database** | `gohorsejobs_dev` | Dedicated Dev DB (Isolated from `main_db`) | -| **User** | `yuki` | Owner of public schema | -| **Network** | `web_proxy` | Shared Bridge Network | -| **SSL Mode** | `disable` | Internal traffic is unencrypted | - -### πŸš€ Access & Management - -Since the database runs inside a container and is not exposed to the public internet, use the following methods for access: - -**1. CLI Access (via SSH)** -```bash -# Connect to PostgreSQL shell -ssh root@apolo 'podman exec -it postgres-main psql -U yuki -d gohorsejobs_dev' -``` - -**2. Run Migrations** -Migrations are applied using the Backend service or manually piped: -```bash -# Manual Pipe (from local machine) -cat backend/migrations/*.sql | ssh root@apolo 'podman exec -i postgres-main psql -U yuki -d gohorsejobs_dev' -``` - -**3. Seeding Data** -Trigger the Seeder API (running locally) to populate data: -```bash -curl -X POST https://seeder.gohorsejobs.com/seed -``` - - -## οΏ½πŸ“Š Entity Relationship Diagram +The core data model centers around `users` acting in different capacities (`candidate`, `recruiter`, `admin`) interacting with `companies` and `jobs`. ```mermaid erDiagram - %% Core Entities - users ||--o{ user_companies : "belongs to" - users ||--o{ user_roles : "has roles" - users ||--o{ applications : "submits" - users ||--o{ favorite_jobs : "saves" - users ||--o{ notifications : "receives" - users ||--o{ tickets : "opens" - users ||--o{ ticket_messages : "sends" - users ||--o{ login_audits : "generates" - users ||--o{ activity_logs : "generates" + USERS ||--o{ USER_COMPANIES : "belongs to" + COMPANIES ||--o{ USER_COMPANIES : "contains" - companies ||--o{ user_companies : "has members" - companies ||--o{ jobs : "posts" + COMPANIES ||--o{ JOBS : "posts" + USERS ||--o{ JOBS : "creates (author)" - jobs ||--o{ applications : "receives" - jobs ||--o{ favorite_jobs : "saved by" - jobs ||--o{ job_payments : "has payments" + USERS ||--o{ APPLICATIONS : "submits" + JOBS ||--o{ APPLICATIONS : "receives" - regions ||--o{ cities : "contains" - regions ||--o{ companies : "located in" - regions ||--o{ jobs : "located in" + USERS ||--o{ FAVORITE_JOBS : "saves" + JOBS ||--o{ FAVORITE_JOBS : "favorited" - tickets ||--o{ ticket_messages : "contains" + USERS ||--o{ TICKETS : "opens" + USERS ||--o{ TICKET_MESSAGES : "sends" + TICKETS ||--o{ TICKET_MESSAGES : "has" - %% Entities - users { - int id PK "SERIAL" - varchar identifier UK "login" - varchar password_hash - varchar role "enum" - varchar full_name - varchar email - varchar name - int tenant_id FK "nullable" - varchar status + USERS { + uuid id PK + string role "superadmin, admin, recruiter, candidate" + string identifier "username or email" + string email UK + string password_hash + boolean is_active + timestamp created_at } - user_roles { - int user_id PK,FK - varchar role PK "composite key" + COMPANIES { + uuid id PK + string name + string document "CNPJ" + string domain UK + string location + string industry + string logo_url + string status "pending, active, rejected" } - companies { - int id PK "SERIAL" - varchar name - varchar slug UK - varchar type - varchar document - text address - int region_id FK - varchar email - varchar website - boolean verified - boolean active + USER_COMPANIES { + uuid id PK + uuid user_id FK + uuid company_id FK + string role "owner, admin, member" } - - jobs { - int id PK "SERIAL" - int company_id FK - int created_by FK - varchar title + + JOBS { + uuid id PK + uuid company_id FK + uuid author_id FK + string title text description - decimal salary_min - decimal salary_max - varchar salary_type - varchar currency - varchar employment_type - varchar work_mode - varchar status - boolean is_featured + string type "full-time, remote, etc" + decimal max_salary + decimal min_salary + string status "draft, published, closed" } - - applications { - int id PK "SERIAL" - int job_id FK - int user_id FK "nullable" - varchar status - text message - varchar resume_url - } - - regions { - int id PK "SERIAL" - varchar name - varchar country_code - varchar code - } - - cities { - int id PK "SERIAL" - int region_id FK - varchar name - } - - notifications { + + APPLICATIONS { uuid id PK - int user_id FK - varchar type - varchar title - text message - boolean read + uuid job_id FK + uuid candidate_id FK + string status "pending, reviewing, interviewed, rejected, accepted" + text cover_letter } - job_payments { + FAVORITE_JOBS { uuid id PK - int job_id FK - int user_id FK - decimal amount - varchar status - varchar stripe_session_id + uuid user_id FK + uuid job_id FK + timestamp created_at } - tickets { + TICKETS { uuid id PK - int user_id FK - varchar subject - varchar status - varchar priority - } - - ticket_messages { - uuid id PK - uuid ticket_id FK - int user_id FK - text message - boolean is_staff - } - - job_posting_prices { - int id PK - varchar name - decimal price - int duration_days - } - - login_audits { - int id PK - int user_id FK - varchar identifier - boolean success - varchar ip_address - } - - activity_logs { - int id PK - int user_id FK - varchar entity_type - varchar action - jsonb details + uuid user_id FK "nullable for guests" + string subject + string category "Support, Bug, Billing..." + string status "open, in_progress, resolved, closed" + string email "fallback for guests" + string name "fallback for guests" } ``` --- -## πŸ—οΈ Table Overview +## πŸ”‘ Key Strategies -| Table | ID Type | Description | Migration | -|-------|---------|-------------|-----------| -| `users` | UUID v7 | System users (candidates, recruiters, admins) | 001/021 | -| `user_roles` | Composite | Additional roles per user | 020 | -| `companies` | UUID v7 | Employer organizations | 002/021 | -| `user_companies` | UUID v7 | User ↔ Company mapping | 003/021 | -| `regions` | SERIAL | States/Provinces/Prefectures | 004 | -| `cities` | SERIAL | Cities within regions | 004 | -| `jobs` | UUID v7 | Job postings | 005/021 | -| `applications` | UUID v7 | Job applications | 006/021 | -| `favorite_jobs` | SERIAL | Saved jobs by users | 007 | -| `password_resets` | SERIAL | Password reset tokens | 008 | -| `notifications` | UUID v7 | User notifications | 016 | -| `tickets` | UUID v7 | Support tickets | 017 | -| `ticket_messages` | UUID v7 | Ticket messages | 017 | -| `job_payments` | UUID v7 | Payment records | 019 | -| `job_posting_prices` | SERIAL | Pricing configuration | 019 | -| `login_audits` | SERIAL | Login audit trail | 013 | -| `activity_logs` | SERIAL | Activity logging | 013 | +### 1. Primary Keys (UUID v7) +We aggressively use **UUID v7** for all major business entities (`users`, `companies`, `jobs`). +* **Why?** UUID v7 contains a timestamp component, making it naturally sortable by insertion time (like SERIAL/auto-increment) while avoiding the predictability and distributed generation bottlenecks of standard integers. ---- +### 2. Multi-Role Profiles +The system handles permissions through the `role` enum in the `users` table: +* `superadmin`: Global control over GoHorseJobs. +* `admin`: Can moderate companies/jobs inside the Backoffice. +* `recruiter`: A user attached to a `company_id` via `user_companies` who can post `jobs`. +* `candidate`: A standard user seeking jobs and making `applications`. -## πŸ“‹ Core Tables +A single email can only correspond to one role globally. If a user needs another role, they must change it or use a different credential. -### `users` +### 3. Migrations +Migrations are written in pure SQL and stored in `backend/migrations/`. +They follow a strict sequential numbering index (e.g., `000_schema.sql`, `001_roles.sql`, etc). The backend runtime executes these on startup if the database is out of sync. -System users including Candidates, Recruiters, and Admins. - -```sql -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - identifier VARCHAR(100) UNIQUE NOT NULL, -- Login (username/email) - password_hash VARCHAR(255) NOT NULL, - role VARCHAR(20) NOT NULL, -- 'superadmin'|'admin'|'recruiter'|'candidate' - - -- Profile - full_name VARCHAR(255), - email VARCHAR(255), - name VARCHAR(255), - phone VARCHAR(30), - - -- Candidate Profile Fields (015) - city VARCHAR(100), - state VARCHAR(50), - title VARCHAR(100), - experience VARCHAR(100), - skills TEXT[], - bio TEXT, - objective TEXT, - - -- Multi-tenancy (020) - tenant_id INT REFERENCES companies(id), - status VARCHAR(20) DEFAULT 'active', - - -- Settings - language VARCHAR(5) DEFAULT 'en', - active BOOLEAN DEFAULT true, - - -- Timestamps - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - last_login_at TIMESTAMP -); -``` - -**Roles:** -- `superadmin` - Platform administrator -- `admin` - Company administrator -- `recruiter` - Job poster/recruiter -- `candidate` - Candidate/job seeker - ---- - -### `user_roles` - -Additional roles per user (for multi-role support). - -```sql -CREATE TABLE user_roles ( - user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - role VARCHAR(50) NOT NULL, - PRIMARY KEY (user_id, role) -); +### 4. Background Seeding +Seeding (populating test data) is explicitly decoupled from migrations. It lives isolated in the Node.js `seeder-api`. +To reset your local database to a clean, populated state: +```bash +# Deletes everything, remigrates, and inserts base data (except 153k cities) +cd seeder-api && npm run seed:lite ``` --- -### `companies` +## ⚠️ Security Notes -Employer organizations that post jobs. - -```sql -CREATE TABLE companies ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - slug VARCHAR(255) UNIQUE NOT NULL, -- URL-friendly name - type VARCHAR(50) DEFAULT 'company', -- 'company'|'agency'|'system' - document VARCHAR(100), -- CNPJ/Tax ID - - -- Location - address TEXT, - region_id INT REFERENCES regions(id), - city_id INT REFERENCES cities(id), - - -- Contact - phone VARCHAR(30), - email VARCHAR(255), - website VARCHAR(255), - - -- Branding - logo_url TEXT, - description TEXT, -- JSON or plain text - - -- Status - active BOOLEAN DEFAULT true, - verified BOOLEAN DEFAULT false, - - -- Timestamps - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -### `jobs` - -Job postings created by companies. - -```sql -CREATE TABLE jobs ( - id SERIAL PRIMARY KEY, - company_id INT NOT NULL REFERENCES companies(id) ON DELETE CASCADE, - created_by INT NOT NULL REFERENCES users(id), - - -- Job Details - title VARCHAR(255) NOT NULL, - description TEXT NOT NULL, - - -- Salary - salary_min DECIMAL(12,2), - salary_max DECIMAL(12,2), - salary_type VARCHAR(20), -- 'hourly'|'daily'|'weekly'|'monthly'|'yearly' - currency VARCHAR(3), -- 'BRL'|'USD'|'EUR'|'GBP'|'JPY' - - -- Employment - employment_type VARCHAR(30), -- 'full-time'|'part-time'|'contract'|'dispatch'|... - work_mode VARCHAR(10), -- 'onsite'|'hybrid'|'remote' - working_hours VARCHAR(100), - - -- Location - location VARCHAR(255), - region_id INT REFERENCES regions(id), - city_id INT REFERENCES cities(id), - - -- Requirements & Benefits - requirements JSONB, -- Array of skills/requirements - benefits JSONB, -- Array of benefits - - -- Visa & Language - visa_support BOOLEAN DEFAULT false, - language_level VARCHAR(20), -- 'N5'|'N4'|'N3'|'N2'|'N1'|'beginner'|'none' - - -- Status - status VARCHAR(20) DEFAULT 'open', -- 'draft'|'open'|'closed'|'published'|... - is_featured BOOLEAN DEFAULT false, - - -- Timestamps - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - -**Status Values:** -- `draft` - Not published yet -- `open` - Accepting applications -- `published` - Live and visible -- `paused` - Temporarily hidden -- `closed` - No longer accepting -- `expired` - Past expiration date -- `archived` - Archived by employer -- `reported` - Flagged for review -- `review` - Under admin review - ---- - -### `applications` - -Job applications from candidates. - -```sql -CREATE TABLE applications ( - id SERIAL PRIMARY KEY, - job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, - user_id INT REFERENCES users(id), -- Nullable for guest applications - - -- Applicant Info - name VARCHAR(255) NOT NULL, - email VARCHAR(255) NOT NULL, - phone VARCHAR(30), - line_id VARCHAR(100), - whatsapp VARCHAR(30), - - -- Application Content - message TEXT, - resume_url VARCHAR(500), - documents JSONB, - - -- Status - status VARCHAR(20) DEFAULT 'pending', -- 'pending'|'reviewed'|'shortlisted'|'rejected'|'hired' - notes TEXT, -- Recruiter notes - - -- Timestamps - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -## 🌍 Geographic Tables - -### `regions` - -States, provinces, or prefectures. - -```sql -CREATE TABLE regions ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) NOT NULL, - country_code VARCHAR(2) NOT NULL, -- 'BR'|'US'|'JP' - code VARCHAR(10) NOT NULL -- 'SP'|'CA'|'13' -); -``` - -**Seeded Regions:** -- πŸ‡§πŸ‡· Brazil: SΓ£o Paulo (SP), Rio de Janeiro (RJ), Minas Gerais (MG) -- πŸ‡ΊπŸ‡Έ USA: California (CA), New York (NY), Texas (TX) -- πŸ‡―πŸ‡΅ Japan: Tokyo (13), Osaka (27) - ---- - -### `cities` - -Cities within regions. - -```sql -CREATE TABLE cities ( - id SERIAL PRIMARY KEY, - region_id INT NOT NULL REFERENCES regions(id), - name VARCHAR(100) NOT NULL -); -``` - ---- - -## πŸ’° Payment Tables - -### `job_payments` (UUID) - -Payment records for job postings. - -```sql -CREATE TABLE job_payments ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, - user_id INT REFERENCES users(id) ON DELETE SET NULL, - - -- Stripe - stripe_session_id VARCHAR(255), - stripe_payment_intent VARCHAR(255), - stripe_customer_id VARCHAR(255), - - -- Amount - amount DECIMAL(12,2) NOT NULL, - currency VARCHAR(3) DEFAULT 'USD', - - -- Status - status VARCHAR(20) DEFAULT 'pending', -- 'pending'|'completed'|'failed'|'refunded'|'expired' - - -- Billing - billing_type VARCHAR(20), -- 'company'|'individual' - billing_name VARCHAR(255), - billing_email VARCHAR(255), - - -- Job Details - duration_days INT DEFAULT 30, - is_featured BOOLEAN DEFAULT false, - - -- Timestamps - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - paid_at TIMESTAMP, - expires_at TIMESTAMP -); -``` - ---- - -### `job_posting_prices` - -Pricing configuration for job postings. - -```sql -CREATE TABLE job_posting_prices ( - id SERIAL PRIMARY KEY, - name VARCHAR(100) NOT NULL, - description TEXT, - price DECIMAL(12,2) NOT NULL, - currency VARCHAR(3) DEFAULT 'USD', - duration_days INT DEFAULT 30, - is_featured BOOLEAN DEFAULT false, - stripe_price_id VARCHAR(255), - active BOOLEAN DEFAULT true, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -## πŸ”” Notification Tables - -### `notifications` (UUID) - -User notifications. - -```sql -CREATE TABLE notifications ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - type VARCHAR(50), - title VARCHAR(255), - message TEXT, - read BOOLEAN DEFAULT false, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -## 🎫 Support Tables - -### `tickets` (UUID) - -Support tickets. - -```sql -CREATE TABLE tickets ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - user_id INT NOT NULL, - subject VARCHAR(255) NOT NULL, - status VARCHAR(20) DEFAULT 'open', -- 'open'|'in_progress'|'resolved'|'closed' - priority VARCHAR(10) DEFAULT 'medium', -- 'low'|'medium'|'high'|'urgent' - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - -### `ticket_messages` (UUID) - -Messages within tickets. - -```sql -CREATE TABLE ticket_messages ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - ticket_id UUID NOT NULL REFERENCES tickets(id) ON DELETE CASCADE, - user_id INT NOT NULL, - message TEXT NOT NULL, - is_staff BOOLEAN DEFAULT false, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -## πŸ“ Audit Tables - -### `login_audits` - -Login attempt tracking. - -```sql -CREATE TABLE login_audits ( - id SERIAL PRIMARY KEY, - user_id INT, - identifier VARCHAR(100), - success BOOLEAN, - ip_address VARCHAR(45), - user_agent TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - -### `activity_logs` - -General activity logging. - -```sql -CREATE TABLE activity_logs ( - id SERIAL PRIMARY KEY, - user_id INT, - entity_type VARCHAR(50), - entity_id INT, - action VARCHAR(50), - details JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - ---- - -## πŸ”— Junction Tables - -### `user_companies` - -Maps users to companies (N:M relationship). - -```sql -CREATE TABLE user_companies ( - id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - company_id INT NOT NULL REFERENCES companies(id) ON DELETE CASCADE, - role VARCHAR(30) DEFAULT 'recruiter', -- 'admin'|'recruiter' - permissions JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, company_id) -); -``` - -### `favorite_jobs` - -Saved jobs by users. - -```sql -CREATE TABLE favorite_jobs ( - id SERIAL PRIMARY KEY, - user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - job_id INT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, job_id) -); -``` - ---- - -## πŸ”„ Migrations History - -| # | File | Description | -|---|------|-------------| -| 001 | `001_create_users_table.sql` | Core users table | -| 002 | `002_create_companies_table.sql` | Companies/employers | -| 003 | `003_create_user_companies_table.sql` | User ↔ Company mapping | -| 004 | `004_create_prefectures_cities_tables.sql` | Geographic data | -| 005 | `005_create_jobs_table.sql` | Job postings | -| 006 | `006_create_applications_table.sql` | Job applications | -| 007 | `007_create_favorite_jobs_table.sql` | Saved jobs | -| 008 | `008_create_password_resets_table.sql` | Password reset tokens | -| 010 | `010_seed_super_admin.sql` | Default superadmin | -| 011 | `011_add_is_featured_to_jobs.sql` | Featured jobs flag | -| 012 | `012_add_work_mode.sql` | Work mode column | -| 013 | `013_create_backoffice_tables.sql` | Audit tables | -| 014 | `014_update_job_status_constraint.sql` | Status enum update | -| 015 | `015_add_candidate_profile_fields.sql` | Candidate profile | -| 016 | `016_create_notifications_table.sql` | Notifications | -| 017 | `017_create_tickets_table.sql` | Support tickets | -| 018 | `018_add_currency_to_jobs.sql` | Currency support | -| 019 | `019_create_job_payments_table.sql` | Payment tracking | -| 020 | `020_unify_schema.sql` | Schema unification | -| 999 | `999_fix_gohorse_schema.sql` | Schema fixes | - ---- - -## ⚠️ ID Strategy Notes - -The database uses a **hybrid ID strategy**: - -| Strategy | Tables | Rationale | -|----------|--------|-----------| -| **UUID v7** | users, companies, jobs, applications, notifications, tickets, job_payments | Time-ordered, distributed-friendly, sortable, scalable | -| **SERIAL (INT)** | regions, cities, job_posting_prices, job_tags | Reference/Static data, low volume | - -### UUID v7 (RFC 9562) - -Starting from migration `021_create_uuid_v7_function.sql`, the database uses **UUID v7** instead of UUID v4: - -```sql --- UUID v7 is generated by uuid_generate_v7() function -SELECT uuid_generate_v7(); --- Returns: 019438a1-2b3c-7abc-8123-4567890abcdef --- ^^^^^^^^ time component (sortable) -``` - -**Benefits of UUID v7:** -- ⏱️ Time-ordered (sortable by creation time) -- 🌐 Distributed-friendly (no coordination needed) -- πŸ“Š Better index performance than UUID v4 -- πŸ”’ Contains embedded timestamp - ---- - -## πŸ”’ Security Notes - -1. **Password Hashing:** BCrypt with optional `PASSWORD_PEPPER` environment variable -2. **Soft Deletes:** Not implemented (uses `CASCADE` for referential integrity) -3. **Row-Level Security:** Not implemented (uses application-level checks) -4. **Audit Trail:** `login_audits` and `activity_logs` for compliance - ---- - -## πŸ“š Related Documentation - -- [Backend README](../backend/README.md) - API documentation -- [Seeder README](../seeder-api/README.md) - Database seeding -- [Backoffice README](../backoffice/README.md) - Admin panel +### The `password_hash` & Pepper +Bcrypt hashes stored in the standard `users` table require a secret pepper (`PASSWORD_PEPPER`). +* The raw hash stored in the DB cannot be cracked immediately due to the pepper. +* The seeder relies on reading the `PASSWORD_PEPPER` from `.env` to forge the initial super-admin and test accounts hashes. **If you change the `.env` pepper, the entire DB of seeded passwords immediately invalidates.** You must re-run `npm run seed`. diff --git a/docs/DEVOPS.md b/docs/DEVOPS.md index 2e33a14..def980b 100644 --- a/docs/DEVOPS.md +++ b/docs/DEVOPS.md @@ -1,651 +1,119 @@ -# DevOps - GoHorseJobs (Development Environment) +# ☁️ DevOps & Infrastructure - GoHorseJobs -Infraestrutura, CI/CD e deploy do projeto GoHorseJobs no servidor `apolo`. - -> **Last Updated:** 2026-02-18 -> **Servers:** Apolo VPS (Podman), Redbull VPS (Coolify) -> **Tech Stack:** Podman, Systemd (Quadlet), Traefik, PostgreSQL, Coolify - ---- - -## ☁️ Cloudflare DNS Zone - -### Zone Info - -| Property | Value | -|----------|-------| -| **Zone ID** | `5e7e9286849525abf7f30b451b7964ac` | -| **Domain** | gohorsejobs.com | -| **Account** | gohorsejobs | -| **Email** | yamamoto@rede5.com.br | -| **Plan** | Free Website | -| **Name Servers** | chuck.ns.cloudflare.com, fatima.ns.cloudflare.com | - -### API Access - -```bash -# Token location: ~/.ssh/cloudflare-token -export CF_AUTH_EMAIL="yamamoto@rede5.com.br" -export CF_AUTH_KEY="5dcfd89a9d4ec330dede0d4074a518f26818e" - -# List zones -curl -s -H "X-Auth-Email: $CF_AUTH_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" \ - "https://api.cloudflare.com/client/v4/zones" - -# List DNS records -curl -s -H "X-Auth-Email: $CF_AUTH_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" \ - "https://api.cloudflare.com/client/v4/zones/5e7e9286849525abf7f30b451b7964ac/dns_records" - -# Purge cache -curl -s -X DELETE -H "X-Auth-Email: $CF_AUTH_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" \ - -H "Content-Type: application/json" \ - "https://api.cloudflare.com/client/v4/zones/5e7e9286849525abf7f30b451b7964ac/purge_cache" \ - -d '{"purge_everything":true}' -``` - -### Active DNS Records (gohorsejobs.com) - -| Subdomain | Type | IP/Target | Proxied | -|-----------|------|------------|---------| -| dev.gohorsejobs.com | A | 38.19.201.52 | No | -| api.gohorsejobs.com | A | 86.48.29.139 | Yes | -| api-dev.gohorsejobs.com | A | 86.48.29.139 | Yes | -| api-local.gohorsejobs.com | A | 38.19.201.52 | No | -| b-local.gohorsejobs.com | A | 38.19.201.52 | No | -| s-local.gohorsejobs.com | A | 38.19.201.52 | No | -| coolify-dev.gohorsejobs.com | A | 185.194.141.70 | No | -| local.gohorsejobs.com | A | 185.194.141.70 | No | -| api-local.gohorsejobs.com | A | 185.194.141.70 | No | -| b-local.gohorsejobs.com | A | 185.194.141.70 | No | -| s-local.gohorsejobs.com | A | 185.194.141.70 | No | -| panel.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| pipe.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| alert.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| task.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| stats.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| storage.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| base.gohorsejobs.com | A | Multiple | No | -| reg.gohorsejobs.com | A | Multiple (Load Balanced) | Yes | -| gohorsejobs.com | CNAME | gohorsejobs.pages.dev | Yes | -| *.gohorsejobs.com | CNAME | 8a3f435b-f374-4268-90f7-5610f577c706.cfargotunnel.com | Yes | -| mail.gohorsejobs.com | CNAME | everest.mxrouting.net | No | - -> Total: 190 DNS records (paginados) - ---- - -## ☁️ Coolify DEV Environment (Redbull) - -Ambiente de desenvolvimento no Coolify para deploy automatizado via Git. - -### Server Info - -| Property | Value | -|----------|-------| -| **Host** | redbull (185.194.141.70) | -| **Coolify URL** | https://redbull.rede5.com.br | -| **API Token** | `~/.ssh/coolify-redbull-token` | -| **SSH Key** | `~/.ssh/civo` | -| **Project UUID** | `gkgksco0ow4kgwo8ow4cgs8c` | -| **Environment** | `dev` | - -### Resources Created - -| Resource | UUID | Port | Domain | Status | -|----------|------|------|--------|--------| -| Backend | `iw4sow8s0kkg4cccsk08gsoo` | 8521 | https://api-local.gohorsejobs.com | running | -| Frontend | `ao8g40scws0w4cgo8coc8o40` | 3000 | https://local.gohorsejobs.com | running | -| Backoffice | `hg48wkw4wggwsswcwc8sooo4` | 3001 | https://b-local.gohorsejobs.com | running | -| Seeder | `q4w48gos8cgssso00o8w8gck` | 8080 | https://s-local.gohorsejobs.com | running:healthy | -| Database (PostgreSQL) | `bgws48os8wgwk08o48wg8k80` | 5432 | Internal only | running:healthy | - -### API Reference - -**Base URL:** `https://redbull.rede5.com.br/api/v1` - -**Server UUID:** `m844o4gkwkwcc0k48swgs8c8` - -```bash -# Listar aplicaΓ§Γ΅es -curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/applications" - -# Atualizar domΓ­nios (requer http:// ou https://) -curl -s -X PATCH -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - -H "Content-Type: application/json" \ - "https://redbull.rede5.com.br/api/v1/applications/" \ - -d '{"domains":"http://local.gohorsejobs.com","instant_deploy":true}' - -# Deploy aplicaΓ§Γ£o -curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/deploy?uuid=" - -# Ver domΓ­nios do servidor -curl -s -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/servers/m844o4gkwkwcc0k48swgs8c8/domains" -``` - -### Architecture - -``` -GitHub (rede5/gohorsejobs.git) ←→ Forgejo (pipe.gohorsejobs.com) - β”‚ β”‚ - β–Ό β–Ό - Coolify (redbull.rede5.com.br) - β”œβ”€β”€ Traefik (reverse proxy + TLS via Let's Encrypt) - β”‚ - β”œβ”€β”€ gohorsejobs-backend-dev β†’ https://api-local.gohorsejobs.com - β”œβ”€β”€ gohorsejobs-frontend-dev β†’ https://local.gohorsejobs.com - β”œβ”€β”€ gohorsejobs-backoffice-dev β†’ https://b-local.gohorsejobs.com - β”œβ”€β”€ gohorsejobs-seeder-dev β†’ https://s-local.gohorsejobs.com - β”‚ - └── PostgreSQL 16 (gohorsejobs-dev) β†’ Internal network only -``` - -### Environment Variables - -Configured via Coolify UI or API: - -```bash -DATABASE_URL=postgres://gohorsejobs:gohorsejobs123@bgws48os8wgwk08o48wg8k80:5432/gohorsejobs?sslmode=disable -BACKEND_PORT=8521 -ENV=development -JWT_SECRET=gohorsejobs-dev-jwt-secret-2024-very-secure-key-32ch -JWT_EXPIRATION=7d -PASSWORD_PEPPER=gohorse-pepper -COOKIE_SECRET=gohorsejobs-cookie-secret-dev -CORS_ORIGINS=https://local.gohorsejobs.com,https://b-local.gohorsejobs.com,http://localhost:3000,http://localhost:8521 -``` - -> ⚠️ **`PASSWORD_PEPPER` Γ© crΓ­tico.** Todas as migration seeds e o seeder-api usam `gohorse-pepper`. -> Se este valor for alterado no Coolify sem regravar os hashes no banco, **todos os logins falharΓ£o** -> com `invalid credentials`. Veja a seΓ§Γ£o de troubleshooting abaixo. - -### ⚠️ Troubleshooting: Login retorna `invalid credentials` - -**Causa:** O `PASSWORD_PEPPER` no Coolify nΓ£o coincide com o pepper usado para gerar os hashes no banco. - -**DiagnΓ³stico via SSH:** -```bash -ssh redbull -# Verificar pepper no container em execuΓ§Γ£o: -docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' | grep PEPPER - -# Testar login direto (sem escaping de shell): -cat > /tmp/login.json <<'EOF' -{"email":"lol","password":"Admin@2025!"} -EOF -docker run --rm --network coolify -v /tmp/login.json:/tmp/login.json \ - curlimages/curl:latest -s -X POST \ - http://:8521/api/v1/auth/login \ - -H 'Content-Type: application/json' -d @/tmp/login.json -``` - -**Fix β€” opΓ§Γ£o 1 (preferΓ­vel): corrigir o pepper no Coolify e re-rodar o seeder:** -```bash -TOKEN=$(cat ~/.ssh/coolify-redbull-token) - -# 1. Atualizar PASSWORD_PEPPER -curl -s -X PATCH \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{"key":"PASSWORD_PEPPER","value":"gohorse-pepper"}' \ - "https://redbull.rede5.com.br/api/v1/applications/iw4sow8s0kkg4cccsk08gsoo/envs" - -# 2. Reiniciar o backend (para ele pegar o novo pepper) -curl -s -H "Authorization: Bearer $TOKEN" \ - "https://redbull.rede5.com.br/api/v1/applications/iw4sow8s0kkg4cccsk08gsoo/restart" - -# 3. Re-rodar o seeder (ele regrava o hash com o pepper correto automaticamente) -curl -s -H "Authorization: Bearer $TOKEN" \ - "https://redbull.rede5.com.br/api/v1/deploy?uuid=q4w48gos8cgssso00o8w8gck" -``` - -> O seeder (`seedUsers()`) sempre faz upsert do superadmin/lol com `bcrypt(senha + PEPPER)`. -> Mudar o pepper e re-rodar o seeder Γ© suficiente β€” nenhuma migration precisa ser tocada. - -**Fix β€” opΓ§Γ£o 2 (emergΓͺncia, sem seeder): regravar hash direto no banco:** -```bash -ssh redbull - -# Gerar novo hash com node (usando arquivo para evitar expansΓ£o de $ pelo shell): -mkdir -p /tmp/hashgen && cat > /tmp/hashgen/gen.js <<'EOF' -const b = require("./node_modules/bcryptjs"); -console.log(b.hashSync("Admin@2025!" + process.env.PEPPER, 10)); -EOF -docker run --rm -v /tmp/hashgen:/app -w /app -e PEPPER=gohorse-pepper \ - node:20-alpine sh -c "npm install bcryptjs -s && node gen.js" - -# Aplicar no banco (SEMPRE usar -f, nunca -c, para preservar os $ do hash): -cat > /tmp/fix_hash.sql <<'EOF' -UPDATE users SET password_hash = '', status = 'active' -WHERE identifier IN ('lol', 'superadmin'); -EOF -docker cp /tmp/fix_hash.sql bgws48os8wgwk08o48wg8k80:/tmp/fix_hash.sql -docker exec bgws48os8wgwk08o48wg8k80 psql -U gohorsejobs -d gohorsejobs -f /tmp/fix_hash.sql -``` - -> ⚠️ **Nunca passe hash bcrypt via `-c '...'`** na linha de comando β€” o shell expande os `$` -> e corrompe o hash silenciosamente. Use sempre um arquivo e `-f`. - -### Deploy via API - -```bash -# Deploy application -curl -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/deploy?uuid=iw4sow8s0kkg4cccsk08gsoo" - -# Check deployment status -curl -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/deployments/" - -# List applications -curl -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/applications" - -# List databases -curl -H "Authorization: Bearer $(cat ~/.ssh/coolify-redbull-token)" \ - "https://redbull.rede5.com.br/api/v1/databases" -``` - -### Coolify Reference - -- **Docs:** https://coolify.io/docs/get-started/introduction -- **API Reference:** https://coolify.io/docs/api-reference/authorization -- **GitHub Integration:** Uses SSH deploy key for private repo access +This document maps out the comprehensive DevOps lifecycle, the server topologies, the container orchestrations, and CI/CD operations powering GoHorseJobs. --- ## πŸ—οΈ Architecture Diagrams -### Full Infrastructure Overview +### 1. Global Infrastructure Overview (DEV / HML Environments) +A look into how our development environment handles requests through Cloudflare down to the Coolify-managed `Redbull` VPS. ```mermaid graph TB - subgraph Clients ["Clients"] - Browser["Browser / Mobile"] + subgraph Clients ["Public Clients"] + Browser["Web Browser / Mobile App"] end - subgraph CF ["Cloudflare (DNS + CDN)"] + subgraph CF ["Cloudflare (DNS + Proxy + CDN)"] DNS["DNS Zone: gohorsejobs.com"] + WAF["Web Application Firewall (WAF)"] end - subgraph Redbull ["Redbull VPS (185.194.141.70) β€” Coolify DEV"] - TraefikR("Traefik + Let's Encrypt") - - subgraph CoolifyApps ["Coolify Applications"] - FE_C["Frontend (:3000)"] - BE_C["Backend API (:8521)"] - BO_C["Backoffice (:3001)"] - SE_C["Seeder API (:8080)"] + subgraph Redbull ["Redbull VPS (185.194.141.70) β€” Ubuntu"] + TraefikR("Traefik (Reverse Proxy + Let's Encrypt)") + + subgraph CoolifyApps ["Coolify Application Containers"] + FE_C["Frontend (Next.js)"] + BE_C["Backend API (Go)"] + BO_C["Backoffice (NestJS)"] + SE_C["Seeder API (Node.js)"] end - PG_C[("PostgreSQL 16\ngohorsejobs-dev")] - end - - subgraph Apolo ["Apolo VPS (38.19.201.52) β€” Podman/Quadlet"] - TraefikA("Traefik") - - subgraph PodmanApps ["Podman Containers (Systemd/Quadlet)"] - FE_A["Frontend (:3000)"] - BE_A["Backend API (:8521)"] - BO_A["Backoffice (:3001)"] - SE_A["Seeder API (:8080)"] + subgraph CoolifyData ["Coolify Data Containers"] + PG_C[("PostgreSQL 16")] + MQ_C["LavinMQ / RabbitMQ"] + Redis_C[("Redis (Caching/Sessions)")] end - - PG_A[("PostgreSQL\npostgres-main")] - Storage["/mnt/data\n(configs + DB data)"] end - subgraph Git ["Git Repositories"] - GH["GitHub\nrede5/gohorsejobs"] - FJ["Forgejo (pipe)\npipe.gohorsejobs.com"] - end + Browser -->|HTTPS| DNS + DNS --> WAF + WAF -->|Proxy/Cache| TraefikR + TraefikR -->|local.gohorsejobs.com| FE_C + TraefikR -->|api-local.gohorsejobs.com| BE_C + TraefikR -->|b-local.gohorsejobs.com| BO_C - subgraph External ["External Services"] - Stripe["Stripe (Payments)"] - Firebase["Firebase (FCM)"] - R2["Cloudflare R2 (Storage)"] - LavinMQ["LavinMQ (AMQP)"] - Resend["Resend (Email)"] - end - - %% Client Flow - Browser --> DNS - DNS -- "local.gohorsejobs.com" --> TraefikR - DNS -- "dev.gohorsejobs.com" --> TraefikA - - %% Redbull Routing - TraefikR -- "local.gohorsejobs.com" --> FE_C - TraefikR -- "api-local.gohorsejobs.com" --> BE_C - TraefikR -- "b-local.gohorsejobs.com" --> BO_C - TraefikR -- "s-local.gohorsejobs.com" --> SE_C - BE_C --> PG_C - BO_C --> PG_C - SE_C --> PG_C - - %% Apolo Routing - TraefikA -- "dev.gohorsejobs.com" --> FE_A - TraefikA -- "api-tmp.gohorsejobs.com" --> BE_A - TraefikA -- "b-tmp.gohorsejobs.com" --> BO_A - BE_A --> PG_A - BO_A --> PG_A - SE_A --> PG_A - PG_A -.-> Storage - - %% Git Flow - GH <--> FJ - - %% External - BE_C -.-> Stripe - BE_C -.-> Firebase - BE_C -.-> R2 - BO_C -.-> LavinMQ - BO_C -.-> Resend - - style PG_C fill:#336791,stroke:#fff,color:#fff - style PG_A fill:#336791,stroke:#fff,color:#fff - style TraefikR fill:#f5a623,stroke:#fff,color:#fff - style TraefikA fill:#f5a623,stroke:#fff,color:#fff - style CF fill:#f48120,stroke:#fff,color:#fff + FE_C -.->|REST /api/v1| BE_C + BO_C -.->|Queries| PG_C + BE_C <-->|Queries| PG_C + BE_C -.->|AMQP Pub| MQ_C + MQ_C -.->|AMQP Sub| BO_C ``` -### Apolo VPS (Podman/Quadlet) Detail +### 2. CI/CD Operations (Forgejo -> VPS) +How code travels from a `git push` on `dev` to the live container. ```mermaid -graph TD - subgraph Host ["Apolo VPS (Host)"] - - subgraph FS ["File System (/mnt/data)"] - EnvBE["/gohorsejobs/backend/.env"] - EnvBO["/gohorsejobs/backoffice/.env"] - EnvSE["/gohorsejobs/seeder-api/.env"] - DBData[("postgres-general")] - end - - subgraph Net ["Network: web_proxy"] - Traefik("Traefik") - - subgraph App ["Application Containers"] - BE["Backend API (:8521)"] - BO["Backoffice (:3001)"] - SE["Seeder API (:8080)"] - FE["Frontend (:3000)"] - end - - PG[("postgres-main (:5432)")] - end - end - - %% Ingress - Internet((Internet)) --> Traefik - - %% Routing - Traefik -- "dev.gohorsejobs.com" --> FE - Traefik -- "api-tmp.gohorsejobs.com" --> BE - Traefik -- "b-tmp.gohorsejobs.com" --> BO - Traefik -- "seeder.gohorsejobs.com" --> SE - - %% Config Mounts - EnvBE -.-> BE - EnvBO -.-> BO - EnvSE -.-> SE - - %% Data Persistence - PG -.-> DBData - - %% Database Connections - BE --> PG - BO --> PG - SE --> PG - - style PG fill:#336791,stroke:#fff,color:#fff - style Traefik fill:#f5a623,stroke:#fff,color:#fff -``` - -### Coolify DEV (Redbull) Detail - -```mermaid -graph TD - subgraph Redbull ["Redbull VPS β€” Coolify (redbull.rede5.com.br)"] - Traefik("Traefik + Let's Encrypt") - - subgraph Apps ["Applications (auto-deploy via Git)"] - BE["Backend Go\n:8521"] - FE["Frontend Next.js\n:3000"] - BO["Backoffice NestJS\n:3001"] - SE["Seeder API\n:8080"] - end - - PG[("PostgreSQL 16\ngohorsejobs\n:5432")] - end - - GH["GitHub (rede5/gohorsejobs)"] --> |"push dev"| Traefik - - Internet((Internet)) --> Traefik - Traefik -- "api-local.gohorsejobs.com" --> BE - Traefik -- "local.gohorsejobs.com" --> FE - Traefik -- "b-local.gohorsejobs.com" --> BO - Traefik -- "s-local.gohorsejobs.com" --> SE - - BE --> PG - BO --> PG - SE --> PG - - style PG fill:#336791,stroke:#fff,color:#fff - style Traefik fill:#f5a623,stroke:#fff,color:#fff -``` - -### CI/CD Flow (Dual Pipeline) - -Existem **2 pipelines independentes** disparados simultaneamente a cada push: - -```mermaid -graph TD - Dev["Developer\ngit push dev"] - - subgraph Pipeline1 ["Pipeline 1: GitHub β†’ Coolify"] - GH["GitHub\n(origin)"] - Webhook["GitHub Webhook\n(push event)"] - Coolify["Coolify\n(redbull.rede5.com.br)"] - Redbull["Redbull VPS\nFrontend + Backend + Backoffice + Seeder"] - end - - subgraph Pipeline2 ["Pipeline 2: Forgejo β†’ K3s Cluster"] - FJ["Forgejo\n(pipe.gohorsejobs.com)"] - Runner["Forgejo Actions Runner\n(self-hosted, K3s)"] - Registry["Container Registry\npipe.gohorsejobs.com"] - K3s["K3s Cluster\nBackend + Backoffice"] - end - - Dev --> GH - Dev --> FJ - - GH --> Webhook --> Coolify --> |"Docker build"| Redbull - - FJ --> |"push triggers"| Runner - Runner --> |"docker build & push"| Registry - Runner --> |"kubectl apply"| K3s -``` - -| Pipeline | Trigger | Servicos | Destino | -|----------|---------|----------|---------| -| **GitHub β†’ Coolify** | Webhook (push) | Frontend, Backend, Backoffice, Seeder | Redbull VPS (Docker) | -| **Forgejo β†’ K3s** | Forgejo Actions (push) | Backend, Backoffice | K3s Cluster (Kubernetes) | - ---- - -## πŸ”„ Forgejo CI/CD Pipeline (pipe.gohorsejobs.com) - -O pipeline roda automaticamente via Forgejo Actions a cada push na branch `dev`. - -### Workflow: `.forgejo/workflows/deploy.yaml` - -| Job | Descricao | Status Atual | -|-----|-----------|-------------| -| **build-and-push** | Build Docker images (backend + backoffice), push to registry | OK | -| **deploy** | Deploy ao K3s via kubectl (requer KUBECONFIG secret) | OK (fix: KUBE_CONFIG β†’ KUBECONFIG) | - -### Pipeline Steps - -1. **build-and-push** (OK): - - Checkout code - - Docker login no registry `pipe.gohorsejobs.com` - - Build & push backend: `pipe.gohorsejobs.com/bohessefm/gohorsejobs:latest` - - Build & push backoffice: `pipe.gohorsejobs.com/bohessefm/backoffice:latest` - -2. **deploy** (FAIL - K3s nao configurado): - - Install kubectl - - Configure kubeconfig (via `secrets.KUBE_CONFIG`) - - Sync secrets e vars ao namespace `gohorsejobsdev` - - `kubectl apply -f k8s/dev/` - - Set image com SHA do commit - - Rollout restart deployments - -> **Nota:** O job deploy usava `secrets.KUBE_CONFIG` mas o secret se chama `KUBECONFIG`. Corrigido no commit atual. - -### Forgejo Actions Secrets & Variables - -**Secrets** (configurados em Settings > Actions > Secrets): -- `FORGEJO_TOKEN` β€” Login no container registry -- `KUBECONFIG` β€” Kubeconfig para acesso ao K3s cluster - -**Variables** (configurados em Settings > Actions > Variables): -- `DATABASE_URL`, `JWT_SECRET`, `PASSWORD_PEPPER`, `COOKIE_SECRET`, `COOKIE_DOMAIN` -- `BACKEND_PORT`, `BACKEND_HOST`, `ENV`, `CORS_ORIGINS`, `MTU` -- `AMQP_URL`, `S3_BUCKET`, `AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_ENDPOINT` -- `RSA_PRIVATE_KEY_BASE64`, `JWT_EXPIRATION` - -### Forgejo API - -```bash -# Token location: ~/.ssh/forgejo-token -FORGEJO_TOKEN="03d23c54672519c8473bd9c46ae7820b13c8b287" - -# Listar runs do pipeline -curl -s -H "Authorization: token $FORGEJO_TOKEN" \ - "https://pipe.gohorsejobs.com/api/v1/repos/bohessefm/gohorsejobs/actions/tasks?limit=5" - -# Listar repositorios -curl -s -H "Authorization: token $FORGEJO_TOKEN" \ - "https://pipe.gohorsejobs.com/api/v1/user/repos" -``` - -### GitHub Webhooks (Auto-deploy Coolify) - -Webhooks configurados no GitHub apontando para o Coolify: - -| App | Webhook URL | -|-----|-------------| -| Backend | `https://redbull.rede5.com.br/webhooks/source/github/events/manual?uuid=iw4sow8s0kkg4cccsk08gsoo&secret=...` | -| Frontend | `https://redbull.rede5.com.br/webhooks/source/github/events/manual?uuid=ao8g40scws0w4cgo8coc8o40&secret=...` | -| Backoffice | `https://redbull.rede5.com.br/webhooks/source/github/events/manual?uuid=hg48wkw4wggwsswcwc8sooo4&secret=...` | -| Seeder | `https://redbull.rede5.com.br/webhooks/source/github/events/manual?uuid=q4w48gos8cgssso00o8w8gck&secret=...` | - ---- - -## πŸ”‘ Credenciais e Tokens (Referencias) - -Todos os tokens estao armazenados em `~/.ssh/`: - -| Arquivo | Servico | Uso | -|---------|---------|-----| -| `~/.ssh/coolify-redbull-token` | Coolify API | Deploy e gerenciamento de apps | -| `~/.ssh/forgejo-token` | Forgejo API (pipe) | CI/CD, webhooks, repos | -| `~/.ssh/github-token` | GitHub API | Webhooks, repos | -| `~/.ssh/cloudflare-token` | Cloudflare API | DNS, cache | -| `~/.ssh/absam-token` | Absam Cloud API | VPS management | -| `~/.ssh/forgejo-gohorsejobs` | SSH Key | Forgejo Git operations | -| `~/.ssh/civo` | SSH Key | Acesso VPS Redbull | -| `~/.ssh/github` | SSH Key | GitHub Git operations | - ---- - -## πŸ’Ύ Storage & Persistence (`/mnt/data`) - -All persistent data and configuration files are stored in `/mnt/data` on the host. - -| Host Path | Container Path | Purpose | Type | -|-----------|----------------|---------|------| -| `/mnt/data/gohorsejobs/backend/.env` | (Injected Env) | **Backend Config:** Secrets, DB URL, Port settings. | File | -| `/mnt/data/gohorsejobs/backoffice/.env` | (Injected Env) | **Backoffice Config:** Secrets, DB URL. | File | -| `/mnt/data/gohorsejobs/seeder-api/.env` | (Injected Env) | **Seeder Config:** Secrets, DB URL. | File | -| `/mnt/data/postgres-general` | `/var/lib/postgresql/data` | **Database Storage:** Main storage for `postgres-main` container. Contains `gohorsejobs_dev` DB. | Directory | - -> **Backup Note:** To backup the environment, ensure `/mnt/data/gohorsejobs` and `/mnt/data/postgres-general` are included in snapshots. - ---- - -## 🌍 Service Maps & Networking - -### 🚦 Traefik Routing -Services are exposed via Traefik labels defined in the Quadlet `.container` files. - -| Domain | Service | Internal Port | Host Port (Debug) | -|--------|---------|---------------|-------------------| -| `dev.gohorsejobs.com` | `gohorsejobs-frontend-dev` | `3000` | `8523` | -| `api-tmp.gohorsejobs.com` | `gohorsejobs-backend-dev` | `8521` | `8521` | -| `b-tmp.gohorsejobs.com` | `gohorsejobs-backoffice-dev` | `3001` | - | -| `seeder.gohorsejobs.com` | `gohorsejobs-seeder-dev` | `8080` | `8522` | - -### πŸ›‘ Security -- **Backend/Seeder/Frontend** expose ports to the Host (`85xx`) for debugging/direct access if needed. -- **Backoffice** is *only* accessible via Traefik (internal network). -- **PostgreSQL** is *only* accessible internally via `web_proxy` network (no host port binding). - ---- - -## πŸ› οΈ Operational Guide - -### 1. View & Manage Configs -Configurations are **not** inside containers. Edit them on the host: -```bash -# Edit Backend Config -vim /mnt/data/gohorsejobs/backend/.env - -# Apply changes -systemctl restart gohorsejobs-backend-dev -``` - -### 2. Full Environment Restart -To restart all GoHorseJobs related services (excluding Database): -```bash -systemctl restart gohorsejobs-backend-dev gohorsejobs-backoffice-dev gohorsejobs-seeder-dev gohorsejobs-frontend-dev -``` - -### 3. Database Access -Access the local database directly via the `postgres-main` container: -```bash -# Internal Connection -docker exec -it postgres-main psql -U yuki -d gohorsejobs_dev +sequenceDiagram + participant Dev as Developer + participant Git as GitHub (Origin) + participant Forgejo as Pipe / Forgejo (CI) + participant Coolify as Coolify Webhook + participant VPS as Redbull (VPS) + + Dev->>Git: git push origin dev + Dev->>Forgejo: git push pipe dev + Note over Forgejo: Trigger Action (.forgejo/workflows/) + Forgejo->>Coolify: POST Deploy Webhook + Coolify-->>VPS: Fetch latest dev branch + Note over VPS: Coolify builds Nixpacks/Dockerfile + Coolify-->>VPS: docker stop + Coolify-->>VPS: docker run + Coolify-->>Forgejo: Deployment Success + Forgejo-->>Dev: Pipeline Green/Passed ``` --- -## πŸš€ Deployment Pipeline (Manual) +## πŸ› οΈ Environments Topology -Current workflow uses **Local Build** -> **Forgejo Registry** -> **Server Pull**. +| Environment | Branch | Use Case | Server Host | Reverse Proxy | Config Manager | Domains | +| :--- | :--- | :--- | :--- | :--- | :--- | :--- | +| **Local (Host)** | N/A | Developer Sandbox | Laptop / PC | None | `start.sh` (Bare metal) | `localhost:8963` | +| **DEV** | `dev` | Continuous Integration | `Redbull` VPS | Traefik | Coolify v4 | `local.`, `api-local.`, `b-local.` | +| **HML** | `hml` | QA / Staging Testing | `Apolo` VPS | Nginx proxy | Podman (Quadlet) | `hml.`, `api-hml.`, `b-hml.` | +| **PRD** | `main` | Production Live | `Zeus`/`Poseidon` | Traefik Ingress | Kubernetes (K3s) | `gohorsejobs.com`, `api.` | -### 1. Build & Push (Local Machine) +--- + +## πŸ”§ Coolify Instance (Redbull) + +The `dev` branch automatically mirrors to the Redbull server (185.194.141.70) managed by Coolify. +* **Coolify Interface:** `https://redbull.rede5.com.br` +* **GitHub Integration:** Relies on an SSH deployment key injected into the Forgejo actions. + +### Container Rules +* Never manually `docker run` on Redbull. Use the Coolify interface to add environment variables or alter build commands. +* **Secrets:** Managed via Coolify Environment Variables. (e.g., `PASSWORD_PEPPER`, `JWT_SECRET`). + +--- + +## πŸ’‘ Troubleshooting & Known Faultlines + +### 1. `Invalid Credentials` Right After DB Seed +If the Backend Go server complains about invalid passwords right after you run `npm run seed`: +1. Check the `PASSWORD_PEPPER` inside the Coolify instance for the `seeder-api`. +2. It **must exactly match** the pepper configured for the Backend API. +3. If they matched, rerun `npm run seed` via the Coolify interface to force hash recalculation over the raw DB rows. + +### 2. Out of Memory (OOMKilled) on Build +Node.js (for Next.js and NestJS) and Go can eat a lot of RAM during concurrent Coolify builds. +* **Fix:** Ensure the Server Actions inside Coolify are set to stagger deployments, preventing out-of-memory cascading crashes on Redbull. + +### 3. SSH Connectivity ```bash -# Login -podman login forgejo-gru.rede5.com.br - -# Build -cd backend -podman build -t forgejo-gru.rede5.com.br/rede5/gohorsejobs-backend:latest . - -# Push -podman push forgejo-gru.rede5.com.br/rede5/gohorsejobs-backend:latest -``` - -### 2. Deploy (On Apolo Server) -```bash -ssh root@apolo - -# Pull new image -podman pull forgejo-gru.rede5.com.br/rede5/gohorsejobs-backend:latest - -# Restart service (Systemd handles container recreation) -systemctl restart gohorsejobs-backend-dev +# Connecting to Redbull +ssh root@185.194.141.70 -p 22 ``` +If access is denied, ensure your local public key is registered in `~/.ssh/authorized_keys` or injected via the VPS admin panel. diff --git a/docs/README.md b/docs/README.md index 1fb7d46..50af649 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,48 @@ -# Project Documentation - GoHorse Jobs +# πŸ“š Project Documentation - GoHorseJobs -This directory holds internal team documentation, guides, and extended context regarding project structure and architecture. +Welcome to the central documentation hub for **GoHorseJobs**. This repository contains the collective knowledge, architecture decisions, and operational guides for our B2B SaaS recruitment platform. -## 🚨 AI Rules Warning 🚨 -Before making ANY changes to infrastructure deployment manifests or Kubernetes files, please refer to the project-wide AI Rules located at `.agent/rules.md`. -**Do NOT touch `k3s` or `k8s` files.** Do not alter raw RSA/base64 authentication keys. +--- + +## 🧭 Navigation & Index + +Choose a specific domain below to dive deep into our technical implementation and guides. + +### πŸ€– 1. AI & Developer Directives +* **[Context for AI Agents (AGENTS.md)](AGENTS.md)**: The supreme source of truth for Claude, Cursor, and other AI coding assistants. Contains stack rules, passwords, gotchas, and layout contexts. +* **[Rules (.agent/rules.md)](../.agent/rules.md)**: Absolute system boundaries and deployment limits. + +### πŸ—οΈ 2. High-Level Architecture +* **[DevOps & Infrastructure (DEVOPS.md)](DEVOPS.md)**: Full mapping of Cloudflare DNS, Traefik, VPS (Redbull/Apolo), Docker/Coolify containers, and CI/CD pipelines (Forgejo/Drone). Includes rich Mermaid diagrams. +* **[Database Schema (DATABASE.md)](DATABASE.md)**: PostgreSQL schemas, relationships, UUID v7 indexing strategies, and ERD visualizing the core data flow. + +### πŸ”Œ 3. Application Interfaces (APIs) +* **[API Routes (API.md)](API.md)**: Endpoints mapped for the Go Backend (`/api/v1`), NestJS Backoffice services, and internal Node.js Seeder-API. +* **[API Security (API_SECURITY.md)](API_SECURITY.md)**: Details on HS256 JWT implementations, RBAC (Role-Based Access Control) levels, and CORS policies. +* **[AppSec Strategy (APPSEC_STRATEGY.md)](APPSEC_STRATEGY.md)**: The core mitigation plan against XSS, IDOR, Mass Assignment, and testing vectors within Next.js. + +### πŸ«‚ 4. Operations & Testing +* **[Test Users & Data (TEST_USERS.md)](TEST_USERS.md)**: Comprehensive list of robust local credentials, passwords, dummy candidates, and seeded companies. +* **[Deployment Routes (WORKFLOWS.md)](WORKFLOWS.md)**: A catalog of our `.forgejo` and GitHub Actions synchronizations. +* **[Tasks (TASKS.md)](TASKS.md)**: Open checklist of internal improvements. + +--- + +## πŸš€ Quick Launch (Dev) + +If you are a new developer or setting up the environment post-clone, rely on our interactive script: + +```bash +cd /path/to/gohorsejobs +./start.sh +``` + +**Options overview**: +* `1`: General Start (Frontend + Backend) +* `2`: Fresh Start (Reset Postgres Data -> Run Migrations -> Seed Core Data -> Start) +* `3`: God Mode (Frontend + Backend + Backoffice) + +--- + +## 🚨 Final Notice +**Do NOT** alter base configuration settings (such as encryption secrets, or Kubernetes `k3s`/`k8s` manifests) unless explicitly guided by the Lead Engineer or following the strict protocols in `.agent/rules.md`. diff --git a/docs/TEST_USERS.md b/docs/TEST_USERS.md index 18442bd..1502c57 100644 --- a/docs/TEST_USERS.md +++ b/docs/TEST_USERS.md @@ -1,6 +1,55 @@ -# Test Users - GoHorseJobs +# πŸ§ͺ Test Users & Data Scenarios - GoHorseJobs -> **DEPRECATED / MOVED** -> Este arquivo foi depreciado e toda a documentaΓ§Γ£o das matrizes de UsuΓ‘rios de Teste foi movida e unificada diretamente no **`[README.md](../README.md)`** da raiz do projeto. -> -> As rotas antigas do `superadmin` foram aposentadas via migrations da base, onde o perfil oficial master passa a se chamar unicamente: `lol`. +When running the platform locally (via `start.sh`) or on the DEV server (`local.gohorsejobs.com`), the `seeder-api` provisions a rich set of test accounts representing distinct personas. + +All accounts use the exact same password to simplify development. + +**Universal Test Password:** `Admin@2025!` + +--- + +## πŸ‘₯ Core Personas (The "Golden" Accounts) + +These are the primary accounts you should use for daily development and E2E testing. + +| Role | Identifier (Username) | Email | Purpose | +| :--- | :--- | :--- | :--- | +| **Superadmin** | `lol` | `lol@gohorsejobs.com` | Complete system access. Bypass company walls. Manage features. | +| **Superadmin** | `superadmin` | `admin@gohorsejobs.com` | Secondary global admin. | +| **Admin** | `admin` | `moderator@gohorsejobs.com` | Reviews pending companies/jobs in backoffice. | +| **Recruiter** | `recruiter` | `hr@techcorp.com` | Posts jobs, manages applicants for "TechCorp" (Company ID: 1). | +| **Recruiter** | `jane_hr` | `jane.doe@startup.io` | Recruiter for a pending/unapproved company to test gating. | +| **Candidate** | `candidate` | `carlos.dev@gmail.com` | Standard job seeker. Pre-filled resume and skills. Has 3 active applications. | +| **Candidate** | `newbie` | `new.user@hotmail.com` | Fresh account with 0 applications and empty profile to test onboarding flows. | + +--- + +## 🏒 Auto-Generated Companies & Jobs + +The DB seeder simulates a healthy marketplace. +If you run `npm run seed:lite`, the DB receives: +* **50 Companies**: Ranging from "TechCorp" (Active) to "SuspiciousLTDA" (Pending/Rejected). +* **100 Jobs**: Distributed among the active companies. Various statuses (`published`, `draft`, `closed`). +* **200 Applications**: Dummy candidates applying to random jobs, in varied pipelines (`pending`, `reviewing`, `accepted`). + +--- + +## 🚨 Login & Auth "Gotchas" + +### 1. `Invalid Credentials` Right After Fresh Seed +If you completely reset the DB using `start.sh` (Option 6) or manually clear Postgres, and your first login returns "Invalid credentials", **do not panic and do not manually change the DB hash**. +* **Cause**: The Node.js seeder calculates the bcrypt hash using an environment variable called `PASSWORD_PEPPER` (`gohorse-pepper`). If this variable was empty or mismatched with the Backend API, the hash is physically wrong. +* **Fix**: Ensure your `.env` files in both `backend` and `seeder-api` contain `PASSWORD_PEPPER=gohorse-pepper`. Then simply run `cd seeder-api && npm run seed` again to fix the hashes. + +### 2. Login Payload Fields +The frontend sends: +```json +{ + "email": "lol", + "password": "..." +} +``` +**Important:** Even though the frontend passes the username `lol`, the JSON key **must** be `"email"`. The Go backend natively handles resolving `"email"` against the `identifier` OR `email` database columns. + +### 3. Account Status Flags +The `users` table has an `is_active` boolean. If a user is manually deactivated by an admin, the login endpoint will return a generic `401 Unauthorized` (or specific Account Locked error depending on the exact route version) to prevent user enumeration.