docs: add API documentation with routes and permissions

- Create docs/API.md with complete API reference
  - 40+ routes documented
  - Permission matrix by role
  - Module breakdown (Auth, Users, Jobs, Applications, etc.)
  - Error response examples
  - ID format documentation

- Fix seeders to use SERIAL (not UUID) for jobs.id
  - jobs.js: let DB auto-generate id
  - acme.js, fictional-companies.js, epic-companies.js: same fix

- Update README.md with API documentation link
This commit is contained in:
Tiago Yamamoto 2025-12-24 11:25:55 -03:00
parent ae4a3e5e63
commit 7f30a214f7
6 changed files with 499 additions and 19 deletions

View file

@ -223,6 +223,7 @@ Veja a documentação completa do banco de dados em: [docs/DATABASE.md](docs/DAT
### Roadmap & Tarefas ### Roadmap & Tarefas
- 🗺️ **Roadmap:** [docs/ROADMAP.md](docs/ROADMAP.md) - Status e progresso do projeto - 🗺️ **Roadmap:** [docs/ROADMAP.md](docs/ROADMAP.md) - Status e progresso do projeto
- 📋 **Tarefas:** [docs/TASKS.md](docs/TASKS.md) - Lista detalhada para evitar retrabalho - 📋 **Tarefas:** [docs/TASKS.md](docs/TASKS.md) - Lista detalhada para evitar retrabalho
- 📡 **API:** [docs/API.md](docs/API.md) - Documentação completa com rotas e permissões
### Documentação por Componente ### Documentação por Componente
| Componente | Documentação | | Componente | Documentação |

480
docs/API.md Normal file
View file

@ -0,0 +1,480 @@
# 📡 GoHorse Jobs - API Documentation
Complete API reference with routes, permissions, and modules.
> **Last Updated:** 2024-12-24
> **Base URL:** `http://localhost:8521/api/v1`
> **Auth:** JWT Bearer Token or HttpOnly Cookie
---
## 🔐 Authentication
### Methods
1. **Authorization Header:** `Authorization: Bearer <token>`
2. **Cookie:** `jwt=<token>` (HttpOnly, Secure)
### Roles
| Role | Code | Level | Description |
|------|------|-------|-------------|
| **SuperAdmin** | `superadmin` | 0 | Platform administrator |
| **CompanyAdmin** | `companyAdmin` | 1 | Company administrator |
| **Recruiter** | `recruiter` | 2 | Job poster |
| **JobSeeker** | `jobSeeker` | 3 | Candidate |
| **Guest** | - | - | No authentication |
---
## 📋 Module: Authentication
### Login
```http
POST /api/v1/auth/login
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public | ❌ | Guest | Authenticate user |
**Request:**
```json
{
"identifier": "superadmin",
"password": "Admin@2025!"
}
```
**Response (200):**
```json
{
"token": "eyJhbGciOiJI...",
"user": { "id": 1, "role": "superadmin", "name": "Super Administrator" }
}
```
---
### Register Candidate
```http
POST /api/v1/auth/register
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public | ❌ | Guest | Register new job seeker |
**Request:**
```json
{
"name": "John Doe",
"email": "john@example.com",
"password": "SecurePass123!"
}
```
---
## 🏢 Module: Companies
### Create Company
```http
POST /api/v1/companies
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public | ❌ | Guest | Register new company |
### List Companies
```http
GET /api/v1/companies
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Smart | ⚪ Optional | Guest/Admin | Public list or admin full list |
### Update Company Status
```http
PATCH /api/v1/companies/{id}/status
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Protected | ✅ | superadmin, admin | Activate/deactivate company |
---
## 👥 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, companyAdmin | 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* | ❌ | companyAdmin, recruiter | Create new job posting |
### Update Job
```http
PUT /api/v1/jobs/{id}
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public* | ❌ | companyAdmin, recruiter | Update job posting |
### Delete Job
```http
DELETE /api/v1/jobs/{id}
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public* | ❌ | companyAdmin, 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 | ❌ | companyAdmin, recruiter | List applications |
### Get Application by ID
```http
GET /api/v1/applications/{id}
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public | ❌ | companyAdmin, recruiter | Get application details |
### Update Application Status
```http
PUT /api/v1/applications/{id}/status
```
| Field | Auth | Roles | Description |
|-------|------|-------|-------------|
| Public | ❌ | companyAdmin, 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 |
---
## 🔑 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)

View file

@ -192,15 +192,15 @@ export async function seedAcmeCorp() {
for (let b = 0; b < 4; b++) { for (let b = 0; b < 4; b++) {
selectedBenefits.push(benefits[(i + b) % benefits.length]); selectedBenefits.push(benefits[(i + b) % benefits.length]);
} }
const jobId = crypto.randomUUID();
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs ( company_id, created_by, title, description,
salary_min, salary_max, salary_type, employment_type, working_hours, salary_min, salary_max, salary_type, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
acmeCompanyId, acmeCompanyId,
seedUserId, seedUserId,
title, title,

View file

@ -294,14 +294,14 @@ async function createCompanyAndJobs(companyData, jobs) {
for (let i = 0; i < jobs.length; i++) { for (let i = 0; i < jobs.length; i++) {
const job = jobs[i]; const job = jobs[i];
const jobId = crypto.randomUUID();
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs ( company_id, created_by, title, description,
salary_min, salary_max, salary_type, employment_type, working_hours, salary_min, salary_max, salary_type, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
companyId, companyId,
seedUserId, seedUserId,
job.title, job.title,

View file

@ -190,14 +190,14 @@ export async function seedStarkIndustries() {
for (let i = 0; i < starkJobs.length; i++) { for (let i = 0; i < starkJobs.length; i++) {
const job = starkJobs[i]; const job = starkJobs[i];
const jobId = crypto.randomUUID();
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs ( company_id, created_by, title, description,
salary_min, salary_max, salary_type, employment_type, working_hours, salary_min, salary_max, salary_type, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
companyId, companyId,
seedUserId, seedUserId,
job.title, job.title,
@ -275,14 +275,14 @@ export async function seedLosPollosHermanos() {
for (let i = 0; i < losPollosJobs.length; i++) { for (let i = 0; i < losPollosJobs.length; i++) {
const job = losPollosJobs[i]; const job = losPollosJobs[i];
const jobId = crypto.randomUUID();
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs ( company_id, created_by, title, description,
salary_min, salary_max, salary_type, employment_type, working_hours, salary_min, salary_max, salary_type, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
companyId, companyId,
seedUserId, seedUserId,
job.title, job.title,
@ -362,14 +362,14 @@ export async function seedSpringfieldNuclear() {
for (let i = 0; i < springfieldJobs.length; i++) { for (let i = 0; i < springfieldJobs.length; i++) {
const job = springfieldJobs[i]; const job = springfieldJobs[i];
const jobId = crypto.randomUUID();
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs ( company_id, created_by, title, description,
salary_min, salary_max, salary_type, employment_type, working_hours, salary_min, salary_max, salary_type, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
companyId, companyId,
seedUserId, seedUserId,
job.title, job.title,

View file

@ -97,15 +97,14 @@ export async function seedJobs() {
const employmentType = employmentTypes[i % employmentTypes.length]; const employmentType = employmentTypes[i % employmentTypes.length];
const currency = currencies[i % currencies.length]; const currency = currencies[i % currencies.length];
const salaryType = salaryTypes[i % salaryTypes.length]; const salaryType = salaryTypes[i % salaryTypes.length];
const jobId = crypto.randomUUID();
// jobs.id is SERIAL - let DB auto-generate
await pool.query(` await pool.query(`
INSERT INTO jobs (id, company_id, created_by, title, description, INSERT INTO jobs (company_id, created_by, title, description,
salary_min, salary_max, salary_type, currency, employment_type, working_hours, salary_min, salary_max, salary_type, currency, employment_type, working_hours,
location, requirements, benefits, visa_support, language_level, status, work_mode) location, requirements, benefits, visa_support, language_level, status, work_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
`, [ `, [
jobId,
company.id, company.id,
seedUserId, seedUserId,
title, title,