| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104 |
- import dotenv from "dotenv";
- import fs from "node:fs";
- dotenv.config();
- function resolveDbSsl() {
- const caPath = process.env.DB_SSL_CA;
- const rejectUnauthorizedEnv = process.env.DB_SSL_REJECT_UNAUTHORIZED;
- if (!caPath && rejectUnauthorizedEnv === undefined) return undefined;
- return {
- rejectUnauthorized: rejectUnauthorizedEnv !== "false",
- ...(caPath ? { ca: fs.readFileSync(caPath, "utf8") } : {})
- };
- }
- export const config = {
- port: Number(process.env.PORT ?? 3001),
- corsOrigin: process.env.CORS_ORIGIN ?? "http://localhost:5173",
- apiKey: process.env.API_KEY ?? "",
- auth: {
- mode: process.env.AUTH_MODE ?? "none"
- },
- db: {
- host: process.env.DB_HOST,
- user: process.env.DB_USER,
- port: process.env.DB_PORT ? Number(process.env.DB_PORT) : undefined,
- password: process.env.DB_PASS,
- schema: process.env.DB_SCHEMA,
- charset: "utf8mb4",
- ssl: resolveDbSsl()
- },
- jwt: {
- secret: process.env.JWT_SECRET ?? "",
- issuer: process.env.JWT_ISSUER ?? "oraculo-api",
- accessTtlSeconds: Number(process.env.JWT_ACCESS_TTL_SECONDS ?? 900),
- refreshTtlSeconds: Number(process.env.JWT_REFRESH_TTL_SECONDS ?? 60 * 60 * 24 * 30),
- refreshTtlShortSeconds: Number(process.env.JWT_REFRESH_TTL_SHORT_SECONDS ?? 60 * 60 * 24)
- },
- qdrant: {
- url: process.env.QDRANT_URL ?? "http://localhost:6333",
- apiKey: process.env.QDRANT_API_KEY ?? "",
- collection: process.env.QDRANT_COLLECTION ?? "oraculo_docs"
- },
- ollama: {
- url: process.env.OLLAMA_URL ?? "http://localhost:11434",
- embeddingsModel: process.env.OLLAMA_EMBEDDINGS_MODEL ?? "nomic-embed-text",
- embedQueryPrefix: process.env.OLLAMA_EMBED_QUERY_PREFIX ?? "",
- embedPassagePrefix: process.env.OLLAMA_EMBED_PASSAGE_PREFIX ?? "",
- chatModel: process.env.OLLAMA_CHAT_MODEL ?? "llama3.1",
- visionModel: process.env.OLLAMA_VISION_MODEL ?? "llava:latest",
- timeoutMs: Number(process.env.OLLAMA_TIMEOUT_MS ?? 180_000)
- },
- rag: {
- topK: Number(process.env.RAG_TOP_K ?? 8),
- minScore: Number(process.env.RAG_MIN_SCORE ?? 0.40),
- chunkSize: Number(process.env.RAG_CHUNK_SIZE ?? 900),
- chunkOverlap: Number(process.env.RAG_CHUNK_OVERLAP ?? 150),
- queryRewrite: process.env.RAG_QUERY_REWRITE === "true",
- historyLimit: Number(process.env.RAG_HISTORY_LIMIT ?? 6),
- rerank: process.env.RAG_RERANK === "true",
- rerankTopN: Number(process.env.RAG_RERANK_TOP_N ?? process.env.RAG_TOP_K ?? 8),
- hyde: process.env.RAG_HYDE === "true"
- },
- llm: {
- temperature: Number(process.env.LLM_TEMPERATURE ?? 0.2),
- topP: Number(process.env.LLM_TOP_P ?? 0.9),
- numPredict: Number(process.env.LLM_NUM_PREDICT ?? 4096),
- repeatPenalty: Number(process.env.LLM_REPEAT_PENALTY ?? 1.1),
- numCtx: Number(process.env.LLM_NUM_CTX ?? 32768)
- },
- rateLimit: {
- perMinute: Number(process.env.RATE_LIMIT_PER_MINUTE ?? 0)
- },
- ifbot: {
- baseUrl: process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api",
- // arquivos de mídia (/data/img, /data/aud) são servidos na raiz do domínio, fora do /api
- mediaBaseUrl: (process.env.IFBOT_MEDIA_BASE_URL ?? (process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api").replace(/\/api\/?$/, "")).replace(/\/+$/, ""),
- token: process.env.IFBOT_TOKEN ?? "",
- authHeader: process.env.IFBOT_AUTH_HEADER ?? "Authorization",
- syncIntervalMinutes: Number(process.env.ATENDIMENTOS_SYNC_INTERVAL_MINUTES ?? 30)
- },
- avaliacao: {
- // modelo usado como juiz; vazio usa o chatModel do Ollama
- model: process.env.OLLAMA_JUDGE_MODEL ?? "",
- intervalMinutes: Number(process.env.ATENDIMENTOS_AVALIACAO_INTERVAL_MINUTES ?? 60),
- batchSize: Number(process.env.ATENDIMENTOS_AVALIACAO_BATCH ?? 10),
- maxChars: Number(process.env.ATENDIMENTOS_AVALIACAO_MAX_CHARS ?? 12000)
- }
- };
- function validateConfig(cfg) {
- const dbRequired = { host: "DB_HOST", user: "DB_USER", password: "DB_PASS", schema: "DB_SCHEMA" };
- for (const [key, envName] of Object.entries(dbRequired)) {
- if (!cfg.db[key]) throw new Error(`[config] ${envName} é obrigatório`);
- }
- if (cfg.auth.mode === "jwt" && !cfg.jwt.secret) {
- throw new Error("[config] JWT_SECRET não pode ser vazio quando AUTH_MODE=jwt");
- }
- if (cfg.rateLimit.perMinute === 0) {
- console.warn("[config] RATE_LIMIT_PER_MINUTE=0 — rate limiting desabilitado");
- }
- }
- validateConfig(config);
|