690419:1831 feat: update CI/CD to use SSH key authentication #05
CI / CD Pipeline / build (push) Failing after 4m57s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
2026-04-19 18:31:30 +07:00
parent 733f3c3987
commit 13745e5874
61 changed files with 6709 additions and 1241 deletions
@@ -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
@@ -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.