1 line
31 KiB
JavaScript
1 line
31 KiB
JavaScript
import{B as e,C as t,E as n,H as r,K as i,M as a,P as o,R as s,S as c,W as l,_ as u,a as d,d as f,h as p,k as m,l as h,m as g,n as _,o as v,r as y,t as b,u as x,v as S,w as C,x as w}from"./ui-CzB-gjVP.js";import{n as ee,t as te}from"./eye-DaP7VxYZ.js";import{i as T,n as ne,r as re,t as E}from"./users-D7ZZ6mzL.js";import{t as ie}from"./hash-ORiZzY9V.js";import{t as D}from"./log-out-BSFDyM4m.js";import{t as ae}from"./message-square-1ZGuav88.js";import{t as O}from"./radio-tower-UMy9OSpZ.js";import{n as oe,t as se}from"./user-round-BkRPX9Ri.js";import{t as k}from"./shield-check-PAk2q1NC.js";import{t as A}from"./video-C1co3G53.js";import{n as ce}from"./index-BTRwyDO1.js";var j=[[`path`,{d:`M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z`,key:`1s6t7t`}],[`circle`,{cx:`16.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`w0ekpg`}]],M=e=>s(S,i(e,{iconNode:j,name:`key-round`})),N=[[`path`,{d:`M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z`,key:`zw3jo`}],[`path`,{d:`M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12`,key:`1wduqc`}],[`path`,{d:`M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17`,key:`kqbvx6`}]],P=e=>s(S,i(e,{iconNode:N,name:`layers`})),F=[[`path`,{d:`M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8`,key:`v9h5vc`}],[`path`,{d:`M21 3v5h-5`,key:`1q7to0`}],[`path`,{d:`M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16`,key:`3uifl3`}],[`path`,{d:`M8 16H3v5`,key:`1cv678`}]],le=e=>s(S,i(e,{iconNode:F,name:`refresh-cw`})),ue=[[`path`,{d:`M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z`,key:`1c8476`}],[`path`,{d:`M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7`,key:`1ydtos`}],[`path`,{d:`M7 3v4a1 1 0 0 0 1 1h7`,key:`t51u73`}]],de=e=>s(S,i(e,{iconNode:ue,name:`save`})),I=[[`path`,{d:`M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915`,key:`1i5ecw`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]],fe=e=>s(S,i(e,{iconNode:I,name:`settings`})),L=[[`path`,{d:`M10 5H3`,key:`1qgfaw`}],[`path`,{d:`M12 19H3`,key:`yhmn1j`}],[`path`,{d:`M14 3v4`,key:`1sua03`}],[`path`,{d:`M16 17v4`,key:`1q0r14`}],[`path`,{d:`M21 12h-9`,key:`1o4lsq`}],[`path`,{d:`M21 19h-5`,key:`1rlt1p`}],[`path`,{d:`M21 5h-7`,key:`1oszz2`}],[`path`,{d:`M8 10v4`,key:`tgpxqk`}],[`path`,{d:`M8 12H3`,key:`a7s4jb`}]],pe=e=>s(S,i(e,{iconNode:L,name:`sliders-horizontal`})),R=[[`path`,{d:`M10 11v6`,key:`nco0om`}],[`path`,{d:`M14 11v6`,key:`outv1u`}],[`path`,{d:`M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6`,key:`miytrc`}],[`path`,{d:`M3 6h18`,key:`d0wm0j`}],[`path`,{d:`M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2`,key:`e791ji`}]],me=e=>s(S,i(e,{iconNode:R,name:`trash-2`})),z=m(`<p class="mt-1 text-xs text-muted-foreground">`),B=m(`<div class="relative border border-line bg-card/60 p-4"><div class="flex items-start justify-between gap-3"><div><p class="micro-label text-muted-foreground"></p><p class="display mt-2 text-3xl"></p></div><span class="flex size-8 items-center justify-center border border-line bg-background text-muted-foreground">`),he=m(`<div class="flex flex-wrap items-start justify-between gap-4"><div><p class="mono-label text-muted-foreground">manage / console</p><h1 class="display mt-2 text-4xl sm:text-5xl">管理控制台</h1><p class="mt-3 max-w-2xl text-sm text-muted-foreground">统一管理房间、角色与权限。参考 <span class="font-mono text-acid-cyan">SyncTV</span> 的位掩码与 Allow/Deny 覆盖模型, 房主拥有全部权限,管理员/成员/访客基于角色默认值 + 个人覆盖计算生效权限。</p></div><div class="flex items-center gap-2">`),ge=m(`<span class="flex items-center gap-2">`),_e=m(`<button class="text-xs underline">关闭`),ve=m(`<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">`),ye=m(`<p class="py-6 text-center text-sm text-muted-foreground">暂无房间数据`),be=m(`<div class="grid gap-4 lg:grid-cols-3"><div class="border border-line bg-card/50 p-4 lg:col-span-2"><div class="flex items-center justify-between gap-3"><h3 class=font-semibold>房间分发状态</h3><span class="mono-label text-muted-foreground"> rooms</span></div><div class="mt-4 space-y-2"></div></div><div class="border border-line bg-card/50 p-4"><h3 class=font-semibold>后端能力</h3><div class="mt-4 space-y-2"></div><div class="mt-6 rounded border border-acid-lime/20 bg-acid-lime/5 p-3"><p class="micro-label text-muted-foreground">权限模型</p><p class="mt-1 text-xs leading-relaxed text-muted-foreground">角色默认值来自 <span class="font-mono text-acid-cyan">RoomSettings</span>,个人覆盖分<span class="font-mono text-foreground"> added / removed </span>且访客仅可分配 Guest 可用位。生效权限 = base | added & ~removed。`),xe=m(`<p class="px-4 py-8 text-center text-sm text-muted-foreground">暂无房间`),Se=m(`<div class="relative border border-line bg-card/60 p-4"><div class="flex flex-wrap items-start justify-between gap-3"><div class=min-w-0><p class="mono-label text-muted-foreground">room / </p><h3 class="display mt-1 truncate text-2xl"></h3><p class="mt-1 text-xs text-muted-foreground">创建者 <!> · </p></div><div class="flex flex-wrap gap-2"></div></div><div class="mt-4 grid gap-3 sm:grid-cols-2"><div class=space-y-1.5><label class=label>描述</label><input class=field placeholder=房间描述></div><div class=space-y-1.5><label class=label>人数上限</label><input class="field field-mono"></div></div><div class="mt-3 flex flex-wrap gap-2">`),Ce=m(`<p class="px-4 py-6 text-center text-sm text-muted-foreground">暂无成员`),we=m(`<div class="border border-line bg-card/50"><div class="flex items-center justify-between border-b border-line/80 px-4 py-3"><h4 class="flex items-center gap-2 font-semibold">成员 </h4><span class="micro-label text-muted-foreground">点击角色可切换 · 权限可在“权限”页批量编辑</span></div><div class="divide-y divide-line/60">`),Te=m(`<div class="mt-4 grid gap-4 sm:grid-cols-2"><label class="flex items-center justify-between gap-3 border border-line bg-background px-3 py-2.5"><span class="flex items-center gap-2 text-sm">聊天启用</span><input type=checkbox></label><label class="flex items-center justify-between gap-3 border border-line bg-background px-3 py-2.5"><span class="flex items-center gap-2 text-sm">允许访客加入</span><input type=checkbox></label><label class="flex items-center justify-between gap-3 border border-line bg-background px-3 py-2.5"><span class="flex items-center gap-2 text-sm">需审批入房</span><input type=checkbox></label><label class="flex items-center justify-between gap-3 border border-line bg-background px-3 py-2.5"><span class="flex items-center gap-2 text-sm">公开可见</span><input type=checkbox>`),Ee=m(`<div class="border border-line bg-card/50 p-4"><h4 class="flex items-center gap-2 font-semibold">房间设置`),De=m(`<div class="grid gap-4 lg:grid-cols-3"><div class=lg:col-span-1><div class="relative border border-line bg-card/60 p-4"><h3 class=font-semibold>创建房间</h3><p class="mt-1 text-xs text-muted-foreground">房主将自动成为 Creator,拥有全部权限。</p><div class="mt-4 space-y-3"><div class=space-y-1.5><label class=label>房间名 *</label><div class="flex items-center gap-2 border border-line bg-background px-3 py-2 focus-within:border-ring"><input class="w-full bg-transparent font-mono text-sm outline-none placeholder:text-muted-foreground/50"placeholder="如 live-101"></div></div><div class=space-y-1.5><label class=label>描述</label><input class=field placeholder=一句话介绍></div><div class="grid grid-cols-2 gap-3"><div class=space-y-1.5><label class=label>可见性</label></div><div class=space-y-1.5><label class=label>人数上限</label><input class="field field-mono"></div></div><div class=space-y-1.5><label class=label>房间密码(可选)</label><div class="flex items-center gap-2 border border-line bg-background px-3 py-2 focus-within:border-ring"><input class="w-full bg-transparent font-mono text-sm outline-none"placeholder=留空为无密码><button type=button class=text-muted-foreground></button></div></div></div></div><div class="mt-4 border border-line bg-card/50"><div class="flex items-center justify-between border-b border-line/80 px-4 py-3"><h3 class=font-semibold>房间列表</h3><span class="mono-label text-muted-foreground"></span></div><div class="max-h-[480px] overflow-auto"></div></div></div><div class="space-y-4 lg:col-span-2">`),Oe=m(`<p class="px-4 py-6 text-center text-sm text-muted-foreground">该房间暂无成员`),ke=m(`<div class=overflow-auto><table class="w-full min-w-[900px] border-collapse"><thead><tr class="border-b border-line bg-muted/20"><th class="px-3 py-2 text-left text-xs font-mono text-muted-foreground">成员 / 角色</th></tr></thead><tbody>`),Ae=m(`<p class="px-4 py-3 text-xs text-muted-foreground">点击格子切换该成员的 added/removed 覆盖位。房主始终拥有全部权限,不可修改。`),je=m(`<div class=space-y-4><div class="border border-line bg-card/60 p-4"><h3 class=font-semibold>角色默认权限(参考 SyncTV)</h3><p class="mt-1 text-xs text-muted-foreground">Creator = 全部 · Admin 缺 delete_room · Member 为发布订阅开箱即用 · Guest 仅只读,额外仅可被授予 send_chat。</p><div class="mt-4 overflow-auto"><table class="w-full min-w-[720px] border-collapse text-xs"><thead><tr class="border-b border-line"><th class="px-2 py-2 text-left font-mono text-muted-foreground">permission</th></tr></thead><tbody></tbody></table></div></div><div class="border border-line bg-card/50"><div class="flex flex-wrap items-center justify-between gap-3 border-b border-line/80 px-4 py-3"><h3 class=font-semibold>成员生效权限 · `),Me=m(`<div class="border border-line bg-card/50"><div class="flex items-center justify-between border-b border-line/80 px-4 py-3"><h3 class="flex items-center gap-2 font-semibold">系统用户与全局角色</h3><span class="mono-label text-muted-foreground"> users`),Ne=m(`<span class="text-xs leading-relaxed">全局角色(viewer/publisher/admin)由 Casbin 控制接口准入;房间内角色(creator/admin/member/guest)由房间位掩码控制具体动作。二者正交:全局 admin 可管理所有房间,但房间内权限仍以 effective 计算结果为准。`),Pe=m(`<div class="grid gap-4 lg:grid-cols-2"><div class="relative border border-line bg-card/60 p-4"><h3 class="flex items-center gap-2 font-semibold">系统与 SFU</h3><div class="mt-4 space-y-3 text-sm"><div class="flex items-center justify-between border border-line bg-background px-3 py-2.5"><span class=text-muted-foreground>HTTP 端口</span><span class=font-mono></span></div><div class="flex items-center justify-between border border-line bg-background px-3 py-2.5"><span class=text-muted-foreground>gRPC</span><span class=font-mono>9090</span></div></div></div><div class="border border-line bg-card/60 p-4"><h3 class="flex items-center gap-2 font-semibold">安全与默认策略</h3><div class="mt-4 space-y-2 text-xs leading-relaxed text-muted-foreground"><p><span class="font-mono text-foreground">JWT_SECRET</span> 必须显式配置;推流 token 使用<span class="font-mono text-foreground"> HS256 </span>签发,TTL 默认 2h。</p><p>私密房间支持密码;公开房间受 <span class="font-mono text-foreground">is_public</span> 与 <span class="font-mono text-foreground">allow_guest_join</span> 联动控制访客可见性。</p><p>权限存储为 <span class="font-mono text-foreground">u64 bits</span>(兼容 Postgres BIGINT), 访客可分配集合受 <span class="font-mono text-foreground">guest_assignable</span> 约束。</p><div class="mt-3 border border-dashed border-line bg-background px-3 py-3"><p class=micro-label>设计参考</p><p class=mt-1>本系统在 <code>internal/room</code> 实现 SyncTV 风格的四角色(creator/admin/member/guest)与四列覆盖位 (added/removed/admin_added/admin_removed),并通过 RoomSettings 维护角色默认值,可平滑扩展至更多权限位而不改表结构。`),Fe=m(`<div class="border border-line bg-card/50 p-4"><h3 class=font-semibold>快捷操作</h3><div class="mt-4 flex flex-wrap gap-2">`),Ie=m(`<div class="mx-auto max-w-[1400px] space-y-6 px-4 py-8 sm:px-6 sm:py-10">`),Le=m(`<div class="flex items-center justify-between gap-3 border border-line/60 bg-background/50 px-3 py-2.5"><div class="flex min-w-0 items-center gap-2.5"><span class="truncate font-mono text-sm"></span></div><div class="flex items-center gap-3 text-xs text-muted-foreground"><span class="hidden sm:inline"> 成员</span><span> targets`),Re=m(`<div class="flex items-center justify-between border border-line/60 px-3 py-2.5"><span class="font-mono text-xs uppercase">`),ze=m(`<button type=button><div class=min-w-0><p class="truncate font-mono text-sm font-medium"></p><p class="truncate text-xs text-muted-foreground"></p></div><div class="flex shrink-0 flex-col items-end gap-1"><span class="micro-label text-muted-foreground"> 成员`),Be=m(`<div class="border border-dashed border-line p-8 text-center text-sm text-muted-foreground">选择一个房间以管理`),Ve=m(`<div class="flex flex-wrap items-center gap-3 px-4 py-3"><span></span><div class="min-w-0 flex-1"><p class="truncate font-mono text-sm font-medium"></p><p class="micro-label truncate text-muted-foreground">`),He=m(`<p class="mt-3 text-sm text-muted-foreground">加载中…`),Ue=m(`<th class="px-2 py-2 text-center font-mono text-muted-foreground">`),We=m(`<tr class="border-b border-line/60"><td class="px-2 py-2 font-mono">`),Ge=m(`<td class="px-2 py-2 text-center">`),Ke=m(`<span class="inline-flex size-2 bg-acid-lime">`),qe=m(`<span class="inline-flex size-2 border border-line">`),Je=m(`<p class="px-4 py-8 text-center text-sm text-muted-foreground">请选择房间`),Ye=m(`<th class="px-1 py-2 text-center text-[10px] font-mono text-muted-foreground">`),Xe=m(`<tr class="border-b border-line/40"><td class="px-3 py-2"><div class="flex items-center gap-2"><span class="font-mono text-xs font-medium">`),Ze=m(`<td class="px-1 py-1 text-center"><button type=button>`),Qe=m(`<p class="px-5 py-8 text-sm text-muted-foreground">暂无用户`),$e=m(`<div><div class="hidden grid-cols-[minmax(0,1fr)_8rem_10rem] gap-4 border-b border-line/80 px-5 py-3 sm:grid"><span class="micro-label text-muted-foreground">username</span><span class="micro-label text-muted-foreground">global role</span><span class="micro-label text-right text-muted-foreground">切换角色`),et=m(`<div class="grid grid-cols-1 items-center gap-3 px-5 py-4 sm:grid-cols-[minmax(0,1fr)_8rem_10rem] sm:gap-4 border-b border-line/60 last:border-0"><div class="flex items-center gap-3"><span></span><div class=min-w-0><p class="truncate font-mono text-sm font-medium"></p><p class="micro-label text-muted-foreground"></p></div></div><div class="flex justify-end">`),tt=m(`<div class="flex items-center justify-between border border-line bg-background px-3 py-2.5"><span class="flex items-center gap-2">`),V=[`send_chat`,`view_chat_history`,`view_members`,`subscribe`,`watch_room`,`publish`,`stop_stream`,`broadcast`,`manage_members`,`add_members`,`remove_members`,`manage_permissions`,`manage_room`,`delete_room`,`delete_chat_messages`,`control_stream`],H={send_chat:`发弹幕`,view_chat_history:`看记录`,view_members:`看成员`,subscribe:`拉流`,watch_room:`监听`,publish:`推流`,stop_stream:`停播`,broadcast:`广播`,manage_members:`管成员`,add_members:`邀成员`,remove_members:`踢人`,manage_permissions:`管权限`,manage_room:`管房间`,delete_room:`删房间`,delete_chat_messages:`删消息`,control_stream:`控流`},U={creator:{label:`房主`,color:`border-amber-400/40 bg-amber-400/10 text-amber-600`},admin:{label:`管理员`,color:`border-acid-magenta/30 bg-acid-magenta/10 text-acid-magenta`},member:{label:`成员`,color:`border-acid-cyan/30 bg-acid-cyan/10 text-acid-cyan`},guest:{label:`访客`,color:`border-line bg-muted text-muted-foreground`}};function W(e){let n=e.icon;return(()=>{var r=B(),i=r.firstChild,a=i.firstChild,c=a.firstChild,l=c.nextSibling,u=a.nextSibling;return t(r,s(d,{}),i),t(c,()=>e.label),t(l,()=>e.value),t(a,s(o,{get when(){return e.hint},get children(){var n=z();return t(n,()=>e.hint),n}}),null),t(u,s(n,{class:`size-4`})),r})()}var G=()=>{let[i,c]=l(`overview`),[m,S]=l(null),[j,N]=l([]),[F,ue]=l(null),[I,L]=l(``),[R,z]=l([]),[B,G]=l([]),[nt,K]=l(``),[rt,it]=l(!1),[q,at]=l(``),[ot,st]=l(``),[ct,lt]=l(!0),[ut,dt]=l(`100`),[ft,pt]=l(``),[mt,ht]=l(!1),[J,gt]=l(null),[_t,vt]=l(``),[yt,bt]=l(``),[Y,X]=l(null),Z=async()=>{try{let e=await u.manageOverview();S(e)}catch(e){K(String(e?.message||e))}},Q=async()=>{try{let e=await u.manageRooms();N(e.rooms||[]),!I()&&e.rooms?.length&&L(e.rooms[0].room.name)}catch(e){K(String(e?.message||e))}},xt=async()=>{try{let e=await u.managePermMatrix();ue(e)}catch{}},St=async()=>{try{let e=await u.listUsers();G(e.users||[])}catch(e){K(String(e?.message||e))}},$=async e=>{if(e)try{let t=await u.manageMembers(e);z(t.members||[]);let n=await u.manageRoomDetail(e);gt(n.room),vt(n.room.description||``),bt(String(n.room.max_members||100)),X(n.room.settings||null)}catch(e){K(String(e?.message||e))}};e(()=>{Z(),Q(),xt(),St()}),e(()=>{let e=I();e&&(i()===`rooms`||i()===`permissions`)&&$(e)});let Ct=async()=>{let e=q().trim();if(e){it(!0);try{await u.manageCreateRoom({name:e,description:ot().trim()||void 0,is_public:ct(),max_members:parseInt(ut())||100,password:ft().trim()||void 0}),at(``),st(``),pt(``),await Q(),await Z()}catch(e){K(String(e?.message||e))}finally{it(!1)}}},wt=async()=>{let e=I();if(!(!e||!J()))try{await u.manageUpdateRoom(e,{description:_t(),max_members:parseInt(yt())||void 0,is_public:J().is_public}),await Q(),await $(e)}catch(e){K(String(e?.message||e))}},Tt=async e=>{if(confirm(`确定删除房间 ${e} ?该操作不可恢复。`))try{await u.manageDeleteRoom(e),await Q(),await Z()}catch(e){K(String(e?.message||e))}},Et=async(e,t,n)=>{try{await u.manageUpdateMemberRole(e,t,n),await $(e)}catch(e){K(String(e?.message||e))}},Dt=async(e,t)=>{if(confirm(`踢出 ${t} ?`))try{await u.manageKickMember(e,t),await $(e)}catch(e){K(String(e?.message||e))}},Ot=async(e,t,n,r)=>{let i=1<<V.indexOf(n),a=R().find(e=>e.username===t);if(!a)return;let o=Number(a.added||0),s=Number(a.removed||0);r?(o&=~i,s|=i):(o|=i,s&=~i);try{await u.manageUpdateMemberPerms(e,t,o,s,!1),await $(e)}catch(e){K(String(e?.message||e))}},kt=async()=>{let e=I();if(!(!e||!Y()))try{await u.manageUpdateRoomSettings(e,Y()),await $(e)}catch(e){K(String(e?.message||e))}};return(()=>{var e=Ie();return t(e,s(h,{get children(){var e=he(),n=e.firstChild.nextSibling;return t(n,s(_,{variant:`outline`,class:`gap-1.5`,get children(){return[s(k,{class:`size-3.5`}),C(()=>p.user()?.username||`guest`),` · `,C(()=>g.role())]}}),null),t(n,s(y,{variant:`ghost`,class:`h-9 gap-1.5`,onClick:()=>{Z(),Q(),St(),xt(),I()&&$(I())},get children(){return[s(le,{class:`size-4`}),`刷新`]}}),null),e}}),null),t(e,s(o,{get when(){return nt()},get children(){return s(b,{tone:`error`,class:`flex items-center justify-between gap-3`,get children(){return[(()=>{var e=ge();return t(e,s(oe,{class:`size-4`}),null),t(e,nt,null),e})(),(()=>{var e=_e();return e.$$click=()=>K(``),e})()]}})}}),null),t(e,s(h,{delay:1,get children(){return s(x,{get value(){return i()},onChange:c,get options(){return[{value:`overview`,label:`概览`,icon:s(P,{class:`size-3.5`})},{value:`rooms`,label:`房间`,icon:s(O,{class:`size-3.5`})},{value:`permissions`,label:`权限`,icon:s(M,{class:`size-3.5`})},{value:`users`,label:`用户`,icon:s(E,{class:`size-3.5`})},{value:`system`,label:`系统`,icon:s(fe,{class:`size-3.5`})}]}})}}),null),t(e,s(o,{get when(){return i()===`overview`},get children(){return[(()=>{var e=ve();return t(e,s(W,{label:`房间总数`,get value(){return m()?.rooms_total??`-`},get hint(){return`${m()?.rooms_live??0} 个正在直播`},icon:P}),null),t(e,s(W,{label:`在线房间`,get value(){return m()?.rooms_live??`-`},hint:`有推流目标的房间`,icon:A}),null),t(e,s(W,{label:`房间成员`,get value(){return m()?.members_total??`-`},hint:`所有房间成员汇总`,icon:E}),null),t(e,s(W,{label:`系统用户`,get value(){return m()?.users_total??`-`},hint:`已注册账号`,icon:k}),null),e})(),(()=>{var e=be(),n=e.firstChild,r=n.firstChild,i=r.firstChild.nextSibling,c=i.firstChild,l=r.nextSibling,u=n.nextSibling.firstChild.nextSibling;return t(i,()=>j().length,c),t(l,s(a,{get each(){return j().slice(0,6)},children:e=>(()=>{var n=Le(),r=n.firstChild,i=r.firstChild,a=r.nextSibling.firstChild,o=a.firstChild,c=a.nextSibling,l=c.firstChild;return t(r,s(v,{get active(){return e.live}}),i),t(i,()=>e.room.name),t(r,s(_,{get variant(){return e.live?`success`:`muted`},class:`hidden sm:inline-flex`,get children(){return e.live?`LIVE`:`IDLE`}}),null),t(a,()=>e.member_count,o),t(c,()=>e.targets?.length??0,l),n})()}),null),t(l,s(o,{get when(){return j().length===0},get children(){return ye()}}),null),t(u,s(a,{get each(){return m()?.backends||[]},children:e=>(()=>{var n=Re(),r=n.firstChild;return t(r,()=>e.name||e.kind),t(n,s(_,{get variant(){return e.configured?`success`:`muted`},get children(){return e.configured?`已配置`:`未配置`}}),null),n})()})),e})()]}}),null),t(e,s(o,{get when(){return i()===`rooms`},get children(){var e=De(),i=e.firstChild,c=i.firstChild,l=c.firstChild,u=l.nextSibling.nextSibling,p=u.firstChild,m=p.firstChild.nextSibling,h=m.firstChild,g=p.nextSibling,b=g.firstChild.nextSibling,x=g.nextSibling,S=x.firstChild;S.firstChild;var D=S.nextSibling.firstChild.nextSibling,O=x.nextSibling.firstChild.nextSibling,k=O.firstChild,A=k.nextSibling,M=c.nextSibling.firstChild,N=M.firstChild.nextSibling,P=M.nextSibling,F=i.nextSibling;return t(c,s(d,{}),l),t(m,s(ie,{class:`size-4 shrink-0 text-muted-foreground`}),h),h.$$input=e=>at(e.currentTarget.value),b.$$input=e=>st(e.currentTarget.value),t(S,s(f,{get value(){return ct()?`public`:`private`},onChange:e=>lt(e===`public`),options:[{value:`public`,label:`公开`},{value:`private`,label:`私密`}]}),null),D.$$input=e=>dt(e.currentTarget.value),t(O,s(re,{class:`size-4 shrink-0 text-muted-foreground`}),k),k.$$input=e=>pt(e.currentTarget.value),A.$$click=()=>ht(e=>!e),t(A,s(o,{get when(){return mt()},get fallback(){return s(te,{class:`size-4`})},get children(){return s(ee,{class:`size-4`})}})),t(u,s(y,{class:`w-full`,onClick:Ct,get disabled(){return rt()||!q().trim()},get children(){return[s(ne,{class:`size-4`}),`创建房间`]}}),null),t(N,()=>j().length),t(P,s(a,{get each(){return j()},children:e=>(()=>{var n=ze(),i=n.firstChild,a=i.firstChild,o=a.nextSibling,c=i.nextSibling,l=c.firstChild,u=l.firstChild;return n.$$click=()=>L(e.room.name),t(a,()=>e.room.name),t(o,()=>e.room.description||`—`),t(c,s(v,{get active(){return e.live}}),l),t(l,()=>e.member_count,u),r(()=>w(n,`flex w-full items-center justify-between gap-3 border-b border-line/60 px-4 py-3 text-left transition-colors last:border-0 hover:bg-accent/40 ${I()===e.room.name?`bg-acid-lime/10 dark:bg-primary/10`:``}`)),n})()}),null),t(P,s(o,{get when(){return j().length===0},get children(){return xe()}}),null),t(F,s(o,{get when(){return I()},get fallback(){return Be()},get children(){return[(()=>{var e=Se(),n=e.firstChild,i=n.firstChild,a=i.firstChild;a.firstChild;var c=a.nextSibling,l=c.nextSibling,u=l.firstChild.nextSibling;u.nextSibling;var f=i.nextSibling,p=n.nextSibling,m=p.firstChild,h=m.firstChild.nextSibling,g=m.nextSibling.firstChild.nextSibling,v=p.nextSibling;return t(e,s(d,{}),n),t(a,I,null),t(c,()=>J()?.display_name||I()),t(l,()=>J()?.creator||`—`,u),t(l,()=>new Date((J()?.created_at||0)*1e3).toLocaleString()||``,null),t(f,s(_,{get variant(){return J()?.is_public?`success`:`warning`},class:`gap-1`,get children(){return[s(o,{get when(){return J()?.is_public},get fallback(){return s(re,{class:`size-3`})},get children(){return s(T,{class:`size-3`})}}),C(()=>J()?.is_public?`公开`:`私密`)]}}),null),t(f,s(_,{variant:`outline`,get children(){return J()?.status||`active`}}),null),h.$$input=e=>vt(e.currentTarget.value),g.$$input=e=>bt(e.currentTarget.value),t(v,s(y,{class:`gap-1.5`,onClick:wt,get children(){return[s(de,{class:`size-4`}),`保存`]}}),null),t(v,s(y,{variant:`danger`,class:`gap-1.5`,onClick:()=>Tt(I()),get children(){return[s(me,{class:`size-4`}),`删除房间`]}}),null),r(()=>h.value=_t()),r(()=>g.value=yt()),e})(),(()=>{var e=we(),n=e.firstChild,i=n.firstChild,c=i.firstChild,l=n.nextSibling;return t(i,s(E,{class:`size-4`}),c),t(i,()=>R().length,null),t(l,s(a,{get each(){return R()},children:e=>(()=>{var n=Ve(),i=n.firstChild,a=i.nextSibling.firstChild,c=a.nextSibling;return t(i,()=>(e.username[0]||`?`).toUpperCase()),t(a,()=>e.username),t(c,()=>e.effective_permissions?.join(`, `)||``),t(n,s(f,{class:`w-28`,get value(){return e.role},onChange:t=>Et(I(),e.username,t),options:[{value:`creator`,label:`房主`},{value:`admin`,label:`管理员`},{value:`member`,label:`成员`},{value:`guest`,label:`访客`}]}),null),t(n,s(_,{get class(){return U[e.role]?.color},get children(){return U[e.role]?.label||e.role}}),null),t(n,s(o,{get when(){return e.role!==`creator`},get children(){return s(y,{variant:`ghost`,class:`h-8 px-2 text-destructive hover:text-destructive`,onClick:()=>Dt(I(),e.username),get children(){return s(ce,{class:`size-4`})}})}}),null),r(()=>w(i,`flex size-8 items-center justify-center border font-mono text-xs font-bold ${U[e.role]?.color||``}`)),n})()}),null),t(l,s(o,{get when(){return R().length===0},get children(){return Ce()}}),null),e})(),(()=>{var e=Ee(),n=e.firstChild,i=n.firstChild;return t(n,s(pe,{class:`size-4`}),i),t(e,s(o,{get when(){return Y()},get fallback(){return He()},get children(){return[(()=>{var e=Te(),n=e.firstChild,i=n.firstChild,a=i.firstChild,o=i.nextSibling,c=n.nextSibling,l=c.firstChild,u=l.firstChild,d=l.nextSibling,f=c.nextSibling,p=f.firstChild,m=p.firstChild,h=p.nextSibling,g=f.nextSibling.firstChild,_=g.firstChild,v=g.nextSibling;return t(i,s(ae,{class:`size-4 text-muted-foreground`}),a),o.$$input=e=>X({...Y(),chat_enabled:e.currentTarget.checked}),t(l,s(se,{class:`size-4 text-muted-foreground`}),u),d.$$input=e=>X({...Y(),allow_guest_join:e.currentTarget.checked}),t(p,s(oe,{class:`size-4 text-muted-foreground`}),m),h.$$input=e=>X({...Y(),require_approval:e.currentTarget.checked}),t(g,s(T,{class:`size-4 text-muted-foreground`}),_),v.$$input=e=>X({...Y(),is_public:e.currentTarget.checked}),r(()=>o.checked=!!Y()?.chat_enabled),r(()=>d.checked=!!Y()?.allow_guest_join),r(()=>h.checked=!!Y()?.require_approval),r(()=>v.checked=!!Y()?.is_public),e})(),s(y,{class:`mt-4 gap-1.5`,onClick:kt,get children(){return[s(de,{class:`size-4`}),`保存设置`]}})]}}),null),e})()]}})),r(()=>n(k,`type`,mt()?`text`:`password`)),r(()=>h.value=q()),r(()=>b.value=ot()),r(()=>D.value=ut()),r(()=>k.value=ft()),e}}),null),t(e,s(o,{get when(){return i()===`permissions`},get children(){var e=je(),i=e.firstChild,c=i.firstChild.nextSibling.nextSibling.firstChild.firstChild,l=c.firstChild;l.firstChild;var u=c.nextSibling,d=i.nextSibling,p=d.firstChild,m=p.firstChild;return m.firstChild,t(l,s(a,{each:[`creator`,`admin`,`member`,`guest`],children:e=>(()=>{var n=Ue();return t(n,e),n})()}),null),t(u,s(a,{each:V,children:e=>(()=>{var n=We(),r=n.firstChild;return t(r,()=>H[e]||e),t(n,s(a,{each:[`creator`,`admin`,`member`,`guest`],children:n=>{let r=F()?.roles?.[n]?.perms?.includes(e);return(()=>{var e=Ge();return t(e,r?Ke():qe()),e})()}}),null),n})()})),t(m,()=>I()||`未选择房间`,null),t(p,s(f,{get value(){return I()},onChange:L,get options(){return j().map(e=>({value:e.room.name,label:e.room.name}))},class:`w-48`}),null),t(d,s(o,{get when(){return I()},get fallback(){return Je()},get children(){return[(()=>{var e=ke(),i=e.firstChild.firstChild,c=i.firstChild;c.firstChild;var l=i.nextSibling;return t(c,s(a,{each:V,children:e=>(()=>{var n=Ye();return t(n,()=>H[e]||e),n})()}),null),t(l,s(a,{get each(){return R()},children:e=>(()=>{var i=Xe(),o=i.firstChild.firstChild,c=o.firstChild;return t(c,()=>e.username),t(o,s(_,{get class(){return`text-[10px] ${U[e.role]?.color||``}`},get children(){return e.role}}),null),t(i,s(a,{each:V,children:i=>{let a=e.effective_permissions?.includes(i),o=e.role===`creator`;return(()=>{var s=Ze(),c=s.firstChild;return c.$$click=()=>Ot(I(),e.username,i,!!a),c.disabled=o,w(c,`inline-flex size-6 items-center justify-center border text-[10px] transition-colors ${a?`border-acid-lime bg-acid-lime text-black`:`border-line bg-background text-muted-foreground hover:border-ring`} ${o?`opacity-50`:``}`),t(c,a?`✓`:``),r(()=>n(c,`title`,`${e.username} · ${i} · ${a?`已授予`:`未授予`}`)),s})()}}),null),i})()})),t(e,s(o,{get when(){return R().length===0},get children(){return Oe()}}),null),e})(),Ae()]}}),null),e}}),null),t(e,s(o,{get when(){return i()===`users`},get children(){return[(()=>{var e=Me(),n=e.firstChild.firstChild,i=n.firstChild,c=n.nextSibling,l=c.firstChild;return t(n,s(E,{class:`size-4`}),i),t(c,()=>B().length,l),t(e,s(o,{get when(){return!g.can(`user:list`)},get fallback(){return(()=>{var e=$e();return e.firstChild,t(e,s(a,{get each(){return B()},children:e=>(()=>{var n=et(),i=n.firstChild,a=i.firstChild,o=a.nextSibling.firstChild,c=o.nextSibling,l=i.nextSibling;return t(a,()=>(e.username[0]||`?`).toUpperCase()),t(o,()=>e.username),t(c,()=>new Date((e.created_at||0)*1e3).toLocaleDateString()),t(n,s(_,{get class(){return U[e.role]?.color||``},get children(){return e.role}}),l),t(l,s(f,{get value(){return e.role},onChange:async t=>{try{await u.updateUserRole(e.username,t),G(n=>n.map(n=>n.username===e.username?{...n,role:t}:n))}catch(e){K(String(e?.message||e))}},options:[{value:`viewer`,label:`viewer`},{value:`publisher`,label:`publisher`},{value:`admin`,label:`admin`}]})),r(()=>w(a,`flex size-8 items-center justify-center border font-mono text-xs font-bold ${U[e.role]?.color||`border-line bg-muted`}`)),n})()}),null),t(e,s(o,{get when(){return B().length===0},get children(){return Qe()}}),null),e})()},get children(){return s(b,{tone:`warning`,class:`m-4`,children:`无权限查看:需要 user:list(管理员)`})}}),null),e})(),s(b,{tone:`info`,get children(){return Ne()}})]}}),null),t(e,s(o,{get when(){return i()===`system`},get children(){return[(()=>{var e=Pe(),n=e.firstChild,r=n.firstChild,i=r.firstChild,o=r.nextSibling,c=o.firstChild,l=c.firstChild.nextSibling;c.nextSibling;var u=n.nextSibling.firstChild,f=u.firstChild;return t(n,s(d,{}),r),t(r,s(fe,{class:`size-4`}),i),t(l,()=>m()?.backends?`8088`:`-`),t(o,s(a,{get each(){return m()?.backends||[]},children:e=>(()=>{var n=tt(),r=n.firstChild;return t(r,s(O,{class:`size-4 text-muted-foreground`}),null),t(r,()=>e.name||e.kind,null),t(n,s(_,{get variant(){return e.configured?`success`:`muted`},get children(){return e.configured?`已配置`:`未配置`}}),null),n})()}),null),t(u,s(M,{class:`size-4`}),f),e})(),(()=>{var e=Fe(),n=e.firstChild.nextSibling;return t(n,s(y,{variant:`outline`,onClick:()=>c(`rooms`),get children(){return[s(ne,{class:`size-4`}),`去创建房间`]}}),null),t(n,s(y,{variant:`outline`,onClick:()=>c(`permissions`),get children(){return[s(M,{class:`size-4`}),`查看权限矩阵`]}}),null),t(n,s(y,{variant:`ghost`,onClick:()=>window.open(`/api/config`,`_blank`),get children(){return[s(T,{class:`size-4`}),`查看 /api/config`]}}),null),t(n,s(y,{variant:`ghost`,onClick:()=>{localStorage.clear(),location.reload()},get children(){return[s(D,{class:`size-4`}),`清理本地缓存`]}}),null),e})()]}}),null),e})()};c([`click`,`input`]);export{G as Manage}; |