fastlib-py 0.4.2__tar.gz → 0.4.4__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 (81) hide show
  1. fastlib_py-0.4.4/PKG-INFO +72 -0
  2. fastlib_py-0.4.4/README.md +32 -0
  3. fastlib_py-0.4.4/fastlib/security.py +177 -0
  4. fastlib_py-0.4.4/fastlib_py.egg-info/PKG-INFO +72 -0
  5. fastlib_py-0.4.4/fastlib_py.egg-info/requires.txt +19 -0
  6. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/pyproject.toml +23 -25
  7. fastlib_py-0.4.2/PKG-INFO +0 -44
  8. fastlib_py-0.4.2/README.md +0 -13
  9. fastlib_py-0.4.2/fastlib/security.py +0 -290
  10. fastlib_py-0.4.2/fastlib_py.egg-info/PKG-INFO +0 -44
  11. fastlib_py-0.4.2/fastlib_py.egg-info/requires.txt +0 -8
  12. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/LICENSE +0 -0
  13. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/__init__.py +0 -0
  14. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/cache/__init__.py +0 -0
  15. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/cache/_memony_cache.py +0 -0
  16. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/cache/_redis_cache.py +0 -0
  17. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/cache/manager.py +0 -0
  18. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/__init__.py +0 -0
  19. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/_database_config.py +0 -0
  20. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/_log_config.py +0 -0
  21. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/_security_config.py +0 -0
  22. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/_server_config.py +0 -0
  23. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/base.py +0 -0
  24. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/decorator.py +0 -0
  25. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/loader.py +0 -0
  26. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/manager.py +0 -0
  27. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/registry.py +0 -0
  28. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/config/utils.py +0 -0
  29. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/constants.py +0 -0
  30. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/contextvars.py +0 -0
  31. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/db_engine.py +0 -0
  32. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/enums.py +0 -0
  33. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/exception/__init__.py +0 -0
  34. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/exception/base.py +0 -0
  35. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/exception/exception_handler.py +0 -0
  36. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/exception/exception_manager.py +0 -0
  37. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/i18n/__init__.py +0 -0
  38. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/i18n/manager.py +0 -0
  39. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/i18n/messages.py +0 -0
  40. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/i18n/types.py +0 -0
  41. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/i18n/utils.py +0 -0
  42. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/logging/__init__.py +0 -0
  43. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/logging/handlers.py +0 -0
  44. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/mapper/__init__.py +0 -0
  45. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/mapper/base_mapper.py +0 -0
  46. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/mapper/impl/__init__.py +0 -0
  47. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/mapper/impl/base_mapper_impl.py +0 -0
  48. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/middleware/__init__.py +0 -0
  49. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/middleware/db_session.py +0 -0
  50. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/middleware/i18n.py +0 -0
  51. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/middleware/jwt.py +0 -0
  52. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/migrate.py +0 -0
  53. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/openapi.py +0 -0
  54. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/py.typed +0 -0
  55. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/request.py +0 -0
  56. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/response.py +0 -0
  57. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/router.py +0 -0
  58. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/schema.py +0 -0
  59. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/service/__init__.py +0 -0
  60. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/service/base_service.py +0 -0
  61. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/service/impl/__init__.py +0 -0
  62. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/service/impl/base_service_impl.py +0 -0
  63. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/__init__.py +0 -0
  64. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/enums.py +0 -0
  65. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/event.py +0 -0
  66. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/handler.py +0 -0
  67. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/schema.py +0 -0
  68. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/stream/sse.py +0 -0
  69. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/__init__.py +0 -0
  70. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/excel_util.py +0 -0
  71. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/file_util.py +0 -0
  72. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/http_util.py +0 -0
  73. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/ip_util.py +0 -0
  74. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/security_util.py +0 -0
  75. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/snowflake_util.py +0 -0
  76. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/str_util.py +0 -0
  77. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib/utils/validate_util.py +0 -0
  78. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib_py.egg-info/SOURCES.txt +0 -0
  79. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib_py.egg-info/dependency_links.txt +0 -0
  80. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/fastlib_py.egg-info/top_level.txt +0 -0
  81. {fastlib_py-0.4.2 → fastlib_py-0.4.4}/setup.cfg +0 -0
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastlib-py
3
+ Version: 0.4.4
4
+ Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
+ Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
+ Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/tyvekzhang/fastlib
9
+ Keywords: fastapi,library,sqlmodel,web
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: aiosqlite>=0.21
23
+ Requires-Dist: alembic>=1.15.2
24
+ Requires-Dist: bcrypt==4.3.0
25
+ Requires-Dist: fastapi>=0.121.0
26
+ Requires-Dist: httpx>=0.28.1
27
+ Requires-Dist: loguru>=0.7.3
28
+ Requires-Dist: openpyxl>=3.1.5
29
+ Requires-Dist: pandas>=2.2.3
30
+ Requires-Dist: passlib[bcrypt]>=1.7.4
31
+ Requires-Dist: pyjwt[crypto]>=2.10.1
32
+ Requires-Dist: python-multipart>=0.0.20
33
+ Requires-Dist: sqlmodel>=0.0.24
34
+ Requires-Dist: uvicorn[standard]>=0.38.0
35
+ Provides-Extra: mysql
36
+ Requires-Dist: aiomysql>=0.3.2; extra == "mysql"
37
+ Provides-Extra: pgsql
38
+ Requires-Dist: asyncpg>=0.30.0; extra == "pgsql"
39
+ Dynamic: license-file
40
+
41
+ # Fastlib
42
+ High performance, easy to use, fast to code, ready for production
43
+
44
+ ## Quick Start
45
+ 1. Download dependencies with [uv](https://docs.astral.sh/uv)
46
+ ```shell
47
+ # Default
48
+ uv add fastlib
49
+
50
+ # MySQL
51
+ uv add fastlib[mysql]
52
+
53
+ # PgSQL
54
+ uv add fastlib[pgsql]
55
+ ```
56
+
57
+ 2. Setup [alembic](https://github.com/sqlalchemy/alembic)
58
+
59
+ 3. Paste files to your project
60
+ [main.py](https://github.com/tyvekzhang/biohunter/blob/main/main.py)
61
+ [server.py](https://github.com/tyvekzhang/biohunter/blob/main/src/main/app/server.py)
62
+ [config.yml](https://github.com/tyvekzhang/biohunter/blob/main/src/main/resource/config.yml)
63
+
64
+ 4. Run the server
65
+
66
+ ```shell
67
+ uv run main.py
68
+ ```
69
+
70
+ ## License
71
+
72
+ [MIT](https://opensource.org/licenses/MIT)
@@ -0,0 +1,32 @@
1
+ # Fastlib
2
+ High performance, easy to use, fast to code, ready for production
3
+
4
+ ## Quick Start
5
+ 1. Download dependencies with [uv](https://docs.astral.sh/uv)
6
+ ```shell
7
+ # Default
8
+ uv add fastlib
9
+
10
+ # MySQL
11
+ uv add fastlib[mysql]
12
+
13
+ # PgSQL
14
+ uv add fastlib[pgsql]
15
+ ```
16
+
17
+ 2. Setup [alembic](https://github.com/sqlalchemy/alembic)
18
+
19
+ 3. Paste files to your project
20
+ [main.py](https://github.com/tyvekzhang/biohunter/blob/main/main.py)
21
+ [server.py](https://github.com/tyvekzhang/biohunter/blob/main/src/main/app/server.py)
22
+ [config.yml](https://github.com/tyvekzhang/biohunter/blob/main/src/main/resource/config.yml)
23
+
24
+ 4. Run the server
25
+
26
+ ```shell
27
+ uv run main.py
28
+ ```
29
+
30
+ ## License
31
+
32
+ [MIT](https://opensource.org/licenses/MIT)
@@ -0,0 +1,177 @@
1
+ from functools import wraps
2
+ from datetime import datetime, timedelta
3
+ from typing import Any, Optional, Callable, Union
4
+
5
+ from fastapi import Depends, HTTPException, status
6
+ from fastapi.security import OAuth2PasswordBearer
7
+ import jwt
8
+ from jwt.exceptions import InvalidTokenError
9
+ from passlib.context import CryptContext
10
+
11
+ from fastlib.config import ConfigManager
12
+ from fastlib.schema import CurrentUser
13
+
14
+ security_config = ConfigManager.get_security_config()
15
+ server_config = ConfigManager.get_server_config()
16
+
17
+ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
18
+
19
+
20
+ def decode_jwt_token(token: str) -> dict[str, Any]:
21
+ """Decode and validate JWT token.
22
+
23
+ Args:
24
+ token: JWT token string
25
+
26
+ Returns:
27
+ Decoded token payload
28
+
29
+ Raises:
30
+ HTTPException: If token is invalid or expired
31
+ """
32
+ try:
33
+ return jwt.decode(
34
+ token,
35
+ security_config.secret_key,
36
+ algorithms=[security_config.algorithm],
37
+ )
38
+ except InvalidTokenError as err:
39
+ raise HTTPException(
40
+ status_code=status.HTTP_403_FORBIDDEN,
41
+ detail="Token has expired. Please log in again.",
42
+ ) from err
43
+ except Exception as err:
44
+ raise HTTPException(
45
+ status_code=status.HTTP_403_FORBIDDEN,
46
+ detail="Could not validate credentials.",
47
+ ) from err
48
+
49
+
50
+ def get_oauth2_scheme() -> OAuth2PasswordBearer:
51
+ oauth2_scheme = OAuth2PasswordBearer(
52
+ tokenUrl=f"{server_config.api_prefix}/v1/auth/auth:signInWithEmailAndPassword"
53
+ )
54
+ return oauth2_scheme
55
+
56
+
57
+ def get_current_user() -> Callable[[], CurrentUser]:
58
+ """
59
+ Acquire current info through access_token
60
+
61
+ Returns:
62
+ CurrentUser instance
63
+ """
64
+
65
+ def current_user(
66
+ access_token: str = Depends(get_oauth2_scheme()),
67
+ ) -> CurrentUser:
68
+ if not security_config.enable:
69
+ user_id = 9
70
+ return CurrentUser(user_id=user_id)
71
+ try:
72
+ user_id = get_user_id(access_token)
73
+ except InvalidTokenError as err:
74
+ raise HTTPException(
75
+ status_code=status.HTTP_403_FORBIDDEN,
76
+ detail="Your token has expired. Please log in again.",
77
+ ) from err
78
+ except Exception:
79
+ raise HTTPException(
80
+ status_code=status.HTTP_403_FORBIDDEN,
81
+ detail="Error when decoding the token. Please check your request.",
82
+ )
83
+
84
+ return CurrentUser(user_id=user_id)
85
+
86
+ return current_user
87
+
88
+
89
+ def create_token(
90
+ subject: Optional[Union[str, int]] = None,
91
+ expires_delta: Optional[timedelta] = None,
92
+ token_type: Optional[str] = None,
93
+ ) -> str:
94
+ """Create new JWT token.
95
+
96
+ Args:
97
+ subject: Token subject (usually user ID)
98
+ expires_delta: Optional timedelta for token expiration
99
+ token_type: Token type identifier
100
+
101
+ Returns:
102
+ Encoded JWT string
103
+ """
104
+ expire = datetime.utcnow() + (
105
+ expires_delta or timedelta(minutes=security_config.refresh_token_expire_minutes)
106
+ )
107
+ to_encode = {"exp": expire, "sub": str(subject), "type": token_type}
108
+ return jwt.encode(
109
+ to_encode,
110
+ security_config.secret_key,
111
+ algorithm=security_config.algorithm,
112
+ )
113
+
114
+
115
+ def verify_password(plain_password: str, hashed_password: str) -> bool:
116
+ """Verify password against hashed version.
117
+
118
+ Args:
119
+ plain_password: Input password
120
+ hashed_password: Stored hashed password
121
+
122
+ Returns:
123
+ True if passwords match
124
+ """
125
+ return pwd_context.verify(plain_password, hashed_password)
126
+
127
+
128
+ def get_password_hash(password: str) -> str:
129
+ """Generate password hash.
130
+
131
+ Args:
132
+ password: Plain text password
133
+
134
+ Returns:
135
+ Hashed password string
136
+ """
137
+ return pwd_context.hash(password)
138
+
139
+
140
+ def validate_token(token: str) -> bool:
141
+ """Check if token is valid and not expired.
142
+
143
+ Args:
144
+ token: JWT token to validate
145
+
146
+ Returns:
147
+ True if token is valid
148
+
149
+ Raises:
150
+ HTTPException: If token is invalid
151
+ """
152
+ try:
153
+ payload = decode_jwt_token(token)
154
+ if datetime.fromtimestamp(payload["exp"]) < datetime.utcnow():
155
+ raise HTTPException(
156
+ status_code=status.HTTP_401_UNAUTHORIZED,
157
+ detail="Token has expired",
158
+ )
159
+ return True
160
+ except InvalidTokenError:
161
+ return False
162
+
163
+
164
+ def get_user_id(token: str) -> int:
165
+ """Extract user ID from JWT token.
166
+
167
+ Args:
168
+ token: JWT token string
169
+
170
+ Returns:
171
+ User ID extracted from the token
172
+
173
+ Raises:
174
+ HTTPException: If token is invalid or expired
175
+ """
176
+ payload = decode_jwt_token(token)
177
+ return int(payload["sub"])
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastlib-py
3
+ Version: 0.4.4
4
+ Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
+ Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
+ Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
7
+ License-Expression: MIT
8
+ Project-URL: Homepage, https://github.com/tyvekzhang/fastlib
9
+ Keywords: fastapi,library,sqlmodel,web
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: aiosqlite>=0.21
23
+ Requires-Dist: alembic>=1.15.2
24
+ Requires-Dist: bcrypt==4.3.0
25
+ Requires-Dist: fastapi>=0.121.0
26
+ Requires-Dist: httpx>=0.28.1
27
+ Requires-Dist: loguru>=0.7.3
28
+ Requires-Dist: openpyxl>=3.1.5
29
+ Requires-Dist: pandas>=2.2.3
30
+ Requires-Dist: passlib[bcrypt]>=1.7.4
31
+ Requires-Dist: pyjwt[crypto]>=2.10.1
32
+ Requires-Dist: python-multipart>=0.0.20
33
+ Requires-Dist: sqlmodel>=0.0.24
34
+ Requires-Dist: uvicorn[standard]>=0.38.0
35
+ Provides-Extra: mysql
36
+ Requires-Dist: aiomysql>=0.3.2; extra == "mysql"
37
+ Provides-Extra: pgsql
38
+ Requires-Dist: asyncpg>=0.30.0; extra == "pgsql"
39
+ Dynamic: license-file
40
+
41
+ # Fastlib
42
+ High performance, easy to use, fast to code, ready for production
43
+
44
+ ## Quick Start
45
+ 1. Download dependencies with [uv](https://docs.astral.sh/uv)
46
+ ```shell
47
+ # Default
48
+ uv add fastlib
49
+
50
+ # MySQL
51
+ uv add fastlib[mysql]
52
+
53
+ # PgSQL
54
+ uv add fastlib[pgsql]
55
+ ```
56
+
57
+ 2. Setup [alembic](https://github.com/sqlalchemy/alembic)
58
+
59
+ 3. Paste files to your project
60
+ [main.py](https://github.com/tyvekzhang/biohunter/blob/main/main.py)
61
+ [server.py](https://github.com/tyvekzhang/biohunter/blob/main/src/main/app/server.py)
62
+ [config.yml](https://github.com/tyvekzhang/biohunter/blob/main/src/main/resource/config.yml)
63
+
64
+ 4. Run the server
65
+
66
+ ```shell
67
+ uv run main.py
68
+ ```
69
+
70
+ ## License
71
+
72
+ [MIT](https://opensource.org/licenses/MIT)
@@ -0,0 +1,19 @@
1
+ aiosqlite>=0.21
2
+ alembic>=1.15.2
3
+ bcrypt==4.3.0
4
+ fastapi>=0.121.0
5
+ httpx>=0.28.1
6
+ loguru>=0.7.3
7
+ openpyxl>=3.1.5
8
+ pandas>=2.2.3
9
+ passlib[bcrypt]>=1.7.4
10
+ pyjwt[crypto]>=2.10.1
11
+ python-multipart>=0.0.20
12
+ sqlmodel>=0.0.24
13
+ uvicorn[standard]>=0.38.0
14
+
15
+ [mysql]
16
+ aiomysql>=0.3.2
17
+
18
+ [pgsql]
19
+ asyncpg>=0.30.0
@@ -1,42 +1,38 @@
1
1
  [project]
2
2
  name = "fastlib-py"
3
- version = "0.4.2"
3
+ version = "0.4.4"
4
4
  description = "Fastlib, high performance, easy to use, fast to code, ready for production"
5
5
  readme = "README.md"
6
- keywords = [
7
- "fastapi",
8
- "library",
9
- "sqlmodel",
10
- "web",
11
- ]
6
+ keywords = ["fastapi", "library", "sqlmodel", "web"]
12
7
  license = "MIT"
13
- maintainers = [
14
- { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
15
- ]
16
- authors = [
17
- { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
18
- ]
19
- requires-python = ">=3.10"
8
+ maintainers = [{ name = "tyvekZhang", email = "tyvekzhang@gmail.com" }]
9
+ authors = [{ name = "tyvekZhang", email = "tyvekzhang@gmail.com" }]
10
+ requires-python = ">=3.11"
20
11
  classifiers = [
21
12
  "Development Status :: 4 - Beta",
22
13
  "Programming Language :: Python",
23
14
  "Programming Language :: Python :: 3 :: Only",
24
- "Programming Language :: Python :: 3.10",
25
15
  "Programming Language :: Python :: 3.11",
26
16
  "Programming Language :: Python :: 3.12",
27
17
  "Programming Language :: Python :: 3.13",
18
+ "Programming Language :: Python :: 3.14",
28
19
  "Programming Language :: Python :: Implementation :: CPython",
29
20
  "Programming Language :: Python :: Implementation :: PyPy",
30
21
  ]
31
22
  dependencies = [
32
23
  "aiosqlite>=0.21",
33
24
  "alembic>=1.15.2",
34
- "fastapi[standard]>=0.115.12",
25
+ "bcrypt==4.3.0",
26
+ "fastapi>=0.121.0",
27
+ "httpx>=0.28.1",
35
28
  "loguru>=0.7.3",
36
29
  "openpyxl>=3.1.5",
37
30
  "pandas>=2.2.3",
31
+ "passlib[bcrypt]>=1.7.4",
38
32
  "pyjwt[crypto]>=2.10.1",
33
+ "python-multipart>=0.0.20",
39
34
  "sqlmodel>=0.0.24",
35
+ "uvicorn[standard]>=0.38.0",
40
36
  ]
41
37
 
42
38
  urls.Homepage = "https://github.com/tyvekzhang/fastlib"
@@ -50,16 +46,19 @@ dev = [
50
46
  "pytest-asyncio>=1.1",
51
47
  ]
52
48
 
53
- docs = [
54
- "sphinx>=7.2.6",
55
- "sphinx-rtd-theme>=2",
56
- ]
49
+ docs = ["sphinx>=7.2.6", "sphinx-rtd-theme>=2"]
50
+
51
+ [project.optional-dependencies]
52
+ mysql = ["aiomysql>=0.3.2"]
53
+
54
+ pgsql = ["asyncpg>=0.30.0"]
55
+
57
56
 
58
57
  [tool.ruff]
59
58
  line-length = 88
60
59
 
61
- include = [ "fastlib/**/*.py", "main.py" ]
62
- exclude = [ "doc/**/*.py" ]
60
+ include = ["fastlib/**/*.py", "main.py"]
61
+ exclude = ["doc/**/*.py"]
63
62
 
64
63
  lint.select = [
65
64
  "B", # flake8-bugbear
@@ -97,11 +96,10 @@ lint.pyupgrade.keep-runtime-typing = true
97
96
 
98
97
  [tool.coverage.run]
99
98
  data_file = "coverage/.coverage"
100
- source = [ "fastlib" ]
99
+ source = ["fastlib"]
101
100
  context = '${CONTEXT}'
102
101
  dynamic_context = "test_function"
103
- omit = [
104
- ]
102
+ omit = []
105
103
 
106
104
  [tool.coverage.report]
107
105
  fail_under = 90
fastlib_py-0.4.2/PKG-INFO DELETED
@@ -1,44 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fastlib-py
3
- Version: 0.4.2
4
- Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
- Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
- Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
7
- License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/tyvekzhang/fastlib
9
- Keywords: fastapi,library,sqlmodel,web
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3 :: Only
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Classifier: Programming Language :: Python :: Implementation :: CPython
18
- Classifier: Programming Language :: Python :: Implementation :: PyPy
19
- Requires-Python: >=3.10
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: aiosqlite>=0.21
23
- Requires-Dist: alembic>=1.15.2
24
- Requires-Dist: fastapi[standard]>=0.115.12
25
- Requires-Dist: loguru>=0.7.3
26
- Requires-Dist: openpyxl>=3.1.5
27
- Requires-Dist: pandas>=2.2.3
28
- Requires-Dist: pyjwt[crypto]>=2.10.1
29
- Requires-Dist: sqlmodel>=0.0.24
30
- Dynamic: license-file
31
-
32
- <div align="center" style="margin-top: 3%">
33
- <h1>
34
- Fastlib
35
- </h1>
36
- <h3>
37
- High performance, easy to use, fast to code, ready for production
38
- </h3>
39
- </div>
40
-
41
-
42
- ## License
43
-
44
- [MIT](https://opensource.org/licenses/MIT).
@@ -1,13 +0,0 @@
1
- <div align="center" style="margin-top: 3%">
2
- <h1>
3
- Fastlib
4
- </h1>
5
- <h3>
6
- High performance, easy to use, fast to code, ready for production
7
- </h3>
8
- </div>
9
-
10
-
11
- ## License
12
-
13
- [MIT](https://opensource.org/licenses/MIT).
@@ -1,290 +0,0 @@
1
- # SPDX-License-Identifier: MIT
2
- import base64
3
- import hashlib
4
- import secrets
5
- from collections.abc import Callable
6
- from datetime import datetime, timedelta
7
- from typing import Any
8
-
9
- import jwt
10
- from fastapi import Depends, HTTPException, status
11
- from fastapi.security import OAuth2PasswordBearer
12
- from jwt.exceptions import InvalidTokenError
13
-
14
- from fastlib.config.manager import ConfigManager
15
- from fastlib.schema import CurrentUser
16
-
17
- # Configuration
18
- security_config = ConfigManager.get_security_config()
19
- server_config = ConfigManager.get_server_config()
20
-
21
-
22
- def decode_jwt_token(token: str) -> dict[str, Any]:
23
- """Decode and validate JWT token.
24
-
25
- Args:
26
- token: JWT token string
27
-
28
- Returns:
29
- Decoded token payload
30
-
31
- Raises:
32
- HTTPException: If token is invalid or expired
33
- """
34
- try:
35
- return jwt.decode(
36
- token,
37
- security_config.secret_key,
38
- algorithms=[security_config.algorithm],
39
- )
40
- except InvalidTokenError as err:
41
- raise HTTPException(
42
- status_code=status.HTTP_403_FORBIDDEN,
43
- detail="Token has expired. Please log in again.",
44
- ) from err
45
- except Exception as err:
46
- raise HTTPException(
47
- status_code=status.HTTP_403_FORBIDDEN,
48
- detail="Could not validate credentials.",
49
- ) from err
50
-
51
-
52
- def get_oauth2_scheme() -> OAuth2PasswordBearer:
53
- """Create OAuth2 password bearer scheme.
54
-
55
- Returns:
56
- OAuth2PasswordBearer instance
57
- """
58
- oauth2_scheme = OAuth2PasswordBearer(
59
- tokenUrl=f"{server_config.api_prefix}/v1/auth/auth:signInWithEmailAndPassword"
60
- )
61
- return oauth2_scheme
62
-
63
-
64
- def get_current_user() -> Callable[[], CurrentUser]:
65
- """Get current user from access token.
66
-
67
- Returns:
68
- CurrentUser instance
69
- """
70
-
71
- def current_user(
72
- access_token: str = Depends(get_oauth2_scheme()),
73
- ) -> CurrentUser:
74
- if not security_config.enable:
75
- user_id = 9
76
- return CurrentUser(user_id=user_id)
77
- try:
78
- user_id = get_user_id(access_token)
79
- except InvalidTokenError as err:
80
- raise HTTPException(
81
- status_code=status.HTTP_403_FORBIDDEN,
82
- detail="Your token has expired. Please log in again.",
83
- ) from err
84
- except Exception as err:
85
- raise HTTPException(
86
- status_code=status.HTTP_403_FORBIDDEN,
87
- detail="Error when decoding the token. Please check your request.",
88
- ) from err
89
-
90
- return CurrentUser(user_id=user_id)
91
-
92
- return current_user
93
-
94
-
95
- def create_token(
96
- subject: str | int | None = None,
97
- expires_delta: timedelta | None = None,
98
- token_type: str | None = None,
99
- ) -> str:
100
- """Create new JWT token.
101
-
102
- Args:
103
- subject: Token subject (usually user ID)
104
- expires_delta: Optional timedelta for token expiration
105
- token_type: Token type identifier
106
-
107
- Returns:
108
- Encoded JWT string
109
- """
110
- expire = datetime.utcnow() + (
111
- expires_delta or timedelta(minutes=security_config.refresh_token_expire_minutes)
112
- )
113
- to_encode = {"exp": expire, "sub": str(subject), "type": token_type}
114
- return jwt.encode(
115
- to_encode,
116
- security_config.secret_key,
117
- algorithm=security_config.algorithm,
118
- )
119
-
120
-
121
- def generate_salt(length: int = 16) -> str:
122
- """Generate a cryptographically secure random salt.
123
-
124
- Args:
125
- length: Length of the salt in bytes
126
-
127
- Returns:
128
- Base64 encoded salt string
129
- """
130
- return base64.b64encode(secrets.token_bytes(length)).decode("utf-8")
131
-
132
-
133
- def hash_password_sha256(password: str, salt: str | None = None) -> tuple[str, str]:
134
- """Hash password using SHA-256 with salt.
135
-
136
- Args:
137
- password: Plain text password to hash
138
- salt: Salt for hashing. If None, generates random salt.
139
-
140
- Returns:
141
- Tuple of (hashed_password, salt)
142
- """
143
- if salt is None:
144
- salt = generate_salt()
145
-
146
- # Decode base64 salt
147
- salt_bytes = base64.b64decode(salt.encode("utf-8"))
148
-
149
- # Create hash with salt and password
150
- hash_obj = hashlib.sha256()
151
- hash_obj.update(salt_bytes + password.encode("utf-8"))
152
-
153
- # Multiple iterations for increased security (optional)
154
- for _ in range(1000): # 1000 iterations
155
- hash_obj = hashlib.sha256(
156
- hash_obj.digest() + salt_bytes + password.encode("utf-8")
157
- )
158
-
159
- hashed_password = base64.b64encode(hash_obj.digest()).decode("utf-8")
160
- return hashed_password, salt
161
-
162
-
163
- def verify_password(
164
- plain_password: str, hashed_password: str, salt: str | None = None
165
- ) -> bool:
166
- """Verify password against hashed version using SHA-256.
167
-
168
- Args:
169
- plain_password: Input password to verify
170
- hashed_password: Stored hashed password
171
- salt: Salt used for the original hash. If None, uses a default salt.
172
-
173
- Returns:
174
- True if passwords match
175
- """
176
- try:
177
- # Use default salt if none provided (for backward compatibility)
178
- if salt is None:
179
- # Default salt - in production, this should be stored with the hash
180
- salt = "default_salt_placeholder"
181
-
182
- # Recalculate hash with the same salt and algorithm
183
- calculated_hash, _ = hash_password_sha256(plain_password, salt)
184
-
185
- # Use timing-safe comparison function
186
- return secrets.compare_digest(calculated_hash, hashed_password)
187
- except Exception:
188
- return False
189
-
190
-
191
- def get_password_hash(password: str) -> tuple[str, str]:
192
- """Generate password hash using SHA-256.
193
-
194
- Args:
195
- password: Plain text password
196
-
197
- Returns:
198
- Tuple of (hashed_password, salt)
199
- """
200
- return hash_password_sha256(password)
201
-
202
-
203
- def validate_token(token: str) -> bool:
204
- """Check if token is valid and not expired.
205
-
206
- Args:
207
- token: JWT token to validate
208
-
209
- Returns:
210
- True if token is valid
211
-
212
- Raises:
213
- HTTPException: If token is invalid
214
- """
215
- try:
216
- payload = decode_jwt_token(token)
217
- if datetime.fromtimestamp(payload["exp"]) < datetime.utcnow():
218
- raise HTTPException(
219
- status_code=status.HTTP_401_UNAUTHORIZED,
220
- detail="Token has expired",
221
- )
222
- return True
223
- except InvalidTokenError:
224
- return False
225
-
226
-
227
- def get_user_id(token: str) -> int:
228
- """Extract user ID from JWT token.
229
-
230
- Args:
231
- token: JWT token string
232
-
233
- Returns:
234
- User ID extracted from the token
235
-
236
- Raises:
237
- HTTPException: If token is invalid or expired
238
- """
239
- payload = decode_jwt_token(token)
240
- return int(payload["sub"])
241
-
242
-
243
- # Backward compatibility wrapper functions
244
- def verify_password_compat(plain_password: str, hashed_password: str) -> bool:
245
- """Compatibility version of password verification with default salt.
246
-
247
- Args:
248
- plain_password: Input password to verify
249
- hashed_password: Stored hashed password
250
-
251
- Returns:
252
- True if passwords match
253
- """
254
- return verify_password(plain_password, hashed_password, salt=None)
255
-
256
-
257
- def get_password_hash_compat(password: str) -> str:
258
- """Compatibility version of password hash function.
259
-
260
- Args:
261
- password: Plain text password
262
-
263
- Returns:
264
- Formatted string containing hash and salt (hash:salt)
265
- """
266
- hashed_password, salt = get_password_hash(password)
267
- return f"{hashed_password}:{salt}"
268
-
269
-
270
- def parse_hashed_password(hashed_data: str) -> tuple[str, str]:
271
- """Parse hashed password data in format 'hash:salt'.
272
-
273
- Args:
274
- hashed_data: String containing hash and salt separated by colon
275
-
276
- Returns:
277
- Tuple of (hashed_password, salt)
278
-
279
- Raises:
280
- ValueError: If format is invalid
281
- """
282
- if ":" not in hashed_data:
283
- # If no salt is present, use default salt
284
- return hashed_data, "default_salt_placeholder"
285
-
286
- parts = hashed_data.split(":", 1)
287
- if len(parts) != 2:
288
- raise ValueError("Invalid hashed password format")
289
-
290
- return parts[0], parts[1]
@@ -1,44 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: fastlib-py
3
- Version: 0.4.2
4
- Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
- Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
- Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
7
- License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/tyvekzhang/fastlib
9
- Keywords: fastapi,library,sqlmodel,web
10
- Classifier: Development Status :: 4 - Beta
11
- Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3 :: Only
13
- Classifier: Programming Language :: Python :: 3.10
14
- Classifier: Programming Language :: Python :: 3.11
15
- Classifier: Programming Language :: Python :: 3.12
16
- Classifier: Programming Language :: Python :: 3.13
17
- Classifier: Programming Language :: Python :: Implementation :: CPython
18
- Classifier: Programming Language :: Python :: Implementation :: PyPy
19
- Requires-Python: >=3.10
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: aiosqlite>=0.21
23
- Requires-Dist: alembic>=1.15.2
24
- Requires-Dist: fastapi[standard]>=0.115.12
25
- Requires-Dist: loguru>=0.7.3
26
- Requires-Dist: openpyxl>=3.1.5
27
- Requires-Dist: pandas>=2.2.3
28
- Requires-Dist: pyjwt[crypto]>=2.10.1
29
- Requires-Dist: sqlmodel>=0.0.24
30
- Dynamic: license-file
31
-
32
- <div align="center" style="margin-top: 3%">
33
- <h1>
34
- Fastlib
35
- </h1>
36
- <h3>
37
- High performance, easy to use, fast to code, ready for production
38
- </h3>
39
- </div>
40
-
41
-
42
- ## License
43
-
44
- [MIT](https://opensource.org/licenses/MIT).
@@ -1,8 +0,0 @@
1
- aiosqlite>=0.21
2
- alembic>=1.15.2
3
- fastapi[standard]>=0.115.12
4
- loguru>=0.7.3
5
- openpyxl>=3.1.5
6
- pandas>=2.2.3
7
- pyjwt[crypto]>=2.10.1
8
- sqlmodel>=0.0.24
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes