59cf77bcbc
- TrackInstrument: gate native live path (SF/VSTi WASAPI bridge bypassed masterBus -> mastering FX + main out VU dead). Fallback to SonicSF WASM through masterBus.input; native kept for offline render only. - MEDIA_LIBRARY_SAMPLES: rename MIDI_Loop_02_Bass/03_Lead/05_Bass to match actual /static/midi files (drag was 404 -> parse error). - Add SonicAppLogger (console + POST /api/v1/logs) wired to: instrument load error, VSTi autosample fail, MIDI item -> mastering fx chain, play -> main out, drag MIDI errors (media explorer + window explorer). - Add backend /api/v1/logs endpoint (server-side log, no DB).
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
# SonicForge Client Logs API — client gửi log hành động (MIDI/mastering/drag
|
|
# errors) qua POST /api/v1/logs. Không lưu DB — in ra server log (uvicorn
|
|
# console). Public: log không chứa dữ liệu nhạy (client tự quyết định).
|
|
import logging
|
|
from typing import Any, Optional
|
|
from fastapi import APIRouter
|
|
from pydantic import BaseModel
|
|
|
|
router = APIRouter()
|
|
_logger = logging.getLogger("sonicforge.client_logs")
|
|
|
|
|
|
class ClientLogEntry(BaseModel):
|
|
category: str = "GENERAL"
|
|
level: str = "info"
|
|
message: str = ""
|
|
data: Optional[dict[str, Any]] = None
|
|
url: Optional[str] = None
|
|
ts: Optional[str] = None
|
|
|
|
|
|
@router.post("")
|
|
async def post_client_log(entry: ClientLogEntry):
|
|
"""Ghi log từ client vào server log (fire-and-forget, luôn 200)."""
|
|
tag = f"[ClientLog][{entry.category}][{entry.level}]"
|
|
line = f"{tag} {entry.message}"
|
|
if entry.data:
|
|
line += f" {entry.data}"
|
|
if entry.ts:
|
|
line += f" (ts={entry.ts})"
|
|
if entry.url:
|
|
line += f" url={entry.url}"
|
|
if entry.level == "error":
|
|
_logger.error(line)
|
|
elif entry.level == "warn":
|
|
_logger.warning(line)
|
|
else:
|
|
_logger.info(line)
|
|
return {"ok": True}
|