From 7885cede659f3255be56f77c1eef2ada7387d6f1 Mon Sep 17 00:00:00 2001
From: chenyc <501753378@qq.com>
Date: 星期日, 22 三月 2026 16:23:21 +0800
Subject: [PATCH] 初始化项目
---
src/httpServer.js | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 151 insertions(+), 0 deletions(-)
diff --git a/src/httpServer.js b/src/httpServer.js
new file mode 100644
index 0000000..636923e
--- /dev/null
+++ b/src/httpServer.js
@@ -0,0 +1,151 @@
+const express = require("express");
+const config = require("./config");
+const logger = require("./logger");
+
+function createCorsMiddleware() {
+ return (req, res, next) => {
+ if (!config.http.cors || !config.http.cors.enabled) {
+ return next();
+ }
+ const origin = req.headers.origin || "*";
+ const allowed =
+ config.http.cors.origins.includes("*") ||
+ config.http.cors.origins.includes(origin);
+
+ if (allowed) {
+ res.header("Access-Control-Allow-Origin", origin);
+ res.header("Access-Control-Allow-Methods", "GET,POST,OPTIONS");
+ res.header("Access-Control-Allow-Headers", "Content-Type");
+ }
+
+ if (req.method === "OPTIONS") {
+ return res.sendStatus(204);
+ }
+
+ next();
+ };
+}
+
+function createHttpServer({ dataCache, rateLimiter, propertyMapper, deviceManager }) {
+ if (!config.http || !config.http.enabled) {
+ logger.info("HTTP server is disabled by config");
+ return null;
+ }
+
+ const app = express();
+ app.use(express.json({ limit: "1mb" }));
+ app.use(createCorsMiddleware());
+
+ app.get("/api/device/data", (req, res) => {
+ const deviceNumber = req.query.deviceNumber;
+ const mapped = String(req.query.mapped || "false").toLowerCase() === "true";
+
+ if (!deviceNumber) {
+ return res.status(400).json({ error: "deviceNumber is required" });
+ }
+
+ const limit = rateLimiter.checkLimit(
+ `device:${deviceNumber}`,
+ config.http.rateLimit.singleDeviceMs
+ );
+ if (!limit.allowed) {
+ return res.status(429).json({
+ error: "too many requests",
+ waitMs: limit.waitMs
+ });
+ }
+
+ const entry = dataCache.getDeviceData(deviceNumber);
+ if (!entry) {
+ return res.status(404).json({ error: "device not found" });
+ }
+
+ const base = mapped
+ ? propertyMapper.transformForHTTP(entry.data, deviceNumber)
+ : { deviceNumber, data: entry.data };
+
+ return res.json({ ...base, updatedAt: entry.updatedAt });
+ });
+
+ app.get("/api/device/all", (req, res) => {
+ const mapped = String(req.query.mapped || "false").toLowerCase() === "true";
+
+ const limit = rateLimiter.checkLimit(
+ "__all_devices__",
+ config.http.rateLimit.allDevicesMs
+ );
+ if (!limit.allowed) {
+ return res.status(429).json({
+ error: "too many requests",
+ waitMs: limit.waitMs
+ });
+ }
+
+ const all = dataCache.getAllDeviceData();
+ const result = {};
+
+ for (const [deviceNumber, entry] of Object.entries(all)) {
+ if (mapped) {
+ result[deviceNumber] = {
+ ...propertyMapper.transformForHTTP(entry.data, deviceNumber),
+ updatedAt: entry.updatedAt
+ };
+ } else {
+ result[deviceNumber] = {
+ deviceNumber,
+ data: entry.data,
+ updatedAt: entry.updatedAt
+ };
+ }
+ }
+
+ return res.json(result);
+ });
+
+ app.get("/api/device/list", (req, res) => {
+ return res.json(dataCache.getDeviceList());
+ });
+
+ app.get("/api/cache/stats", (req, res) => {
+ return res.json(dataCache.getStats());
+ });
+
+ app.post("/api/cache/clear", (req, res) => {
+ dataCache.clear();
+ return res.json({ ok: true });
+ });
+
+ app.get("/api/device/idle", (req, res) => {
+ const timeout = parseInt(req.query.timeout, 10);
+ const timeoutMs = Number.isFinite(timeout) && timeout > 0 ? timeout : 5 * 60 * 1000;
+ return res.json(dataCache.getIdleDevices(timeoutMs));
+ });
+
+ app.get("/api/ratelimit/stats", (req, res) => {
+ return res.json(rateLimiter.getStats());
+ });
+
+ app.post("/api/ratelimit/clear", (req, res) => {
+ rateLimiter.clear();
+ return res.json({ ok: true });
+ });
+
+ app.get("/api/health", (req, res) => {
+ res.json({
+ status: "ok",
+ tcp: "listening",
+ devices: deviceManager ? deviceManager.getStats() : []
+ });
+ });
+
+ const server = app.listen(config.http.port, config.http.host, () => {
+ logger.info("HTTP server listening", {
+ host: config.http.host,
+ port: config.http.port
+ });
+ });
+
+ return server;
+}
+
+module.exports = createHttpServer;
--
Gitblit v1.8.0