feat: routes
This commit is contained in:
@@ -3,12 +3,42 @@
|
|||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.v1.admin import router as admin_router
|
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.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
|
from app.api.v1.users import router as users_router
|
||||||
|
|
||||||
api_v1_router = APIRouter(prefix="/api/v1")
|
api_v1_router = APIRouter(prefix="/api/v1")
|
||||||
api_v1_router.include_router(auth_router)
|
api_v1_router.include_router(auth_router)
|
||||||
api_v1_router.include_router(users_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)
|
api_v1_router.include_router(admin_router)
|
||||||
|
|
||||||
__all__ = ["api_v1_router"]
|
__all__ = ["api_v1_router"]
|
||||||
|
|||||||
+33
-8
@@ -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
|
Registration is admin-only — this is a private instance, there is no public
|
||||||
sign-up (plan §6.4).
|
sign-up (plan §6.4).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from fastapi import APIRouter, Query, status
|
from fastapi import APIRouter, Query, status
|
||||||
|
|
||||||
@@ -16,10 +17,10 @@ from app.api.schemas.user import (
|
|||||||
UserResponse,
|
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(
|
async def list_users(
|
||||||
_admin: SuperUser,
|
_admin: SuperUser,
|
||||||
users: UserServiceDep,
|
users: UserServiceDep,
|
||||||
@@ -30,7 +31,7 @@ async def list_users(
|
|||||||
return [UserResponse.from_entity(u) for u in result]
|
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(
|
async def create_user(
|
||||||
body: CreateUserRequest, _admin: SuperUser, users: UserServiceDep
|
body: CreateUserRequest, _admin: SuperUser, users: UserServiceDep
|
||||||
) -> UserResponse:
|
) -> UserResponse:
|
||||||
@@ -42,12 +43,12 @@ async def create_user(
|
|||||||
return UserResponse.from_entity(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:
|
async def get_user(user_id: uuid.UUID, _admin: SuperUser, users: UserServiceDep) -> UserResponse:
|
||||||
return UserResponse.from_entity(await users.get_user(user_id))
|
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(
|
async def update_user(
|
||||||
user_id: uuid.UUID,
|
user_id: uuid.UUID,
|
||||||
body: UpdateUserRequest,
|
body: UpdateUserRequest,
|
||||||
@@ -62,7 +63,7 @@ async def update_user(
|
|||||||
return UserResponse.from_entity(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(
|
async def reset_password(
|
||||||
user_id: uuid.UUID,
|
user_id: uuid.UUID,
|
||||||
body: ResetPasswordRequest,
|
body: ResetPasswordRequest,
|
||||||
@@ -72,9 +73,33 @@ async def reset_password(
|
|||||||
await users.reset_password(user_id, new_password=body.new_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(
|
async def deactivate_user(
|
||||||
user_id: uuid.UUID, _admin: SuperUser, users: UserServiceDep
|
user_id: uuid.UUID, _admin: SuperUser, users: UserServiceDep
|
||||||
) -> UserResponse:
|
) -> UserResponse:
|
||||||
"""Soft delete — deactivates the account and revokes its sessions."""
|
"""Soft delete — deactivates the account and revokes its sessions."""
|
||||||
return UserResponse.from_entity(await users.deactivate(user_id))
|
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: ...
|
||||||
|
|||||||
+8
-3
@@ -3,11 +3,12 @@
|
|||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, WebSocket
|
||||||
|
|
||||||
from app.api.errors import register_exception_handlers
|
from app.api.errors import register_exception_handlers
|
||||||
from app.api.health import router as health_router
|
from app.api.health import router as health_router
|
||||||
from app.api.middleware import CorrelationIdMiddleware
|
from app.api.middleware import CorrelationIdMiddleware
|
||||||
|
from app.api.rest import subsonic_router
|
||||||
from app.api.v1 import api_v1_router
|
from app.api.v1 import api_v1_router
|
||||||
from app.core.config import get_settings
|
from app.core.config import get_settings
|
||||||
from app.core.logging import configure_logging, get_logger
|
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(health_router)
|
||||||
app.include_router(api_v1_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
|
return app
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user