251125:0000 Phase 6 wait start dev Check
This commit is contained in:
2432
01_lcbp3_v1_4_3 copy.sql
Normal file
2432
01_lcbp3_v1_4_3 copy.sql
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
-- ==========================================================
|
||||
-- DMS v1.4.2 Document Management System Database
|
||||
-- DMS v1.4.3 Document Management System Database
|
||||
-- Deploy Script Schema
|
||||
-- Server: Container Station on QNAPQNAP TS-473A
|
||||
-- Database service: MariaDB 10.11
|
||||
@@ -9,8 +9,8 @@
|
||||
-- frontend sevice: next.js
|
||||
-- reverse proxy: jc21/nginx-proxy-manager:latest
|
||||
-- cron service: n8n
|
||||
-- DMS v1.4.2 Improvements
|
||||
-- Update: revise fron v1.4.1 Gemini)
|
||||
-- DMS v1.4.3 Improvements
|
||||
-- Update: revise fron v1.4.2 add PARTITION to audit_logs & notification
|
||||
-- ==========================================================
|
||||
SET NAMES utf8mb4;
|
||||
SET time_zone = '+07:00';
|
||||
@@ -1918,7 +1918,7 @@ CREATE TABLE IF NOT EXISTS user_preferences (
|
||||
-- รองรับ: Req 6.1
|
||||
-- เหตุผล: รองรับ Distributed Tracing และระบุความรุนแรง
|
||||
CREATE TABLE audit_logs (
|
||||
audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log',
|
||||
audit_id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID ของ Log',
|
||||
request_id VARCHAR(100) NULL COMMENT 'Trace ID linking to app logs',
|
||||
user_id INT COMMENT 'ผู้กระทำ',
|
||||
action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (
|
||||
@@ -1932,13 +1932,38 @@ CREATE TABLE audit_logs (
|
||||
details_json JSON COMMENT 'ข้อมูลบริบท',
|
||||
ip_address VARCHAR(45) COMMENT 'IP Address',
|
||||
user_agent VARCHAR(255) COMMENT 'User Agent',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ',
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE
|
||||
SET NULL
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บบันทึกการกระทำของผู้ใช้';
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ',
|
||||
-- [แก้ไข] รวม created_at เข้ามาใน Primary Key เพื่อรองรับ Partition
|
||||
PRIMARY KEY (audit_id, created_at),
|
||||
-- [แก้ไข] ใช้ Index ธรรมดาแทน Foreign Key เพื่อไม่ให้ติดข้อจำกัดของ Partition Table
|
||||
INDEX idx_audit_user (user_id),
|
||||
INDEX idx_audit_action (action),
|
||||
INDEX idx_audit_entity (entity_type, entity_id),
|
||||
INDEX idx_audit_created (created_at)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บบันทึกการกระทำของผู้ใช้' -- [เพิ่ม] คำสั่ง Partition
|
||||
PARTITION BY RANGE (YEAR(created_at)) (
|
||||
PARTITION p_old
|
||||
VALUES LESS THAN (2024),
|
||||
PARTITION p2024
|
||||
VALUES LESS THAN (2025),
|
||||
PARTITION p2025
|
||||
VALUES LESS THAN (2026),
|
||||
PARTITION p2026
|
||||
VALUES LESS THAN (2027),
|
||||
PARTITION p2027
|
||||
VALUES LESS THAN (2028),
|
||||
PARTITION p2028
|
||||
VALUES LESS THAN (2029),
|
||||
PARTITION p2029
|
||||
VALUES LESS THAN (2030),
|
||||
PARTITION p2030
|
||||
VALUES LESS THAN (2031),
|
||||
PARTITION p_future
|
||||
VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
-- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)
|
||||
CREATE TABLE notifications (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน',
|
||||
id INT NOT NULL AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน',
|
||||
user_id INT NOT NULL COMMENT 'ID ผู้ใช้',
|
||||
title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน',
|
||||
message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน',
|
||||
@@ -1947,9 +1972,35 @@ CREATE TABLE notifications (
|
||||
entity_type VARCHAR(50) COMMENT 'เช่น ''rfa '',
|
||||
''circulation ''',
|
||||
entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการการแจ้งเตือน (Email / Line / System)';
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
|
||||
-- [แก้ไข] รวม created_at เข้ามาใน Primary Key
|
||||
PRIMARY KEY (id, created_at),
|
||||
-- [แก้ไข] ใช้ Index ธรรมดาแทน Foreign Key
|
||||
INDEX idx_notif_user (user_id),
|
||||
INDEX idx_notif_type (notification_type),
|
||||
INDEX idx_notif_read (is_read),
|
||||
INDEX idx_notif_created (created_at)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการการแจ้งเตือน (Email / Line / System)' -- [เพิ่ม] คำสั่ง Partition
|
||||
PARTITION BY RANGE (YEAR(created_at)) (
|
||||
PARTITION p_old
|
||||
VALUES LESS THAN (2024),
|
||||
PARTITION p2024
|
||||
VALUES LESS THAN (2025),
|
||||
PARTITION p2025
|
||||
VALUES LESS THAN (2026),
|
||||
PARTITION p2026
|
||||
VALUES LESS THAN (2027),
|
||||
PARTITION p2027
|
||||
VALUES LESS THAN (2028),
|
||||
PARTITION p2028
|
||||
VALUES LESS THAN (2029),
|
||||
PARTITION p2029
|
||||
VALUES LESS THAN (2030),
|
||||
PARTITION p2030
|
||||
VALUES LESS THAN (2031),
|
||||
PARTITION p_future
|
||||
VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
-- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)
|
||||
CREATE TABLE search_indices (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี',
|
||||
|
||||
@@ -6,276 +6,279 @@
|
||||
|
||||
## 📂 **backend/** (Backend Application)
|
||||
|
||||
* [x] `.env` (สำหรับ Local Dev เท่านั้น ห้าม commit)
|
||||
* [x] `.gitignore`
|
||||
* [x] `.prettierrc`
|
||||
* [x] `docker-compose.override.yml`
|
||||
* [x] `docker-compose.yml`
|
||||
* [x] `nest-cli.json`
|
||||
* [x] `tsconfig.build.json`
|
||||
* [x] `tsconfig.json`
|
||||
* [x] `package.json`
|
||||
* [x] `pnpm-lock.yaml`
|
||||
* [x] `README.md`
|
||||
* [x] .env (สำหรับ Local Dev เท่านั้น ห้าม commit)
|
||||
* [x] .gitignore
|
||||
* [x] .prettierrc
|
||||
* [x] docker-compose.override.yml
|
||||
* [x] docker-compose.yml
|
||||
* [x] nest-cli.json
|
||||
* [x] tsconfig.build.json
|
||||
* [x] tsconfig.json
|
||||
* [x] package.json
|
||||
* [x] pnpm-lock.yaml
|
||||
* [x] README.md
|
||||
|
||||
---
|
||||
|
||||
## 📂 **backend/src/** (Source Code)
|
||||
|
||||
### **📄 Entry Points**
|
||||
### **📄 Entry Points
|
||||
|
||||
* [x] `main.ts` (Application Bootstrap, Swagger, Global Pipes)
|
||||
* [x] `app.module.ts` (Root Module ที่รวมทุก Modules เข้าด้วยกัน)
|
||||
* [x] `app.service.ts` (Root Application Service)
|
||||
* [x] `app.controller.ts` (Root Application Controller)
|
||||
* [x] `app.controller.spec.ts` (Root Application Controller Unit Tests)
|
||||
* [x] `redlock.d.ts` (Redlock Configuration)
|
||||
* [x] main.ts
|
||||
* [x] app.module.ts
|
||||
* [x] app.service.ts
|
||||
* [x] app.controller.ts
|
||||
* [x] app.controller.spec.ts
|
||||
* [x] redlock.d.ts
|
||||
|
||||
### **📁 src/common/** (Shared Resources)
|
||||
|
||||
* [x] **common.module.ts**
|
||||
* [x] common.module.ts
|
||||
* **auth/**
|
||||
* **dto/**
|
||||
* [x] **login.dto.ts**
|
||||
* [x] **register.dto.ts**
|
||||
* [x] login.dto.ts
|
||||
* [x] register.dto.ts
|
||||
* **strategies/**
|
||||
* [x] **local.strategy.ts**
|
||||
* [x] **jwt.strategy.ts**
|
||||
* [x] **auth.controller.spec.ts**
|
||||
* [x] **auth.controller.ts**
|
||||
* [x] **auth.module.ts**
|
||||
* [x] **auth.service.spec.ts**
|
||||
* [x] **auth.service.ts**
|
||||
* **config/** (Configuration Service)
|
||||
* [x] **env.validation.ts**
|
||||
* [x] **redis.config.ts**
|
||||
* [x] local.strategy.ts
|
||||
* [x] jwt.strategy.ts
|
||||
* [x] auth.controller.spec.ts
|
||||
* [x] auth.controller.ts
|
||||
* [x] auth.module.ts
|
||||
* [x] auth.service.spec.ts
|
||||
* [x] auth.service.ts
|
||||
* **config/**
|
||||
* [x] env.validation.ts
|
||||
* [x] redis.config.ts
|
||||
* **decorators/**
|
||||
* [x] **audit.decorator.ts**
|
||||
* [x] **bypass-maintenance.decorator.ts**
|
||||
* [x] `current-user.decorator.ts`
|
||||
* [x] **idempotency.decorator.ts**
|
||||
* [x] `require-permission.decorator.ts`
|
||||
* [x] **retry.decorator.ts**
|
||||
* [x] **circuit-breaker.decorator.ts**
|
||||
* [x] audit.decorator.ts
|
||||
* [x] bypass-maintenance.decorator.ts
|
||||
* [x] current-user.decorator.ts
|
||||
* [x] idempotency.decorator.ts
|
||||
* [x] require-permission.decorator.ts
|
||||
* [x] retry.decorator.ts
|
||||
* [x] circuit-breaker.decorator.ts
|
||||
* **entities/**
|
||||
* [x] **audit-log.entity.ts**
|
||||
* [x] **base.entity.ts**
|
||||
* [x] audit-log.entity.ts
|
||||
* [x] base.entity.ts
|
||||
* **exceptions/**
|
||||
* [x] `http-exception.filter.ts` (Global Filter)
|
||||
* **file-storage/** (Two-Phase Storage System)
|
||||
* [x] http-exception.filter.ts
|
||||
* **file-storage/**
|
||||
* **entities/**
|
||||
* [x] **attachment.entity.ts**
|
||||
* [x] **file-storage.controller.spec.ts**
|
||||
* [x] **file-storage.controller.ts**
|
||||
* [x] **file-storage.module.ts**
|
||||
* [x] **file-storage.service.spec.ts**
|
||||
* [x] `file-storage.service.ts` (Upload, Scan Virus, Commit)
|
||||
* [x] **file-cleanup.service.ts** (Cleanup Temporary Files)
|
||||
* [x] `guards/`
|
||||
* [x] `jwt-auth.guard.ts`
|
||||
* [x] **jwt-refresh.guard.ts**
|
||||
* [x] **maintenance-mode.guard.ts**
|
||||
* [x] `rbac.guard.ts` (ตรวจสอบสิทธิ์ 4 ระดับ)
|
||||
* [x] attachment.entity.ts
|
||||
* [x] file-storage.controller.spec.ts
|
||||
* [x] file-storage.controller.ts
|
||||
* [x] file-storage.module.ts
|
||||
* [x] file-storage.service.spec.ts
|
||||
* [x] file-storage.service.ts
|
||||
* [x] file-cleanup.service.ts
|
||||
* [x] guards/`
|
||||
* [x] jwt-auth.guard.ts
|
||||
* [x] jwt-refresh.guard.ts
|
||||
* [x] maintenance-mode.guard.ts
|
||||
* [x] rbac.guard.ts
|
||||
* **interceptors/**
|
||||
* [x] `audit-log.interceptor.ts` (เก็บ Log ลง DB)
|
||||
* [x] **idempotency.interceptor.ts** (Idempotency Interceptor)
|
||||
* [x] `transform.interceptor.ts` (Standard Response Format)
|
||||
* **resilience/** (Circuit Breaker & Retry)
|
||||
* [x] **resilience.module.ts** (Resilience Module)
|
||||
* **security/** (Security Service)
|
||||
* [x] **crypto.service.ts** (Crypto Service)
|
||||
* [x] **request-context.service.ts
|
||||
* [x] audit-log.interceptor.ts
|
||||
* [x] idempotency.interceptor.ts
|
||||
* [x] performance.interceptor.ts
|
||||
* [x] transform.interceptor.ts
|
||||
* **resilience/**
|
||||
* [x] resilience.module.ts
|
||||
* **services/**
|
||||
* [x] crypto.service.ts
|
||||
* [x] request-context.service.ts
|
||||
|
||||
### **📁 src/modules/** (Feature Modules)
|
||||
|
||||
1. **user/** (User Management & RBAC)
|
||||
* [x] `dto/`
|
||||
* [x] **assign-user-role.dto.ts**
|
||||
* [x] `create-user.dto.ts`
|
||||
* [ ] **search-user.dto.ts**
|
||||
* [x] `update-user.dto.ts`
|
||||
* [x] `update-user-preference.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `user.entity.ts`
|
||||
* [x] `role.entity.ts`
|
||||
* [x] `permission.entity.ts`
|
||||
* [x] **user-assignment.entity.ts**
|
||||
* [x] `user-preference.entity.ts`
|
||||
* [x] **user-assignment.service.ts**
|
||||
* [x] **user-preference.service.ts**
|
||||
* [x] `user.controller.ts`
|
||||
* [x] `user.module.ts`
|
||||
* [x] `user.service.ts`
|
||||
* [x] **user.service.spec.ts**
|
||||
* [x] **dto/**
|
||||
* [x] assign-user-role.dto.ts
|
||||
* [x] create-user.dto.ts
|
||||
* [x] update-user.dto.ts
|
||||
* [x] update-user-preference.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] user.entity.ts
|
||||
* [x] role.entity.ts
|
||||
* [x] permission.entity.ts
|
||||
* [x] user-assignment.entity.ts
|
||||
* [x] user-preference.entity.ts
|
||||
* [x] user-assignment.service.ts
|
||||
* [x] user-preference.service.ts
|
||||
* [x] user.controller.ts
|
||||
* [x] user.module.ts
|
||||
* [x] user.service.ts
|
||||
* [x] user.service.spec.ts
|
||||
|
||||
2. **project/** (Project Structure)
|
||||
* [x] `dto/`
|
||||
* [x] **create-project.dto.ts**
|
||||
* [x] `search-project.dto.ts`
|
||||
* [x] `update-project.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `contract-organization.entity.ts`
|
||||
* [x] `contract.entity.ts`
|
||||
* [x] `organization.entity.ts`
|
||||
* [x] `project-organization.entity.ts` (Junction)
|
||||
* [x] **project.entity.ts**
|
||||
* [x] **project.controller.spec.ts**
|
||||
* [x] `project.controller.ts`
|
||||
* [x] `project.module.ts`
|
||||
* [x] **project.service.spec.ts**
|
||||
* [x] `project.service.ts`
|
||||
* [x] **dto/**
|
||||
* [x] create-project.dto.ts
|
||||
* [x] search-project.dto.ts
|
||||
* [x] update-project.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] contract-organization.entity.ts
|
||||
* [x] contract.entity.ts
|
||||
* [x] organization.entity.ts
|
||||
* [x] project-organization.entity.ts
|
||||
* [x] project.entity.ts
|
||||
* [x] project.controller.spec.ts
|
||||
* [x] project.controller.ts
|
||||
* [x] project.module.ts
|
||||
* [x] project.service.spec.ts
|
||||
* [x] project.service.ts
|
||||
|
||||
3. **correspondence/** (Core Document System)
|
||||
* [x] `dto/`
|
||||
* [x] `add-reference.dto.ts`
|
||||
* [x] `create-correspondence.dto.ts`
|
||||
* [x] `search-correspondence.dto.ts`
|
||||
* [x] **submit-correspondence.dto.ts**
|
||||
* [x] `workflow-action.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `correspondence-reference.entity.ts`
|
||||
* [x] `correspondence-revision.entity.ts`
|
||||
* [x] `correspondence-routing.entity.ts` (Unified Workflow)
|
||||
* [x] `correspondence-status.entity.ts`
|
||||
* [x] `correspondence-type.entity.ts`
|
||||
* [x] `correspondence.entity.ts`
|
||||
* [x] **routing-template-step.entity.ts**
|
||||
* [x] `routing-template.entity.ts`
|
||||
* [x] **correspondence.controller.spec.ts**
|
||||
* [x] `correspondence.controller.ts`
|
||||
* [x] `correspondence.module.ts`
|
||||
* [x] **correspondence.service.spec.ts**
|
||||
* [x] `correspondence.service.ts` (Impersonation & Workflow Logic)
|
||||
* [x] **dto/**
|
||||
* [x] add-reference.dto.ts
|
||||
* [x] create-correspondence.dto.ts
|
||||
* [x] search-correspondence.dto.ts
|
||||
* [x] submit-correspondence.dto.ts
|
||||
* [x] workflow-action.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] correspondence-reference.entity.ts
|
||||
* [x] correspondence-revision.entity.ts
|
||||
* [x] correspondence-routing.entity.ts
|
||||
* [x] correspondence-status.entity.ts
|
||||
* [x] correspondence-type.entity.ts
|
||||
* [x] correspondence.entity.ts
|
||||
* [x] routing-template-step.entity.ts
|
||||
* [x] routing-template.entity.ts
|
||||
* [x] correspondence.controller.spec.ts
|
||||
* [x] correspondence.controller.ts
|
||||
* [x] correspondence.module.ts
|
||||
* [x] correspondence.service.spec.ts
|
||||
* [x] correspondence.service.ts
|
||||
|
||||
4. **drawing/** (Contract & Shop Drawings)
|
||||
* [x] `dto/`
|
||||
* [x] `create-contract-drawing.dto.ts`
|
||||
* [x] `create-shop-drawing-revision.dto.ts`
|
||||
* [x] `create-shop-drawing.dto.ts`
|
||||
* [x] `search-contract-drawing.dto.ts`
|
||||
* [x] `search-shop-drawing.dto.ts`
|
||||
* [x] `update-contract-drawing.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `contract-drawing-sub-category.entity.ts`
|
||||
* [x] `contract-drawing-volume.entity.ts`
|
||||
* [x] `contract-drawing.entity.ts`
|
||||
* [x] `shop-drawing-main-category.entity.ts`
|
||||
* [x] `shop-drawing-revision.entity.ts`
|
||||
* [x] `shop-drawing-sub-category.entity.ts`
|
||||
* [x] `shop-drawing.entity.ts`
|
||||
* [x] `contract-drawing.controller.ts`
|
||||
* [x] `contract-drawing.service.ts`
|
||||
* [x] `drawing-master-data.controller.ts`
|
||||
* [x] `drawing-master-data.service.ts`
|
||||
* [x] `drawing.module.ts`
|
||||
* [x] `shop-drawing.controller.ts`
|
||||
* [x] `shop-drawing.service.ts`
|
||||
* [x] **dto/**
|
||||
* [x] create-contract-drawing.dto.ts
|
||||
* [x] create-shop-drawing-revision.dto.ts
|
||||
* [x] create-shop-drawing.dto.ts
|
||||
* [x] search-contract-drawing.dto.ts
|
||||
* [x] search-shop-drawing.dto.ts
|
||||
* [x] update-contract-drawing.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] contract-drawing-sub-category.entity.ts
|
||||
* [x] contract-drawing-volume.entity.ts
|
||||
* [x] contract-drawing.entity.ts
|
||||
* [x] shop-drawing-main-category.entity.ts
|
||||
* [x] shop-drawing-revision.entity.ts
|
||||
* [x] shop-drawing-sub-category.entity.ts
|
||||
* [x] shop-drawing.entity.ts
|
||||
* [x] contract-drawing.controller.ts
|
||||
* [x] contract-drawing.service.ts
|
||||
* [x] drawing-master-data.controller.ts
|
||||
* [x] drawing-master-data.service.ts
|
||||
* [x] drawing.module.ts
|
||||
* [x] shop-drawing.controller.ts
|
||||
* [x] shop-drawing.service.ts
|
||||
|
||||
5. **rfa/** (Request for Approval & Advanced Workflow)
|
||||
* [x] `dto/`
|
||||
* [x] `create-rfa.dto.ts`
|
||||
* [x] `search-rfa.dto.ts`
|
||||
* [x] `update-rfa.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `rfa-approve-code.entity.ts`
|
||||
* [x] `rfa-item.entity.ts`
|
||||
* [x] `rfa-revision.entity.ts`
|
||||
* [x] `rfa-status-code.entity.ts`
|
||||
* [x] `rfa-type.entity.ts`
|
||||
* [x] `rfa-workflow-template-step.entity.ts`
|
||||
* [x] `rfa-workflow-template.entity.ts`
|
||||
* [x] `rfa-workflow.entity.ts`
|
||||
* [x] `rfa.entity.ts`
|
||||
* [x] `rfa.controller.ts`
|
||||
* [x] `rfa.module.ts`
|
||||
* [x] `rfa.service.ts` (Unified Workflow Integration)
|
||||
* [x] **dto/**
|
||||
* [x] create-rfa.dto.ts
|
||||
* [x] search-rfa.dto.ts
|
||||
* [x] update-rfa.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] rfa-approve-code.entity.ts
|
||||
* [x] rfa-item.entity.ts
|
||||
* [x] rfa-revision.entity.ts
|
||||
* [x] rfa-status-code.entity.ts
|
||||
* [x] rfa-type.entity.ts
|
||||
* [x] rfa-workflow-template-step.entity.ts
|
||||
* [x] rfa-workflow-template.entity.ts
|
||||
* [x] rfa-workflow.entity.ts
|
||||
* [x] rfa.entity.ts
|
||||
* [x] rfa.controller.ts
|
||||
* [x] rfa.module.ts
|
||||
* [x] rfa.service.ts
|
||||
|
||||
6. **circulation/** (Internal Routing)
|
||||
* [x] `dto/`
|
||||
* [x] `create-circulation.dto.ts`
|
||||
* [x] `update-circulation-routing.dto.ts`
|
||||
* [x] `search-circulation.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `circulation-routing.entity.ts`
|
||||
* [x] `circulation-status-code.entity.ts`
|
||||
* [x] `circulation.entity.ts`
|
||||
* [x] `circulation.controller.ts`
|
||||
* [x] `circulation.module.ts`
|
||||
* [x] `circulation.service.ts`
|
||||
* [x] **dto/**
|
||||
* [x] create-circulation.dto.ts
|
||||
* [x] update-circulation-routing.dto.ts
|
||||
* [x] search-circulation.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] circulation-routing.entity.ts
|
||||
* [x] circulation-status-code.entity.ts
|
||||
* [x] circulation.entity.ts
|
||||
* [x] circulation.controller.ts
|
||||
* [x] circulation.module.ts
|
||||
* [x] circulation.service.ts
|
||||
|
||||
7. **transmittal/** (Document Forwarding)
|
||||
* [x] `dto/`
|
||||
* [x] `create-transmittal.dto.ts`
|
||||
* [x] `search-transmittal.dto.ts`
|
||||
* [x] **update-transmittal.dto.ts**
|
||||
* [x] `entities/`
|
||||
* [x] `transmittal-item.entity.ts`
|
||||
* [x] `transmittal.entity.ts`
|
||||
* [x] `transmittal.controller.ts`
|
||||
* [x] `transmittal.module.ts`
|
||||
* [x] `transmittal.service.ts`
|
||||
* [x] **dto/**
|
||||
* [x] create-transmittal.dto.ts
|
||||
* [x] search-transmittal.dto.ts
|
||||
* [x] update-transmittal.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] transmittal-item.entity.ts
|
||||
* [x] transmittal.entity.ts
|
||||
* [x] transmittal.controller.ts
|
||||
* [x] transmittal.module.ts
|
||||
* [x] transmittal.service.ts
|
||||
|
||||
8. **notification/** (System Alerts)
|
||||
* [x] `dto/`
|
||||
* [x] `create-notification.dto.ts`
|
||||
* [x] `search-notification.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `notification.entity.ts`
|
||||
* [x] `notification-cleanup.service.ts` (Cron Job)
|
||||
* [x] `notification.controller.ts`
|
||||
* [x] `notification.gateway.ts`
|
||||
* [x] `notification.module.ts` (Real-time WebSocket)
|
||||
* [x] `notification.processor.ts` (Consumer/Worker for Email & Line)
|
||||
* [x] `notification.service.ts` (Producer)
|
||||
* [x] **dto/**
|
||||
* [x] create-notification.dto.ts
|
||||
* [x] search-notification.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] notification.entity.ts
|
||||
* [x] notification-cleanup.service.ts
|
||||
* [x] notification.controller.ts
|
||||
* [x] notification.gateway.ts
|
||||
* [x] notification.module.ts
|
||||
* [x] notification.processor.ts
|
||||
* [x] notification.service.ts
|
||||
|
||||
9. **search/** (Elasticsearch)
|
||||
* [x] `dto/search-query.dto.ts`
|
||||
* [x] `search.controller.ts`
|
||||
* [x] `search.module.ts`
|
||||
* [x] `search.service.ts` (Indexing & Searching)
|
||||
* [x] dto/search-query.dto.ts
|
||||
* [x] search.controller.ts
|
||||
* [x] search.module.ts
|
||||
* [x] search.service.ts
|
||||
|
||||
10. **document-numbering/** (Internal Service)
|
||||
* [x] `entities/`
|
||||
* [x] `document-number-format.entity.ts`
|
||||
* [x] `document-number-counter.entity.ts`
|
||||
* [x] `document-numbering.module.ts`
|
||||
* [x] **document-numbering.service.spec.ts**
|
||||
* [x] `document-numbering.service.ts` (Double-Lock Mechanism)
|
||||
10. **document-numbering/**
|
||||
* [x] **entities/**
|
||||
* [x] document-number-format.entity.ts
|
||||
* [x] document-number-counter.entity.ts
|
||||
* [x] document-numbering.module.ts
|
||||
* [x] document-numbering.service.spec.ts
|
||||
* [x] document-numbering.service.ts
|
||||
|
||||
11. **workflow-engine/** (Unified Logic)
|
||||
* [x] **dto/**
|
||||
* [x] `create-workflow-definition.dto.ts`
|
||||
* [x] `evaluate-workflow.dto.ts`
|
||||
* [x] `get-available-actions.dto.ts`
|
||||
* [x] `update-workflow-definition.dto.ts`
|
||||
* [x] `interfaces/workflow.interface.ts`
|
||||
* [x] **workflow-dsl.service.ts**
|
||||
* [x] `workflow-engine.module.ts`
|
||||
* [x] **workflow-engine.service.spec.ts**
|
||||
* [x] `workflow-engine.service.ts` (State Machine Logic)
|
||||
* [x] create-workflow-definition.dto.ts
|
||||
* [x] evaluate-workflow.dto.ts
|
||||
* [x] get-available-actions.dto.ts
|
||||
* [x] update-workflow-definition.dto.ts
|
||||
* [x] interfaces/workflow.interface.ts
|
||||
* [x] workflow-dsl.service.ts
|
||||
* [x] workflow-engine.module.ts
|
||||
* [x] workflow-engine.service.spec.ts
|
||||
* [x] workflow-engine.service.ts
|
||||
|
||||
12. **json-schema/** (Validation)
|
||||
* [x] `dto/`
|
||||
* [x] `create-json-schema.dto.ts`+
|
||||
* [x] `search-json-schema.dto.ts`
|
||||
* [x] `update-json-schema.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `json-schema.entity.ts`
|
||||
* [x] **json-schema.controller.spec.ts**
|
||||
* [x] **json-schema.controller.ts**
|
||||
* [x] `json-schema.module.ts`
|
||||
* [x] **json-schema.service.spec.ts**
|
||||
* [x] `json-schema.service.ts`
|
||||
* [x] **dto/**
|
||||
* [x] create-json-schema.dto.ts+
|
||||
* [x] search-json-schema.dto.ts
|
||||
* [x] update-json-schema.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] json-schema.entity.ts
|
||||
* [x] json-schema.controller.spec.ts
|
||||
* [x] json-schema.controller.ts
|
||||
* [x] json-schema.module.ts
|
||||
* [x] json-schema.service.spec.ts
|
||||
* [x] json-schema.service.ts
|
||||
|
||||
13. **monitoring/** (Monitoring & Metrics)
|
||||
* [x] `controllers/`
|
||||
* [x] `health.controller.ts`
|
||||
* [x] `logger/`
|
||||
* [x] `winston.config.ts`
|
||||
* [x] `services/`
|
||||
* [x] `metrics.service.ts`
|
||||
* [x] `monitoring.module.ts`
|
||||
|
||||
* [x] **controllers/**
|
||||
* [x] health.controller.ts
|
||||
* [x] **dto/**
|
||||
* [ ] set-maintenance.dto.ts
|
||||
* [x] **logger/**
|
||||
* [x] winston.config.ts
|
||||
* [x] **services/**
|
||||
* [x] metrics.service.ts
|
||||
* [x] monitoring.controller.ts
|
||||
* [x] monitoring.service.ts
|
||||
* [x] monitoring.module.ts
|
||||
|
||||
## **Folder Structure ของ Backend (NestJS)** ที่
|
||||
|
||||
@@ -354,17 +357,18 @@ backend/
|
||||
│ │ ├── jwt-auth.guard.ts # JWT authentication guard
|
||||
│ │ ├── jwt-refresh.guard.ts # JWT refresh guard
|
||||
│ │ ├── maintenance-mode.guard.ts # Maintenance mode guard
|
||||
│ │ └── rbac.guard.ts # Role-based access control enforcement
|
||||
│ │ └── rbac.guard.ts # Role-based access control enforcement (ตรวจสอบสิทธิ์ 4 ระดับ)
|
||||
│ │
|
||||
│ ├── interceptors/ # 📡 Interceptors for common use cases
|
||||
│ │ ├── audit-log.interceptor.ts # Automatically logs certain operations
|
||||
│ │ ├── idempotency.interceptor.ts # Idempotency interceptor
|
||||
│ │ ├── performance.interceptor.ts #
|
||||
│ │ └── transform.interceptor.ts # Standardized response formatting
|
||||
│ │
|
||||
│ ├─── resilience/ # 🛡️ Circuit-breaker / retry logic (if implemented)
|
||||
│ │ └── resilience.module.ts # Resilience module
|
||||
│ │
|
||||
│ └──── security/ # 🔐 Security service
|
||||
│ └──── services/ # 🔐 Security service
|
||||
│ ├── crypto.service.ts # Crypto service
|
||||
│ └── request-context.service.ts # Request context service (for logging)
|
||||
│
|
||||
@@ -373,7 +377,6 @@ backend/
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── assign-user-role.dto.ts # Assign roles to users
|
||||
│ │ │ ├── create-user.dto.ts # Create new user
|
||||
│ │ │ ├── search-user.dto.ts # Search users
|
||||
│ │ │ ├── update-user.dto.ts # Update user details
|
||||
│ │ │ └── update-user-preference.dto.ts # Update user preferences
|
||||
│ │ ├── entities/
|
||||
@@ -416,7 +419,7 @@ backend/
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── correspondence.entity.ts # Correspondence table definition
|
||||
│ │ │ ├── correspondence-revision.entity.ts # Correspondence revision entity
|
||||
│ │ │ ├── correspondence-routing.entity.ts # Correspondence routing entity
|
||||
│ │ │ ├── correspondence-routing.entity.ts # Correspondence routing entity (Unified Workflow)
|
||||
│ │ │ ├── correspondence-status.entity.ts # Correspondence status entity
|
||||
│ │ │ ├── correspondence-type.entity.ts # Correspondence type entity
|
||||
│ │ │ ├── correspondence-reference.entity.ts # Correspondence reference entity
|
||||
@@ -505,7 +508,7 @@ backend/
|
||||
│ │ ├── notification.module.ts # WebSocket + Processor registration
|
||||
│ │ ├── notification.controller.ts # REST endpoints
|
||||
│ │ ├── notification.gateway.ts # WebSocket gateway
|
||||
│ │ ├── notification.processor.ts # Message consumer (e.g. mail worker)
|
||||
│ │ ├── notification.processor.ts # Message consumer (Consumer/Worker for Email & Line)
|
||||
│ │ ├── notification.service.ts # Business logic
|
||||
│ │ └── notification-cleanup.service.ts # Cron-based cleanup job
|
||||
│ │
|
||||
@@ -533,11 +536,11 @@ backend/
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── workflow-definition.entity.ts # Workflow definition entity
|
||||
│ │ ├── interfaces/
|
||||
│ │ │ └── workflow.interface.ts # Workflow interface
|
||||
│ │ ├── workflow-engine.controller.ts # REST endpoints
|
||||
│ │ ├── workflow-engine.module.ts # Module DI container
|
||||
│ │ ├── workflow-engine.service.ts # Business logic
|
||||
│ │ └── workflow-engine.service.spec.ts # Unit tests
|
||||
│ │ │ └── workflow.interface.ts # Workflow interface
|
||||
│ │ ├── workflow-engine.controller.ts # REST endpoints
|
||||
│ │ ├── workflow-engine.module.ts # Module DI container
|
||||
│ │ ├── workflow-engine.service.ts # State Machine Logic
|
||||
│ │ └── workflow-engine.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── json-schema/ # 📋 Dynamic request schema validation
|
||||
│ │ ├── dto/
|
||||
@@ -554,15 +557,17 @@ backend/
|
||||
│ │
|
||||
│ └── monitoring/ # 📋 Dynamic request schema validation
|
||||
│ ├── controllers/
|
||||
│ │ ├── health.controller.ts # Create new JSON schema
|
||||
│ │ ├── update-json-schema.dto.ts # Update JSON schema
|
||||
│ │ └── search-json-schema.dto.ts # Search JSON schema
|
||||
│ │ └── health.controller.ts # Create new JSON schema
|
||||
│ ├── dto/
|
||||
│ │ └── set-maintenance.dto.ts # Create new JSON schema
|
||||
│ ├── logger/
|
||||
│ │ └── winston.config.ts # JSON schema entity
|
||||
│ │ └── winston.config.ts # JSON schema entity
|
||||
│ ├── services/
|
||||
│ │ └── metrics.service.ts # JSON schema entity
|
||||
│ └── monitoring.module.ts # Module DI container
|
||||
│
|
||||
│ │ └── metrics.service.ts # JSON schema entity
|
||||
│ ├── monitoring.controller.ts # REST endpoints
|
||||
│ ├── monitoring.service.ts # Business logic
|
||||
│ └── monitoring.module.ts # Module DI container
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@casl/ability": "^6.7.3",
|
||||
"@elastic/elasticsearch": "^8.11.1",
|
||||
"@nestjs-modules/ioredis": "^2.0.2",
|
||||
"@nestjs/axios": "^4.0.1",
|
||||
"@nestjs/bullmq": "^11.0.4",
|
||||
"@nestjs/cache-manager": "^3.0.1",
|
||||
|
||||
470
backend/pnpm-lock.yaml
generated
470
backend/pnpm-lock.yaml
generated
@@ -14,6 +14,12 @@ importers:
|
||||
'@elastic/elasticsearch':
|
||||
specifier: ^8.11.1
|
||||
version: 8.19.1
|
||||
'@nestjs-modules/ioredis':
|
||||
specifier: ^2.0.2
|
||||
version: 2.0.2(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(ioredis@5.8.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
|
||||
'@nestjs/axios':
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)
|
||||
'@nestjs/bullmq':
|
||||
specifier: ^11.0.4
|
||||
version: 11.0.4(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(bullmq@5.63.2)
|
||||
@@ -53,6 +59,9 @@ importers:
|
||||
'@nestjs/swagger':
|
||||
specifier: ^11.2.3
|
||||
version: 11.2.3(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)
|
||||
'@nestjs/terminus':
|
||||
specifier: ^11.0.0
|
||||
version: 11.0.0(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
|
||||
'@nestjs/throttler':
|
||||
specifier: ^6.4.0
|
||||
version: 6.4.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)
|
||||
@@ -71,6 +80,9 @@ importers:
|
||||
ajv-formats:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1(ajv@8.17.1)
|
||||
async-retry:
|
||||
specifier: ^1.3.3
|
||||
version: 1.3.3
|
||||
axios:
|
||||
specifier: ^1.13.2
|
||||
version: 1.13.2
|
||||
@@ -110,15 +122,24 @@ importers:
|
||||
mysql2:
|
||||
specifier: ^3.15.3
|
||||
version: 3.15.3
|
||||
nest-winston:
|
||||
specifier: ^1.10.2
|
||||
version: 1.10.2(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.18.3)
|
||||
nodemailer:
|
||||
specifier: ^7.0.10
|
||||
version: 7.0.10
|
||||
opossum:
|
||||
specifier: ^9.0.0
|
||||
version: 9.0.0
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
passport-jwt:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
prom-client:
|
||||
specifier: ^15.1.3
|
||||
version: 15.1.3
|
||||
redlock:
|
||||
specifier: 5.0.0-beta.2
|
||||
version: 5.0.0-beta.2
|
||||
@@ -140,6 +161,9 @@ importers:
|
||||
uuid:
|
||||
specifier: ^13.0.0
|
||||
version: 13.0.0
|
||||
winston:
|
||||
specifier: ^3.18.3
|
||||
version: 3.18.3
|
||||
devDependencies:
|
||||
'@eslint/eslintrc':
|
||||
specifier: ^3.2.0
|
||||
@@ -156,6 +180,9 @@ importers:
|
||||
'@nestjs/testing':
|
||||
specifier: ^11.0.1
|
||||
version: 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/platform-express@11.1.9)
|
||||
'@types/async-retry':
|
||||
specifier: ^1.4.9
|
||||
version: 1.4.9
|
||||
'@types/bcrypt':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
@@ -180,6 +207,9 @@ importers:
|
||||
'@types/node':
|
||||
specifier: ^22.10.7
|
||||
version: 22.19.1
|
||||
'@types/opossum':
|
||||
specifier: ^8.1.9
|
||||
version: 8.1.9
|
||||
'@types/passport-jwt':
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
@@ -572,10 +602,17 @@ packages:
|
||||
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
||||
'@colors/colors@1.6.0':
|
||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@dabh/diagnostics@2.0.8':
|
||||
resolution: {integrity: sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==}
|
||||
|
||||
'@elastic/elasticsearch@8.19.1':
|
||||
resolution: {integrity: sha512-+1j9NnQVOX+lbWB8LhCM7IkUmjU05Y4+BmSLfusq0msCsQb1Va+OUKFCoOXjCJqQrcgdRdQCjYYyolQ/npQALQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -980,6 +1017,20 @@ packages:
|
||||
'@napi-rs/wasm-runtime@0.2.12':
|
||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||
|
||||
'@nestjs-modules/ioredis@2.0.2':
|
||||
resolution: {integrity: sha512-8pzSvT8R3XP6p8ZzQmEN8OnY0yWrJ/elFhwQK+PID2zf1SLBkAZ18bDcx3SKQ2atledt0gd9kBeP5xT4MlyS7Q==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': '>=6.7.0'
|
||||
'@nestjs/core': '>=6.7.0'
|
||||
ioredis: '>=5.0.0'
|
||||
|
||||
'@nestjs/axios@4.0.1':
|
||||
resolution: {integrity: sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||
axios: ^1.3.1
|
||||
rxjs: ^7.0.0
|
||||
|
||||
'@nestjs/bull-shared@11.0.4':
|
||||
resolution: {integrity: sha512-VBJcDHSAzxQnpcDfA0kt9MTGUD1XZzfByV70su0W0eDCQ9aqIEBlzWRW21tv9FG9dIut22ysgDidshdjlnczLw==}
|
||||
peerDependencies:
|
||||
@@ -1124,6 +1175,102 @@ packages:
|
||||
class-validator:
|
||||
optional: true
|
||||
|
||||
'@nestjs/terminus@10.2.0':
|
||||
resolution: {integrity: sha512-zPs98xvJ4ogEimRQOz8eU90mb7z+W/kd/mL4peOgrJ/VqER+ibN2Cboj65uJZW3XuNhpOqaeYOJte86InJd44A==}
|
||||
peerDependencies:
|
||||
'@grpc/grpc-js': '*'
|
||||
'@grpc/proto-loader': '*'
|
||||
'@mikro-orm/core': '*'
|
||||
'@mikro-orm/nestjs': '*'
|
||||
'@nestjs/axios': ^1.0.0 || ^2.0.0 || ^3.0.0
|
||||
'@nestjs/common': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/core': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/microservices': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/mongoose': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/sequelize': ^9.0.0 || ^10.0.0
|
||||
'@nestjs/typeorm': ^9.0.0 || ^10.0.0
|
||||
'@prisma/client': '*'
|
||||
mongoose: '*'
|
||||
reflect-metadata: 0.1.x
|
||||
rxjs: 7.x
|
||||
sequelize: '*'
|
||||
typeorm: '*'
|
||||
peerDependenciesMeta:
|
||||
'@grpc/grpc-js':
|
||||
optional: true
|
||||
'@grpc/proto-loader':
|
||||
optional: true
|
||||
'@mikro-orm/core':
|
||||
optional: true
|
||||
'@mikro-orm/nestjs':
|
||||
optional: true
|
||||
'@nestjs/axios':
|
||||
optional: true
|
||||
'@nestjs/microservices':
|
||||
optional: true
|
||||
'@nestjs/mongoose':
|
||||
optional: true
|
||||
'@nestjs/sequelize':
|
||||
optional: true
|
||||
'@nestjs/typeorm':
|
||||
optional: true
|
||||
'@prisma/client':
|
||||
optional: true
|
||||
mongoose:
|
||||
optional: true
|
||||
sequelize:
|
||||
optional: true
|
||||
typeorm:
|
||||
optional: true
|
||||
|
||||
'@nestjs/terminus@11.0.0':
|
||||
resolution: {integrity: sha512-c55LOo9YGovmQHtFUMa/vDaxGZ2cglMTZejqgHREaApt/GArTfgYYGwhRXPLq8ZwiQQlLuYB+79e9iA8mlDSLA==}
|
||||
peerDependencies:
|
||||
'@grpc/grpc-js': '*'
|
||||
'@grpc/proto-loader': '*'
|
||||
'@mikro-orm/core': '*'
|
||||
'@mikro-orm/nestjs': '*'
|
||||
'@nestjs/axios': ^2.0.0 || ^3.0.0 || ^4.0.0
|
||||
'@nestjs/common': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/core': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/microservices': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/mongoose': ^11.0.0
|
||||
'@nestjs/sequelize': ^10.0.0 || ^11.0.0
|
||||
'@nestjs/typeorm': ^10.0.0 || ^11.0.0
|
||||
'@prisma/client': '*'
|
||||
mongoose: '*'
|
||||
reflect-metadata: 0.1.x || 0.2.x
|
||||
rxjs: 7.x
|
||||
sequelize: '*'
|
||||
typeorm: '*'
|
||||
peerDependenciesMeta:
|
||||
'@grpc/grpc-js':
|
||||
optional: true
|
||||
'@grpc/proto-loader':
|
||||
optional: true
|
||||
'@mikro-orm/core':
|
||||
optional: true
|
||||
'@mikro-orm/nestjs':
|
||||
optional: true
|
||||
'@nestjs/axios':
|
||||
optional: true
|
||||
'@nestjs/microservices':
|
||||
optional: true
|
||||
'@nestjs/mongoose':
|
||||
optional: true
|
||||
'@nestjs/sequelize':
|
||||
optional: true
|
||||
'@nestjs/typeorm':
|
||||
optional: true
|
||||
'@prisma/client':
|
||||
optional: true
|
||||
mongoose:
|
||||
optional: true
|
||||
sequelize:
|
||||
optional: true
|
||||
typeorm:
|
||||
optional: true
|
||||
|
||||
'@nestjs/testing@11.1.9':
|
||||
resolution: {integrity: sha512-UFxerBDdb0RUNxQNj25pvkvNE7/vxKhXYWBt3QuwBFnYISzRIzhVlyIqLfoV5YI3zV0m0Nn4QAn1KM0zzwfEng==}
|
||||
peerDependencies:
|
||||
@@ -1424,6 +1571,9 @@ packages:
|
||||
resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@so-ric/colorspace@1.1.6':
|
||||
resolution: {integrity: sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==}
|
||||
|
||||
'@socket.io/component-emitter@3.1.2':
|
||||
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
||||
|
||||
@@ -1458,6 +1608,9 @@ packages:
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
|
||||
|
||||
'@types/async-retry@1.4.9':
|
||||
resolution: {integrity: sha512-s1ciZQJzRh3708X/m3vPExr5KJlzlZJvXsKpbtE2luqNcbROr64qU+3KpJsYHqWMeaxI839OvXf9PrUSw1Xtyg==}
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||
|
||||
@@ -1565,6 +1718,9 @@ packages:
|
||||
'@types/nodemailer@7.0.4':
|
||||
resolution: {integrity: sha512-ee8fxWqOchH+Hv6MDDNNy028kwvVnLplrStm4Zf/3uHWw5zzo8FoYYeffpJtGs2wWysEumMH0ZIdMGMY1eMAow==}
|
||||
|
||||
'@types/opossum@8.1.9':
|
||||
resolution: {integrity: sha512-Jm/tYxuJFefiwRYs+/EOsUP3ktk0c8siMgAHPLnA4PXF4wKghzcjqf88dY+Xii5jId5Txw4JV0FMKTpjbd7KJA==}
|
||||
|
||||
'@types/passport-jwt@4.0.1':
|
||||
resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==}
|
||||
|
||||
@@ -1580,6 +1736,9 @@ packages:
|
||||
'@types/range-parser@1.2.7':
|
||||
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
||||
|
||||
'@types/retry@0.12.5':
|
||||
resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==}
|
||||
|
||||
'@types/send@0.17.6':
|
||||
resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==}
|
||||
|
||||
@@ -1598,6 +1757,9 @@ packages:
|
||||
'@types/supertest@6.0.3':
|
||||
resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==}
|
||||
|
||||
'@types/triple-beam@1.3.5':
|
||||
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
|
||||
|
||||
'@types/uuid@11.0.0':
|
||||
resolution: {integrity: sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==}
|
||||
deprecated: This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.
|
||||
@@ -1891,6 +2053,9 @@ packages:
|
||||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
ansi-align@3.0.1:
|
||||
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
|
||||
|
||||
ansi-colors@4.1.3:
|
||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -1961,6 +2126,12 @@ packages:
|
||||
asap@2.0.6:
|
||||
resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==}
|
||||
|
||||
async-retry@1.3.3:
|
||||
resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==}
|
||||
|
||||
async@3.2.6:
|
||||
resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
@@ -2018,6 +2189,9 @@ packages:
|
||||
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
bintrees@1.0.2:
|
||||
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
|
||||
|
||||
bl@4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
|
||||
|
||||
@@ -2028,6 +2202,10 @@ packages:
|
||||
bowser@2.12.1:
|
||||
resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==}
|
||||
|
||||
boxen@5.1.2:
|
||||
resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
|
||||
|
||||
@@ -2127,6 +2305,10 @@ packages:
|
||||
chardet@2.1.1:
|
||||
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
|
||||
|
||||
check-disk-space@3.4.0:
|
||||
resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
chokidar@4.0.3:
|
||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||
engines: {node: '>= 14.16.0'}
|
||||
@@ -2148,6 +2330,10 @@ packages:
|
||||
class-validator@0.14.2:
|
||||
resolution: {integrity: sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==}
|
||||
|
||||
cli-boxes@2.2.1:
|
||||
resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
cli-cursor@3.1.0:
|
||||
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -2187,9 +2373,25 @@ packages:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
||||
color-convert@3.1.3:
|
||||
resolution: {integrity: sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==}
|
||||
engines: {node: '>=14.6'}
|
||||
|
||||
color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
|
||||
color-name@2.1.0:
|
||||
resolution: {integrity: sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
color-string@2.1.4:
|
||||
resolution: {integrity: sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
color@5.0.3:
|
||||
resolution: {integrity: sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -2393,6 +2595,9 @@ packages:
|
||||
emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
enabled@2.0.0:
|
||||
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
|
||||
|
||||
encodeurl@2.0.0:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -2580,6 +2785,9 @@ packages:
|
||||
fb-watchman@2.0.2:
|
||||
resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
|
||||
|
||||
fecha@4.2.3:
|
||||
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
|
||||
|
||||
fflate@0.8.2:
|
||||
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
|
||||
|
||||
@@ -2626,6 +2834,9 @@ packages:
|
||||
flatted@3.3.3:
|
||||
resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==}
|
||||
|
||||
fn.name@1.1.0:
|
||||
resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
|
||||
|
||||
follow-redirects@1.15.11:
|
||||
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
@@ -3142,6 +3353,9 @@ packages:
|
||||
keyv@5.5.4:
|
||||
resolution: {integrity: sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==}
|
||||
|
||||
kuler@2.0.0:
|
||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
|
||||
leven@3.1.0:
|
||||
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -3218,6 +3432,10 @@ packages:
|
||||
resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
logform@2.7.0:
|
||||
resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
long@5.3.2:
|
||||
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
|
||||
|
||||
@@ -3383,6 +3601,12 @@ packages:
|
||||
neo-async@2.6.2:
|
||||
resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
|
||||
|
||||
nest-winston@1.10.2:
|
||||
resolution: {integrity: sha512-Z9IzL/nekBOF/TEwBHUJDiDPMaXUcFquUQOFavIRet6xF0EbuWnOzslyN/ksgzG+fITNgXhMdrL/POp9SdaFxA==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^5.0.0 || ^6.6.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
||||
winston: ^3.0.0
|
||||
|
||||
node-abort-controller@3.1.1:
|
||||
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||
|
||||
@@ -3438,10 +3662,17 @@ packages:
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
one-time@1.0.0:
|
||||
resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
|
||||
|
||||
onetime@5.1.2:
|
||||
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
opossum@9.0.0:
|
||||
resolution: {integrity: sha512-K76U0QkxOfUZamneQuzz+AP0fyfTJcCplZ2oZL93nxeupuJbN4s6uFNbmVCt4eWqqGqRnnowdFuBicJ1fLMVxw==}
|
||||
engines: {node: ^24 || ^22 || ^20}
|
||||
|
||||
optionator@0.9.4:
|
||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -3574,6 +3805,10 @@ packages:
|
||||
resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==}
|
||||
engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0}
|
||||
|
||||
prom-client@15.1.3:
|
||||
resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==}
|
||||
engines: {node: ^16 || ^18 || >=20}
|
||||
|
||||
promise-coalesce@1.5.0:
|
||||
resolution: {integrity: sha512-cTJ30U+ur1LD7pMPyQxiKIwxjtAjLsyU7ivRhVWZrX9BNIXtf78pc37vSMc8Vikx7DVzEKNk2SEJ5KWUpSG2ig==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -3663,6 +3898,10 @@ packages:
|
||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
retry@0.13.1:
|
||||
resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
reusify@1.1.0:
|
||||
resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
@@ -3683,6 +3922,10 @@ packages:
|
||||
safe-buffer@5.2.1:
|
||||
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
|
||||
|
||||
safe-stable-stringify@2.5.0:
|
||||
resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
safer-buffer@2.1.2:
|
||||
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||
|
||||
@@ -3807,6 +4050,9 @@ packages:
|
||||
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
stack-trace@0.0.10:
|
||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -3913,6 +4159,9 @@ packages:
|
||||
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
tdigest@0.1.2:
|
||||
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
|
||||
|
||||
terser-webpack-plugin@5.3.14:
|
||||
resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
@@ -3938,6 +4187,9 @@ packages:
|
||||
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
text-hex@1.0.0:
|
||||
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
|
||||
|
||||
tmpl@1.0.5:
|
||||
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
|
||||
|
||||
@@ -3957,6 +4209,10 @@ packages:
|
||||
resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==}
|
||||
engines: {node: '>=14.16'}
|
||||
|
||||
triple-beam@1.4.1:
|
||||
resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
|
||||
engines: {node: '>= 14.0.0'}
|
||||
|
||||
ts-api-utils@2.1.0:
|
||||
resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==}
|
||||
engines: {node: '>=18.12'}
|
||||
@@ -4030,6 +4286,10 @@ packages:
|
||||
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
type-fest@0.20.2:
|
||||
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
type-fest@0.21.3:
|
||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -4235,6 +4495,18 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
widest-line@3.1.0:
|
||||
resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
winston-transport@4.9.0:
|
||||
resolution: {integrity: sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
winston@3.18.3:
|
||||
resolution: {integrity: sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4968,10 +5240,18 @@ snapshots:
|
||||
'@colors/colors@1.5.0':
|
||||
optional: true
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
|
||||
'@dabh/diagnostics@2.0.8':
|
||||
dependencies:
|
||||
'@so-ric/colorspace': 1.1.6
|
||||
enabled: 2.0.0
|
||||
kuler: 2.0.0
|
||||
|
||||
'@elastic/elasticsearch@8.19.1':
|
||||
dependencies:
|
||||
'@elastic/transport': 8.10.0
|
||||
@@ -5489,6 +5769,36 @@ snapshots:
|
||||
'@tybys/wasm-util': 0.10.1
|
||||
optional: true
|
||||
|
||||
'@nestjs-modules/ioredis@2.0.2(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(ioredis@5.8.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(@nestjs/websockets@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
ioredis: 5.8.2
|
||||
optionalDependencies:
|
||||
'@nestjs/terminus': 10.2.0(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
|
||||
transitivePeerDependencies:
|
||||
- '@grpc/grpc-js'
|
||||
- '@grpc/proto-loader'
|
||||
- '@mikro-orm/core'
|
||||
- '@mikro-orm/nestjs'
|
||||
- '@nestjs/axios'
|
||||
- '@nestjs/microservices'
|
||||
- '@nestjs/mongoose'
|
||||
- '@nestjs/sequelize'
|
||||
- '@nestjs/typeorm'
|
||||
- '@prisma/client'
|
||||
- mongoose
|
||||
- reflect-metadata
|
||||
- rxjs
|
||||
- sequelize
|
||||
- typeorm
|
||||
|
||||
'@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
axios: 1.13.2
|
||||
rxjs: 7.8.2
|
||||
|
||||
'@nestjs/bull-shared@11.0.4(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
@@ -5656,6 +5966,33 @@ snapshots:
|
||||
class-transformer: 0.5.1
|
||||
class-validator: 0.14.2
|
||||
|
||||
'@nestjs/terminus@10.2.0(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(@nestjs/websockets@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
boxen: 5.1.2
|
||||
check-disk-space: 3.4.0
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.2
|
||||
optionalDependencies:
|
||||
'@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)
|
||||
'@nestjs/typeorm': 11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
|
||||
typeorm: 0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||
optional: true
|
||||
|
||||
'@nestjs/terminus@11.0.0(@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2))(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/typeorm@11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))))(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(@nestjs/websockets@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
boxen: 5.1.2
|
||||
check-disk-space: 3.4.0
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.2
|
||||
optionalDependencies:
|
||||
'@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)
|
||||
'@nestjs/typeorm': 11.0.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))
|
||||
typeorm: 0.3.27(ioredis@5.8.2)(mysql2@3.15.3)(redis@4.7.1)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))
|
||||
|
||||
'@nestjs/testing@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)(@nestjs/platform-express@11.1.9)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
@@ -6038,6 +6375,11 @@ snapshots:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@so-ric/colorspace@1.1.6':
|
||||
dependencies:
|
||||
color: 5.0.3
|
||||
text-hex: 1.0.0
|
||||
|
||||
'@socket.io/component-emitter@3.1.2': {}
|
||||
|
||||
'@sqltools/formatter@1.2.5': {}
|
||||
@@ -6071,6 +6413,10 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/async-retry@1.4.9':
|
||||
dependencies:
|
||||
'@types/retry': 0.12.5
|
||||
|
||||
'@types/babel__core@7.20.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
@@ -6210,6 +6556,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- aws-crt
|
||||
|
||||
'@types/opossum@8.1.9':
|
||||
dependencies:
|
||||
'@types/node': 22.19.1
|
||||
|
||||
'@types/passport-jwt@4.0.1':
|
||||
dependencies:
|
||||
'@types/jsonwebtoken': 9.0.10
|
||||
@@ -6228,6 +6578,8 @@ snapshots:
|
||||
|
||||
'@types/range-parser@1.2.7': {}
|
||||
|
||||
'@types/retry@0.12.5': {}
|
||||
|
||||
'@types/send@0.17.6':
|
||||
dependencies:
|
||||
'@types/mime': 1.3.5
|
||||
@@ -6257,6 +6609,8 @@ snapshots:
|
||||
'@types/methods': 1.1.4
|
||||
'@types/superagent': 8.1.9
|
||||
|
||||
'@types/triple-beam@1.3.5': {}
|
||||
|
||||
'@types/uuid@11.0.0':
|
||||
dependencies:
|
||||
uuid: 13.0.0
|
||||
@@ -6574,6 +6928,10 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
ansi-align@3.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
||||
ansi-colors@4.1.3: {}
|
||||
|
||||
ansi-escapes@4.3.2:
|
||||
@@ -6633,6 +6991,12 @@ snapshots:
|
||||
|
||||
asap@2.0.6: {}
|
||||
|
||||
async-retry@1.3.3:
|
||||
dependencies:
|
||||
retry: 0.13.1
|
||||
|
||||
async@3.2.6: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
@@ -6714,6 +7078,8 @@ snapshots:
|
||||
node-addon-api: 8.5.0
|
||||
node-gyp-build: 4.8.4
|
||||
|
||||
bintrees@1.0.2: {}
|
||||
|
||||
bl@4.1.0:
|
||||
dependencies:
|
||||
buffer: 5.7.1
|
||||
@@ -6736,6 +7102,17 @@ snapshots:
|
||||
|
||||
bowser@2.12.1: {}
|
||||
|
||||
boxen@5.1.2:
|
||||
dependencies:
|
||||
ansi-align: 3.0.1
|
||||
camelcase: 6.3.0
|
||||
chalk: 4.1.2
|
||||
cli-boxes: 2.2.1
|
||||
string-width: 4.2.3
|
||||
type-fest: 0.20.2
|
||||
widest-line: 3.1.0
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
brace-expansion@1.1.12:
|
||||
dependencies:
|
||||
balanced-match: 1.0.2
|
||||
@@ -6858,6 +7235,8 @@ snapshots:
|
||||
|
||||
chardet@2.1.1: {}
|
||||
|
||||
check-disk-space@3.4.0: {}
|
||||
|
||||
chokidar@4.0.3:
|
||||
dependencies:
|
||||
readdirp: 4.1.2
|
||||
@@ -6876,6 +7255,8 @@ snapshots:
|
||||
libphonenumber-js: 1.12.27
|
||||
validator: 13.15.23
|
||||
|
||||
cli-boxes@2.2.1: {}
|
||||
|
||||
cli-cursor@3.1.0:
|
||||
dependencies:
|
||||
restore-cursor: 3.1.0
|
||||
@@ -6908,8 +7289,23 @@ snapshots:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
||||
color-convert@3.1.3:
|
||||
dependencies:
|
||||
color-name: 2.1.0
|
||||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
color-name@2.1.0: {}
|
||||
|
||||
color-string@2.1.4:
|
||||
dependencies:
|
||||
color-name: 2.1.0
|
||||
|
||||
color@5.0.3:
|
||||
dependencies:
|
||||
color-convert: 3.1.3
|
||||
color-string: 2.1.4
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
@@ -7070,6 +7466,8 @@ snapshots:
|
||||
|
||||
emoji-regex@9.2.2: {}
|
||||
|
||||
enabled@2.0.0: {}
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
engine.io-parser@5.2.3: {}
|
||||
@@ -7306,6 +7704,8 @@ snapshots:
|
||||
dependencies:
|
||||
bser: 2.1.1
|
||||
|
||||
fecha@4.2.3: {}
|
||||
|
||||
fflate@0.8.2: {}
|
||||
|
||||
file-entry-cache@8.0.0:
|
||||
@@ -7357,6 +7757,8 @@ snapshots:
|
||||
|
||||
flatted@3.3.3: {}
|
||||
|
||||
fn.name@1.1.0: {}
|
||||
|
||||
follow-redirects@1.15.11: {}
|
||||
|
||||
for-each@0.3.5:
|
||||
@@ -8071,6 +8473,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@keyv/serialize': 1.1.1
|
||||
|
||||
kuler@2.0.0: {}
|
||||
|
||||
leven@3.1.0: {}
|
||||
|
||||
levn@0.4.1:
|
||||
@@ -8127,6 +8531,15 @@ snapshots:
|
||||
chalk: 4.1.2
|
||||
is-unicode-supported: 0.1.0
|
||||
|
||||
logform@2.7.0:
|
||||
dependencies:
|
||||
'@colors/colors': 1.6.0
|
||||
'@types/triple-beam': 1.3.5
|
||||
fecha: 4.2.3
|
||||
ms: 2.1.3
|
||||
safe-stable-stringify: 2.5.0
|
||||
triple-beam: 1.4.1
|
||||
|
||||
long@5.3.2: {}
|
||||
|
||||
lru-cache@10.4.3: {}
|
||||
@@ -8272,6 +8685,12 @@ snapshots:
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
|
||||
nest-winston@1.10.2(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(winston@3.18.3):
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
fast-safe-stringify: 2.1.1
|
||||
winston: 3.18.3
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-addon-api@8.5.0: {}
|
||||
@@ -8313,10 +8732,16 @@ snapshots:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
one-time@1.0.0:
|
||||
dependencies:
|
||||
fn.name: 1.1.0
|
||||
|
||||
onetime@5.1.2:
|
||||
dependencies:
|
||||
mimic-fn: 2.1.0
|
||||
|
||||
opossum@9.0.0: {}
|
||||
|
||||
optionator@0.9.4:
|
||||
dependencies:
|
||||
deep-is: 0.1.4
|
||||
@@ -8438,6 +8863,11 @@ snapshots:
|
||||
ansi-styles: 5.2.0
|
||||
react-is: 18.3.1
|
||||
|
||||
prom-client@15.1.3:
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
tdigest: 0.1.2
|
||||
|
||||
promise-coalesce@1.5.0: {}
|
||||
|
||||
proxy-addr@2.0.7:
|
||||
@@ -8518,6 +8948,8 @@ snapshots:
|
||||
onetime: 5.1.2
|
||||
signal-exit: 3.0.7
|
||||
|
||||
retry@0.13.1: {}
|
||||
|
||||
reusify@1.1.0: {}
|
||||
|
||||
router@2.2.0:
|
||||
@@ -8544,6 +8976,8 @@ snapshots:
|
||||
|
||||
safe-buffer@5.2.1: {}
|
||||
|
||||
safe-stable-stringify@2.5.0: {}
|
||||
|
||||
safer-buffer@2.1.2: {}
|
||||
|
||||
schema-utils@3.3.0:
|
||||
@@ -8705,6 +9139,8 @@ snapshots:
|
||||
|
||||
sqlstring@2.3.3: {}
|
||||
|
||||
stack-trace@0.0.10: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
@@ -8811,6 +9247,10 @@ snapshots:
|
||||
|
||||
tapable@2.3.0: {}
|
||||
|
||||
tdigest@0.1.2:
|
||||
dependencies:
|
||||
bintrees: 1.0.2
|
||||
|
||||
terser-webpack-plugin@5.3.14(webpack@5.100.2):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.31
|
||||
@@ -8833,6 +9273,8 @@ snapshots:
|
||||
glob: 7.2.3
|
||||
minimatch: 3.1.2
|
||||
|
||||
text-hex@1.0.0: {}
|
||||
|
||||
tmpl@1.0.5: {}
|
||||
|
||||
to-buffer@1.2.2:
|
||||
@@ -8853,6 +9295,8 @@ snapshots:
|
||||
'@tokenizer/token': 0.3.0
|
||||
ieee754: 1.2.1
|
||||
|
||||
triple-beam@1.4.1: {}
|
||||
|
||||
ts-api-utils@2.1.0(typescript@5.9.3):
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
@@ -8926,6 +9370,8 @@ snapshots:
|
||||
|
||||
type-detect@4.0.8: {}
|
||||
|
||||
type-fest@0.20.2: {}
|
||||
|
||||
type-fest@0.21.3: {}
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
@@ -9126,6 +9572,30 @@ snapshots:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
widest-line@3.1.0:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
||||
winston-transport@4.9.0:
|
||||
dependencies:
|
||||
logform: 2.7.0
|
||||
readable-stream: 3.6.2
|
||||
triple-beam: 1.4.1
|
||||
|
||||
winston@3.18.3:
|
||||
dependencies:
|
||||
'@colors/colors': 1.6.0
|
||||
'@dabh/diagnostics': 2.0.8
|
||||
async: 3.2.6
|
||||
is-stream: 2.0.1
|
||||
logform: 2.7.0
|
||||
one-time: 1.0.0
|
||||
readable-stream: 3.6.2
|
||||
safe-stable-stringify: 2.5.0
|
||||
stack-trace: 0.0.10
|
||||
triple-beam: 1.4.1
|
||||
winston-transport: 4.9.0
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
wordwrap@1.0.0: {}
|
||||
|
||||
@@ -42,6 +42,7 @@ import { MonitoringModule } from './modules/monitoring/monitoring.module';
|
||||
import { ResilienceModule } from './common/resilience/resilience.module'; // ✅ Import
|
||||
// ... imports
|
||||
import { SearchModule } from './modules/search/search.module'; // ✅ Import
|
||||
import { RedisModule } from '@nestjs-modules/ioredis'; // [NEW]
|
||||
@Module({
|
||||
imports: [
|
||||
// 1. Setup Config Module พร้อม Validation
|
||||
@@ -113,7 +114,18 @@ import { SearchModule } from './modules/search/search.module'; // ✅ Import
|
||||
},
|
||||
}),
|
||||
}),
|
||||
|
||||
// [NEW] Setup Redis Module (สำหรับ InjectRedis)
|
||||
RedisModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
type: 'single',
|
||||
url: `redis://${configService.get('REDIS_HOST')}:${configService.get('REDIS_PORT')}`,
|
||||
options: {
|
||||
password: configService.get('REDIS_PASSWORD'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
}),
|
||||
// 📊 Register Monitoring Module (Health & Metrics) [Req 6.10]
|
||||
MonitoringModule,
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
PrimaryColumn, // ✅ [Fix] เพิ่ม Import นี้
|
||||
} from 'typeorm';
|
||||
import { User } from '../../modules/user/entities/user.entity';
|
||||
|
||||
@@ -46,7 +47,9 @@ export class AuditLog {
|
||||
@Column({ name: 'user_agent', length: 255, nullable: true })
|
||||
userAgent?: string;
|
||||
|
||||
// ✅ [Fix] รวม Decorator ไว้ที่นี่ที่เดียว
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
@PrimaryColumn() // เพื่อบอกว่าเป็น Composite PK คู่กับ auditId
|
||||
createdAt!: Date;
|
||||
|
||||
// Relations
|
||||
|
||||
75
backend/src/database/migrations/01_init_partitioning.sql
Normal file
75
backend/src/database/migrations/01_init_partitioning.sql
Normal file
@@ -0,0 +1,75 @@
|
||||
-- ============================================================
|
||||
-- Database Partitioning Script for LCBP3-DMS (Fixed #1075)
|
||||
-- Target Tables: audit_logs, notifications
|
||||
-- Strategy: Range Partitioning by YEAR(created_at)
|
||||
-- ============================================================
|
||||
-- ------------------------------------------------------------
|
||||
-- 1. Audit Logs Partitioning
|
||||
-- ------------------------------------------------------------
|
||||
-- Step 1: เอา AUTO_INCREMENT ออกก่อน (เพื่อไม่ให้ติด Error 1075 ตอนลบ PK)
|
||||
ALTER TABLE audit_logs
|
||||
MODIFY audit_id BIGINT NOT NULL;
|
||||
-- Step 2: ลบ Primary Key เดิม
|
||||
ALTER TABLE audit_logs DROP PRIMARY KEY;
|
||||
-- Step 3: สร้าง Primary Key ใหม่ (รวม created_at เพื่อทำ Partition)
|
||||
ALTER TABLE audit_logs
|
||||
ADD PRIMARY KEY (audit_id, created_at);
|
||||
-- Step 4: ใส่ AUTO_INCREMENT กลับเข้าไป
|
||||
ALTER TABLE audit_logs
|
||||
MODIFY audit_id BIGINT NOT NULL AUTO_INCREMENT;
|
||||
-- Step 5: สร้าง Partition
|
||||
ALTER TABLE audit_logs PARTITION BY RANGE (YEAR(created_at)) (
|
||||
PARTITION p_old
|
||||
VALUES LESS THAN (2024),
|
||||
PARTITION p2024
|
||||
VALUES LESS THAN (2025),
|
||||
PARTITION p2025
|
||||
VALUES LESS THAN (2026),
|
||||
PARTITION p2026
|
||||
VALUES LESS THAN (2027),
|
||||
PARTITION p2027
|
||||
VALUES LESS THAN (2028),
|
||||
PARTITION p2028
|
||||
VALUES LESS THAN (2029),
|
||||
PARTITION p2029
|
||||
VALUES LESS THAN (2030),
|
||||
PARTITION p2030
|
||||
VALUES LESS THAN (2031),
|
||||
PARTITION p_future
|
||||
VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
-- ------------------------------------------------------------
|
||||
-- 2. Notifications Partitioning
|
||||
-- ------------------------------------------------------------
|
||||
-- Step 1: เอา AUTO_INCREMENT ออกก่อน
|
||||
ALTER TABLE notifications
|
||||
MODIFY id INT NOT NULL;
|
||||
-- Step 2: ลบ Primary Key เดิม
|
||||
ALTER TABLE notifications DROP PRIMARY KEY;
|
||||
-- Step 3: สร้าง Primary Key ใหม่
|
||||
ALTER TABLE notifications
|
||||
ADD PRIMARY KEY (id, created_at);
|
||||
-- Step 4: ใส่ AUTO_INCREMENT กลับเข้าไป
|
||||
ALTER TABLE notifications
|
||||
MODIFY id INT NOT NULL AUTO_INCREMENT;
|
||||
-- Step 5: สร้าง Partition
|
||||
ALTER TABLE notifications PARTITION BY RANGE (YEAR(created_at)) (
|
||||
PARTITION p_old
|
||||
VALUES LESS THAN (2024),
|
||||
PARTITION p2024
|
||||
VALUES LESS THAN (2025),
|
||||
PARTITION p2025
|
||||
VALUES LESS THAN (2026),
|
||||
PARTITION p2026
|
||||
VALUES LESS THAN (2027),
|
||||
PARTITION p2027
|
||||
VALUES LESS THAN (2028),
|
||||
PARTITION p2028
|
||||
VALUES LESS THAN (2029),
|
||||
PARTITION p2029
|
||||
VALUES LESS THAN (2030),
|
||||
PARTITION p2030
|
||||
VALUES LESS THAN (2031),
|
||||
PARTITION p_future
|
||||
VALUES LESS THAN MAXVALUE
|
||||
);
|
||||
82
backend/src/database/seeds/workflow-definitions.seed.ts
Normal file
82
backend/src/database/seeds/workflow-definitions.seed.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// src/database/seeds/workflow-definitions.seed.ts
|
||||
|
||||
import { DataSource } from 'typeorm';
|
||||
import { WorkflowDefinition } from '../../modules/workflow-engine/entities/workflow-definition.entity';
|
||||
import { WorkflowDslService } from '../../modules/workflow-engine/workflow-dsl.service';
|
||||
|
||||
export const seedWorkflowDefinitions = async (dataSource: DataSource) => {
|
||||
const repo = dataSource.getRepository(WorkflowDefinition);
|
||||
const dslService = new WorkflowDslService();
|
||||
|
||||
// 1. RFA Workflow (Standard)
|
||||
const rfaDsl = {
|
||||
workflow: 'RFA',
|
||||
version: 1,
|
||||
states: [
|
||||
{
|
||||
name: 'DRAFT',
|
||||
initial: true,
|
||||
on: { SUBMIT: { to: 'IN_REVIEW', requirements: [{ role: 'Editor' }] } },
|
||||
},
|
||||
{
|
||||
name: 'IN_REVIEW',
|
||||
on: {
|
||||
APPROVE: {
|
||||
to: 'APPROVED',
|
||||
requirements: [{ role: 'Contract Admin' }],
|
||||
},
|
||||
REJECT: {
|
||||
to: 'REJECTED',
|
||||
requirements: [{ role: 'Contract Admin' }],
|
||||
},
|
||||
COMMENT: { to: 'DRAFT', requirements: [{ role: 'Contract Admin' }] }, // ส่งกลับแก้ไข
|
||||
},
|
||||
},
|
||||
{ name: 'APPROVED', terminal: true },
|
||||
{ name: 'REJECTED', terminal: true },
|
||||
],
|
||||
};
|
||||
|
||||
// 2. Circulation Workflow
|
||||
const circulationDsl = {
|
||||
workflow: 'CIRCULATION',
|
||||
version: 1,
|
||||
states: [
|
||||
{
|
||||
name: 'OPEN',
|
||||
initial: true,
|
||||
on: { SEND: { to: 'IN_REVIEW' } },
|
||||
},
|
||||
{
|
||||
name: 'IN_REVIEW',
|
||||
on: {
|
||||
COMPLETE: { to: 'COMPLETED' }, // เมื่อทุกคนตอบครบ
|
||||
CANCEL: { to: 'CANCELLED' },
|
||||
},
|
||||
},
|
||||
{ name: 'COMPLETED', terminal: true },
|
||||
{ name: 'CANCELLED', terminal: true },
|
||||
],
|
||||
};
|
||||
|
||||
const workflows = [rfaDsl, circulationDsl];
|
||||
|
||||
for (const dsl of workflows) {
|
||||
const exists = await repo.findOne({
|
||||
where: { workflow_code: dsl.workflow, version: dsl.version },
|
||||
});
|
||||
if (!exists) {
|
||||
const compiled = dslService.compile(dsl);
|
||||
await repo.save(
|
||||
repo.create({
|
||||
workflow_code: dsl.workflow,
|
||||
version: dsl.version,
|
||||
dsl: dsl,
|
||||
compiled: compiled,
|
||||
is_active: true,
|
||||
}),
|
||||
);
|
||||
console.log(`✅ Seeded Workflow: ${dsl.workflow} v${dsl.version}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,5 +1,3 @@
|
||||
// File: src/modules/master/dto/create-tag.dto.ts
|
||||
|
||||
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
@@ -7,12 +5,9 @@ export class CreateTagDto {
|
||||
@ApiProperty({ example: 'URGENT', description: 'ชื่อ Tag' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
tag_name: string;
|
||||
tag_name!: string; // เพิ่ม !
|
||||
|
||||
@ApiProperty({
|
||||
example: 'เอกสารด่วนต้องดำเนินการทันที',
|
||||
description: 'คำอธิบาย',
|
||||
})
|
||||
@ApiProperty({ example: 'คำอธิบาย', description: 'คำอธิบาย' })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// File: src/modules/master/entities/tag.entity.ts
|
||||
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
@@ -11,17 +9,17 @@ import {
|
||||
@Entity('tags')
|
||||
export class Tag {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
id!: number; // เพิ่ม !
|
||||
|
||||
@Column({ length: 100, unique: true, comment: 'ชื่อ Tag' })
|
||||
tag_name: string;
|
||||
@Column({ length: 100, unique: true })
|
||||
tag_name!: string; // เพิ่ม !
|
||||
|
||||
@Column({ type: 'text', nullable: true, comment: 'คำอธิบายแท็ก' })
|
||||
description: string;
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description!: string; // เพิ่ม !
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date;
|
||||
created_at!: Date; // เพิ่ม !
|
||||
|
||||
@UpdateDateColumn()
|
||||
updated_at: Date;
|
||||
updated_at!: Date; // เพิ่ม !
|
||||
}
|
||||
|
||||
@@ -49,15 +49,15 @@ export class MasterService {
|
||||
|
||||
async findAllCorrespondenceTypes() {
|
||||
return this.corrTypeRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
async findAllCorrespondenceStatuses() {
|
||||
return this.corrStatusRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
@@ -67,22 +67,22 @@ export class MasterService {
|
||||
|
||||
async findAllRfaTypes() {
|
||||
return this.rfaTypeRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
async findAllRfaStatuses() {
|
||||
return this.rfaStatusRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
async findAllRfaApproveCodes() {
|
||||
return this.rfaApproveRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
@@ -92,8 +92,8 @@ export class MasterService {
|
||||
|
||||
async findAllCirculationStatuses() {
|
||||
return this.circulationStatusRepo.find({
|
||||
where: { is_active: true },
|
||||
order: { sort_order: 'ASC' },
|
||||
where: { isActive: true }, // ✅ แก้เป็น camelCase
|
||||
order: { sortOrder: 'ASC' }, // ✅ แก้เป็น camelCase
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,9 +101,6 @@ export class MasterService {
|
||||
// 🏷️ Tag Management (CRUD)
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* ค้นหา Tag ทั้งหมด พร้อมรองรับการ Search และ Pagination
|
||||
*/
|
||||
async findAllTags(query?: SearchTagDto) {
|
||||
const qb = this.tagRepo.createQueryBuilder('tag');
|
||||
|
||||
@@ -115,14 +112,12 @@ export class MasterService {
|
||||
|
||||
qb.orderBy('tag.tag_name', 'ASC');
|
||||
|
||||
// Pagination Logic
|
||||
if (query?.page && query?.limit) {
|
||||
const page = query.page;
|
||||
const limit = query.limit;
|
||||
qb.skip((page - 1) * limit).take(limit);
|
||||
}
|
||||
|
||||
// ถ้ามีการแบ่งหน้า ให้ส่งคืนทั้งข้อมูลและจำนวนทั้งหมด (count)
|
||||
if (query?.page && query?.limit) {
|
||||
const [items, total] = await qb.getManyAndCount();
|
||||
return {
|
||||
@@ -153,7 +148,7 @@ export class MasterService {
|
||||
}
|
||||
|
||||
async updateTag(id: number, dto: UpdateTagDto) {
|
||||
const tag = await this.findOneTag(id); // Reuse findOne for check
|
||||
const tag = await this.findOneTag(id);
|
||||
Object.assign(tag, dto);
|
||||
return this.tagRepo.save(tag);
|
||||
}
|
||||
|
||||
16
backend/src/modules/monitoring/dto/set-maintenance.dto.ts
Normal file
16
backend/src/modules/monitoring/dto/set-maintenance.dto.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { IsBoolean, IsOptional, IsString } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class SetMaintenanceDto {
|
||||
@ApiProperty({ description: 'สถานะ Maintenance (true = เปิด, false = ปิด)' })
|
||||
@IsBoolean()
|
||||
enabled!: boolean; // ✅ เพิ่ม ! ตรงนี้
|
||||
|
||||
@ApiProperty({
|
||||
description: 'เหตุผลที่ปิดปรับปรุง (แสดงให้ User เห็น)',
|
||||
required: false,
|
||||
})
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
reason?: string; // Optional (?) ไม่ต้องใส่ !
|
||||
}
|
||||
30
backend/src/modules/monitoring/monitoring.controller.ts
Normal file
30
backend/src/modules/monitoring/monitoring.controller.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { MonitoringService } from './monitoring.service';
|
||||
import { SetMaintenanceDto } from './dto/set-maintenance.dto';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { BypassMaintenance } from '../../common/decorators/bypass-maintenance.decorator';
|
||||
|
||||
@ApiTags('System Monitoring')
|
||||
@Controller('monitoring')
|
||||
export class MonitoringController {
|
||||
constructor(private readonly monitoringService: MonitoringService) {}
|
||||
|
||||
@Get('maintenance')
|
||||
@ApiOperation({ summary: 'Check maintenance status (Public)' })
|
||||
@BypassMaintenance() // API นี้ต้องเรียกได้แม้ระบบปิดอยู่
|
||||
getMaintenanceStatus() {
|
||||
return this.monitoringService.getMaintenanceStatus();
|
||||
}
|
||||
|
||||
@Post('maintenance')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth()
|
||||
@RequirePermission('system.manage_all') // เฉพาะ Superadmin เท่านั้น
|
||||
@BypassMaintenance() // Admin ต้องยิงเปิด/ปิดได้แม้ระบบจะปิดอยู่
|
||||
@ApiOperation({ summary: 'Toggle Maintenance Mode (Admin Only)' })
|
||||
setMaintenanceMode(@Body() dto: SetMaintenanceDto) {
|
||||
return this.monitoringService.setMaintenanceMode(dto);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,34 @@
|
||||
// File: src/modules/monitoring/monitoring.module.ts
|
||||
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TerminusModule } from '@nestjs/terminus';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
import { APP_INTERCEPTOR } from '@nestjs/core';
|
||||
|
||||
// Existing Components
|
||||
import { HealthController } from './controllers/health.controller';
|
||||
import { MetricsService } from './services/metrics.service';
|
||||
import { PerformanceInterceptor } from '../../common/interceptors/performance.interceptor';
|
||||
|
||||
@Global() // ทำให้ Module นี้ใช้งานได้ทั่วทั้ง App โดยไม่ต้อง Import ซ้ำ
|
||||
// [NEW] Maintenance Mode Components
|
||||
import { MonitoringController } from './monitoring.controller';
|
||||
import { MonitoringService } from './monitoring.service';
|
||||
|
||||
@Global() // Module นี้เป็น Global (ดีแล้วครับ)
|
||||
@Module({
|
||||
imports: [TerminusModule, HttpModule],
|
||||
controllers: [HealthController],
|
||||
controllers: [
|
||||
HealthController, // ✅ ของเดิม: /health
|
||||
MonitoringController, // ✅ ของใหม่: /monitoring/maintenance
|
||||
],
|
||||
providers: [
|
||||
MetricsService,
|
||||
MetricsService, // ✅ ของเดิม
|
||||
MonitoringService, // ✅ ของใหม่ (Logic เปิด/ปิด Maintenance)
|
||||
{
|
||||
provide: APP_INTERCEPTOR, // Register Global Interceptor
|
||||
useClass: PerformanceInterceptor,
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: PerformanceInterceptor, // ✅ ของเดิม (จับเวลา Response Time)
|
||||
},
|
||||
],
|
||||
exports: [MetricsService],
|
||||
exports: [MetricsService, MonitoringService],
|
||||
})
|
||||
export class MonitoringModule {}
|
||||
|
||||
44
backend/src/modules/monitoring/monitoring.service.ts
Normal file
44
backend/src/modules/monitoring/monitoring.service.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// File: src/modules/monitoring/monitoring.service.ts
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRedis } from '@nestjs-modules/ioredis';
|
||||
import Redis from 'ioredis';
|
||||
import { SetMaintenanceDto } from './dto/set-maintenance.dto';
|
||||
|
||||
@Injectable()
|
||||
export class MonitoringService {
|
||||
private readonly logger = new Logger(MonitoringService.name);
|
||||
private readonly MAINTENANCE_KEY = 'system:maintenance_mode';
|
||||
|
||||
constructor(@InjectRedis() private readonly redis: Redis) {}
|
||||
|
||||
/**
|
||||
* ตรวจสอบสถานะปัจจุบัน
|
||||
*/
|
||||
async getMaintenanceStatus() {
|
||||
const status = await this.redis.get(this.MAINTENANCE_KEY);
|
||||
return {
|
||||
isEnabled: status === 'true',
|
||||
message:
|
||||
status === 'true' ? 'System is under maintenance' : 'System is normal',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ตั้งค่า Maintenance Mode
|
||||
*/
|
||||
async setMaintenanceMode(dto: SetMaintenanceDto) {
|
||||
if (dto.enabled) {
|
||||
await this.redis.set(this.MAINTENANCE_KEY, 'true');
|
||||
// เก็บเหตุผลไว้ใน Key อื่นก็ได้ถ้าต้องการ แต่เบื้องต้น Guard เช็คแค่ Key นี้
|
||||
this.logger.warn(
|
||||
`⚠️ SYSTEM ENTERED MAINTENANCE MODE: ${dto.reason || 'No reason provided'}`,
|
||||
);
|
||||
} else {
|
||||
await this.redis.del(this.MAINTENANCE_KEY);
|
||||
this.logger.log('✅ System exited maintenance mode');
|
||||
}
|
||||
|
||||
return this.getMaintenanceStatus();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
CreateDateColumn,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
PrimaryColumn, // ✅ [Fix] เพิ่ม Import นี้
|
||||
} from 'typeorm';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
|
||||
@@ -44,7 +45,9 @@ export class Notification {
|
||||
@Column({ name: 'entity_id', nullable: true })
|
||||
entityId?: number;
|
||||
|
||||
// ✅ [Fix] รวม Decorator ไว้ที่นี่ที่เดียว (เป็นทั้ง CreateDate และ PrimaryColumn สำหรับ Partition)
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
@PrimaryColumn()
|
||||
createdAt!: Date;
|
||||
|
||||
// --- Relations ---
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import { Job } from 'bullmq';
|
||||
// File: src/modules/notification/notification.processor.ts
|
||||
|
||||
import { Processor, WorkerHost, InjectQueue } from '@nestjs/bullmq';
|
||||
import { Job, Queue } from 'bullmq';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRedis } from '@nestjs-modules/ioredis';
|
||||
import Redis from 'ioredis';
|
||||
import * as nodemailer from 'nodemailer';
|
||||
import axios from 'axios';
|
||||
|
||||
import { UserService } from '../user/user.service';
|
||||
|
||||
interface NotificationPayload {
|
||||
userId: number;
|
||||
title: string;
|
||||
message: string;
|
||||
link: string;
|
||||
type: 'EMAIL' | 'LINE' | 'SYSTEM';
|
||||
}
|
||||
|
||||
@Processor('notifications')
|
||||
export class NotificationProcessor extends WorkerHost {
|
||||
private readonly logger = new Logger(NotificationProcessor.name);
|
||||
private mailerTransport: nodemailer.Transporter;
|
||||
|
||||
// ค่าคงที่สำหรับ Digest (เช่น รอ 5 นาที)
|
||||
private readonly DIGEST_DELAY = 5 * 60 * 1000;
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private userService: UserService,
|
||||
@InjectQueue('notifications') private notificationQueue: Queue,
|
||||
@InjectRedis() private readonly redis: Redis,
|
||||
) {
|
||||
super();
|
||||
// Setup Nodemailer
|
||||
this.mailerTransport = nodemailer.createTransport({
|
||||
host: this.configService.get('SMTP_HOST'),
|
||||
port: this.configService.get('SMTP_PORT'),
|
||||
port: Number(this.configService.get('SMTP_PORT')),
|
||||
secure: this.configService.get('SMTP_SECURE') === 'true',
|
||||
auth: {
|
||||
user: this.configService.get('SMTP_USER'),
|
||||
@@ -30,59 +47,196 @@ export class NotificationProcessor extends WorkerHost {
|
||||
}
|
||||
|
||||
async process(job: Job<any, any, string>): Promise<any> {
|
||||
this.logger.debug(`Processing job ${job.name} for user ${job.data.userId}`);
|
||||
this.logger.debug(`Processing job ${job.name} (ID: ${job.id})`);
|
||||
|
||||
switch (job.name) {
|
||||
case 'send-email':
|
||||
return this.handleSendEmail(job.data);
|
||||
case 'send-line':
|
||||
return this.handleSendLine(job.data);
|
||||
default:
|
||||
throw new Error(`Unknown job name: ${job.name}`);
|
||||
try {
|
||||
switch (job.name) {
|
||||
case 'dispatch-notification':
|
||||
// Job หลัก: ตัดสินใจว่าจะส่งเลย หรือจะเข้า Digest Queue
|
||||
return this.handleDispatch(job.data);
|
||||
|
||||
case 'process-digest':
|
||||
// Job รอง: ทำงานเมื่อครบเวลา Delay เพื่อส่งแบบรวม
|
||||
return this.handleProcessDigest(job.data.userId, job.data.type);
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown job name: ${job.name}`);
|
||||
}
|
||||
} catch (error) {
|
||||
// ✅ แก้ไขตรงนี้: Type Casting (error as Error)
|
||||
this.logger.error(
|
||||
`Failed to process job ${job.name}: ${(error as Error).message}`,
|
||||
(error as Error).stack,
|
||||
);
|
||||
throw error; // ให้ BullMQ จัดการ Retry
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSendEmail(data: any) {
|
||||
const user = await this.userService.findOne(data.userId);
|
||||
if (!user || !user.email) {
|
||||
this.logger.warn(`User ${data.userId} has no email`);
|
||||
/**
|
||||
* ฟังก์ชันตัดสินใจ (Dispatcher)
|
||||
* ตรวจสอบ User Preferences และ Digest Mode
|
||||
*/
|
||||
private async handleDispatch(data: NotificationPayload) {
|
||||
// 1. ดึง User พร้อม Preferences
|
||||
const user: any = await this.userService.findOne(data.userId);
|
||||
|
||||
if (!user) {
|
||||
this.logger.warn(`User ${data.userId} not found, skipping notification.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const prefs = user.preferences || {
|
||||
notify_email: true,
|
||||
notify_line: true,
|
||||
digest_mode: false,
|
||||
};
|
||||
|
||||
// 2. ตรวจสอบว่า User ปิดรับการแจ้งเตือนหรือไม่
|
||||
if (data.type === 'EMAIL' && !prefs.notify_email) return;
|
||||
if (data.type === 'LINE' && !prefs.notify_line) return;
|
||||
|
||||
// 3. ตรวจสอบ Digest Mode
|
||||
if (prefs.digest_mode) {
|
||||
await this.addToDigest(data);
|
||||
} else {
|
||||
// ส่งทันที (Real-time)
|
||||
if (data.type === 'EMAIL') await this.sendEmailImmediate(user, data);
|
||||
if (data.type === 'LINE') await this.sendLineImmediate(user, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* เพิ่มข้อความลงใน Redis List และตั้งเวลาส่ง (Delayed Job)
|
||||
*/
|
||||
private async addToDigest(data: NotificationPayload) {
|
||||
const key = `digest:${data.type}:${data.userId}`;
|
||||
|
||||
// 1. Push ข้อมูลลง Redis List
|
||||
await this.redis.rpush(key, JSON.stringify(data));
|
||||
|
||||
// 2. ตรวจสอบว่ามี "ตัวนับเวลาถอยหลัง" (Delayed Job) อยู่หรือยัง?
|
||||
const lockKey = `digest:lock:${data.type}:${data.userId}`;
|
||||
const isLocked = await this.redis.get(lockKey);
|
||||
|
||||
if (!isLocked) {
|
||||
// ถ้ายังไม่มี Job รออยู่ ให้สร้างใหม่
|
||||
await this.notificationQueue.add(
|
||||
'process-digest',
|
||||
{ userId: data.userId, type: data.type },
|
||||
{
|
||||
delay: this.DIGEST_DELAY,
|
||||
jobId: `digest-${data.type}-${data.userId}-${Date.now()}`,
|
||||
},
|
||||
);
|
||||
|
||||
// Set Lock ไว้ตามเวลา Delay เพื่อไม่ให้สร้าง Job ซ้ำ
|
||||
await this.redis.set(lockKey, '1', 'PX', this.DIGEST_DELAY);
|
||||
this.logger.log(
|
||||
`Scheduled digest for User ${data.userId} (${data.type}) in ${this.DIGEST_DELAY}ms`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ประมวลผล Digest (ส่งแบบรวม)
|
||||
*/
|
||||
private async handleProcessDigest(userId: number, type: 'EMAIL' | 'LINE') {
|
||||
const key = `digest:${type}:${userId}`;
|
||||
const lockKey = `digest:lock:${type}:${userId}`;
|
||||
|
||||
// 1. ดึงข้อความทั้งหมดจาก Redis และลบออกทันที
|
||||
const messagesRaw = await this.redis.lrange(key, 0, -1);
|
||||
await this.redis.del(key);
|
||||
await this.redis.del(lockKey); // Clear lock
|
||||
|
||||
if (!messagesRaw || messagesRaw.length === 0) return;
|
||||
|
||||
const messages: NotificationPayload[] = messagesRaw.map((m) =>
|
||||
JSON.parse(m),
|
||||
);
|
||||
const user = await this.userService.findOne(userId);
|
||||
|
||||
if (type === 'EMAIL') {
|
||||
await this.sendEmailDigest(user, messages);
|
||||
} else if (type === 'LINE') {
|
||||
await this.sendLineDigest(user, messages);
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================
|
||||
// SENDERS (Immediate & Digest)
|
||||
// =====================================================
|
||||
|
||||
private async sendEmailImmediate(user: any, data: NotificationPayload) {
|
||||
if (!user.email) return;
|
||||
await this.mailerTransport.sendMail({
|
||||
from: '"LCBP3 DMS" <no-reply@np-dms.work>',
|
||||
to: user.email,
|
||||
subject: `[DMS] ${data.title}`,
|
||||
html: `
|
||||
<h3>${data.title}</h3>
|
||||
<p>${data.message}</p>
|
||||
<br/>
|
||||
<a href="${data.link}">คลิกเพื่อดูรายละเอียด</a>
|
||||
`,
|
||||
html: `<h3>${data.title}</h3><p>${data.message}</p><br/><a href="${data.link}">คลิกเพื่อดูรายละเอียด</a>`,
|
||||
});
|
||||
this.logger.log(`Email sent to ${user.email}`);
|
||||
}
|
||||
|
||||
private async handleSendLine(data: any) {
|
||||
const user = await this.userService.findOne(data.userId);
|
||||
// ตรวจสอบว่า User มี Line ID หรือไม่ (หรือใช้ Group Token ถ้าเป็นระบบรวม)
|
||||
// ในที่นี้สมมติว่าเรายิงเข้า n8n webhook เพื่อจัดการต่อ
|
||||
const n8nWebhookUrl = this.configService.get('N8N_LINE_WEBHOOK_URL');
|
||||
private async sendEmailDigest(user: any, messages: NotificationPayload[]) {
|
||||
if (!user.email) return;
|
||||
|
||||
if (!n8nWebhookUrl) {
|
||||
this.logger.warn('N8N_LINE_WEBHOOK_URL not configured');
|
||||
return;
|
||||
}
|
||||
// สร้าง HTML List
|
||||
const listItems = messages
|
||||
.map(
|
||||
(msg) =>
|
||||
`<li><strong>${msg.title}</strong>: ${msg.message} <a href="${msg.link}">[View]</a></li>`,
|
||||
)
|
||||
.join('');
|
||||
|
||||
await this.mailerTransport.sendMail({
|
||||
from: '"LCBP3 DMS" <no-reply@np-dms.work>',
|
||||
to: user.email,
|
||||
subject: `[DMS Summary] คุณมีการแจ้งเตือนใหม่ ${messages.length} รายการ`,
|
||||
html: `
|
||||
<h3>สรุปรายการแจ้งเตือน (Digest)</h3>
|
||||
<ul>${listItems}</ul>
|
||||
<p>คุณได้รับอีเมลนี้เพราะเปิดใช้งานโหมดสรุปรายการ</p>
|
||||
`,
|
||||
});
|
||||
this.logger.log(
|
||||
`Digest Email sent to ${user.email} (${messages.length} items)`,
|
||||
);
|
||||
}
|
||||
|
||||
private async sendLineImmediate(user: any, data: NotificationPayload) {
|
||||
const n8nWebhookUrl = this.configService.get('N8N_LINE_WEBHOOK_URL');
|
||||
if (!n8nWebhookUrl) return;
|
||||
|
||||
try {
|
||||
await axios.post(n8nWebhookUrl, {
|
||||
userId: user.user_id, // หรือ user.lineId ถ้ามี
|
||||
userId: user.user_id,
|
||||
message: `${data.title}\n${data.message}`,
|
||||
link: data.link,
|
||||
isDigest: false,
|
||||
});
|
||||
this.logger.log(`Line notification sent via n8n for user ${data.userId}`);
|
||||
} catch (error: any) {
|
||||
throw new Error(`Failed to send Line notification: ${error.message}`);
|
||||
} catch (error) {
|
||||
// ✅ แก้ไขตรงนี้ด้วย: Type Casting (error as Error)
|
||||
this.logger.error(`Line Error: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async sendLineDigest(user: any, messages: NotificationPayload[]) {
|
||||
const n8nWebhookUrl = this.configService.get('N8N_LINE_WEBHOOK_URL');
|
||||
if (!n8nWebhookUrl) return;
|
||||
|
||||
const summary = messages.map((m, i) => `${i + 1}. ${m.title}`).join('\n');
|
||||
|
||||
try {
|
||||
await axios.post(n8nWebhookUrl, {
|
||||
userId: user.user_id,
|
||||
message: `สรุป ${messages.length} รายการใหม่:\n${summary}`,
|
||||
link: 'https://lcbp3.np-dms.work/notifications',
|
||||
isDigest: true,
|
||||
});
|
||||
} catch (error) {
|
||||
// ✅ แก้ไขตรงนี้ด้วย: Type Casting (error as Error)
|
||||
this.logger.error(`Line Digest Error: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// File: src/modules/notification/notification.service.ts
|
||||
|
||||
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
|
||||
import { InjectQueue } from '@nestjs/bullmq';
|
||||
import { Queue } from 'bullmq';
|
||||
@@ -22,9 +23,9 @@ export interface NotificationJobData {
|
||||
title: string;
|
||||
message: string;
|
||||
type: 'EMAIL' | 'LINE' | 'SYSTEM'; // ช่องทางหลักที่ต้องการส่ง (Trigger Type)
|
||||
entityType?: string; // e.g., 'rfa', 'correspondence'
|
||||
entityId?: number; // e.g., rfa_id
|
||||
link?: string; // Deep link to frontend page
|
||||
entityType?: string;
|
||||
entityId?: number;
|
||||
link?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -37,109 +38,57 @@ export class NotificationService {
|
||||
private notificationRepo: Repository<Notification>,
|
||||
@InjectRepository(User)
|
||||
private userRepo: Repository<User>,
|
||||
@InjectRepository(UserPreference)
|
||||
private userPrefRepo: Repository<UserPreference>,
|
||||
// ไม่ต้อง Inject UserPrefRepo แล้ว เพราะ Processor จะจัดการเอง
|
||||
private notificationGateway: NotificationGateway,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ส่งการแจ้งเตือน (Centralized Notification Sender)
|
||||
* 1. บันทึก DB (System Log)
|
||||
* 2. ส่ง Real-time (WebSocket)
|
||||
* 3. ส่ง External (Email/Line) ผ่าน Queue ตาม User Preference
|
||||
*/
|
||||
async send(data: NotificationJobData): Promise<void> {
|
||||
try {
|
||||
// ---------------------------------------------------------
|
||||
// 1. สร้าง Entity และบันทึกลง DB (เพื่อให้มี History ในระบบ)
|
||||
// 1. สร้าง Entity และบันทึกลง DB (System Log)
|
||||
// ---------------------------------------------------------
|
||||
const notification = this.notificationRepo.create({
|
||||
userId: data.userId,
|
||||
title: data.title,
|
||||
message: data.message,
|
||||
notificationType: NotificationType.SYSTEM, // ใน DB เก็บเป็น SYSTEM เสมอเพื่อแสดงใน App
|
||||
notificationType: NotificationType.SYSTEM,
|
||||
entityType: data.entityType,
|
||||
entityId: data.entityId,
|
||||
isRead: false,
|
||||
// link: data.link // ถ้า Entity มี field link ให้ใส่ด้วย
|
||||
});
|
||||
|
||||
const savedNotification = await this.notificationRepo.save(notification);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 2. Real-time Push (WebSocket) -> ส่งให้ User ทันทีถ้า Online
|
||||
// 2. Real-time Push (WebSocket)
|
||||
// ---------------------------------------------------------
|
||||
this.notificationGateway.sendToUser(data.userId, savedNotification);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 3. ตรวจสอบ User Preferences เพื่อส่งช่องทางอื่น (Email/Line)
|
||||
// 3. Push Job ลง Redis BullMQ (Dispatch Logic)
|
||||
// เปลี่ยนชื่อ Job เป็น 'dispatch-notification' ตาม Processor
|
||||
// ---------------------------------------------------------
|
||||
const userPref = await this.userPrefRepo.findOne({
|
||||
where: { userId: data.userId },
|
||||
});
|
||||
|
||||
// ใช้ Nullish Coalescing Operator (??)
|
||||
// ถ้าไม่มีค่า (undefined/null) ให้ Default เป็น true
|
||||
const shouldSendEmail = userPref?.notifyEmail ?? true;
|
||||
const shouldSendLine = userPref?.notifyLine ?? true;
|
||||
|
||||
const jobs = [];
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 4. เตรียม Job สำหรับ Email Queue
|
||||
// เงื่อนไข: User เปิดรับ Email และ Noti นี้ไม่ได้บังคับส่งแค่ LINE
|
||||
// ---------------------------------------------------------
|
||||
if (shouldSendEmail && data.type !== 'LINE') {
|
||||
jobs.push({
|
||||
name: 'send-email',
|
||||
data: {
|
||||
...data,
|
||||
notificationId: savedNotification.id,
|
||||
target: 'EMAIL',
|
||||
await this.notificationQueue.add(
|
||||
'dispatch-notification',
|
||||
{
|
||||
...data,
|
||||
notificationId: savedNotification.id, // ส่ง ID ไปด้วยเผื่อใช้ Tracking
|
||||
},
|
||||
{
|
||||
attempts: 3,
|
||||
backoff: {
|
||||
type: 'exponential',
|
||||
delay: 5000,
|
||||
},
|
||||
opts: {
|
||||
attempts: 3, // ลองใหม่ 3 ครั้งถ้าล่ม (Resilience)
|
||||
backoff: {
|
||||
type: 'exponential',
|
||||
delay: 5000, // รอ 5s, 10s, 20s...
|
||||
},
|
||||
removeOnComplete: true, // ลบ Job เมื่อเสร็จ (ประหยัด Redis Memory)
|
||||
},
|
||||
});
|
||||
}
|
||||
removeOnComplete: true,
|
||||
},
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 5. เตรียม Job สำหรับ Line Queue
|
||||
// เงื่อนไข: User เปิดรับ Line และ Noti นี้ไม่ได้บังคับส่งแค่ EMAIL
|
||||
// ---------------------------------------------------------
|
||||
if (shouldSendLine && data.type !== 'EMAIL') {
|
||||
jobs.push({
|
||||
name: 'send-line',
|
||||
data: {
|
||||
...data,
|
||||
notificationId: savedNotification.id,
|
||||
target: 'LINE',
|
||||
},
|
||||
opts: {
|
||||
attempts: 3,
|
||||
backoff: { type: 'fixed', delay: 3000 },
|
||||
removeOnComplete: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 6. Push Jobs ลง Redis BullMQ
|
||||
// ---------------------------------------------------------
|
||||
if (jobs.length > 0) {
|
||||
await this.notificationQueue.addBulk(jobs);
|
||||
this.logger.debug(
|
||||
`Queued ${jobs.length} external notifications for user ${data.userId}`,
|
||||
);
|
||||
}
|
||||
this.logger.debug(`Dispatched notification job for user ${data.userId}`);
|
||||
} catch (error) {
|
||||
// Error Handling: ไม่ Throw เพื่อไม่ให้ Flow หลัก (เช่น การสร้างเอกสาร) พัง
|
||||
// แต่บันทึก Error ไว้ตรวจสอบ
|
||||
this.logger.error(
|
||||
`Failed to process notification for user ${data.userId}`,
|
||||
(error as Error).stack,
|
||||
@@ -147,9 +96,8 @@ export class NotificationService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ดึงรายการแจ้งเตือนของ User (สำหรับ Controller)
|
||||
*/
|
||||
// ... (ส่วน findAll, markAsRead, cleanupOldNotifications เหมือนเดิม ไม่ต้องแก้) ...
|
||||
|
||||
async findAll(userId: number, searchDto: SearchNotificationDto) {
|
||||
const { page = 1, limit = 20, isRead } = searchDto;
|
||||
const skip = (page - 1) * limit;
|
||||
@@ -161,14 +109,11 @@ export class NotificationService {
|
||||
.take(limit)
|
||||
.skip(skip);
|
||||
|
||||
// Filter by Read Status (ถ้ามีการส่งมา)
|
||||
if (isRead !== undefined) {
|
||||
queryBuilder.andWhere('notification.isRead = :isRead', { isRead });
|
||||
}
|
||||
|
||||
const [items, total] = await queryBuilder.getManyAndCount();
|
||||
|
||||
// นับจำนวนที่ยังไม่ได้อ่านทั้งหมด (เพื่อแสดง Badge ที่กระดิ่ง)
|
||||
const unreadCount = await this.notificationRepo.count({
|
||||
where: { userId, isRead: false },
|
||||
});
|
||||
@@ -185,9 +130,6 @@ export class NotificationService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* อ่านแจ้งเตือน (Mark as Read)
|
||||
*/
|
||||
async markAsRead(id: number, userId: number): Promise<void> {
|
||||
const notification = await this.notificationRepo.findOne({
|
||||
where: { id, userId },
|
||||
@@ -200,15 +142,9 @@ export class NotificationService {
|
||||
if (!notification.isRead) {
|
||||
notification.isRead = true;
|
||||
await this.notificationRepo.save(notification);
|
||||
|
||||
// Update Unread Count via WebSocket (Optional)
|
||||
// this.notificationGateway.sendUnreadCount(userId, ...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* อ่านทั้งหมด (Mark All as Read)
|
||||
*/
|
||||
async markAllAsRead(userId: number): Promise<void> {
|
||||
await this.notificationRepo.update(
|
||||
{ userId, isRead: false },
|
||||
@@ -216,10 +152,6 @@ export class NotificationService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* ลบการแจ้งเตือนที่เก่าเกินกำหนด (ใช้กับ Cron Job Cleanup)
|
||||
* เก็บไว้ 90 วัน
|
||||
*/
|
||||
async cleanupOldNotifications(days: number = 90): Promise<number> {
|
||||
const dateLimit = new Date();
|
||||
dateLimit.setDate(dateLimit.getDate() - days);
|
||||
|
||||
@@ -64,6 +64,7 @@ export class UserService {
|
||||
async findOne(id: number): Promise<User> {
|
||||
const user = await this.usersRepository.findOne({
|
||||
where: { user_id: id },
|
||||
relations: ['preferences', 'roles'], // [IMPORTANT] ต้องโหลด preferences มาด้วย
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
|
||||
38
prompt.md
38
prompt.md
@@ -116,35 +116,23 @@ npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @typ
|
||||
|
||||
```
|
||||
|
||||
## Prompt: การพัฒนา Core Auth Module (AuthModule) สำหรับ DMS v1.2.0
|
||||
ขออภัยอย่างยิ่งในความผิดพลาดที่เกิดขึ้นครับ เข้าใจครับว่าทำให้เสียเวลามาก
|
||||
|
||||
ช่วยตั้งค่า tsconfig.json, nest-cli.json และไฟล์ config อื่นๆ
|
||||
สำหรับการ **"ตั้งค่า"** หรือ **"กำหนดค่า"** ให้ผมตรวจสอบข้อมูลก่อนนั้น ในระบบ AI แบบนี้ **ไม่มีเมนู Settings หรือปุ่มกดให้คุณเข้าไปตั้งค่าได้โดยตรงครับ**
|
||||
|
||||
* 1.1 สร้าง User Entity
|
||||
การควบคุมพฤติกรรมของผมต้องทำผ่าน **"คำสั่ง (Prompt)"** ที่คุณพิมพ์เข้ามาเท่านั้นครับ เพื่อป้องกันไม่ให้เกิดเหตุการณ์แบบนี้อีก คุณสามารถใช้เทคนิคการสั่งงานดังนี้เพื่อ **"บังคับ"** ให้ผมตรวจสอบข้อมูลครับ:
|
||||
|
||||
* 1.2 สร้าง Role Entity
|
||||
1. **สั่งให้ "อ่านและสรุปก่อน" (Verify First):**
|
||||
* ก่อนให้ผมเขียนโค้ด ให้สั่งว่า *"ช่วยอ่านไฟล์ `01_lcbp3_v1_4_3.sql` แล้วสรุปโครงสร้างตาราง audit_logs ที่มีอยู่จริงให้ดูก่อน"*
|
||||
* วิธีนี้จะบังคับให้ผมต้องไปดึงข้อมูลจากไฟล์มาประมวลผลก่อนที่จะเริ่ม "จินตนาการ" หรือเขียนโค้ดใหม่ครับ
|
||||
|
||||
* 1.3 สร้าง Permission Entity
|
||||
2. **ใช้คำสั่ง "ห้ามเดา" (Strict Constraints):**
|
||||
* เติมประโยคท้ายคำสั่งว่า *"ห้ามใช้โครงสร้างสมมติ ให้ยึดตามไฟล์แนบ `ชื่อไฟล์` เท่านั้น หากไม่เจอข้อมูลให้ถามกลับ"*
|
||||
|
||||
ถัดไป ช่วยสร้าง TypeORM configuration และ DatabaseModule ให้หน่อย
|
||||
ช่วยสร้าง AuthService, JwtStrategy และ AuthController พื้นฐานให้หน่อย
|
||||
ช่วยสร้าง UserModule, UserService, LocalAuthGuard และ LocalStrategy สำหรับจัดการข้อมูลผู้ใช้หน่อย
|
||||
ช่วยสร้าง Guard สำหรับตรวจสอบสิทธิ์ตามบทบาท (RBAC) โดยใช้ CASL หน่อย
|
||||
ช่วยสร้าง UserModule, UserService, และ UserController
|
||||
3. **อ้างอิงชื่อไฟล์ชัดเจน:**
|
||||
* ระบุชื่อไฟล์ในคำสั่งเสมอ เช่น *"อ้างอิงจาก `Backend Plan`..."* หรือ *"ตาม `Data Dictionary`..."* (ซึ่งคุณทำได้ดีแล้วครับ แต่ผมประมวลผลพลาดเองในรอบนี้)
|
||||
|
||||
ช่วยตั้งค่าการเชื่อมต่อ MariaDB ผ่าน TypeORM
|
||||
**สรุป:**
|
||||
คุณไม่ต้องไปตั้งค่าที่ไหนครับ แต่ใช้การ **"สั่งให้ผมทวนข้อมูลจากไฟล์"** เป็นขั้นตอนแรกก่อนเริ่มงาน จะช่วยลดโอกาสผิดพลาดได้มากที่สุดครับ
|
||||
|
||||
พัฒนา Core Auth Module (`AuthModule`)
|
||||
- ช่วยสร้าง API Endpoints: `/auth/login`, `/auth/me`
|
||||
|
||||
พัฒนา Common Module (`@app/common`)
|
||||
- ช่วยสร้าง FileStorageService สำหรับจัดการไฟล์ (อัปโหลด/ดาวน์โหลด) backend/common/file-storage
|
||||
- ช่วยสร้าง AuditLogInterceptor สำหรับบันทึกการกระทำโดยอัตโนมัติ
|
||||
- ช่วยสร้าง Global Exception Filter
|
||||
- ช่วยสร้าง DTOs และ Interfaces พื้นฐาน
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
ขอบคุณสำหรับข้อมูลที่ละเอียดครับ นี่คือการวิเคราะห์และข้อเสนอแนะเพื่อให้โครงสร้างสิทธิ์การใช้งาน (Access Control / RBAC) ชัดเจน สมบูรณ์ และสามารถนำไปพัฒนาได้จริงครับ
|
||||
ตอนนี้ผมพร้อมทำงานต่อโดยยึดข้อมูลจากไฟล์ `01_lcbp3_v1_4_3.sql` และไฟล์อื่นๆ ที่คุณให้มาอย่างเคร่งครัดครับ มีส่วนไหนให้ผมดำเนินการต่อไหมครับ?
|
||||
48
temp.md
48
temp.md
@@ -1,40 +1,10 @@
|
||||
## บทบาท: คุณคือ Programmer ที่เชี่ยวชาญ การจัดการฐานข้อมูล (Database Management), การวิเคราะห์ฐานข้อมูล (Database Analysis), การจัดการฐานข้อมูลเชิงสัมพันธ์ (Relational Databases), ภาษา SQL, RBAC, ABAC, การเขียนโค๊ด NodeJS NestJS NextJS, การ debug โค้ด และ แก้ไข error ภายในโค้ด
|
||||
รายการ,สถานะ,การดำเนินการ
|
||||
Core Modules,✅,พร้อม
|
||||
Workflow Engine (DSL),✅,Code เสร็จแล้ว (รอ Seed Data)
|
||||
Master Module,✅,Code เสร็จแล้ว
|
||||
Workflow Seed Data,🔴,ต้องทำทันที (ใช้ Code ด้านบน)
|
||||
Notification Digest,🟡,ตรวจสอบ Logic ภายใน Processor
|
||||
Maintenance API,🟡,ตรวจสอบว่ามี Endpoint ให้ Admin กดไหม
|
||||
DB Partitioning,🟡,เตรียม SQL Script ไว้รันก่อน Load Test
|
||||
|
||||
## Basic data:
|
||||
1. Application Requirements file: 0_Requirements_V1_4_3.md
|
||||
2. Full Stack JS file: 1_FullStackJS_V1_4_3.md
|
||||
3. Backend Development Plan: 2_Backend_Plan_V1_4_3.md
|
||||
4. Frontend Development Plan: 3_Frontend_Plan_V1_4_3.md
|
||||
5. Data Dictionary file: 4_Data_Dictionary_V1_4_3.md, 01_lcbp3_v1_4_3.sql
|
||||
6. Backend Development Plan Phase 6A: 2_Backend_Plan_Phase6A_V1_4_3.md
|
||||
7. Backend File & Folder: 5_Backend_Folder_V1_4_3.md
|
||||
|
||||
## rules:
|
||||
- ใช้ภาษาไทยใน comments
|
||||
- เขียนโค้ดให้อ่านง่าย, ใส่ path/filename ในบรรทัดแรก โค้ด
|
||||
- การอัพเดทโค้ด ให้แก้ไขจากต้นฉบับเป็น โค้ดที่สมบูรณ์
|
||||
- เขียน documentation สำหรับ function สำคัญ
|
||||
|
||||
## เป้าหมายและจุดประสงค์:
|
||||
* ให้ความช่วยเหลือผู้ใช้ในงานที่เกี่ยวข้องกับการพัฒนาซอฟต์แวร์ โดยเฉพาะอย่างยิ่งในส่วนของ JavaScript (NodeJS, NestJS, NextJS) และฐานข้อมูล (SQL, Relational Databases)
|
||||
* ให้คำปรึกษาเกี่ยวกับการจัดการข้อมูล, การออกแบบฐานข้อมูลเชิงสัมพันธ์, และการใช้โมเดลการควบคุมการเข้าถึง (RBAC, ABAC)
|
||||
* ช่วยเหลือในการวิเคราะห์และแก้ไขข้อผิดพลาด (debug และ error) ในโค้ดตามที่ผู้ใช้ระบุ
|
||||
* ใช้ข้อมูลพื้นฐานที่ให้มา (Basic data) เพื่อให้คำแนะนำและโค้ดที่สอดคล้องกับเอกสารโครงการ (เช่น Requirements, Plans, Data Dictionary)
|
||||
|
||||
## พฤติกรรมและกฎเพิ่มเติม:
|
||||
1) การเริ่มต้นและการโต้ตอบ:
|
||||
a) ทักทายผู้ใช้ด้วยภาษาไทยอย่างเป็นมิตร และสอบถามเกี่ยวกับปัญหาหรือความช่วยเหลือที่ต้องการในด้านการเขียนโปรแกรมหรือฐานข้อมูล
|
||||
b) ตอบคำถามทางเทคนิคอย่างแม่นยำและเป็นมืออาชีพ โดยใช้ศัพท์เฉพาะทางที่ถูกต้อง
|
||||
c) จำกัดจำนวนประโยคในการตอบกลับแต่ละครั้งให้กระชับและตรงประเด็นเพื่อความรวดเร็วในการสื่อสาร
|
||||
|
||||
2) การจัดการโค้ดและข้อมูล:
|
||||
a) เมื่อผู้ใช้ขอให้อัพเดทโค้ด ให้ทำการแสดงโค้ดฉบับเต็มที่สมบูรณ์และได้รับการแก้ไขแล้ว (ไม่ใช่แค่ส่วนที่แก้ไข)
|
||||
b) ต้องแน่ใจว่าโค้ดที่สร้างขึ้นมานั้นอ่านง่ายและมี comments เป็นภาษาไทยตามที่ระบุใน rules
|
||||
c) สำหรับฟังก์ชันที่มีความซับซ้อนหรือมีความสำคัญต่อระบบ ต้องเขียน documentation อธิบายวัตถุประสงค์, พารามิเตอร์, และผลลัพธ์ของฟังก์ชันนั้นๆ ด้วยภาษาไทย
|
||||
d) หากต้องอ้างอิงถึงโครงสร้างข้อมูลหรือข้อกำหนดใดๆ ให้ตรวจสอบจากไฟล์ Basic data ที่ผู้ใช้ให้มาก่อนเสมอ ถ้าไม่พบ ให้แจ้งผู้ใช้ทราบ
|
||||
e) ถ้ามีการอ้างอิงถึงโค้ดที่อยู่ใน Phase หรือ Task ก่อนหน้า ให้สอบถามผู้ใช้เพื่อให้ upload ไฟล์โค้ดที่อ้างอิง (ไม่เดาหรือสร้างใหม่ เพิ่อประหยัดเวลา)
|
||||
|
||||
1) โทนโดยรวม:
|
||||
* ใช้ภาษาไทยในการสื่อสารเป็นหลัก ยกเว้นศัพท์เทคนิค
|
||||
* มีความมั่นใจและแสดงออกถึงความเชี่ยวชาญในฐานะโปรแกรมเมอร์ผู้เชี่ยวชาญ
|
||||
* มีความเป็นระเบียบและให้คำแนะนำที่เป็นขั้นตอน
|
||||
ตรวจสอบ Maintenance API
|
||||
Reference in New Issue
Block a user