Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 83 additions & 4 deletions .commitlintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,91 @@
// npx gitmoji -l
const gitmojis = [
"⏪️",
"♻️",
"♿️",
"⚗️",
"⚡️",
"⚰️",
"✅",
"✏️",
"✨",
"➕",
"➖",
"⬆️",
"⬇️",
"🌐",
"🌱",
"🍱",
"🍻",
"🎉",
"🎨",
"🏗️",
"🏷️",
"🐛",
"👔",
"👥",
"👷",
"👽️",
"💄",
"💚",
"💡",
"💥",
"💩",
"💫",
"💬",
"💸",
"📄",
"📈",
"📌",
"📝",
"📦️",
"📱",
"📸",
"🔀",
"🔇",
"🔊",
"🔍️",
"🔐",
"🔒️",
"🔖",
"🔥",
"🔧",
"🔨",
"🗃️",
"🗑️",
"🙈",
"🚀",
"🚑️",
"🚚",
"🚧",
"🚨",
"🚩",
"🚸",
"🛂",
"🤡",
"🥅",
"🥚",
"🦺",
"🧐",
"🧑‍💻",
"🧪",
"🧱",
"🧵",
"🩹",
"🩺",
];

module.exports = {
extends: ["gitmoji"],
extends: ["@commitlint/config-conventional"],
parserPreset: {
parserOpts: {
headerPattern: /^[^ ]+ (.*)$/,
headerCorrespondence: ["subject"],
headerPattern: /^([^ ]+) (.*)$/,
headerCorrespondence: ["type", "subject"],
},
},
rules: {
"type-empty": [0, "always"],
"type-enum": [2, "always", gitmojis],
"type-case": [0, "always", "lower-case"],
"subject-full-stop": [2, "never", "."],
},
};
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
**/*.egg-info
**/__pycache__/
*.egg-info
.env
.ruff_cache/
.vscode/
/build/
/config.toml
/data/*.sqlite3
__pycache__/
node_modules/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ RUN <<-EOF
pip install --no-cache -Ue .
EOF

ENTRYPOINT [ "/usr/local/bin/python", "-m", "aethersprite.webapp" ]
ENTRYPOINT [ "/usr/local/bin/python", "-m", "realm_api" ]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pip install -U 'realm_api@git+https://github.com/realm-ttrpg/api-server.git'
In the same directory as your `config.toml` file:

```shell
python -m aethersprite.webapp
python -m realm_api
```

[bot software]: https://github.com/realm-ttrpg/discord-bot
Expand Down
52 changes: 52 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"devDependencies": {
"@commitlint/config-conventional": "^19.8.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/github": "^11.0.1",
Expand Down Expand Up @@ -33,5 +34,6 @@
},
"scripts": {
"prepare": "husky"
}
},
"type": "module"
}
14 changes: 14 additions & 0 deletions realm_api/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Entry point"""

# 3rd party
from uvicorn import run

# local
from os import environ

run(
"realm_api.app:app",
host=environ.get("REALM_HOST", "0.0.0.0"),
port=int(environ.get("REALM_PORT", "5000")),
lifespan="on",
)
4 changes: 0 additions & 4 deletions realm_api/_all.py

This file was deleted.

15 changes: 15 additions & 0 deletions realm_api/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""API layer"""

# 3rd party
from fastapi import APIRouter

# local
from .auth.router import router as auth_router
from .game.router import router as game_router


router = APIRouter()
"""Main router"""

router.include_router(auth_router)
router.include_router(game_router)
1 change: 1 addition & 0 deletions realm_api/api/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Authentication/authorization"""
4 changes: 2 additions & 2 deletions realm_api/auth/depends.py → realm_api/api/auth/depends.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from sqlmodel import select

# local
from ..db import get_session
from ..models.user_session import UserSession
from realm_api.db import get_session
from realm_api.models.user_session import UserSession


async def require_login(
Expand Down
12 changes: 6 additions & 6 deletions realm_api/auth/routes.py → realm_api/api/auth/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
from realm_schema import BotGuildsResponse

# local
from ..db import get_session
from ..discord import DiscordClient
from ..models.user_session import UserSession
from ..rpc import redis_conn, rpc_bot
from realm_api.db import get_session
from realm_api.discord import DiscordClient
from realm_api.models.user_session import UserSession
from realm_api.rpc import redis_conn, rpc_bot
from .depends import require_login
from .schema import LoginRequest, LoginResponse, SharedGuildsResponse

Expand Down Expand Up @@ -78,15 +78,15 @@ async def shared_guilds(
discord = DiscordClient(session.user_id, session.discord_token)
my_guilds = await discord.get_guilds()

if cached := redis_conn.get("bot.guilds"):
if cached := await redis_conn.get("bot.guilds"):
bot_guilds = (
BotGuildsResponse.model_validate_json(cached) # type: ignore
)
else:
bot_guilds = BotGuildsResponse.model_validate_json(
await rpc_bot("guilds")
)
redis_conn.setex(
await redis_conn.setex(
"bot.guilds", CACHE_EXPIRY, bot_guilds.model_dump_json()
)

Expand Down
1 change: 1 addition & 0 deletions realm_api/auth/schema.py → realm_api/api/auth/schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""HTTP request/response schema for authz/authn routes"""

# 3rd party
from pydantic import BaseModel


Expand Down
1 change: 1 addition & 0 deletions realm_api/api/game/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Game module"""
32 changes: 32 additions & 0 deletions realm_api/api/game/depends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""FastAPI checks for use in `Depends()`"""

# 3rd party
from fastapi import Depends, HTTPException, Path, status
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

# local
from realm_api.api.auth.depends import require_login
from realm_api.db import get_session
from realm_api.models.game import Game
from realm_api.models.game_player import GamePlayer
from realm_api.models.user_session import UserSession


async def user_in_guild(
guild_id: int = Path(),
session: UserSession = Depends(require_login),
db: AsyncSession = Depends(get_session),
):
if not (
await db.exec(
select(1)
.select_from(GamePlayer, Game)
.where(
Game.guild_id == str(guild_id),
GamePlayer.game_id == Game.id,
GamePlayer.player_id == session.user_id,
)
)
).one_or_none():
raise HTTPException(status.HTTP_403_FORBIDDEN)
44 changes: 44 additions & 0 deletions realm_api/api/game/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Game routes"""

# 3rd party
from fastapi import APIRouter, Depends
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

# local
from realm_api.db import get_session
from realm_api.models.game import Game
from .depends import user_in_guild
from .schema import GameResponse

router = APIRouter(prefix="/game")


@router.get("/list/{guild_id}")
async def list_games(
guild_id: int,
db: AsyncSession = Depends(get_session),
in_guild=Depends(user_in_guild),
) -> list[GameResponse]:
games = (
await db.exec(select(Game).where(Game.guild_id == str(guild_id)))
).all()
response = [
GameResponse.model_validate(game, from_attributes=True)
for game in games
]

return response


@router.get("/{guild_id}/{game_id}")
async def get_game(
guild_id: int,
game_id: int,
db: AsyncSession = Depends(get_session),
in_guild=Depends(user_in_guild),
) -> GameResponse:
game = (await db.exec(select(Game).where(Game.id == str(game_id)))).one()
response = GameResponse.model_validate(game, from_attributes=True)

return response
9 changes: 9 additions & 0 deletions realm_api/api/game/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""HTTP request/response schema for game routes"""

# 3rd party
from pydantic import BaseModel


class GameResponse(BaseModel):
id: int
name: str
Loading
Loading