# retoor 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, [], {})