Skip to content

Commit 2bba23c

Browse files
committed
Initial commit
1 parent 8782fef commit 2bba23c

File tree

11 files changed

+315
-0
lines changed

11 files changed

+315
-0
lines changed

config.example.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
server:
2+
port: 4141
3+
host: localhost
4+
sessions:
5+
max_age: 604800
6+
valkey:
7+
db: 0
8+
port: 6379

eira/.gitkeep

Whitespace-only changes.

ember/__init__.py

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from .app import App as App
17+
from .config import config as config

ember/app.py

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from __future__ import annotations
17+
18+
from typing import TYPE_CHECKING, Any
19+
20+
from litestar import Litestar
21+
from litestar.middleware.session.server_side import ServerSideSessionConfig
22+
from litestar.stores.valkey import ValkeyStore
23+
24+
from .config import config
25+
from .controllers import *
26+
27+
28+
if TYPE_CHECKING:
29+
from litestar import Controller
30+
from litestar.middleware import DefineMiddleware
31+
32+
33+
class App(Litestar):
34+
def __init__(self, **kwargs: Any) -> None:
35+
self.config = config
36+
37+
store = ValkeyStore.with_client(db=config["valkey"]["db"], port=config["valkey"]["port"])
38+
stores: dict[str, ValkeyStore] = {"sessions": store}
39+
40+
sessions = ServerSideSessionConfig(max_age=config["sessions"]["max_age"], renew_on_access=True, secure=True)
41+
middleware: list[DefineMiddleware] = [sessions.middleware]
42+
43+
controllers: list[type[Controller]] = [APIControllerV1]
44+
super().__init__(route_handlers=controllers, stores=stores, middleware=middleware, **kwargs) # type: ignore

ember/config.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from __future__ import annotations
17+
18+
from typing import TYPE_CHECKING
19+
20+
import yaml
21+
22+
23+
if TYPE_CHECKING:
24+
from types_.config import ConfigT
25+
26+
27+
with open("config.yaml", "rb") as fp:
28+
config: ConfigT = yaml.load(fp.read(), Loader=yaml.Loader)

ember/controllers/__init__.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from .api import *

ember/controllers/api.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from __future__ import annotations
17+
18+
from typing import TYPE_CHECKING
19+
20+
import litestar
21+
22+
23+
if TYPE_CHECKING:
24+
from litestar import Request
25+
from litestar.datastructures import State
26+
27+
28+
__all__ = ("APIControllerV1",)
29+
30+
31+
class APIControllerV1(litestar.Controller):
32+
path = "/api/v1"
33+
34+
@litestar.get("/test")
35+
async def test_endpoint(self, request: Request[str, str, State]) -> str:
36+
return "Hello World!"

ember/types_/config.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
from typing import TypedDict
17+
18+
19+
class ServerT(TypedDict):
20+
port: int
21+
host: str
22+
23+
24+
class SessionsT(TypedDict):
25+
max_age: int
26+
27+
28+
class ValkeyT(TypedDict):
29+
db: int
30+
port: int
31+
32+
33+
class ConfigT(TypedDict):
34+
server: ServerT
35+
sessions: SessionsT
36+
valkey: ValkeyT

main.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Copyright 2025 PythonistaGuild
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
"""
15+
16+
import asyncio
17+
import logging
18+
19+
import uvicorn
20+
21+
from ember import App, config
22+
23+
24+
LOGGER: logging.Logger = logging.getLogger(__name__)
25+
26+
27+
def create_app() -> App:
28+
app = App()
29+
return app
30+
31+
32+
def main() -> None:
33+
host = config["server"]["host"]
34+
port = config["server"]["port"]
35+
36+
async def runner() -> None:
37+
conf = uvicorn.Config("main:create_app", host=host, port=port, proxy_headers=True, forwarded_allow_ips="*")
38+
server = uvicorn.Server(conf)
39+
40+
await server.serve()
41+
42+
try:
43+
asyncio.run(runner())
44+
except KeyboardInterrupt:
45+
LOGGER.warning("Shutting down due to KeyboardInterrupt...")
46+
47+
48+
if __name__ == "__main__":
49+
main()

pyproject.toml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
[project]
2+
name = "twitchio-token-relay"
3+
authors = [{ name = "PythonistaGuild" }]
4+
description = "A small API to relay OAuth to Twitch bots"
5+
version = "0.0.1a"
6+
readme = "README.md"
7+
dynamic = ["dependencies"]
8+
requires-python = ">=3.13"
9+
10+
[tool.setuptools.dynamic]
11+
dependencies = { file = ["requirements.txt"] }
12+
13+
[project.optional-dependencies]
14+
dev = [
15+
"ruff",
16+
"pyright",
17+
"isort",
18+
]
19+
20+
[tool.ruff.lint]
21+
select = [
22+
"C4",
23+
"F",
24+
"G",
25+
"I",
26+
"PTH",
27+
"RUF",
28+
"SIM",
29+
"TCH",
30+
"UP",
31+
"W",
32+
"PERF",
33+
"ANN",
34+
]
35+
ignore = [
36+
"F401",
37+
"F402",
38+
"F403",
39+
"F405",
40+
"PERF203",
41+
"RUF001",
42+
"RUF009",
43+
"SIM105",
44+
"UP034",
45+
"UP038",
46+
"ANN401",
47+
"UP031",
48+
"PTH123",
49+
"RUF006",
50+
]
51+
52+
[tool.ruff]
53+
line-length = 125
54+
exclude = ["venv"]
55+
56+
[tool.ruff.lint.isort]
57+
split-on-trailing-comma = true
58+
combine-as-imports = true
59+
lines-after-imports = 2
60+
61+
[tool.ruff.lint.flake8-annotations]
62+
allow-star-arg-any = true
63+
64+
[tool.ruff.lint.flake8-quotes]
65+
inline-quotes = "double"
66+
67+
[tool.ruff.format]
68+
quote-style = "double"
69+
indent-style = "space"
70+
skip-magic-trailing-comma = false
71+
line-ending = "auto"
72+
73+
[tool.pyright]
74+
exclude = ["venv"]
75+
useLibraryCodeForTypes = true
76+
typeCheckingMode = "strict"
77+
reportImportCycles = false
78+
reportPrivateUsage = false

requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
litestar[standard, picologging]~=2.15
2+
valkey~=6.1
3+
PyYAML~=6.0

0 commit comments

Comments
 (0)