This commit is contained in:
2026-07-23 01:15:04 +02:00
parent 582e37d176
commit b534a496fd
79 changed files with 4086 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
# Database Service (stub)
@@ -0,0 +1,52 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import os
from pathlib import Path
from devplacepy.config import DATA_DIR, DATABASE_URL
from devplacepy_services.base.sqlite_broker import SQLiteFileBroker
def _db_path() -> Path:
# DEVPLACE_DATABASE_URL is the documented override (tests use a dedicated
# tempfile for isolation - see tests/conftest.py). DATABASE_URL already
# falls back to DATA_DIR / "devplace.db" when the env var is unset, so
# deriving the broker's path from it (instead of hardcoding DATA_DIR
# directly) keeps the broker and every other DEVPLACE_DATABASE_URL
# consumer (devplacepy/database/core.py) pointed at the same file.
if DATABASE_URL.startswith("sqlite:///"):
raw = DATABASE_URL[len("sqlite:///") :]
if raw and raw != ":memory:":
return Path(raw)
return DATA_DIR / "devplace.db"
_broker = SQLiteFileBroker("main", _db_path())
def get_broker() -> SQLiteFileBroker:
return _broker
async def startup() -> None:
await _broker.startup()
from devplacepy_services.database.db_patch import patch_all_db, set_fallback_db
patch_all_db()
set_fallback_db(_broker._write_db)
os.environ["DEVPLACE_DB_SERVICE"] = "1"
from devplacepy.database import init_db
init_db()
from devplacepy.database.settings import internal_gateway_key
key = (internal_gateway_key() or "").strip()
if key:
os.environ["DEVPLACE_GATEWAY_INTERNAL_KEY"] = key
(DATA_DIR / ".internal_key").write_text(key + "\n")
async def shutdown() -> None:
await _broker.shutdown()
@@ -0,0 +1,135 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
from devplacepy.database import (
get_daily_topic,
get_polls_by_post_uids,
get_reactions_by_targets,
get_recent_comments_by_post_uids,
get_site_stats,
get_top_authors,
get_user_bookmarks,
get_users_by_uids,
)
from devplacepy.database.attachments_data import get_attachments_by_type
from devplacepy.routers.feed import get_feed_posts
def build_feed_page(
user: dict | None = None,
tab: str = "all",
topic: str | None = None,
search: str = "",
before: str | None = None,
) -> dict:
posts, next_cursor = get_feed_posts(user, tab, topic, search, before)
stats = get_site_stats()
top_authors = get_top_authors(5)
daily_topic = get_daily_topic()
post_uids = [item["post"]["uid"] for item in posts]
attachments_map = get_attachments_by_type("post", post_uids)
recent_comments = get_recent_comments_by_post_uids(post_uids, 3, user)
reactions_map = get_reactions_by_targets("post", post_uids, user)
bookmark_set = (
get_user_bookmarks(user["uid"], "post", post_uids) if user else set()
)
polls_map = get_polls_by_post_uids(post_uids, user)
for item in posts:
uid = item["post"]["uid"]
item["attachments"] = attachments_map.get(uid, [])
item["recent_comments"] = recent_comments.get(uid, [])
item["reactions"] = reactions_map.get(uid, {"counts": {}, "mine": []})
item["bookmarked"] = uid in bookmark_set
item["poll"] = polls_map.get(uid)
return {
"posts": posts,
"next_cursor": next_cursor,
"stats": stats,
"top_authors": top_authors,
"daily_topic": daily_topic,
"online_users": [],
"current_tab": tab,
"current_topic": topic,
"search": search,
}
def build_post_detail(post_uid: str, user: dict | None = None) -> dict:
from devplacepy.database import get_table, load_comments
post = get_table("posts").find_one(uid=post_uid, deleted_at=None)
if not post:
return {"post": None, "author": None, "comments": [], "attachments": []}
author = get_users_by_uids([post["user_uid"]]).get(post["user_uid"])
comments = load_comments("post", post_uid, user)
attachments = get_attachments_by_type("post", [post_uid]).get(post_uid, [])
return {
"post": post,
"author": author,
"comments": comments,
"attachments": attachments,
}
def build_profile_bundle(profile_uid: str, viewer: dict | None = None) -> dict:
from devplacepy.database import get_table
from devplacepy.database.follows import get_follow_counts
user = get_table("users").find_one(uid=profile_uid)
if not user:
return {"user": None, "follow_counts": {}, "awards": [], "posts_count": 0}
follow_counts = get_follow_counts(profile_uid)
return {
"user": user,
"follow_counts": follow_counts,
"awards": [],
"posts_count": 0,
"viewer_relation": {},
}
def build_messages_page(user_uid: str) -> dict:
return {"threads": [], "unread": 0, "partners": {}}
def build_notifications_page(user_uid: str) -> dict:
return {"notifications": [], "actors": {}, "unread_count": 0}
def build_leaderboard_page(viewer_uid: str | None = None) -> dict:
from devplacepy.database import get_leaderboard, get_user_rank
return {
"leaderboard": get_leaderboard(),
"viewer_rank": get_user_rank(viewer_uid) if viewer_uid else None,
}
def build_project_detail(project_uid: str) -> dict:
from devplacepy.database import get_table
project = get_table("projects").find_one(uid=project_uid, deleted_at=None)
owner = None
if project:
owner = get_users_by_uids([project["user_uid"]]).get(project["user_uid"])
return {"project": project, "owner": owner, "files": [], "containers": []}
def build_gist_detail(gist_uid: str, user: dict | None = None) -> dict:
from devplacepy.database import get_table, load_comments
gist = get_table("gists").find_one(uid=gist_uid, deleted_at=None)
author = None
if gist:
author = get_users_by_uids([gist["user_uid"]]).get(gist["user_uid"])
comments = load_comments("gist", gist_uid, user) if gist else []
return {"gist": gist, "author": author, "comments": comments}
def build_game_state(user_uid: str | None = None) -> dict:
return {"store": {}, "leaderboard": []}
def build_admin_dashboard() -> dict:
return {"stats": get_site_stats(), "service_states": []}
@@ -0,0 +1,138 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
from devplacepy.database import (
get_attachments_by_type,
get_comment_counts_by_post_uids,
get_follow_counts,
get_following_among,
get_leaderboard,
get_muted_uids,
get_blocked_uids,
get_user_relations,
get_notification_prefs,
get_online_users,
get_polls_by_post_uids,
get_prominent_award,
get_reactions_by_targets,
get_recent_comments_by_post_uids,
get_seo_metadata_batch,
get_site_stats,
get_top_authors,
get_user_awards,
get_user_bookmarks,
get_user_media,
get_user_rank,
get_users_by_uids,
get_vote_counts,
load_comments,
)
def users_by_uids(uids: list[str]) -> dict:
return get_users_by_uids(uids)
def comment_counts(post_uids: list[str]) -> dict:
return get_comment_counts_by_post_uids(post_uids)
def vote_counts(target_uids: list[str]) -> dict:
ups, downs = get_vote_counts(target_uids)
return {"ups": ups, "downs": downs}
def reactions(target_type: str, target_uids: list[str], user: dict | None = None) -> dict:
return get_reactions_by_targets(target_type, target_uids, user)
def bookmarks(user_uid: str, target_type: str, target_uids: list[str]) -> list[str]:
result = get_user_bookmarks(user_uid, target_type, target_uids)
return sorted(result)
def polls(post_uids: list[str], user: dict | None = None) -> dict:
return get_polls_by_post_uids(post_uids, user)
def recent_comments(
post_uids: list[str], limit: int = 3, user: dict | None = None
) -> dict:
return get_recent_comments_by_post_uids(post_uids, limit, user)
def comments(target_type: str, target_uid: str, user: dict | None = None) -> list:
return load_comments(target_type, target_uid, user)
def follow_bundle(user_uid: str, target_uids: list[str] | None = None) -> dict:
payload = {
"counts": get_follow_counts(user_uid),
"following_among": sorted(get_following_among(user_uid, target_uids or [])),
}
return payload
def relations_bundle(viewer_uid: str | None) -> dict:
relations = get_user_relations(viewer_uid)
return {
"relations": {
"block": sorted(relations["block"]),
"mute": sorted(relations["mute"]),
},
"blocked_uids": sorted(get_blocked_uids(viewer_uid)),
"muted_uids": sorted(get_muted_uids(viewer_uid)),
}
def online_users_bundle(cutoff_iso: str, limit: int = 30) -> list:
return get_online_users(cutoff_iso, limit)
def notification_prefs_bundle(user_uid: str) -> list:
return get_notification_prefs(user_uid)
def leaderboard_bundle(
limit: int = 50, offset: int = 0, viewer_uid: str | None = None
) -> dict:
return {
"leaderboard": get_leaderboard(limit, offset),
"viewer_rank": get_user_rank(viewer_uid) if viewer_uid else None,
}
def site_sidebar(authors_limit: int = 5) -> dict:
return {
"stats": get_site_stats(),
"top_authors": get_top_authors(authors_limit),
}
def awards_bundle(
receiver_uid: str,
page: int = 1,
per_page: int = 12,
profile_user: dict | None = None,
) -> dict:
items, pagination = get_user_awards(receiver_uid, page, per_page)
prominent = get_prominent_award(profile_user) if profile_user else None
return {
"items": items,
"pagination": pagination,
"prominent": prominent,
}
def attachments_batch(resource_type: str, resource_uids: list[str]) -> dict:
return get_attachments_by_type(resource_type, resource_uids)
def user_media_bundle(user_uid: str, page: int = 1, per_page: int = 24) -> dict:
items, pagination = get_user_media(user_uid, page, per_page)
return {"items": items, "pagination": pagination}
def seo_meta_batch(target_type: str, uids: list[str]) -> dict:
return get_seo_metadata_batch(target_type, uids)
+119
View File
@@ -0,0 +1,119 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import contextvars
import importlib
from typing import Any
_DB_MODULES = (
"schema",
"content",
"pagination",
"stats",
"forks",
"awards",
"customization",
"soft_delete",
"usage",
"email",
"follows",
"settings",
"seo_meta",
"deepsearch",
"activity",
"attachments_data",
"users",
"notifications",
"comments",
"ranking",
"engagement",
"relations",
)
_EXTERNAL_DB_MODULES = (
"devplacepy.services.audit.store",
"devplacepy.services.backup.store",
"devplacepy.services.openai_gateway.routing",
"devplacepy.services.openai_gateway.usage",
"devplacepy.services.openai_gateway.analytics",
"devplacepy.services.base",
"devplacepy.services.jobs.queue",
"devplacepy.services.containers.store",
"devplacepy.services.devii.store",
"devplacepy.services.devii.hub",
"devplacepy.attachments",
"devplacepy.project_files",
)
# The read pool runs on asyncio.to_thread worker threads while the write
# lane runs on the main event loop task - a plain module-global "current
# connection" swapped in/out per call is a data race between them (a
# concurrent read could reassign the global to a read-only connection
# while a write is still mid-flight, raising "attempt to write a readonly
# database"). asyncio.to_thread propagates a COPY of the calling task's
# contextvars.Context into the new thread, so storing the active
# connection in a ContextVar gives each call its own isolated view with
# no cross-talk, while every db-layer module keeps a single shared proxy
# object as its permanent `db` binding.
_CURRENT_DB: contextvars.ContextVar[Any] = contextvars.ContextVar("current_db", default=None)
# Fallback for any code path that touches the db outside an explicit
# use_db()-scoped call (e.g. startup's own init_db()) - write-once at
# broker startup, read-only afterward, so it carries no race of its own.
_fallback_db: Any = None
def _resolve() -> Any:
db = _CURRENT_DB.get()
if db is not None:
return db
if _fallback_db is not None:
return _fallback_db
raise RuntimeError("no database connection configured for this context")
class _ContextDb:
def __getattr__(self, name: str) -> Any:
return getattr(_resolve(), name)
def __getitem__(self, name: str) -> Any:
return _resolve()[name]
def __enter__(self):
return _resolve().__enter__()
def __exit__(self, exc_type, exc, tb):
return _resolve().__exit__(exc_type, exc, tb)
_CONTEXT_DB = _ContextDb()
def patch_all_db() -> None:
import devplacepy.database as database_pkg
import devplacepy.database.core as core
core.db = _CONTEXT_DB
database_pkg.db = _CONTEXT_DB
for name in _DB_MODULES:
mod = importlib.import_module(f"devplacepy.database.{name}")
if hasattr(mod, "db"):
mod.db = _CONTEXT_DB
for name in _EXTERNAL_DB_MODULES:
mod = importlib.import_module(name)
if hasattr(mod, "db"):
mod.db = _CONTEXT_DB
def set_fallback_db(db_conn) -> None:
global _fallback_db
_fallback_db = db_conn
def use_db(db_conn) -> contextvars.Token:
return _CURRENT_DB.set(db_conn)
def reset_db(token: contextvars.Token) -> None:
_CURRENT_DB.reset(token)
@@ -0,0 +1,40 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
import inspect
from typing import Any
import devplacepy.database as db_module
from devplacepy_services.base.db_codec import decode_arg, encode_result, is_write
def build_registry() -> dict[str, Any]:
registry: dict[str, Any] = {}
for name in db_module.__all__:
target = getattr(db_module, name, None)
if target is None or not callable(target):
continue
if inspect.isclass(target):
continue
registry[name] = target
return registry
REGISTRY = build_registry()
_encode_result = encode_result
def run_with_db(db_conn, fn, args, kwargs):
from devplacepy_services.database.db_patch import use_db, reset_db
token = use_db(db_conn)
try:
decoded_args = decode_arg(args)
decoded_kwargs = decode_arg(kwargs)
return fn(*decoded_args, **decoded_kwargs)
finally:
reset_db(token)
+52
View File
@@ -0,0 +1,52 @@
# retoor <retoor@molodetz.nl>
from __future__ import annotations
from contextlib import asynccontextmanager
from fastapi import FastAPI
from devplacepy_services.base.health import health_router
from devplacepy_services.base.http import shutdown_pool, startup_pool
from devplacepy_services.base.middleware import (
InternalAuthMiddleware,
InternalCallCounterMiddleware,
RequestStatsMiddleware,
SanitizeErrorsMiddleware,
)
from devplacepy_services.base.service import BaseMicroservice
from devplacepy_services.database import broker_setup
from devplacepy_services.database.routes import router as database_router
class DatabaseService(BaseMicroservice):
name = "database"
title = "Database"
default_port = 10601
workers = 1
stateful = True
depends_on = []
@asynccontextmanager
async def lifespan(self, app: FastAPI):
app.state.service_name = self.name
self.started_at = __import__("time").monotonic()
await startup_pool()
await broker_setup.startup()
yield
await broker_setup.shutdown()
await shutdown_pool()
def build_app(self):
app = self.create_app()
app.add_middleware(SanitizeErrorsMiddleware)
app.add_middleware(InternalAuthMiddleware)
app.add_middleware(InternalCallCounterMiddleware)
app.add_middleware(RequestStatsMiddleware)
app.include_router(health_router(self))
app.include_router(database_router)
return app
_service = DatabaseService()
app = _service.build_app()
+421
View File
@@ -0,0 +1,421 @@
# 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, [], {})