// Task Team — API Server — 2026-03-29 require("dotenv").config(); const Fastify = require("fastify"); const cors = require("@fastify/cors"); const jwt = require("@fastify/jwt"); const rateLimit = require("@fastify/rate-limit"); const { Pool } = require("pg"); const Redis = require("ioredis"); // Database pool const pool = new Pool({ connectionString: process.env.DATABASE_URL || "postgresql://taskteam:TaskTeam2026!@10.10.10.10:5432/taskteam" }); // Redis client const redis = new Redis(process.env.REDIS_URL || "redis://:Redis2026!@10.10.10.10:6379"); redis.on("connect", () => { console.log("Redis connected"); }); redis.on("error", (err) => { console.error("Redis error: " + err.message); }); // Graceful shutdown const shutdown = async (signal) => { console.log(`Received ${signal}, shutting down gracefully...`); await redis.quit(); await pool.end(); process.exit(0); }; process.on("SIGINT", () => shutdown("SIGINT")); process.on("SIGTERM", () => shutdown("SIGTERM")); // Start const start = async () => { const app = Fastify({ logger: true }); // Decorate with db and redis app.decorate("db", pool); app.decorate("redis", redis); // Plugins (must await for Fastify 5 compatibility) await app.register(cors, { origin: true }); await app.register(rateLimit, { max: 100, timeWindow: "1 minute", keyGenerator: (req) => req.ip, errorResponseBuilder: () => ({ error: "Too many requests", statusCode: 429 }) }); await app.register(jwt, { secret: process.env.JWT_SECRET || "taskteam-jwt-secret-2026" }); // Health check (excluded from rate limit) app.get("/health", { config: { rateLimit: false } }, async () => ({ status: "ok", timestamp: new Date().toISOString(), pid: process.pid, redis: redis.status })); // Register routes await app.register(require("./routes/tasks"), { prefix: "/api/v1" }); await app.register(require("./routes/groups"), { prefix: "/api/v1" }); await app.register(require("./routes/auth"), { prefix: "/api/v1" }); await app.register(require("./routes/connectors"), { prefix: "/api/v1" }); await app.register(require("./routes/connectors/odoo"), { prefix: "/api/v1" }); await app.register(require("./routes/connectors/moodle"), { prefix: "/api/v1" }); await app.register(require("./routes/connectors/pohoda"), { prefix: "/api/v1" }); await app.register(require("./routes/chat"), { prefix: "/api/v1" }); await app.register(require("./routes/notifications"), { prefix: "/api/v1" }); await app.register(require("./routes/goals"), { prefix: "/api/v1" }); await app.register(require("./routes/deploy"), { prefix: "/api/v1" }); await app.register(require("./routes/system"), { prefix: "/api/v1" }); try { await app.listen({ port: process.env.PORT || 3000, host: "0.0.0.0" }); console.log("Task Team API listening on port " + (process.env.PORT || 3000) + " (pid: " + process.pid + ")"); } catch (err) { console.error(err); process.exit(1); } }; start();