From 83abcd02dd0b5d4660f4388a2a2ffda1a486fb1f Mon Sep 17 00:00:00 2001 From: ollyhearn Date: Sat, 6 Jun 2026 12:59:52 +0300 Subject: [PATCH] feat: routes --- app/api/v1/__init__.py | 30 ++++++++++++++++++++++++++++++ app/api/v1/admin.py | 41 +++++++++++++++++++++++++++++++++-------- app/main.py | 11 ++++++++--- 3 files changed, 71 insertions(+), 11 deletions(-) diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index 3ff2824..280d2fd 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -3,12 +3,42 @@ from fastapi import APIRouter from app.api.v1.admin import router as admin_router +from app.api.v1.albums import router as albums_router +from app.api.v1.artists import router as artists_router from app.api.v1.auth import router as auth_router +from app.api.v1.downloads import router as downloads_router +from app.api.v1.history import router as history_router +from app.api.v1.likes import router as likes_router +from app.api.v1.playlists import router as playlists_router +from app.api.v1.radio import router as radio_router +from app.api.v1.search import router as search_router +from app.api.v1.sources import router as sources_router +from app.api.v1.storage import router as storage_router +from app.api.v1.streaming import router as streaming_router +from app.api.v1.sync import router as sync_router +from app.api.v1.tracks import router as tracks_router +from app.api.v1.upload import router as upload_router +from app.api.v1.user_settings import router as user_settings_router from app.api.v1.users import router as users_router api_v1_router = APIRouter(prefix="/api/v1") api_v1_router.include_router(auth_router) api_v1_router.include_router(users_router) +api_v1_router.include_router(tracks_router) +api_v1_router.include_router(albums_router) +api_v1_router.include_router(artists_router) +api_v1_router.include_router(search_router) +api_v1_router.include_router(playlists_router) +api_v1_router.include_router(likes_router) +api_v1_router.include_router(history_router) +api_v1_router.include_router(streaming_router) +api_v1_router.include_router(sources_router) +api_v1_router.include_router(downloads_router) +api_v1_router.include_router(upload_router) +api_v1_router.include_router(radio_router) +api_v1_router.include_router(storage_router) +api_v1_router.include_router(sync_router) +api_v1_router.include_router(user_settings_router) api_v1_router.include_router(admin_router) __all__ = ["api_v1_router"] diff --git a/app/api/v1/admin.py b/app/api/v1/admin.py index c672e40..c55235a 100644 --- a/app/api/v1/admin.py +++ b/app/api/v1/admin.py @@ -1,10 +1,11 @@ -"""Admin user-management endpoints. Every route requires a superuser. +"""Admin endpoints: user management, services, sources, reindex, settings. Registration is admin-only — this is a private instance, there is no public sign-up (plan §6.4). """ import uuid +from typing import Any from fastapi import APIRouter, Query, status @@ -16,10 +17,10 @@ from app.api.schemas.user import ( UserResponse, ) -router = APIRouter(prefix="/admin/users", tags=["admin"]) +router = APIRouter(prefix="/admin", tags=["admin"]) -@router.get("", response_model=list[UserResponse]) +@router.get("/users", response_model=list[UserResponse]) async def list_users( _admin: SuperUser, users: UserServiceDep, @@ -30,7 +31,7 @@ async def list_users( return [UserResponse.from_entity(u) for u in result] -@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED) +@router.post("/users", response_model=UserResponse, status_code=status.HTTP_201_CREATED) async def create_user( body: CreateUserRequest, _admin: SuperUser, users: UserServiceDep ) -> UserResponse: @@ -42,12 +43,12 @@ async def create_user( return UserResponse.from_entity(user) -@router.get("/{user_id}", response_model=UserResponse) +@router.get("/users/{user_id}", response_model=UserResponse) async def get_user(user_id: uuid.UUID, _admin: SuperUser, users: UserServiceDep) -> UserResponse: return UserResponse.from_entity(await users.get_user(user_id)) -@router.patch("/{user_id}", response_model=UserResponse) +@router.patch("/users/{user_id}", response_model=UserResponse) async def update_user( user_id: uuid.UUID, body: UpdateUserRequest, @@ -62,7 +63,7 @@ async def update_user( return UserResponse.from_entity(user) -@router.post("/{user_id}/reset-password", status_code=status.HTTP_204_NO_CONTENT) +@router.post("/users/{user_id}/reset-password", status_code=status.HTTP_204_NO_CONTENT) async def reset_password( user_id: uuid.UUID, body: ResetPasswordRequest, @@ -72,9 +73,33 @@ async def reset_password( await users.reset_password(user_id, new_password=body.new_password) -@router.delete("/{user_id}", response_model=UserResponse) +@router.delete("/users/{user_id}", response_model=UserResponse) async def deactivate_user( user_id: uuid.UUID, _admin: SuperUser, users: UserServiceDep ) -> UserResponse: """Soft delete — deactivates the account and revokes its sessions.""" return UserResponse.from_entity(await users.deactivate(user_id)) + + +@router.get("/services") +async def list_services(_admin: SuperUser) -> Any: ... + + +@router.get("/sources") +async def list_admin_sources(_admin: SuperUser) -> Any: ... + + +@router.patch("/sources/{source}") +async def update_admin_source(source: str, _admin: SuperUser) -> Any: ... + + +@router.post("/reindex") +async def trigger_reindex(_admin: SuperUser) -> Any: ... + + +@router.get("/settings") +async def get_admin_settings(_admin: SuperUser) -> Any: ... + + +@router.patch("/settings") +async def update_admin_settings(_admin: SuperUser) -> Any: ... diff --git a/app/main.py b/app/main.py index 756c434..9875ccf 100644 --- a/app/main.py +++ b/app/main.py @@ -3,11 +3,12 @@ from collections.abc import AsyncIterator from contextlib import asynccontextmanager -from fastapi import FastAPI +from fastapi import FastAPI, WebSocket from app.api.errors import register_exception_handlers from app.api.health import router as health_router from app.api.middleware import CorrelationIdMiddleware +from app.api.rest import subsonic_router from app.api.v1 import api_v1_router from app.core.config import get_settings from app.core.logging import configure_logging, get_logger @@ -43,8 +44,12 @@ def create_app() -> FastAPI: app.include_router(health_router) app.include_router(api_v1_router) - # Subsonic-compatible layer is mounted in a later step: - # app.include_router(subsonic_router, prefix="/rest") + app.include_router(subsonic_router, prefix="/rest") + + @app.websocket("/ws") + async def ws_stub(websocket: WebSocket) -> None: + await websocket.accept() + await websocket.close(1001) return app