one-public-api 0.1.0a0__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.
- one_public_api-0.1.0a0/PKG-INFO +47 -0
- one_public_api-0.1.0a0/README.md +1 -0
- one_public_api-0.1.0a0/pyproject.toml +68 -0
- one_public_api-0.1.0a0/src/one_public_api/__init__.py +3 -0
- one_public_api-0.1.0a0/src/one_public_api/common/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/common/constants.py +163 -0
- one_public_api-0.1.0a0/src/one_public_api/common/init_data.py +77 -0
- one_public_api-0.1.0a0/src/one_public_api/common/query_param.py +52 -0
- one_public_api-0.1.0a0/src/one_public_api/common/tools.py +96 -0
- one_public_api-0.1.0a0/src/one_public_api/common/utility/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/common/utility/files.py +23 -0
- one_public_api-0.1.0a0/src/one_public_api/common/utility/str.py +59 -0
- one_public_api-0.1.0a0/src/one_public_api/core/__init__.py +10 -0
- one_public_api-0.1.0a0/src/one_public_api/core/database.py +42 -0
- one_public_api-0.1.0a0/src/one_public_api/core/exceptions.py +66 -0
- one_public_api-0.1.0a0/src/one_public_api/core/extensions.py +100 -0
- one_public_api-0.1.0a0/src/one_public_api/core/i18n.py +82 -0
- one_public_api-0.1.0a0/src/one_public_api/core/log.py +189 -0
- one_public_api-0.1.0a0/src/one_public_api/core/py.typed +0 -0
- one_public_api-0.1.0a0/src/one_public_api/core/settings.py +168 -0
- one_public_api-0.1.0a0/src/one_public_api/crud/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/crud/data_creator.py +107 -0
- one_public_api-0.1.0a0/src/one_public_api/crud/data_deleter.py +42 -0
- one_public_api-0.1.0a0/src/one_public_api/crud/data_reader.py +105 -0
- one_public_api-0.1.0a0/src/one_public_api/crud/data_updater.py +43 -0
- one_public_api-0.1.0a0/src/one_public_api/locales/en/LC_MESSAGES/messages.mo +0 -0
- one_public_api-0.1.0a0/src/one_public_api/locales/en/LC_MESSAGES/messages.po +379 -0
- one_public_api-0.1.0a0/src/one_public_api/locales/ja/LC_MESSAGES/messages.mo +0 -0
- one_public_api-0.1.0a0/src/one_public_api/locales/ja/LC_MESSAGES/messages.po +379 -0
- one_public_api-0.1.0a0/src/one_public_api/locales/messages.pot +377 -0
- one_public_api-0.1.0a0/src/one_public_api/main.py +20 -0
- one_public_api-0.1.0a0/src/one_public_api/models/__init__.py +5 -0
- one_public_api-0.1.0a0/src/one_public_api/models/mixins/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/models/mixins/id_mixin.py +23 -0
- one_public_api-0.1.0a0/src/one_public_api/models/mixins/maintenance_mixin.py +21 -0
- one_public_api-0.1.0a0/src/one_public_api/models/mixins/password_mixin.py +11 -0
- one_public_api-0.1.0a0/src/one_public_api/models/mixins/timestamp_mixin.py +18 -0
- one_public_api-0.1.0a0/src/one_public_api/models/py.typed +0 -0
- one_public_api-0.1.0a0/src/one_public_api/models/system/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/models/system/configuration_model.py +111 -0
- one_public_api-0.1.0a0/src/one_public_api/models/system/feature_model.py +65 -0
- one_public_api-0.1.0a0/src/one_public_api/models/system/user_model.py +97 -0
- one_public_api-0.1.0a0/src/one_public_api/py.typed +0 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/authenticate_router.py +117 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/base_route.py +63 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/configuration_router.py +156 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/feature_router.py +124 -0
- one_public_api-0.1.0a0/src/one_public_api/routers/user_router.py +126 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/authenticate_schema.py +68 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/configuration_schema.py +72 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/feature_schema.py +65 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/response_schema.py +38 -0
- one_public_api-0.1.0a0/src/one_public_api/schemas/user_schema.py +78 -0
- one_public_api-0.1.0a0/src/one_public_api/services/__init__.py +0 -0
- one_public_api-0.1.0a0/src/one_public_api/services/authenticate_service.py +164 -0
- one_public_api-0.1.0a0/src/one_public_api/services/base_service.py +109 -0
- one_public_api-0.1.0a0/src/one_public_api/services/configuration_service.py +22 -0
- one_public_api-0.1.0a0/src/one_public_api/services/feture_service.py +30 -0
- one_public_api-0.1.0a0/src/one_public_api/services/user_service.py +32 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: one-public-api
|
|
3
|
+
Version: 0.1.0a0
|
|
4
|
+
Summary: This package provides the API layer of the One Public Framework.
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2025 Roba
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
Author: Roba
|
|
27
|
+
Author-email: roba@one-coder.com
|
|
28
|
+
Requires-Python: >=3.11,<4.0
|
|
29
|
+
Classifier: License :: Other/Proprietary License
|
|
30
|
+
Classifier: Programming Language :: Python :: 3
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
33
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
34
|
+
Requires-Dist: bcrypt (>=4.3.0,<5.0.0)
|
|
35
|
+
Requires-Dist: fastapi (>=0.115.12,<0.116.0)
|
|
36
|
+
Requires-Dist: passlib[bcrypt] (>=1.7.4,<2.0.0)
|
|
37
|
+
Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0)
|
|
38
|
+
Requires-Dist: pydantic-settings (>=2.9.1,<3.0.0)
|
|
39
|
+
Requires-Dist: pydantic[email] (>=2.11.7,<3.0.0)
|
|
40
|
+
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
|
41
|
+
Requires-Dist: python-multipart (>=0.0.20,<0.0.21)
|
|
42
|
+
Requires-Dist: sqlmodel (>=0.0.24,<0.0.25)
|
|
43
|
+
Requires-Dist: uvicorn[standard] (>=0.34.3,<0.35.0)
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# One Public API
|
|
47
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# One Public API
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "one-public-api"
|
|
3
|
+
version = "0.1.0-alpha"
|
|
4
|
+
description = "This package provides the API layer of the One Public Framework."
|
|
5
|
+
license = {file = "../LICENSE"}
|
|
6
|
+
authors = [
|
|
7
|
+
{name = "Roba",email = "roba@one-coder.com"}
|
|
8
|
+
]
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11,<4.0"
|
|
11
|
+
|
|
12
|
+
[tool.poetry]
|
|
13
|
+
name = "one-public-api"
|
|
14
|
+
version = "0.1.0-alpha"
|
|
15
|
+
description = "This package provides the API layer of the One Public Framework."
|
|
16
|
+
authors = ["Roba <roba@one-coder.com>"]
|
|
17
|
+
packages = [{ include = "one_public_api", from = "src" }]
|
|
18
|
+
include = ["src/one_public_api/locales/**/*.mo"]
|
|
19
|
+
|
|
20
|
+
[tool.poetry.dependencies]
|
|
21
|
+
python = ">=3.11,<4.0"
|
|
22
|
+
fastapi = ">=0.115.12,<0.116.0"
|
|
23
|
+
pydantic = { version = ">=2.11.7,<3.0.0", extras = ["email"] }
|
|
24
|
+
pydantic-settings = ">=2.9.1,<3.0.0"
|
|
25
|
+
python-multipart = ">=0.0.20,<0.0.21"
|
|
26
|
+
psycopg2-binary = ">=2.9.10,<3.0.0"
|
|
27
|
+
sqlmodel = ">=0.0.24,<0.0.25"
|
|
28
|
+
uvicorn = { version = ">=0.34.3,<0.35.0", extras = ["standard"] }
|
|
29
|
+
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
|
|
30
|
+
bcrypt = "^4.3.0"
|
|
31
|
+
pyjwt = "^2.10.1"
|
|
32
|
+
|
|
33
|
+
[tool.poetry.group.dev.dependencies]
|
|
34
|
+
alembic = "^1.16.1"
|
|
35
|
+
babel = "^2.17.0"
|
|
36
|
+
docformatter = "^1.7.7"
|
|
37
|
+
httpx = "^0.28.1"
|
|
38
|
+
mypy = "^1.16.0"
|
|
39
|
+
pre-commit = "^4.2.0"
|
|
40
|
+
pytest = "^8.4.0"
|
|
41
|
+
ruff = "^0.11.13"
|
|
42
|
+
types-passlib = "^1.7.7.20250602"
|
|
43
|
+
|
|
44
|
+
[build-system]
|
|
45
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
46
|
+
build-backend = "poetry.core.masonry.api"
|
|
47
|
+
|
|
48
|
+
[tool.mypy]
|
|
49
|
+
mypy_path = "src"
|
|
50
|
+
python_version = "3.11"
|
|
51
|
+
strict = true
|
|
52
|
+
exclude = [".venv", "migrations/versions", "tests"]
|
|
53
|
+
|
|
54
|
+
[tool.black]
|
|
55
|
+
line-length = 88
|
|
56
|
+
target-version = ["py311"]
|
|
57
|
+
|
|
58
|
+
[tool.ruff]
|
|
59
|
+
line-length = 88
|
|
60
|
+
target-version = "py311"
|
|
61
|
+
exclude = [".venv", "__pycache__", "migrations"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff.lint]
|
|
64
|
+
select = ["E", "F", "I"]
|
|
65
|
+
|
|
66
|
+
[tool.docformatter]
|
|
67
|
+
pre-summary-newline = true
|
|
68
|
+
recursive = true
|
|
File without changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# ===== Constant Definitions ===========================================================
|
|
2
|
+
import importlib.resources
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Tuple
|
|
6
|
+
|
|
7
|
+
# Version of the One Public API
|
|
8
|
+
VERSION: str = "0.1.0-alpha"
|
|
9
|
+
# Default Language
|
|
10
|
+
DEFAULT_LANGUAGE: str = "en"
|
|
11
|
+
# Default path for locale files
|
|
12
|
+
DEFAULT_LOCALES_PATH: str = "locales"
|
|
13
|
+
|
|
14
|
+
# ----- Encoding Constants -------------------------------------------------------------
|
|
15
|
+
# Encoding format: UTF-8
|
|
16
|
+
ENCODE_UTF8: str = "utf-8"
|
|
17
|
+
|
|
18
|
+
# ----- File Settings ------------------------------------------------------------------
|
|
19
|
+
# Log File Extension
|
|
20
|
+
EXT_LOG = ".log"
|
|
21
|
+
# SQLite File Extension
|
|
22
|
+
EXT_SQLITE = ".sqlite3"
|
|
23
|
+
|
|
24
|
+
# ----- Security Settings --------------------------------------------------------------
|
|
25
|
+
# Access token expiration time (in minutes)
|
|
26
|
+
ACCESS_TOKEN_EXPIRE = 15
|
|
27
|
+
# Refresh token expiration time (in minutes)
|
|
28
|
+
REFRESH_TOKEN_EXPIRE = 30 * 24 * 60
|
|
29
|
+
# Algorithm used to sign the JWT tokens (e.g., HS256, RS256)
|
|
30
|
+
JWT_ALGORITHM = "HS256"
|
|
31
|
+
|
|
32
|
+
# File name for .env files
|
|
33
|
+
FILES_ENV: List[str] = [".env", ".env.dev", ".env.test", ".env.stage", ".env.prod"]
|
|
34
|
+
|
|
35
|
+
# Folder for localization resources
|
|
36
|
+
FOLDER_LOCALES: str = "locales"
|
|
37
|
+
# Output folder for log files
|
|
38
|
+
FOLDER_LOGS: str = "logs"
|
|
39
|
+
# Root folder of the source code
|
|
40
|
+
FOLDER_SRC: str = "src"
|
|
41
|
+
# Root folder of the OPA
|
|
42
|
+
FOLDER_OPA: str = "one_public_api"
|
|
43
|
+
|
|
44
|
+
# Absolute Path of Application directory
|
|
45
|
+
PATH_APP: str = str(Path(__file__).resolve().parent.parent.parent.parent.parent)
|
|
46
|
+
# Absolute Path of backend directory
|
|
47
|
+
PATH_ROOT: str = str(Path(__file__).resolve().parent.parent.parent.parent)
|
|
48
|
+
# Absolute Path of log directory
|
|
49
|
+
PATH_LOG: str = os.path.join(PATH_APP, FOLDER_LOGS)
|
|
50
|
+
# Absolute Path of src directory
|
|
51
|
+
PATH_SRC: str = os.path.join(PATH_ROOT, FOLDER_SRC)
|
|
52
|
+
# Absolute Path of OPA directory
|
|
53
|
+
PATH_OPA = importlib.resources.files("one_public_api")
|
|
54
|
+
# Absolute Path of language package directory
|
|
55
|
+
PATH_LOCALES = PATH_OPA.joinpath(FOLDER_LOCALES)
|
|
56
|
+
# Environment File Path
|
|
57
|
+
# Files listed later have higher priority; earlier ones are ignored if multiple exists.
|
|
58
|
+
PATHS_ENV: Tuple[str, ...] = tuple(os.path.join(PATH_APP, env) for env in FILES_ENV)
|
|
59
|
+
|
|
60
|
+
# ----- Database Settings --------------------------------------------------------------
|
|
61
|
+
# The default number of connections to allow in connection pool "overflow"
|
|
62
|
+
DB_DEFAULT_MAX_OVERFLOW_SIZE: int = 10
|
|
63
|
+
# The default number of connections to keep open inside the connection pool.
|
|
64
|
+
DB_DEFAULT_POOL_SIZE: int = 5
|
|
65
|
+
# The default number of seconds to wait before giving up on getting a connection from
|
|
66
|
+
# the pool.
|
|
67
|
+
DB_DEFAULT_TIMEOUT: int = 30
|
|
68
|
+
|
|
69
|
+
# The default number of rows to return in a query result.
|
|
70
|
+
DB_DEFAULT_LIMIT: int = 10
|
|
71
|
+
# The maximum number of rows to return in a query result.
|
|
72
|
+
DB_MAX_LIMIT: int = 100
|
|
73
|
+
|
|
74
|
+
# Table name prefix for system tables
|
|
75
|
+
DB_PREFIX_SYS: str = "sys_"
|
|
76
|
+
|
|
77
|
+
# ----- API URL Settings ---------------------------------------------------------------
|
|
78
|
+
# Common router path: blank
|
|
79
|
+
ROUTER_COMMON_BLANK = ""
|
|
80
|
+
# Common router path: ID
|
|
81
|
+
ROUTER_COMMON_WITH_ID = "/{target_id}"
|
|
82
|
+
# Common router path: admin
|
|
83
|
+
ROUTER_COMMON_ADMIN = "/admin"
|
|
84
|
+
# Common router path: admin with ID
|
|
85
|
+
ROUTER_COMMON_ADMIN_WITH_ID = "/admin/{target_id}"
|
|
86
|
+
|
|
87
|
+
# Signup API router path
|
|
88
|
+
ROUTER_AUTH_SIGNUP = "/signup"
|
|
89
|
+
# Login API router path
|
|
90
|
+
ROUTER_AUTH_LOGIN = "/login"
|
|
91
|
+
# Refresh Token API router path
|
|
92
|
+
ROUTER_AUTH_REFRESH = "/refresh"
|
|
93
|
+
# Profile API router path
|
|
94
|
+
ROUTER_AUTH_PROFILE = "/me"
|
|
95
|
+
# Logout API router path
|
|
96
|
+
ROUTER_AUTH_LOGOUT = "/logout"
|
|
97
|
+
|
|
98
|
+
# Path prefix for the authentication API router
|
|
99
|
+
ROUTER_PREFIX_AUTHENTICATION = "/auth"
|
|
100
|
+
# Path prefix for the feature API router
|
|
101
|
+
ROUTER_PREFIX_FEATURE = "/features"
|
|
102
|
+
# Path prefix for the configuration API router
|
|
103
|
+
ROUTER_PREFIX_CONFIGURATION = "/configurations"
|
|
104
|
+
# Path prefix for the user API router
|
|
105
|
+
ROUTER_PREFIX_USER = "/users"
|
|
106
|
+
|
|
107
|
+
# ----- Log Settings -------------------------------------------------------------------
|
|
108
|
+
# Default logging level for the API.
|
|
109
|
+
LOG_DEFAULT_LEVEL: str = "DEBUG"
|
|
110
|
+
# Default path for the log files.
|
|
111
|
+
LOG_DEFAULT_PATH: str = "logs/"
|
|
112
|
+
# Default name for the logger instance.
|
|
113
|
+
LOG_DEFAULT_NAME: str = "api"
|
|
114
|
+
# Defines the rotation policy for log files.
|
|
115
|
+
LOG_DEFAULT_ROTATING_WHEN: str = "D"
|
|
116
|
+
# Number of backup log files to be kept after rotation.
|
|
117
|
+
LOG_DEFAULT_ROTATING_BACKUP_COUNT: int = 7
|
|
118
|
+
# Specifies the default format of log messages.
|
|
119
|
+
LOG_DEFAULT_FORMAT: str = "%(asctime)s %(levelname)s\t%(name)s %(message)s"
|
|
120
|
+
|
|
121
|
+
# ----- Various Strings ----------------------------------------------------------------
|
|
122
|
+
# Text Logo
|
|
123
|
+
CHAR_LOGO: str = (
|
|
124
|
+
"S3U7S1U6S1U700S2P1S7P1S3U2S1B1S4U3P100S2P1S3"
|
|
125
|
+
"H1S3P1S4U2L1S4U3P100S2P1U7P1U3P1S2P1U3P1S4"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Prefix for items loadable from the .env file
|
|
129
|
+
CHAR_PREFIX_ENV: str = "API_"
|
|
130
|
+
# Newline character
|
|
131
|
+
CHAR_NEW_LINE: str = "\n"
|
|
132
|
+
# Key name used to store or retrieve the refresh token in cookies
|
|
133
|
+
CHAR_REFRESH_TOKEN_KEY = "refresh_token"
|
|
134
|
+
|
|
135
|
+
# Authenticate Header Name
|
|
136
|
+
HEADER_NAME_AUTHENTICATE = "WWW-Authenticate"
|
|
137
|
+
# Language Header Name
|
|
138
|
+
HEADER_NAME_LANGUAGE = "Accept-Language"
|
|
139
|
+
|
|
140
|
+
# ----- Various Numeric Values ---------------------------------------------------------
|
|
141
|
+
MAX_LENGTH_3: int = 3
|
|
142
|
+
MAX_LENGTH_6: int = 6
|
|
143
|
+
MAX_LENGTH_13: int = 13
|
|
144
|
+
MAX_LENGTH_55: int = 55
|
|
145
|
+
MAX_LENGTH_64: int = 64
|
|
146
|
+
MAX_LENGTH_100: int = 100
|
|
147
|
+
MAX_LENGTH_255: int = 255
|
|
148
|
+
MAX_LENGTH_500: int = 500
|
|
149
|
+
MAX_LENGTH_1000: int = 1000
|
|
150
|
+
|
|
151
|
+
# Maximum number of allowed failed login attempts before locking the account
|
|
152
|
+
MAX_LOGIN_FAILED_TIMES: int = 5
|
|
153
|
+
|
|
154
|
+
# ----- System Messages ----------------------------------------------------------------
|
|
155
|
+
# Debug Messages
|
|
156
|
+
MSG_D0000000: str = "Settings: %s"
|
|
157
|
+
|
|
158
|
+
# Error Messages
|
|
159
|
+
MSG_E0000001: str = "Implemented Error: %s"
|
|
160
|
+
MSG_E0000002: str = "Cannot write to the log file: %s permission denied."
|
|
161
|
+
MSG_E0000003: str = "Logger initialization failed: invalid configuration %s."
|
|
162
|
+
MSG_E0000004: str = "Logger initialization failed."
|
|
163
|
+
MSG_E0010001: str = "Unsupported Database Engine: %s"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from typing import Any, Dict, List
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI, HTTPException
|
|
4
|
+
from sqlmodel import Session
|
|
5
|
+
|
|
6
|
+
from one_public_api.common.utility.str import get_hashed_password
|
|
7
|
+
from one_public_api.crud.data_creator import DataCreator
|
|
8
|
+
from one_public_api.crud.data_reader import DataReader
|
|
9
|
+
from one_public_api.models import Configuration, Feature, User
|
|
10
|
+
from one_public_api.models.system.configuration_model import ConfigurationType
|
|
11
|
+
from one_public_api.routers.base_route import BaseRoute
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def init_configurations(session: Session) -> None:
|
|
15
|
+
configurations: List[Dict[str, Any]] = [
|
|
16
|
+
{
|
|
17
|
+
"name": "Application Name",
|
|
18
|
+
"key": "app_name",
|
|
19
|
+
"value": "One Public Framework",
|
|
20
|
+
"type": ConfigurationType.SYS,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "Application URL",
|
|
24
|
+
"key": "app_url",
|
|
25
|
+
"value": "http://localhost:5173",
|
|
26
|
+
"type": ConfigurationType.SYS,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Time Zone",
|
|
30
|
+
"key": "time_zone",
|
|
31
|
+
"value": "Asia/Tokyo",
|
|
32
|
+
"type": ConfigurationType.SYS,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "Language",
|
|
36
|
+
"key": "language",
|
|
37
|
+
"value": "en",
|
|
38
|
+
"type": ConfigurationType.SYS,
|
|
39
|
+
},
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
dc = DataCreator(session)
|
|
43
|
+
dc.all_if_not_exists(Configuration, configurations)
|
|
44
|
+
session.commit()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def init_features(app: FastAPI, session: Session) -> None:
|
|
48
|
+
features: List[Dict[str, Any]] = []
|
|
49
|
+
for route in app.routes:
|
|
50
|
+
if isinstance(route, BaseRoute):
|
|
51
|
+
features.append(
|
|
52
|
+
{
|
|
53
|
+
"name": getattr(route, "name"),
|
|
54
|
+
"description": getattr(route, "description"),
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
dc = DataCreator(session)
|
|
59
|
+
dc.all_if_not_exists(Feature, features)
|
|
60
|
+
session.commit()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def init_users(session: Session) -> None:
|
|
64
|
+
try:
|
|
65
|
+
dr = DataReader(session)
|
|
66
|
+
dr.one(User, {"name": "admin"})
|
|
67
|
+
except HTTPException:
|
|
68
|
+
users: List[Dict[str, Any]] = [
|
|
69
|
+
{
|
|
70
|
+
"name": "admin",
|
|
71
|
+
"password": get_hashed_password("<PASSWORD>"),
|
|
72
|
+
"email": "test@test.com",
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
dc = DataCreator(session)
|
|
76
|
+
dc.all_if_not_exists(User, users)
|
|
77
|
+
session.commit()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
from one_public_api.common import constants
|
|
6
|
+
from one_public_api.common.utility.str import to_camel
|
|
7
|
+
from one_public_api.core.i18n import translate as _
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class QueryParam(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Represents query parameters for database queries.
|
|
13
|
+
|
|
14
|
+
This class is used to encapsulate query parameters such as pagination settings,
|
|
15
|
+
sorting, and keyword filtering. It ensures that the provided parameters adhere
|
|
16
|
+
to specified constraints and formats.
|
|
17
|
+
|
|
18
|
+
Attributes
|
|
19
|
+
----------
|
|
20
|
+
offset : int
|
|
21
|
+
The offset from where to start fetching results. Must be greater than or
|
|
22
|
+
equal to 0. Default is 0.
|
|
23
|
+
limit : int
|
|
24
|
+
The maximum number of results to fetch. Must be greater than 0 and less
|
|
25
|
+
than or equal to the defined maximum limit (constants.DB_MAX_LIMIT). Default
|
|
26
|
+
is constants.DB_DEFAULT_LIMIT.
|
|
27
|
+
order_by : List[str]
|
|
28
|
+
A list of fields to order the query results by. Default is an empty list.
|
|
29
|
+
keywords : List[str]
|
|
30
|
+
A list of keywords for filtering query results. Default is an empty list.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
offset: int = Field(
|
|
34
|
+
default=0,
|
|
35
|
+
ge=0,
|
|
36
|
+
description=_("Offset from where to start"),
|
|
37
|
+
)
|
|
38
|
+
limit: int = Field(
|
|
39
|
+
default=constants.DB_DEFAULT_LIMIT,
|
|
40
|
+
gt=0,
|
|
41
|
+
le=constants.DB_MAX_LIMIT,
|
|
42
|
+
description=_("Limit"),
|
|
43
|
+
)
|
|
44
|
+
order_by: List[str] = Field(default=[], description=_("Order by"))
|
|
45
|
+
keywords: List[str] = Field(default=[], description=_("Keywords"))
|
|
46
|
+
# filtering: Dict[str, Any] = Field()
|
|
47
|
+
|
|
48
|
+
model_config = ConfigDict(
|
|
49
|
+
alias_generator=to_camel,
|
|
50
|
+
populate_by_name=True,
|
|
51
|
+
extra="forbid",
|
|
52
|
+
)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import importlib.util
|
|
3
|
+
from typing import Any, Awaitable, Callable, List, TypeVar, cast
|
|
4
|
+
|
|
5
|
+
import jwt
|
|
6
|
+
from fastapi import Request, Response
|
|
7
|
+
from pydantic.generics import GenericModel
|
|
8
|
+
from sqlmodel import SQLModel
|
|
9
|
+
|
|
10
|
+
from one_public_api.common import constants
|
|
11
|
+
from one_public_api.core.settings import settings
|
|
12
|
+
from one_public_api.schemas.response_schema import MessageSchema, ResponseSchema
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T", bound=SQLModel)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_response_data(
|
|
18
|
+
schema: GenericModel,
|
|
19
|
+
results: Any | List[Any] | None = None,
|
|
20
|
+
count: int | None = None,
|
|
21
|
+
detail: List[MessageSchema] | None = None,
|
|
22
|
+
) -> ResponseSchema[T]:
|
|
23
|
+
"""
|
|
24
|
+
Creates a response data object compliant with the `ResponseSchema[T]`
|
|
25
|
+
model.
|
|
26
|
+
|
|
27
|
+
This method validates the provided results data against the
|
|
28
|
+
specified schema and then encapsulates it in a standardized response
|
|
29
|
+
object. It supports single or multiple data results by handling both
|
|
30
|
+
individual and list inputs. Optionally, it includes additional metadata
|
|
31
|
+
such as count and detailed message schema.
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
schema : GenericModel
|
|
36
|
+
The schema model used to validate the provided results data. This should be
|
|
37
|
+
callable with a
|
|
38
|
+
`model_validate` method to perform validation.
|
|
39
|
+
results : Any or List[Any] or None
|
|
40
|
+
The data object(s) to be validated and included in the response. It can be a
|
|
41
|
+
single instance,
|
|
42
|
+
a list of instances, or None.
|
|
43
|
+
count : int or None, optional
|
|
44
|
+
The count metadata is typically used to indicate the total number of items in a
|
|
45
|
+
paginated context, or can be None if not applicable.
|
|
46
|
+
detail : List[MessageSchema] or None, optional
|
|
47
|
+
A list of detail messages that provide additional information regarding the
|
|
48
|
+
response or process, or can be None if no details are provided.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
ResponseSchema[T]
|
|
53
|
+
A response object containing the validated results data, optional count
|
|
54
|
+
metadata, and optional detailed messages.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if type(results) is list:
|
|
58
|
+
rst = [getattr(schema, "model_validate")(d) for d in results]
|
|
59
|
+
elif results is None:
|
|
60
|
+
rst = None
|
|
61
|
+
else:
|
|
62
|
+
rst = getattr(schema, "model_validate")(results)
|
|
63
|
+
|
|
64
|
+
rsp: ResponseSchema[T] = ResponseSchema(results=rst, count=count, detail=detail)
|
|
65
|
+
|
|
66
|
+
return rsp
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_username_from_token(token: str) -> str | None:
|
|
70
|
+
payload = jwt.decode(
|
|
71
|
+
token, settings.SECRET_KEY, algorithms=[constants.JWT_ALGORITHM]
|
|
72
|
+
)
|
|
73
|
+
return str(payload.get("sub")) if payload.get("sub") else None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def load_route_handler(
|
|
77
|
+
input_dir: str, module_name: str, handler_name: str
|
|
78
|
+
) -> (
|
|
79
|
+
Callable[[Request, Callable[[Request], Awaitable[Response]]], Awaitable[Response]]
|
|
80
|
+
| None
|
|
81
|
+
):
|
|
82
|
+
for file in glob.glob(input_dir, recursive=True):
|
|
83
|
+
spec = importlib.util.spec_from_file_location(module_name, file)
|
|
84
|
+
if spec:
|
|
85
|
+
mod = importlib.util.module_from_spec(spec)
|
|
86
|
+
if spec.loader and mod:
|
|
87
|
+
spec.loader.exec_module(mod)
|
|
88
|
+
return cast(
|
|
89
|
+
Callable[
|
|
90
|
+
[Request, Callable[[Request], Awaitable[Response]]],
|
|
91
|
+
Awaitable[Response],
|
|
92
|
+
],
|
|
93
|
+
getattr(mod, handler_name),
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def is_path_exists(path: str) -> bool:
|
|
5
|
+
"""
|
|
6
|
+
Checks the existence of a specified file system path.
|
|
7
|
+
|
|
8
|
+
This function determines whether a given file system path exists. It leverages
|
|
9
|
+
the `os.path.exists` method to perform this check. The function returns a
|
|
10
|
+
boolean value indicating the presence or absence of the specified path.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
path : str
|
|
15
|
+
The file system path to be checked for existence.
|
|
16
|
+
|
|
17
|
+
Returns
|
|
18
|
+
-------
|
|
19
|
+
bool
|
|
20
|
+
True if the specified path exists, otherwise False.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
return os.path.exists(path)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from passlib.context import CryptContext
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def convert_text_logo(text: str, with_version: bool = True) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Converts a text representation of a logo based on a mapping system into a
|
|
9
|
+
graphic-like string format.
|
|
10
|
+
|
|
11
|
+
This function takes an input string composed of specific codes that represent
|
|
12
|
+
various graphic characters and uses a mapping system to convert these codes
|
|
13
|
+
into a logo-like visual representation. The output string can optionally
|
|
14
|
+
include a version placeholder.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
text : str
|
|
19
|
+
A string representation of the logo code, where each two-character code
|
|
20
|
+
represents a mapping from the `b` list to the `a` list. The first
|
|
21
|
+
character of the code determines the character from `a` through its index
|
|
22
|
+
in `b`, and the second character determines the repetition count.
|
|
23
|
+
with_version : bool, optionally
|
|
24
|
+
If True, appends a version placeholder to the resulting logo.
|
|
25
|
+
|
|
26
|
+
Returns
|
|
27
|
+
-------
|
|
28
|
+
str
|
|
29
|
+
The generated logo as a string based on the input code. Returns an empty
|
|
30
|
+
string if the input contains invalid mappings.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
b: List[str] = ["S", "U", "H", "P", "L", "B"]
|
|
34
|
+
a: List[str] = [" ", "_", "-", "|", "/", "\\"]
|
|
35
|
+
|
|
36
|
+
logo: str = ""
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
for i in range(0, len(text), 2):
|
|
40
|
+
tmp = text[i : i + 2]
|
|
41
|
+
if tmp == "00":
|
|
42
|
+
logo += "\n"
|
|
43
|
+
else:
|
|
44
|
+
logo += a[b.index(tmp[0])] * int(tmp[1])
|
|
45
|
+
except ValueError:
|
|
46
|
+
logo = ""
|
|
47
|
+
|
|
48
|
+
if with_version:
|
|
49
|
+
logo += " v%s"
|
|
50
|
+
return logo
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def to_camel(string: str) -> str:
|
|
54
|
+
parts = string.split("_")
|
|
55
|
+
return parts[0] + "".join(word.capitalize() for word in parts[1:])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def get_hashed_password(password: str) -> str:
|
|
59
|
+
return CryptContext(schemes=["bcrypt"], deprecated="auto").hash(password)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from typing import Any, Dict, Generator
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Session, create_engine
|
|
4
|
+
|
|
5
|
+
from one_public_api.core.settings import settings
|
|
6
|
+
from one_public_api.models import * # noqa
|
|
7
|
+
|
|
8
|
+
engine_options: Dict[str, Any] = {
|
|
9
|
+
"url": str(settings.db_uri),
|
|
10
|
+
"echo": settings.LOG_ECHO_SQL,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if settings.DB_ENGINE == "postgresql":
|
|
14
|
+
engine_options.update(
|
|
15
|
+
{
|
|
16
|
+
"max_overflow": settings.DB_MAX_OVERFLOW_SIZE,
|
|
17
|
+
"pool_size": settings.DB_POOL_SIZE,
|
|
18
|
+
"pool_timeout": settings.DB_TIMEOUT,
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
engine = create_engine(**engine_options)
|
|
23
|
+
|
|
24
|
+
session: Any = Session(engine)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_session() -> Generator[Session, Any, None]:
|
|
28
|
+
"""
|
|
29
|
+
Generate a session from the database engine.
|
|
30
|
+
|
|
31
|
+
This function initializes a session using the provided database engine
|
|
32
|
+
and yields it for use. The session is designed to be used as a context
|
|
33
|
+
manager, ensuring that resources are properly cleaned up after use.
|
|
34
|
+
|
|
35
|
+
Yields
|
|
36
|
+
------
|
|
37
|
+
Generator[Session, Any, None]
|
|
38
|
+
A database session instance generated using the configured engine.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
with Session(engine) as s:
|
|
42
|
+
yield s
|