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.
Files changed (61) hide show
  1. one_public_api-0.1.0a0/PKG-INFO +47 -0
  2. one_public_api-0.1.0a0/README.md +1 -0
  3. one_public_api-0.1.0a0/pyproject.toml +68 -0
  4. one_public_api-0.1.0a0/src/one_public_api/__init__.py +3 -0
  5. one_public_api-0.1.0a0/src/one_public_api/common/__init__.py +0 -0
  6. one_public_api-0.1.0a0/src/one_public_api/common/constants.py +163 -0
  7. one_public_api-0.1.0a0/src/one_public_api/common/init_data.py +77 -0
  8. one_public_api-0.1.0a0/src/one_public_api/common/query_param.py +52 -0
  9. one_public_api-0.1.0a0/src/one_public_api/common/tools.py +96 -0
  10. one_public_api-0.1.0a0/src/one_public_api/common/utility/__init__.py +0 -0
  11. one_public_api-0.1.0a0/src/one_public_api/common/utility/files.py +23 -0
  12. one_public_api-0.1.0a0/src/one_public_api/common/utility/str.py +59 -0
  13. one_public_api-0.1.0a0/src/one_public_api/core/__init__.py +10 -0
  14. one_public_api-0.1.0a0/src/one_public_api/core/database.py +42 -0
  15. one_public_api-0.1.0a0/src/one_public_api/core/exceptions.py +66 -0
  16. one_public_api-0.1.0a0/src/one_public_api/core/extensions.py +100 -0
  17. one_public_api-0.1.0a0/src/one_public_api/core/i18n.py +82 -0
  18. one_public_api-0.1.0a0/src/one_public_api/core/log.py +189 -0
  19. one_public_api-0.1.0a0/src/one_public_api/core/py.typed +0 -0
  20. one_public_api-0.1.0a0/src/one_public_api/core/settings.py +168 -0
  21. one_public_api-0.1.0a0/src/one_public_api/crud/__init__.py +0 -0
  22. one_public_api-0.1.0a0/src/one_public_api/crud/data_creator.py +107 -0
  23. one_public_api-0.1.0a0/src/one_public_api/crud/data_deleter.py +42 -0
  24. one_public_api-0.1.0a0/src/one_public_api/crud/data_reader.py +105 -0
  25. one_public_api-0.1.0a0/src/one_public_api/crud/data_updater.py +43 -0
  26. one_public_api-0.1.0a0/src/one_public_api/locales/en/LC_MESSAGES/messages.mo +0 -0
  27. one_public_api-0.1.0a0/src/one_public_api/locales/en/LC_MESSAGES/messages.po +379 -0
  28. one_public_api-0.1.0a0/src/one_public_api/locales/ja/LC_MESSAGES/messages.mo +0 -0
  29. one_public_api-0.1.0a0/src/one_public_api/locales/ja/LC_MESSAGES/messages.po +379 -0
  30. one_public_api-0.1.0a0/src/one_public_api/locales/messages.pot +377 -0
  31. one_public_api-0.1.0a0/src/one_public_api/main.py +20 -0
  32. one_public_api-0.1.0a0/src/one_public_api/models/__init__.py +5 -0
  33. one_public_api-0.1.0a0/src/one_public_api/models/mixins/__init__.py +0 -0
  34. one_public_api-0.1.0a0/src/one_public_api/models/mixins/id_mixin.py +23 -0
  35. one_public_api-0.1.0a0/src/one_public_api/models/mixins/maintenance_mixin.py +21 -0
  36. one_public_api-0.1.0a0/src/one_public_api/models/mixins/password_mixin.py +11 -0
  37. one_public_api-0.1.0a0/src/one_public_api/models/mixins/timestamp_mixin.py +18 -0
  38. one_public_api-0.1.0a0/src/one_public_api/models/py.typed +0 -0
  39. one_public_api-0.1.0a0/src/one_public_api/models/system/__init__.py +0 -0
  40. one_public_api-0.1.0a0/src/one_public_api/models/system/configuration_model.py +111 -0
  41. one_public_api-0.1.0a0/src/one_public_api/models/system/feature_model.py +65 -0
  42. one_public_api-0.1.0a0/src/one_public_api/models/system/user_model.py +97 -0
  43. one_public_api-0.1.0a0/src/one_public_api/py.typed +0 -0
  44. one_public_api-0.1.0a0/src/one_public_api/routers/__init__.py +0 -0
  45. one_public_api-0.1.0a0/src/one_public_api/routers/authenticate_router.py +117 -0
  46. one_public_api-0.1.0a0/src/one_public_api/routers/base_route.py +63 -0
  47. one_public_api-0.1.0a0/src/one_public_api/routers/configuration_router.py +156 -0
  48. one_public_api-0.1.0a0/src/one_public_api/routers/feature_router.py +124 -0
  49. one_public_api-0.1.0a0/src/one_public_api/routers/user_router.py +126 -0
  50. one_public_api-0.1.0a0/src/one_public_api/schemas/__init__.py +0 -0
  51. one_public_api-0.1.0a0/src/one_public_api/schemas/authenticate_schema.py +68 -0
  52. one_public_api-0.1.0a0/src/one_public_api/schemas/configuration_schema.py +72 -0
  53. one_public_api-0.1.0a0/src/one_public_api/schemas/feature_schema.py +65 -0
  54. one_public_api-0.1.0a0/src/one_public_api/schemas/response_schema.py +38 -0
  55. one_public_api-0.1.0a0/src/one_public_api/schemas/user_schema.py +78 -0
  56. one_public_api-0.1.0a0/src/one_public_api/services/__init__.py +0 -0
  57. one_public_api-0.1.0a0/src/one_public_api/services/authenticate_service.py +164 -0
  58. one_public_api-0.1.0a0/src/one_public_api/services/base_service.py +109 -0
  59. one_public_api-0.1.0a0/src/one_public_api/services/configuration_service.py +22 -0
  60. one_public_api-0.1.0a0/src/one_public_api/services/feture_service.py +30 -0
  61. 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
@@ -0,0 +1,3 @@
1
+ from one_public_api.main import app
2
+
3
+ __all__ = ["app"]
@@ -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
@@ -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,10 @@
1
+ from one_public_api.core.database import get_session
2
+ from one_public_api.core.extensions import initialize, lifespan
3
+ from one_public_api.core.i18n import translate
4
+
5
+ __all__ = [
6
+ "get_session",
7
+ "initialize",
8
+ "lifespan",
9
+ "translate",
10
+ ]
@@ -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