Kamixitong/app/web/templates/settings/index.html

1276 lines
55 KiB
HTML
Raw Normal View History

2025-11-11 21:39:12 +08:00
{% extends "base.html" %}
{% block title %}系统设置 - 软件授权管理系统{% endblock %}
{% block page_title %}系统设置{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8">
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">基本设置</h6>
</div>
<div class="card-body">
<form id="basic-settings-form">
<div class="mb-3">
<label for="site_name" class="form-label">系统名称</label>
<input type="text" class="form-control" id="site_name" name="site_name" value="{{ config.SITE_NAME or '软件授权管理系统' }}">
</div>
<div class="mb-3">
<label for="admin_email" class="form-label">管理员邮箱</label>
<input type="email" class="form-control" id="admin_email" name="admin_email" value="{{ config.ADMIN_EMAIL or '' }}">
</div>
2025-11-16 19:06:49 +08:00
<div class="mb-3">
<label for="frontend_domain" class="form-label">前端域名</label>
<input type="text" class="form-control" id="frontend_domain" name="frontend_domain"
value="{{ config.FRONTEND_DOMAIN or '' }}">
<div class="form-text">前端API请求使用的域名留空则使用当前访问域名</div>
</div>
2025-11-11 21:39:12 +08:00
<div class="mb-3">
<label for="max_failed_attempts" class="form-label">最大失败尝试次数</label>
<input type="number" class="form-control" id="max_failed_attempts" name="max_failed_attempts"
value="{{ config.MAX_FAILED_ATTEMPTS or 5 }}" min="1" max="20">
</div>
<div class="mb-3">
<label for="lockout_minutes" class="form-label">锁定时间(分钟)</label>
<input type="number" class="form-control" id="lockout_minutes" name="lockout_minutes"
value="{{ config.LOCKOUT_MINUTES or 10 }}" min="1" max="1440">
</div>
<div class="mb-3">
<label for="max_unbind_times" class="form-label">最大解绑次数</label>
<input type="number" class="form-control" id="max_unbind_times" name="max_unbind_times"
value="{{ config.MAX_UNBIND_TIMES or 3 }}" min="0" max="100">
</div>
<div class="mb-3">
<label for="auth_secret_key" class="form-label">授权验证密钥</label>
<input type="text" class="form-control" id="auth_secret_key" name="auth_secret_key"
value="{{ config.AUTH_SECRET_KEY or 'auth-validator-secret-key' }}">
<div class="form-text">用于授权验证的密钥,请妥善保管</div>
</div>
<button type="submit" class="btn btn-primary" id="save-basic-btn">
<i class="fas fa-save me-2"></i>
<span id="save-basic-text">保存设置</span>
</button>
</form>
</div>
</div>
2025-11-22 16:48:45 +08:00
<!-- 新增的安全配置 -->
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">安全配置</h6>
</div>
<div class="card-body">
<form id="security-settings-form">
<div class="mb-3">
<label for="secret_key" class="form-label">应用密钥</label>
<div class="input-group">
<input type="password" class="form-control" id="secret_key" name="secret_key"
placeholder="••••••••">
<button class="btn btn-outline-secondary" type="button" id="generate-secret-key">
生成新密钥
</button>
</div>
<div class="form-text">Flask应用密钥用于会话和CSRF保护</div>
</div>
<div class="mb-3">
<label for="session_cookie_secure" class="form-label">会话Cookie安全</label>
<select class="form-select" id="session_cookie_secure" name="session_cookie_secure">
<option value="True" {{ 'selected' if config.SESSION_COOKIE_SECURE else '' }}>启用</option>
<option value="False" {{ 'selected' if not config.SESSION_COOKIE_SECURE else '' }}>禁用</option>
</select>
<div class="form-text">在HTTPS环境下启用以增强安全性</div>
</div>
<div class="mb-3">
<label for="session_cookie_httponly" class="form-label">会话Cookie HttpOnly</label>
<select class="form-select" id="session_cookie_httponly" name="session_cookie_httponly">
<option value="True" {{ 'selected' if config.SESSION_COOKIE_HTTPONLY else '' }}>启用</option>
<option value="False" {{ 'selected' if not config.SESSION_COOKIE_HTTPONLY else '' }}>禁用</option>
</select>
<div class="form-text">防止客户端脚本访问Cookie</div>
</div>
<div class="mb-3">
<label for="session_cookie_samesite" class="form-label">会话Cookie SameSite</label>
<select class="form-select" id="session_cookie_samesite" name="session_cookie_samesite">
<option value="Lax" {{ 'selected' if config.SESSION_COOKIE_SAMESITE == 'Lax' else '' }}>Lax</option>
<option value="Strict" {{ 'selected' if config.SESSION_COOKIE_SAMESITE == 'Strict' else '' }}>Strict</option>
<option value="None" {{ 'selected' if config.SESSION_COOKIE_SAMESITE == 'None' else '' }}>None</option>
</select>
<div class="form-text">控制Cookie在跨站请求中的发送</div>
</div>
<button type="submit" class="btn btn-primary" id="save-security-btn">
<i class="fas fa-save me-2"></i>
<span id="save-security-text">保存设置</span>
</button>
</form>
</div>
</div>
2025-11-11 21:39:12 +08:00
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">卡密设置</h6>
</div>
<div class="card-body">
<form id="license-settings-form">
<div class="mb-3">
<label for="license_key_length" class="form-label">卡密长度</label>
<input type="number" class="form-control" id="license_key_length" name="license_key_length"
value="{{ config.LICENSE_KEY_LENGTH or 32 }}" min="16" max="128">
</div>
<div class="mb-3">
<label for="license_key_prefix" class="form-label">卡密前缀</label>
<input type="text" class="form-control" id="license_key_prefix" name="license_key_prefix"
value="{{ config.LICENSE_KEY_PREFIX or '' }}" maxlength="10">
</div>
<div class="mb-3">
<label for="trial_prefix" class="form-label">试用卡密前缀</label>
<input type="text" class="form-control" id="trial_prefix" name="trial_prefix"
value="{{ config.TRIAL_PREFIX or 'TRIAL_' }}" maxlength="10">
</div>
<div class="mb-3">
<label for="offline_cache_days" class="form-label">离线缓存天数</label>
<input type="number" class="form-control" id="offline_cache_days" name="offline_cache_days"
value="{{ config.OFFLINE_CACHE_DAYS or 7 }}" min="1" max="365">
</div>
<button type="submit" class="btn btn-primary" id="save-license-btn">
<i class="fas fa-save me-2"></i>
<span id="save-license-text">保存设置</span>
</button>
</form>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">API设置</h6>
</div>
<div class="card-body">
<form id="api-settings-form">
<div class="mb-3">
<label for="api_version" class="form-label">API版本</label>
<input type="text" class="form-control" id="api_version" name="api_version"
value="{{ config.API_VERSION or 'v1' }}">
</div>
<div class="mb-3">
<label for="items_per_page" class="form-label">每页显示条目数</label>
<input type="number" class="form-control" id="items_per_page" name="items_per_page"
value="{{ config.ITEMS_PER_PAGE or 20 }}" min="5" max="100">
</div>
<button type="submit" class="btn btn-primary" id="save-api-btn">
<i class="fas fa-save me-2"></i>
<span id="save-api-text">保存设置</span>
</button>
</form>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">文件上传设置</h6>
</div>
<div class="card-body">
<form id="upload-settings-form">
<div class="mb-3">
<label for="max_content_length" class="form-label">最大上传文件大小 (MB)</label>
<input type="number" class="form-control" id="max_content_length" name="max_content_length"
value="{{ (config.MAX_CONTENT_LENGTH / 1024 / 1024) | int or 500 }}" min="1" max="1000">
<div class="form-text">当前设置: {{ config.MAX_CONTENT_LENGTH / 1024 / 1024 }} MB</div>
</div>
<div class="mb-3">
<label for="upload_folder" class="form-label">上传文件夹</label>
<input type="text" class="form-control" id="upload_folder" name="upload_folder"
value="{{ config.UPLOAD_FOLDER or 'static/uploads' }}">
</div>
<button type="submit" class="btn btn-primary" id="save-upload-btn">
<i class="fas fa-save me-2"></i>
<span id="save-upload-text">保存设置</span>
</button>
</form>
</div>
</div>
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">会话设置</h6>
</div>
<div class="card-body">
<form id="session-settings-form">
<div class="mb-3">
<label for="session_lifetime_hours" class="form-label">会话有效期(小时)</label>
<input type="number" class="form-control" id="session_lifetime_hours" name="session_lifetime_hours"
value="{{ (config.PERMANENT_SESSION_LIFETIME.seconds / 3600) | int or 24 }}" min="1" max="720">
<div class="form-text">当前设置: {{ config.PERMANENT_SESSION_LIFETIME.days * 24 + config.PERMANENT_SESSION_LIFETIME.seconds / 3600 }} 小时</div>
</div>
<button type="submit" class="btn btn-primary" id="save-session-btn">
<i class="fas fa-save me-2"></i>
<span id="save-session-text">保存设置</span>
</button>
</form>
</div>
</div>
2025-11-22 16:48:45 +08:00
<!-- 新增的记住我设置 -->
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">记住我设置</h6>
</div>
<div class="card-body">
<form id="remember-settings-form">
<div class="mb-3">
<label for="remember_cookie_duration" class="form-label">记住我持续时间(天)</label>
<input type="number" class="form-control" id="remember_cookie_duration" name="remember_cookie_duration"
value="{{ (config.REMEMBER_COOKIE_DURATION.days) | int or 30 }}" min="1" max="365">
<div class="form-text">记住我功能的持续时间</div>
</div>
<div class="mb-3">
<label for="remember_cookie_secure" class="form-label">记住我Cookie安全</label>
<select class="form-select" id="remember_cookie_secure" name="remember_cookie_secure">
<option value="True" {{ 'selected' if config.REMEMBER_COOKIE_SECURE else '' }}>启用</option>
<option value="False" {{ 'selected' if not config.REMEMBER_COOKIE_SECURE else '' }}>禁用</option>
</select>
<div class="form-text">在HTTPS环境下启用以增强安全性</div>
</div>
<div class="mb-3">
<label for="remember_cookie_httponly" class="form-label">记住我Cookie HttpOnly</label>
<select class="form-select" id="remember_cookie_httponly" name="remember_cookie_httponly">
<option value="True" {{ 'selected' if config.REMEMBER_COOKIE_HTTPONLY else '' }}>启用</option>
<option value="False" {{ 'selected' if not config.REMEMBER_COOKIE_HTTPONLY else '' }}>禁用</option>
</select>
<div class="form-text">防止客户端脚本访问Cookie</div>
</div>
<div class="mb-3">
<label for="remember_cookie_samesite" class="form-label">记住我Cookie SameSite</label>
<select class="form-select" id="remember_cookie_samesite" name="remember_cookie_samesite">
<option value="Lax" {{ 'selected' if config.REMEMBER_COOKIE_SAMESITE == 'Lax' else '' }}>Lax</option>
<option value="Strict" {{ 'selected' if config.REMEMBER_COOKIE_SAMESITE == 'Strict' else '' }}>Strict</option>
<option value="None" {{ 'selected' if config.REMEMBER_COOKIE_SAMESITE == 'None' else '' }}>None</option>
</select>
<div class="form-text">控制Cookie在跨站请求中的发送</div>
</div>
<button type="submit" class="btn btn-primary" id="save-remember-btn">
<i class="fas fa-save me-2"></i>
<span id="save-remember-text">保存设置</span>
</button>
</form>
</div>
</div>
2025-12-12 11:35:14 +08:00
<!-- 日志配置 -->
2025-11-22 16:48:45 +08:00
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">日志配置</h6>
</div>
<div class="card-body">
<form id="log-settings-form">
<div class="mb-3">
<label for="log_level" class="form-label">日志级别</label>
<select class="form-select" id="log_level" name="log_level">
<option value="DEBUG" {{ 'selected' if config.LOG_LEVEL == 'DEBUG' else '' }}>DEBUG</option>
<option value="INFO" {{ 'selected' if config.LOG_LEVEL == 'INFO' else '' }}>INFO</option>
<option value="WARNING" {{ 'selected' if config.LOG_LEVEL == 'WARNING' else '' }}>WARNING</option>
<option value="ERROR" {{ 'selected' if config.LOG_LEVEL == 'ERROR' else '' }}>ERROR</option>
<option value="CRITICAL" {{ 'selected' if config.LOG_LEVEL == 'CRITICAL' else '' }}>CRITICAL</option>
</select>
<div class="form-text">设置日志记录的详细程度</div>
</div>
2025-12-12 11:35:14 +08:00
2025-11-22 16:48:45 +08:00
<button type="submit" class="btn btn-primary" id="save-log-btn">
<i class="fas fa-save me-2"></i>
<span id="save-log-text">保存设置</span>
</button>
</form>
</div>
</div>
2025-12-12 11:35:14 +08:00
<!-- 支付配置 -->
<div class="card shadow mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0">支付配置</h6>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="payment_enabled" name="payment_enabled"
{{ 'checked' if config.PAYMENT_ENABLED else '' }}>
<label class="form-check-label" for="payment_enabled">
启用支付功能
</label>
</div>
</div>
<div class="card-body">
<form id="payment-settings-form">
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
请正确配置支付宝参数以启用支付功能
</div>
<div class="mb-3">
<label for="alipay_app_id" class="form-label">支付宝应用ID <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="alipay_app_id" name="alipay_app_id"
value="{{ config.ALIPAY_APP_ID or '' }}" placeholder="输入支付宝应用APP_ID">
<div class="form-text">从支付宝开放平台获取的应用ID</div>
</div>
<div class="mb-3">
<label for="alipay_private_key" class="form-label">应用私钥 <span class="text-danger">*</span></label>
<textarea class="form-control" id="alipay_private_key" name="alipay_private_key"
rows="4" placeholder="-----BEGIN PRIVATE KEY-----">{{ config.ALIPAY_PRIVATE_KEY or '' }}</textarea>
<div class="form-text">您的应用私钥RSA2格式</div>
</div>
<div class="mb-3">
<label for="alipay_public_key" class="form-label">支付宝公钥 <span class="text-danger">*</span></label>
<textarea class="form-control" id="alipay_public_key" name="alipay_public_key"
rows="4" placeholder="-----BEGIN PUBLIC KEY-----">{{ config.ALIPAY_PUBLIC_KEY or '' }}</textarea>
<div class="form-text">您的应用公钥RSA2格式</div>
</div>
<div class="mb-3">
<label for="alipay_alipay_public_key" class="form-label">支付宝平台公钥 <span class="text-danger">*</span></label>
<textarea class="form-control" id="alipay_alipay_public_key" name="alipay_alipay_public_key"
rows="4" placeholder="-----BEGIN PUBLIC KEY-----">{{ config.ALIPAY_ALIPAY_PUBLIC_KEY or '' }}</textarea>
<div class="form-text">支付宝平台的公钥RSA2格式</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="alipay_notify_url" class="form-label">异步通知URL</label>
<input type="url" class="form-control" id="alipay_notify_url" name="alipay_notify_url"
value="{{ config.ALIPAY_NOTIFY_URL or '' }}" placeholder="https://your-domain.com/api/v1/pay/alipay/notify">
<div class="form-text">支付宝异步通知地址</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="alipay_return_url" class="form-label">同步返回URL</label>
<input type="url" class="form-control" id="alipay_return_url" name="alipay_return_url"
value="{{ config.ALIPAY_RETURN_URL or '' }}" placeholder="https://your-domain.com/payment/result">
<div class="form-text">支付完成后的返回地址</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="alipay_gateway" class="form-label">支付宝网关地址</label>
<input type="url" class="form-control" id="alipay_gateway" name="alipay_gateway"
value="{{ config.ALIPAY_GATEWAY or 'https://openapi.alipay.com/gateway.do' }}">
<div class="form-text">默认使用正式环境网关</div>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="alipay_timeout_express" class="form-label">支付超时时间(分钟)</label>
<input type="number" class="form-control" id="alipay_timeout_express" name="alipay_timeout_express"
value="{{ config.ALIPAY_TIMEOUT_EXPRESS or 30 }}" min="5" max="120">
<div class="form-text">订单支付超时时间</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="alipay_sign_type" class="form-label">签名算法</label>
<select class="form-select" id="alipay_sign_type" name="alipay_sign_type">
<option value="RSA2" {{ 'selected' if config.ALIPAY_SIGN_TYPE == 'RSA2' else '' }}>RSA2</option>
<option value="RSA" {{ 'selected' if config.ALIPAY_SIGN_TYPE == 'RSA' else '' }}>RSA</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="alipay_charset" class="form-label">编码格式</label>
<select class="form-select" id="alipay_charset" name="alipay_charset">
<option value="utf-8" {{ 'selected' if config.ALIPAY_CHARSET == 'utf-8' else '' }}>utf-8</option>
<option value="gbk" {{ 'selected' if config.ALIPAY_CHARSET == 'gbk' else '' }}>gbk</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="alipay_version" class="form-label">API版本</label>
<select class="form-select" id="alipay_version" name="alipay_version">
<option value="1.0" {{ 'selected' if config.ALIPAY_VERSION == '1.0' else '' }}>1.0</option>
</select>
</div>
</div>
</div>
<div class="mb-3">
<button type="button" class="btn btn-info" id="test-alipay-config-btn">
<i class="fas fa-check-circle me-2"></i>
测试支付宝配置
</button>
<button type="submit" class="btn btn-primary" id="save-payment-btn">
<i class="fas fa-save me-2"></i>
<span id="save-payment-text">保存设置</span>
</button>
</div>
</form>
</div>
</div>
2025-11-11 21:39:12 +08:00
</div>
<div class="col-lg-4">
<div class="card shadow mb-4">
<div class="card-header">
<h6 class="mb-0">系统信息</h6>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-6">系统版本:</dt>
<dd class="col-sm-6">{{ system_version or 'v1.0.0' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">Python版本:</dt>
<dd class="col-sm-6">{{ python_version or 'Unknown' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">Flask版本:</dt>
<dd class="col-sm-6">{{ flask_version or 'Unknown' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">数据库:</dt>
<dd class="col-sm-6">{{ database_type or 'Unknown' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">运行时间:</dt>
<dd class="col-sm-6">{{ uptime or 'Unknown' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">API版本:</dt>
<dd class="col-sm-6">{{ config.API_VERSION or 'v1' }}</dd>
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
<dt class="col-sm-6">上传限制:</dt>
<dd class="col-sm-6">{{ config.MAX_CONTENT_LENGTH / 1024 / 1024 }} MB</dd>
2025-12-12 11:35:14 +08:00
<dt class="col-sm-6">支付功能:</dt>
<dd class="col-sm-6">
{% if config.PAYMENT_ENABLED %}
<span class="badge bg-success">已启用</span>
{% else %}
<span class="badge bg-secondary">未启用</span>
{% endif %}
</dd>
<dt class="col-sm-6">支付宝配置:</dt>
<dd class="col-sm-6">
{% if config.ALIPAY_APP_ID %}
<span class="badge bg-success">已配置</span>
{% else %}
<span class="badge bg-warning">未配置</span>
{% endif %}
</dd>
2025-11-11 21:39:12 +08:00
</dl>
</div>
</div>
<div class="card shadow">
<div class="card-header">
<h6 class="mb-0">操作</h6>
</div>
<div class="card-body">
<div class="d-grid gap-2">
2025-12-12 11:35:14 +08:00
{% if config.PAYMENT_ENABLED %}
<button type="button" class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#testPaymentModal">
<i class="fas fa-credit-card me-2"></i>
测试支付功能
</button>
{% endif %}
2025-11-11 21:39:12 +08:00
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#backupModal">
<i class="fas fa-database me-2"></i>
数据备份
</button>
<button type="button" class="btn btn-outline-warning" data-bs-toggle="modal" data-bs-target="#clearCacheModal">
<i class="fas fa-broom me-2"></i>
清理缓存
</button>
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#resetModal">
<i class="fas fa-exclamation-triangle me-2"></i>
重置系统
</button>
</div>
</div>
</div>
</div>
</div>
<!-- 数据备份模态框 -->
<div class="modal fade" id="backupModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">数据备份</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>确定要创建数据备份吗?</p>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include_logs" checked>
<label class="form-check-label" for="include_logs">
包含日志文件
</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="backup-btn">开始备份</button>
</div>
</div>
</div>
</div>
<!-- 清理缓存模态框 -->
<div class="modal fade" id="clearCacheModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">清理缓存</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>确定要清理系统缓存吗?这将清除所有临时文件和缓存数据。</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-warning" id="clear-cache-btn">确认清理</button>
</div>
</div>
</div>
</div>
2025-12-12 11:35:14 +08:00
<!-- 测试支付功能模态框 -->
<div class="modal fade" id="testPaymentModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">测试支付功能</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>此操作将创建一个测试订单并生成支付链接,用于验证支付功能是否正常工作。</p>
<div class="alert alert-warning">
<i class="fas fa-exclamation-triangle me-2"></i>
请确保已完成支付宝配置并启用支付功能
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-success" id="test-payment-btn">开始测试</button>
</div>
</div>
</div>
</div>
2025-11-11 21:39:12 +08:00
<!-- 重置系统模态框 -->
<div class="modal fade" id="resetModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">重置系统</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="alert alert-danger">
<strong>警告!</strong> 此操作将删除所有数据并重置系统到初始状态,无法恢复。
</div>
<p>请输入"RESET"确认操作:</p>
<input type="text" class="form-control" id="reset-confirm" placeholder="输入RESET确认">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-danger" id="reset-btn" disabled>确认重置</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initEventListeners();
});
// 初始化事件监听器
function initEventListeners() {
// 基本设置表单
document.getElementById('basic-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveBasicSettings();
});
2025-11-22 16:48:45 +08:00
// 安全设置表单
document.getElementById('security-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveSecuritySettings();
});
// 生成密钥按钮
document.getElementById('generate-secret-key').addEventListener('click', function() {
generateSecretKey();
});
2025-11-11 21:39:12 +08:00
// 卡密设置表单
document.getElementById('license-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveLicenseSettings();
});
// API设置表单
document.getElementById('api-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveApiSettings();
});
// 文件上传设置表单
document.getElementById('upload-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveUploadSettings();
});
// 会话设置表单
document.getElementById('session-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveSessionSettings();
});
2025-11-22 16:48:45 +08:00
// 记住我设置表单
document.getElementById('remember-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveRememberSettings();
});
// 日志设置表单
document.getElementById('log-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
saveLogSettings();
});
2025-11-11 21:39:12 +08:00
// 重置确认输入
document.getElementById('reset-confirm').addEventListener('input', function() {
const resetBtn = document.getElementById('reset-btn');
resetBtn.disabled = this.value !== 'RESET';
});
// 重置按钮
document.getElementById('reset-btn').addEventListener('click', function() {
resetSystem();
});
// 清理缓存按钮
document.getElementById('clear-cache-btn').addEventListener('click', function() {
clearCache();
});
// 备份按钮
document.getElementById('backup-btn').addEventListener('click', function() {
backupData();
});
2025-12-12 11:35:14 +08:00
// 支付设置表单
document.getElementById('payment-settings-form').addEventListener('submit', function(e) {
e.preventDefault();
savePaymentSettings();
});
// 测试支付宝配置按钮
document.getElementById('test-alipay-config-btn').addEventListener('click', function() {
testAlipayConfig();
});
// 测试支付功能按钮
document.getElementById('test-payment-btn').addEventListener('click', function() {
testPaymentFunction();
});
2025-11-11 21:39:12 +08:00
}
2025-11-22 16:48:45 +08:00
// 生成安全密钥
function generateSecretKey() {
// 生成一个随机的32字符密钥
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
let key = '';
for (let i = 0; i < 32; i++) {
key += chars.charAt(Math.floor(Math.random() * chars.length));
}
document.getElementById('secret_key').value = key;
}
2025-11-11 21:39:12 +08:00
// 保存基本设置
function saveBasicSettings() {
const saveBtn = document.getElementById('save-basic-btn');
const saveText = document.getElementById('save-basic-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
site_name: document.getElementById('site_name').value,
admin_email: document.getElementById('admin_email').value,
2025-11-16 19:06:49 +08:00
frontend_domain: document.getElementById('frontend_domain').value,
2025-11-11 21:39:12 +08:00
max_failed_attempts: parseInt(document.getElementById('max_failed_attempts').value),
lockout_minutes: parseInt(document.getElementById('lockout_minutes').value),
max_unbind_times: parseInt(document.getElementById('max_unbind_times').value),
auth_secret_key: document.getElementById('auth_secret_key').value
};
// 发送请求保存设置
2025-11-22 16:48:45 +08:00
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
2025-11-11 21:39:12 +08:00
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('基本设置保存成功', 'success');
// 更新页面标题(如果修改了系统名称)
if (formData.site_name) {
document.title = formData.site_name + ' - 软件授权管理系统';
// 更新页面标题元素
const pageTitle = document.querySelector('h1.h2');
if (pageTitle) {
pageTitle.textContent = formData.site_name;
}
}
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
2025-11-22 16:48:45 +08:00
// 保存安全设置
function saveSecuritySettings() {
const saveBtn = document.getElementById('save-security-btn');
const saveText = document.getElementById('save-security-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
secret_key: document.getElementById('secret_key').value,
session_cookie_secure: document.getElementById('session_cookie_secure').value === 'True',
session_cookie_httponly: document.getElementById('session_cookie_httponly').value === 'True',
session_cookie_samesite: document.getElementById('session_cookie_samesite').value
};
// 如果密钥为空,则不发送
if (!formData.secret_key) {
delete formData.secret_key;
}
// 发送请求保存设置
apiRequest('/api/v1/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('安全设置保存成功', 'success');
// 清空密钥输入框
document.getElementById('secret_key').value = '';
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
2025-11-11 21:39:12 +08:00
// 保存卡密设置
function saveLicenseSettings() {
const saveBtn = document.getElementById('save-license-btn');
const saveText = document.getElementById('save-license-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
license_key_length: parseInt(document.getElementById('license_key_length').value),
license_key_prefix: document.getElementById('license_key_prefix').value,
trial_prefix: document.getElementById('trial_prefix').value,
offline_cache_days: parseInt(document.getElementById('offline_cache_days').value)
};
// 发送请求保存设置
2025-11-22 16:48:45 +08:00
apiRequest('/api/v1/settings', {
2025-11-11 21:39:12 +08:00
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('卡密设置保存成功', 'success');
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
// 保存API设置
function saveApiSettings() {
const saveBtn = document.getElementById('save-api-btn');
const saveText = document.getElementById('save-api-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
api_version: document.getElementById('api_version').value,
items_per_page: parseInt(document.getElementById('items_per_page').value)
};
// 发送请求保存设置
2025-11-22 16:48:45 +08:00
apiRequest('/api/v1/settings', {
2025-11-11 21:39:12 +08:00
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('API设置保存成功', 'success');
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
// 保存文件上传设置
function saveUploadSettings() {
const saveBtn = document.getElementById('save-upload-btn');
const saveText = document.getElementById('save-upload-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
max_content_length: parseInt(document.getElementById('max_content_length').value),
upload_folder: document.getElementById('upload_folder').value
};
// 发送请求保存设置
2025-11-22 16:48:45 +08:00
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
2025-11-11 21:39:12 +08:00
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('文件上传设置保存成功', 'success');
// 更新显示的上传限制信息
const maxContentLength = formData.max_content_length;
const uploadInfoElements = document.querySelectorAll('div.form-text');
uploadInfoElements.forEach(element => {
if (element.textContent.includes('当前设置:')) {
element.textContent = `当前设置: ${maxContentLength} MB`;
}
});
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
// 保存会话设置
function saveSessionSettings() {
const saveBtn = document.getElementById('save-session-btn');
const saveText = document.getElementById('save-session-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
session_lifetime_hours: parseInt(document.getElementById('session_lifetime_hours').value)
};
// 发送请求保存设置
2025-11-22 16:48:45 +08:00
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
2025-11-11 21:39:12 +08:00
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('会话设置保存成功', 'success');
// 更新显示的会话时间信息
const sessionHours = formData.session_lifetime_hours;
const sessionInfoElements = document.querySelectorAll('div.form-text');
sessionInfoElements.forEach(element => {
if (element.textContent.includes('当前设置:')) {
element.textContent = `当前设置: ${sessionHours} 小时`;
}
});
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
2025-11-22 16:48:45 +08:00
// 保存记住我设置
function saveRememberSettings() {
const saveBtn = document.getElementById('save-remember-btn');
const saveText = document.getElementById('save-remember-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
remember_cookie_duration: parseInt(document.getElementById('remember_cookie_duration').value),
remember_cookie_secure: document.getElementById('remember_cookie_secure').value === 'True',
remember_cookie_httponly: document.getElementById('remember_cookie_httponly').value === 'True',
remember_cookie_samesite: document.getElementById('remember_cookie_samesite').value
};
// 发送请求保存设置
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('记住我设置保存成功', 'success');
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
// 保存日志设置
function saveLogSettings() {
const saveBtn = document.getElementById('save-log-btn');
const saveText = document.getElementById('save-log-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
log_level: document.getElementById('log_level').value
};
// 发送请求保存设置
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('日志设置保存成功', 'success');
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
2025-11-11 21:39:12 +08:00
// 重置系统
function resetSystem() {
if (confirm('确定要重置系统吗?此操作无法恢复!')) {
showNotification('系统重置功能开发中...', 'info');
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('resetModal'));
modal.hide();
}
}
// 清理缓存
function clearCache() {
showNotification('正在清理缓存...', 'info');
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('clearCacheModal'));
modal.hide();
// 模拟清理过程
setTimeout(() => {
showNotification('缓存清理完成', 'success');
}, 2000);
}
// 数据备份
function backupData() {
showNotification('正在创建数据备份...', 'info');
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('backupModal'));
modal.hide();
2025-12-12 11:35:14 +08:00
2025-11-11 21:39:12 +08:00
// 模拟备份过程
setTimeout(() => {
showNotification('数据备份创建成功', 'success');
}, 3000);
}
2025-12-12 11:35:14 +08:00
// 保存支付设置
function savePaymentSettings() {
const saveBtn = document.getElementById('save-payment-btn');
const saveText = document.getElementById('save-payment-text');
// 显示加载状态
saveBtn.disabled = true;
saveText.textContent = '保存中...';
// 收集表单数据
const formData = {
payment_enabled: document.getElementById('payment_enabled').checked,
alipay_app_id: document.getElementById('alipay_app_id').value,
alipay_private_key: document.getElementById('alipay_private_key').value,
alipay_public_key: document.getElementById('alipay_public_key').value,
alipay_alipay_public_key: document.getElementById('alipay_alipay_public_key').value,
alipay_gateway: document.getElementById('alipay_gateway').value,
alipay_notify_url: document.getElementById('alipay_notify_url').value,
alipay_return_url: document.getElementById('alipay_return_url').value,
alipay_timeout_express: parseInt(document.getElementById('alipay_timeout_express').value),
alipay_sign_type: document.getElementById('alipay_sign_type').value,
alipay_charset: document.getElementById('alipay_charset').value,
alipay_version: document.getElementById('alipay_version').value
};
// 发送请求保存设置
const saveUrl = '/api/v1/settings';
apiRequest(saveUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
})
.then(data => {
if (data.success) {
showNotification('支付设置保存成功', 'success');
} else {
showNotification('保存失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('保存失败,请查看控制台了解详情', 'error');
})
.finally(() => {
saveBtn.disabled = false;
saveText.textContent = '保存设置';
});
}
// 测试支付宝配置
function testAlipayConfig() {
const testBtn = document.getElementById('test-alipay-config-btn');
const originalText = testBtn.innerHTML;
// 显示加载状态
testBtn.disabled = true;
testBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>测试中...';
// 收集当前配置
const configData = {
alipay_app_id: document.getElementById('alipay_app_id').value,
alipay_private_key: document.getElementById('alipay_private_key').value,
alipay_public_key: document.getElementById('alipay_public_key').value,
alipay_alipay_public_key: document.getElementById('alipay_alipay_public_key').value,
alipay_gateway: document.getElementById('alipay_gateway').value
};
// 验证必填项
if (!configData.alipay_app_id || !configData.alipay_private_key ||
!configData.alipay_public_key || !configData.alipay_alipay_public_key) {
showNotification('请先填写完整的支付宝配置信息', 'error');
testBtn.disabled = false;
testBtn.innerHTML = originalText;
return;
}
// 发送测试请求
apiRequest('/api/v1/settings/test-alipay', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(configData)
})
.then(data => {
if (data.success) {
showNotification('支付宝配置测试成功!', 'success');
} else {
showNotification('支付宝配置测试失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('测试失败,请查看控制台了解详情', 'error');
})
.finally(() => {
testBtn.disabled = false;
testBtn.innerHTML = originalText;
});
}
// 测试支付功能
function testPaymentFunction() {
const testBtn = document.getElementById('test-payment-btn');
const originalText = testBtn.textContent;
// 显示加载状态
testBtn.disabled = true;
testBtn.textContent = '测试中...';
// 发送测试请求
apiRequest('/api/v1/settings/test-payment', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({})
})
.then(data => {
if (data.success) {
showNotification('支付功能测试成功!', 'success');
// 关闭模态框
const modal = bootstrap.Modal.getInstance(document.getElementById('testPaymentModal'));
modal.hide();
// 显示支付链接
if (data.payment_url) {
setTimeout(() => {
if (confirm('支付测试成功!是否打开支付链接?')) {
window.open(data.payment_url, '_blank');
}
}, 500);
}
} else {
showNotification('支付功能测试失败: ' + data.message, 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('测试失败,请查看控制台了解详情', 'error');
})
.finally(() => {
testBtn.disabled = false;
testBtn.textContent = originalText;
});
}
2025-11-11 21:39:12 +08:00
</script>
{% endblock %}