diff --git a/freak/__init__.py b/freak/__init__.py index c191ef2..7b98de3 100644 --- a/freak/__init__.py +++ b/freak/__init__.py @@ -26,7 +26,7 @@ from suou import twocolon_list, WantsContentType from .colors import color_themes, theme_classes -__version__ = '0.5.0-dev42' +__version__ = '0.5.0-dev43' APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__)) diff --git a/freak/accounts.py b/freak/accounts.py index 5ae845c..db34516 100644 --- a/freak/accounts.py +++ b/freak/accounts.py @@ -5,6 +5,7 @@ import enum from sqlalchemy import select from sqlalchemy.orm import selectinload +from suou.sqlalchemy.asyncio import AsyncSession from .models import User, db from quart_auth import AuthUser, Action as _Action @@ -42,7 +43,7 @@ class UserLoader(AuthUser): def __init__(self, auth_id: str | None, action: _Action= _Action.PASS): self._auth_id = auth_id self._auth_obj = None - self._auth_sess = None + self._auth_sess: AsyncSession | None = None self.action = action @property @@ -69,6 +70,10 @@ class UserLoader(AuthUser): def __bool__(self): return self._auth_obj is not None + @property + def session(self): + return self._auth_sess + async def _unload(self): # user is not expected to mutate if self._auth_sess: diff --git a/freak/rest/__init__.py b/freak/rest/__init__.py index 60bec90..e89dc85 100644 --- a/freak/rest/__init__.py +++ b/freak/rest/__init__.py @@ -1,15 +1,16 @@ from __future__ import annotations -from typing import Iterable +from typing import Iterable, TypeVar from quart import session from quart import abort, Blueprint, redirect, request, url_for from pydantic import BaseModel -from quart_auth import AuthUser, current_user, login_required, login_user, logout_user -from quart_schema import QuartSchema, validate_request, validate_response +from quart_auth import current_user, login_required, login_user, logout_user +from quart_schema import validate_request, validate_response from sqlalchemy import delete, insert, select from suou import Snowflake, deprecated, makelist, not_implemented, want_isodate +from suou.classtools import MISSING, MissingType from werkzeug.security import check_password_hash from suou.quart import add_rest @@ -20,6 +21,8 @@ from freak.search import SearchQuery from ..models import Comment, Guild, Post, PostUpvote, User, db from .. import UserLoader, app, app_config, __version__ as freak_version, csrf +_T = TypeVar('_T') + bp = Blueprint('rest', __name__, url_prefix='/v1') rest = add_rest(app, '/v1', '/ajax') @@ -332,7 +335,7 @@ async def search_top(data: QueryIn): async with db as session: sq = SearchQuery(data.query) - result: Iterable[Post] = (await session.execute(sq.select(Post, [Post.title]).limit(20))).scalars() + result = (await session.execute(sq.select(Post, [Post.title]).limit(20))).scalars() return dict(has = [p.feed_info() for p in result]) @@ -353,3 +356,39 @@ async def suggest_guild(data: QueryIn): return dict(has = [g.simple_info() for g in result if await g.allows_posting(current_user.user)]) +## SETTINGS + +@bp.get("/settings/appearance") +@login_required +async def get_settings_appearance(): + return dict( + color_theme = current_user.user.color_theme + ) + + +class SettingsAppearanceIn(BaseModel): + color_theme : int | None = None + color_scheme : int | None = None + + +def _missing_or(obj: _T | MissingType, obj2: _T) -> _T: + if obj is None: + return obj2 + return obj + +@bp.patch("/settings/appearance") +@login_required +@validate_request(SettingsAppearanceIn) +async def patch_settings_appearance(data: SettingsIn): + u = current_user.user + if u is None: + abort(401) + + u.color_theme = ( + _missing_or(data.color_theme, u.color_theme % (1 << 8)) % 256 + + _missing_or(data.color_scheme, u.color_theme >> 8) << 8 + ) + current_user.session.add(u) + await current_user.session.commit() + + return '', 204 diff --git a/pyproject.toml b/pyproject.toml index dab79c6..6f60780 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ dependencies = [ "libsass", "setuptools>=78.1.0", "Hypercorn", - "suou>=0.6.1" + "suou[sqlalchemy]>=0.7.5" ] requires-python = ">=3.10" classifiers = [