diff --git a/database.db b/database.db index 0ec678d..8976140 100644 Binary files a/database.db and b/database.db differ diff --git a/main.py b/main.py index 3b25299..144e466 100644 --- a/main.py +++ b/main.py @@ -9,12 +9,31 @@ from werkzeug.routing import BaseConverter from utils.login import login_quark from utils.tools import get_cnb_weburl from utils.detebase import CloudDriveDatabase +from datetime import datetime, timezone +import logging +from logging.handlers import RotatingFileHandler app = Flask(__name__) #app = Flask(__name__,template_folder='templates') #修改模板目录 app.jinja_env.auto_reload = True +# --- Logging Setup Start --- +if not os.path.exists('logs'): + os.mkdir('logs') + +# Use RotatingFileHandler to prevent log file from growing indefinitely +file_handler = RotatingFileHandler('logs/app.log', maxBytes=10240, backupCount=10) +file_handler.setFormatter(logging.Formatter( + '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' +)) +file_handler.setLevel(logging.INFO) # Set level to INFO to capture info, warning, error + +app.logger.addHandler(file_handler) +app.logger.setLevel(logging.INFO) +app.logger.info('Flask App startup') +# --- Logging Setup End --- + # 数据库配置 DATABASE = 'database.db' @@ -149,17 +168,33 @@ def qrlink(id): if link_info: # 检查是否已过期 - from datetime import datetime expiry_time = link_info.get('expiry_time') if expiry_time: try: - expiry_datetime = datetime.strptime(expiry_time, '%Y-%m-%d %H:%M:%S') - if datetime.now() > expiry_datetime: + # 使用 fromisoformat 解析 ISO 8601 UTC 字符串 + # Python < 3.11 doesn't handle Z directly, remove it. + if expiry_time.endswith('Z'): + expiry_time_str = expiry_time[:-1] + '+00:00' + else: + expiry_time_str = expiry_time # Assume it might already be offset-aware or naive + + expiry_datetime = datetime.fromisoformat(expiry_time_str) + + # Ensure expiry_datetime is offset-aware (UTC) + if expiry_datetime.tzinfo is None: + # If parsing resulted in naive, assume it was UTC as intended + expiry_datetime = expiry_datetime.replace(tzinfo=timezone.utc) + + # 获取当前 UTC 时间进行比较 + if datetime.now(timezone.utc) > expiry_datetime: data["message"] = "此外链已过期" return render_template('exlink_error.html', message=data["message"]) - except (ValueError, TypeError): - # 如果日期格式有误,忽略过期检查 - pass + except (ValueError, TypeError) as e: + # 解析失败,记录错误,并可能视为无效链接 + print(f"Error parsing expiry_time '{expiry_time}': {e}") + data["message"] = "外链信息有误(无效的过期时间)" + return render_template('exlink_error.html', message=data["message"]) + # 检查使用次数是否超过限制 used_quota = link_info.get('used_quota', 0) @@ -456,8 +491,60 @@ def delete_external_link(): return Exlink().delete() -if __name__ == "__main__": +# 新增:仪表盘数据 API +@app.route('/admin/dashboard_data', methods=['GET']) +def get_dashboard_data(): + db = get_db() + try: + user_drives_count = db.get_total_user_drives_count() + active_links_count = db.get_active_external_links_count() + # 使用外链总数作为"今日访问量"的简化替代 + total_links_count = db.get_total_external_links_count() + + data = { + "status": True, + "data": { + "user_drives_count": user_drives_count, + "active_links_count": active_links_count, + "total_links_count": total_links_count + } + } + except Exception as e: + print(f"获取仪表盘数据错误: {e}") + data = {"status": False, "message": "获取仪表盘数据失败"} + return jsonify(data) + +# 新增:统计分析数据 API +@app.route('/admin/statistics_data', methods=['GET']) +def get_statistics_data(): + db = get_db() + try: + drives_by_provider = db.get_user_drives_count_by_provider() + # 这里可以添加更多统计数据,例如外链访问趋势 (需要记录访问日志) + # 暂时只返回网盘分布数据 + + data = { + "status": True, + "data": { + "drives_by_provider": drives_by_provider, + # "access_trend": [] # 示例:将来可以添加访问趋势数据 + } + } + except Exception as e: + print(f"获取统计数据错误: {e}") + data = {"status": False, "message": "获取统计数据失败"} + return jsonify(data) + + +# ----------------------------- +# 应用程序运行入口 +# ----------------------------- +if __name__ == '__main__': + # 初始化数据库 (如果需要) + # init_db() + + # 启动Flask应用 weburl = get_cnb_weburl(5000) print("Run_url:",weburl) app.config.from_pyfile("config.py") diff --git a/templates/admin.html b/templates/admin.html index 1b28297..d4848ce 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -33,6 +33,89 @@ background-color: #0d6efd; color: white; } + + /* Digital Cockpit Styles */ + body { + /* Optional: Set a base background for the whole page */ + /* background-color: #1a1a2e; */ + /* color: #e0e0e0; */ + } + .main-content { + /* background-color: #1f1f38; /* Slightly lighter background for content */ + /* border-radius: 10px; */ + /* margin-top: 10px; */ + } + #dashboard { + /* background: linear-gradient(to bottom right, #2a2a4a, #1f1f38); */ + padding: 30px; + border-radius: 8px; + /* box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); */ + } + #dashboard h2, #statistics h2 { + color: #343a40; /* Keep headings readable */ + border-bottom: 2px solid #0d6efd; + padding-bottom: 10px; + margin-bottom: 30px !important; /* Override default margin */ + } + .kpi-card { + background-color: #ffffff; /* White card on potentially dark background */ + border: none; + border-radius: 10px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out; + margin-bottom: 20px; /* Add spacing between cards */ + color: #333; + overflow: hidden; /* Ensure consistency */ + position: relative; /* For potential pseudo-elements */ + } + .kpi-card:hover { + transform: translateY(-5px); + box-shadow: 0 5px 15px rgba(0,0,0,0.15); + } + .kpi-card .card-body { + padding: 25px; + text-align: center; + } + .kpi-card .card-title { + font-size: 1rem; + font-weight: 500; + color: #6c757d; /* Muted color for title */ + margin-bottom: 15px; + text-transform: uppercase; + letter-spacing: 0.5px; + } + .kpi-card .kpi-value { + font-size: 2.8rem; + font-weight: 700; + color: #0d6efd; /* Highlight color for value */ + line-height: 1.2; + } + .kpi-card .kpi-icon { + font-size: 1.5rem; + margin-bottom: 10px; + color: #0d6efd; + opacity: 0.7; + display: block; /* Center icon */ + } + /* Style for charts container */ + #statistics .card { + background-color: #ffffff; + border: none; + border-radius: 10px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + color: #333; + margin-bottom: 20px; + } + #statistics .card-body { + padding: 25px; + } + #statistics .card-title { + font-size: 1.1rem; + font-weight: 600; + color: #343a40; + margin-bottom: 20px; + text-align: center; + }
@@ -74,26 +157,29 @@0
+0
0
+0
0
+ +0