# retoor <retoor@molodetz.nl>
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from devplacepy.database import bump_cache_version, get_int_setting, get_setting
from devplacepy_services.base.auth import internal_user
from devplacepy_services.base.compound import (
AttachmentsIn,
AwardsBundleIn,
BookmarksIn,
CacheBumpIn,
CommentCountsIn,
CommentsIn,
FeedPageIn,
FollowBundleIn,
GameStateIn,
GistDetailIn,
InvokeIn,
LeaderboardBundleIn,
LeaderboardPageIn,
MessagesPageIn,
NotificationPrefsIn,
NotificationsPageIn,
OnlineUsersIn,
PollsIn,
PostDetailIn,
ProfileBundleIn,
ProjectDetailIn,
ReactionsIn,
RecentCommentsIn,
RelationsIn,
SeoMetaIn,
SiteSidebarIn,
UserMediaIn,
UsersByUidsIn,
VoteCountsIn,
)
from devplacepy_services.base.db_codec import is_write_sql
from devplacepy_services.base.schemas import InternalUser
from devplacepy_services.database import compounds_page, compounds_primitive
from devplacepy_services.database.broker_setup import get_broker
from devplacepy_services.database.invoke_registry import (
REGISTRY,
_encode_result,
is_write,
run_with_db,
)
router = APIRouter()
class DbOpIn(BaseModel):
op: str
table: str | None = None
method: str | None = None
args: list[Any] = Field(default_factory=list)
kwargs: dict[str, Any] = Field(default_factory=dict)
write: bool = False
def _db_tables(db_conn):
return list(db_conn.tables)
def _db_query(db_conn, sql: str, **params):
return list(db_conn.query(sql, **params))
def _db_table_op(db_conn, table: str, method: str, args: list, kwargs: dict):
target = db_conn[table]
fn = getattr(target, method)
result = fn(*args, **kwargs)
if hasattr(result, "__iter__") and not isinstance(result, (str, bytes, dict)):
try:
return [_encode_result(row) for row in result]
except TypeError:
pass
return _encode_result(result)
@router.post("/internal/invoke")
async def internal_invoke(body: InvokeIn, _: InternalUser = Depends(internal_user)):
fn = REGISTRY.get(body.fn)
if fn is None:
return {"error": f"Unknown function: {body.fn}", "code": "unknown_function"}
write = body.write or is_write(body.fn)
broker = get_broker()
if write:
result = await broker.write(run_with_db, fn, body.args, body.kwargs)
else:
result = await broker.read(run_with_db, fn, body.args, body.kwargs)
return {"result": _encode_result(result)}
@router.post("/internal/db-op")
async def internal_db_op(body: DbOpIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
if body.op == "tables":
tables = await broker.read(_db_tables)
return tables
if body.op == "query":
sql = body.args[0] if body.args else ""
write = body.write or is_write_sql(sql)
if write:
rows = await broker.write(_db_query, sql, **body.kwargs)
else:
rows = await broker.read(_db_query, sql, **body.kwargs)
return [_encode_result(row) for row in rows]
if body.op == "table_op":
if not body.table or not body.method:
return {"error": "table and method required", "code": "invalid_request"}
write_methods = {"insert", "update", "delete", "create_column_by_example"}
write = body.write or body.method in write_methods
if write:
result = await broker.write(
_db_table_op, body.table, body.method, body.args, body.kwargs
)
else:
result = await broker.read(
_db_table_op, body.table, body.method, body.args, body.kwargs
)
return result
return {"error": f"Unknown op: {body.op}", "code": "unknown_op"}
@router.get("/settings/{key}")
async def settings_get(key: str, default: str = "", _: InternalUser = Depends(internal_user)):
broker = get_broker()
value = await broker.read(run_with_db, get_setting, [key, default], {})
return {"key": key, "value": value}
@router.post("/cache/bump")
async def cache_bump(body: CacheBumpIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
await broker.write(run_with_db, bump_cache_version, [body.name], {})
return {"ok": True, "name": body.name}
@router.post("/compound/users-by-uids")
async def compound_users_by_uids(body: UsersByUidsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(run_with_db, compounds_primitive.users_by_uids, [body.uids], {})
@router.post("/compound/comment-counts")
async def compound_comment_counts(body: CommentCountsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_primitive.comment_counts, [body.post_uids], {}
)
@router.post("/compound/vote-counts")
async def compound_vote_counts(body: VoteCountsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_primitive.vote_counts, [body.target_uids], {}
)
@router.post("/compound/reactions")
async def compound_reactions(body: ReactionsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.reactions,
[body.target_type, body.target_uids, body.user],
{},
)
@router.post("/compound/bookmarks")
async def compound_bookmarks(body: BookmarksIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.bookmarks,
[body.user_uid, body.target_type, body.target_uids],
{},
)
@router.post("/compound/polls")
async def compound_polls(body: PollsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_primitive.polls, [body.post_uids, body.user], {}
)
@router.post("/compound/recent-comments")
async def compound_recent_comments(
body: RecentCommentsIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.recent_comments,
[body.post_uids, body.limit, body.user],
{},
)
@router.post("/compound/comments")
async def compound_comments(body: CommentsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.comments,
[body.target_type, body.target_uid, body.user],
{},
)
@router.post("/compound/follow-bundle")
async def compound_follow_bundle(body: FollowBundleIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.follow_bundle,
[body.user_uid, body.target_uids],
{},
)
@router.post("/compound/relations")
async def compound_relations(body: RelationsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_primitive.relations_bundle, [body.viewer_uid], {}
)
@router.post("/compound/online-users")
async def compound_online_users(body: OnlineUsersIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.online_users_bundle,
[body.cutoff_iso, body.limit],
{},
)
@router.post("/compound/notification-prefs")
async def compound_notification_prefs(
body: NotificationPrefsIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.notification_prefs_bundle,
[body.user_uid],
{},
)
@router.post("/compound/leaderboard")
async def compound_leaderboard(
body: LeaderboardBundleIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.leaderboard_bundle,
[body.limit, body.offset, body.viewer_uid],
{},
)
@router.post("/compound/site-sidebar")
async def compound_site_sidebar(body: SiteSidebarIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_primitive.site_sidebar, [body.authors_limit], {}
)
@router.post("/compound/awards")
async def compound_awards(body: AwardsBundleIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.awards_bundle,
[body.receiver_uid, body.page, body.per_page, body.profile_user],
{},
)
@router.post("/compound/attachments")
async def compound_attachments(body: AttachmentsIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.attachments_batch,
[body.resource_type, body.resource_uids],
{},
)
@router.post("/compound/user-media")
async def compound_user_media(body: UserMediaIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.user_media_bundle,
[body.user_uid, body.page, body.per_page],
{},
)
@router.post("/compound/seo-meta")
async def compound_seo_meta(body: SeoMetaIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_primitive.seo_meta_batch,
[body.target_type, body.uids],
{},
)
@router.post("/compound/feed-page")
async def compound_feed_page(body: FeedPageIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_feed_page, [], body.model_dump()
)
@router.post("/compound/post-detail")
async def compound_post_detail(body: PostDetailIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_page.build_post_detail,
[body.post_uid, body.user],
{},
)
@router.post("/compound/profile-bundle")
async def compound_profile_bundle(
body: ProfileBundleIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_page.build_profile_bundle,
[body.profile_uid, body.viewer],
{},
)
@router.post("/compound/messages-page")
async def compound_messages_page(body: MessagesPageIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_messages_page, [body.user_uid], {}
)
@router.post("/compound/notifications-page")
async def compound_notifications_page(
body: NotificationsPageIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_notifications_page, [body.user_uid], {}
)
@router.post("/compound/leaderboard-page")
async def compound_leaderboard_page(
body: LeaderboardPageIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_leaderboard_page, [body.viewer_uid], {}
)
@router.post("/compound/project-detail")
async def compound_project_detail(
body: ProjectDetailIn, _: InternalUser = Depends(internal_user)
):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_project_detail, [body.project_uid], {}
)
@router.post("/compound/gist-detail")
async def compound_gist_detail(body: GistDetailIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db,
compounds_page.build_gist_detail,
[body.gist_uid, body.user],
{},
)
@router.post("/compound/game-state")
async def compound_game_state(body: GameStateIn, _: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(
run_with_db, compounds_page.build_game_state, [body.user_uid], {}
)
@router.post("/compound/admin-dashboard")
async def compound_admin_dashboard(_: InternalUser = Depends(internal_user)):
broker = get_broker()
return await broker.read(run_with_db, compounds_page.build_admin_dashboard, [], {})