คู่มือติดตั้งและเชื่อมต่อ

การ deploy ระบบขึ้น server และการเชื่อมต่อจาก API SMT Front / API OPA Front
1. สถาปัตยกรรมระบบ
[Client ภายนอก]
      │
      ▼
https://pvd.ktam.co.th
      │
      ├── /WSSMTFront  → API SMT Front ──→ API SMT Back   (Back อยู่ภายใน ห้ามออกตรง)
      └── /OPA_SMT     → API OPA Front ──→ API OPA Back
                              │   ▲
        push log / เช็คสถานะ   │   │   ตอบสถานะเปิด-ปิด
                              ▼   │
                    KTAM-API-CENTER (ระบบนี้)
  • KTAM-API-CENTER เรียกออกภายนอกได้เฉพาะ https://pvd.ktam.co.th เท่านั้น (ใช้ตอน health check)
  • ฝั่ง Back ห้ามเรียก API-CENTER โดยตรง — Front เป็นผู้ relay log ของ Back และส่งต่อสถานะเปิด/ปิดให้ Back
  • ไม่ใช้ database — ทะเบียน API เก็บที่ App_Data/apis.json และ log เก็บที่ App_Data/logs/yyyyMMdd.jsonl
2. ขึ้น server แล้วต้องเปลี่ยน path หรือไม่?
ไม่ต้องเปลี่ยน path ของ endpoint ที่ลงทะเบียนไว้ — ทั้ง 836 เส้นเก็บเป็น path สัมพัทธ์ (เช่น /api/SMT/setcustomerchangepassword_v2) แยกจาก BaseUrl ดังนั้น URL จริง = BaseUrl + path เหมือนเดิมทุกสภาพแวดล้อม
สิ่งที่อาจต้องปรับเมื่อ deploy มีเพียง 2 จุด:
กรณีสิ่งที่ต้องแก้แก้ที่ไหน
ชื่อ IIS application ของ Front ไม่ตรงกับที่ตั้งไว้ (ปัจจุบัน WSSMTFront และ OPA_SMT) แก้ BaseUrl ของ service นั้น App_Data/apis.json ของ API-CENTER
URL ของ API-CENTER บน server (เช่น ย้ายจาก localhost ไป server จริง) แก้ URL ที่ Front ใช้เรียกเข้ามา (เช็คสถานะ / push log) appsettings.json ของ SMT Front และ OPA Front
ฝั่ง Back ไม่ต้องแก้อะไรเลย เพราะ API-CENTER ไม่เรียก Back ตรง และ Back ไม่เรียก API-CENTER ตรง
3. การติดตั้ง KTAM-API-CENTER บน server (IIS)
3.1 เตรียมเครื่อง server
  • ติดตั้ง .NET 10 Hosting Bundle (ASP.NET Core Runtime + IIS Module) แล้ว restart IIS
  • เปิด role IIS พร้อม WebSocket ไม่จำเป็น — ระบบใช้ HTTP ปกติ
3.2 Publish โปรเจค
cd C:\Nilecon\Project\KTAM\CENTER-API\CENTER-API
dotnet publish -c Release -o C:\publish\KTAM-API-CENTER
โฟลเดอร์ publish จะมี App_Data/apis.json (ทะเบียน API ทั้ง 836 เส้น) ติดไปด้วยอัตโนมัติ
3.3 สร้าง site บน IIS
  • สร้าง Application Pool ใหม่ → .NET CLR = No Managed Code
  • สร้าง Website/Application ชี้ physical path ไป C:\publish\KTAM-API-CENTER
  • สำคัญ: ให้สิทธิ์ IIS_IUSRS (หรือ identity ของ App Pool) Modify ที่โฟลเดอร์ App_Data เพราะระบบเขียนไฟล์ log และบันทึกสถานะเปิด/ปิดลงไฟล์
  • แนะนำติดตั้งเป็นระบบภายใน (intranet) — หน้าเว็บนี้ไม่ควรเปิด public
3.4 ทดสอบหลังติดตั้ง
# เปิด browser ไปที่หน้า Dashboard
http://<server>:<port>/

# ทดสอบ REST API
GET http://<server>:<port>/api/center/status
→ ต้องได้ JSON รายการ 4 services
4. ค่า config ที่ต้องตรวจสอบ (appsettings.json ของ API-CENTER)
"CenterApi": {
  "SystemName": "KTAM-API-CENTER",
  "GatewayEndpoint": "https://pvd.ktam.co.th",   // host เดียวที่อนุญาตให้ health check ออก
  "DataFolder": "App_Data",                       // ที่เก็บ apis.json และ log
  "LogRetentionDays": 30,                         // เก็บ log กี่วัน (เกินแล้วลบอัตโนมัติ)
  "MaxBodyLength": 20000                          // ตัด body ที่ยาวเกิน (ตัวอักษร)
}
5. การเชื่อมต่อจากฝั่ง Front (SMT Front / OPA Front)
ติดตั้งให้แล้วทั้ง 4 โปรเจค — เพิ่ม ApiCenter/ApiCenterMiddleware.cs + section "ApiCenter" ใน appsettings.json ของ WSSMTFront, WSSMTBack, API_OPA_FRONT, API_OPA_BACK และเพิ่ม Controllers/CenterRelayController.cs ใน Front ทั้งสองสำหรับ relay ฝั่ง Back สิ่งเดียวที่ต้องแก้ตอน deploy คือค่า ApiCenter:BaseUrl ให้ตรงกับ server จริง (middleware เป็นแบบ fail-open — ถ้า center ล่มหรือ URL ผิด ระบบเดิมทำงานตามปกติ)
โปรเจคModeServiceIdBaseUrl ชี้ไปที่
WSSMTFrontdirectsmt-frontKTAM-API-CENTER (เช่น https://uat-pvd.ktam.co.th/APICENTER)
WSSMTBackrelaysmt-backSMT Front (https://uat-pvd.ktam.co.th/WSSMTFront)
API_OPA_FRONTdirectopa-frontKTAM-API-CENTER (เช่น https://uat-pvd.ktam.co.th/APICENTER)
API_OPA_BACKrelayopa-backOPA Front (https://uat-pvd.ktam.co.th/OPA_SMT)
รายละเอียด config ใน appsettings.json ของโปรเจค Front:
"ApiCenter": {
  "BaseUrl": "http://<server-api-center>:<port>",
  "ServiceId": "smt-front",        // opa-front สำหรับ OPA
  "BackServiceId": "smt-back",     // opa-back สำหรับ OPA
  "StatusCacheSeconds": 30
}
5.1 เช็คสถานะเปิด/ปิดก่อนให้บริการ — GET /api/center/status/{serviceId}?path=...
// ตัวอย่าง: เรียกตอนรับ request (ควร cache ผลไว้ตาม StatusCacheSeconds เพื่อไม่ให้หน่วง)
public async Task<bool> IsApiEnabledAsync(string path)
{
    var url = $"{_centerBaseUrl}/api/center/status/{_serviceId}?path={Uri.EscapeDataString(path)}";
    using var resp = await _httpClient.GetAsync(url);
    if (!resp.IsSuccessStatusCode) return true; // center ล่ม → เปิดให้บริการตามปกติ (fail-open)
    var json = JsonDocument.Parse(await resp.Content.ReadAsStringAsync());
    return json.RootElement.GetProperty("enabled").GetBoolean();
}

// ใน middleware หรือ action filter ของ Front
if (!await IsApiEnabledAsync(HttpContext.Request.Path))
{
    context.Response.StatusCode = 503;
    await context.Response.WriteAsJsonAsync(new { success = false, message = "API ปิดให้บริการชั่วคราว" });
    return;
}
5.2 ส่ง log หลังตอบ response — POST /api/center/logs
// แนะนำยิงแบบ fire-and-forget เพื่อไม่หน่วง response
public void PushLog(HttpContext ctx, int statusCode, long durationMs,
                    string requestBody, string responseBody, string correlationId)
{
    var entry = new
    {
        serviceId = _serviceId,                       // "smt-front" หรือ "opa-front"
        method = ctx.Request.Method,
        path = ctx.Request.Path.Value,
        statusCode,
        durationMs,
        clientIp = ctx.Connection.RemoteIpAddress?.ToString(),
        correlationId,
        requestBody,                                   // ควร mask ข้อมูล sensitive เช่น password/pin
        responseBody
    };
    _ = _httpClient.PostAsJsonAsync($"{_centerBaseUrl}/api/center/logs", entry); // ไม่ await
}
คำแนะนำด้านความปลอดภัย: mask ค่า password, PIN, เลขบัตร, token ใน requestBody/responseBody ก่อนส่งมาเก็บที่ API-CENTER เสมอ
6. การส่ง log ของฝั่ง Back (relay ผ่าน Front)
ติดตั้งให้แล้ว: Back ทั้งสอง (WSSMTBack, API_OPA_BACK) ใช้ middleware ตัวเดียวกันในโหมด relay — เช็คสถานะและส่ง log ผ่านเส้น /api/CenterRelay/* ของ Front ซึ่ง Front จะ forward ต่อไปยัง KTAM-API-CENTER ให้อัตโนมัติ
หลักการทำงานของ relay:
  • แบบที่ 1 (แนะนำ): Front เป็นคนบันทึก log แทน Back — Front รู้อยู่แล้วว่าเรียก Back เส้นไหน ได้ status อะไร ใช้เวลาเท่าไร ให้ Front ส่ง log 2 รายการต่อ 1 request (ของตัวเอง serviceId=smt-front และของ Back serviceId=smt-back)
  • แบบที่ 2: Back เก็บ log ลงไฟล์/ตารางชั่วคราวของตัวเอง แล้วให้ Front มีตัว job ดึงไป relay เป็นชุดผ่าน POST /api/center/logs/batch
// ตัวอย่างแบบที่ 1 — ใน Front หลังเรียก Back เสร็จ
var backLog = new
{
    serviceId = _backServiceId,               // "smt-back" หรือ "opa-back"
    method = "POST",
    path = "/api/smt/setcustomerchangepassword_v2",   // path ของ Back ที่เรียก
    statusCode = (int)backResponse.StatusCode,
    durationMs = backElapsedMs,
    correlationId,                             // ใช้ตัวเดียวกับ log ของ Front เพื่อ trace ข้ามชั้นได้
    requestBody = maskedBackRequest,
    responseBody = maskedBackResponse
};
_ = _httpClient.PostAsJsonAsync($"{_centerBaseUrl}/api/center/logs", backLog);

// ตัวอย่างแบบที่ 2 — batch relay
// POST {center}/api/center/logs/batch   body: [ {log1}, {log2}, ... ]
การเปิด/ปิดของ Back ก็เช่นกัน — Front ดึงสถานะ smt-back/opa-back จาก GET /api/center/status แล้วตัดสินใจไม่ส่งต่อไป Back เมื่อถูกปิด
7. Checklist ก่อนใช้งานจริง
  • ☐ ติดตั้ง .NET 10 Hosting Bundle บน server และ restart IIS แล้ว
  • ☐ App Pool = No Managed Code และ IIS_IUSRS มีสิทธิ์ Modify ที่ App_Data
  • ☐ เปิด GET /api/center/status แล้วเห็น 4 services ครบ
  • BaseUrl ใน apis.json ตรงกับชื่อ IIS application จริงของ Front (WSSMTFront, OPA_SMT)
  • ☐ ตั้งค่า ApiCenter:BaseUrl ใน appsettings ของ SMT Front / OPA Front ชี้มาที่ server API-CENTER
  • ☐ ทดสอบปิด API 1 เส้นจากหน้า "จัดการ API" แล้วยิงเส้นนั้น → Front ต้องตอบ 503
  • ☐ ยิง request ทดสอบแล้วเห็น log ขึ้นในหน้า "Log Request/Response"
  • ☐ ข้อมูล sensitive (password/PIN/token) ถูก mask ก่อนส่ง log
  • ☐ กดปุ่ม "ตรวจสถานะปลายทาง" ในหน้าจัดการ API แล้ว Front ทั้งสองตอบกลับ