Updated for Multi-Product
All checks were successful
build-binaries / build (, amd64, linux) (push) Has been skipped
build-binaries / build (, arm, 7, linux) (push) Has been skipped
build-binaries / build (, arm64, linux) (push) Has been skipped
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / release (push) Has been skipped
build-binaries / publish-agent (push) Has been skipped
release-tag / release-image (push) Successful in 2m7s
All checks were successful
build-binaries / build (, amd64, linux) (push) Has been skipped
build-binaries / build (, arm, 7, linux) (push) Has been skipped
build-binaries / build (, arm64, linux) (push) Has been skipped
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / release (push) Has been skipped
build-binaries / publish-agent (push) Has been skipped
release-tag / release-image (push) Successful in 2m7s
This commit is contained in:
412
admin.html
412
admin.html
@@ -1,187 +1,269 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Version Agent Admin</title>
|
||||
<style>
|
||||
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell;max-width:1000px;margin:40px auto;padding:0 16px}
|
||||
header{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}
|
||||
section{border:1px solid #ddd;border-radius:12px;padding:16px;margin-bottom:24px;box-shadow:0 1px 3px rgba(0,0,0,.05)}
|
||||
label{display:block;margin:.3rem 0 .1rem;color:#333}
|
||||
input,select,textarea{width:100%;padding:.5rem;border:1px solid #ccc;border-radius:8px}
|
||||
button{padding:.6rem 1rem;border:0;border-radius:10px;cursor:pointer}
|
||||
.btn{background:#111;color:#fff}
|
||||
.grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
|
||||
.assets{margin-top:8px}
|
||||
.asset-row{display:grid;grid-template-columns:2fr 2fr 1fr 1fr;gap:8px;margin-bottom:8px}
|
||||
.small{font-size:.9rem;color:#555}
|
||||
code{background:#f6f6f6;padding:.2rem .4rem;border-radius:6px}
|
||||
pre{background:#f6f6f6;padding:8px;border-radius:8px;overflow:auto}
|
||||
</style>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Version Agent Admin</title>
|
||||
<style>
|
||||
:root { --gap: 12px; }
|
||||
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell;max-width:1100px;margin:40px auto;padding:0 16px}
|
||||
header{display:flex;gap:24px;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;margin-bottom:24px}
|
||||
section{border:1px solid #ddd;border-radius:12px;padding:16px;margin-bottom:24px;box-shadow:0 1px 3px rgba(0,0,0,.05)}
|
||||
h1{font-size:1.4rem;margin:0 0 8px}
|
||||
h2{font-size:1.1rem;margin:.2rem 0 .8rem}
|
||||
label{display:block;margin:.3rem 0 .1rem;color:#333}
|
||||
input,select,textarea{width:100%;padding:.5rem;border:1px solid #ccc;border-radius:8px}
|
||||
button{padding:.6rem 1rem;border:0;border-radius:10px;cursor:pointer}
|
||||
.btn{background:#111;color:#fff}
|
||||
.muted{color:#666}
|
||||
.row{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--gap)}
|
||||
.row-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--gap)}
|
||||
.row-2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap)}
|
||||
.assets{margin-top:8px}
|
||||
.asset-row{display:grid;grid-template-columns:2fr 2fr 1fr 2fr;gap:8px;margin-bottom:8px}
|
||||
pre{background:#f6f6f6;padding:8px;border-radius:8px;overflow:auto;max-height:360px}
|
||||
.right{display:flex;gap:8px;align-items:end}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>Version Agent Admin</h1>
|
||||
<div>
|
||||
<label>API Token (for POST)</label>
|
||||
<input id="token" placeholder="Bearer Token" />
|
||||
<div class="small">will be saved in <code>localStorage</code></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<h2>Configuration</h2>
|
||||
<div class="grid">
|
||||
<header>
|
||||
<div>
|
||||
<label>Vendor</label><input id="vendor" />
|
||||
<h1>Version Agent Admin</h1>
|
||||
<div class="muted">Pflege mehrerer Produkte pro Vendor</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Product</label><input id="product" />
|
||||
<div class="right">
|
||||
<div>
|
||||
<label>API Token (für POST)</label>
|
||||
<input id="token" placeholder="Bearer Token" />
|
||||
<div class="muted">lokal gespeichert</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Produkt</label>
|
||||
<div class="row-2">
|
||||
<select id="product"></select>
|
||||
<button id="makeDefault" title="Als Default-Produkt setzen">Als Default</button>
|
||||
</div>
|
||||
<div class="muted">wird in <code>localStorage</code> gemerkt</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Default Branch</label><input id="defBranch" placeholder="eg. 12.x" />
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<h2>Produkt-Konfiguration</h2>
|
||||
<div class="row">
|
||||
<div>
|
||||
<label>Default Branch</label>
|
||||
<input id="defBranch" placeholder="z.B. 12.x" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Default Channel</label>
|
||||
<select id="defChannel"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Vendor (optional global)</label>
|
||||
<input id="vendor" placeholder="Vendor-Name" />
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;align-items:flex-end">
|
||||
<button class="btn" id="saveProductCfg">Produkt-Defaults speichern</button>
|
||||
<button id="saveVendorCfg">Vendor speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Default Channel</label>
|
||||
<select id="defChannel"></select>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Pflege: Latest setzen</h2>
|
||||
<div class="row">
|
||||
<div><label>Branch</label><input id="branch" placeholder="z.B. 12.x"/></div>
|
||||
<div><label>Channel</label><select id="channel"></select></div>
|
||||
<div><label>Arch</label><select id="arch"></select></div>
|
||||
<div><label>Bit</label><select id="bit"></select></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top:12px"><button class="btn" id="saveConfig">Save</button></div>
|
||||
</section>
|
||||
<div class="row" style="margin-top:var(--gap)">
|
||||
<div><label>OS</label><select id="os"></select></div>
|
||||
<div><label>Version</label><input id="version" placeholder="12.3.1"/></div>
|
||||
<div><label>Build</label><input id="build" placeholder="optional"/></div>
|
||||
<div><label>Released At (RFC3339)</label><input id="releasedAt" placeholder="2025-10-15T12:34:56Z"/></div>
|
||||
</div>
|
||||
<div style="margin-top:var(--gap)">
|
||||
<label>Notes URL</label><input id="notesUrl" placeholder="https://example.com/release-notes"/>
|
||||
</div>
|
||||
<div class="assets">
|
||||
<h3>Assets</h3>
|
||||
<div id="assets"></div>
|
||||
<button id="addAsset">Asset hinzufügen</button>
|
||||
</div>
|
||||
<div style="margin-top:var(--gap)"><button class="btn" id="publish">Publish</button> <button id="loadLatest">Aktuelles Latest laden</button></div>
|
||||
<pre id="log"></pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Maintenance: Set Latest</h2>
|
||||
<div class="grid">
|
||||
<div><label>Branch</label><input id="branch" placeholder="eg. 12.x"/></div>
|
||||
<div><label>Channel</label><select id="channel"></select></div>
|
||||
<div><label>Arch</label><select id="arch"></select></div>
|
||||
<div><label>Bit</label><select id="bit"></select></div>
|
||||
</div>
|
||||
<div class="grid" style="margin-top:12px">
|
||||
<div><label>OS</label><select id="os"></select></div>
|
||||
<div><label>Version</label><input id="version" placeholder="12.3.1"/></div>
|
||||
<div><label>Build</label><input id="build" placeholder="optional"/></div>
|
||||
<div><label>Released At (RFC3339)</label><input id="releasedAt" placeholder="2025-10-15T12:34:56Z"/></div>
|
||||
</div>
|
||||
<div style="margin-top:12px">
|
||||
<label>Notes URL</label><input id="notesUrl" placeholder="https://example.com/release-notes"/>
|
||||
</div>
|
||||
<div class="assets">
|
||||
<h3>Assets</h3>
|
||||
<div id="assets"></div>
|
||||
<button id="addAsset">Add Asset</button>
|
||||
</div>
|
||||
<div style="margin-top:12px"><button class="btn" id="publish">Publish</button></div>
|
||||
<div style="margin-top:12px">
|
||||
<button id="loadLatest">Load Latest Info</button>
|
||||
</div>
|
||||
<pre id="log"></pre>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Manifest (Produkt-spezifisch)</h2>
|
||||
<div class="muted">GET <code>/v1/manifest?product=...</code></div>
|
||||
<pre id="manifest"></pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Manifest</h2>
|
||||
<div class="small">ETag-aware GET <code>/v1/manifest</code></div>
|
||||
<pre id="manifest"></pre>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Neues Produkt anlegen</h2>
|
||||
<div class="row-3">
|
||||
<div><label>Produkt-Name</label><input id="newProdName" placeholder="z.B. wol-server"/></div>
|
||||
<div><label>Default Branch</label><input id="newProdBranch" placeholder="z.B. 1.x"/></div>
|
||||
<div><label>Default Channel</label><select id="newProdChannel"></select></div>
|
||||
</div>
|
||||
<div style="margin-top:var(--gap)"><button id="createProduct">Produkt anlegen/aktualisieren</button></div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const $ = sel => document.querySelector(sel);
|
||||
const log = (msg) => { const el=$('#log'); el.textContent = (new Date()).toISOString()+"\n"+msg; }
|
||||
<script>
|
||||
const $ = s => document.querySelector(s);
|
||||
const tokenHeader = () => { const t=$('#token').value.trim(); return t? { 'Authorization': 'Bearer '+t } : {}; }
|
||||
const api = (path, opt={}) => fetch(path, opt);
|
||||
const product = () => $('#product').value.trim();
|
||||
const log = (msg) => { const el=$('#log'); el.textContent = (new Date()).toISOString()+"\n"+msg; }
|
||||
|
||||
function tokenHeader(){ const t=$('#token').value.trim(); return t? { 'Authorization': 'Bearer '+t } : {}; }
|
||||
function fillSelect(sel, arr, val){ const s=$(sel); s.innerHTML=''; arr.forEach(v=>{ const o=document.createElement('option'); o.value=v; o.textContent=v; s.appendChild(o); }); if(val && arr.includes(val)) s.value=val; }
|
||||
|
||||
async function loadValues(){
|
||||
const r = await fetch('/v1/values'); const j = await r.json();
|
||||
const fill = (id, arr) => { const s=$(id); s.innerHTML=''; arr.forEach(v=>{ const o=document.createElement('option'); o.value=v; o.textContent=v; s.appendChild(o); }); };
|
||||
fill('#arch', j.arch); fill('#bit', j.bit); fill('#os', j.os); fill('#channel', j.channels); fill('#defChannel', j.channels);
|
||||
$('#defBranch').value = j.defaults.branch || '';
|
||||
$('#defChannel').value = j.defaults.channel || 'stable';
|
||||
$('#vendor').value = j.meta.vendor || '';
|
||||
$('#product').value = j.meta.product || '';
|
||||
}
|
||||
function addAssetRow(data={}){
|
||||
const wrap=document.createElement('div'); wrap.className='asset-row';
|
||||
wrap.innerHTML = `
|
||||
<input placeholder="URL" value="${data.url||''}"/>
|
||||
<input placeholder="SHA256" value="${data.sha256||''}"/>
|
||||
<input placeholder="Size (bytes)" value="${data.size_bytes||''}"/>
|
||||
<input placeholder="Signature URL" value="${data.signature_url||''}"/>
|
||||
`;
|
||||
$('#assets').appendChild(wrap);
|
||||
}
|
||||
|
||||
async function loadManifest(){ const r=await fetch('/v1/manifest'); const j=await r.json(); $('#manifest').textContent = JSON.stringify(j, null, 2); }
|
||||
async function loadProducts(){
|
||||
const r = await api('/v1/products');
|
||||
const j = await r.json();
|
||||
const list = j.products || [];
|
||||
const def = j.default || '';
|
||||
const last = localStorage.getItem('product') || '';
|
||||
const chosen = list.includes(last) ? last : (def || list[0] || '');
|
||||
fillSelect('#product', list, chosen);
|
||||
localStorage.setItem('product', chosen||'');
|
||||
await Promise.all([loadValues(), loadManifest()]);
|
||||
}
|
||||
|
||||
function addAssetRow(data={}){
|
||||
const wrap=document.createElement('div'); wrap.className='asset-row';
|
||||
wrap.innerHTML = `
|
||||
<input placeholder="URL" value="${data.url||''}"/>
|
||||
<input placeholder="SHA256" value="${data.sha256||''}"/>
|
||||
<input placeholder="Size (bytes)" value="${data.size_bytes||''}"/>
|
||||
<input placeholder="Signature URL" value="${data.signature_url||''}"/>
|
||||
`;
|
||||
$('#assets').appendChild(wrap);
|
||||
}
|
||||
async function loadValues(){
|
||||
const r = await api('/v1/values?product='+encodeURIComponent(product()));
|
||||
const j = await r.json();
|
||||
fillSelect('#arch', j.arch||[]);
|
||||
fillSelect('#bit', j.bit||[]);
|
||||
fillSelect('#os', j.os||[]);
|
||||
fillSelect('#channel', j.channels||[], (j.defaults && j.defaults.channel) || 'stable');
|
||||
fillSelect('#defChannel', j.channels||[], (j.defaults && j.defaults.channel) || 'stable');
|
||||
fillSelect('#newProdChannel', j.channels||[], 'stable');
|
||||
|
||||
async function publish(){
|
||||
const assets = Array.from(document.querySelectorAll('.asset-row')).map(row=>{
|
||||
const [url,sha,size,sig] = row.querySelectorAll('input');
|
||||
const a={ url:url.value.trim(), sha256:sha.value.trim() };
|
||||
if(size.value.trim()) a.size_bytes = parseInt(size.value.trim(),10);
|
||||
if(sig.value.trim()) a.signature_url = sig.value.trim();
|
||||
return a;
|
||||
}).filter(a=>a.url && a.sha256);
|
||||
$('#defBranch').value = (j.defaults && j.defaults.branch) || '';
|
||||
$('#vendor').value = (j.meta && j.meta.vendor) || '';
|
||||
|
||||
const payload = {
|
||||
branch: $('#branch').value.trim() || $('#defBranch').value.trim(),
|
||||
channel: $('#channel').value,
|
||||
arch: $('#arch').value,
|
||||
bit: $('#bit').value,
|
||||
os: $('#os').value,
|
||||
release: {
|
||||
version: $('#version').value.trim(),
|
||||
build: $('#build').value.trim(),
|
||||
released_at: $('#releasedAt').value.trim(),
|
||||
notes_url: $('#notesUrl').value.trim(),
|
||||
assets
|
||||
}
|
||||
};
|
||||
// Vorauswahl für Publish-Form
|
||||
$('#branch').value = $('#defBranch').value;
|
||||
$('#assets').innerHTML=''; addAssetRow();
|
||||
}
|
||||
|
||||
const r = await fetch('/v1/publish', { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt); await loadManifest();
|
||||
}
|
||||
async function loadManifest(){
|
||||
const p = product();
|
||||
const r = await api('/v1/manifest'+(p? ('?product='+encodeURIComponent(p)) : ''));
|
||||
const j = await r.json();
|
||||
$('#manifest').textContent = JSON.stringify(j, null, 2);
|
||||
}
|
||||
|
||||
async function loadLatest(){
|
||||
const params = new URLSearchParams({
|
||||
branch: $('#branch').value.trim() || $('#defBranch').value.trim(),
|
||||
channel: $('#channel').value,
|
||||
arch: $('#arch').value,
|
||||
bit: $('#bit').value,
|
||||
os: $('#os').value
|
||||
});
|
||||
const r = await fetch('/v1/latest?'+params.toString());
|
||||
if(!r.ok){ log('not found'); return; }
|
||||
const j = await r.json();
|
||||
$('#version').value = j.release.version || '';
|
||||
$('#build').value = j.release.build || '';
|
||||
$('#releasedAt').value = (j.release.released_at||'');
|
||||
$('#notesUrl').value = j.release.notes_url || '';
|
||||
$('#assets').innerHTML='';
|
||||
(j.release.assets||[]).forEach(a=>addAssetRow({ url:a.url, sha256:a.sha256, size_bytes:a.size_bytes||'', signature_url:a.signature_url||'' }));
|
||||
if((j.release.assets||[]).length===0) addAssetRow();
|
||||
}
|
||||
async function saveProductCfg(){
|
||||
const p = product(); if(!p){ alert('Kein Produkt gewählt'); return }
|
||||
const payload = { DefaultBranch: $('#defBranch').value.trim(), DefaultChannel: $('#defChannel').value };
|
||||
const r = await api('/v1/config?product='+encodeURIComponent(p), { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt); await Promise.all([loadValues(), loadManifest()]);
|
||||
}
|
||||
|
||||
async function saveConfig(){
|
||||
const payload = {
|
||||
vendor: $('#vendor').value.trim(),
|
||||
product: $('#product').value.trim(),
|
||||
default_branch: $('#defBranch').value.trim(),
|
||||
default_channel: $('#defChannel').value
|
||||
};
|
||||
const r = await fetch('/v1/config', { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt); await loadValues(); await loadManifest();
|
||||
}
|
||||
async function saveVendorCfg(){
|
||||
const payload = { Vendor: $('#vendor').value.trim() };
|
||||
const r = await api('/v1/config', { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt);
|
||||
}
|
||||
|
||||
// init
|
||||
(function(){
|
||||
$('#token').value = localStorage.getItem('apiToken')||'';
|
||||
$('#token').addEventListener('input', e=> localStorage.setItem('apiToken', e.target.value));
|
||||
$('#addAsset').addEventListener('click', e=>{ e.preventDefault(); addAssetRow(); });
|
||||
$('#publish').addEventListener('click', e=>{ e.preventDefault(); publish(); });
|
||||
$('#loadLatest').addEventListener('click', e=>{ e.preventDefault(); loadLatest(); });
|
||||
$('#saveConfig').addEventListener('click', e=>{ e.preventDefault(); saveConfig(); });
|
||||
addAssetRow(); loadValues(); loadManifest();
|
||||
})();
|
||||
</script>
|
||||
</body></html>
|
||||
async function makeDefault(){
|
||||
const p = product(); if(!p){ alert('Kein Produkt gewählt'); return }
|
||||
const payload = { DefaultProduct: p };
|
||||
const r = await api('/v1/config', { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt); await loadProducts();
|
||||
}
|
||||
|
||||
async function createProduct(){
|
||||
const name = $('#newProdName').value.trim(); if(!name){ alert('Produkt-Name fehlt'); return }
|
||||
const payload = { product: name, default_branch: $('#newProdBranch').value.trim(), default_channel: $('#newProdChannel').value };
|
||||
const r = await api('/v1/products', { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt);
|
||||
await loadProducts();
|
||||
$('#product').value = name; localStorage.setItem('product', name);
|
||||
await Promise.all([loadValues(), loadManifest()]);
|
||||
}
|
||||
|
||||
async function publish(){
|
||||
const p = product(); if(!p){ alert('Kein Produkt gewählt'); return }
|
||||
const assets = Array.from(document.querySelectorAll('.asset-row')).map(row=>{
|
||||
const [url,sha,size,sig] = row.querySelectorAll('input');
|
||||
const a={ url:url.value.trim(), sha256:sha.value.trim() };
|
||||
if(size.value.trim()) a.size_bytes = parseInt(size.value.trim(),10);
|
||||
if(sig.value.trim()) a.signature_url = sig.value.trim();
|
||||
return a;
|
||||
}).filter(a=>a.url && a.sha256);
|
||||
|
||||
const payload = {
|
||||
branch: $('#branch').value.trim() || $('#defBranch').value.trim(),
|
||||
channel: $('#channel').value,
|
||||
arch: $('#arch').value,
|
||||
bit: $('#bit').value,
|
||||
os: $('#os').value,
|
||||
release: {
|
||||
version: $('#version').value.trim(),
|
||||
build: $('#build').value.trim(),
|
||||
released_at: $('#releasedAt').value.trim(),
|
||||
notes_url: $('#notesUrl').value.trim(),
|
||||
assets
|
||||
}
|
||||
};
|
||||
|
||||
const r = await api('/v1/publish?product='+encodeURIComponent(p), { method:'POST', headers:{ 'Content-Type':'application/json', ...tokenHeader() }, body: JSON.stringify(payload) });
|
||||
const txt = await r.text(); log(txt); await loadManifest();
|
||||
}
|
||||
|
||||
async function loadLatest(){
|
||||
const p = product(); if(!p){ alert('Kein Produkt gewählt'); return }
|
||||
const params = new URLSearchParams({
|
||||
product: p,
|
||||
branch: $('#branch').value.trim() || $('#defBranch').value.trim(),
|
||||
channel: $('#channel').value,
|
||||
arch: $('#arch').value,
|
||||
bit: $('#bit').value,
|
||||
os: $('#os').value
|
||||
});
|
||||
const r = await api('/v1/latest?'+params.toString());
|
||||
if(!r.ok){ log('not found'); return; }
|
||||
const j = await r.json();
|
||||
$('#version').value = j.release.version || '';
|
||||
$('#build').value = j.release.build || '';
|
||||
$('#releasedAt').value = (j.release.released_at||'');
|
||||
$('#notesUrl').value = j.release.notes_url || '';
|
||||
$('#assets').innerHTML='';
|
||||
(j.release.assets||[]).forEach(a=>addAssetRow({ url:a.url, sha256:a.sha256, size_bytes:a.size_bytes||'', signature_url:a.signature_url||'' }));
|
||||
if((j.release.assets||[]).length===0) addAssetRow();
|
||||
}
|
||||
|
||||
// init
|
||||
(function(){
|
||||
$('#token').value = localStorage.getItem('apiToken')||'';
|
||||
$('#token').addEventListener('input', e=> localStorage.setItem('apiToken', e.target.value));
|
||||
$('#product').addEventListener('change', async e=>{ localStorage.setItem('product', e.target.value); await Promise.all([loadValues(), loadManifest()]); });
|
||||
$('#addAsset').addEventListener('click', e=>{ e.preventDefault(); addAssetRow(); });
|
||||
$('#publish').addEventListener('click', e=>{ e.preventDefault(); publish(); });
|
||||
$('#loadLatest').addEventListener('click', e=>{ e.preventDefault(); loadLatest(); });
|
||||
$('#saveProductCfg').addEventListener('click', e=>{ e.preventDefault(); saveProductCfg(); });
|
||||
$('#saveVendorCfg').addEventListener('click', e=>{ e.preventDefault(); saveVendorCfg(); });
|
||||
$('#makeDefault').addEventListener('click', e=>{ e.preventDefault(); makeDefault(); });
|
||||
$('#createProduct').addEventListener('click', e=>{ e.preventDefault(); createProduct(); });
|
||||
addAssetRow(); loadProducts();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user