Add admin official account connection UI and persistent session secrets
This commit is contained in:
@@ -81,9 +81,24 @@ async function officialContentInfo(){
|
||||
try {
|
||||
const data=await api('/api/v1/devices/'+state.device+'/widgets');
|
||||
const label=data.source==='live'?(zh?'实时目录':'Live catalog'):data.source==='mock'?'Mock':(zh?'历史目录 · '+data.observed_at:'Catalog snapshot · '+data.observed_at);
|
||||
const currentLabel=!data.configured?(zh?'尚未授权官方账号,当前状态不可读取。':'Official account not authorized; current state is unavailable.'):data.status==='NO_WIDGET'?(zh?'未使用官方小组件':'No official widget selected'):data.current_verified&&data.current?((zh?'当前小组件:':'Current widget: ')+data.current.name):(zh?'当前小组件尚未核实':'Current widget is unverified');
|
||||
const currentLabel=data.status==='DEVICE_REQUIRED'?(zh?'官方账号已连接,请先关联画框':'Official account connected; link a frame first'):!data.configured?(zh?'尚未授权官方账号,当前状态不可读取。':'Official account not authorized; current state is unavailable.'):data.status==='NO_WIDGET'?(zh?'未使用官方小组件':'No official widget selected'):data.current_verified&&data.current?((zh?'当前小组件:':'Current widget: ')+data.current.name):(zh?'当前小组件尚未核实':'Current widget is unverified');
|
||||
info.innerHTML=`<summary>${zh?'官方小组件':'Official widgets'} · ${data.catalog.length}${!data.configured?(zh?' · 需官方账号授权':' · Official account required'):''}</summary><p>${esc(label)}</p><p class="notice">${esc(currentLabel)}</p><p class="notice widget-auth-notice">${zh?'如需使用官方小组件,需要另外授权 KoKonna 官方账号登录信息。现有 API Key 接入继续用于设备状态、图片和图库,不受影响。':'Official widgets require separate KoKonna account authorization. Your existing API Key continues to support device status, images and gallery.'}</p><p>${zh?'此入口只读,不会切换或取消官方订阅。':'Read only. This does not change official subscriptions.'}</p><input type="search" aria-label="${zh?'搜索小组件':'Search widgets'}" placeholder="${zh?'搜索名称或分类':'Search name or category'}"><div class="widget-list"></div>`;
|
||||
const render=()=>{const query=info.querySelector('input').value.toLowerCase();info.querySelector('.widget-list').innerHTML=data.catalog.filter(x=>(x.name+' '+x.tags.join(' ')).toLowerCase().includes(query)).map(x=>`<article class="card"><strong>${esc(x.name)}</strong><p>${esc(x.tags.join(' · '))}</p><small>${esc(x.arg_keys.join(' · '))}</small></article>`).join('');};
|
||||
info.querySelector('input').oninput=render;render();
|
||||
info.querySelector('input').oninput=render;render();officialConnectionPanel(info);
|
||||
}catch(e){info.querySelector('p').textContent=e.message;}
|
||||
}
|
||||
|
||||
async function officialConnectionPanel(info){
|
||||
const zh=state.lang==='zh',path='/api/v1/devices/'+state.device+'/official/connection';
|
||||
const panel=document.createElement('section');panel.className='official-account-panel';info.querySelector('.widget-auth-notice').after(panel);
|
||||
const render=connection=>{
|
||||
panel.innerHTML=connection.authorized?`<h3>${zh?'官方账号已连接':'Official account connected'}</h3><p>${esc(connection.username)}</p><p>${connection.robot_id?(zh?'已关联画框':'Frame linked'):(zh?'请选择并关联你的画框':'Choose and link your frame')}</p><div class="actions"><button id="official-discover" type="button">${zh?'查询账号设备':'Discover devices'}</button><button id="official-disconnect" type="button">${zh?'断开连接':'Disconnect'}</button></div><div id="official-device-picker"></div>`:`<h3>${zh?'连接 KoKonna 官方账号':'Connect KoKonna official account'}</h3><p>${zh?'此处不是 Frame Flow 或 NAS 账号。密码不保存,登录会话会保存为 NAS 上受限的 secret 文件。连接仅查询,不切换小组件。':'Use your KoKonna account, not Frame Flow or NAS credentials. Passwords are not saved; the session is stored in a restricted NAS secret file. Connecting only queries data.'}</p><form id="official-login-form"><div class="field"><label for="official-username">${zh?'官方账号':'Official username'}</label><input id="official-username" required maxlength="120" autocomplete="username"></div><div class="field"><label for="official-password">${zh?'官方密码':'Official password'}</label><input id="official-password" required maxlength="256" type="password" autocomplete="off"></div><button type="submit" class="primary">${zh?'授权连接':'Authorize connection'}</button></form>`;
|
||||
if(connection.authorized){
|
||||
$('#official-disconnect').onclick=busy(async()=>{render(await api(path,{method:'DELETE'}));toast(zh?'已断开官方连接':'Official connection disconnected');});
|
||||
const picker=devices=>{const box=$('#official-device-picker');box.innerHTML=devices.length?`<form id="official-select-form"><div class="field"><label for="official-device">${zh?'官方账号下的画框':'Frames in your official account'}</label><select id="official-device">${devices.map(d=>`<option value="${d.id}" ${d.id===connection.robot_id?'selected':''}>${esc(d.name)} · ${esc(d.model_code||'')} · ${d.online?'ONLINE':'OFFLINE'}</option>`).join('')}</select></div><button type="submit" class="primary">${zh?'关联到当前 Frame Flow 设备':'Link to this Frame Flow device'}</button></form>`:`<p>${zh?'账号下没有可关联设备':'No devices found in this account'}</p>`;if(devices.length)$('#official-select-form').onsubmit=busy(async e=>{e.preventDefault();await api(path,{method:'PUT',body:{robot_id:Number($('#official-device').value)}});await navigate('templates');});};
|
||||
$('#official-discover').onclick=busy(async()=>{picker((await api(path+'?discover=true')).devices);});
|
||||
if(connection.devices.length)picker(connection.devices);
|
||||
}else $('#official-login-form').onsubmit=busy(async e=>{e.preventDefault();const username=$('#official-username').value.trim(),password=$('#official-password').value;$('#official-password').value='';render(await api(path,{method:'POST',body:{username,password}}));});
|
||||
};
|
||||
try{render(await api(path));}catch{panel.textContent=zh?'官方账号连接由管理员管理':'Official account connection is managed by an administrator';}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user