diff --git a/.env b/.env index 1623f41..f0a5683 100644 --- a/.env +++ b/.env @@ -23,6 +23,9 @@ MAX_UNBIND_TIMES=3 LICENSE_KEY_LENGTH=32 LICENSE_KEY_PREFIX= +FRONTEND_DOMAIN=http://km.taisan.online + + # API配置 API_VERSION=v1 ITEMS_PER_PAGE=20 diff --git a/.env.example b/.env.example index d89bb33..2a66827 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,7 @@ FLASK_DEBUG=True # 安全配置 SECRET_KEY=your-super-secret-key-change-this-in-production AUTH_SECRET_KEY=your-auth-validator-secret-key +FRONTEND_DOMAIN= # 验证器配置 OFFLINE_CACHE_DAYS=7 @@ -30,6 +31,8 @@ ITEMS_PER_PAGE=20 # 服务器配置 HOST=0.0.0.0 PORT=5000 +FASTAPI_HOST=127.0.0.1 +FASTAPI_PORT=9000 # 文件上传配置 MAX_CONTENT_LENGTH=524288000 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index 48afdb3..d027f17 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -11,6 +11,7 @@ 5. [启动服务](#启动服务) 6. [常见问题解决](#常见问题解决) 7. [维护与管理](#维护与管理) +8. [前端域名配置](#前端域名配置-1) --- @@ -184,6 +185,27 @@ pip install PyMySQL==1.1.0 - `localhost`:数据库地址(本地部署就用 localhost) - `kamaxitong`:数据库名称 +### 第二步:配置前端域名(可选但推荐) + +1. **为什么要配置前端域名** + 在本地开发环境中,系统默认通过 `http://localhost:5000` 访问。当部署到服务器并通过域名访问时,如果不配置前端域名,前端页面中的接口调用可能会仍然指向 `localhost:5000`,导致接口调用失败。 + +2. **配置方法** + 在 `.env` 文件中添加或修改以下配置: + ```bash + # 前端域名配置 + FRONTEND_DOMAIN=https://your-domain.com + ``` + + 或者通过管理后台配置: + - 登录系统管理后台 + - 进入"系统设置"页面 + - 在"基本设置"中找到"前端域名"配置项 + - 输入你的域名,例如:`https://your-domain.com` + - 点击"保存设置" + + > 注意:如果两种方式都配置了,环境变量的优先级更高。 + ### 第二步:创建数据库 #### 方法一:使用命令行(推荐) @@ -510,6 +532,39 @@ tail -f logs/kamaxitong.log --- +## 前端域名配置 + +### 问题描述 + +在本地开发环境中,系统默认通过 `http://localhost:5000` 访问。当部署到服务器并通过域名访问时,前端页面中的接口调用可能会仍然指向 `localhost:5000`,导致接口调用失败。 + +### 解决方案 + +系统已通过修改前端JavaScript中的 `apiRequest` 函数来自动检测当前访问的主机并构建正确的API调用地址。现在还支持通过环境变量或管理后台配置前端域名。 + +前端JavaScript现在会使用配置的域名或 `window.location.origin` 来获取当前访问的完整域名,并自动构建完整的API URL。 + +### 配置方法 + +1. **通过环境变量配置(推荐)** + 在 `.env` 文件中添加: + ```bash + FRONTEND_DOMAIN=https://your-domain.com + ``` + +2. **通过管理后台配置** + - 登录系统管理后台 + - 进入"系统设置"页面 + - 在"基本设置"中找到"前端域名"配置项 + - 输入你的域名,例如:`https://your-domain.com` + - 点击"保存设置" + +### 验证配置 + +1. 在浏览器中打开应用,按F12打开开发者工具 +2. 切换到Network标签页 +3. 刷新页面,观察API请求的URL是否为域名地址而非localhost + ## 🎉 部署完成! 恭喜你!如果以上步骤都成功执行,你的 KaMiXiTong 系统已经成功部署了! diff --git a/MYSQL_CONFIG_GUIDE.md b/MYSQL_CONFIG_GUIDE.md index cf26118..44324c0 100644 --- a/MYSQL_CONFIG_GUIDE.md +++ b/MYSQL_CONFIG_GUIDE.md @@ -1,262 +1,96 @@ -# MySQL数据库配置指南 +# MySQL 数据库配置与初始化指南 -## 概述 +## 目录 +1. [环境要求](#环境要求) +2. [安装依赖](#安装依赖) +3. [配置数据库连接](#配置数据库连接) +4. [创建数据库](#创建数据库) +5. [初始化数据库](#初始化数据库) +6. [启动应用](#启动应用) -系统已经配置为使用MySQL数据库,并从`.env`文件读取配置。 +## 环境要求 -## 配置步骤 +- MySQL 5.7 或更高版本 +- Python 3.8 或更高版本 +- PyMySQL 驱动 -### 1. 确保安装MySQL客户端依赖 - -安装PyMySQL(MySQL Python驱动): +## 安装依赖 ```bash pip install PyMySQL -# 或者 -pip install pymysql ``` -### 2. 配置.env文件 +## 配置数据库连接 -编辑项目根目录的`.env`文件: +复制 [.env.example](.env.example) 文件为 .env 并修改数据库配置: + +```bash +cp .env.example .env +``` + +在 .env 文件中修改数据库连接配置: ```env -# 数据库配置 -# 格式: mysql+pymysql://用户名:密码@主机:端口/数据库名 -DATABASE_URL=mysql+pymysql://root:你的密码@localhost/kamaxitong - -# 示例: -DATABASE_URL=mysql+pymysql://root:taiyi1224@localhost/kamaxitong +DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名 ``` -### 3. 创建数据库 +示例: +```env +DATABASE_URL=mysql+pymysql://root:password@localhost:3306/kamaxitong +``` -在MySQL中创建数据库: +## 创建数据库 + +在 MySQL 中手动创建数据库: ```sql --- 登录MySQL -mysql -u root -p - --- 创建数据库 CREATE DATABASE kamaxitong CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - --- 授权(可选) -GRANT ALL PRIVILEGES ON kamaxitong.* TO 'root'@'localhost'; -FLUSH PRIVILEGES; ``` -### 4. 更新.env文件中的数据库URL - -`.env`文件中的`DATABASE_URL`格式: - -```env -# 标准格式 -DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名 - -# 本地示例 -DATABASE_URL=mysql+pymysql://root:taiyi1224@localhost/kamaxitong - -# 远程示例 -DATABASE_URL=mysql+pymysql://root:password@192.168.1.100:3306/kamaxitong -``` - -### 5. 初始化数据库 - -#### 方式1: 使用Flask-Migrate(推荐) +或者使用提供的脚本自动创建: ```bash -# 初始化迁移(如果还没有) -flask db init - -# 生成迁移文件 -flask db migrate -m "Initial migration" - -# 应用迁移 -flask db upgrade +python setup_mysql.py ``` -#### 方式2: 使用快速修复脚本 +## 初始化数据库 + +有两种方式初始化数据库: + +### 方式一:使用专用初始化脚本(推荐) ```bash -python quick_fix.py +python init_db_mysql.py ``` -### 6. 启动应用 +该脚本将: +1. 从 .env 文件读取数据库配置 +2. 清理现有表结构 +3. 创建所有数据表 +4. 插入初始数据(管理员账号、示例产品等) + +### 方式二:使用通用配置脚本 + +```bash +python setup_mysql.py +``` + +## 启动应用 ```bash python run.py ``` -## 数据库配置参数说明 - -| 参数 | 说明 | 示例 | -|------|------|------| -| 数据库类型 | mysql+pymysql | mysql+pymysql | -| 用户名 | MySQL用户名 | root | -| 密码 | MySQL密码 | taiyi1224 | -| 主机 | 数据库主机 | localhost 或 192.168.1.100 | -| 端口 | MySQL端口(可选) | 3306(默认) | -| 数据库名 | 要使用的数据库名 | kamaxitong | - -## 完整的.env文件示例 - -```env -# 环境配置 -FLASK_ENV=development -FLASK_DEBUG=True - -# 数据库配置 - MySQL -DATABASE_URL=mysql+pymysql://root:taiyi1224@localhost/kamaxitong - -# 安全配置 -SECRET_KEY=taiyi1224 -AUTH_SECRET_KEY=taiyi1224 - -# 验证器配置 -OFFLINE_CACHE_DAYS=7 -MAX_FAILED_ATTEMPTS=5 -LOCKOUT_MINUTES=10 -MAX_UNBIND_TIMES=3 - -# 卡密配置 -LICENSE_KEY_LENGTH=32 -LICENSE_KEY_PREFIX= - -# API配置 -API_VERSION=v1 -ITEMS_PER_PAGE=20 - -# 服务器配置 -HOST=0.0.0.0 -PORT=5000 - -# 文件上传配置 -MAX_CONTENT_LENGTH=16777216 -UPLOAD_FOLDER=static/uploads - -# 日志配置 -LOG_LEVEL=INFO -LOG_FILE=logs/kamaxitong.log -``` - -## 验证配置 - -### 1. 检查.env文件是否被加载 - -启动应用时查看控制台输出: - -``` -成功加载.env文件 -``` - -### 2. 验证数据库连接 - -在Python中测试: - -```python -from app import create_app -from app import db - -app = create_app() -with app.app_context(): - print("数据库URI:", app.config['SQLALCHEMY_DATABASE_URI']) - db.create_all() # 测试连接 - print("数据库连接成功!") -``` - -## 常见问题 - -### Q1: ImportError: No module named 'pymysql' - -**解决方案:** -```bash -pip install pymysql -``` - -### Q2: 1049 (42000): Unknown database 'kamaxitong' - -**解决方案:** -```sql -CREATE DATABASE kamaxitong CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -``` - -### Q3: 1045 (28000): Access denied for user 'root'@'localhost' - -**解决方案:** -1. 检查.env文件中的用户名和密码是否正确 -2. 确保MySQL用户有权限访问数据库 -3. 重置MySQL root密码 - -### Q4: 2003 (HY000): Can't connect to MySQL server - -**解决方案:** -1. 确保MySQL服务已启动 -2. 检查主机和端口是否正确 -3. 检查防火墙设置 - -### Q5: .env文件没有被加载 - -**解决方案:** -1. 确保python-dotenv已安装:`pip install python-dotenv` -2. 确保.env文件在项目根目录 -3. 重启应用 - -## 性能优化建议 - -### 1. 配置MySQL连接池 - -在config.py中已经配置: - -```python -SQLALCHEMY_ENGINE_OPTIONS = { - "future": True, - "pool_pre_ping": True, - "pool_size": 10, - "pool_recycle": 3600, - "max_overflow": 20 -} -``` - -### 2. 启用MySQL查询缓存 - -在MySQL配置文件中(my.cnf): - -```ini -[mysqld] -query_cache_type = 1 -query_cache_size = 256M -``` - -### 3. 设置合适的字符集 - -确保数据库、表、列都使用utf8mb4字符集: - -```sql -CREATE DATABASE kamaxitong -CHARACTER SET utf8mb4 -COLLATE utf8mb4_unicode_ci; -``` - -## 备份和恢复 - -### 备份数据库 +或 ```bash -mysqldump -u root -p kamaxitong > backup.sql +python start.py ``` -### 恢复数据库 +访问地址:http://localhost:5000 -```bash -mysql -u root -p kamaxitong < backup.sql -``` +默认管理员账号: +- 超级管理员: admin / admin123 +- 普通管理员: test_admin / test123 -## 总结 - -系统已经配置为使用MySQL和.env文件: -- ✅ .env文件支持 -- ✅ MySQL配置已就绪 -- ✅ 从环境变量读取配置 -- ✅ 支持开发/生产/测试环境 - -只需确保MySQL服务运行、依赖安装正确、.env文件配置正确即可! +⚠️ 请在生产环境中修改默认密码! \ No newline at end of file diff --git a/PROJECT_SUMMARY.md b/PROJECT_SUMMARY.md index 8c79ff9..5cc170e 100644 --- a/PROJECT_SUMMARY.md +++ b/PROJECT_SUMMARY.md @@ -183,13 +183,20 @@ python start.py ### 生产环境 ```bash -# 使用Gunicorn +# 使用一键部署脚本(推荐) +python deploy.py --setup-service --setup-nginx + +# 或者手动部署 pip install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 run:app # 使用Nginx反向代理 # 配置SSL证书 # 设置防火墙规则 + +# 前端域名配置 +# 在.env文件中设置FRONTEND_DOMAIN=https://your-domain.com +# 或通过管理后台配置 ``` ### Docker部署 diff --git a/README.md b/README.md index 2e8d1d1..eda2436 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ KaMiXiTong/ ## 使用方式 -### 快速开始 +### 快速开始 (SQLite) ```bash # 克隆项目 @@ -50,7 +50,7 @@ cd kamaxitong # 安装依赖 pip install -r requirements.txt -# 初始化数据库 +# 初始化SQLite数据库 python init_db_sqlite.py # 启动服务 @@ -60,6 +60,36 @@ python run.py 访问地址: http://localhost:5000 默认账号: admin / admin123 +### MySQL数据库支持 + +系统也支持使用MySQL数据库,提供更好的性能和并发支持。 + +#### 初始化MySQL数据库 + +```bash +# 安装MySQL依赖 +pip install PyMySQL + +# 配置数据库连接 +# 复制 .env.example 为 .env 并修改 DATABASE_URL 配置 +cp .env.example .env + +# 编辑 .env 文件,设置MySQL连接信息 +# DATABASE_URL=mysql+pymysql://用户名:密码@主机:端口/数据库名 + +# 初始化MySQL数据库 +python init_db_mysql.py +``` + +或者使用配置脚本: + +```bash +# 使用配置脚本自动创建数据库和表 +python setup_mysql.py +``` + +详细配置说明请参考 [MySQL配置指南](MYSQL_CONFIG_GUIDE.md) + ### FastAPI接口 系统还提供了现代化的FastAPI接口,具有自动生成的交互式文档: @@ -85,7 +115,10 @@ python start.py ### 生产环境 ```bash -# 使用Gunicorn +# 使用一键部署脚本(推荐) +python deploy.py --setup-service --setup-nginx + +# 或者手动部署 pip install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 run:app @@ -107,6 +140,8 @@ gunicorn -w 4 -b 0.0.0.0:5000 run:app - [集成指南](docs/INTEGRATION.md) - [使用示例](docs/EXAMPLES.md) - [FastAPI接口文档](docs/FASTAPI.md) +- [部署说明](docs/DEPLOYMENT.md) +- [MySQL配置指南](MYSQL_CONFIG_GUIDE.md) ## 许可证 diff --git a/app/api/settings.py b/app/api/settings.py index d5dac74..a302641 100644 --- a/app/api/settings.py +++ b/app/api/settings.py @@ -8,6 +8,7 @@ CONFIG_MAPPING = { # 基本设置 'site_name': 'SITE_NAME', 'admin_email': 'ADMIN_EMAIL', + 'frontend_domain': 'FRONTEND_DOMAIN', 'max_failed_attempts': 'MAX_FAILED_ATTEMPTS', 'lockout_minutes': 'LOCKOUT_MINUTES', 'max_unbind_times': 'MAX_UNBIND_TIMES', diff --git a/app/utils/auth_validator.py b/app/utils/auth_validator.py index a4fa0b5..c16952c 100644 --- a/app/utils/auth_validator.py +++ b/app/utils/auth_validator.py @@ -228,34 +228,20 @@ class AuthValidator: return "" def _validate_license_format(self, license_key: str) -> bool: - """验证卡密格式(与服务端保持一致)""" + """验证卡密格式""" if not license_key: return False - # 去除空格和制表符,并转为大写 license_key = license_key.strip().replace(' ', '').replace('\t', '').upper() - # 检查是否为XXXX-XXXX-XXXX-XXXX格式 - if '-' in license_key: - parts = license_key.split('-') - # 应该有4部分,每部分8个字符 - if len(parts) == 4 and all(len(part) == 8 for part in parts): - # 检查所有字符是否为大写字母或数字 - combined = ''.join(parts) - if len(combined) == 32: - pattern = r'^[A-Z0-9]+$' - import re - return bool(re.match(pattern, combined)) + # 检查长度(16-32位) + if len(license_key) < 16 or len(license_key) > 32: return False - else: - # 兼容旧格式:检查长度(16-32位) - if len(license_key) < 16 or len(license_key) > 32: - return False - # 检查字符(只允许大写字母、数字和下划线) - pattern = r'^[A-Z0-9_]+$' - import re - return bool(re.match(pattern, license_key)) + # 检查字符(只允许大写字母、数字和下划线) + import re + pattern = r'^[A-Z0-9_]+$' + return bool(re.match(pattern, license_key)) def _input_license_key(self) -> str: """输入卡密""" @@ -407,55 +393,117 @@ class AuthValidator: max_attempts = 3 # 最多尝试3次 for attempt in range(max_attempts): - # 获取卡密 + # 输入卡密 license_key = self._input_license_key() if not license_key: - self._show_message("验证取消", "用户取消了验证操作") + self._show_message("验证取消", "未输入卡密,程序退出", True) return False # 验证卡密格式 if not self._validate_license_format(license_key): - self._show_message("格式错误", "卡密格式不正确,请重新输入", True) + self._show_message("格式错误", "卡密格式错误,请检查后重新输入", True) continue # 在线验证 success, message, auth_info = self._online_verify(license_key) - - if success: - # 缓存授权信息 - if auth_info: - self._cache_auth_info(auth_info) - self._show_message("验证成功", message) + + if success and auth_info: + # 验证成功,缓存授权信息 + self._cache_auth_info(auth_info) + + # 检查是否需要更新 + force_update = auth_info.get('force_update', False) + download_url = auth_info.get('download_url') + new_version = auth_info.get('new_version') + + if force_update and download_url: + self._show_message("需要更新", f"发现新版本 {new_version}\n请下载更新后重新启动程序", True) + # 尝试打开下载链接 + try: + import webbrowser + webbrowser.open(download_url) + except: + pass + return False + + self._show_message("验证成功", f"授权验证成功!\n卡密: {license_key}\n有效期至: {auth_info.get('expire_time', '永久')}") return True + else: - # 记录失败尝试 + # 验证失败 self.failed_attempts += 1 - if self.failed_attempts >= 3: - self._lock_account(10) # 锁定10分钟 + self.last_attempt_time = datetime.utcnow() + + if self.failed_attempts >= 5: # 失败5次锁定 + self._lock_account() self._show_message("验证失败", self._get_lock_message(), True) return False - else: - remaining_attempts = 3 - self.failed_attempts - self._show_message( - "验证失败", - f"{message}\n\n剩余尝试次数: {remaining_attempts}", - True - ) - continue - # 所有尝试都失败 - self._show_message("验证失败", "已达到最大尝试次数", True) + self._show_message("验证失败", f"验证失败: {message}\n剩余尝试次数: {5 - self.failed_attempts}", True) + + # 如果不是最后一次尝试,询问是否继续 + if attempt < max_attempts - 1: + if self.gui_mode: + try: + import tkinter as tk + from tkinter import messagebox + + root = tk.Tk() + root.withdraw() + result = messagebox.askyesno("继续验证", "是否继续输入卡密验证?") + root.destroy() + + if not result: + return False + except ImportError: + continue + else: + continue + else: + return False + return False - def get_auth_info(self) -> Optional[Dict[str, Any]]: - """ - 获取当前授权信息 - - Returns: - Optional[Dict[str, Any]]: 授权信息 - """ - return self.cache.get_auth_info(self.software_id) + def get_software_info(self) -> Optional[Dict[str, Any]]: + """获取软件信息""" + try: + url = f"{self.api_url}/software/info" + params = {"software_id": self.software_id} - def clear_auth_cache(self): - """清除授权缓存""" - self.cache.clear_cache(self.software_id) \ No newline at end of file + response = requests.get(url, params=params, timeout=self.timeout) + if response.status_code == 200: + result = response.json() + if result.get('success'): + return result.get('data') + except Exception: + pass + return None + + def clear_cache(self): + """清除本地缓存""" + self.cache.clear_cache(self.software_id) + # 删除机器码缓存文件 + try: + if os.path.exists(".machine_code"): + os.remove(".machine_code") + except Exception: + pass + +# 便捷函数 +def validate_license(software_id: str, **kwargs) -> bool: + """ + 便捷的验证函数 + + Args: + software_id: 软件ID + **kwargs: 其他参数(api_url, secret_key, cache_days, timeout, gui_mode) + + Returns: + bool: 验证是否成功 + """ + validator = AuthValidator(software_id, **kwargs) + return validator.validate() + +def get_machine_code() -> str: + """获取当前机器码""" + return MachineCodeGenerator.generate() \ No newline at end of file diff --git a/app/web/__init__.py b/app/web/__init__.py index c2afb11..e5483a0 100644 --- a/app/web/__init__.py +++ b/app/web/__init__.py @@ -19,48 +19,55 @@ def login(): username = request.form.get('username', '').strip() password = request.form.get('password', '') - - if not username or not password: - print("DEBUG: Missing username or password") + # 对于AJAX请求,返回JSON错误 + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'success': False, + 'message': '请输入用户名和密码' + }), 400 + # 对于普通表单提交,使用flash消息 flash('请输入用户名和密码', 'error') return render_template('login.html') # 查找用户 admin = Admin.query.filter_by(username=username).first() - if admin: - if admin and admin.check_password(password) and admin.is_active: - # 登录成功 - login_user(admin, remember=True) + if admin and admin.check_password(password) and admin.is_active: + # 登录成功 + login_user(admin, remember=True) - # 更新最后登录信息 - admin.update_last_login(request.remote_addr) + # 更新最后登录信息 + admin.update_last_login(request.remote_addr) - # 生成简单的token(实际项目中应使用JWT) - import secrets - token = secrets.token_urlsafe(32) + # 生成简单的token(实际项目中应使用JWT) + import secrets + token = secrets.token_urlsafe(32) - # 如果是AJAX请求,返回JSON - if request.headers.get('X-Requested-With') == 'XMLHttpRequest': - return jsonify({ - 'success': True, - 'token': token, - 'user': { - 'username': admin.username, - 'role': admin.role, - 'is_super_admin': admin.is_super_admin() - }, - 'redirect': url_for('web.dashboard') - }) + # 如果是AJAX请求,返回JSON + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'success': True, + 'token': token, + 'user': { + 'username': admin.username, + 'role': admin.role, + 'is_super_admin': admin.is_super_admin() + }, + 'redirect': url_for('web.dashboard') + }) - # 获取next参数 - next_page = request.args.get('next') - if next_page: - return redirect(next_page) - return redirect(url_for('web.dashboard')) - else: - flash('用户名或密码错误', 'error') + # 获取next参数 + next_page = request.args.get('next') + if next_page: + return redirect(next_page) + return redirect(url_for('web.dashboard')) else: + # 登录失败 + if request.headers.get('X-Requested-With') == 'XMLHttpRequest': + return jsonify({ + 'success': False, + 'message': '用户名或密码错误' + }), 401 flash('用户名或密码错误', 'error') return render_template('login.html') @@ -86,6 +93,4 @@ def favicon(): return Response(status=204) # No Content # 导入视图函数 -from . import views - - +from . import views \ No newline at end of file diff --git a/app/web/templates/base.html b/app/web/templates/base.html index d58b63a..9b94be8 100644 --- a/app/web/templates/base.html +++ b/app/web/templates/base.html @@ -53,6 +53,14 @@ left: 50%; transform: translate(-50%, -50%); } + /* 消息通知样式 */ + .notification-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 10000; + width: 300px; + } {% block extra_css %}{% endblock %} @@ -66,6 +74,9 @@ + + +
{% if current_user.is_authenticated %} @@ -201,6 +212,9 @@ -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/app/web/templates/settings/index.html b/app/web/templates/settings/index.html index f254072..134b5a8 100644 --- a/app/web/templates/settings/index.html +++ b/app/web/templates/settings/index.html @@ -23,6 +23,13 @@ +
+ + +
前端API请求使用的域名,留空则使用当前访问域名
+
+
- `; - - // 插入到页面中 - const container = document.querySelector('main') || document.body; - container.insertBefore(alertDiv, container.firstChild); - - // 5秒后自动隐藏 - setTimeout(() => { - if (alertDiv.parentNode) { - alertDiv.remove(); - } - }, 5000); -} - // 格式化日期 function formatDate(dateString) { if (!dateString) return '-'; @@ -56,16 +39,18 @@ function formatFileSize(bytes) { // API请求函数 - 添加认证支持 function apiRequest(url, options = {}) { - // 确保URL是完整的路径 + // 自动构建完整的API URL let fullUrl = url; - if (!url.startsWith('/')) { - fullUrl = '/' + url; - } - if (!url.startsWith('/api/')) { - fullUrl = '/api/v1' + fullUrl; - } - if (!url.startsWith('/')) { - fullUrl = '/' + fullUrl; + + // 检查是否配置了前端域名 + const frontendDomain = window.FRONTEND_DOMAIN || ''; + + if (url.startsWith('/')) { + // 如果是相对路径,则使用配置的域名或当前主机 + fullUrl = (frontendDomain || window.location.origin) + url; + } else if (!url.startsWith('http')) { + // 如果不是完整URL且不以http开头,则添加API前缀 + fullUrl = (frontendDomain || window.location.origin) + '/api/v1/' + url; } // 设置默认选项 diff --git a/tests/test_validator.py b/tests/test_validator.py index 153314f..95f5130 100644 --- a/tests/test_validator.py +++ b/tests/test_validator.py @@ -74,7 +74,7 @@ class TestAuthValidator(unittest.TestCase): self.software_id = "TEST_SOFTWARE_ID" self.validator = AuthValidator( software_id=self.software_id, - api_url="http://localhost:5000/api/v1", + api_url="http://km.taisan.online/api/v1", timeout=1 # 快速超时 )