초기 커밋

This commit is contained in:
2026-03-01 07:44:19 +09:00
commit 09359f30be
146 changed files with 6120 additions and 0 deletions

View File

View File

@@ -0,0 +1,73 @@
from __future__ import annotations
from datetime import datetime
from fastapi import APIRouter, Depends, Query
from app.core.constants import Role
from app.core.dependencies import require_role
from app.schemas.analytics import (
AnalyticsResultRead,
ReportResponse,
TelemetryAggregateResponse,
)
from app.services.analytics_service import AnalyticsService
router = APIRouter(prefix="/analytics", tags=["analytics"])
@router.get("/telemetry/{device_id}", response_model=TelemetryAggregateResponse)
async def get_telemetry_aggregate(
device_id: str,
start: datetime = Query(...),
end: datetime = Query(...),
interval: str = Query("1h"),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
) -> TelemetryAggregateResponse:
service = AnalyticsService()
return await service.get_telemetry_aggregate(device_id, start, end, interval)
@router.post("/reports/{device_id}", response_model=ReportResponse)
async def generate_report(
device_id: str,
start: datetime = Query(...),
end: datetime = Query(...),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
) -> ReportResponse:
service = AnalyticsService()
return await service.generate_report(device_id, start, end)
@router.get("/status/{device_id}")
async def device_status_analysis(
device_id: str,
start: datetime = Query(...),
end: datetime = Query(...),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
) -> dict:
service = AnalyticsService()
return await service.get_device_status_analysis(device_id, start, end)
@router.get("/trends/{device_id}")
async def trend_analysis(
device_id: str,
start: datetime = Query(...),
end: datetime = Query(...),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
) -> dict:
service = AnalyticsService()
return await service.get_trend_analysis(device_id, start, end)
@router.get("/results", response_model=list[AnalyticsResultRead])
async def list_analytics_results(
analysis_type: str = Query(...),
device_id: str | None = Query(None),
skip: int = Query(0, ge=0),
limit: int = Query(20, ge=1, le=100),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
) -> list[AnalyticsResultRead]:
service = AnalyticsService()
return await service.list_results(analysis_type, device_id, skip, limit)

View File

@@ -0,0 +1,53 @@
from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_session
from app.core.dependencies import get_current_user_id
from app.schemas.auth import (
LoginRequest,
RefreshTokenRequest,
RegisterRequest,
TokenResponse,
)
from app.services.auth_service import AuthService
router = APIRouter(prefix="/auth", tags=["auth"])
@router.post("/register", response_model=TokenResponse, status_code=201)
async def register(
body: RegisterRequest,
session: AsyncSession = Depends(get_session),
) -> TokenResponse:
service = AuthService(session)
user = await service.register(body.email, body.password, body.full_name)
return await service._create_tokens(user)
@router.post("/login", response_model=TokenResponse)
async def login(
body: LoginRequest,
session: AsyncSession = Depends(get_session),
) -> TokenResponse:
service = AuthService(session)
return await service.login(body.email, body.password)
@router.post("/refresh", response_model=TokenResponse)
async def refresh_token(
body: RefreshTokenRequest,
session: AsyncSession = Depends(get_session),
) -> TokenResponse:
service = AuthService(session)
return await service.refresh(body.refresh_token)
@router.post("/logout", status_code=204)
async def logout(
user_id: int = Depends(get_current_user_id),
session: AsyncSession = Depends(get_session),
) -> None:
service = AuthService(session)
await service.logout(user_id)

View File

@@ -0,0 +1,70 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_session
from app.core.constants import Role
from app.core.dependencies import get_current_user_payload, require_role
from app.schemas.common import PaginatedResponse
from app.schemas.device import DeviceCreate, DeviceRead, DeviceUpdate
from app.services.device_service import DeviceService
router = APIRouter(prefix="/devices", tags=["devices"])
@router.get("", response_model=PaginatedResponse[DeviceRead])
async def list_devices(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
_: dict = Depends(get_current_user_payload),
session: AsyncSession = Depends(get_session),
) -> PaginatedResponse[DeviceRead]:
service = DeviceService(session)
skip = (page - 1) * size
items = await service.list_devices(skip=skip, limit=size)
total = await service.count_devices()
return PaginatedResponse(
items=items, total=total, page=page, size=size, pages=(total + size - 1) // size
)
@router.get("/{device_id}", response_model=DeviceRead)
async def get_device(
device_id: int,
_: dict = Depends(get_current_user_payload),
session: AsyncSession = Depends(get_session),
) -> DeviceRead:
service = DeviceService(session)
return await service.get_device(device_id)
@router.post("", response_model=DeviceRead, status_code=201)
async def create_device(
body: DeviceCreate,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN, Role.MANAGER)),
session: AsyncSession = Depends(get_session),
) -> DeviceRead:
service = DeviceService(session)
return await service.create_device(body)
@router.patch("/{device_id}", response_model=DeviceRead)
async def update_device(
device_id: int,
body: DeviceUpdate,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN, Role.MANAGER)),
session: AsyncSession = Depends(get_session),
) -> DeviceRead:
service = DeviceService(session)
return await service.update_device(device_id, body)
@router.delete("/{device_id}", status_code=204)
async def delete_device(
device_id: int,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> None:
service = DeviceService(session)
await service.delete_device(device_id)

View File

@@ -0,0 +1,61 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_session
from app.core.constants import Role
from app.core.dependencies import get_current_user_id, require_role
from app.schemas.monitoring import AlertRead, AlertRuleCreate, AlertRuleRead, SystemHealthResponse
from app.services.monitoring_service import MonitoringService
router = APIRouter(prefix="/monitoring", tags=["monitoring"])
@router.get("/health", response_model=SystemHealthResponse)
async def system_health(
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
session: AsyncSession = Depends(get_session),
) -> SystemHealthResponse:
service = MonitoringService(session)
return await service.get_system_health()
@router.get("/alerts", response_model=list[AlertRead])
async def list_alerts(
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=200),
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
session: AsyncSession = Depends(get_session),
) -> list[AlertRead]:
service = MonitoringService(session)
return await service.list_active_alerts(skip=skip, limit=limit)
@router.post("/alerts/{alert_id}/acknowledge", response_model=AlertRead)
async def acknowledge_alert(
alert_id: int,
user_id: int = Depends(get_current_user_id),
session: AsyncSession = Depends(get_session),
) -> AlertRead:
service = MonitoringService(session)
return await service.acknowledge_alert(alert_id, user_id)
@router.get("/alert-rules", response_model=list[AlertRuleRead])
async def list_alert_rules(
_: dict = Depends(require_role(*Role.MANAGEMENT_ROLES)),
session: AsyncSession = Depends(get_session),
) -> list[AlertRuleRead]:
service = MonitoringService(session)
return await service.list_alert_rules()
@router.post("/alert-rules", response_model=AlertRuleRead, status_code=201)
async def create_alert_rule(
body: AlertRuleCreate,
user_id: int = Depends(get_current_user_id),
session: AsyncSession = Depends(get_session),
) -> AlertRuleRead:
service = MonitoringService(session)
return await service.create_alert_rule(body, user_id)

View File

@@ -0,0 +1,32 @@
from __future__ import annotations
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_session
from app.core.config import settings
from app.core.constants import Role
from app.core.dependencies import require_role
router = APIRouter(prefix="/system", tags=["system"])
@router.get("/health")
async def health_check() -> dict:
return {
"status": "ok",
"service": settings.APP_NAME,
"version": "0.1.0",
}
@router.get("/info")
async def system_info(
_: dict = Depends(require_role(Role.SUPERADMIN)),
) -> dict:
return {
"app_name": settings.APP_NAME,
"environment": settings.APP_ENV,
"debug": settings.DEBUG,
"api_prefix": settings.API_V1_PREFIX,
}

View File

@@ -0,0 +1,91 @@
from __future__ import annotations
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from app.api.deps import get_session
from app.core.constants import Role
from app.core.dependencies import get_current_user_id, require_role
from app.schemas.common import PaginatedResponse
from app.schemas.user import UserCreate, UserRead, UserUpdate
from app.services.user_service import UserService
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/me", response_model=UserRead)
async def get_me(
user_id: int = Depends(get_current_user_id),
session: AsyncSession = Depends(get_session),
) -> UserRead:
service = UserService(session)
return await service.get_user(user_id)
@router.patch("/me", response_model=UserRead)
async def update_me(
body: UserUpdate,
user_id: int = Depends(get_current_user_id),
session: AsyncSession = Depends(get_session),
) -> UserRead:
body.role = None
body.is_active = None
service = UserService(session)
return await service.update_user(user_id, body)
@router.get("", response_model=PaginatedResponse[UserRead])
async def list_users(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> PaginatedResponse[UserRead]:
service = UserService(session)
skip = (page - 1) * size
items = await service.list_users(skip=skip, limit=size)
total = await service.count_users()
return PaginatedResponse(
items=items, total=total, page=page, size=size, pages=(total + size - 1) // size
)
@router.get("/{user_id}", response_model=UserRead)
async def get_user(
user_id: int,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> UserRead:
service = UserService(session)
return await service.get_user(user_id)
@router.post("", response_model=UserRead, status_code=201)
async def create_user(
body: UserCreate,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> UserRead:
service = UserService(session)
return await service.create_user(body)
@router.patch("/{user_id}", response_model=UserRead)
async def update_user(
user_id: int,
body: UserUpdate,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> UserRead:
service = UserService(session)
return await service.update_user(user_id, body)
@router.delete("/{user_id}", status_code=204)
async def delete_user(
user_id: int,
_: dict = Depends(require_role(Role.SUPERADMIN, Role.ADMIN)),
session: AsyncSession = Depends(get_session),
) -> None:
service = UserService(session)
await service.delete_user(user_id)