Skip to content

Commit ee6d9e4

Browse files
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # backend/.env.example # backend/app/admin/service/auth_service.py # backend/core/conf.py # deploy/backend/docker-compose/.env.server
2 parents 0cc6c9e + eddfa56 commit ee6d9e4

File tree

4 files changed

+82
-178
lines changed

4 files changed

+82
-178
lines changed

backend/.env.example

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,19 @@ REDIS_DATABASE=0
1515
TOKEN_SECRET_KEY='1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk'
1616
# Opera Log
1717
OPERA_LOG_ENCRYPT_SECRET_KEY='d77b25790a804c2b4a339dd0207941e4cefa5751935a33735bc73bb7071a005b'
18-
# App Admin
19-
# OAuth2
20-
OAUTH2_GITHUB_CLIENT_ID='test'
21-
OAUTH2_GITHUB_CLIENT_SECRET='test'
22-
OAUTH2_LINUX_DO_CLIENT_ID='test'
23-
OAUTH2_LINUX_DO_CLIENT_SECRET='test'
24-
# App Task
18+
# [ App ] task
2519
# Celery
2620
CELERY_BROKER_REDIS_DATABASE=1
2721
# Rabbitmq
2822
CELERY_RABBITMQ_HOST='127.0.0.1'
2923
CELERY_RABBITMQ_PORT=5672
3024
CELERY_RABBITMQ_USERNAME='guest'
3125
CELERY_RABBITMQ_PASSWORD='guest'
32-
# SMS
33-
TENCENTCLOUD_SECRET_ID='' # 腾讯云密钥ID
34-
TENCENTCLOUD_SECRET_KEY='' # 腾讯云密钥KEY
35-
SMS_LOGIN_TEMPLATE_ID='' # 短信登录模板ID
36-
SMS_SIGN_NAME='' # 短信签名
37-
SMS_SDK_APP_ID='' # 短信应用ID
26+
# [ Plugin ] oauth2
27+
OAUTH2_GITHUB_CLIENT_ID='test'
28+
OAUTH2_GITHUB_CLIENT_SECRET='test'
29+
OAUTH2_LINUX_DO_CLIENT_ID='test'
30+
OAUTH2_LINUX_DO_CLIENT_SECRET='test'
31+
# [ Plugin ] email
32+
EMAIL_USERNAME=''
33+
EMAIL_PASSWORD=''

backend/app/admin/service/auth_service.py

Lines changed: 1 addition & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from backend.app.admin.crud.crud_user import user_dao
1010
from backend.app.admin.model import User
1111
from backend.app.admin.schema.token import GetLoginToken, GetNewToken
12-
from backend.app.admin.schema.user import AuthLoginParam, SmsLoginParam
12+
from backend.app.admin.schema.user import AuthLoginParam
1313
from backend.app.admin.service.login_log_service import login_log_service
1414
from backend.common.enums import LoginLogStatusType
1515
from backend.common.exception import errors
@@ -163,108 +163,6 @@ async def login(
163163
)
164164
return data
165165

166-
async def login_by_sms(
167-
self, *, request: Request, response: Response, obj: SmsLoginParam, background_tasks: BackgroundTasks
168-
) -> GetLoginToken:
169-
"""
170-
短信验证码登录
171-
172-
:param request: 请求对象
173-
:param response: 响应对象
174-
:param obj: 短信登录参数
175-
:param background_tasks: 后台任务
176-
:return:
177-
"""
178-
async with async_db_session.begin() as db:
179-
user = None
180-
try:
181-
# 从Redis获取验证码
182-
sms_code = await redis_client.get(f'{settings.SMS_LOGIN_REDIS_PREFIX}:{obj.phone}')
183-
if not sms_code:
184-
raise errors.RequestError(msg='验证码失效,请重新获取')
185-
if sms_code != obj.code:
186-
raise errors.CustomError(error=CustomErrorCode.CAPTCHA_ERROR, msg='验证码错误')
187-
188-
# 验证码正确,删除Redis中的验证码
189-
await redis_client.delete(f'{settings.SMS_LOGIN_REDIS_PREFIX}:{obj.phone}')
190-
191-
# 根据手机号获取用户
192-
user = await user_dao.get_by_phone(db, obj.phone)
193-
if not user:
194-
raise errors.NotFoundError(msg='该手机号未注册')
195-
196-
if not user.status:
197-
raise errors.AuthorizationError(msg='用户已被锁定, 请联系统管理员')
198-
199-
# 更新登录时间
200-
await user_dao.update_login_time(db, user.username)
201-
await db.refresh(user)
202-
203-
# 创建访问令牌
204-
access_token = await create_access_token(
205-
user.id,
206-
user.is_multi_login,
207-
# extra info
208-
username=user.username,
209-
nickname=user.nickname,
210-
last_login_time=timezone.to_str(user.last_login_time),
211-
ip=request.state.ip,
212-
os=request.state.os,
213-
browser=request.state.browser,
214-
device=request.state.device,
215-
)
216-
217-
# 创建刷新令牌
218-
refresh_token = await create_refresh_token(access_token.session_uuid, user.id, user.is_multi_login)
219-
response.set_cookie(
220-
key=settings.COOKIE_REFRESH_TOKEN_KEY,
221-
value=refresh_token.refresh_token,
222-
max_age=settings.COOKIE_REFRESH_TOKEN_EXPIRE_SECONDS,
223-
expires=timezone.to_utc(refresh_token.refresh_token_expire_time),
224-
httponly=True,
225-
)
226-
except errors.NotFoundError as e:
227-
log.error(f'短信登录错误: {e.msg}')
228-
raise errors.NotFoundError(msg=e.msg)
229-
except (errors.RequestError, errors.CustomError) as e:
230-
log.error(f'短信登录错误: {e.msg}')
231-
task = BackgroundTask(
232-
login_log_service.create,
233-
**dict(
234-
db=db,
235-
request=request,
236-
user_uuid=user.uuid if user else uuid4_str(),
237-
username=user.username if user else obj.phone,
238-
login_time=timezone.now(),
239-
status=LoginLogStatusType.fail.value,
240-
msg=e.msg,
241-
),
242-
)
243-
raise errors.RequestError(msg=e.msg, background=task)
244-
except Exception as e:
245-
log.error(f'短信登录错误: {e}')
246-
raise e
247-
else:
248-
background_tasks.add_task(
249-
login_log_service.create,
250-
**dict(
251-
db=db,
252-
request=request,
253-
user_uuid=user.uuid,
254-
username=user.username,
255-
login_time=timezone.now(),
256-
status=LoginLogStatusType.success.value,
257-
msg='短信验证码登录成功',
258-
),
259-
)
260-
data = GetLoginToken(
261-
access_token=access_token.access_token,
262-
access_token_expire_time=access_token.access_token_expire_time,
263-
session_uuid=access_token.session_uuid,
264-
user=user, # type: ignore
265-
)
266-
return data
267-
268166
@staticmethod
269167
async def get_codes(*, request: Request) -> list[str]:
270168
"""

backend/core/conf.py

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from pydantic_settings import BaseSettings, SettingsConfigDict
88

99
from backend.core.path_conf import BASE_PATH
10-
import os
1110

1211

1312
class Settings(BaseSettings):
@@ -20,27 +19,8 @@ class Settings(BaseSettings):
2019
case_sensitive=True,
2120
)
2221

23-
# .env 环境
24-
ENVIRONMENT: Literal['dev', 'pro']
25-
26-
# .env 数据库
27-
DATABASE_TYPE: Literal['mysql', 'postgresql']
28-
DATABASE_HOST: str
29-
DATABASE_PORT: int
30-
DATABASE_USER: str
31-
DATABASE_PASSWORD: str
32-
33-
# .env Redis
34-
REDIS_HOST: str
35-
REDIS_PORT: int
36-
REDIS_PASSWORD: str
37-
REDIS_DATABASE: int
38-
39-
# .env Token
40-
TOKEN_SECRET_KEY: str # 密钥 secrets.token_urlsafe(32)
41-
42-
# .env 操作日志加密密钥
43-
OPERA_LOG_ENCRYPT_SECRET_KEY: str # 密钥 os.urandom(32), 需使用 bytes.hex() 方法转换为 str
22+
# .env 当前环境
23+
ENVIRONMENT: Literal['dev', 'prod']
4424

4525
# FastAPI
4626
FASTAPI_API_V1_PATH: str = '/api/v1'
@@ -52,15 +32,31 @@ class Settings(BaseSettings):
5232
FASTAPI_OPENAPI_URL: str | None = '/openapi'
5333
FASTAPI_STATIC_FILES: bool = True
5434

35+
# .env 数据库
36+
DATABASE_TYPE: Literal['mysql', 'postgresql']
37+
DATABASE_HOST: str
38+
DATABASE_PORT: int
39+
DATABASE_USER: str
40+
DATABASE_PASSWORD: str
41+
5542
# 数据库
5643
DATABASE_ECHO: bool | Literal['debug'] = False
5744
DATABASE_POOL_ECHO: bool | Literal['debug'] = False
5845
DATABASE_SCHEMA: str = 'fba'
5946
DATABASE_CHARSET: str = 'utf8mb4'
6047

48+
# .env Redis
49+
REDIS_HOST: str
50+
REDIS_PORT: int
51+
REDIS_PASSWORD: str
52+
REDIS_DATABASE: int
53+
6154
# Redis
6255
REDIS_TIMEOUT: int = 5
6356

57+
# .env Token
58+
TOKEN_SECRET_KEY: str # 密钥 secrets.token_urlsafe(32)
59+
6460
# Token
6561
TOKEN_ALGORITHM: str = 'HS256'
6662
TOKEN_EXPIRE_SECONDS: int = 60 * 60 * 24 # 1 天
@@ -90,7 +86,11 @@ class Settings(BaseSettings):
9086
COOKIE_REFRESH_TOKEN_KEY: str = 'fba_refresh_token'
9187
COOKIE_REFRESH_TOKEN_EXPIRE_SECONDS: int = 60 * 60 * 24 * 7 # 7 天
9288

93-
# 数据权限配置
89+
# 验证码
90+
CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba:login:captcha'
91+
CAPTCHA_LOGIN_EXPIRE_SECONDS: int = 60 * 5 # 3 分钟
92+
93+
# 数据权限
9494
DATA_PERMISSION_MODELS: dict[str, str] = { # 允许进行数据过滤的 SQLA 模型,它必须以模块字符串的方式定义
9595
'部门': 'backend.app.admin.model.Dept',
9696
}
@@ -163,6 +163,9 @@ class Settings(BaseSettings):
163163
LOG_ACCESS_FILENAME: str = 'fba_access.log'
164164
LOG_ERROR_FILENAME: str = 'fba_error.log'
165165

166+
# .env 操作日志
167+
OPERA_LOG_ENCRYPT_SECRET_KEY: str # 密钥 os.urandom(32), 需使用 bytes.hex() 方法转换为 str
168+
166169
# 操作日志
167170
OPERA_LOG_PATH_EXCLUDE: list[str] = [
168171
'/favicon.ico',
@@ -188,30 +191,12 @@ class Settings(BaseSettings):
188191
PLUGIN_PIP_INDEX_URL: str = 'https://mirrors.aliyun.com/pypi/simple/'
189192
PLUGIN_REDIS_PREFIX: str = 'fba:plugin'
190193

191-
# App Admin
192-
# .env OAuth2
193-
OAUTH2_GITHUB_CLIENT_ID: str
194-
OAUTH2_GITHUB_CLIENT_SECRET: str
195-
OAUTH2_LINUX_DO_CLIENT_ID: str
196-
OAUTH2_LINUX_DO_CLIENT_SECRET: str
194+
# I18n 配置
195+
I18N_DEFAULT_LANGUAGE: str = 'zh-CN'
197196

198-
# OAuth2
199-
OAUTH2_FRONTEND_REDIRECT_URI: str = 'http://localhost:5173/oauth2/callback'
200-
201-
# 验证码
202-
CAPTCHA_LOGIN_REDIS_PREFIX: str = 'fba:login:captcha'
203-
CAPTCHA_LOGIN_EXPIRE_SECONDS: int = 60 * 5 # 3 分钟
204-
205-
# 短信验证码
206-
TENCENTCLOUD_SECRET_ID: str
207-
TENCENTCLOUD_SECRET_KEY: str
208-
SMS_LOGIN_REDIS_PREFIX: str = "fba:sms:login"
209-
SMS_LOGIN_EXPIRE_SECONDS: int = 300 # 短信验证码有效期,5分钟
210-
SMS_LOGIN_TEMPLATE_ID: str # 短信登录模板ID
211-
SMS_SIGN_NAME: str # 短信签名
212-
SMS_SDK_APP_ID: str # 短信应用ID
213-
214-
# App Task
197+
##################################################
198+
# [ App ] task
199+
##################################################
215200
# .env Redis
216201
CELERY_BROKER_REDIS_DATABASE: int
217202

@@ -227,18 +212,47 @@ class Settings(BaseSettings):
227212
CELERY_REDIS_PREFIX: str = 'fba:celery'
228213
CELERY_TASK_MAX_RETRIES: int = 5
229214

230-
# Plugin Code Generator
215+
##################################################
216+
# [ Plugin ] code_generator
217+
##################################################
231218
CODE_GENERATOR_DOWNLOAD_ZIP_FILENAME: str = 'fba_generator'
232219

220+
##################################################
221+
# [ Plugin ] oauth2
222+
##################################################
223+
# .env
224+
OAUTH2_GITHUB_CLIENT_ID: str
225+
OAUTH2_GITHUB_CLIENT_SECRET: str
226+
OAUTH2_LINUX_DO_CLIENT_ID: str
227+
OAUTH2_LINUX_DO_CLIENT_SECRET: str
228+
229+
# 基础配置
230+
OAUTH2_FRONTEND_REDIRECT_URI: str = 'http://localhost:5173/oauth2/callback'
231+
232+
##################################################
233+
# [ Plugin ] email
234+
##################################################
235+
# .env
236+
EMAIL_USERNAME: str
237+
EMAIL_PASSWORD: str
238+
239+
# 基础配置
240+
EMAIL_HOST: str = 'smtp.qq.com'
241+
EMAIL_PORT: int = 465
242+
EMAIL_SSL: bool = True
243+
EMAIL_CAPTCHA_REDIS_PREFIX: str = 'fba:email:captcha'
244+
EMAIL_CAPTCHA_EXPIRE_SECONDS: int = 60 * 3 # 3 分钟
245+
233246
@model_validator(mode='before')
234247
@classmethod
235248
def check_env(cls, values: Any) -> Any:
236249
"""检查环境变量"""
237-
if values.get('ENVIRONMENT') == 'pro':
250+
if values.get('ENVIRONMENT') == 'prod':
238251
# FastAPI
239252
values['FASTAPI_OPENAPI_URL'] = None
240253
values['FASTAPI_STATIC_FILES'] = False
241-
# Task
254+
255+
# task
242256
values['CELERY_BROKER'] = 'rabbitmq'
243257

244258
return values

deploy/backend/docker-compose/.env.server

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,19 @@ REDIS_DATABASE=0
1515
TOKEN_SECRET_KEY='1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk'
1616
# Opera Log
1717
OPERA_LOG_ENCRYPT_SECRET_KEY='d77b25790a804c2b4a339dd0207941e4cefa5751935a33735bc73bb7071a005b'
18-
# Admin
19-
# OAuth2
20-
OAUTH2_GITHUB_CLIENT_ID='test'
21-
OAUTH2_GITHUB_CLIENT_SECRET='test'
22-
OAUTH2_LINUX_DO_CLIENT_ID='test'
23-
OAUTH2_LINUX_DO_CLIENT_SECRET='test'
24-
# Task
18+
# [ App ] task
2519
# Celery
2620
CELERY_BROKER_REDIS_DATABASE=1
2721
# Rabbitmq
2822
CELERY_RABBITMQ_HOST='fba_rabbitmq'
2923
CELERY_RABBITMQ_PORT=5672
3024
CELERY_RABBITMQ_USERNAME='guest'
3125
CELERY_RABBITMQ_PASSWORD='guest'
32-
# SMS
33-
TENCENTCLOUD_SECRET_ID=''
34-
TENCENTCLOUD_SECRET_KEY=''
35-
SMS_LOGIN_TEMPLATE_ID='' # 短信登录模板ID
36-
SMS_SIGN_NAME='' # 短信签名
37-
SMS_SDK_APP_ID='' # 短信应用ID
26+
# [ Plugin ] oauth2
27+
OAUTH2_GITHUB_CLIENT_ID='test'
28+
OAUTH2_GITHUB_CLIENT_SECRET='test'
29+
OAUTH2_LINUX_DO_CLIENT_ID='test'
30+
OAUTH2_LINUX_DO_CLIENT_SECRET='test'
31+
# [ Plugin ] email
32+
EMAIL_USERNAME=''
33+
EMAIL_PASSWORD=''

0 commit comments

Comments
 (0)