Compare commits

...

7 Commits

Author SHA1 Message Date
chenwj113 fc5d6b8953 fix: 更新Dockerfile文件
Gitea Action for docker build runtime / build-runtime (push) Successful in 5m53s Details
2024-09-02 10:11:46 +08:00
chenwj113 6d69a18b03 fix: 运行时环境替换为alpine
Gitea Action for docker build / build-app (push) Successful in 1m23s Details
Gitea Action for docker build runtime / build-runtime (push) Successful in 4m18s Details
2024-08-16 18:37:51 +08:00
chenwj113 61f91fa932 fix: 去除gitea action的pyproject.toml触发path
Gitea Action for docker build / build-app (push) Successful in 1m1s Details
2024-08-15 18:35:00 +08:00
chenweijia ee14c5591a bugfix: 修复gitea action构建脚本的bug
Gitea Action for docker build / build-app (push) Successful in 25s Details
Gitea Action for docker build runtime / build-runtime (push) Successful in 4m17s Details
2024-08-15 17:11:05 +08:00
chenweijia dce678ca95 fix: 更改gitea action获取版本号的方式
Gitea Action for docker build runtime / build-runtime (push) Successful in 4m27s Details
Gitea Action for docker build / build-app (push) Successful in 33s Details
2024-08-15 17:03:16 +08:00
chenweijia 8982dd82d8 fix: 添加用户验证功能
Gitea Action for docker build / build-app (push) Successful in 47s Details
Gitea Action for docker build runtime / build-runtime (push) Successful in 3m46s Details
2024-08-15 13:33:43 +08:00
chenweijia 8459e0c447 fix: 增加一个请求限制功能
Gitea Action for docker build / build-app (push) Successful in 34s Details
Gitea Action for docker build runtime / build-runtime (push) Successful in 5m20s Details
2024-08-14 14:20:19 +08:00
12 changed files with 583 additions and 103 deletions

View File

@ -5,9 +5,10 @@ on:
branches: [master] branches: [master]
paths-ignore: paths-ignore:
- 'requirements.txt' - 'requirements.txt'
- 'RuntimeDockerfile' - '*RuntimeDockerfile'
- '.gitignore' - '.gitignore'
- 'README.md' - 'README.md'
- 'pyproject.toml'
- '.gitea/workflows/**' - '.gitea/workflows/**'
@ -27,16 +28,8 @@ jobs:
- run: | - run: |
var=${{ gitea.repository }} var=${{ gitea.repository }}
repo=${var##*/} repo=${var##*/}
url="https://hub.airpig.cn/api/v2.0/projects/library/repositories/${repo}/artifacts?page=1&page_size=10&with_tag=true&with_label=false&with_scan_overview=false&with_signature=false&with_immutable_status=false&with_accessory=false" version=$(grep -oP '(?<=version = ")(.*)(?=")' "pyproject.toml")
version=$(curl -X 'GET' $url \ new_version="v${version}"
-H 'accept: application/json' \
-H 'X-Accept-Vulnerabilities: application/vnd.security.vulnerability.report; version=1.1, application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0' \
-u 'admin:Chenweijia!' | jq '.[0].tags[0].name')
if [ ! $version ]; then
new_version=v1.0.0
else
new_version=$(echo $version | sed 's/\"//g' | awk -F. '{$NF = $NF + 1;} 1' OFS=.)
fi
docker build -t hub.airpig.cn/library/$repo:$new_version . docker build -t hub.airpig.cn/library/$repo:$new_version .
docker push hub.airpig.cn/library/$repo:$new_version docker push hub.airpig.cn/library/$repo:$new_version
docker rmi hub.airpig.cn/library/$repo:$new_version docker rmi hub.airpig.cn/library/$repo:$new_version

View File

@ -6,8 +6,7 @@ on:
paths: paths:
- 'requirements.txt' - 'requirements.txt'
- 'poetry.lock' - 'poetry.lock'
- 'pyproject.toml' - '*RuntimeDockerfile'
- 'RuntimeDockerfile'
jobs: jobs:
build-runtime: build-runtime:
@ -25,18 +24,9 @@ jobs:
- run: | - run: |
var=${{ gitea.repository }} var=${{ gitea.repository }}
repo=${var##*/}"-runtime" repo=${var##*/}"-runtime"
echo $repo version=$(grep -oP '(?<=version = ")(.*)(?=")' "pyproject.toml")
url="https://hub.airpig.cn/api/v2.0/projects/library/repositories/${repo}/artifacts?page=1&page_size=10&with_tag=true&with_label=false&with_scan_overview=false&with_signature=false&with_immutable_status=false&with_accessory=false" new_version="v${version}"
version=$(curl -X 'GET' $url \ docker build -t hub.airpig.cn/library/$repo:$new_version -f AlpineRuntimeDockerfile .
-H 'accept: application/json' \
-H 'X-Accept-Vulnerabilities: application/vnd.security.vulnerability.report; version=1.1, application/vnd.scanner.adapter.vuln.report.harbor+json; version=1.0' \
-u 'admin:Chenweijia!' | jq '.[0].tags[0].name')
if [ ! $version ]; then
new_version=v1.0.0
else
new_version=$(echo $version | sed 's/\"//g' | awk -F. '{$NF = $NF + 1;} 1' OFS=.)
fi
docker build -t hub.airpig.cn/library/$repo:$new_version -f RuntimeDockerfile .
docker push hub.airpig.cn/library/$repo:$new_version docker push hub.airpig.cn/library/$repo:$new_version
docker rmi hub.airpig.cn/library/$repo:$new_version docker rmi hub.airpig.cn/library/$repo:$new_version
- run: echo "This job's status is ${{ job.status }}." - run: echo "This job's status is ${{ job.status }}."

30
AlpineRuntimeDockerfile Normal file
View File

@ -0,0 +1,30 @@
FROM python:3.11-alpine as builder
RUN pip install poetry -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /venv
COPY poetry.lock pyproject.toml /venv/
RUN poetry config virtualenvs.options.no-pip true \
&& poetry config virtualenvs.options.no-setuptools true \
&& poetry config virtualenvs.in-project true \
&& poetry install
FROM python:3.11-alpine as release
COPY --from=builder /venv /venv
ENV PATH="/venv/.venv/bin:${PATH}"
RUN chmod a+x /venv/.venv/bin/activate \
&& source /venv/.venv/bin/activate
WORKDIR /app
COPY . /app
ENV FAST_API_ENV=prod
CMD ["/usr/local/bin/uvicorn", "main:fast_api_app", "--reload", "--host", "0.0.0.0", "--port", "80"]

View File

@ -1,7 +1,7 @@
# FastAPI App 文档 # FastAPI App 文档
## 0 开发说明 ## 0 开发说明
Python(3.8.6+) and pip(20.2.4+) Python(3.10+) and pip(20.2.4+)
### 开发命名规范 ### 开发命名规范
> * 避免采用的名字 > * 避免采用的名字
> 不要使用字符l小写字母elO大写字母ohI大写字母eye作为单字符变量名。 > 不要使用字符l小写字母elO大写字母ohI大写字母eye作为单字符变量名。
@ -32,12 +32,12 @@ database=test
## 2 开发环境下安装依赖和运行项目 ## 2 开发环境下安装依赖和运行项目
``` bash ``` bash
pip install poetry -i https://mirrors.aliyun.com/pypi/simple/ \ pip install poetry -i https://pypi.tuna.tsinghua.edu.cn/simple/ \
&& poetry source add --priority=primary mirrors https://pypi.tuna.tsinghua.edu.cn/simple/ \ && poetry source add --priority=primary mirrors https://pypi.tuna.tsinghua.edu.cn/simple/ \
&& poetry config virtualenvs.path /install \ && poetry config virtualenvs.path /install \
&& poetry install && poetry install
./start.bat (windows) ./start.bat (windows)
./start.sh (mac) ./start.sh (mac/linux)
``` ```

47
main.py
View File

@ -1,27 +1,26 @@
import logging.config as logging_config import logging.config as logging_config
import os import os
from contextlib import asynccontextmanager
import fastapi_plugins import fastapi_plugins
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.exceptions import HTTPException, RequestValidationError
from fastapi.middleware.wsgi import WSGIMiddleware # from fastapi.middleware.wsgi import WSGIMiddleware
from fastapi_sqlalchemy import DBSessionMiddleware from fastapi_sqlalchemy import DBSessionMiddleware
from authx.exceptions import AuthXException
from config import init_config from config import init_config
# from src.middleware.flask import flask_app # from src.middleware.flask import flask_app
from src.utils.exception import (http_exception_handler,
request_validation_error_handler)
from src.utils.exception import http_exception_handler, request_validation_error_handler, authx_exception_handler
# 请求限制
import redis.asyncio as aio_redis
from fastapi_limiter import FastAPILimiter
def create_app(): def create_app():
app = FastAPI()
mysql_config, redis_config = init_config() mysql_config, redis_config = init_config()
# 添加sqlalchemy数据库中间件
# once the middleware is applied, any route can then access the database session from the global ``db``
app.add_middleware(DBSessionMiddleware, db_url=mysql_config.sqlalchemy_db_uri)
@app.on_event("startup") @asynccontextmanager
async def startup_event(): async def lifespan(app: FastAPI):
# 创建日志文件夹和临时文件上传文件夹 # 创建日志文件夹和临时文件上传文件夹
if not os.path.exists("files"): if not os.path.exists("files"):
os.mkdir("files") os.mkdir("files")
@ -32,25 +31,33 @@ def create_app():
logging_config.fileConfig('conf/log.ini') logging_config.fileConfig('conf/log.ini')
# 初始化配置文件 # 初始化配置文件
# Redis 缓存初始化 # Redis 缓存初始化
print(redis_config)
await fastapi_plugins.redis_plugin.init_app(app, redis_config) await fastapi_plugins.redis_plugin.init_app(app, redis_config)
await fastapi_plugins.redis_plugin.init() await fastapi_plugins.redis_plugin.init()
# 请求限制
@app.on_event("shutdown") await FastAPILimiter.init(redis=aio_redis.from_url("redis://localhost:6379", encoding="utf8"))
async def shutdown_event(): yield
# 应用关闭时关闭redis连接
await fastapi_plugins.redis_plugin.terminate() await fastapi_plugins.redis_plugin.terminate()
await FastAPILimiter.close()
app = FastAPI(lifespan=lifespan)
# 添加sqlalchemy数据库中间件
# once the middleware is applied, any route can then access the database session from the global ``db``
app.add_middleware(DBSessionMiddleware, db_url=mysql_config.sqlalchemy_db_uri)
# 添加异常处理 # 添加异常处理
app.add_exception_handler(HTTPException, http_exception_handler) app.add_exception_handler(HTTPException, http_exception_handler)
app.add_exception_handler(RequestValidationError, request_validation_error_handler) app.add_exception_handler(RequestValidationError, request_validation_error_handler)
# AuthX异常处理
app.add_exception_handler(AuthXException, authx_exception_handler)
# 可以在这里挂载Flask的应用复用之前项目的相关代码 # 可以在这里挂载Flask的应用复用之前项目的相关代码
# app.mount("/v1", WSGIMiddleware(flask_app)) # app.mount("/v1", WSGIMiddleware(flask_app))
# 在这里添加API route
from src.api import example
app.include_router(example.router, tags=["API示例"], prefix="/example")
# 在这里添加API route
from src.api import example,auth_example
app.include_router(example.router, tags=["API示例"], prefix="/v1/example")
app.include_router(auth_example.router, tags=["认证示例"], prefix="/v1/auth_example")
return app return app

361
poetry.lock generated
View File

@ -127,6 +127,31 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "authx"
version = "1.3.0"
description = "Ready to use and customizable Authentications and Oauth2 management for FastAPI"
optional = false
python-versions = ">=3.8"
files = [
{file = "authx-1.3.0-py3-none-any.whl", hash = "sha256:347485b78a5016e655ac78b0ff0dd0c2aef3935148af4a863145399a20c718e8"},
{file = "authx-1.3.0.tar.gz", hash = "sha256:1f7c102a74d082c6a8850c26ff1e8fba73a146589fd95d84531df41e091586a5"},
]
[package.dependencies]
fastapi = ">=0.111.0"
pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0"
pydantic-settings = ">=2.1.0"
pyjwt = {version = ">=2.6.0,<3.0.0", extras = ["crypto"]}
python-dateutil = ">=2.8,<3.0.0"
python-jose = ">=3.3.0,<4.0.0"
pytz = ">=2023.3,<2025.0"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "captcha" name = "captcha"
version = "0.6.0" version = "0.6.0"
@ -162,6 +187,90 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "cffi"
version = "1.17.0"
description = "Foreign Function Interface for Python calling C code."
optional = false
python-versions = ">=3.8"
files = [
{file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"},
{file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"},
{file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"},
{file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"},
{file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"},
{file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"},
{file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"},
{file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"},
{file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"},
{file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"},
{file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"},
{file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"},
{file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"},
{file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"},
{file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"},
{file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"},
{file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"},
{file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"},
{file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"},
{file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"},
{file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"},
{file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"},
{file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"},
{file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"},
{file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"},
{file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"},
{file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"},
{file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"},
{file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"},
{file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"},
{file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"},
{file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"},
{file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"},
{file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"},
{file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"},
{file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"},
{file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"},
]
[package.dependencies]
pycparser = "*"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "charset-normalizer" name = "charset-normalizer"
version = "3.3.2" version = "3.3.2"
@ -301,6 +410,60 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "cryptography"
version = "43.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false
python-versions = ">=3.7"
files = [
{file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"},
{file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"},
{file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"},
{file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"},
{file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"},
{file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"},
{file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"},
{file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"},
{file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"},
{file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"},
{file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"},
{file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"},
{file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"},
]
[package.dependencies]
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "dnspython" name = "dnspython"
version = "2.6.1" version = "2.6.1"
@ -326,6 +489,29 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "ecdsa"
version = "0.19.0"
description = "ECDSA cryptographic signature library (pure python)"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"},
{file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"},
]
[package.dependencies]
six = ">=1.9.0"
[package.extras]
gmpy = ["gmpy"]
gmpy2 = ["gmpy2"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "email-validator" name = "email-validator"
version = "2.1.1" version = "2.1.1"
@ -419,6 +605,26 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "fastapi-limiter"
version = "0.1.6"
description = "A request rate limiter for fastapi"
optional = false
python-versions = ">=3.9,<4.0"
files = [
{file = "fastapi_limiter-0.1.6-py3-none-any.whl", hash = "sha256:2e53179a4208b8f2c8795e38bb001324d3dc37d2800ff49fd28ec5caabf7a240"},
{file = "fastapi_limiter-0.1.6.tar.gz", hash = "sha256:6f5fde8efebe12eb33861bdffb91009f699369a3c2862cdc7c1d9acf912ff443"},
]
[package.dependencies]
fastapi = "*"
redis = ">=4.2.0rc1"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "fastapi-plugins" name = "fastapi-plugins"
version = "0.13.0" version = "0.13.0"
@ -809,6 +1015,22 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "itsdangerous"
version = "2.2.0"
description = "Safely pass data to untrusted environments and back."
optional = false
python-versions = ">=3.8"
files = [
{file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"},
{file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"},
]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "jinja2" name = "jinja2"
version = "3.1.4" version = "3.1.4"
@ -1134,6 +1356,38 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "pyasn1"
version = "0.6.0"
description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"},
{file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"},
]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]]
name = "pycparser"
version = "2.22"
description = "C parser in Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.7.3" version = "2.7.3"
@ -1297,6 +1551,31 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "pyjwt"
version = "2.9.0"
description = "JSON Web Token implementation in Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"},
{file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"},
]
[package.dependencies]
cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""}
[package.extras]
crypto = ["cryptography (>=3.4.0)"]
dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "pymysql" name = "pymysql"
version = "1.1.1" version = "1.1.1"
@ -1317,6 +1596,25 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
six = ">=1.5"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "python-dotenv" name = "python-dotenv"
version = "1.0.1" version = "1.0.1"
@ -1336,6 +1634,32 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "python-jose"
version = "3.3.0"
description = "JOSE implementation in Python"
optional = false
python-versions = "*"
files = [
{file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"},
{file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"},
]
[package.dependencies]
ecdsa = "!=0.15"
pyasn1 = "*"
rsa = "*"
[package.extras]
cryptography = ["cryptography (>=3.4.0)"]
pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"]
pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "python-json-logger" name = "python-json-logger"
version = "2.0.7" version = "2.0.7"
@ -1547,6 +1871,25 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "rsa"
version = "4.9"
description = "Pure-Python RSA implementation"
optional = false
python-versions = ">=3.6,<4"
files = [
{file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
{file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
]
[package.dependencies]
pyasn1 = ">=0.1.3"
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "shellingham" name = "shellingham"
version = "1.5.4" version = "1.5.4"
@ -1563,6 +1906,22 @@ type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple" url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors" reference = "mirrors"
[[package]]
name = "six"
version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
]
[package.source]
type = "legacy"
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
reference = "mirrors"
[[package]] [[package]]
name = "sniffio" name = "sniffio"
version = "1.3.1" version = "1.3.1"
@ -2116,4 +2475,4 @@ reference = "mirrors"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "39a4bc6981b275faba68cb572e406a60300d2d868e646fbbf62ad251e2288e37" content-hash = "d43623d3a4da31efcc1a54e1318db9ef412e1791ae9af50374d0c5d6faae6357"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "fastapi-app-template" name = "fastapi-template"
version = "0.1.0" version = "0.1.5"
description = "" description = ""
authors = ["chenwj113 <chenwj113@gmail.com>"] authors = ["chenwj113 <chenwj113@gmail.com>"]
license = "MIT" license = "MIT"
@ -19,6 +19,9 @@ pytz = "^2024.1"
qiniu = "^7.13.2" qiniu = "^7.13.2"
pillow = "^10.4.0" pillow = "^10.4.0"
captcha = "^0.6.0" captcha = "^0.6.0"
fastapi-limiter = "^0.1.6"
authx = "^1.3.0"
itsdangerous = "^2.2.0"
[[tool.poetry.source]] [[tool.poetry.source]]

View File

@ -1,25 +1,75 @@
aiofiles==22.1.0 --index-url https://pypi.tuna.tsinghua.edu.cn/simple
aioredis==2.0.1
aiomysql==0.1.1 aiojobs==1.2.1 ; python_version >= "3.10" and python_version < "4.0"
bcrypt==4.0.1 aiomysql==0.1.1 ; python_version >= "3.10" and python_version < "4.0"
email_validator>=2.0.0 aioredis==2.0.1 ; python_version >= "3.10" and python_version < "4.0"
fastapi==0.111.0 annotated-types==0.7.0 ; python_version >= "3.10" and python_version < "4.0"
fastapi-plugins==0.13.0 anyio==4.4.0 ; python_version >= "3.10" and python_version < "4.0"
FastAPI-SQLAlchemy==0.2.1 async-timeout==4.0.3 ; python_version >= "3.10" and python_version < "4.0"
pydantic>=2.0.0 authx==1.3.0 ; python_version >= "3.10" and python_version < "4.0"
pydantic-settings==2.2.1 captcha==0.6.0 ; python_version >= "3.10" and python_version < "4.0"
# pydantic-sqlalchemy==0.0.9 certifi==2024.6.2 ; python_version >= "3.10" and python_version < "4.0"
python-multipart>=0.0.7 cffi==1.17.0 ; python_version >= "3.10" and python_version < "4.0" and platform_python_implementation != "PyPy"
pytest==7.2.1 charset-normalizer==3.3.2 ; python_version >= "3.10" and python_version < "4.0"
requests==2.28.2 click==8.1.7 ; python_version >= "3.10" and python_version < "4.0"
sqlacodegen==2.3.0 colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows")
SQLAlchemy==2.0.1 cryptography==43.0.0 ; python_version >= "3.10" and python_version < "4.0"
uvicorn==0.20.0 dnspython==2.6.1 ; python_version >= "3.10" and python_version < "4.0"
PyJWT==2.6.0 ecdsa==0.19.0 ; python_version >= "3.10" and python_version < "4.0"
passlib==1.7.4 email-validator==2.1.1 ; python_version >= "3.10" and python_version < "4.0"
Pillow==9.4.0 exceptiongroup==1.2.1 ; python_version >= "3.10" and python_version < "3.11"
captcha==0.4 fastapi-cli==0.0.4 ; python_version >= "3.10" and python_version < "4.0"
jinja2==3.1.2 fastapi-limiter==0.1.6 ; python_version >= "3.10" and python_version < "4.0"
pycryptodome==3.17 fastapi-plugins==0.13.0 ; python_version >= "3.10" and python_version < "4.0"
qiniu==7.10.0 fastapi-sqlalchemy==0.2.1 ; python_version >= "3.10" and python_version < "4.0"
pytz==2022.7.1 fastapi==0.111.0 ; python_version >= "3.10" and python_version < "4.0"
greenlet==3.0.3 ; python_version >= "3.10" and python_version < "4.0" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32")
h11==0.14.0 ; python_version >= "3.10" and python_version < "4.0"
hiredis==2.3.2 ; python_version >= "3.10" and python_version < "4.0"
httpcore==1.0.5 ; python_version >= "3.10" and python_version < "4.0"
httptools==0.6.1 ; python_version >= "3.10" and python_version < "4.0"
httpx==0.27.0 ; python_version >= "3.10" and python_version < "4.0"
idna==3.7 ; python_version >= "3.10" and python_version < "4.0"
itsdangerous==2.2.0 ; python_version >= "3.10" and python_version < "4.0"
jinja2==3.1.4 ; python_version >= "3.10" and python_version < "4.0"
markdown-it-py==3.0.0 ; python_version >= "3.10" and python_version < "4.0"
markupsafe==2.1.5 ; python_version >= "3.10" and python_version < "4.0"
mdurl==0.1.2 ; python_version >= "3.10" and python_version < "4.0"
orjson==3.10.4 ; python_version >= "3.10" and python_version < "4.0"
passlib==1.7.4 ; python_version >= "3.10" and python_version < "4.0"
pillow==10.4.0 ; python_version >= "3.10" and python_version < "4.0"
pyasn1==0.6.0 ; python_version >= "3.10" and python_version < "4.0"
pycparser==2.22 ; python_version >= "3.10" and python_version < "4.0" and platform_python_implementation != "PyPy"
pydantic-core==2.18.4 ; python_version >= "3.10" and python_version < "4.0"
pydantic-settings==2.3.1 ; python_version >= "3.10" and python_version < "4.0"
pydantic==2.7.3 ; python_version >= "3.10" and python_version < "4.0"
pygments==2.18.0 ; python_version >= "3.10" and python_version < "4.0"
pyjwt[crypto]==2.9.0 ; python_version >= "3.10" and python_version < "4.0"
pymysql==1.1.1 ; python_version >= "3.10" and python_version < "4.0"
python-dateutil==2.9.0.post0 ; python_version >= "3.10" and python_version < "4.0"
python-dotenv==1.0.1 ; python_version >= "3.10" and python_version < "4.0"
python-jose==3.3.0 ; python_version >= "3.10" and python_version < "4.0"
python-json-logger==2.0.7 ; python_version >= "3.10" and python_version < "4.0"
python-multipart==0.0.9 ; python_version >= "3.10" and python_version < "4.0"
pytz==2024.1 ; python_version >= "3.10" and python_version < "4.0"
pyyaml==6.0.1 ; python_version >= "3.10" and python_version < "4.0"
qiniu==7.13.2 ; python_version >= "3.10" and python_version < "4.0"
redis==5.0.5 ; python_version >= "3.10" and python_version < "4.0"
redis[hiredis]==5.0.5 ; python_version >= "3.10" and python_version < "4.0"
requests==2.32.3 ; python_version >= "3.10" and python_version < "4.0"
rich==13.7.1 ; python_version >= "3.10" and python_version < "4.0"
rsa==4.9 ; python_version >= "3.10" and python_version < "4"
shellingham==1.5.4 ; python_version >= "3.10" and python_version < "4.0"
six==1.16.0 ; python_version >= "3.10" and python_version < "4.0"
sniffio==1.3.1 ; python_version >= "3.10" and python_version < "4.0"
sqlalchemy==2.0.0 ; python_version >= "3.10" and python_version < "4.0"
starlette==0.37.2 ; python_version >= "3.10" and python_version < "4.0"
tenacity==8.3.0 ; python_version >= "3.10" and python_version < "4.0"
typer==0.12.3 ; python_version >= "3.10" and python_version < "4.0"
typing-extensions==4.12.2 ; python_version >= "3.10" and python_version < "4.0"
ujson==5.10.0 ; python_version >= "3.10" and python_version < "4.0"
urllib3==2.2.2 ; python_version >= "3.10" and python_version < "4.0"
uvicorn[standard]==0.30.1 ; python_version >= "3.10" and python_version < "4.0"
uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_version >= "3.10" and python_version < "4.0"
watchfiles==0.22.0 ; python_version >= "3.10" and python_version < "4.0"
websockets==12.0 ; python_version >= "3.10" and python_version < "4.0"

16
src/api/auth_example.py Normal file
View File

@ -0,0 +1,16 @@
from fastapi import APIRouter, HTTPException, Depends
# 校验授权token
from src.middleware.auth import security
router = APIRouter()
@router.get('/', dependencies=[Depends(security.access_token_required)])
def index():
return {"message": "Hello World"}
@router.get('/login')
def login(username: str, password: str):
if username == "test" and password == "test":
token = security.create_access_token(uid=username)
return {"access_token": token}
raise HTTPException(401, detail={"message": "Bad credentials"})

View File

@ -7,6 +7,8 @@ from fastapi.security import OAuth2PasswordBearer
from fastapi_plugins import depends_redis from fastapi_plugins import depends_redis
from fastapi_sqlalchemy import db from fastapi_sqlalchemy import db
from sqlalchemy.sql import text from sqlalchemy.sql import text
from fastapi_limiter.depends import RateLimiter
from pydantic import BaseModel from pydantic import BaseModel
from src.service.example import get_user_by_id from src.service.example import get_user_by_id
@ -15,8 +17,8 @@ from src.dtos.example import UserExampleListPagesResult
router = APIRouter() router = APIRouter()
# 并发数限制 每5秒最多100次请求
@router.get("/") @router.get("/", dependencies=[Depends(RateLimiter(times=100, seconds=5))])
def index(): def index():
return {"msg": "This is Index Page"} return {"msg": "This is Index Page"}

13
src/middleware/auth.py Normal file
View File

@ -0,0 +1,13 @@
from authx import AuthX, AuthXConfig
from datetime import timedelta
config = AuthXConfig()
config.JWT_ALGORITHM = "HS256"
config.JWT_SECRET_KEY = "SECRET_KEY"
config.JWT_TOKEN_LOCATION = ["headers", "query", "cookies", "json"]
config.JWT_HEADER_NAME = "X-Token"
config.JWT_HEADER_TYPE = ""
# access token expires in 24 hours
config.JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=24)
security = AuthX(config=config)

View File

@ -1,9 +1,10 @@
import logging import logging
from collections import defaultdict
from fastapi import Request, status from fastapi import Request, status
from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.exceptions import HTTPException, RequestValidationError
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import authx.exceptions as authx_exceptions
from src.dtos import BaseResponse, ErrorModel from src.dtos import BaseResponse, ErrorModel
logger = logging.getLogger("uvicorn.error") logger = logging.getLogger("uvicorn.error")
@ -11,35 +12,32 @@ logger = logging.getLogger("uvicorn.error")
def request_validation_error_handler(req: Request, exc: RequestValidationError): def request_validation_error_handler(req: Request, exc: RequestValidationError):
""" """
请求响应校验错误处理 请求校验错误处理
:param req: :param req:
:param exc: :param exc:
:return: :return:
""" """
errors = exc.errors() errors = exc.errors()
logger.error(f"{req.method} {req.url} , errors: {errors}") logger.error(f"{req.method} {req.url} , errors: {errors}")
response_errors = [error for error in errors if error["loc"][0] == "response"] group_err = defaultdict(list)
if len(response_errors) > 0: for err in errors:
message = "接口响应异常 " group_err[err["type"]].append(err)
else: details = ""
message = "接口请求异常 " for err_type, err_list in group_err.items():
json_err = [err["msg"] for err in errors if "value_error.jsondecode" == err["type"]] match err_type:
if json_err: case "missing" | "value_error.missing" :
message += "请求参数JSON编码有误;" details += f"缺少字段:{', '.join([err['loc'][-1] for err in err_list])};"
value_err = [err["msg"] for err in errors if "value_error" == err["type"]] case "value_error.jsondecode":
if value_err: details += "请求参数JSON编码有误;"
message += "; ".join(value_err) case "value_error":
missing_err = [err["loc"][-1] for err in errors if "value_error.missing" == err["type"]] details += "; ".join([err["msg"] for err in err_list])
if missing_err: case "type_error":
message += "缺少字段:{} ".format(", ".join(missing_err)) details += "; ".join([err["msg"].replace("value", f"{err['loc'][1]}") for err in err_list])
type_err = [err["msg"].replace("value", f"{err['loc'][1]}") for err in errors if "type_error" == err["type"]] case "type_error.none.not_allowed":
if type_err: details += f"字段:{', '.join([err['loc'][-1] for err in err_list])} 不能为空;"
message += "; ".join(type_err) case _:
not_none_err = [err["loc"][-1] for err in errors if "type_error.none.not_allowed" == err["type"]] pass
if not_none_err: err_model = ErrorModel(code=status.HTTP_400_BAD_REQUEST, message="接口请求校验异常", details=details)
message += "字段:{} 不能为空".format(", ".join(not_none_err))
err_model = ErrorModel(code=status.HTTP_400_BAD_REQUEST, message=message, details=message)
res = BaseResponse(error=err_model) res = BaseResponse(error=err_model)
return JSONResponse(status_code=200, content=res.dict()) return JSONResponse(status_code=200, content=res.dict())
@ -54,4 +52,23 @@ def http_exception_handler(req: Request, exc: HTTPException):
logger.error(f"{req.method} {req.url} , exception:{exc.detail}") logger.error(f"{req.method} {req.url} , exception:{exc.detail}")
err = ErrorModel(code=exc.status_code, message=exc.detail) err = ErrorModel(code=exc.status_code, message=exc.detail)
res = BaseResponse(result=None, error=err) res = BaseResponse(result=None, error=err)
return JSONResponse(status_code=exc.status_code, content=res.dict()) return JSONResponse(status_code=exc.status_code, content=res.model_dump())
def authx_exception_handler(req: Request, exc: authx_exceptions.AuthXException):
"""
AuthX Exception 错误处理
:param req:
:param exc:
:return:
"""
logger.error(f"{req.method} {req.url} , exception:{exc}")
match type(exc):
case authx_exceptions.MissingTokenError:
err = ErrorModel(code=status.HTTP_401_UNAUTHORIZED, message="请求头缺失X-Token")
case authx_exceptions.JWTDecodeError:
err = ErrorModel(code=status.HTTP_401_UNAUTHORIZED, message="X-Token解析失败")
case _:
err = ErrorModel(code=status.HTTP_401_UNAUTHORIZED, message="未知的错误导致认证失败")
res = BaseResponse(result=None, error=err)
return JSONResponse(status_code=err.code, content=res.model_dump())