diff options
21 files changed, 72 insertions, 49 deletions
diff --git a/backend/authentication/user.py b/backend/authentication/user.py index 5e99546..6ad4c63 100644 --- a/backend/authentication/user.py +++ b/backend/authentication/user.py @@ -4,8 +4,9 @@ import jwt from pymongo.database import Database from starlette.authentication import BaseUser -from backend import discord, models +from backend import discord from backend.constants import SECRET_KEY +from backend.models import dtos class User(BaseUser): @@ -15,7 +16,7 @@ class User(BaseUser): self, token: str, payload: dict[str, t.Any], - member: models.DiscordMember | None, + member: dtos.DiscordMember | None, ) -> None: self.token = token self.payload = payload diff --git a/backend/discord.py b/backend/discord.py index 4a1ecf5..22826e2 100644 --- a/backend/discord.py +++ b/backend/discord.py @@ -6,7 +6,8 @@ import httpx import starlette.requests from starlette import exceptions -from backend import constants, models +from backend import constants +from backend.models import dtos async def fetch_bearer_token(code: str, redirect: str, *, refresh: bool) -> dict: @@ -51,7 +52,7 @@ async def fetch_user_details(bearer_token: str) -> dict: return r.json() -async def _get_role_info() -> list[models.DiscordRole]: +async def _get_role_info() -> list[dtos.DiscordRole]: """Get information about the roles in the configured guild.""" async with httpx.AsyncClient() as client: r = await client.get( @@ -60,13 +61,13 @@ async def _get_role_info() -> list[models.DiscordRole]: ) r.raise_for_status() - return [models.DiscordRole(**role) for role in r.json()] + return [dtos.DiscordRole(**role) for role in r.json()] async def get_roles( *, force_refresh: bool = False, -) -> list[models.DiscordRole]: +) -> list[dtos.DiscordRole]: """ Get a list of all roles from the cache, or discord API if not available. @@ -77,7 +78,7 @@ async def get_roles( roles = await constants.REDIS_CLIENT.hgetall(role_cache_key) if roles: return [ - models.DiscordRole(**json.loads(role_data)) for role_id, role_data in roles.items() + dtos.DiscordRole(**json.loads(role_data)) for role_id, role_data in roles.items() ] roles = await _get_role_info() @@ -86,7 +87,7 @@ async def get_roles( return roles -async def _fetch_member_api(member_id: str) -> models.DiscordMember | None: +async def _fetch_member_api(member_id: str) -> dtos.DiscordMember | None: """Get a member by ID from the configured guild using the discord API.""" async with httpx.AsyncClient() as client: r = await client.get( @@ -99,14 +100,14 @@ async def _fetch_member_api(member_id: str) -> models.DiscordMember | None: return None r.raise_for_status() - return models.DiscordMember(**r.json()) + return dtos.DiscordMember(**r.json()) async def get_member( user_id: str, *, force_refresh: bool = False, -) -> models.DiscordMember | None: +) -> dtos.DiscordMember | None: """ Get a member from the cache, or from the discord API. @@ -118,7 +119,7 @@ async def get_member( if not force_refresh: result = await constants.REDIS_CLIENT.get(member_key) if result: - return models.DiscordMember(**json.loads(result)) + return dtos.DiscordMember(**json.loads(result)) member = await _fetch_member_api(user_id) if member: @@ -150,14 +151,14 @@ async def _verify_access_helper( if "admin" in request.auth.scopes: return - form = models.Form(**form) + form = dtos.Form(**form) for role_id in getattr(form, attribute, None) or []: role = await request.state.db.roles.find_one({"id": role_id}) if not role: continue - role = models.DiscordRole(**json.loads(role["data"])) + role = dtos.DiscordRole(**json.loads(role["data"])) if role.name in request.auth.scopes: return diff --git a/backend/models/__init__.py b/backend/models/__init__.py index 336e28b..e69de29 100644 --- a/backend/models/__init__.py +++ b/backend/models/__init__.py @@ -1,19 +0,0 @@ -from .antispam import AntiSpam -from .discord_role import DiscordRole -from .discord_user import DiscordMember, DiscordUser -from .form import Form, FormList -from .form_response import FormResponse, ResponseList -from .question import CodeQuestion, Question - -__all__ = [ - "AntiSpam", - "CodeQuestion", - "DiscordMember", - "DiscordRole", - "DiscordUser", - "Form", - "FormList", - "FormResponse", - "Question", - "ResponseList", -] diff --git a/backend/models/dtos/__init__.py b/backend/models/dtos/__init__.py new file mode 100644 index 0000000..336e28b --- /dev/null +++ b/backend/models/dtos/__init__.py @@ -0,0 +1,19 @@ +from .antispam import AntiSpam +from .discord_role import DiscordRole +from .discord_user import DiscordMember, DiscordUser +from .form import Form, FormList +from .form_response import FormResponse, ResponseList +from .question import CodeQuestion, Question + +__all__ = [ + "AntiSpam", + "CodeQuestion", + "DiscordMember", + "DiscordRole", + "DiscordUser", + "Form", + "FormList", + "FormResponse", + "Question", + "ResponseList", +] diff --git a/backend/models/antispam.py b/backend/models/dtos/antispam.py index b596d4d..b596d4d 100644 --- a/backend/models/antispam.py +++ b/backend/models/dtos/antispam.py diff --git a/backend/models/discord_role.py b/backend/models/dtos/discord_role.py index 195f557..195f557 100644 --- a/backend/models/discord_role.py +++ b/backend/models/dtos/discord_role.py diff --git a/backend/models/discord_user.py b/backend/models/dtos/discord_user.py index be10672..be10672 100644 --- a/backend/models/discord_user.py +++ b/backend/models/dtos/discord_user.py diff --git a/backend/models/form.py b/backend/models/dtos/form.py index 739464e..739464e 100644 --- a/backend/models/form.py +++ b/backend/models/dtos/form.py diff --git a/backend/models/form_response.py b/backend/models/dtos/form_response.py index 3c8297b..3c8297b 100644 --- a/backend/models/form_response.py +++ b/backend/models/dtos/form_response.py diff --git a/backend/models/question.py b/backend/models/dtos/question.py index a13ce93..a13ce93 100644 --- a/backend/models/question.py +++ b/backend/models/dtos/question.py diff --git a/backend/models/orm/__init__.py b/backend/models/orm/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/backend/models/orm/__init__.py diff --git a/backend/routes/discord.py b/backend/routes/discord.py index 5cd6b47..1a56aac 100644 --- a/backend/routes/discord.py +++ b/backend/routes/discord.py @@ -6,7 +6,8 @@ from starlette.authentication import requires from starlette.responses import JSONResponse from starlette.routing import Request -from backend import discord, models, route +from backend import discord, route +from backend.models import dtos from backend.validation import ErrorMessage, api NOT_FOUND_EXCEPTION = JSONResponse( @@ -24,7 +25,7 @@ class RolesRoute(route.Route): class RolesResponse(pydantic.BaseModel): """A list of all roles on the configured server.""" - roles: list[models.DiscordRole] + roles: list[dtos.DiscordRole] @requires(["authenticated", "admin"]) @api.validate( @@ -53,7 +54,7 @@ class MemberRoute(route.Route): @requires(["authenticated", "admin"]) @api.validate( - resp=Response(HTTP_200=models.DiscordMember, HTTP_400=ErrorMessage), + resp=Response(HTTP_200=dtos.DiscordMember, HTTP_400=ErrorMessage), json=MemberRequest, tags=["auth"], ) @@ -68,7 +69,7 @@ class MemberRoute(route.Route): @requires(["authenticated", "admin"]) @api.validate( - resp=Response(HTTP_200=models.DiscordMember, HTTP_400=ErrorMessage), + resp=Response(HTTP_200=dtos.DiscordMember, HTTP_400=ErrorMessage), json=MemberRequest, tags=["auth"], ) diff --git a/backend/routes/forms/condorcet.py b/backend/routes/forms/condorcet.py index 902770b..ac7e52e 100644 --- a/backend/routes/forms/condorcet.py +++ b/backend/routes/forms/condorcet.py @@ -9,7 +9,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend import discord -from backend.models import Form, FormResponse, Question +from backend.models.dtos import Form, FormResponse, Question from backend.route import Route from backend.validation import api diff --git a/backend/routes/forms/discover.py b/backend/routes/forms/discover.py index 0fe10b5..43e6cf3 100644 --- a/backend/routes/forms/discover.py +++ b/backend/routes/forms/discover.py @@ -5,7 +5,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend import constants -from backend.models import Form, FormList, Question +from backend.models.dtos import Form, FormList, Question from backend.route import Route from backend.validation import api diff --git a/backend/routes/forms/form.py b/backend/routes/forms/form.py index 86bbf49..c96d0d6 100644 --- a/backend/routes/forms/form.py +++ b/backend/routes/forms/form.py @@ -10,7 +10,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend import constants, discord -from backend.models import Form +from backend.models.dtos import Form from backend.route import Route from backend.routes.forms.discover import AUTH_FORM from backend.validation import ErrorMessage, OkayResponse, api diff --git a/backend/routes/forms/index.py b/backend/routes/forms/index.py index 1fdfc48..4b55af2 100644 --- a/backend/routes/forms/index.py +++ b/backend/routes/forms/index.py @@ -6,8 +6,8 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend.constants import WebHook -from backend.models import Form, FormList -from backend.models.form import validate_hook_url +from backend.models.dtos import Form, FormList +from backend.models.dtos.form import validate_hook_url from backend.route import Route from backend.validation import ErrorMessage, OkayResponse, api diff --git a/backend/routes/forms/response.py b/backend/routes/forms/response.py index b4f7f04..ac80b74 100644 --- a/backend/routes/forms/response.py +++ b/backend/routes/forms/response.py @@ -6,7 +6,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend import discord -from backend.models import FormResponse +from backend.models.dtos import FormResponse from backend.route import Route from backend.validation import ErrorMessage, OkayResponse, api diff --git a/backend/routes/forms/responses.py b/backend/routes/forms/responses.py index 85e5af2..4228af8 100644 --- a/backend/routes/forms/responses.py +++ b/backend/routes/forms/responses.py @@ -7,7 +7,7 @@ from starlette.requests import Request from starlette.responses import JSONResponse from backend import discord -from backend.models import FormResponse, ResponseList +from backend.models.dtos import FormResponse, ResponseList from backend.route import Route from backend.validation import ErrorMessage, OkayResponse, api diff --git a/backend/routes/forms/submit.py b/backend/routes/forms/submit.py index 01c32cc..45636b7 100644 --- a/backend/routes/forms/submit.py +++ b/backend/routes/forms/submit.py @@ -19,7 +19,7 @@ from starlette.responses import JSONResponse from backend import constants from backend.authentication.user import User -from backend.models import Form, FormResponse +from backend.models.dtos import Form, FormResponse from backend.route import Route from backend.routes.auth.authorize import set_response_token from backend.routes.forms.discover import AUTH_FORM diff --git a/backend/routes/forms/unittesting.py b/backend/routes/forms/unittesting.py index 57c3a86..469243c 100644 --- a/backend/routes/forms/unittesting.py +++ b/backend/routes/forms/unittesting.py @@ -8,7 +8,7 @@ import httpx from httpx import HTTPStatusError from backend.constants import SNEKBOX_URL -from backend.models import Form, FormResponse +from backend.models.dtos import Form, FormResponse with Path("resources/unittest_template.py").open(encoding="utf8") as file: TEST_TEMPLATE = file.read() diff --git a/docker-compose.yml b/docker-compose.yml index a9363f8..b8d58da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,22 +20,42 @@ services: ports: - "127.0.0.1:6379:6379" + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: backend + POSTGRES_PASSWORD: backend + POSTGRES_USER: backend + healthcheck: + test: ["CMD-SHELL", "pg_isready -U backend"] + interval: 2s + timeout: 1s + retries: 5 + ports: + - 5000:5432 + backend: build: . command: ["uvicorn", "--reload", "--host", "0.0.0.0", "backend:app"] ports: - "127.0.0.1:8000:8000" depends_on: - - mongo - - snekbox - - redis + mongo: + condition: service_started + snekbox: + condition: service_started + redis: + condition: service_started + postgres: + condition: service_healthy tty: true env_file: - .env volumes: - .:/app:ro environment: - - DATABASE_URL=mongodb://forms-backend:forms-backend@mongo:27017 + - MONGO_DATABASE_URL=mongodb://forms-backend:forms-backend@mongo:27017 + - PSQL_DATABASE_URL=postgresql+psycopg_async://backend:backend@postgres:5432/backend - SNEKBOX_URL=http://snekbox:8060/eval - OAUTH2_CLIENT_ID - OAUTH2_CLIENT_SECRET |