commit d1b72930b6f68bd0b37541810893d0e1dfca10d1 Author: Ittipat Lusuk Date: Mon Apr 27 09:55:10 2026 +0700 taobin_log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c1c7fc7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.9-slim + +WORKDIR /app + +ENV TZ=Asia/Bangkok + +RUN pip install fastapi uvicorn + +COPY ./app ./app + +RUN mkdir -p /app/data + +EXPOSE 8123 + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8123"] \ No newline at end of file diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..f623fd5 --- /dev/null +++ b/app/main.py @@ -0,0 +1,172 @@ +from fastapi import FastAPI, Query +from pydantic import BaseModel, Field +from typing import Optional, List, Dict, Any +from datetime import datetime, timedelta +import sqlite3 +import asyncio + +app = FastAPI(title="Log-Server") +DB_PATH = "/app/data/logs.db" +SERVICE_NAME = "log-service" + +def get_db(): + conn = sqlite3.connect(DB_PATH, check_same_thread=False) + conn.row_factory = sqlite3.Row + return conn + +def init_db(): + with get_db() as conn: + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS service_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + service_name TEXT NOT NULL, + log_level TEXT NOT NULL, + message TEXT NOT NULL, + timestamp DATETIME NOT NULL + ) + ''') + cursor.execute(''' + CREATE TABLE IF NOT EXISTS audit_logs ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + requester_name TEXT NOT NULL, + target_service TEXT NOT NULL, + query_start_time TEXT, + query_end_time TEXT, + request_at DATETIME DEFAULT (datetime('now', '+7 hours')) + ) + ''') + conn.commit() + +async def retention_policy_task(): + while True: + try: + limit_date = (datetime.utcnow() - timedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S') + with get_db() as conn: + cursor = conn.cursor() + cursor.execute("DELETE FROM service_logs WHERE timestamp < ?", (limit_date,)) + conn.commit() + print(f"Cleanup: Deleted logs older than {limit_date}") + except Exception as e: + print(f"Cleanup Error: {e}") + await asyncio.sleep(86400) + +@app.on_event("startup") +async def startup_event(): + init_db() + asyncio.create_task(retention_policy_task()) + +# class LogEntry(BaseModel): +# service_name: str = Field(..., example="AUTH-SERVICE") +# log_level: str = Field(..., example="INFO") +# message: str = Field(..., example="User logged in successfully") +# timestamp: str = Field(..., example="2024-05-20 14:30:05") + +class UserInfo(BaseModel): + displayName: str + email: str + uid: str + +class LogValues(BaseModel): + log_level: str + message: str + timestamp: str + +class LogPayloadPost(BaseModel): + user_info: UserInfo + srv_name: str + values: LogValues + +class LogPayloadGet(BaseModel): + user_info: UserInfo + srv_name: str + start_time: Optional[str] = None + end_time: Optional[str] = None + +class LogRequestPost(BaseModel): + type: str + payload: LogPayloadPost + +class LogRequestGet(BaseModel): + type: str + payload: LogPayloadGet + +@app.post("/add/logs", tags=["Logs Management"]) +async def post_log(req: LogRequestPost): + payload = req.payload + values = payload.values + + with get_db() as conn: + cursor = conn.cursor() + cursor.execute( + "INSERT INTO service_logs (service_name, log_level, message, timestamp) VALUES (?, ?, ?, ?)", + (payload.srv_name, values.log_level, values.message, values.timestamp) + ) + conn.commit() + + return { + "status": "success", + "message": "Log recorded successfully", + "entry_details": { + "service": payload.srv_name, + "level": values.log_level, + "logged_at": values.timestamp, + "user": payload.user_info.displayName + } + } + +@app.post("/get/logs", tags=["Logs Management"]) +async def get_logs(req: LogRequestGet): + payload = req.payload + + with get_db() as conn: + cursor = conn.cursor() + + # audit log + cursor.execute( + "INSERT INTO audit_logs (requester_name, target_service, query_start_time, query_end_time) VALUES (?, ?, ?, ?)", + ( + payload.user_info.displayName, + payload.srv_name, + payload.start_time, + payload.end_time + ) + ) + + query = "SELECT * FROM service_logs WHERE service_name = ?" + params = [payload.srv_name] + + if payload.start_time: + query += " AND datetime(timestamp) >= datetime(?)" + params.append(payload.start_time) + + if payload.end_time: + query += " AND datetime(timestamp) <= datetime(?)" + params.append(payload.end_time) + + query += " ORDER BY timestamp DESC" + + cursor.execute(query, params) + logs = [dict(row) for row in cursor.fetchall()] + + return { + "metadata": { + "requester": payload.user_info.displayName, + "target_service": payload.srv_name, + "total_found": len(logs) + }, + "results": logs + } + +@app.get("/audit-trail", tags=["Requester log"]) +async def view_audit(): + with get_db() as conn: + cursor = conn.cursor() + cursor.execute("SELECT id, requester_name, target_service, request_at FROM audit_logs ORDER BY request_at DESC") + audits = [dict(row) for row in cursor.fetchall()] + + return { + "system_status": "operational", + "requester_count": len(audits), + "history": audits + } \ No newline at end of file diff --git a/data/logs.db b/data/logs.db new file mode 100644 index 0000000..52a9bb1 Binary files /dev/null and b/data/logs.db differ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..fb7d6cf --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: '3.8' + +services: + server: + build: . + container_name: taobin-log-container + ports: + - "8123:8123" + volumes: + - ./data:/app/data + restart: always \ No newline at end of file