Compare commits
No commits in common. "dev" and "main" have entirely different histories.
8 changed files with 16 additions and 306 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -21,7 +21,6 @@ Thumbs.db
|
||||||
.env.local
|
.env.local
|
||||||
.env.*.local
|
.env.*.local
|
||||||
!.env.example
|
!.env.example
|
||||||
config/mcp.gohorsejobs.json
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logs
|
# Logs
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -48,70 +47,9 @@ 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,7 +12,6 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -262,15 +261,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() map[string]string{
|
services := map[string]func() interface{}{
|
||||||
"stripe": func() map[string]string {
|
"stripe": func() interface{} {
|
||||||
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() map[string]string {
|
"storage": func() interface{} {
|
||||||
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"),
|
||||||
|
|
@ -279,37 +278,37 @@ func (s *CredentialsService) BootstrapCredentials(ctx context.Context) error {
|
||||||
"region": os.Getenv("AWS_REGION"),
|
"region": os.Getenv("AWS_REGION"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lavinmq": func() map[string]string {
|
"lavinmq": func() interface{} {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"amqpUrl": os.Getenv("AMQP_URL"),
|
"amqpUrl": os.Getenv("AMQP_URL"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloudflare_config": func() map[string]string {
|
"cloudflare_config": func() interface{} {
|
||||||
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() map[string]string {
|
"cpanel": func() interface{} {
|
||||||
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() map[string]string {
|
"appwrite": func() interface{} {
|
||||||
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() map[string]string {
|
"fcm_service_account": func() interface{} {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
"serviceAccountJson": os.Getenv("FCM_SERVICE_ACCOUNT_JSON"),
|
"serviceAccountJson": os.Getenv("FCM_SERVICE_ACCOUNT_JSON"),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"smtp": func() map[string]string {
|
"smtp": func() interface{} {
|
||||||
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"),
|
||||||
|
|
@ -322,17 +321,6 @@ 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)
|
||||||
|
|
@ -343,27 +331,19 @@ 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
|
||||||
for _, v := range data {
|
jsonBytes, _ := json.Marshal(data)
|
||||||
if strings.TrimSpace(v) != "" {
|
var stringMap map[string]string
|
||||||
|
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 {
|
||||||
|
|
@ -381,35 +361,6 @@ 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)`
|
||||||
|
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
# 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,8 +15,6 @@ 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,6 +49,7 @@ 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
|
||||||
|
|
||||||
|
|
@ -62,9 +63,6 @@ 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)
|
||||||
|
|
@ -117,23 +115,3 @@ 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
|
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
# 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