Allowing unauthenticated users to see scopes
This commit is contained in:
1 parent
2ce4f91e4f
commit
3253d8e858
8 files changed
+136
-27
No files matched your search
+45
-10
@@ -5,7 +5,7 @@ non-Scope part type listing the accessories that fit each scope. Supports HTMX
|
||||
search/filter over scope attributes and required accessory fits.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, Request
|
||||
from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -16,6 +16,7 @@ from app.crud import manufacturer as manufacturer_crud
|
||||
from app.crud import part_type as part_type_crud
|
||||
from app.crud import scope_model as scope_model_crud
|
||||
from app.crud import sub_part_type as sub_part_type_crud
|
||||
from app.deps import get_optional_web_user
|
||||
from app.models.user import User
|
||||
from app.services import scope_guide as scope_guide_service
|
||||
from app.web.admin import web_require
|
||||
@@ -27,17 +28,39 @@ router = APIRouter(include_in_schema=False)
|
||||
_PAGE_SIZE = 50
|
||||
_TABLE_TARGET = "#scopes-table-card"
|
||||
_FILTER_PATH = "/scopes/filter"
|
||||
_REQUIRED_PERMS = {"augmented_parts:read", "fits:read"}
|
||||
|
||||
|
||||
def _to_int(value: str) -> int | None:
|
||||
return int(value) if value.strip() else None
|
||||
|
||||
|
||||
def _can_write_scopes(user: User) -> bool:
|
||||
return user.is_superuser or "augmented_parts:write" in user.permissions
|
||||
def _can_write_scopes(user: User | None) -> bool:
|
||||
return user is not None and (
|
||||
user.is_superuser or "augmented_parts:write" in user.permissions
|
||||
)
|
||||
|
||||
|
||||
async def _build(db: AsyncSession, filters: dict) -> dict:
|
||||
async def _optional_scopes_user(
|
||||
user: User | None = Depends(get_optional_web_user),
|
||||
) -> User | None:
|
||||
"""Anyone may view the scopes page; logged-in users still need the read perms.
|
||||
|
||||
Anonymous visitors are allowed through (and get an exact-fits-only view —
|
||||
see ``only_exact`` in ``scope_guide.build``); a logged-in user missing the
|
||||
permissions gets the usual 403, matching ``web_require``'s behaviour.
|
||||
"""
|
||||
if user is not None and not (
|
||||
user.is_superuser or _REQUIRED_PERMS.issubset(user.permissions)
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You do not have access to this page.",
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
async def _build(db: AsyncSession, filters: dict, only_exact: bool = False) -> dict:
|
||||
"""Run the guide query for the given raw (string) filter values.
|
||||
|
||||
Always returns the full matching row set — pagination is applied by the
|
||||
@@ -50,17 +73,22 @@ async def _build(db: AsyncSession, filters: dict) -> dict:
|
||||
scope_model_id=_to_int(filters["scope_model_id"]),
|
||||
sub_part_type_id=_to_int(filters["sub_part_type_id"]),
|
||||
fits_part_type_id=_to_int(filters["fits_part_type_id"]),
|
||||
only_exact=only_exact,
|
||||
)
|
||||
|
||||
|
||||
async def _paginated_guide(
|
||||
db: AsyncSession, filters: dict, page: int, page_size: int
|
||||
db: AsyncSession,
|
||||
filters: dict,
|
||||
page: int,
|
||||
page_size: int,
|
||||
only_exact: bool = False,
|
||||
) -> tuple[dict, dict]:
|
||||
"""Run the guide query, then slice its rows to the requested page.
|
||||
|
||||
Returns ``(guide, pagination)`` where ``guide["rows"]`` is already sliced.
|
||||
"""
|
||||
guide = await _build(db, filters)
|
||||
guide = await _build(db, filters, only_exact=only_exact)
|
||||
all_rows = guide["rows"]
|
||||
pagination = paginate(
|
||||
_FILTER_PATH, filters, page, page_size, len(all_rows), _TABLE_TARGET
|
||||
@@ -95,7 +123,7 @@ async def scopes_page(
|
||||
sub_part_type_id: str = "",
|
||||
fits_part_type_id: str = "",
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(web_require("augmented_parts:read", "fits:read")),
|
||||
user: User | None = Depends(_optional_scopes_user),
|
||||
):
|
||||
filters = {
|
||||
"description": description,
|
||||
@@ -104,7 +132,10 @@ async def scopes_page(
|
||||
"sub_part_type_id": sub_part_type_id,
|
||||
"fits_part_type_id": fits_part_type_id,
|
||||
}
|
||||
guide, pagination = await _paginated_guide(db, filters, 1, _PAGE_SIZE)
|
||||
only_exact = user is None
|
||||
guide, pagination = await _paginated_guide(
|
||||
db, filters, 1, _PAGE_SIZE, only_exact=only_exact
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"scopes.html",
|
||||
@@ -112,6 +143,7 @@ async def scopes_page(
|
||||
"user": user,
|
||||
"filters": filters,
|
||||
"can_write_scopes": _can_write_scopes(user),
|
||||
"only_exact": only_exact,
|
||||
"pagination": pagination,
|
||||
**guide,
|
||||
**(await _filter_options(db)),
|
||||
@@ -130,7 +162,7 @@ async def scopes_filter(
|
||||
page: int = 1,
|
||||
page_size: int = _PAGE_SIZE,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(web_require("augmented_parts:read", "fits:read")),
|
||||
user: User | None = Depends(_optional_scopes_user),
|
||||
):
|
||||
filters = {
|
||||
"description": description,
|
||||
@@ -139,7 +171,10 @@ async def scopes_filter(
|
||||
"sub_part_type_id": sub_part_type_id,
|
||||
"fits_part_type_id": fits_part_type_id,
|
||||
}
|
||||
guide, pagination = await _paginated_guide(db, filters, page, page_size)
|
||||
only_exact = user is None
|
||||
guide, pagination = await _paginated_guide(
|
||||
db, filters, page, page_size, only_exact=only_exact
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"_scopes_table_card.html",
|
||||
|
||||
Reference in new issue
Block a user