Compare commits
2 commits
1630605a78
...
6ae4f01f5c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ae4f01f5c | ||
|
|
e1c61289af |
8 changed files with 306 additions and 16 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -21,6 +21,7 @@ Thumbs.db
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
!.env.example
|
!.env.example
|
||||||
|
config/mcp.gohorsejobs.json
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logs
|
# Logs
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -47,9 +48,70 @@ func BuildConnectionString() (string, error) {
|
||||||
return dbURL, nil
|
return dbURL, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if mcpPath := strings.TrimSpace(os.Getenv("MCP_JSON_PATH")); mcpPath != "" {
|
||||||
|
dbURL, err := databaseURLFromMCPJSON(mcpPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to load database url from MCP json (%s): %w", mcpPath, err)
|
||||||
|
}
|
||||||
|
if dbURL != "" {
|
||||||
|
log.Printf("Using DATABASE_URL from MCP_JSON_PATH (%s)", mcpPath)
|
||||||
|
return dbURL, nil
|
||||||
|
}
|
||||||
|
log.Printf("MCP_JSON_PATH is set but no database URL was found in %s", mcpPath)
|
||||||
|
}
|
||||||
|
|
||||||
return "", fmt.Errorf("DATABASE_URL environment variable not set")
|
return "", fmt.Errorf("DATABASE_URL environment variable not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func databaseURLFromMCPJSON(path string) (string, error) {
|
||||||
|
raw, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload map[string]interface{}
|
||||||
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := [][]string{
|
||||||
|
{"database_url"},
|
||||||
|
{"databaseUrl"},
|
||||||
|
{"database", "url"},
|
||||||
|
{"infra", "database_url"},
|
||||||
|
{"infra", "databaseUrl"},
|
||||||
|
{"infra", "database", "url"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pathKeys := range candidates {
|
||||||
|
if val := nestedString(payload, pathKeys...); val != "" {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nestedString(input map[string]interface{}, keys ...string) string {
|
||||||
|
var current interface{} = input
|
||||||
|
for _, key := range keys {
|
||||||
|
obj, ok := current.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
current, ok = obj[key]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
str, ok := current.(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(str)
|
||||||
|
}
|
||||||
|
|
||||||
func RunMigrations() {
|
func RunMigrations() {
|
||||||
migrationDir, err := resolveMigrationDir()
|
migrationDir, err := resolveMigrationDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -261,15 +262,15 @@ func (s *CredentialsService) EncryptPayload(payload string) (string, error) {
|
||||||
// BootstrapCredentials checks if credentials are in DB, if not, migrates from Env
|
// BootstrapCredentials checks if credentials are in DB, if not, migrates from Env
|
||||||
func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
// List of services and their env mapping
|
// List of services and their env mapping
|
||||||
services := map[string]func() interface{}{
|
services := map[string]func() map[string]string{
|
||||||
"stripe": func() interface{} {
|
"stripe": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"secretKey": os.Getenv("STRIPE_SECRET_KEY"),
|
"secretKey": os.Getenv("STRIPE_SECRET_KEY"),
|
||||||
"webhookSecret": os.Getenv("STRIPE_WEBHOOK_SECRET"),
|
"webhookSecret": os.Getenv("STRIPE_WEBHOOK_SECRET"),
|
||||||
"publishableKey": os.Getenv("STRIPE_PUBLISHABLE_KEY"),
|
"publishableKey": os.Getenv("STRIPE_PUBLISHABLE_KEY"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"storage": func() interface{} {
|
"storage": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"endpoint": os.Getenv("AWS_ENDPOINT"),
|
"endpoint": os.Getenv("AWS_ENDPOINT"),
|
||||||
"accessKey": os.Getenv("AWS_ACCESS_KEY_ID"),
|
"accessKey": os.Getenv("AWS_ACCESS_KEY_ID"),
|
||||||
|
|
@ -278,37 +279,37 @@ func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
"region": os.Getenv("AWS_REGION"),
|
"region": os.Getenv("AWS_REGION"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lavinmq": func() interface{} {
|
"lavinmq": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"amqpUrl": os.Getenv("AMQP_URL"),
|
"amqpUrl": os.Getenv("AMQP_URL"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloudflare_config": func() interface{} {
|
"cloudflare_config": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"apiToken": os.Getenv("CLOUDFLARE_API_TOKEN"),
|
"apiToken": os.Getenv("CLOUDFLARE_API_TOKEN"),
|
||||||
"zoneId": os.Getenv("CLOUDFLARE_ZONE_ID"),
|
"zoneId": os.Getenv("CLOUDFLARE_ZONE_ID"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cpanel": func() interface{} {
|
"cpanel": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"host": os.Getenv("CPANEL_HOST"),
|
"host": os.Getenv("CPANEL_HOST"),
|
||||||
"username": os.Getenv("CPANEL_USERNAME"),
|
"username": os.Getenv("CPANEL_USERNAME"),
|
||||||
"apiToken": os.Getenv("CPANEL_API_TOKEN"),
|
"apiToken": os.Getenv("CPANEL_API_TOKEN"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"appwrite": func() interface{} {
|
"appwrite": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"endpoint": os.Getenv("APPWRITE_ENDPOINT"),
|
"endpoint": os.Getenv("APPWRITE_ENDPOINT"),
|
||||||
"projectId": os.Getenv("APPWRITE_PROJECT_ID"),
|
"projectId": os.Getenv("APPWRITE_PROJECT_ID"),
|
||||||
"apiKey": os.Getenv("APPWRITE_API_KEY"),
|
"apiKey": os.Getenv("APPWRITE_API_KEY"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fcm_service_account": func() interface{} {
|
"fcm_service_account": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"serviceAccountJson": os.Getenv("FCM_SERVICE_ACCOUNT_JSON"),
|
"serviceAccountJson": os.Getenv("FCM_SERVICE_ACCOUNT_JSON"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"smtp": func() interface{} {
|
"smtp": func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"host": os.Getenv("SMTP_HOST"),
|
"host": os.Getenv("SMTP_HOST"),
|
||||||
"port": os.Getenv("SMTP_PORT"),
|
"port": os.Getenv("SMTP_PORT"),
|
||||||
|
|
@ -321,6 +322,17 @@ func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mcpServices := map[string]map[string]string{}
|
||||||
|
if mcpPath := strings.TrimSpace(os.Getenv("MCP_JSON_PATH")); mcpPath != "" {
|
||||||
|
loaded, err := loadMCPCredentialsFromFile(mcpPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("[CredentialsBootstrap] Warning: failed to read MCP JSON (%s): %v\n", mcpPath, err)
|
||||||
|
} else {
|
||||||
|
mcpServices = loaded
|
||||||
|
fmt.Printf("[CredentialsBootstrap] Loaded MCP JSON services (%d) from %s\n", len(mcpServices), mcpPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for service, getEnvData := range services {
|
for service, getEnvData := range services {
|
||||||
// Check if already configured
|
// Check if already configured
|
||||||
configured, err := s.isServiceConfigured(ctx, service)
|
configured, err := s.isServiceConfigured(ctx, service)
|
||||||
|
|
@ -331,19 +343,27 @@ func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
|
|
||||||
if !configured {
|
if !configured {
|
||||||
data := getEnvData()
|
data := getEnvData()
|
||||||
// Validate if env vars exist (naive check: at least one field not empty)
|
|
||||||
|
// MCP JSON values take priority when present.
|
||||||
|
if fromMCP, ok := mcpServices[service]; ok {
|
||||||
|
for k, v := range fromMCP {
|
||||||
|
if strings.TrimSpace(v) != "" {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if at least one field is present.
|
||||||
hasData := false
|
hasData := false
|
||||||
jsonBytes, _ := json.Marshal(data)
|
for _, v := range data {
|
||||||
var stringMap map[string]string
|
if strings.TrimSpace(v) != "" {
|
||||||
json.Unmarshal(jsonBytes, &stringMap)
|
|
||||||
for _, v := range stringMap {
|
|
||||||
if v != "" {
|
|
||||||
hasData = true
|
hasData = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasData {
|
if hasData {
|
||||||
|
jsonBytes, _ := json.Marshal(data)
|
||||||
fmt.Printf("[CredentialsBootstrap] Migrating %s from Env to DB...\n", service)
|
fmt.Printf("[CredentialsBootstrap] Migrating %s from Env to DB...\n", service)
|
||||||
encrypted, err := s.EncryptPayload(string(jsonBytes))
|
encrypted, err := s.EncryptPayload(string(jsonBytes))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -361,6 +381,35 @@ func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadMCPCredentialsFromFile(path string) (map[string]map[string]string, error) {
|
||||||
|
raw, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload struct {
|
||||||
|
Services map[string]map[string]string `json:"services"`
|
||||||
|
Credentials map[string]map[string]string `json:"credentials"`
|
||||||
|
ExternalServices map[string]map[string]string `json:"external_services"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &payload); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
merged := map[string]map[string]string{}
|
||||||
|
for svc, values := range payload.Services {
|
||||||
|
merged[svc] = values
|
||||||
|
}
|
||||||
|
for svc, values := range payload.Credentials {
|
||||||
|
merged[svc] = values
|
||||||
|
}
|
||||||
|
for svc, values := range payload.ExternalServices {
|
||||||
|
merged[svc] = values
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *CredentialsService) isServiceConfigured(ctx context.Context, serviceName string) (bool, error) {
|
func (s *CredentialsService) isServiceConfigured(ctx context.Context, serviceName string) (bool, error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
query := `SELECT EXISTS(SELECT 1 FROM external_services_credentials WHERE service_name = $1)`
|
query := `SELECT EXISTS(SELECT 1 FROM external_services_credentials WHERE service_name = $1)`
|
||||||
|
|
|
||||||
44
config/mcp.gohorsejobs.example.json
Normal file
44
config/mcp.gohorsejobs.example.json
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"infra": {
|
||||||
|
"database": {
|
||||||
|
"url": "postgresql://user:password@host:5432/gohorsejobs_dev?sslmode=require"
|
||||||
|
},
|
||||||
|
"cloud": {
|
||||||
|
"provider": "cloudflare",
|
||||||
|
"region": "sa-east-1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"external_services": {
|
||||||
|
"cloudflare_config": {
|
||||||
|
"apiToken": "CF_API_TOKEN",
|
||||||
|
"zoneId": "CF_ZONE_ID"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"endpoint": "https://s3.example.com",
|
||||||
|
"accessKey": "ACCESS_KEY",
|
||||||
|
"secretKey": "SECRET_KEY",
|
||||||
|
"bucket": "gohorsejobs",
|
||||||
|
"region": "sa-east-1"
|
||||||
|
},
|
||||||
|
"lavinmq": {
|
||||||
|
"amqpUrl": "amqps://user:pass@host/vhost"
|
||||||
|
},
|
||||||
|
"appwrite": {
|
||||||
|
"endpoint": "https://cloud.appwrite.io/v1",
|
||||||
|
"projectId": "PROJECT_ID",
|
||||||
|
"apiKey": "APPWRITE_API_KEY"
|
||||||
|
},
|
||||||
|
"fcm_service_account": {
|
||||||
|
"serviceAccountJson": "{\"type\":\"service_account\",\"project_id\":\"...\"}"
|
||||||
|
},
|
||||||
|
"smtp": {
|
||||||
|
"host": "smtp.example.com",
|
||||||
|
"port": "587",
|
||||||
|
"username": "user@example.com",
|
||||||
|
"password": "password",
|
||||||
|
"from_email": "noreply@gohorsejobs.com",
|
||||||
|
"from_name": "GoHorse Jobs",
|
||||||
|
"secure": "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
56
docs/MCP_INTEGRATION.md
Normal file
56
docs/MCP_INTEGRATION.md
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
# MCP Integration (JSON) - GoHorseJobs
|
||||||
|
|
||||||
|
Este guia centraliza como conectar o GoHorseJobs a um arquivo JSON de configuração para MCP/infra cloud/banco.
|
||||||
|
|
||||||
|
## Objetivo
|
||||||
|
|
||||||
|
Permitir bootstrap de credenciais e URL de banco sem hardcode, usando um JSON local controlado por variável de ambiente.
|
||||||
|
|
||||||
|
## Variável de ambiente
|
||||||
|
|
||||||
|
Defina:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
MCP_JSON_PATH=/caminho/absoluto/para/mcp.gohorsejobs.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Estrutura esperada do JSON
|
||||||
|
|
||||||
|
Use como base:
|
||||||
|
|
||||||
|
- `config/mcp.gohorsejobs.example.json`
|
||||||
|
|
||||||
|
Campos suportados pelo backend:
|
||||||
|
|
||||||
|
- Banco:
|
||||||
|
- `infra.database.url`
|
||||||
|
- `database.url`
|
||||||
|
- `database_url`
|
||||||
|
- `databaseUrl`
|
||||||
|
- Credenciais de serviços:
|
||||||
|
- `external_services.<service>`
|
||||||
|
- `credentials.<service>`
|
||||||
|
- `services.<service>`
|
||||||
|
|
||||||
|
## Serviços reconhecidos no bootstrap
|
||||||
|
|
||||||
|
- `stripe`
|
||||||
|
- `storage`
|
||||||
|
- `cloudflare_config`
|
||||||
|
- `cpanel`
|
||||||
|
- `lavinmq`
|
||||||
|
- `appwrite`
|
||||||
|
- `fcm_service_account`
|
||||||
|
- `smtp`
|
||||||
|
|
||||||
|
## Comportamento de prioridade
|
||||||
|
|
||||||
|
1. `DATABASE_URL` no ambiente continua tendo prioridade máxima.
|
||||||
|
2. Se `DATABASE_URL` não existir, o backend tenta `MCP_JSON_PATH`.
|
||||||
|
3. Para credenciais de serviços, o bootstrap usa env vars e sobrescreve com valores do JSON quando presentes.
|
||||||
|
|
||||||
|
## Segurança operacional
|
||||||
|
|
||||||
|
- Não commitar arquivo real com segredos.
|
||||||
|
- Commite apenas o template `config/mcp.gohorsejobs.example.json`.
|
||||||
|
- Mantenha `config/mcp.gohorsejobs.json` local e ignorado no git.
|
||||||
|
|
@ -15,6 +15,8 @@ Choose a specific domain below to dive deep into our technical implementation an
|
||||||
### 🏗️ 2. High-Level Architecture
|
### 🏗️ 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.
|
* **[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.
|
* **[Database Schema (DATABASE.md)](DATABASE.md)**: PostgreSQL schemas, relationships, UUID v7 indexing strategies, and ERD visualizing the core data flow.
|
||||||
|
* **[MCP Integration (MCP_INTEGRATION.md)](MCP_INTEGRATION.md)**: JSON-based integration for infra/cloud/database bootstrap.
|
||||||
|
* **[Unified Status (UNIFIED_STATUS.md)](UNIFIED_STATUS.md)**: Consolidated view of docs + pending activities on `dev`.
|
||||||
|
|
||||||
### 🔌 3. Application Interfaces (APIs)
|
### 🔌 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 Routes (API.md)](API.md)**: Endpoints mapped for the Go Backend (`/api/v1`), NestJS Backoffice services, and internal Node.js Seeder-API.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,6 @@ Lista detalhada de tarefas para evitar retrabalho.
|
||||||
## 🔥 Sprint Atual
|
## 🔥 Sprint Atual
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
- [ ] Video interviews endpoint
|
|
||||||
- [ ] AI matching algorithm
|
- [ ] AI matching algorithm
|
||||||
- [ ] Webhook sistema
|
- [ ] Webhook sistema
|
||||||
|
|
||||||
|
|
@ -63,6 +62,9 @@ Lista detalhada de tarefas para evitar retrabalho.
|
||||||
- [ ] User analytics
|
- [ ] User analytics
|
||||||
- [ ] Export features
|
- [ ] Export features
|
||||||
|
|
||||||
|
### Adiado (fora do escopo por enquanto)
|
||||||
|
- [ ] Video interviews endpoint (decisão: adiado em 2026-03-09)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚧 Não Fazer (Evitar Retrabalho)
|
## 🚧 Não Fazer (Evitar Retrabalho)
|
||||||
|
|
@ -115,3 +117,23 @@ NestJS → Consume → Fetch template → Render → Send
|
||||||
- [ROADMAP.md](ROADMAP.md) - Roadmap geral
|
- [ROADMAP.md](ROADMAP.md) - Roadmap geral
|
||||||
- [API_SECURITY.md](API_SECURITY.md) - Segurança
|
- [API_SECURITY.md](API_SECURITY.md) - Segurança
|
||||||
- [DEVOPS.md](DEVOPS.md) - Infraestrutura
|
- [DEVOPS.md](DEVOPS.md) - Infraestrutura
|
||||||
|
- [MCP_INTEGRATION.md](MCP_INTEGRATION.md) - Integração MCP via JSON
|
||||||
|
- [UNIFIED_STATUS.md](UNIFIED_STATUS.md) - Consolidação de status e pendências
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Verificação de Pendências (2026-03-09)
|
||||||
|
|
||||||
|
Status conferido na branch `dev`:
|
||||||
|
|
||||||
|
- Sprint atual permanece em aberto:
|
||||||
|
- AI matching algorithm
|
||||||
|
- Webhook sistema
|
||||||
|
- PWA manifest
|
||||||
|
- Service worker
|
||||||
|
- Offline support
|
||||||
|
- Revenue reports
|
||||||
|
- User analytics
|
||||||
|
- Export features
|
||||||
|
- Itens adiados:
|
||||||
|
- Video interviews endpoint
|
||||||
|
|
|
||||||
54
docs/UNIFIED_STATUS.md
Normal file
54
docs/UNIFIED_STATUS.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# GoHorseJobs - Documentacao Unificada e Status (dev)
|
||||||
|
|
||||||
|
Atualizado em: 2026-03-09
|
||||||
|
|
||||||
|
## Escopo consolidado
|
||||||
|
|
||||||
|
- Arquitetura e infraestrutura: `docs/DEVOPS.md`, `docs/DATABASE.md`
|
||||||
|
- APIs e seguranca: `docs/API.md`, `docs/API_SECURITY.md`, `docs/APPSEC_STRATEGY.md`
|
||||||
|
- Regras de agente: `docs/AGENTS.md`, `.agent/rules.md`
|
||||||
|
- Operacao MCP/JSON: `docs/MCP_INTEGRATION.md`
|
||||||
|
|
||||||
|
## Pendencias verificadas
|
||||||
|
|
||||||
|
Fonte principal: `docs/TASKS.md` e `docs/ROADMAP.md`
|
||||||
|
|
||||||
|
Sprint atual em aberto:
|
||||||
|
|
||||||
|
- Backend:
|
||||||
|
- [ ] AI matching algorithm
|
||||||
|
- [ ] Webhook sistema
|
||||||
|
- Frontend:
|
||||||
|
- [ ] PWA manifest
|
||||||
|
- [ ] Service worker
|
||||||
|
- [ ] Offline support
|
||||||
|
- Backoffice:
|
||||||
|
- [ ] Revenue reports
|
||||||
|
- [ ] User analytics
|
||||||
|
- [ ] Export features
|
||||||
|
|
||||||
|
Itens adiados (fora de escopo no momento):
|
||||||
|
|
||||||
|
- Backend:
|
||||||
|
- [ ] Video interviews endpoint
|
||||||
|
|
||||||
|
Prioridades tecnicas em andamento (roadmap):
|
||||||
|
|
||||||
|
- Confiabilidade do fluxo de vagas/candidaturas
|
||||||
|
- Seguranca e governanca (RBAC + auditoria)
|
||||||
|
- Operacao/deploy (padronizacao dev/hml/prd, rollback, scripts)
|
||||||
|
|
||||||
|
## Decisao tecnica aplicada neste update
|
||||||
|
|
||||||
|
- O backend passa a aceitar configuracao JSON via `MCP_JSON_PATH`:
|
||||||
|
- URL de banco como fallback quando `DATABASE_URL` nao estiver definido.
|
||||||
|
- Credenciais de servicos para bootstrap no banco.
|
||||||
|
- Template de referencia adicionado em `config/mcp.gohorsejobs.example.json`.
|
||||||
|
|
||||||
|
## Proximos passos recomendados
|
||||||
|
|
||||||
|
- Criar arquivo local real `config/mcp.gohorsejobs.json` (nao versionado).
|
||||||
|
- Setar `MCP_JSON_PATH` em dev/hml.
|
||||||
|
- Validar boot do backend com:
|
||||||
|
- banco via JSON
|
||||||
|
- bootstrap de credenciais sem regressao.
|
||||||
Loading…
Reference in a new issue