diff --git a/.env.development b/.env.development index 9fd2d99..705b647 100644 --- a/.env.development +++ b/.env.development @@ -55,7 +55,8 @@ REDIS_PASSWORD=wacefems-redis-password-PPw09!keep # MQTT Broker MQTT_BROKER_URL=mqtt://fems-mqtt:1883 -MQTT_PORT=1884 +MQTT_WSS_URL=ws://fems-mqtt:8883 +MQTT_PORT=1883 MQTT_WSS_PORT=8883 MQTT_USERNAME=fems MQTT_PASSWORD=fems123! diff --git a/docker-compose.db.yml b/docker-compose.db.yml index c9b2015..af8de80 100644 --- a/docker-compose.db.yml +++ b/docker-compose.db.yml @@ -91,22 +91,27 @@ services: timeout: 5s retries: 3 - # fems-mqtt: - # build: - # context: ./docker/mqtt - # dockerfile: Dockerfile - # container_name: fems-mqtt - # env_file: - # - .env.${NODE_ENV:-development} - # ports: - # - "${MQTT_PORT}:1883" - # - "${MQTT_WSS_PORT}:8883" - # volumes: - # - mqtt_data:/mosquitto/data - # - mqtt_log:/mosquitto/log - # environment: - # - NODE_ENV=${NODE_ENV:-development} - # restart: unless-stopped + fems-mqtt: + image: eclipse-mosquitto:latest + container_name: fems-mqtt + restart: unless-stopped + ports: + - "${MQTT_PORT}:1883" # MQTT + - "${MQTT_WSS_PORT}:8883" # MQTT over WebSocket + volumes: + - ./fems-mqtt/config:/mosquitto/config + - ./fems-mqtt/data:/mosquitto/data + - ./fems-mqtt/log:/mosquitto/log + - ./fems-mqtt/certs:/mosquitto/certs + environment: + - TZ=Asia/Seoul + networks: + - fems-network + healthcheck: + test: ["CMD", "/usr/sbin/mosquitto_sub", "-t", "$$SYS/#", "-C", "1"] + interval: 30s + timeout: 10s + retries: 3 volumes: fems_postgres: diff --git a/fems-api/src/controllers/admin/companies/companies.controller.js b/fems-api/src/controllers/admin/companies/companies.controller.js index 818aabb..5375904 100644 --- a/fems-api/src/controllers/admin/companies/companies.controller.js +++ b/fems-api/src/controllers/admin/companies/companies.controller.js @@ -1,8 +1,7 @@ // src/controllers/admin/companies/companies.controller.js -// src/controllers/admin/companies/companies.controller.js const express = require("express"); const router = express.Router(); -const companyService = require("../../../services/company.service"); +const companyService = require("../../../services/companies.service"); const authMiddleware = require("../../../middleware/auth.middleware"); const roleCheck = require("../../../middleware/roleCheck.middleware"); const { body, param } = require("express-validator"); @@ -117,4 +116,31 @@ router.delete( } ); +// Edge 서버용 회사/지점 정보 조회 +router.get("/info", authMiddleware, async (req, res, next) => { + try { + // 구독 상태 확인 (이제 branchId와 branchName 포함) + const subscriptionStatus = await companyService.checkSubscriptionStatus( + req.user.companyId + ); + + if (!subscriptionStatus.isValid) { + return res.status(403).json({ + message: subscriptionStatus.reason, + }); + } + + res.json({ + companyId: subscriptionStatus.companyId, + companyName: subscriptionStatus.companyName, + businessNumber: subscriptionStatus.businessNumber, + branchId: subscriptionStatus.branchId, + branchName: subscriptionStatus.branchName, + contractInfo: subscriptionStatus.contractInfo, + }); + } catch (error) { + next(error); + } +}); + module.exports = router; diff --git a/fems-api/src/routes/app.js b/fems-api/src/routes/app.js index aeab15e..8407ccb 100644 --- a/fems-api/src/routes/app.js +++ b/fems-api/src/routes/app.js @@ -14,6 +14,7 @@ const partsController = require("../controllers/app/parts/parts.controller"); const equipmentPartsController = require("../controllers/app/equipmentParts/equipmentParts.controller"); // 추가 const departmentController = require("../controllers/app/department/department.controller"); const healthController = require("../controllers/app/health/health.controller"); +const companiesController = require("../controllers/admin/companies/companies.controller"); router.use("/health", healthController); router.use("/auth", authController); @@ -27,5 +28,6 @@ router.use("/personnel", personnelController); router.use("/parts", partsController); router.use("/equipment-parts", equipmentPartsController); router.use("/department", departmentController); +router.use("/companies", companiesController); module.exports = router; diff --git a/fems-api/src/services/company.service.js b/fems-api/src/services/companies.service.js similarity index 55% rename from fems-api/src/services/company.service.js rename to fems-api/src/services/companies.service.js index a0f1aad..ef2e5e0 100644 --- a/fems-api/src/services/company.service.js +++ b/fems-api/src/services/companies.service.js @@ -1,4 +1,4 @@ -// src/services/company.service.js +// src/services/companies.service.js const { Company, Branch, User, sequelize } = require("../models"); const apiKeyService = require("./apiKey.service"); const alertService = require("./alert.service"); @@ -63,15 +63,43 @@ class CompanyService { } } + // async findById(id) { + // return await Company.findByPk(id, { + // include: [ + // { model: Branch }, + // { + // model: User, + // attributes: { exclude: ["password"] }, + // }, + // ], + // }); + // } + async findById(id) { return await Company.findByPk(id, { include: [ - { model: Branch }, + { + model: Branch, + where: { isActive: true }, + required: false, + }, { model: User, attributes: { exclude: ["password"] }, }, ], + attributes: { + include: [ + "id", + "name", + "businessNumber", + "isActive", + "contractStartDate", + "contractEndDate", + "createdAt", + "updatedAt", + ], + }, }); } @@ -111,6 +139,68 @@ class CompanyService { await company.destroy(); return true; } + + // 회사의 구독 상태 확인 + async checkSubscriptionStatus(companyId) { + try { + const { company, activeBranch } = await this.validateCompanyStatus( + companyId + ); + const now = new Date(); + + return { + isValid: true, + companyId: company.id, + companyName: company.name, + businessNumber: company.businessNumber, + branchId: activeBranch.id, // activeBranch 활용 + branchName: activeBranch.name, // activeBranch 활용 + contractInfo: { + startDate: company.contractStartDate, + endDate: company.contractEndDate, + remainingDays: Math.ceil( + (new Date(company.contractEndDate) - now) / (1000 * 60 * 60 * 24) + ), + status: + now < new Date(company.contractEndDate) ? "active" : "expired", + }, + }; + } catch (error) { + return { + isValid: false, + reason: error.message, + }; + } + } + + async validateCompanyStatus(companyId) { + const company = await this.findById(companyId); + + if (!company) { + throw new Error("Company not found"); + } + + if (!company.isActive) { + throw new Error("Company is not active"); + } + + const now = new Date(); + if (company.contractEndDate && new Date(company.contractEndDate) < now) { + throw new Error("Company contract has expired"); + } + + // 활성 상태인 지점 찾기 + const activeBranch = company.Branches?.find((branch) => branch.isActive); + if (!activeBranch) { + throw new Error("No active branch found"); + } + + return { + valid: true, + company, + activeBranch, // 첫 번째 활성 지점 반환 + }; + } } module.exports = new CompanyService();