185 lines
9.9 KiB
HTML
185 lines
9.9 KiB
HTML
|
|
{% extends 'base.html' %}
|
|||
|
|
{% block title %}功能设置 - {{ domain.name }}{% endblock %}
|
|||
|
|
{% block extra_head %}
|
|||
|
|
<style>
|
|||
|
|
.form-text { font-size: 0.85rem; }
|
|||
|
|
.sidebar-sticky { position: sticky; top: 80px; }
|
|||
|
|
@media (max-width: 991.98px){ .sidebar-sticky { position: static; } }
|
|||
|
|
textarea.json-field { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; }
|
|||
|
|
.disabled-hint { color: #6c757d; font-size: 0.85rem; }
|
|||
|
|
</style>
|
|||
|
|
{% endblock %}
|
|||
|
|
|
|||
|
|
{% block content %}
|
|||
|
|
<div class="row g-4">
|
|||
|
|
<div class="col-lg-2 col-md-3">
|
|||
|
|
<div class="sidebar-sticky">
|
|||
|
|
<div class="list-group">
|
|||
|
|
<a class="list-group-item list-group-item-action" href="{% url 'domains:detail' domain_id=domain.id %}">概览</a>
|
|||
|
|
<a class="list-group-item list-group-item-action" href="{% url 'domains:detail' domain_id=domain.id %}#stats">统计</a>
|
|||
|
|
<a class="list-group-item list-group-item-action" href="{% url 'domains:detail' domain_id=domain.id %}#cname">接入信息</a>
|
|||
|
|
<a class="list-group-item list-group-item-action active" href="#">设置</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-lg-10 col-md-9">
|
|||
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|||
|
|
<h3>功能设置:{{ domain.name }}</h3>
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<a href="{% url 'domains:detail' domain_id=domain.id %}" class="btn btn-outline-secondary">返回详情</a>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="alert alert-info">
|
|||
|
|
根据当前套餐的功能开关,部分设置可能不可用或被禁用。变更套餐可启用更多功能。
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<form method="post" action="{% url 'domains:settings' domain_id=domain.id %}">
|
|||
|
|
{% csrf_token %}
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">安全与网络</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="form-check form-switch mb-2">
|
|||
|
|
<input type="checkbox" class="form-check-input" id="id_waf_enabled" name="waf_enabled" {% if form.waf_enabled.value %}checked{% endif %} {% if not plan_features.waf_enabled %}disabled{% endif %}>
|
|||
|
|
<label class="form-check-label" for="id_waf_enabled">启用 WAF</label>
|
|||
|
|
{% if not plan_features.waf_enabled %}<div class="disabled-hint">当前套餐不支持 WAF。</div>{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check form-switch mb-2">
|
|||
|
|
<input type="checkbox" class="form-check-input" id="id_http3_enabled" name="http3_enabled" {% if form.http3_enabled.value %}checked{% endif %} {% if not plan_features.http3_enabled %}disabled{% endif %}>
|
|||
|
|
<label class="form-check-label" for="id_http3_enabled">启用 HTTP/3</label>
|
|||
|
|
{% if not plan_features.http3_enabled %}<div class="disabled-hint">当前套餐不支持 HTTP/3。</div>{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check form-switch mb-2">
|
|||
|
|
<input type="checkbox" class="form-check-input" id="id_websocket_enabled" name="websocket_enabled" {% if form.websocket_enabled.value %}checked{% endif %} {% if not plan_features.websocket_enabled %}disabled{% endif %}>
|
|||
|
|
<label class="form-check-label" for="id_websocket_enabled">启用 WebSocket</label>
|
|||
|
|
{% if not plan_features.websocket_enabled %}<div class="disabled-hint">当前套餐不支持 WebSocket。</div>{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check form-switch mb-2">
|
|||
|
|
<input type="checkbox" class="form-check-input" id="id_logs_enabled" name="logs_enabled" {% if form.logs_enabled.value %}checked{% endif %} {% if not plan_features.logs_enabled %}disabled{% endif %}>
|
|||
|
|
<label class="form-check-label" for="id_logs_enabled">启用日志下载</label>
|
|||
|
|
{% if not plan_features.logs_enabled %}<div class="disabled-hint">当前套餐不支持实时/离线日志。</div>{% endif %}
|
|||
|
|
</div>
|
|||
|
|
<div class="form-check form-switch mb-2">
|
|||
|
|
<input type="checkbox" class="form-check-input" id="id_redirect_https_enabled" name="redirect_https_enabled" {% if form.redirect_https_enabled.value %}checked{% endif %}>
|
|||
|
|
<label class="form-check-label" for="id_redirect_https_enabled">强制 HTTP→HTTPS 跳转</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">缓存与页面规则</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<div class="btn-group">
|
|||
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="applyCacheTemplate('static')">套用静态资源缓存</button>
|
|||
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="applyPageTemplate('redirect_https')">套用强制 HTTPS 跳转</button>
|
|||
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="applyPageTemplate('rewrite_www')">套用 www→非www 重写</button>
|
|||
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="downloadJson('id_cache_rules_json')">导出缓存 JSON</button>
|
|||
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="downloadJson('id_page_rules_json')">导出页面规则 JSON</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label for="id_cache_rules_json" class="form-label">缓存规则(JSON)</label>
|
|||
|
|
<textarea class="form-control json-field" id="id_cache_rules_json" name="cache_rules_json" rows="6">{{ form.cache_rules_json.value }}</textarea>
|
|||
|
|
<div class="form-text">示例:{"rules": [{"path": "/images/*", "ttl": 3600}]}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label for="id_page_rules_json" class="form-label">页面规则(JSON)</label>
|
|||
|
|
<textarea class="form-control json-field" id="id_page_rules_json" name="page_rules_json" rows="6">{{ form.page_rules_json.value }}</textarea>
|
|||
|
|
<div class="form-text">示例:{"rules": [{"match": "example.com/*", "action": "cache_bypass"}]}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="card mb-4">
|
|||
|
|
<div class="card-header">IP 控制</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label for="id_ip_whitelist" class="form-label">IP 白名单(逗号分隔)</label>
|
|||
|
|
<input type="text" class="form-control" id="id_ip_whitelist" name="ip_whitelist" value="{{ form.ip_whitelist.value }}">
|
|||
|
|
<div class="form-text">示例:1.2.3.4, 10.0.0.1</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-3">
|
|||
|
|
<label for="id_ip_blacklist" class="form-label">IP 黑名单(逗号分隔)</label>
|
|||
|
|
<input type="text" class="form-control" id="id_ip_blacklist" name="ip_blacklist" value="{{ form.ip_blacklist.value }}">
|
|||
|
|
<div class="form-text">示例:203.0.113.0/24</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="d-flex gap-2">
|
|||
|
|
<button type="submit" class="btn btn-primary" onclick="return confirm('保存设置会立即影响后端配置,确认提交?');">保存设置</button>
|
|||
|
|
<a href="{% url 'domains:detail' domain_id=domain.id %}" class="btn btn-outline-secondary">取消</a>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
|
|||
|
|
<div class="card mt-4">
|
|||
|
|
<div class="card-header">套餐功能可用性</div>
|
|||
|
|
<div class="card-body">
|
|||
|
|
<div class="row">
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<ul class="list-group list-group-flush">
|
|||
|
|
<li class="list-group-item">WAF:{% if plan_features.waf_enabled %}<span class="text-success">可用</span>{% else %}<span class="text-muted">不可用</span>{% endif %}</li>
|
|||
|
|
<li class="list-group-item">HTTP/3:{% if plan_features.http3_enabled %}<span class="text-success">可用</span>{% else %}<span class="text-muted">不可用</span>{% endif %}</li>
|
|||
|
|
<li class="list-group-item">WebSocket:{% if plan_features.websocket_enabled %}<span class="text-success">可用</span>{% else %}<span class="text-muted">不可用</span>{% endif %}</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
<div class="col-md-6">
|
|||
|
|
<ul class="list-group list-group-flush">
|
|||
|
|
<li class="list-group-item">日志下载:{% if plan_features.logs_enabled %}<span class="text-success">可用</span>{% else %}<span class="text-muted">不可用</span>{% endif %}</li>
|
|||
|
|
<li class="list-group-item">自定义规则:{% if plan_features.page_rules_limit %}<span class="text-success">上限 {{ plan_features.page_rules_limit }}</span>{% else %}<span class="text-muted">不可用</span>{% endif %}</li>
|
|||
|
|
</ul>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="small text-muted mt-2">提示:套餐功能由管理员在“套餐管理”中配置。</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
{% endblock %}
|
|||
|
|
{% block extra_scripts %}
|
|||
|
|
<script>
|
|||
|
|
function applyCacheTemplate(name){
|
|||
|
|
var el = document.getElementById('id_cache_rules_json');
|
|||
|
|
if(!el) return;
|
|||
|
|
var tpl = {};
|
|||
|
|
if(name === 'static'){
|
|||
|
|
tpl = {
|
|||
|
|
rules: [
|
|||
|
|
{ path: '/static/*', ttl: 3600 },
|
|||
|
|
{ path: '/images/*', ttl: 3600 },
|
|||
|
|
{ path: '/css/*', ttl: 1800 },
|
|||
|
|
{ path: '/js/*', ttl: 1800 }
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
el.value = JSON.stringify(tpl, null, 2);
|
|||
|
|
}
|
|||
|
|
function applyPageTemplate(name){
|
|||
|
|
var el = document.getElementById('id_page_rules_json');
|
|||
|
|
if(!el) return;
|
|||
|
|
var tpl = {};
|
|||
|
|
if(name === 'redirect_https'){
|
|||
|
|
tpl = { redirectToHTTPS: { isOn: true } };
|
|||
|
|
var chk = document.getElementById('id_redirect_https_enabled');
|
|||
|
|
if(chk) chk.checked = true;
|
|||
|
|
}
|
|||
|
|
if(name === 'rewrite_www'){
|
|||
|
|
tpl = { rewriteRules: [{ pattern: '^www\\.(.*)$', replace: '$1', flags: 'R=301' }] };
|
|||
|
|
}
|
|||
|
|
el.value = JSON.stringify(tpl, null, 2);
|
|||
|
|
}
|
|||
|
|
function downloadJson(textareaId){
|
|||
|
|
var el = document.getElementById(textareaId);
|
|||
|
|
if(!el) return;
|
|||
|
|
var blob = new Blob([el.value || '{}'], {type: 'application/json'});
|
|||
|
|
var a = document.createElement('a');
|
|||
|
|
a.href = URL.createObjectURL(blob);
|
|||
|
|
a.download = textareaId + '.json';
|
|||
|
|
document.body.appendChild(a);
|
|||
|
|
a.click();
|
|||
|
|
document.body.removeChild(a);
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
{% endblock %}
|