feat: OTA重构 — manifest.json统一+业务页三渠道
- manifest.json: stable/beta/nightly 统一H5+APK配置 - index-stable.html: 主业务页(⚙齿轮渠道切换) - index-beta/index-nightly: meta refresh→stable(开发期)
This commit is contained in:
1
ota/h5/index-beta.html
Normal file
1
ota/h5/index-beta.html
Normal file
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=index-stable.html"></head><body></body></html>
|
||||
1
ota/h5/index-nightly.html
Normal file
1
ota/h5/index-nightly.html
Normal file
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta http-equiv="refresh" content="0;url=index-stable.html"></head><body></body></html>
|
||||
76
ota/h5/index-stable.html
Normal file
76
ota/h5/index-stable.html
Normal file
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{font-family:system-ui;background:#0f172a;color:#e2e8f0;padding:16px}
|
||||
h1{font-size:18px;color:#38bdf8}
|
||||
.card{background:#1e293b;border-radius:8px;padding:16px;margin:12px 0}
|
||||
.card-title{font-size:14px;color:#94a3b8;margin-bottom:8px}
|
||||
.btn{display:block;width:100%;padding:14px;margin:8px 0;border:none;border-radius:8px;font-size:16px;cursor:pointer}
|
||||
.btn-check{background:#1e40af;color:#fff}
|
||||
.btn-download{background:#15803d;color:#fff;display:none}
|
||||
.btn-download.show{display:block}
|
||||
.log{background:#0f172a;padding:12px;border-radius:8px;margin-top:12px;max-height:200px;overflow-y:auto;font-size:11px;white-space:pre-wrap;color:#94a3b8}
|
||||
.ver{color:#94a3b8;font-size:12px;margin:4px 0}
|
||||
.gear{position:absolute;top:20px;right:16px;font-size:20px;cursor:pointer;color:#94a3b8}
|
||||
.modal{display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.7);z-index:99}
|
||||
.modal.show{display:flex;align-items:center;justify-content:center}
|
||||
.modal-box{background:#1e293b;border-radius:12px;padding:24px;width:80%;max-width:360px}
|
||||
.modal-title{font-size:16px;color:#38bdf8;margin-bottom:16px}
|
||||
.ch-opt{display:block;width:100%;padding:12px;margin:6px 0;border:1px solid #334155;border-radius:6px;background:transparent;color:#e2e8f0;font-size:14px;cursor:pointer;text-align:left}
|
||||
.ch-opt.on{border-color:#38bdf8;background:#1e40af40}
|
||||
.badge{display:inline-block;padding:2px 8px;border-radius:4px;font-size:10px;margin-left:8px}
|
||||
.badge-new{background:#dc2626;color:#fff}
|
||||
</style></head><body>
|
||||
<div class="gear" onclick="openSettings()">⚙️</div>
|
||||
<h1>Yuzu GCA</h1>
|
||||
<div class="ver"><span id="ver"></span><span id="badge" class="badge badge-new" style="display:none">NEW</span></div>
|
||||
<div class="card"><div class="card-title">当前渠道: <span id="chLabel">Stable</span></div></div>
|
||||
<button class="btn btn-check" onclick="checkUpdate()">检查更新</button>
|
||||
<button class="btn btn-download" id="btnApply" onclick="applyUpdate()">下载更新</button>
|
||||
<div class="log" id="log"></div>
|
||||
|
||||
<div class="modal" id="modal">
|
||||
<div class="modal-box">
|
||||
<div class="modal-title">选择渠道</div>
|
||||
<button class="ch-opt on" onclick="setChannel('stable')">Stable (发行)</button>
|
||||
<button class="ch-opt" onclick="setChannel('beta')">Beta (测试)</button>
|
||||
<button class="ch-opt" onclick="setChannel('nightly')">Nightly (开发)</button>
|
||||
<button class="btn btn-check" style="margin-top:12px" onclick="closeSettings()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var channel=localStorage.getItem('gca_channel')||'stable';
|
||||
var dlUrl='',dlVer='',dlSha='';
|
||||
function log(m){var l=document.getElementById('log');l.innerText+=m+'\n';l.scrollTop=l.scrollHeight}
|
||||
function openSettings(){document.getElementById('modal').classList.add('show')}
|
||||
function closeSettings(){document.getElementById('modal').classList.remove('show')}
|
||||
function setChannel(ch){
|
||||
channel=ch;localStorage.setItem('gca_channel',ch);
|
||||
document.getElementById('chLabel').innerText=ch==='stable'?'Stable (发行)':ch==='beta'?'Beta (测试)':'Nightly (开发)';
|
||||
closeSettings();checkUpdate()
|
||||
}
|
||||
function checkUpdate(){
|
||||
log('channel: '+channel+' checking manifest...');
|
||||
var j=JSON.parse(Android.fetchManifest(channel));
|
||||
if(j.error){log('ERR: '+j.error);return}
|
||||
var h5=j.h5;log('latest: '+h5.latest+' min: '+h5.min_version);
|
||||
var v=h5.min_version.replace(/[^0-9]/g,''),my=(document.getElementById('ver').innerText||'').replace(/[^0-9]/g,'');
|
||||
if(parseInt(my||'0')<parseInt(v||'0')){log('ERR: APK too old (need v'+h5.min_version+')');return}
|
||||
dlUrl=h5.url;dlSha=h5.sha256||'';
|
||||
if(!dlSha||/^0{64}$/.test(dlSha)){log('ERR: channel no build');return}
|
||||
if(h5.latest!==document.getElementById('ver').innerText){
|
||||
dlVer=h5.latest;document.getElementById('btnApply').classList.add('show');log('*** NEW VERSION ***')}
|
||||
else log('up to date')
|
||||
}
|
||||
function applyUpdate(){
|
||||
log('downloading...');
|
||||
var t=Android.fetchText(h5UrlRelative(dlUrl));
|
||||
if(typeof t!=='string'||t.startsWith('ERROR:')){log('ERR: '+t);return}
|
||||
var h=Android.sha256(t);
|
||||
if(h!==dlSha){log('SHA256 MISMATCH!');return}
|
||||
Android.saveCache(t);log('OK! restart app to apply')
|
||||
}
|
||||
function h5UrlRelative(u){var p=u.split('/ota/');return p.length>1?'h5/'+p[1].split('/h5/')[1]:u}
|
||||
document.getElementById('ver').innerText=Android.fetchManifest('stable').match(/"latest":"([^"]+)"/)[1];
|
||||
</script></body></html>
|
||||
Reference in New Issue
Block a user