test0001 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- test0001-0.1.0/LICENSE +21 -0
- test0001-0.1.0/PKG-INFO +95 -0
- test0001-0.1.0/README.md +58 -0
- test0001-0.1.0/pyproject.toml +188 -0
- test0001-0.1.0/src/shomer/__init__.py +9 -0
- test0001-0.1.0/src/shomer/app.py +64 -0
- test0001-0.1.0/src/shomer/cli.py +23 -0
- test0001-0.1.0/src/shomer/core/__init__.py +4 -0
- test0001-0.1.0/src/shomer/core/database.py +26 -0
- test0001-0.1.0/src/shomer/core/settings.py +42 -0
- test0001-0.1.0/src/shomer/py.typed +0 -0
- test0001-0.1.0/src/shomer/routes/__init__.py +4 -0
- test0001-0.1.0/src/shomer/routes/docs.py +32 -0
- test0001-0.1.0/src/shomer/routes/health.py +34 -0
- test0001-0.1.0/src/shomer/routes/views.py +19 -0
- test0001-0.1.0/src/shomer/static/favicon.ico +0 -0
- test0001-0.1.0/src/shomer/tasks/__init__.py +4 -0
- test0001-0.1.0/src/shomer/templates/index.html +14 -0
- test0001-0.1.0/src/shomer/worker.py +29 -0
test0001-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
test0001-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: test0001
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: OIDC/OAuth2 authentication service.
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: oidc,oauth2,authentication,authorization,identity,openid-connect,jwt,token,sso,security
|
|
7
|
+
Author: Chris
|
|
8
|
+
Author-email: goabonga@pm.me
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Session
|
|
22
|
+
Classifier: Topic :: Security
|
|
23
|
+
Classifier: Topic :: System :: Systems Administration :: Authentication/Directory
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Provides-Extra: server
|
|
26
|
+
Provides-Extra: worker
|
|
27
|
+
Requires-Dist: alembic ; extra == "server"
|
|
28
|
+
Requires-Dist: asyncpg ; extra == "server"
|
|
29
|
+
Requires-Dist: celery[redis] ; extra == "worker"
|
|
30
|
+
Requires-Dist: fastapi ; extra == "server"
|
|
31
|
+
Requires-Dist: jinja2 ; extra == "server"
|
|
32
|
+
Requires-Dist: pydantic-settings
|
|
33
|
+
Requires-Dist: sqlalchemy[asyncio] ; extra == "server"
|
|
34
|
+
Requires-Dist: uvicorn[standard] ; extra == "server"
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# Shomer
|
|
38
|
+
|
|
39
|
+
OIDC/OAuth2 authentication service.
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### CLI only
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install shomer
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### CLI + Server
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install shomer[server]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### CLI + Worker
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install shomer[worker]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### All components
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install shomer[server,worker]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Docker
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
docker pull ghcr.io/goabonga/shomer-server:latest
|
|
71
|
+
docker run -p 8000:8000 ghcr.io/goabonga/shomer-server:latest
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Docker Compose
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/goabonga/shomer.git
|
|
78
|
+
cd shomer
|
|
79
|
+
make docker-up
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Helm
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
helm install shomer oci://ghcr.io/goabonga/shomer-chart/shomer --version 0.1.0
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Development
|
|
89
|
+
|
|
90
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
[MIT](LICENSE)
|
|
95
|
+
|
test0001-0.1.0/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Shomer
|
|
2
|
+
|
|
3
|
+
OIDC/OAuth2 authentication service.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### CLI only
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install shomer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### CLI + Server
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install shomer[server]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### CLI + Worker
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install shomer[worker]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### All components
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install shomer[server,worker]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Docker
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
docker pull ghcr.io/goabonga/shomer-server:latest
|
|
35
|
+
docker run -p 8000:8000 ghcr.io/goabonga/shomer-server:latest
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Docker Compose
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/goabonga/shomer.git
|
|
42
|
+
cd shomer
|
|
43
|
+
make docker-up
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Helm
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
helm install shomer oci://ghcr.io/goabonga/shomer-chart/shomer --version 0.1.0
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Development
|
|
53
|
+
|
|
54
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contribution guide.
|
|
55
|
+
|
|
56
|
+
## License
|
|
57
|
+
|
|
58
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "test0001"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "OIDC/OAuth2 authentication service."
|
|
5
|
+
authors = ["Chris <goabonga@pm.me>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
packages = [
|
|
9
|
+
{ include = "shomer", from = "src" }
|
|
10
|
+
]
|
|
11
|
+
include = [
|
|
12
|
+
"src/shomer/py.typed"
|
|
13
|
+
]
|
|
14
|
+
exclude = [
|
|
15
|
+
"tests",
|
|
16
|
+
"scripts",
|
|
17
|
+
"docs",
|
|
18
|
+
"Makefile"
|
|
19
|
+
]
|
|
20
|
+
keywords = [
|
|
21
|
+
"oidc",
|
|
22
|
+
"oauth2",
|
|
23
|
+
"authentication",
|
|
24
|
+
"authorization",
|
|
25
|
+
"identity",
|
|
26
|
+
"openid-connect",
|
|
27
|
+
"jwt",
|
|
28
|
+
"token",
|
|
29
|
+
"sso",
|
|
30
|
+
"security"
|
|
31
|
+
]
|
|
32
|
+
classifiers = [
|
|
33
|
+
"Development Status :: 3 - Alpha",
|
|
34
|
+
"Intended Audience :: Developers",
|
|
35
|
+
"License :: OSI Approved :: MIT License",
|
|
36
|
+
"Natural Language :: English",
|
|
37
|
+
"Operating System :: OS Independent",
|
|
38
|
+
"Programming Language :: Python :: 3",
|
|
39
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
40
|
+
"Programming Language :: Python :: 3.10",
|
|
41
|
+
"Programming Language :: Python :: 3.11",
|
|
42
|
+
"Programming Language :: Python :: 3.12",
|
|
43
|
+
"Programming Language :: Python :: 3.13",
|
|
44
|
+
"Topic :: Security",
|
|
45
|
+
"Topic :: Internet :: WWW/HTTP :: Session",
|
|
46
|
+
"Topic :: System :: Systems Administration :: Authentication/Directory",
|
|
47
|
+
"Typing :: Typed"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.poetry.dependencies]
|
|
51
|
+
python = ">=3.10"
|
|
52
|
+
pydantic-settings = "*"
|
|
53
|
+
fastapi = {version = "*", optional = true}
|
|
54
|
+
uvicorn = {extras = ["standard"], version = "*", optional = true}
|
|
55
|
+
jinja2 = {version = "*", optional = true}
|
|
56
|
+
sqlalchemy = {extras = ["asyncio"], version = "*", optional = true}
|
|
57
|
+
asyncpg = {version = "*", optional = true}
|
|
58
|
+
alembic = {version = "*", optional = true}
|
|
59
|
+
celery = {extras = ["redis"], version = "*", optional = true}
|
|
60
|
+
|
|
61
|
+
[tool.poetry.extras]
|
|
62
|
+
server = ["fastapi", "uvicorn", "jinja2", "sqlalchemy", "asyncpg", "alembic"]
|
|
63
|
+
worker = ["celery"]
|
|
64
|
+
|
|
65
|
+
[tool.poetry.group.dev]
|
|
66
|
+
optional = true
|
|
67
|
+
|
|
68
|
+
[tool.poetry.group.dev.dependencies]
|
|
69
|
+
commitizen = "*"
|
|
70
|
+
pytest = "*"
|
|
71
|
+
pytest-cov = "*"
|
|
72
|
+
ruff = "*"
|
|
73
|
+
isort = "*"
|
|
74
|
+
mypy = "*"
|
|
75
|
+
|
|
76
|
+
[tool.poetry.group.test]
|
|
77
|
+
optional = true
|
|
78
|
+
|
|
79
|
+
[tool.poetry.group.test.dependencies]
|
|
80
|
+
behave = "*"
|
|
81
|
+
playwright = "*"
|
|
82
|
+
|
|
83
|
+
[tool.poetry.group.favicon]
|
|
84
|
+
optional = true
|
|
85
|
+
|
|
86
|
+
[tool.poetry.group.favicon.dependencies]
|
|
87
|
+
cairosvg = "*"
|
|
88
|
+
Pillow = "*"
|
|
89
|
+
|
|
90
|
+
[tool.poetry.group.docs]
|
|
91
|
+
optional = true
|
|
92
|
+
|
|
93
|
+
[tool.poetry.group.docs.dependencies]
|
|
94
|
+
mkdocs = "*"
|
|
95
|
+
mkdocs-material = "*"
|
|
96
|
+
mkdocs-gen-files = "*"
|
|
97
|
+
mkdocs-autorefs = "*"
|
|
98
|
+
mkdocstrings = {extras = ["python"], version = "*"}
|
|
99
|
+
mkdocs-macros-plugin = "*"
|
|
100
|
+
|
|
101
|
+
[tool.poetry.scripts]
|
|
102
|
+
shomer = "shomer.cli:main"
|
|
103
|
+
|
|
104
|
+
[tool.behave]
|
|
105
|
+
paths = ["features"]
|
|
106
|
+
|
|
107
|
+
[[tool.poetry-auto-export.exports]]
|
|
108
|
+
output = "requirements.txt"
|
|
109
|
+
without_hashes = true
|
|
110
|
+
|
|
111
|
+
[[tool.poetry-auto-export.exports]]
|
|
112
|
+
output = "requirements-server.txt"
|
|
113
|
+
without_hashes = true
|
|
114
|
+
extras = ["server"]
|
|
115
|
+
|
|
116
|
+
[[tool.poetry-auto-export.exports]]
|
|
117
|
+
output = "requirements-worker.txt"
|
|
118
|
+
without_hashes = true
|
|
119
|
+
extras = ["worker"]
|
|
120
|
+
|
|
121
|
+
[[tool.poetry-auto-export.exports]]
|
|
122
|
+
output = "requirements-dev.txt"
|
|
123
|
+
without_hashes = true
|
|
124
|
+
with = ["dev", "docs", "test"]
|
|
125
|
+
extras = ["server", "worker"]
|
|
126
|
+
|
|
127
|
+
[build-system]
|
|
128
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
129
|
+
build-backend = "poetry.core.masonry.api"
|
|
130
|
+
|
|
131
|
+
[tool.commitizen]
|
|
132
|
+
name = "cz_conventional_commits"
|
|
133
|
+
version = "0.1.0"
|
|
134
|
+
tag_format = "v$version"
|
|
135
|
+
update_changelog_on_bump = true
|
|
136
|
+
changelog_file = "CHANGELOG.md"
|
|
137
|
+
changelog_incremental = true
|
|
138
|
+
major_version_zero = true
|
|
139
|
+
version_files = [
|
|
140
|
+
"pyproject.toml:version",
|
|
141
|
+
"src/shomer/__init__.py",
|
|
142
|
+
"tests/test_version.py:__expected_version__",
|
|
143
|
+
"features/steps/ui_steps.py:__expected_version__",
|
|
144
|
+
"chart/shomer/Chart.yaml:version",
|
|
145
|
+
"chart/shomer/Chart.yaml:appVersion",
|
|
146
|
+
"README.md:--version",
|
|
147
|
+
"mkdocs.yml:version"
|
|
148
|
+
]
|
|
149
|
+
gpg_sign = true
|
|
150
|
+
|
|
151
|
+
[tool.pytest.ini_options]
|
|
152
|
+
minversion = "7.0"
|
|
153
|
+
addopts = "--cov=src/shomer --cov-report=term --cov-report=xml --cov-report=html --junitxml=junit.xml -o junit_family=legacy"
|
|
154
|
+
testpaths = ["tests"]
|
|
155
|
+
|
|
156
|
+
[tool.coverage.run]
|
|
157
|
+
omit = [
|
|
158
|
+
"tests/*"
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
[tool.coverage.report]
|
|
162
|
+
exclude_lines = [
|
|
163
|
+
"pragma: no cover",
|
|
164
|
+
"if TYPE_CHECKING:",
|
|
165
|
+
"if __name__ == .__main__.:",
|
|
166
|
+
"raise NotImplementedError",
|
|
167
|
+
"pass # abstract",
|
|
168
|
+
"\\.\\.\\.",
|
|
169
|
+
"except ImportError",
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
[tool.isort]
|
|
173
|
+
profile = "black"
|
|
174
|
+
src_paths = ["src", "tests"]
|
|
175
|
+
|
|
176
|
+
[tool.ruff]
|
|
177
|
+
line-length = 88
|
|
178
|
+
src = ["src", "tests"]
|
|
179
|
+
exclude = ["__pycache__"]
|
|
180
|
+
|
|
181
|
+
[tool.mypy]
|
|
182
|
+
python_version = "3.11"
|
|
183
|
+
strict = true
|
|
184
|
+
files = ["src", "tests"]
|
|
185
|
+
|
|
186
|
+
[[tool.mypy.overrides]]
|
|
187
|
+
module = ["celery", "celery.*"]
|
|
188
|
+
ignore_missing_imports = true
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""FastAPI application for the Shomer authentication service."""
|
|
5
|
+
|
|
6
|
+
import asyncio
|
|
7
|
+
from contextlib import asynccontextmanager
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import AsyncIterator
|
|
10
|
+
|
|
11
|
+
from fastapi import FastAPI
|
|
12
|
+
from fastapi.staticfiles import StaticFiles
|
|
13
|
+
from fastapi.templating import Jinja2Templates
|
|
14
|
+
|
|
15
|
+
from . import __version__
|
|
16
|
+
from .core.settings import get_settings
|
|
17
|
+
|
|
18
|
+
settings = get_settings()
|
|
19
|
+
|
|
20
|
+
state: dict[str, str] = {"status": "starting"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@asynccontextmanager
|
|
24
|
+
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
25
|
+
"""Application lifespan handler."""
|
|
26
|
+
from shomer.core.database import engine
|
|
27
|
+
|
|
28
|
+
await asyncio.sleep(settings.startup_delay)
|
|
29
|
+
state["status"] = "ready"
|
|
30
|
+
yield
|
|
31
|
+
state["status"] = "shutting_down"
|
|
32
|
+
await engine.dispose()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create_app() -> FastAPI:
|
|
36
|
+
"""Create and configure the FastAPI application."""
|
|
37
|
+
application = FastAPI(
|
|
38
|
+
title="Shomer",
|
|
39
|
+
description="OIDC/OAuth2 authentication service.",
|
|
40
|
+
version=__version__,
|
|
41
|
+
docs_url=None,
|
|
42
|
+
redoc_url=None,
|
|
43
|
+
lifespan=lifespan,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
application.mount(
|
|
47
|
+
"/static",
|
|
48
|
+
StaticFiles(directory=str(Path(__file__).parent / "static")),
|
|
49
|
+
name="static",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
from shomer.routes.docs import router as docs_router
|
|
53
|
+
from shomer.routes.health import router as health_router
|
|
54
|
+
from shomer.routes.views import router as views_router
|
|
55
|
+
|
|
56
|
+
application.include_router(health_router)
|
|
57
|
+
application.include_router(docs_router)
|
|
58
|
+
application.include_router(views_router)
|
|
59
|
+
|
|
60
|
+
return application
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))
|
|
64
|
+
app = create_app()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Command-line interface for the Shomer authentication service."""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
|
|
8
|
+
from shomer import __version__
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> None:
|
|
12
|
+
"""Parse command-line arguments and execute the CLI."""
|
|
13
|
+
parser = argparse.ArgumentParser(
|
|
14
|
+
prog="shomer", description="OIDC/OAuth2 authentication service."
|
|
15
|
+
)
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--version", action="version", version=f"%(prog)s v{__version__}"
|
|
18
|
+
)
|
|
19
|
+
parser.parse_args()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if __name__ == "__main__":
|
|
23
|
+
main()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Async SQLAlchemy database setup."""
|
|
5
|
+
|
|
6
|
+
from collections.abc import AsyncIterator
|
|
7
|
+
|
|
8
|
+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
|
|
9
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
10
|
+
|
|
11
|
+
from shomer.core.settings import get_settings
|
|
12
|
+
|
|
13
|
+
settings = get_settings()
|
|
14
|
+
|
|
15
|
+
engine = create_async_engine(settings.database_url, echo=False)
|
|
16
|
+
async_session = async_sessionmaker(engine, expire_on_commit=False)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Base(DeclarativeBase):
|
|
20
|
+
"""Base class for SQLAlchemy models."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
async def get_db() -> AsyncIterator[AsyncSession]: # pragma: no cover
|
|
24
|
+
"""Yield an async database session."""
|
|
25
|
+
async with async_session() as session:
|
|
26
|
+
yield session
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Application settings using pydantic-settings."""
|
|
5
|
+
|
|
6
|
+
from functools import lru_cache
|
|
7
|
+
|
|
8
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Settings(BaseSettings):
|
|
12
|
+
"""Application configuration loaded from environment variables."""
|
|
13
|
+
|
|
14
|
+
model_config = SettingsConfigDict(env_prefix="SHOMER_", env_file=".env")
|
|
15
|
+
|
|
16
|
+
# Server
|
|
17
|
+
host: str = "0.0.0.0"
|
|
18
|
+
port: int = 8000
|
|
19
|
+
startup_delay: float = 1.0
|
|
20
|
+
|
|
21
|
+
# Database
|
|
22
|
+
database_url: str = "postgresql+asyncpg://shomer:shomer@localhost:5432/shomer"
|
|
23
|
+
|
|
24
|
+
# Celery
|
|
25
|
+
celery_broker_url: str = "redis://localhost:6379/0"
|
|
26
|
+
celery_result_backend: str = ""
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def celery_backend(self) -> str:
|
|
30
|
+
"""Return result backend, defaulting to broker URL."""
|
|
31
|
+
return self.celery_result_backend or self.celery_broker_url
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def database_url_sync(self) -> str:
|
|
35
|
+
"""Return sync database URL for Alembic."""
|
|
36
|
+
return self.database_url.replace("+asyncpg", "")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@lru_cache
|
|
40
|
+
def get_settings() -> Settings:
|
|
41
|
+
"""Return cached settings instance."""
|
|
42
|
+
return Settings()
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Documentation routes."""
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter, Request
|
|
7
|
+
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
|
8
|
+
from fastapi.responses import HTMLResponse
|
|
9
|
+
|
|
10
|
+
router = APIRouter()
|
|
11
|
+
|
|
12
|
+
FAVICON_URL = "/static/favicon.ico"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@router.get("/docs", include_in_schema=False)
|
|
16
|
+
def docs(request: Request) -> HTMLResponse:
|
|
17
|
+
"""Render the Swagger UI documentation."""
|
|
18
|
+
return get_swagger_ui_html(
|
|
19
|
+
openapi_url=request.app.openapi_url,
|
|
20
|
+
title=f"{request.app.title} - Docs",
|
|
21
|
+
swagger_favicon_url=FAVICON_URL,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@router.get("/redoc", include_in_schema=False)
|
|
26
|
+
def redoc(request: Request) -> HTMLResponse:
|
|
27
|
+
"""Render the ReDoc documentation."""
|
|
28
|
+
return get_redoc_html(
|
|
29
|
+
openapi_url=request.app.openapi_url,
|
|
30
|
+
title=f"{request.app.title} - ReDoc",
|
|
31
|
+
redoc_favicon_url=FAVICON_URL,
|
|
32
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Health check routes."""
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter
|
|
7
|
+
from fastapi.responses import JSONResponse
|
|
8
|
+
|
|
9
|
+
router = APIRouter()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Imported at function level to avoid circular imports
|
|
13
|
+
def _get_state() -> dict[str, str]:
|
|
14
|
+
from shomer.app import state
|
|
15
|
+
|
|
16
|
+
return state
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@router.get("/liveness", include_in_schema=False)
|
|
20
|
+
def liveness() -> JSONResponse:
|
|
21
|
+
"""Liveness probe: is the process alive? (k8s livenessProbe / docker HEALTHCHECK)."""
|
|
22
|
+
state = _get_state()
|
|
23
|
+
if state["status"] == "shutting_down":
|
|
24
|
+
return JSONResponse({"status": state["status"]}, status_code=503)
|
|
25
|
+
return JSONResponse({"status": state["status"]}, status_code=200)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.get("/readiness", include_in_schema=False)
|
|
29
|
+
def readiness() -> JSONResponse:
|
|
30
|
+
"""Readiness probe: is the app ready to serve traffic? (k8s readinessProbe)."""
|
|
31
|
+
state = _get_state()
|
|
32
|
+
if state["status"] != "ready":
|
|
33
|
+
return JSONResponse({"status": state["status"]}, status_code=503)
|
|
34
|
+
return JSONResponse({"status": state["status"]}, status_code=200)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""HTML view routes."""
|
|
5
|
+
|
|
6
|
+
from fastapi import APIRouter, Request
|
|
7
|
+
from fastapi.responses import HTMLResponse
|
|
8
|
+
|
|
9
|
+
from shomer import __version__
|
|
10
|
+
|
|
11
|
+
router = APIRouter()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@router.get("/", response_class=HTMLResponse, include_in_schema=False)
|
|
15
|
+
def index(request: Request) -> HTMLResponse:
|
|
16
|
+
"""Render the home page."""
|
|
17
|
+
from shomer.app import templates
|
|
18
|
+
|
|
19
|
+
return templates.TemplateResponse(request, "index.html", {"version": __version__})
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Shomer</title>
|
|
7
|
+
<link rel="icon" href="/static/favicon.ico" type="image/x-icon">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Shomer</h1>
|
|
11
|
+
<p>OIDC/OAuth2 authentication service.</p>
|
|
12
|
+
<p>v{{ version }}</p>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 Chris <goabonga@pm.me>
|
|
3
|
+
|
|
4
|
+
"""Celery worker for the Shomer authentication service."""
|
|
5
|
+
|
|
6
|
+
from celery import Celery
|
|
7
|
+
|
|
8
|
+
from shomer.core.settings import get_settings
|
|
9
|
+
|
|
10
|
+
settings = get_settings()
|
|
11
|
+
|
|
12
|
+
app = Celery(
|
|
13
|
+
"shomer",
|
|
14
|
+
broker=settings.celery_broker_url,
|
|
15
|
+
backend=settings.celery_backend,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
app.config_from_object(
|
|
19
|
+
{
|
|
20
|
+
"task_serializer": "json",
|
|
21
|
+
"result_serializer": "json",
|
|
22
|
+
"accept_content": ["json"],
|
|
23
|
+
"timezone": "UTC",
|
|
24
|
+
"enable_utc": True,
|
|
25
|
+
"beat_schedule": {},
|
|
26
|
+
}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
app.autodiscover_tasks(["shomer.tasks"])
|