Skip to content

Commit e29dec1

Browse files
authored
更新架构并修复问题 (#25)
* 更新架构并修复问题 * 修复问题
1 parent c013cda commit e29dec1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1590
-1335
lines changed

.pre-commit-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v5.0.0
44
hooks:
55
- id: check-added-large-files
66
- id: end-of-file-fixer
77
- id: check-yaml
88
- id: check-toml
99

1010
- repo: https://github.com/charliermarsh/ruff-pre-commit
11-
rev: v0.4.5
11+
rev: v0.9.5
1212
hooks:
1313
- id: ruff
1414
args:
@@ -19,7 +19,7 @@ repos:
1919
- id: ruff-format
2020

2121
- repo: https://github.com/pdm-project/pdm
22-
rev: 2.12.4
22+
rev: 2.22.3
2323
hooks:
2424
- id: pdm-export
2525
args:

.ruff.toml

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,35 @@
11
line-length = 120
2+
unsafe-fixes = true
3+
cache-dir = ".ruff_cache"
24
target-version = "py310"
3-
cache-dir = "./.ruff_cache"
45

56
[lint]
67
select = [
78
"E",
89
"F",
9-
"I",
1010
"W505",
11-
"PT018",
1211
"SIM101",
1312
"SIM114",
1413
"PGH004",
1514
"PLE1142",
1615
"RUF100",
16+
"I002",
1717
"F404",
18-
"TCH",
18+
"TC",
1919
"UP007"
2020
]
21+
preview = true
2122

2223
[lint.isort]
2324
lines-between-types = 1
25+
order-by-type = true
2426

2527
[lint.per-file-ignores]
26-
"backend/app/api/v1/*.py" = ["TCH"]
27-
"backend/app/models/*.py" = ["TCH003"]
28-
"backend/app/**/__init__.py" = ["F401"]
28+
"**/api/v1/*.py" = ["TC"]
29+
"**/model/*.py" = ["TC003"]
30+
"**/model/__init__.py" = ["F401"]
2931

3032
[format]
33+
preview = true
3134
quote-style = "single"
35+
docstring-code-format = true

Dockerfile

+16-9
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@ WORKDIR /ftm
44

55
COPY . .
66

7-
RUN sed -i s@/deb.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
8-
&& sed -i s@/security.debian.org/@/mirrors.aliyun.com/@g /etc/apt/sources.list
7+
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources \
8+
&& sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list.d/debian.sources
99

1010
RUN apt-get update \
11-
&& apt-get install -y --no-install-recommends gcc python3-dev \
12-
&& rm -rf /var/lib/apt/lists/*
11+
&& apt-get install -y --no-install-recommends gcc python3-dev supervisor \
12+
&& rm -rf /var/lib/apt/lists/* \
13+
# 某些包可能存在同步不及时导致安装失败的情况,可更改为官方源:https://pypi.org/simple
14+
&& pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple \
15+
&& pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple \
16+
&& pip install gunicorn wait-for-it -i https://mirrors.aliyun.com/pypi/simple
1317

14-
RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple \
15-
&& pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple
18+
ENV TZ="Asia/Shanghai"
1619

17-
ENV TZ = Asia/Shanghai
20+
RUN mkdir -p /var/log/fastapi_server \
21+
&& mkdir -p /var/log/supervisor \
22+
&& mkdir -p /etc/supervisor/conf.d
1823

19-
RUN mkdir -p /var/log/fastapi_server
24+
COPY deploy/supervisor.conf /etc/supervisor/supervisord.conf
25+
26+
COPY deploy/fastapi_server.conf /etc/supervisor/conf.d/
2027

2128
EXPOSE 8001
2229

23-
CMD ["uvicorn", "backend.main:app", "--host", "127.0.0.1", "--port", "8000"]
30+
CMD ["uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8001"]

README.md

+6-23
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
# FastAPI Tortoise Architecture
22

3-
作为 FastAPI 框架的一个基础项目,基于 python3.10 开发
3+
## 本地开发
44

5-
## 特征
6-
7-
- [x] FastAPI > 0.100.0
8-
- [x] Async design
9-
- [x] Restful API
10-
- [x] Tortoise-orm > 0.20.0
11-
- [x] Pydantic 2.0
12-
- [x] Docker
13-
- [ ] ......
14-
15-
## 使用
16-
17-
> [!WARNING]
18-
> 此过程请格外注意端口占用情况, 特别是 8000, 3306, 6379...
19-
20-
### 1: 传统
5+
* Python 3.10+
6+
* Mysql 8.0+
7+
* Redis 推荐最新稳定版
218

229
1. 安装依赖项
2310

@@ -64,7 +51,7 @@
6451
# 开发模式
6552
fastapi dev main.py
6653
```
67-
54+
6855
9. 浏览器访问: http://127.0.0.1:8000/api/v1/docs
6956

7057
---
@@ -86,11 +73,7 @@
8673
```
8774

8875
3. 等待命令自动完成
89-
4. 浏览器访问:http://127.0.0.1:8000/api/v1/docs
90-
91-
## 互动
92-
93-
[WeChat / QQ](https://github.com/wu-clan)
76+
4. 浏览器访问:http://127.0.0.1:8000/docs
9477

9578
## 赞助
9679

backend/.env.example

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Env: dev、pro
22
ENVIRONMENT='dev'
3-
# MySQL
4-
DB_HOST='127.0.0.1'
5-
DB_PORT=3306
6-
DB_USER='root'
7-
DB_PASSWORD='123456'
3+
# Database
4+
DATABASE_HOST='127.0.0.1'
5+
DATABASE_PORT=3306
6+
DATABASE_USER='root'
7+
DATABASE_PASSWORD='123456'
88
# Redis
99
REDIS_HOST='127.0.0.1'
1010
REDIS_PORT=6379

backend/app/admin/api/router.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from backend.app.admin.api.v1.user import router as user_router
77
from backend.core.conf import settings
88

9-
v1 = APIRouter(prefix=settings.API_V1_STR)
9+
v1 = APIRouter(prefix=settings.FASTAPI_API_V1_PATH)
1010

11-
v1.include_router(auth_router, prefix='/auth', tags=['认证'])
11+
v1.include_router(auth_router)
1212
v1.include_router(user_router, prefix='/users', tags=['用户'])

backend/app/admin/api/v1/auth/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from backend.app.admin.api.v1.auth.auth import router as auth_router
66
from backend.app.admin.api.v1.auth.captcha import router as captcha_router
77

8-
router = APIRouter()
8+
router = APIRouter(prefix='/auth')
99

10-
router.include_router(auth_router)
11-
router.include_router(captcha_router)
10+
router.include_router(auth_router, tags=['授权'])
11+
router.include_router(captcha_router, prefix='/captcha', tags=['验证码'])

backend/app/admin/api/v1/auth/auth.py

+16-22
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,27 @@
33
from fastapi import APIRouter, Depends, Request
44
from fastapi.security import OAuth2PasswordRequestForm
55

6-
from backend.app.admin.schemas.token import Token
7-
from backend.app.admin.schemas.user import Auth, Auth2
8-
from backend.app.admin.services.user_service import UserService
9-
from backend.common.jwt import DependsJwtUser
10-
from backend.common.response.response_schema import response_base
6+
from backend.app.admin.schema.token import GetLoginToken, GetSwaggerToken
7+
from backend.app.admin.schema.user import Auth2
8+
from backend.app.admin.service.auth_service import auth_service
9+
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
10+
from backend.common.security.jwt import DependsJwtAuth
1111

1212
router = APIRouter()
1313

1414

15-
@router.post('/swagger_login', summary='swagger 表单登录', description='form 格式登录,仅用于 swagger 文档调试接口')
16-
async def login1(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
17-
token, user = await UserService.login_swagger(form_data)
18-
return Token(access_token=token, user=user)
15+
@router.post('/login/swagger', summary='swagger 调试专用', description='用于快捷进行 swagger 认证')
16+
async def swagger_login(form_data: OAuth2PasswordRequestForm = Depends()) -> GetSwaggerToken:
17+
token, user = await auth_service.swagger_login(form_data=form_data)
18+
return GetSwaggerToken(access_token=token, user=user) # type: ignore
1919

2020

21-
@router.post('/login', summary='json登录')
22-
async def login2(obj: Auth) -> Token:
23-
token, user = await UserService.login_json(obj)
24-
return Token(access_token=token, user=user)
21+
@router.post('/login', summary='验证码登录')
22+
async def user_login(request: Request, obj: Auth2) -> ResponseSchemaModel[GetLoginToken]:
23+
data = await auth_service.login(request=request, obj=obj)
24+
return response_base.success(data=data)
2525

2626

27-
@router.post('/captcha_login', summary='验证码登录')
28-
async def login3(request: Request, obj: Auth2) -> Token:
29-
token, user = await UserService.login_captcha(obj=obj, request=request)
30-
return Token(access_token=token, user=user)
31-
32-
33-
@router.post('/logout', summary='登出', dependencies=[DependsJwtUser])
34-
async def user_logout():
35-
return await response_base.response_200()
27+
@router.post('/logout', summary='用户登出', dependencies=[DependsJwtAuth])
28+
async def user_logout() -> ResponseModel:
29+
return response_base.success()
+24-9
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
11
#!/usr/bin/env python3
22
# -*- coding: utf-8 -*-
3+
from uuid import uuid4
4+
35
from fast_captcha import img_captcha
46
from fastapi import APIRouter, Depends, Request
57
from fastapi_limiter.depends import RateLimiter
68
from starlette.concurrency import run_in_threadpool
7-
from starlette.responses import StreamingResponse
89

9-
from backend.common.redis import redis_client
10+
from backend.app.admin.schema.captcha import GetCaptchaDetail
11+
from backend.common.response.response_schema import ResponseSchemaModel, response_base
1012
from backend.core.conf import settings
11-
from backend.utils.generate_string import get_uuid4_str
13+
from backend.database.redis import redis_client
1214

1315
router = APIRouter()
1416

1517

16-
@router.get('/captcha', summary='获取验证码', dependencies=[Depends(RateLimiter(times=5, seconds=10))])
17-
async def get_captcha(request: Request):
18-
img, code = await run_in_threadpool(img_captcha)
19-
uuid = get_uuid4_str()
18+
@router.get(
19+
'',
20+
summary='获取登录验证码',
21+
dependencies=[Depends(RateLimiter(times=5, seconds=10))],
22+
)
23+
async def get_captcha(request: Request) -> ResponseSchemaModel[GetCaptchaDetail]:
24+
"""
25+
此接口可能存在性能损耗,尽管是异步接口,但是验证码生成是IO密集型任务,使用线程池尽量减少性能损耗
26+
"""
27+
img_type: str = 'base64'
28+
img, code = await run_in_threadpool(img_captcha, img_byte=img_type)
29+
uuid = str(uuid4())
2030
request.app.state.captcha_uuid = uuid
21-
await redis_client.set(uuid, code, settings.CAPTCHA_EXPIRATION_TIME)
22-
return StreamingResponse(content=img, media_type='image/jpeg')
31+
await redis_client.set(
32+
f'{settings.CAPTCHA_LOGIN_REDIS_PREFIX}:{uuid}',
33+
code,
34+
ex=settings.CAPTCHA_LOGIN_EXPIRE_SECONDS,
35+
)
36+
data = GetCaptchaDetail(image_type=img_type, image=img)
37+
return response_base.success(data=data)

0 commit comments

Comments
 (0)