live-sfu-demo/internal/server/static/assets/manage-BUOThicK.js

1 line
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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};