Skip to content

Commit 026c6aa

Browse files
committed
Migra motor de scraping do Node.js para Go
- migra toda a pipeline de scraping do backend Node.js para o serviço scraper-go - centraliza gerenciamento de cache dentro do serviço Go - implementa providers de cache em memória e Redis no Go - adiciona factory de cache com fallback automático para Redis - implementa helpers genéricos de cache usando generics do Go - adiciona deduplicação de requisições com singleflight - migra lógica de searchJobsWithCache para pipeline em Go - migra orquestração do scrapeAllSources para Go - adiciona normalização de cache keys e geração determinística de chaves - implementa normalização e persistência de keywords no Go - adiciona suporte a armazenamento de keywords via Redis - adiciona loader JSON para keywords padrão - cria pacote keywords com separação entre store/defaults/normalize - remove responsabilidade de scraping do backend Node.js - transforma backend Node.js em camada de API/auth/adapters - mantém compatibilidade com frontend usando adapter pattern - mantém endpoints de keywords no Node delegando persistência ao Go - melhora separação entre API de negócio e engine de scraping - melhora escalabilidade para scraping concorrente - melhora limites arquiteturais entre backend e scraper - reduz responsabilidade de processamento e memória do Node.js - prepara infraestrutura para escalar scrapers distribuídos - reorganiza módulos internos do scraper-go: - adapters - cache - dedup - inflight - keywords - pipeline - adiciona sistema de registry para adapters - melhora integração Docker do serviço scraper-go - padroniza logs e relatórios de execução - remove responsabilidades obsoletas de cache do backend Node - simplifica responsabilidades do backend para: - autenticação - módulos de usuário/domínio - gateway de API - integração com frontend - swagger/documentação Evolução arquitetural: o backend Node.js agora atua como camada de aplicação/API, enquanto o Go se tornou a engine dedicada de scraping de alta performance."
1 parent f5dd9f3 commit 026c6aa

318 files changed

Lines changed: 89887 additions & 4419 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cspell/custom-dictionary-workspace.txt

Lines changed: 292 additions & 0 deletions
Large diffs are not rendered by default.

.dockerignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Node
2+
node_modules
3+
**/node_modules
4+
5+
# Build outputs
6+
frontend/dist
7+
backend/output
8+
9+
# Go
10+
scraper-go/vendor
11+
12+
# Electron
13+
electron
14+
15+
# Salesforce
16+
.sfdx
17+
18+
# Misc
19+
.git
20+
coverage
21+
**/*.log

backend/Dockerfile

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@ FROM node:24-alpine
22

33
WORKDIR /app
44

5+
# Copia os package.json da raiz e do backend
56
COPY package*.json ./
6-
RUN npm ci --omit=dev
7+
COPY backend/package*.json ./backend/
78

8-
COPY . .
9-
RUN mkdir -p /app/output
9+
# Instala tudo com workspaces (tsx vem da raiz)
10+
RUN npm ci --workspace=backend --include-workspace-root
1011

11-
CMD ["node", "index.js"]
12+
# Copia o código do backend
13+
COPY backend/ ./backend/
14+
15+
RUN mkdir -p /app/backend/output
16+
17+
EXPOSE 3001
18+
19+
# Roda a partir da pasta do workspace
20+
WORKDIR /app/backend
21+
CMD ["npx", "tsx", "src/server.ts"]

backend/drizzle.config.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,3 @@ export default {
77
url: process.env.DATABASE_URL,
88
},
99
};
10-
11-
// import { defineConfig } from "drizzle-kit";
12-
13-
// export default defineConfig({
14-
// schema: "./src/db/schema/index.js",
15-
// out: "./src/db/migrations",
16-
// dialect: "postgresql",
17-
// dbCredentials: {
18-
// url: process.env.DATABASE_URL,
19-
// },
20-
// });

backend/package.json

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22
"name": "backend",
33
"version": "1.0.0",
44
"description": "Backend para scraping e API de vagas do LinkedIn",
5-
"main": "index.js",
5+
"main": "index.ts",
66
"type": "module",
77
"scripts": {
8-
"start": "node src/server.js",
9-
"dev": "node src/server.js",
10-
"scraper": "node index.js",
11-
"scraper:watch": "nodemon index.js",
12-
"api": "node src/server.js",
8+
"start": "tsx src/server.ts",
9+
"dev": "tsx src/server.ts",
10+
"scraper": "tsx index.ts",
11+
"scraper:watch": "nodemon index.ts",
12+
"api": "tsx src/server.ts",
1313
"test": "vitest run",
1414
"test:coverage": "vitest run --coverage",
1515
"test:watch": "vitest --watch",
1616
"validate": "npm test",
1717
"db:generate": "drizzle-kit generate",
1818
"db:migrate": "drizzle-kit migrate",
19-
"db:push": "drizzle-kit push"
19+
"db:push": "drizzle-kit push",
20+
"clear-cache": "tsx src/cache/clearCache.ts"
2021
},
2122
"keywords": [
2223
"linkedin",
@@ -43,7 +44,8 @@
4344
"redis": "^4.7.1",
4445
"swagger-jsdoc": "^6.2.8",
4546
"swagger-ui-express": "^5.0.1",
46-
"xlsx": "^0.18.5"
47+
"xlsx": "^0.18.5",
48+
"zod": "^4.4.3"
4749
},
4850
"engines": {
4951
"node": ">=22.0.0"

backend/src/adapters/adzuna.js

Lines changed: 0 additions & 84 deletions
This file was deleted.

backend/src/adapters/goKeywords.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { logWarn } from "../logger";
2+
3+
const GO_BASE_URL =
4+
process.env.GO_SCRAPER_URL?.trim() || "http://scraper-go:8080";
5+
6+
type Keyword = string;
7+
8+
export function normalizeKeywords(keywords: unknown): Keyword[] | null {
9+
if (!Array.isArray(keywords)) {
10+
return null;
11+
}
12+
13+
return [
14+
...new Set(
15+
keywords.map((item) => String(item ?? "").trim()).filter(Boolean),
16+
),
17+
];
18+
}
19+
20+
export async function loadKeywords(
21+
fallback: Keyword[] = [],
22+
): Promise<Keyword[]> {
23+
try {
24+
const response = await fetch(`${GO_BASE_URL}/api/keywords`);
25+
26+
if (!response.ok) {
27+
return fallback;
28+
}
29+
30+
const data = await response.json();
31+
32+
return normalizeKeywords(data.keywords) ?? fallback;
33+
} catch (error) {
34+
logWarn("Erro ao carregar keywords do Go", {
35+
error: (error as Error).message,
36+
});
37+
38+
return fallback;
39+
}
40+
}
41+
42+
export async function saveKeywords(
43+
keywords: unknown,
44+
): Promise<Keyword[] | null> {
45+
const normalized = normalizeKeywords(keywords);
46+
47+
if (normalized === null) {
48+
return null;
49+
}
50+
51+
const response = await fetch(`${GO_BASE_URL}/api/keywords`, {
52+
method: "POST",
53+
headers: {
54+
"Content-Type": "application/json",
55+
},
56+
body: JSON.stringify({
57+
keywords: normalized,
58+
}),
59+
});
60+
61+
if (!response.ok) {
62+
throw new Error(`Erro ao salvar keywords no Go`);
63+
}
64+
65+
const data = await response.json();
66+
67+
return normalizeKeywords(data.keywords);
68+
}

backend/src/adapters/goScraper.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { logWarn } from "../logger";
2+
import type { ScrapeParams, ScrapeResponse } from "./types/scraper.types";
3+
import {
4+
ScrapeParamsSchema,
5+
ScrapeResponseSchema,
6+
} from "./types/scraper.types";
7+
8+
export type { ScrapeParams, ScrapeResponse };
9+
10+
const GO_SCRAPER_URL = process.env.GO_SCRAPER_URL ?? "http://localhost:8081";
11+
12+
export async function searchJobs(
13+
params: ScrapeParams,
14+
): Promise<ScrapeResponse> {
15+
const validatedParams = ScrapeParamsSchema.parse(params);
16+
17+
const res = await fetch(`${GO_SCRAPER_URL}/scrape`, {
18+
method: "POST",
19+
headers: { "Content-Type": "application/json" },
20+
body: JSON.stringify(validatedParams),
21+
});
22+
23+
if (!res.ok) {
24+
throw new Error(`Go scraper: ${res.status} ${res.statusText}`);
25+
}
26+
27+
const raw = await res.json();
28+
const parsed = ScrapeResponseSchema.safeParse(raw);
29+
30+
if (!parsed.success) {
31+
logWarn("Go scraper: resposta fora do contrato", {
32+
error: parsed.error.message,
33+
});
34+
throw new Error("Go scraper: resposta invalida");
35+
}
36+
37+
return parsed.data;
38+
}

backend/src/adapters/greenhouse.js

Lines changed: 0 additions & 33 deletions
This file was deleted.

backend/src/adapters/lever.js

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)