"use strict";
// oms/src/event/app.ts
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
// Load environment variables (e.g., RABBITMQ_URL, RABBITMQ_EXCHANGE, RABBITMQ_LOG_QUEUE, RABBITMQ_OMS_QUEUE, PORT)
const dotenv = __importStar(require("dotenv"));
dotenv.config();
const express_1 = __importDefault(require("express"));
const amqplib_1 = __importDefault(require("amqplib")); // Import amqplib for RabbitMQ interaction
const morgan_1 = __importDefault(require("morgan")); // 👈 新增：导入 morgan 库
const mongoose_1 = __importDefault(require("mongoose")); // 👈 导入 mongoose 用于生成 ObjectId
const app = (0, express_1.default)();
const port = process.env.EVENT_PORT ? parseInt(process.env.EVENT_PORT, 10) : 3001; // Event API 监听端口，默认为 3001
// RabbitMQ Configuration
const RABBITMQ_URL = process.env.RABBITMQ_URL || "amqp://coloring:coloring123.@localhost:5672";
const RABBITMQ_EXCHANGE_NAME = process.env.RABBITMQ_EXCHANGE || "event_exchange"; // <-- 从环境变量读取交换机名称
const RABBITMQ_LOG_QUEUE = process.env.RABBITMQ_LOG_QUEUE || "log-event-queue"; // <-- 从环境变量读取日志队列名称
const RABBITMQ_OMS_QUEUE = process.env.RABBITMQ_OMS_QUEUE || "oms_event_queue"; // <-- 从环境变量读取摄取器队列名称
let amqpConnection;
let amqpChannel;
// --- Initialize RabbitMQ Connection and Channel ---
async function connectRabbitMQ() {
    try {
        amqpConnection = await amqplib_1.default.connect(RABBITMQ_URL);
        amqpConnection.on("error", (err) => {
            console.error("[RabbitMQ] Connection error:", err);
            // Implement reconnection logic here for production
            setTimeout(connectRabbitMQ, 5000); // Attempt to reconnect after 5 seconds
        });
        amqpConnection.on("close", () => {
            console.error("[RabbitMQ] Connection closed. Reconnecting...");
            setTimeout(connectRabbitMQ, 5000); // Attempt to reconnect after 5 seconds
        });
        amqpChannel = await amqpConnection.createChannel();
        console.log("[RabbitMQ] Channel created.");
        // Assert the exchange for persistent broadcast
        // 'fanout' exchange will broadcast messages to all bound queues.
        await amqpChannel.assertExchange(RABBITMQ_EXCHANGE_NAME, "fanout", {
            durable: true,
        });
        console.log(`[RabbitMQ] Exchange '${RABBITMQ_EXCHANGE_NAME}' asserted.`);
        // Assert and bind the Log Queue
        await amqpChannel.assertQueue(RABBITMQ_LOG_QUEUE, { durable: true });
        console.log(`[RabbitMQ] Queue '${RABBITMQ_LOG_QUEUE}' asserted.`);
        await amqpChannel.bindQueue(RABBITMQ_LOG_QUEUE, RABBITMQ_EXCHANGE_NAME, ""); // Routing key is ignored for fanout
        console.log(`[RabbitMQ] Queue '${RABBITMQ_LOG_QUEUE}' bound to exchange '${RABBITMQ_EXCHANGE_NAME}'.`);
        // Assert and bind the Ingestor Queue
        await amqpChannel.assertQueue(RABBITMQ_OMS_QUEUE, { durable: true });
        console.log(`[RabbitMQ] Queue '${RABBITMQ_OMS_QUEUE}' asserted.`);
        await amqpChannel.bindQueue(RABBITMQ_OMS_QUEUE, RABBITMQ_EXCHANGE_NAME, ""); // Routing key is ignored for fanout
        console.log(`[RabbitMQ] Queue '${RABBITMQ_OMS_QUEUE}' bound to exchange '${RABBITMQ_EXCHANGE_NAME}'.`);
        console.log(`Event Producer connected to RabbitMQ: ${RABBITMQ_URL}`);
    }
    catch (error) {
        console.error("[RabbitMQ] Failed to connect or setup RabbitMQ:", error);
        // Retry connection
        setTimeout(connectRabbitMQ, 5000);
    }
}
// --- Middleware ---
// 服务部署在反向代理（如 Nginx）后面，需设置此项以正确获取客户端 IP
app.set("trust proxy", true);
// 👈 新增：使用 morgan 中间件来记录所有请求
// :remote-addr 和 :req[host] 会通过 app.set("trust proxy", true) 正常工作
app.use((0, morgan_1.default)('[:date[clf]] :remote-addr :req[host] :status :response-time ms :res[content-length] ":method :url HTTP/:http-version" ":referrer" ":user-agent"'));
app.use(express_1.default.json()); // To parse JSON request bodies
// 提取出的处理函数
const eventHandler = async (req, res) => {
    if (!amqpChannel) {
        console.error("[Event API] RabbitMQ channel not available.");
        return res.status(500).json({ message: "Server is not ready to process events." });
    }
    const eventData = req.body;
    if (!eventData || Object.keys(eventData).length === 0) {
        return res.status(400).json({ message: "Event data is required." });
    }
    // 添加必要字段
    eventData.ip = req.ip;
    eventData.t = new Date();
    eventData.cc = req.header("x-country-code") || "nil";
    eventData._id = new mongoose_1.default.Types.ObjectId();
    try {
        const message = JSON.stringify(eventData);
        // Publish the message to the exchange for broadcast
        // persistent: true ensures the message survives RabbitMQ restarts
        // '' as routing key for fanout exchange means it goes to all bound queues
        const published = amqpChannel.publish(RABBITMQ_EXCHANGE_NAME, "", // Routing key (ignored by fanout exchange)
        Buffer.from(message), { persistent: true } // Mark message as persistent
        );
        if (published) {
            // console.log('[Event API] Event published to RabbitMQ:', eventData); // Commented for high volume
            res.status(200).json({ msg: "ok" });
        }
        else {
            // This case indicates the RabbitMQ buffer is full.
            // In a real-world scenario, you might want to implement backpressure or retry.
            console.warn("[Event API] Failed to publish event to RabbitMQ (channel full). Event:", eventData);
            res.status(503).json({ message: "Service temporarily unavailable, please retry." });
        }
    }
    catch (error) {
        console.error("[Event API] Error publishing event to RabbitMQ:", error);
        res.status(500).json({ message: "Failed to process event." });
    }
};
app.post("/napi/event/v2", eventHandler);
app.post("/napi/event/", eventHandler);
// --- Start the Express Server ---
async function startServer() {
    await connectRabbitMQ(); // Connect to RabbitMQ before starting the server
    app
        .listen(port, () => {
        console.log(`Event Producer API listening on port ${port}`);
        console.log(`Event endpoint: /napi/event/v2`);
    })
        .on("error", (err) => {
        if (err.code === "EADDRINUSE") {
            console.error(`Port ${port} is already in use.`);
            process.exit(1);
        }
        else {
            console.error(`Failed to start Event Producer API:`, err);
            process.exit(1);
        }
    });
}
// Start the Event API server
startServer().catch(console.error);
// Handle graceful shutdown
process.on("SIGINT", async () => {
    console.log("[Event API] Shutting down...");
    if (amqpChannel)
        await amqpChannel.close();
    if (amqpConnection)
        await amqpConnection.close();
    process.exit(0);
});
process.on("SIGTERM", async () => {
    console.log("[Event API] Shutting down...");
    if (amqpChannel)
        await amqpChannel.close();
    if (amqpConnection)
        await amqpConnection.close();
    process.exit(0);
});
