Merry99's picture
oracle DB Connection
cb442f8
raw
history blame
4.72 kB
import os
import json
from typing import List, Optional
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, conlist
import oracledb
# ----- ํ™˜๊ฒฝ ๋ณ€์ˆ˜ -----
DB_USER = os.environ["DB_USER"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
WALLET_DIR = os.environ["WALLET_DIR"]
WALLET_PASSWORD = os.environ["WALLET_PASSWORD"]
# tnsnames.ora ์•ˆ์˜ alias ํ™•์ธ (๋ณดํ†ต *_high)
TNS_ALIAS = os.environ.get("DB_TNS_ALIAS", "musclecare_high")
# ----- ์—ฐ๊ฒฐ ํ’€: Thin + mTLS (์ง€๊ฐ‘) -----
# ์ ˆ๋Œ€ ํ˜ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”: oracledb.init_oracle_client() # (Thick๋กœ ๋น ์ ธ์„œ ์‹คํŒจ ๊ฐ€๋Šฅ)
pool: oracledb.ConnectionPool = oracledb.create_pool(
user=DB_USER,
password=DB_PASSWORD,
dsn=TNS_ALIAS, # Wallet tnsnames.ora์˜ alias
config_dir=WALLET_DIR,
wallet_location=WALLET_DIR,
wallet_password=WALLET_PASSWORD,
min=1, max=4, increment=1,
homogeneous=True,
timeout=60,
retry_count=6, retry_delay=2
)
app = FastAPI(title="MuscleCare Hybrid Server (mTLS)")
# ----- ๋ชจ๋ธ -----
class StatePayload(BaseModel):
user_id: str
rms_base: Optional[float] = None
freq_base: Optional[float] = None
user_emb: Optional[List[float]] = Field(default=None, description="length=12")
model_version: Optional[str] = None
class LogItem(BaseModel):
user_id: str
session_id: Optional[str] = None
measure_date: str # 'YYYY-MM-DD'
rms: Optional[float] = None
freq: Optional[float] = None
fatigue: Optional[float] = None
mode: Optional[str] = Field(default="EMA", description="EMA/Hybrid/E2E")
window_count: Optional[int] = None
class LogsPayload(BaseModel):
items: conlist(LogItem, min_items=1)
# ----- ์œ ํ‹ธ -----
def clob_json(obj) -> str:
return json.dumps(obj, separators=(",", ":"), ensure_ascii=False)
# ----- ์—”๋“œํฌ์ธํŠธ -----
@app.get("/health")
def health():
try:
with pool.acquire() as conn:
with conn.cursor() as cur:
cur.execute("SELECT 1 FROM DUAL")
v = cur.fetchone()[0]
return {"ok": True, "db": v}
except Exception as e:
raise HTTPException(500, f"DB health check failed: {e}")
@app.post("/upload_state")
def upload_state(p: StatePayload):
# MERGE INTO MuscleCare.user_state
try:
emb_json = None
if p.user_emb is not None:
if len(p.user_emb) != 12:
raise HTTPException(400, "user_emb must have length=12")
emb_json = clob_json(p.user_emb)
with pool.acquire() as conn:
with conn.cursor() as cur:
cur.execute("""
MERGE INTO MuscleCare.user_state t
USING (
SELECT :user_id AS user_id FROM dual
) s
ON (t.user_id = s.user_id)
WHEN MATCHED THEN UPDATE SET
rms_base = :rms_base,
freq_base = :freq_base,
user_emb = :user_emb,
model_version = :model_version,
last_sync = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN INSERT
(user_id, rms_base, freq_base, user_emb, model_version, last_sync)
VALUES
(:user_id, :rms_base, :freq_base, :user_emb, :model_version, CURRENT_TIMESTAMP)
""", dict(
user_id=p.user_id,
rms_base=p.rms_base,
freq_base=p.freq_base,
user_emb=emb_json,
model_version=p.model_version
))
conn.commit()
return {"ok": True}
except HTTPException:
raise
except Exception as e:
raise HTTPException(500, f"upload_state failed: {e}")
@app.post("/upload_logs")
def upload_logs(body: LogsPayload):
try:
rows = [(
it.user_id, it.session_id, it.measure_date,
it.rms, it.freq, it.fatigue, it.mode, it.window_count
) for it in body.items]
with pool.acquire() as conn:
with conn.cursor() as cur:
cur.executemany("""
INSERT INTO MuscleCare.fatigue_logs
(user_id, session_id, measure_date, rms, freq, fatigue, mode, window_count, created_at)
VALUES
(:1, :2, TO_DATE(:3,'YYYY-MM-DD'), :4, :5, :6, :7, :8, CURRENT_TIMESTAMP)
""", rows)
conn.commit()
return {"ok": True, "inserted": len(rows)}
except Exception as e:
raise HTTPException(500, f"upload_logs failed: {e}")