api: add roles to user endpoint; add post_id to mark/review/collection/note

This commit is contained in:
mein Name 2025-01-22 22:04:32 -05:00 committed by Henri Dickson
parent ffcf33fb3b
commit c186d7d6ed
9 changed files with 70 additions and 23 deletions

View file

@ -6,12 +6,34 @@ from django.http import HttpResponse
from django.utils import timezone
from ninja import Schema
from common.api import *
from common.api import RedirectedResult, Result, api
from .common import *
from .models import *
from .common import SiteManager
from .models import (
Album,
AlbumSchema,
Edition,
EditionSchema,
Game,
GameSchema,
Item,
ItemSchema,
Movie,
MovieSchema,
Performance,
PerformanceProduction,
PerformanceProductionSchema,
PerformanceSchema,
Podcast,
PodcastSchema,
TVEpisode,
TVEpisodeSchema,
TVSeason,
TVSeasonSchema,
TVShow,
TVShowSchema,
)
from .search.models import enqueue_fetch, get_fetch_lock, query_index
from .sites import *
class SearchResult(Schema):

View file

@ -7,7 +7,7 @@ class CatalogConfig(AppConfig):
def ready(self):
# load key modules in proper order, make sure class inject and signal works as expected
from catalog import api, models, sites # noqa
from catalog import apis, models, sites # noqa
from catalog.models import init_catalog_audit_log, init_catalog_search_models
from journal import models as journal_models # noqa

View file

@ -13,8 +13,8 @@ from ..models import Collection
class CollectionSchema(Schema):
uuid: str
url: str
visibility: int = Field(ge=0, le=2)
post_id: int | None = Field(alias="latest_post_id")
created_time: datetime
title: str
brief: str

View file

@ -12,6 +12,7 @@ from ..models import Note
class NoteSchema(Schema):
uuid: str
post_id: int | None = Field(alias="latest_post_id")
item: ItemSchema
title: str
content: str

View file

@ -16,8 +16,8 @@ from ..models import (
class ReviewSchema(Schema):
url: str
visibility: int = Field(ge=0, le=2)
post_id: int | None = Field(alias="latest_post_id")
item: ItemSchema
created_time: datetime
title: str

View file

@ -9,6 +9,7 @@ from ninja.pagination import paginate
from catalog.common.models import AvailableItemCategory, Item, ItemCategory, ItemSchema
from common.api import PageNumberPagination, Result, api
from journal.models.common import q_owned_piece_visible_to_user
from journal.models.shelf import ShelfMember
from users.models.apidentity import APIdentity
from ..models import (
@ -21,7 +22,7 @@ from ..models import (
class MarkSchema(Schema):
shelf_type: ShelfType
visibility: int = Field(ge=0, le=2)
post_id: int | None = Field(alias="latest_post_id")
item: ItemSchema
created_time: datetime
comment_text: str | None
@ -40,9 +41,9 @@ class MarkInSchema(Schema):
@api.get(
"/user/{username}/shelf/{type}",
response={200: List[MarkSchema], 401: Result, 403: Result},
tags=["mark"],
"/user/{handle}/shelf/{type}",
response={200: List[MarkSchema], 401: Result, 403: Result, 404: Result},
tags=["shelf"],
)
@paginate(PageNumberPagination)
def list_marks_on_user_shelf(
@ -57,13 +58,18 @@ def list_marks_on_user_shelf(
Shelf's `type` should be one of `wishlist` / `progress` / `complete` / `dropped`;
`category` is optional, marks for all categories will be returned if not specified.
"""
target = APIdentity.get_by_handle(handle)
try:
target = APIdentity.get_by_handle(handle)
except APIdentity.DoesNotExist:
return ShelfMember.objects.none()
viewer = request.user.identity
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
return 403, {"message": "unavailable"}
return ShelfMember.objects.none()
qv = q_owned_piece_visible_to_user(request.user, target)
queryset = (
target.shelf_manager.get_latest_members(type, ItemCategory(category))
target.shelf_manager.get_latest_members(
type, ItemCategory(category) if category else None
)
.filter(qv)
.prefetch_related("item")
)
@ -73,7 +79,7 @@ def list_marks_on_user_shelf(
@api.get(
"/me/shelf/{type}",
response={200: List[MarkSchema], 401: Result, 403: Result},
tags=["mark"],
tags=["shelf"],
)
@paginate(PageNumberPagination)
def list_marks_on_shelf(
@ -94,7 +100,7 @@ def list_marks_on_shelf(
@api.get(
"/me/shelf/item/{item_uuid}",
response={200: MarkSchema, 302: Result, 401: Result, 403: Result, 404: Result},
tags=["mark"],
tags=["shelf"],
)
def get_mark_by_item(request, item_uuid: str, response: HttpResponse):
"""
@ -115,7 +121,7 @@ def get_mark_by_item(request, item_uuid: str, response: HttpResponse):
@api.post(
"/me/shelf/item/{item_uuid}",
response={200: Result, 401: Result, 403: Result, 404: Result},
tags=["mark"],
tags=["shelf"],
)
def mark_item(request, item_uuid: str, mark: MarkInSchema):
"""
@ -147,7 +153,7 @@ def mark_item(request, item_uuid: str, mark: MarkInSchema):
@api.delete(
"/me/shelf/item/{item_uuid}",
response={200: Result, 401: Result, 403: Result, 404: Result},
tags=["mark"],
tags=["shelf"],
)
def delete_mark(request, item_uuid: str):
"""

View file

@ -1,8 +1,12 @@
from typing import Literal
from django.conf import settings
from ninja import Schema
from ninja.schema import Field
from common.api import *
from mastodon.models.common import SocialAccount
from common.api import NOT_FOUND, Result, api
from mastodon.models import SocialAccount
from users.models import APIdentity
class ExternalAccountSchema(Schema):
@ -18,6 +22,7 @@ class UserSchema(Schema):
display_name: str
avatar: str
username: str
roles: list[Literal["admin", "staff"]]
@api.get(
@ -37,12 +42,13 @@ def me(request):
"external_accounts": accts,
"display_name": request.user.display_name,
"avatar": request.user.avatar,
"roles": request.user.get_roles(),
}
@api.get(
"/user/{handle}",
response={200: UserSchema, 401: Result, 403: Result},
response={200: UserSchema, 401: Result, 403: Result, 404: Result},
tags=["user"],
)
def user(request, handle: str):
@ -51,7 +57,10 @@ def user(request, handle: str):
More detailed info can be fetched from Mastodon API
"""
target = APIdentity.get_by_handle(handle)
try:
target = APIdentity.get_by_handle(handle)
except APIdentity.DoesNotExist:
return NOT_FOUND
viewer = request.user.identity
if target.is_blocking(viewer) or target.is_blocked_by(viewer):
return 403, {"message": "unavailable"}
@ -62,4 +71,5 @@ def user(request, handle: str):
"external_accounts": [],
"display_name": target.display_name,
"avatar": target.avatar,
"roles": target.user.get_roles() if target.local else [],
}

View file

@ -5,7 +5,7 @@ class UsersConfig(AppConfig):
name = "users"
def ready(self):
from . import api # noqa
from . import apis # noqa
# register cron jobs
from users.jobs import MastodonUserSync # noqa

View file

@ -171,6 +171,14 @@ class User(AbstractUser):
def __str__(self):
return f"{self.pk}:{self.username or '<missing>'}"
def get_roles(self):
roles = []
if self.is_staff:
roles.append("staff")
if self.is_superuser:
roles.append("admin")
return roles
@property
def registration_complete(self):
return self.username is not None