chore: add .gitignore, clean tracked junk files

This commit is contained in:
2026-05-29 08:33:35 +08:00
parent 2d5c5d4c0d
commit 1ccb116f80
53 changed files with 189 additions and 2445 deletions
+3 -2
View File
@@ -2,7 +2,7 @@
SECRET_KEY=kU/W5y3uFbWuK1x+RTTOPgLp38yM6Z7d
# 数据库连接
DATABASE_URI=mysql+pymysql://4an:GtbJp6Ai5azntnTB@10.10.10.14/4an
SQLALCHEMY_DATABASE_URI=mysql+pymysql://4an:GtbJp6Ai5azntnTB@10.10.10.14/4an
# 量子密信消息Webhook
LZMX_WEBHOOK_URL=http://imtwo.zdxlz.com/im-external/v1/webhook/send?key=ef1dc605827f425e96491ce5725b5274
@@ -41,4 +41,5 @@ CASDOOR_CLIENT_ID=8f323670e073612794ef
CASDOOR_CLIENT_SECRET=bc37d89f9220fe5a46c17125c731d2daf22f1299
CASDOOR_REDIRECT_URI=https://zd.dhdx.fun/callback
CASDOOR_ORGANIZATION_NAME=dahua
CASDOOR_APPLICATION_NAME=4an
CASDOOR_APPLICATION_NAME=4an
PROJECT_NAME=4an
Binary file not shown.
Binary file not shown.
+7
View File
@@ -16,6 +16,7 @@ import logging
from logging.handlers import RotatingFileHandler
import os
from utils.error_handlers import ErrorHandler
from utils.db_utils import retry_database_connection
def create_app():
# 创建应用实例
@@ -49,6 +50,12 @@ def create_app():
# 初始化数据库
db.init_app(app)
# 确保数据库连接成功后再继续启动
app.logger.info("正在连接数据库...")
if not retry_database_connection(db, app):
raise RuntimeError("无法连接到数据库,应用启动失败")
migrate = Migrate(app, db)
# 初始化异常处理器
+22 -2
View File
@@ -1,7 +1,14 @@
import os
from pathlib import Path
from dotenv import load_dotenv
# 加载.env文件 (相对于应用根目录)
# 尽量兼容多种 .env 放置方式
BASE_DIR = Path(__file__).resolve().parent.parent # /home/.../4an
# 1. 优先加载项目根目录 .env
load_dotenv(BASE_DIR / '.env')
# 2. 兼容放在 app 目录下的 .env
load_dotenv(BASE_DIR / 'app' / '.env')
# 3. 再加载一次默认搜索路径(当前工作目录及其父级)
load_dotenv()
@@ -10,7 +17,18 @@ class Config:
环境变量优先级高于默认值,可在系统环境变量中设置这些参数
"""
SECRET_KEY = os.environ.get('SECRET_KEY', os.urandom(24))
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI')
# 优先读取 SQLALCHEMY_DATABASE_URI,兼容旧配置;否则回退到 DATABASE_URI
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI') or os.environ.get('DATABASE_URI')
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_pre_ping': True, # 每次取连接前验证是否可用
'pool_recycle': 1800, # 连接空闲 30 分钟后自动回收重建
'pool_timeout': 10, # 等待连接池超时(秒)
'connect_args': {
'connect_timeout': 5, # 连接 MySQL 超时
'read_timeout': 15, # 读取超时
'write_timeout': 15, # 写入超时
},
}
LZMX_WEBHOOK_URL = os.environ.get('LZMX_WEBHOOK_URL')
SQLALCHEMY_TRACK_MODIFICATIONS = False
UPLOAD_FOLDER = os.environ.get('UPLOAD_FOLDER', 'static/uploads')
@@ -31,6 +49,8 @@ class Config:
CASDOOR_ORGANIZATION_NAME = os.environ.get('CASDOOR_ORGANIZATION_NAME', 'built-in')
CASDOOR_APPLICATION_NAME = os.environ.get('CASDOOR_APPLICATION_NAME', 'app-built-in')
CASDOOR_REDIRECT_URI = os.environ.get('CASDOOR_REDIRECT_URI', 'http://localhost:18061/callback')
# 项目名称(用于与 Casdoor 用户标签匹配权限)
PROJECT_NAME = os.environ.get('PROJECT_NAME')
# 会话 Cookie 配置(跨域 SSO 时建议设置)
SESSION_COOKIE_SAMESITE = os.environ.get('SESSION_COOKIE_SAMESITE', 'Lax')
-1101
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

+8 -4
View File
@@ -26,10 +26,14 @@
<p style="margin-top: 12px; color: #666;">正在跳转至 Casdoor 登录页,如未跳转请点击按钮。</p>
</div>
<script>
// 自动跳转至 Casdoor 登录
window.onload = function() {
window.location.href = "{{ url_for('casdoor_login') }}";
};
// 有错误提示时停留在本页,方便用户查看信息
const hasError = {{ 'true' if get_flashed_messages() else 'false' }};
if (!hasError) {
// 自动跳转至 Casdoor 登录
window.onload = function() {
window.location.href = "{{ url_for('casdoor_login') }}";
};
}
</script>
</div>
</div>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+28
View File
@@ -108,6 +108,34 @@ class CasdoorAuth:
pass # 如果无法记录日志,忽略错误
return None
def get_account_info(self, access_token):
"""
使用访问令牌获取完整账户信息(包含标签等字段)
"""
account_url = f"{self.endpoint}/api/get-account"
headers = {
'Authorization': f'Bearer {access_token}'
}
try:
response = requests.get(account_url, headers=headers)
response.raise_for_status()
result = response.json()
# Casdoor 通常返回 {"status": "ok", "data": {...}}
if isinstance(result, dict):
if result.get('status') == 'ok' and isinstance(result.get('data'), dict):
return result['data']
if 'data' in result and isinstance(result.get('data'), dict):
return result['data']
return result if isinstance(result, dict) else None
except requests.exceptions.RequestException as e:
try:
current_app.logger.error(f"获取 Casdoor 账户信息失败: {str(e)}")
except:
pass
return None
def verify_state(self, state):
"""
验证 state 参数,防止 CSRF 攻击
+39
View File
@@ -3,6 +3,7 @@ import time
from flask import current_app
import logging
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import text
from scripts.models import db
from collections import defaultdict
@@ -121,6 +122,44 @@ def clear_table_cache(table_name):
if key in query_cache.access_times:
del query_cache.access_times[key]
# 数据库连接重试机制
def retry_database_connection(db_instance, app, max_retries=10, retry_delay=5):
"""
尝试连接数据库,在连接失败时进行重试
:param db_instance: SQLAlchemy数据库实例
:param app: Flask应用实例
:param max_retries: 最大重试次数
:param retry_delay: 重试间隔(秒)
:return: 连接成功返回True,失败返回False
"""
import sqlalchemy.exc as sa_exc
for attempt in range(max_retries):
try:
app.logger.info(f"尝试连接数据库... (尝试 {attempt + 1}/{max_retries})")
# 使用with语句确保应用上下文正确
with app.app_context():
# 尝试建立连接 - SQLAlchemy 2.0 兼容方式
with db_instance.engine.connect() as connection:
connection.execute(text("SELECT 1"))
connection.commit() # 确保事务提交
app.logger.info("数据库连接成功!")
return True
except (sa_exc.OperationalError, sa_exc.DatabaseError, Exception) as e:
app.logger.warning(f"数据库连接失败: {str(e)}")
if attempt < max_retries - 1:
app.logger.info(f"等待 {retry_delay} 秒后重试...")
time.sleep(retry_delay)
else:
app.logger.error(f"数据库连接失败,已重试 {max_retries} 次,放弃连接")
return False
return False
# 通用查询函数,包含异常处理和缓存
@db_operation()
@cache_query()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+29 -5
View File
@@ -13,8 +13,8 @@ def init_auth_routes(app):
# 如果已登录,直接跳转到仪表盘
if current_user.is_authenticated:
return redirect(url_for('dashboard'))
# 未登录直接跳转 Casdoor 登录页
return redirect(url_for('casdoor_login'))
# 未登录展示登录页(登录页会自动跳转 Casdoor,如有错误提示则不跳转)
return render_template('login.html')
@app.route('/casdoor/login')
def casdoor_login():
@@ -65,18 +65,44 @@ def init_auth_routes(app):
flash('登录失败:无法获取用户信息')
return redirect(url_for('login'))
# 获取完整账户信息(包含标签)
account_info = casdoor_auth.get_account_info(access_token)
if not account_info:
app.logger.error('获取 Casdoor 账户信息失败')
flash('登录失败:无法获取账户信息')
return redirect(url_for('login'))
# 从 Casdoor 用户信息中提取数据
# Casdoor 返回的用户信息可能包含:name, email, phone, id 等字段
casdoor_id = user_info.get('sub') or user_info.get('id') or user_info.get('name')
phone = user_info.get('phone') or user_info.get('phoneNumber') or casdoor_id
name = user_info.get('name') or user_info.get('displayName') or '未知用户'
# 基于标签的访问控制:用户标签必须等于项目名
project_name = app.config.get('PROJECT_NAME')
if not project_name:
app.logger.error('未配置 PROJECT_NAME 环境变量,拒绝登录')
flash('登录失败:未配置项目名')
return redirect(url_for('login'))
raw_tags = account_info.get('tag') or account_info.get('tags')
if isinstance(raw_tags, str):
user_tags = [t.strip() for t in raw_tags.split(',') if t.strip()]
elif isinstance(raw_tags, list):
user_tags = [str(t).strip() for t in raw_tags if str(t).strip()]
else:
user_tags = []
if project_name not in user_tags:
app.logger.warning(f'用户 {name}({phone}) 标签 {user_tags} 不匹配项目 {project_name}')
flash('您没有本系统的访问权限,请联系管理员处理')
return redirect(url_for('login'))
app.logger.info(f'Casdoor 用户信息: {user_info}')
# 查找或创建本地用户
user = User.query.filter_by(phone=phone).first()
if not user:
# 如果用户不存在,创建新用户
user = User(
phone=phone,
name=name,
@@ -87,7 +113,6 @@ def init_auth_routes(app):
db.session.commit()
app.logger.info(f'创建新用户: {name}({phone})')
else:
# 更新用户信息
user.name = name
if user_info.get('affiliation'):
user.branch = user_info.get('affiliation')
@@ -96,7 +121,6 @@ def init_auth_routes(app):
db.session.commit()
app.logger.info(f'更新用户信息: {name}({phone})')
# 登录用户
login_user(user)
app.logger.info(f'用户 {user.name}({user.phone}) 通过 Casdoor 登录成功')