fastapi-authly 0.1.26__tar.gz → 0.1.28__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.
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/PKG-INFO +1 -1
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/pyproject.toml +1 -1
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/__about__.py +1 -1
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/config/base.py +10 -1
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/logging.py +111 -5
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/.gitignore +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/LICENSE +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/README.md +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/README.zh.md +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/ScienceClaw-master.zip +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/docs//345/207/275/346/225/260/345/274/217/347/274/226/347/250/213/344/270/203/346/255/246/345/231/250.md" +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/examples/chart_tools.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/examples/correct_usage.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/ai/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/ai/embeddings.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/ai/output_parsers.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/ai/rerank.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/ai/vectorstores.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/auth.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/charts.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/contrib/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/contrib/tortoise_pg.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/config/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/config/auth.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/config/database.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/error_codes.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/exception_handlers.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/exceptions.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/pagination.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/response.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/security.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/deps.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/docs.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/interfaces.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/llm/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/llm/config.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/llm/exceptions.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/llm/factory.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/llm/providers.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/models/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/models/user.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/repo.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/schemas/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/schemas/user.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/__init__.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/langchain_adapter.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/models.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/registry.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/runtime.py +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/static/echarts.min.js +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/static/favicon.svg +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/static/scalar/standalone.js +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/static/scalar/style.css +0 -0
- {fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastapi-authly
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.28
|
|
4
4
|
Summary: A modular authentication system for FastAPI with OAuth2, JWT, and password recovery
|
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/fastapi-auth-module
|
|
6
6
|
Project-URL: Documentation, https://yourusername.github.io/fastapi-auth-module/
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "fastapi-authly"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.28"
|
|
8
8
|
description = "A modular authentication system for FastAPI with OAuth2, JWT, and password recovery"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field, field_validator
|
|
2
2
|
from typing import Any, Optional, Literal
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
class BaseBackendModel(BaseModel):
|
|
5
6
|
@field_validator("*", mode="before")
|
|
@@ -30,6 +31,14 @@ class MinioConfig(BaseBackendModel):
|
|
|
30
31
|
bucket_name: str = Field(..., description="MinIO 存储桶名称")
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
class LoggingFileConfig(BaseModel):
|
|
35
|
+
enabled: bool = False
|
|
36
|
+
path: Path = Path("logs/app.jsonl")
|
|
37
|
+
max_bytes: int = Field(default=100 * 1024 * 1024, gt=0)
|
|
38
|
+
backup_count: int = Field(default=5, ge=0)
|
|
39
|
+
|
|
40
|
+
|
|
33
41
|
class LoggingConfig(BaseModel):
|
|
34
42
|
level: Literal["DEBUG","INFO","WARNING","ERROR","CRITICAL"] = "INFO"
|
|
35
|
-
format: Literal["console","rich"
|
|
43
|
+
format: Literal["console", "rich"] = "console"
|
|
44
|
+
file: LoggingFileConfig = Field(default_factory=LoggingFileConfig)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import inspect
|
|
2
|
+
import json
|
|
2
3
|
import logging
|
|
3
4
|
import os
|
|
4
5
|
import re
|
|
5
6
|
import sys
|
|
6
7
|
from contextvars import ContextVar
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from logging.handlers import RotatingFileHandler
|
|
10
|
+
from pathlib import Path
|
|
7
11
|
from time import perf_counter
|
|
8
|
-
from typing import Literal
|
|
12
|
+
from typing import Any, Literal
|
|
9
13
|
from uuid import uuid4
|
|
10
14
|
|
|
11
15
|
from fastapi import FastAPI, Request
|
|
@@ -20,8 +24,24 @@ _http_logger = logging.getLogger("http.request")
|
|
|
20
24
|
|
|
21
25
|
|
|
22
26
|
class RequestContextFilter(logging.Filter):
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
*,
|
|
30
|
+
service: str | None = None,
|
|
31
|
+
environment: str | None = None,
|
|
32
|
+
version: str | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
self.service = service
|
|
36
|
+
self.environment = environment
|
|
37
|
+
self.version = version
|
|
38
|
+
|
|
23
39
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
24
40
|
request_id = _REQUEST_ID.get()
|
|
41
|
+
record.request_id = request_id
|
|
42
|
+
record.service = getattr(record, "service", self.service)
|
|
43
|
+
record.environment = getattr(record, "environment", self.environment)
|
|
44
|
+
record.version = getattr(record, "version", self.version)
|
|
25
45
|
record.request_context = (
|
|
26
46
|
f"request_id={request_id} | " if request_id is not None else ""
|
|
27
47
|
)
|
|
@@ -33,6 +53,48 @@ class RequestContextFilter(logging.Filter):
|
|
|
33
53
|
return True
|
|
34
54
|
|
|
35
55
|
|
|
56
|
+
class JsonLinesFormatter(logging.Formatter):
|
|
57
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
58
|
+
payload: dict[str, Any] = {
|
|
59
|
+
"schema_version": "1.0",
|
|
60
|
+
"timestamp": datetime.fromtimestamp(
|
|
61
|
+
record.created,
|
|
62
|
+
tz=timezone.utc,
|
|
63
|
+
).isoformat(),
|
|
64
|
+
"level": record.levelname,
|
|
65
|
+
"logger": record.name,
|
|
66
|
+
"message": record.getMessage(),
|
|
67
|
+
"module": record.module,
|
|
68
|
+
"function": record.funcName,
|
|
69
|
+
"line": record.lineno,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for field in (
|
|
73
|
+
"service",
|
|
74
|
+
"environment",
|
|
75
|
+
"version",
|
|
76
|
+
"request_id",
|
|
77
|
+
"endpoint",
|
|
78
|
+
"method",
|
|
79
|
+
"path",
|
|
80
|
+
"status_code",
|
|
81
|
+
"duration_ms",
|
|
82
|
+
):
|
|
83
|
+
value = getattr(record, field, None)
|
|
84
|
+
if value is not None:
|
|
85
|
+
payload[field] = value
|
|
86
|
+
|
|
87
|
+
if record.exc_info:
|
|
88
|
+
exception_type, exception, _ = record.exc_info
|
|
89
|
+
payload["exception"] = {
|
|
90
|
+
"type": exception_type.__name__,
|
|
91
|
+
"message": str(exception),
|
|
92
|
+
"stacktrace": self.formatException(record.exc_info),
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return json.dumps(payload, ensure_ascii=False, default=str)
|
|
96
|
+
|
|
97
|
+
|
|
36
98
|
def _normalize_level(level: int | str | None) -> int:
|
|
37
99
|
value = level or os.getenv("LOG_LEVEL", "INFO")
|
|
38
100
|
if isinstance(value, int):
|
|
@@ -76,10 +138,25 @@ def _get_endpoint_name(request: Request) -> str:
|
|
|
76
138
|
def setup_logging(
|
|
77
139
|
level: int | str | None = None,
|
|
78
140
|
format: LogFormat | str | None = None,
|
|
141
|
+
*,
|
|
142
|
+
service: str | None = None,
|
|
143
|
+
environment: str | None = None,
|
|
144
|
+
version: str | None = None,
|
|
145
|
+
file_path: str | Path | None = None,
|
|
146
|
+
file_max_bytes: int = 100 * 1024 * 1024,
|
|
147
|
+
file_backup_count: int = 5,
|
|
79
148
|
) -> None:
|
|
80
149
|
resolved_level = _normalize_level(level)
|
|
81
150
|
resolved_format = _normalize_format(format)
|
|
82
|
-
|
|
151
|
+
if file_max_bytes <= 0:
|
|
152
|
+
raise ValueError("file_max_bytes 必须大于 0")
|
|
153
|
+
if file_backup_count < 0:
|
|
154
|
+
raise ValueError("file_backup_count 不能小于 0")
|
|
155
|
+
context_filter = RequestContextFilter(
|
|
156
|
+
service=service,
|
|
157
|
+
environment=environment,
|
|
158
|
+
version=version,
|
|
159
|
+
)
|
|
83
160
|
|
|
84
161
|
if resolved_format == "rich":
|
|
85
162
|
handler = RichHandler(
|
|
@@ -110,10 +187,28 @@ def setup_logging(
|
|
|
110
187
|
)
|
|
111
188
|
|
|
112
189
|
handler.addFilter(context_filter)
|
|
190
|
+
handlers: list[logging.Handler] = [handler]
|
|
191
|
+
|
|
192
|
+
resolved_file_path = file_path or os.getenv("LOG_FILE_PATH")
|
|
193
|
+
if resolved_file_path:
|
|
194
|
+
log_path = Path(resolved_file_path)
|
|
195
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
196
|
+
file_handler = RotatingFileHandler(
|
|
197
|
+
log_path,
|
|
198
|
+
maxBytes=file_max_bytes,
|
|
199
|
+
backupCount=file_backup_count,
|
|
200
|
+
encoding="utf-8",
|
|
201
|
+
)
|
|
202
|
+
file_handler.setLevel(resolved_level)
|
|
203
|
+
file_handler.setFormatter(JsonLinesFormatter())
|
|
204
|
+
file_handler.addFilter(context_filter)
|
|
205
|
+
handlers.append(file_handler)
|
|
206
|
+
|
|
113
207
|
root_logger = logging.getLogger()
|
|
114
208
|
root_logger.handlers.clear()
|
|
115
209
|
root_logger.setLevel(resolved_level)
|
|
116
|
-
|
|
210
|
+
for configured_handler in handlers:
|
|
211
|
+
root_logger.addHandler(configured_handler)
|
|
117
212
|
|
|
118
213
|
for logger_name in ("uvicorn", "uvicorn.error"):
|
|
119
214
|
framework_logger = logging.getLogger(logger_name)
|
|
@@ -160,7 +255,12 @@ def setup_request_logging(app: FastAPI) -> None:
|
|
|
160
255
|
request.method,
|
|
161
256
|
path,
|
|
162
257
|
duration_ms,
|
|
163
|
-
extra={
|
|
258
|
+
extra={
|
|
259
|
+
"endpoint": _get_endpoint_name(request),
|
|
260
|
+
"method": request.method,
|
|
261
|
+
"path": path,
|
|
262
|
+
"duration_ms": round(duration_ms, 2),
|
|
263
|
+
},
|
|
164
264
|
)
|
|
165
265
|
raise
|
|
166
266
|
else:
|
|
@@ -179,7 +279,13 @@ def setup_request_logging(app: FastAPI) -> None:
|
|
|
179
279
|
path,
|
|
180
280
|
status_code,
|
|
181
281
|
duration_ms,
|
|
182
|
-
extra={
|
|
282
|
+
extra={
|
|
283
|
+
"endpoint": _get_endpoint_name(request),
|
|
284
|
+
"method": request.method,
|
|
285
|
+
"path": path,
|
|
286
|
+
"status_code": status_code,
|
|
287
|
+
"duration_ms": round(duration_ms, 2),
|
|
288
|
+
},
|
|
183
289
|
)
|
|
184
290
|
response.headers["X-Request-ID"] = request_id
|
|
185
291
|
return response
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/core/exception_handlers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/skill_runtime/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fastapi_authly-0.1.26 → fastapi_authly-0.1.28}/src/fastapi_authly/static/scalar/standalone.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|