690419:1831 feat: update CI/CD to use SSH key authentication #05
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
-- Delta 08: ADR-022 RAG — เพิ่ม rag_status และ rag_last_error ในตาราง attachments
|
||||
-- Apply: 2026-04-19
|
||||
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/data-model.md §1.1
|
||||
|
||||
ALTER TABLE attachments
|
||||
ADD COLUMN rag_status ENUM('PENDING', 'PROCESSING', 'INDEXED', 'FAILED')
|
||||
NOT NULL DEFAULT 'PENDING'
|
||||
COMMENT 'สถานะ RAG ingestion ระดับ file',
|
||||
ADD COLUMN rag_last_error TEXT NULL
|
||||
COMMENT 'Error message ล่าสุดเมื่อ rag_status = FAILED',
|
||||
ADD INDEX idx_attachments_rag_status (rag_status);
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Delta 08b: ADR-022 RAG — สร้างตาราง document_chunks สำหรับเก็บ vector metadata
|
||||
-- Apply: 2026-04-19
|
||||
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/data-model.md §1.2
|
||||
|
||||
CREATE TABLE document_chunks (
|
||||
id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID = Qdrant point ID',
|
||||
document_id CHAR(36) NOT NULL COMMENT 'FK → attachments.public_id (UUIDv7)',
|
||||
chunk_index INT NOT NULL COMMENT 'ลำดับ chunk ภายใน document',
|
||||
content TEXT NOT NULL COMMENT 'เนื้อหา chunk หลัง PyThaiNLP normalize',
|
||||
doc_type VARCHAR(20) NOT NULL COMMENT 'CORR, RFA, DRAWING, CONTRACT, RPT, TRANS',
|
||||
doc_number VARCHAR(100) NULL COMMENT 'หมายเลขเอกสาร เช่น REF-2026-001',
|
||||
revision VARCHAR(20) NULL COMMENT 'Revision เช่น Rev.A',
|
||||
project_code VARCHAR(50) NOT NULL COMMENT 'รหัสโครงการ (ใช้ filter)',
|
||||
project_public_id CHAR(36) NOT NULL COMMENT 'UUIDv7 ของโครงการ (Qdrant tenant key)',
|
||||
version VARCHAR(20) NULL COMMENT 'เวอร์ชันเอกสาร เช่น 1.0, 2.1 (ถ้ามี)',
|
||||
classification ENUM('PUBLIC', 'INTERNAL', 'CONFIDENTIAL')
|
||||
NOT NULL DEFAULT 'INTERNAL',
|
||||
embedding_model VARCHAR(100) NOT NULL DEFAULT 'nomic-embed-text',
|
||||
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
|
||||
INDEX idx_chunks_document_id (document_id),
|
||||
INDEX idx_chunks_doc_number_rev (doc_number, revision),
|
||||
INDEX idx_chunks_project (project_public_id),
|
||||
FULLTEXT INDEX ft_chunks_content (content)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Delta 08c: ADR-022 RAG — เพิ่ม permissions สำหรับ RAG feature
|
||||
-- Apply: 2026-04-19
|
||||
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/tasks.md T014
|
||||
|
||||
INSERT IGNORE INTO permissions (permission_name, description, module, created_at, updated_at)
|
||||
VALUES
|
||||
('rag.query', 'ใช้งาน RAG Q&A เพื่อค้นหาคำตอบจากเอกสาร', 'rag', NOW(), NOW()),
|
||||
('rag.manage', 'จัดการ RAG ingestion, re-index, ลบ vectors', 'rag', NOW(), NOW());
|
||||
@@ -1 +1 @@
|
||||
GRAFANA_ADMIN_PASSWORD=
|
||||
GRAFANA_ADMIN_PASSWORD=Center#2025
|
||||
|
||||
+1
-1
@@ -77,7 +77,7 @@ services:
|
||||
GF_SERVER_ROOT_URL: 'https://grafana.np-dms.work'
|
||||
GF_INSTALL_PLUGINS: grafana-clock-panel,grafana-piechart-panel
|
||||
ports:
|
||||
- '3000:3000'
|
||||
- '3003:3000'
|
||||
networks:
|
||||
- lcbp3
|
||||
volumes:
|
||||
|
||||
@@ -5,14 +5,94 @@
|
||||
# วิธีใช้ (บน QNAP):
|
||||
# cp /share/np-dms/.env.master /share/np-dms/app/.env
|
||||
# chmod 600 /share/np-dms/app/.env
|
||||
|
||||
# --- ใช้โดย docker-compose-app.yml ---
|
||||
DB_PASSWORD=
|
||||
REDIS_PASSWORD=
|
||||
# File: .env (Unified for QNAP / Gitea Runner)
|
||||
# Change Log: 2026-04-19
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 1. Backend Service Configuration
|
||||
# ---------------------------------------------------------
|
||||
TZ=Asia/Bangkok
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
|
||||
# --- Database (MariaDB) ---
|
||||
DB_HOST=mariadb
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=lcbp3
|
||||
DB_USERNAME=center
|
||||
DB_PASSWORD=Center#2025
|
||||
|
||||
# --- Redis (Cache & Queue) ---
|
||||
REDIS_HOST=cache
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=redis3ac466bf9b6
|
||||
|
||||
# --- Search (Elasticsearch) ---
|
||||
ELASTICSEARCH_HOST=search
|
||||
ELASTICSEARCH_PORT=9200
|
||||
ELASTICSEARCH_USERNAME=elastic
|
||||
ELASTICSEARCH_PASSWORD=
|
||||
JWT_SECRET=
|
||||
JWT_REFRESH_SECRET=
|
||||
AUTH_SECRET=
|
||||
ELASTICSEARCH_PASSWORD=elasticed0bbde94
|
||||
|
||||
# --- Security (JWT) ---
|
||||
JWT_SECRET=jwtsecret65adde8c76c6a0847d9649b2b67a06db1504693e6c912e51499b76e
|
||||
JWT_EXPIRATION=24h
|
||||
JWT_REFRESH_SECRET=jwtrefreshf6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
|
||||
|
||||
# --- Numbering Logic ---
|
||||
NUMBERING_LOCK_TIMEOUT=5000
|
||||
NUMBERING_RESERVATION_TTL=300
|
||||
|
||||
# --- File Storage ---
|
||||
UPLOAD_TEMP_DIR=/share/np-dms-as/data/uploads/temp
|
||||
UPLOAD_PERMANENT_DIR=/share/np-dms-as/data/uploads/permanent
|
||||
MAX_FILE_SIZE=52428800
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# 2. Frontend Service Configuration
|
||||
# ---------------------------------------------------------
|
||||
# หมายเหตุ: ค่าเหล่านี้จะถูกใช้ตอน Docker Build (ตาม deploy.sh)
|
||||
NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api
|
||||
AUTH_URL=https://lcbp3.np-dms.work
|
||||
|
||||
# --- NextAuth ---
|
||||
# ค่านี้ต้องตรงกับ JWT_SECRET หรือตั้งแยกตามความปลอดภัย
|
||||
AUTH_SECRET=jwtsecret65adde8c76c6a0847d9649b2b67a06db1504693e6c912e51499b76e
|
||||
AUTH_TRUST_HOST=true
|
||||
|
||||
# --- Shared Context ---
|
||||
INTERNAL_API_URL=http://backend:3000/api
|
||||
HOSTNAME=0.0.0.0
|
||||
|
||||
# --- Docker Image ---
|
||||
BACKEND_IMAGE_TAG=latest
|
||||
FRONTEND_IMAGE_TAG=latest
|
||||
|
||||
# ClamAV
|
||||
CLAMAV_HOST=localhost
|
||||
CLAMAV_PORT=3310
|
||||
|
||||
# ========================================
|
||||
# ADR-022 RAG — Retrieval-Augmented Generation
|
||||
# ========================================
|
||||
|
||||
# Qdrant vector store (local docker-compose or QNAP)
|
||||
QDRANT_URL=http://localhost:6333
|
||||
|
||||
# Ollama (Admin Desktop Desk-5439 — ADR-018 AI boundary)
|
||||
OLLAMA_EMBED_MODEL=nomic-embed-text
|
||||
OLLAMA_RAG_MODEL=gemma3:12b
|
||||
OLLAMA_URL=http://192.168.20.200:11434
|
||||
|
||||
# Thai preprocessing microservice (PyThaiNLP — Admin Desktop)
|
||||
THAI_PREPROCESS_URL=http://192.168.20.200:8765
|
||||
|
||||
# Typhoon API (cloud LLM — PUBLIC/INTERNAL only, never CONFIDENTIAL)
|
||||
TYPHOON_API_KEY=your-typhoon-api-key-here
|
||||
TYPHOON_API_URL=https://api.opentyphoon.ai/v1
|
||||
|
||||
# RAG query config
|
||||
RAG_TOPK=20
|
||||
RAG_FINAL_K=5
|
||||
RAG_TIMEOUT_MS=5000
|
||||
RAG_QUERY_CACHE_TTL=300
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Per-stack .env.example — n8n + postgres + tika + docker-socket-proxy
|
||||
N8N_DB_PASSWORD=
|
||||
N8N_ENCRYPTION_KEY=
|
||||
N8N_DB_PASSWORD=Np721220$
|
||||
N8N_ENCRYPTION_KEY=9AAIB7Da9DW1qAhJE5/Bz4SnbQjeAngI
|
||||
|
||||
@@ -15,6 +15,7 @@ x-logging: &default_logging
|
||||
options:
|
||||
max-size: '10m'
|
||||
max-file: '5'
|
||||
name: lcbp3-n8n
|
||||
services:
|
||||
n8n-db:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
@@ -112,7 +113,9 @@ services:
|
||||
|
||||
n8n:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: n8nio/n8n:1.66.0
|
||||
build:
|
||||
context: ./n8n-custom
|
||||
dockerfile: Dockerfile
|
||||
container_name: n8n
|
||||
depends_on:
|
||||
n8n-db:
|
||||
@@ -163,6 +166,8 @@ services:
|
||||
EXECUTIONS_DATA_PRUNE: 'true'
|
||||
EXECUTIONS_DATA_MAX_AGE: 168
|
||||
# EXECUTIONS_DATA_PRUNE_TIMEOUT: 60
|
||||
# Storage Migration (fix deprecation warning)
|
||||
N8N_MIGRATE_FS_STORAGE_PATH: 'true'
|
||||
|
||||
ports:
|
||||
- '5678:5678'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM n8nio/n8n:latest-debian
|
||||
FROM n8nio/n8n:2.16.1
|
||||
|
||||
USER root
|
||||
|
||||
@@ -6,6 +6,6 @@ USER root
|
||||
RUN echo "deb http://archive.debian.org/debian buster main" > /etc/apt/sources.list && \
|
||||
echo "deb http://archive.debian.org/debian-security buster/updates main" >> /etc/apt/sources.list && \
|
||||
apt-get update -y && \
|
||||
apt-get install -y poppler-utils
|
||||
apt-get install -y poppler-utils python3 python3-pip
|
||||
|
||||
USER node
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
# Per-stack .env.example — services (cache, search)
|
||||
# Source: ../../.env.template
|
||||
REDIS_PASSWORD=
|
||||
ELASTICSEARCH_PASSWORD=
|
||||
# --- Redis (Cache & Queue) ---
|
||||
REDIS_HOST=cache
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=redis3ac466bf9b6
|
||||
|
||||
# --- Search (Elasticsearch) ---
|
||||
ELASTICSEARCH_HOST=search
|
||||
ELASTICSEARCH_PORT=9200
|
||||
ELASTICSEARCH_USERNAME=elastic
|
||||
ELASTICSEARCH_PASSWORD=elasticed0bbde94
|
||||
|
||||
@@ -23,6 +23,7 @@ networks:
|
||||
lcbp3:
|
||||
external: true
|
||||
|
||||
name: lcbp3-services
|
||||
services:
|
||||
# ----------------------------------------------------------------
|
||||
# 1. Redis (Caching + Distributed Lock + BullMQ queues)
|
||||
@@ -30,13 +31,13 @@ services:
|
||||
# ----------------------------------------------------------------
|
||||
cache:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: redis:7-alpine
|
||||
image: redis:7-alpine # ใช้ Alpine image เพื่อให้มีขน
|
||||
container_name: cache
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 2G
|
||||
memory: 2G # Redis เป็น in-memory, ให้ memory เพียงพอต่อการ
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 512M
|
||||
@@ -80,12 +81,12 @@ services:
|
||||
# ----------------------------------------------------------------
|
||||
search:
|
||||
<<: [*restart_policy, *default_logging]
|
||||
image: elasticsearch:8.11.1
|
||||
image: elasticsearch:8.11.1 # แนะนำให้ระบุเวอร์ชันชัดเจน
|
||||
container_name: search
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2.0'
|
||||
cpus: '2.0' # Elasticsearch ใช้ CPU และ Memory ค่อนข้างห
|
||||
memory: 4G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
@@ -100,7 +101,7 @@ services:
|
||||
# NOTE: หากเปิด xpack.security ต้องตั้ง ELASTIC_PASSWORD และอัปเดต backend client config
|
||||
# ค่าเริ่มต้น keep ปิดไว้เพราะ network เข้าถึงได้เฉพาะภายใน lcbp3 (ไม่มี host port)
|
||||
xpack.security.enabled: 'false'
|
||||
# --- Performance ---
|
||||
# --- Performance กำหนด Heap size (1GB) ให้เหมาะสมกับ memory limit (4G ---
|
||||
ES_JAVA_OPTS: '-Xms1g -Xmx1g'
|
||||
ulimits:
|
||||
memlock:
|
||||
|
||||
@@ -144,13 +144,25 @@ cd frontend && pnpm test --run hooks/use-workflow-action
|
||||
- [x] T029 [US3] Wire `useWorkflowAction` into `IntegratedBanner` action buttons in `frontend/components/workflow/integrated-banner.tsx` — `onAction` callback receives `(action, comment, attachmentPublicIds[])` and delegates to hook's `execute()` method; show loading spinner during `isPending`
|
||||
- [x] T030 [US3] Add `WorkflowTransitionGuard` unit tests in `backend/src/modules/workflow-engine/guards/workflow-transition.guard.spec.ts` — test all RBAC levels: (1) Superadmin pass, (2) Org Admin same-org pass, (3) Level 2.5 contract membership — user org in same contract pass / cross-contract org → ForbiddenException, (4) Assigned Handler pass, (5) unauthorized user → ForbiddenException
|
||||
- [x] T031 [US3] Add extended `processTransition()` unit tests in `backend/src/modules/workflow-engine/workflow-engine.service.spec.ts` — test: attachments linked to correct historyId, non-committed attachment rejected, idempotent replay returns cached result
|
||||
- [x] T031a [US3] Add new unit tests in `workflow-engine.service.spec.ts` — 6 test cases — **DONE 2026-04-19** (15/15 tests passing):
|
||||
- [x] T031a [US3] Add new unit tests in `workflow-engine.service.spec.ts` — 7 test cases — **DONE 2026-04-19** (16/16 tests passing):
|
||||
- C3: upload in `APPROVED` state → `ConflictException` 409
|
||||
- C3: upload in `REJECTED` state → `ConflictException` 409
|
||||
- C3: skip state check when no attachments (backward compat)
|
||||
- C1: Redlock acquire fail → `ServiceUnavailableException` 503 (**ไม่ใช่ 409**)
|
||||
- C2: `affected < expected` → `WorkflowException` + rollback + Redlock release
|
||||
- **H1: TOCTOU state change between pre-check and pessimistic lock → `ConflictException` 409 + rollback** (code review 2026-04-19)
|
||||
- C1: Redlock release สำเร็จแม้ transition ไม่โยนค่า
|
||||
- [x] T031b [US3] **Code Review fixes 2026-04-19** — 9 issues resolved (H1 + M1-M3 + L1-L2 + S1-S3):
|
||||
- **H1** Backend: State check ซ้ำภายใน pessimistic lock (`workflow-engine.service.ts:419-429`) — ปิด TOCTOU race
|
||||
- **M1** Frontend: 403 handler ใช้ backend message แทน hardcoded string (`use-workflow-action.ts:80-86`)
|
||||
- **M2** Backend: Migrate `@nestjs/common` ConflictException + ServiceUnavailableException → custom `common/exceptions` (ADR-007 layered payload) — เพิ่ม `ErrorType.SERVICE_UNAVAILABLE` (503) + `ServiceUnavailableException` ใน `base.exception.ts`
|
||||
- **M3** Frontend: Reset idempotency key on 409 (`use-workflow-action.ts:71-78`) — preserve บน 503 เท่านั้น
|
||||
- **L1** k6 script: แทน remote `jslib.k6.io` import ด้วย `k6/crypto` built-in UUID v4 generator — ไม่ต้องมีอินเทอร์เน็ตตอนรัน
|
||||
- **L2** Backend: ลบ redundant `updatedContext` alias + `eventsToDispatch` outer declaration — ใช้ `context`/`evaluation.events` โดยตรง; ลบ unused `RawEvent` import
|
||||
- **S1** Backend: Prometheus metrics สำหรับ Redlock observability — `workflow_redlock_acquire_duration_ms` (Histogram labeled by outcome) + `workflow_redlock_acquire_failures_total` (Counter); register ใน module, inject via `@InjectMetric`
|
||||
- **S2** Backend: เพิ่ม comment ใน pre-check เพื่อชี้แจงว่า `WorkflowInstance.id` = CHAR(36) UUID direct PK (ไม่ใช่ UuidBaseEntity pattern)
|
||||
- **S3** Frontend: Harden `isApiErrorResponse` type guard ป้องกัน `{ error: null }` edge case
|
||||
- Tests: Backend 16/16 + Frontend 8/8 passing
|
||||
|
||||
**Checkpoint**: ✅ **VERIFIED 2026-04-19** — POST transition with `attachmentPublicIds` สำเร็จ; `attachment.workflow_history_id` ถูก set; duplicate `Idempotency-Key` → cached response; unauthorized user → 403; Upload in Terminal state → 409 (C3); Redis failure → 503 fail-closed (C1); temp/foreign attachment → rollback (C2). `workflow-engine.service.spec.ts`: 15/15 tests passing.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user