352 lines
20 KiB
HTML
352 lines
20 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}构建平台配置 · RustDesk 客户端定制平台{% endblock %}
|
||
|
||
{% block content %}
|
||
<nav class="breadcrumb">
|
||
<a href="{% url 'dashboard' %}">后台管理</a>
|
||
<span class="breadcrumb-sep">/</span>
|
||
<span>构建平台配置</span>
|
||
</nav>
|
||
|
||
<div class="page-head">
|
||
<h1 class="page-title">
|
||
<svg class="icon"><use href="#i-sliders"/></svg>
|
||
构建平台配置
|
||
</h1>
|
||
<p class="page-subtitle">
|
||
选择实际执行构建任务的平台:GitHub Actions(云托管构建机)或自建 Gitea Actions(构建机自备、可全内网运行)。
|
||
支持在本页直接配置(优先级最高),也继续兼容 docker-compose 环境变量方式。
|
||
</p>
|
||
</div>
|
||
|
||
{# ===== 配置状态总览 ===== #}
|
||
<div class="card config-card">
|
||
<div class="card-header">
|
||
<svg class="icon"><use href="#i-shield"/></svg> 配置状态检查<span class="platform-tag" style="margin-left:8px">当前平台:{{ build_platform_label }}</span>
|
||
</div>
|
||
<div class="card-body">
|
||
{% if build_ready and not warnings %}
|
||
<div class="config-ok">
|
||
<svg class="icon"><use href="#i-check-circle"/></svg>
|
||
{{ build_platform_label }} 构建服务配置完整,可以正常提交客户端构建任务。
|
||
</div>
|
||
{% else %}
|
||
<ul class="config-check-list">
|
||
{% if build_ready %}
|
||
<li class="is-ok"><svg class="icon"><use href="#i-check-circle"/></svg> {{ build_platform_label }} 连接信息已配置</li>
|
||
{% else %}
|
||
<li class="is-bad"><svg class="icon"><use href="#i-alert"/></svg> {{ build_platform_label }} 连接信息缺失,暂无法触发构建</li>
|
||
{% endif %}
|
||
{% for w in warnings %}
|
||
<li class="is-warn"><svg class="icon"><use href="#i-alert"/></svg> {{ w }}</li>
|
||
{% endfor %}
|
||
</ul>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
{# ===== 配置表单 ===== #}
|
||
<form method="post" action="{% url 'github_settings' %}" class="settings-form">
|
||
{% csrf_token %}
|
||
|
||
{# --- 构建平台选择(自定义平台卡,单选) --- #}
|
||
<div class="card">
|
||
<div class="card-header"><svg class="icon"><use href="#i-sliders"/></svg> 构建平台</div>
|
||
<div class="card-body">
|
||
<p class="card-desc">切换后仅影响新提交的构建任务;历史任务仍保存在原平台并可继续查询状态与日志。</p>
|
||
<div class="build-platform-grid">
|
||
<label class="platform-card {% if build_platform != 'gitea' %}active{% endif %}" data-platform-card="github">
|
||
<input type="radio" name="build_platform" value="github" class="visually-hidden"
|
||
{% if build_platform != 'gitea' %}checked{% endif %}>
|
||
<svg class="icon brand-github"><use href="#b-github"/></svg>
|
||
<div class="platform-name">GitHub Actions</div>
|
||
<div class="platform-desc">官方云托管 Windows / macOS / Linux 构建机,零运维;国内网络可能需要代理。</div>
|
||
</label>
|
||
<label class="platform-card {% if build_platform == 'gitea' %}active{% endif %}" data-platform-card="gitea">
|
||
<input type="radio" name="build_platform" value="gitea" class="visually-hidden"
|
||
{% if build_platform == 'gitea' %}checked{% endif %}>
|
||
<svg class="icon"><use href="#b-gitea"/></svg>
|
||
<div class="platform-name">Gitea Actions(自建)</div>
|
||
<div class="platform-desc">API 与工作流语法兼容 GitHub,可全内网运行、回传快;需自备全部构建机。</div>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{# --- 各配置分组(platform 组已在上方自定义渲染) --- #}
|
||
{% for group_key, group_title, group_desc in groups %}{% if group_key != 'platform' %}
|
||
<div class="card backend-panel
|
||
{% if group_key == 'gitea' %}panel-gitea{% if build_platform != 'gitea' %} panel-hidden{% endif %}
|
||
{% elif group_key == 'basic' or group_key == 'network' %}panel-github{% if build_platform == 'gitea' %} panel-hidden{% endif %}{% endif %}">
|
||
<div class="card-header"><svg class="icon"><use href="#i-sliders"/></svg> {{ group_title }}</div>
|
||
<div class="card-body">
|
||
<p class="card-desc">{{ group_desc }}</p>
|
||
{% for row in rows %}{% if row.group == group_key %}
|
||
<div class="field setting-row{% if row.key == 'SH_SECRET' %} panel-github{% if build_platform == 'gitea' %} panel-hidden{% endif %}{% endif %}">
|
||
<label class="setting-label" for="{{ row.field.id_for_label }}">
|
||
{{ row.label }}
|
||
<span class="source-badge source-{{ row.source }}">{{ row.source_label }}</span>
|
||
</label>
|
||
{{ row.field }}
|
||
{% if row.sensitive %}
|
||
<div class="secret-meta">
|
||
<label class="clear-check" for="{{ row.clear_field.id_for_label }}">
|
||
{{ row.clear_field }} 清除后台配置,回退到环境变量/默认值
|
||
</label>
|
||
{% if row.source == 'db' %}
|
||
<span class="secret-state is-set">已在后台保存</span>
|
||
{% elif row.source == 'env' %}
|
||
<span class="secret-state is-env">已通过环境变量配置</span>
|
||
{% else %}
|
||
<span class="secret-state is-empty">未配置(使用默认值)</span>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% if row.help %}<div class="field-hint">{{ row.help }}</div>{% endif %}
|
||
</div>
|
||
{% endif %}{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}{% endfor %}
|
||
|
||
{# --- 保存与连接测试(始终可见,测试目标跟随当前所选平台) --- #}
|
||
<div class="card settings-submit-card">
|
||
<div class="card-body settings-actions">
|
||
<div class="btn-row">
|
||
<button type="submit" class="btn btn-primary">
|
||
<svg class="icon"><use href="#i-save"/></svg> 保存配置
|
||
</button>
|
||
<button type="button" class="btn btn-secondary" id="testBuildBtn">
|
||
<svg class="icon"><use href="#i-zap"/></svg> <span id="testBuildBtnLabel">测试 GitHub 连接</span>
|
||
</button>
|
||
</div>
|
||
<div id="testResult" class="test-result" hidden></div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
{# ===== GitHub 配置引导 ===== #}
|
||
<div class="card guide-card guide-panel guide-github {% if build_platform == 'gitea' %}panel-hidden{% endif %}">
|
||
<div class="card-header">
|
||
<svg class="icon brand-github"><use href="#b-github"/></svg> GitHub 首次配置引导(约 5 分钟)
|
||
</div>
|
||
<div class="card-body">
|
||
<ol class="guide-steps">
|
||
<li>
|
||
<div class="guide-title">Fork 构建仓库</div>
|
||
<p>
|
||
打开上游仓库 <a href="https://github.com/bryangerlach/rdgen" target="_blank" rel="noopener">github.com/bryangerlach/rdgen
|
||
<svg class="icon icon-inline"><use href="#i-external"/></svg></a>,
|
||
点击右上角 <strong>Fork</strong> 复制到你自己的 GitHub 账号下。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">启用 GitHub Actions</div>
|
||
<p>
|
||
进入 Fork 后的仓库页面,打开
|
||
<a href="https://github.com/{% if gh_user %}{{ gh_user }}{% else %}你的用户名{% endif %}/{% if gh_repo %}{{ gh_repo }}{% else %}rdgen{% endif %}/actions"
|
||
target="_blank" rel="noopener">Actions 页面
|
||
<svg class="icon icon-inline"><use href="#i-external"/></svg></a>,
|
||
如页面提示则点击绿色的 <strong>Enable Actions</strong> 按钮。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">创建 Fine-grained 访问令牌(PAT)</div>
|
||
<p>
|
||
打开
|
||
<a href="https://github.com/settings/personal-access-tokens/new" target="_blank" rel="noopener">
|
||
Generate new token <svg class="icon icon-inline"><use href="#i-external"/></svg></a>,
|
||
按如下设置后生成,并把令牌(github_pat_ 开头)填入上方「GitHub 访问令牌」:
|
||
</p>
|
||
<ul class="guide-sublist">
|
||
<li><strong>Repository access</strong>:选择 Only select repositories,勾选你 Fork 的仓库</li>
|
||
<li><strong>Repository permissions</strong>:Actions 设为 <strong>Read and write</strong>,Workflows 设为 <strong>Read and write</strong></li>
|
||
<li>Expiration 建议按需设置,过期后需要回到这里更新令牌</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">添加两个仓库 Secret</div>
|
||
<p>
|
||
打开仓库
|
||
<a href="https://github.com/{% if gh_user %}{{ gh_user }}{% else %}你的用户名{% endif %}/{% if gh_repo %}{{ gh_repo }}{% else %}rdgen{% endif %}/settings/secrets/actions"
|
||
target="_blank" rel="noopener">Settings → Secrets and variables → Actions
|
||
<svg class="icon icon-inline"><use href="#i-external"/></svg></a>,
|
||
分别 New repository secret:
|
||
</p>
|
||
<ul class="guide-sublist">
|
||
<li><strong>GENURL</strong>:本平台的公网访问地址,须与上方「本平台外部访问地址」一致,例如 https://rdgen.example.com</li>
|
||
<li><strong>ZIP_PASSWORD</strong>:一个随机强密码(可用 <code>python3 -c 'import secrets; print(secrets.token_hex(100))'</code> 生成),
|
||
须与上方「配置压缩包密码」<strong>完全一致</strong></li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">保存并验证</div>
|
||
<p>填写上方表单后点击「保存配置」,再点击「测试 GitHub 连接」,看到「连接正常」提示即配置完成,可以去客户端定制页提交构建了。</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
|
||
{# ===== Gitea 配置引导 ===== #}
|
||
<div class="card guide-card guide-panel guide-gitea {% if build_platform != 'gitea' %}panel-hidden{% endif %}">
|
||
<div class="card-header">
|
||
<svg class="icon"><use href="#b-gitea"/></svg> Gitea 首次配置引导(需自备 Gitea 与构建机)
|
||
</div>
|
||
<div class="card-body">
|
||
<ol class="guide-steps">
|
||
<li>
|
||
<div class="guide-title">部署 Gitea 1.27+ 并准备构建仓库</div>
|
||
<p>
|
||
自建一个 Gitea 实例(建议 1.27 或更高版本,低版本不支持运行详情回传),
|
||
把本项目仓库推送到该实例(仓库内含 <code>.gitea/workflows</code> 适配工作流)。
|
||
<a href="https://docs.gitea.com/usage/actions/quickstart" target="_blank" rel="noopener">
|
||
官方 Quick Start <svg class="icon icon-inline"><use href="#i-external"/></svg></a>
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">启用仓库 Actions</div>
|
||
<p>
|
||
进入
|
||
{% if gitea_server and gitea_owner %}
|
||
<a href="{{ gitea_server }}/{{ gitea_owner }}/{{ gitea_repo|default:'rdgen' }}/settings" target="_blank" rel="noopener">
|
||
仓库设置页 <svg class="icon icon-inline"><use href="#i-external"/></svg></a>
|
||
{% else %}仓库设置页{% endif %}
|
||
,勾选 <strong>Enable Repository Actions</strong>。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">注册 act_runner 构建机</div>
|
||
<p>在仓库 Settings → Actions → Runners 创建注册令牌,并在构建机上安装注册 act_runner:</p>
|
||
<ul class="guide-sublist">
|
||
<li><strong>Linux</strong>:可用 Docker 模式(需预装 QEMU 以构建 arm64);</li>
|
||
<li><strong>Windows</strong>:只能 host 模式直接跑在 Windows 机器上(MSVC/Flutter/vcpkg 环境);</li>
|
||
<li><strong>macOS</strong>:只能 host 模式跑在 Apple 硬件上;</li>
|
||
<li>runner 自定义标签需与工作流一致:<code>ubuntu-22.04</code>、<code>ubuntu-22.04-arm</code>、<code>windows-2022</code>、<code>macos-14</code>、<code>macos-15-intel</code>。</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">配置仓库 Secrets</div>
|
||
<p>
|
||
在仓库 Settings → Actions → Secrets 中添加
|
||
<strong>GENURL</strong>(本平台地址)与 <strong>ZIP_PASSWORD</strong>(与上方完全一致);
|
||
如需 Android/macOS 签名,再添加 ANDROID_SIGNING_KEY、MACOS_P12_BASE64、SIGN_BASE_URL、SIGN_API_KEY。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">第三方 actions 的网络说明</div>
|
||
<p>
|
||
工作流使用的第三方 action 默认从 github.com 拉取;纯内网环境请为 runner 配置出网白名单,
|
||
或在 Gitea 的 <code>app.ini</code> 配置 <code>[actions].DEFAULT_ACTIONS_URL</code> 走自建 action 镜像。
|
||
完整部署步骤(含 vcpkg 文件缓存配置)见项目 setup.md 的「Gitea 部署附录」。
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<div class="guide-title">保存并验证</div>
|
||
<p>填写上方 Gitea 配置后点击「保存配置」,再点击「测试 Gitea 连接」,看到「连接正常」提示即可提交构建。</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
</div>
|
||
|
||
{# ===== 环境变量方式说明 ===== #}
|
||
<div class="card">
|
||
<div class="card-header">
|
||
<svg class="icon"><use href="#i-server"/></svg> 环境变量方式(可选,继续支持)
|
||
</div>
|
||
<div class="card-body">
|
||
<p class="card-desc">
|
||
也可以在 <code>docker-compose.yml</code> 的 environment 中配置以下变量,容器重启后自动生效;
|
||
本页保存的同名配置优先级更高(值与环境变量相同时会自动回退,不产生重复覆盖)。
|
||
</p>
|
||
<div class="table-wrap">
|
||
<table class="table">
|
||
<thead><tr><th>环境变量</th><th>对应配置</th><th>说明</th></tr></thead>
|
||
<tbody>
|
||
<tr><td class="mono">BUILD_PLATFORM</td><td>构建平台</td><td>github(默认)或 gitea</td></tr>
|
||
<tr><td class="mono">GHUSER</td><td>GitHub 用户名</td><td>Fork 仓库所在账号</td></tr>
|
||
<tr><td class="mono">GHBEARER</td><td>GitHub 访问令牌</td><td>Fine-grained PAT,Actions/Workflows 读写</td></tr>
|
||
<tr><td class="mono">GENURL</td><td>平台外部访问地址</td><td>两平台共用,供构建机回传产物,公网/内网可访问</td></tr>
|
||
<tr><td class="mono">ZIP_PASSWORD</td><td>配置压缩包密码</td><td>两平台共用,须与仓库 Secret 一致</td></tr>
|
||
<tr><td class="mono">REPONAME</td><td>GitHub 仓库名称</td><td>默认 rdgen</td></tr>
|
||
<tr><td class="mono">GHBRANCH</td><td>GitHub 工作流分支</td><td>默认 master</td></tr>
|
||
<tr><td class="mono">PROTOCOL</td><td>平台访问协议</td><td>https(默认)或 http</td></tr>
|
||
<tr><td class="mono">SH_SECRET</td><td>GitHub 自托管 Runner 密钥</td><td>仅 GitHub 自托管构建时使用</td></tr>
|
||
<tr><td class="mono">WEBHOOK_SECRET</td><td>回调校验密钥</td><td>两平台共用,留空不校验</td></tr>
|
||
<tr><td class="mono">GH_PROXY</td><td>GitHub 访问代理</td><td>如 http://host:port,留空直连</td></tr>
|
||
<tr><td class="mono">GITEA_SERVER_URL</td><td>Gitea 服务器地址</td><td>如 https://gitea.example.com</td></tr>
|
||
<tr><td class="mono">GITEA_TOKEN</td><td>Gitea 访问令牌</td><td>需仓库 Actions 读写权限</td></tr>
|
||
<tr><td class="mono">GITEA_OWNER</td><td>Gitea 仓库属主</td><td>用户名或组织名</td></tr>
|
||
<tr><td class="mono">GITEA_REPO</td><td>Gitea 仓库名称</td><td>默认 rdgen</td></tr>
|
||
<tr><td class="mono">GITEA_BRANCH</td><td>Gitea 工作流分支</td><td>默认 master</td></tr>
|
||
<tr><td class="mono">GITEA_PROXY</td><td>Gitea 访问代理</td><td>内网直连通常留空</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block head_extra %}
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
var radios = document.querySelectorAll('input[name=build_platform]');
|
||
var btn = document.getElementById('testBuildBtn');
|
||
var btnLabel = document.getElementById('testBuildBtnLabel');
|
||
var box = document.getElementById('testResult');
|
||
|
||
function currentPlatform() {
|
||
var checked = document.querySelector('input[name=build_platform]:checked');
|
||
return checked ? checked.value : 'github';
|
||
}
|
||
|
||
function syncPanels() {
|
||
var platform = currentPlatform();
|
||
document.querySelectorAll('.panel-gitea, .guide-gitea').forEach(function (el) {
|
||
el.classList.toggle('panel-hidden', platform !== 'gitea');
|
||
});
|
||
document.querySelectorAll('.panel-github, .guide-github').forEach(function (el) {
|
||
el.classList.toggle('panel-hidden', platform !== 'github');
|
||
});
|
||
document.querySelectorAll('[data-platform-card]').forEach(function (card) {
|
||
card.classList.toggle('active', card.getAttribute('data-platform-card') === platform);
|
||
});
|
||
btnLabel.textContent = platform === 'gitea' ? '测试 Gitea 连接' : '测试 GitHub 连接';
|
||
}
|
||
|
||
radios.forEach(function (radio) { radio.addEventListener('change', syncPanels); });
|
||
syncPanels();
|
||
|
||
btn.addEventListener('click', function () {
|
||
var platform = currentPlatform();
|
||
btn.disabled = true;
|
||
box.hidden = false;
|
||
box.className = 'test-result is-loading';
|
||
box.textContent = platform === 'gitea' ? '正在调用 Gitea API 验证配置…' : '正在调用 GitHub API 验证配置…';
|
||
|
||
var csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
|
||
fetch('{% url "test_build" %}', {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRFToken': csrfToken,
|
||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||
},
|
||
body: 'platform=' + encodeURIComponent(platform),
|
||
})
|
||
.then(function (r) { return r.json().then(function (j) { return {ok: r.ok && j.ok, message: j.message}; }); })
|
||
.then(function (res) {
|
||
box.className = 'test-result ' + (res.ok ? 'is-ok' : 'is-bad');
|
||
box.textContent = res.message;
|
||
})
|
||
.catch(function () {
|
||
box.className = 'test-result is-bad';
|
||
box.textContent = '测试请求失败,请检查网络或服务日志。';
|
||
})
|
||
.finally(function () {
|
||
btn.disabled = false;
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
{% endblock %}
|