SiamCafe.net Blog
Cybersecurity

Qwik Resumability Audit Trail Logging

qwik resumability audit trail logging
Qwik Resumability Audit Trail Logging | SiamCafe Blog
2026-01-28· อ. บอม — SiamCafe.net· 11,169 คำ

Qwik Audit Trail

Qwik Resumability Audit Trail Logging server$ routeAction$ Middleware Structured Logging Compliance SOC 2 ISO 27001 PDPA Immutable

Event TypeActionLog FieldsCompliance
Authenticationlogin logout registeractor_id ip user_agent resultAll Standards
Data CRUDcreate read update deleteresource_type resource_id changesSOC 2 ISO PDPA
Permissiongrant revoke change_roletarget_user old_role new_roleSOC 2 ISO
Settingsconfig_change feature_togglesetting_key old_value new_valueSOC 2
API Accessapi_call webhookendpoint method status_codeAll Standards

Database Schema & Implementation

# === Audit Trail Database Schema ===

# CREATE TABLE audit_logs (
#     id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
#     timestamp TIMESTAMPTZ NOT NULL DEFAULT now(),
#     actor_id VARCHAR(255),
#     actor_ip INET,
#     actor_email VARCHAR(255),
#     session_id VARCHAR(255),
#     user_agent TEXT,
#     action VARCHAR(100) NOT NULL,
#     resource_type VARCHAR(100) NOT NULL,
#     resource_id VARCHAR(255),
#     result VARCHAR(20) NOT NULL,  -- success, failure, error
#     details JSONB,
#     correlation_id VARCHAR(255),
#     created_at TIMESTAMPTZ NOT NULL DEFAULT now()
# ) PARTITION BY RANGE (timestamp);
#
# -- Partitions by month
# CREATE TABLE audit_logs_2024_01 PARTITION OF audit_logs
#     FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
#
# -- Indexes
# CREATE INDEX idx_audit_actor ON audit_logs (actor_id, timestamp);
# CREATE INDEX idx_audit_resource ON audit_logs (resource_type, resource_id);
# CREATE INDEX idx_audit_action ON audit_logs (action, timestamp);
# CREATE INDEX idx_audit_correlation ON audit_logs (correlation_id);

from dataclasses import dataclass, field
from datetime import datetime

@dataclass
class AuditLogEntry:
    timestamp: str
    actor_id: str
    actor_ip: str
    action: str
    resource_type: str
    resource_id: str
    result: str
    details: dict = field(default_factory=dict)

# Example audit entries for a Qwik e-commerce app
entries = [
    AuditLogEntry("2024-01-15T10:00:00Z", "user_123", "203.0.113.50",
        "auth.login", "session", "sess_abc", "success",
        {"method": "password", "mfa": True}),
    AuditLogEntry("2024-01-15T10:05:00Z", "user_123", "203.0.113.50",
        "order.create", "order", "ord_456", "success",
        {"total": 1500, "items": 3, "payment": "credit_card"}),
    AuditLogEntry("2024-01-15T10:10:00Z", "admin_001", "10.0.0.5",
        "user.update_role", "user", "user_789", "success",
        {"old_role": "user", "new_role": "admin", "reason": "promotion"}),
    AuditLogEntry("2024-01-15T10:15:00Z", "user_456", "198.51.100.20",
        "product.delete", "product", "prod_321", "failure",
        {"error": "permission_denied", "required_role": "admin"}),
]

print("=== Audit Log Entries ===")
for e in entries:
    print(f"\n  [{e.timestamp}] {e.action} → {e.result}")
    print(f"    Actor: {e.actor_id} ({e.actor_ip})")
    print(f"    Resource: {e.resource_type}/{e.resource_id}")
    print(f"    Details: {e.details}")

Qwik Middleware

# === Qwik City Audit Middleware ===

# // src/routes/layout.tsx - Global Audit Middleware
# import { type RequestHandler } from '@builder.io/qwik-city';
#
# export const onRequest: RequestHandler = async ({ request, next, sharedMap }) => {
#   const correlationId = crypto.randomUUID();
#   sharedMap.set('correlationId', correlationId);
#   sharedMap.set('requestStart', Date.now());
#
#   await next();
#
#   // Log after response
#   const duration = Date.now() - (sharedMap.get('requestStart') as number);
#   console.log(JSON.stringify({
#     timestamp: new Date().toISOString(),
#     correlation_id: correlationId,
#     method: request.method,
#     url: request.url,
#     duration_ms: duration,
#     user_agent: request.headers.get('user-agent'),
#   }));
# };

# // src/lib/audit.ts - Audit Logger
# import { server$ } from '@builder.io/qwik-city';
#
# export const auditLog = server$(async function(params: {
#   action: string;
#   resourceType: string;
#   resourceId: string;
#   result: 'success' | 'failure';
#   details?: Record;
# }) {
#   const session = this.sharedMap.get('session');
#   await db.insert('audit_logs', {
#     actor_id: session?.userId,
#     actor_ip: this.request.headers.get('x-forwarded-for'),
#     correlation_id: this.sharedMap.get('correlationId'),
#     ...params,
#   });
# });

@dataclass
class MiddlewareConfig:
    hook: str
    purpose: str
    data_captured: str
    example: str

configs = [
    MiddlewareConfig("onRequest",
        "ดักจับทุก Request เข้า Server",
        "method url timestamp duration user_agent correlation_id",
        "Global Audit Logging ทุก Route"),
    MiddlewareConfig("routeAction$",
        "ดักจับ Form Submission / Mutation",
        "action resource changes result actor_id",
        "order.create user.update product.delete"),
    MiddlewareConfig("routeLoader$",
        "ดักจับ Data Loading (อ่าน)",
        "resource_type query_params result actor_id",
        "user.list order.detail product.search"),
    MiddlewareConfig("server$",
        "ดักจับ Server Function Call",
        "function_name params result actor_id",
        "Custom API calls, Business Logic"),
]

print("=== Middleware Configs ===")
for c in configs:
    print(f"  [{c.hook}] {c.purpose}")
    print(f"    Data: {c.data_captured}")
    print(f"    Example: {c.example}")

Monitoring & Compliance

# === Audit Monitoring & Compliance ===

@dataclass
class ComplianceCheck:
    standard: str
    requirement: str
    implementation: str
    monitoring: str

compliance = [
    ComplianceCheck("SOC 2 (CC6.1)",
        "Log all access to systems",
        "onRequest middleware logs ทุก Request, routeAction$ logs ทุก Mutation",
        "Dashboard: Request Count per User, Failed Auth Attempts"),
    ComplianceCheck("ISO 27001 (A.12.4)",
        "Event logging and monitoring",
        "Structured JSON Logs → ELK Stack, Retention 1 ปี",
        "Alert: Unusual Activity, Mass Delete, Permission Change"),
    ComplianceCheck("PDPA (Section 37)",
        "Record personal data processing",
        "Log ทุกครั้งที่ Access ข้อมูลส่วนบุคคล resource_type=user",
        "Report: Data Access Log per User สำหรับ Data Subject Request"),
    ComplianceCheck("PCI-DSS (10.2)",
        "Audit trail for payment",
        "Log ทุก Access ไป Order Payment Transaction",
        "Alert: Payment Data Access นอกเวลาทำการ"),
    ComplianceCheck("Immutability",
        "Log ห้ามแก้ไข ลบ",
        "PostgreSQL ไม่มี UPDATE DELETE Policy + S3 Object Lock Archive",
        "Alert: ถ้ามีความพยายาม DELETE audit_logs"),
]

print("=== Compliance Checks ===")
for c in compliance:
    print(f"\n  [{c.standard}] {c.requirement}")
    print(f"    Implementation: {c.implementation}")
    print(f"    Monitoring: {c.monitoring}")

เคล็ดลับ

Audit Trail ใน Qwik คืออะไร

บันทึกทุก Action server$ routeAction$ ใครทำอะไรเมื่อไหร่ Resumability Server-side Log ELK Loki Compliance SOC 2 ISO PDPA

ออกแบบ Schema อย่างไร

UUID timestamp actor_id actor_ip action resource_type resource_id result details JSONB Partition Month Index Immutable No UPDATE DELETE

Implement อย่างไร

onRequest Middleware routeAction$ server$ auditLog function Structured JSON Correlation ID Message Queue Async Unit Test Middleware Pattern

Compliance ต้องทำอะไร

SOC 2 ISO 27001 PDPA PCI-DSS GDPR Immutable Log Retention 1-7 ปี Access Control Review Monitoring Alert Dashboard Report

สรุป

Qwik Audit Trail server$ routeAction$ Middleware Structured JSON Correlation ID PostgreSQL Partition Immutable Compliance SOC 2 ISO PDPA ELK

📖 บทความที่เกี่ยวข้อง

BigQuery Scheduled Query Audit Trail Loggingอ่านบทความ → DNSSEC Implementation Audit Trail Loggingอ่านบทความ → Apache Kafka Streams Audit Trail Loggingอ่านบทความ → Qwik Resumability IoT Gatewayอ่านบทความ → Datadog APM Audit Trail Loggingอ่านบทความ →

📚 ดูบทความทั้งหมด →