koishi-plugin-new-auth 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -29
- package/dist/index.js +1 -1
- package/dist/style.css +1 -1
- package/lib/index.d.ts +23 -9
- package/lib/index.js +104 -42
- package/newauth.md +4 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# koishi-plugin-new-auth
|
|
2
2
|
|
|
3
|
-
`koishi-plugin-new-auth`
|
|
3
|
+
`koishi-plugin-new-auth` 是一个保守的 Koishi 指令权限层,按 `newauth.md` 中的设计实现“角色 + 作用域 + 指令策略”。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
当前版本已经覆盖:
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- Koishi Console
|
|
7
|
+
- 指令注册后进入权限表;
|
|
8
|
+
- 新发现的指令默认是 `pending`;
|
|
9
|
+
- 待配置指令默认只有 Bot 管理员可执行;
|
|
10
|
+
- 旧 `authority` 只记录为建议,不直接放权;
|
|
11
|
+
- 策略按 `scope + role + command` 判断;
|
|
12
|
+
- 群主、群管理员、群成员与 Bot 管理员分离;
|
|
13
|
+
- 自定义角色、显式成员、角色权限复制;
|
|
14
|
+
- Koishi Console 中的 `⌗ 新权限` WebUI。
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## 配置
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
export default {
|
|
@@ -25,13 +25,13 @@ export default {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
`botAdmins`
|
|
28
|
+
`botAdmins` 是显式 Bot 实例管理员,平台群主不会自动成为 Bot 管理员。
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
为了兼容旧实例,`trustLegacyAuthorityAsAdmin` 打开时,`authority >= legacyAdminAuthority` 的 Koishi 用户会被视为 Bot 管理员。这个兼容只来自 Koishi 用户权限,不来自平台群主身份。
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## 管理指令
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
所有管理指令都在 `newauth` 下。
|
|
35
35
|
|
|
36
36
|
```txt
|
|
37
37
|
newauth.commands --pending
|
|
@@ -39,6 +39,7 @@ newauth.roles
|
|
|
39
39
|
newauth.allow <roleId> <command> [scope]
|
|
40
40
|
newauth.deny <roleId> <command> [scope]
|
|
41
41
|
newauth.inherit <roleId> <command> [scope]
|
|
42
|
+
newauth.assign <command> <roleIds> [scope]
|
|
42
43
|
newauth.disable <command>
|
|
43
44
|
newauth.enable <command>
|
|
44
45
|
newauth.admin.add <uid>
|
|
@@ -46,18 +47,22 @@ newauth.admin.remove <uid>
|
|
|
46
47
|
newauth.role.create <id> <name> [scopeType]
|
|
47
48
|
newauth.member.add <roleId> <uid> [scope]
|
|
48
49
|
newauth.member.remove <roleId> <uid> [scope]
|
|
50
|
+
newauth.role.copy <sourceRoleId> <targetRoleId> [scope]
|
|
51
|
+
newauth.plugin.assign <plugin> <target> [scope]
|
|
49
52
|
```
|
|
50
53
|
|
|
51
|
-
`uid`
|
|
54
|
+
`uid` 使用 `platform:userId`,例如 `onebot:10000`。
|
|
52
55
|
|
|
53
|
-
`scope`
|
|
56
|
+
`scope` 可以是:
|
|
54
57
|
|
|
55
58
|
- `global`
|
|
56
59
|
- `guild:<platform>:<guildId>`
|
|
57
60
|
|
|
58
|
-
|
|
61
|
+
省略 `scope` 时,策略指令默认写入 `global`。
|
|
59
62
|
|
|
60
|
-
|
|
63
|
+
`newauth.assign` 与待配置 WebUI 的角色勾选行为一致:列出的角色写为允许,其他角色在该作用域下写为关闭,并把待配置指令变为已配置。
|
|
64
|
+
|
|
65
|
+
## 内置角色
|
|
61
66
|
|
|
62
67
|
```txt
|
|
63
68
|
bot-admin
|
|
@@ -67,19 +72,23 @@ guild-member
|
|
|
67
72
|
guest
|
|
68
73
|
```
|
|
69
74
|
|
|
70
|
-
|
|
75
|
+
自定义角色不继承其他角色。要让自定义角色拥有某些指令,需要显式写策略,或在 WebUI / `newauth.role.copy` 中复制另一个角色当前的显式策略。
|
|
71
76
|
|
|
72
77
|
## WebUI
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
安装 Koishi Console 后打开 `⌗ 新权限` 页面。
|
|
80
|
+
|
|
81
|
+
页面提供:
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
- 以角色为入口的指令权限编辑;
|
|
84
|
+
- 全局默认和单群作用域切换;
|
|
85
|
+
- 待配置指令审查和角色勾选分配;
|
|
86
|
+
- 旧 `authority` 建议与 AI 风格自动分配建议;
|
|
87
|
+
- 指令状态和群内自治开关;
|
|
88
|
+
- 自定义角色创建、显式成员管理;
|
|
89
|
+
- 从其他角色复制权限;
|
|
90
|
+
- 按插件批量采用建议、给群成员、给群管理员或仅给 Bot 管理员。
|
|
77
91
|
|
|
78
|
-
|
|
79
|
-
- global and guild scope switching;
|
|
80
|
-
- pending command review;
|
|
81
|
-
- legacy `authority` suggestions;
|
|
82
|
-
- AI-style automatic assignment for pending commands;
|
|
83
|
-
- command status and guild override toggles.
|
|
92
|
+
WebUI 是 Koishi 实例管理员界面,Console 入口和所有事件都要求 `authority: 4`。群主自治只应影响允许自治的单群指令,不等于进入实例管理面板。
|
|
84
93
|
|
|
85
|
-
|
|
94
|
+
自动分配策略默认保守:实例级或高危操作只给 `bot-admin`,群管理操作给 `guild-admin` 和 `guild-owner`,普通用户功能给 `guild-member` 及以上,无法判断时回退到旧 `authority` 建议。
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{defineComponent as H,ref as v,computed as c,watch as J,openBlock as o,createElementBlock as a,createElementVNode as t,toDisplayString as s,withDirectives as y,Fragment as m,renderList as x,vModelSelect as U,normalizeClass as g,createCommentVNode as G,vModelCheckbox as K,createTextVNode as T,vModelText as D}from"vue";import{store as E,send as k}from"@koishijs/client";const Q={class:"new-auth-page"},W={class:"topbar"},X={class:"top-actions"},Y=["value"],Z=["disabled"],ee=["disabled"],te={class:"tabs"},le={key:0,class:"error"},ne={key:1,class:"role-layout"},se={class:"role-list"},oe=["onClick"],ae={key:0,class:"detail"},ie={class:"detail-head"},ue={class:"check"},de={class:"filters"},re={class:"command-list"},ce={class:"command-main"},ve={class:"state-buttons"},pe=["onClick"],ge=["onClick"],he=["onClick"],_e={key:2,class:"pending-grid"},fe={class:"row-actions"},be=["disabled","onClick"],ye=["disabled","onClick"],ke={key:0,class:"empty"},Ce={key:3,class:"commands-table"},we={class:"filters"},me={class:"command-main"},xe={class:"command-controls"},Ae={class:"check"},Ie=["checked","onChange"],Ve=["value","onChange"],$e=H({__name:"new-auth",setup(C){const i=v("roles"),u=v("global"),d=v(""),p=v(""),h=v("all"),$=v(false),_=v(false),A=v(""),w=c(()=>E.newauth||{roles:[],commands:[],policies:[],scopes:[{id:"global",name:"全局默认"}],pendingCount:0}),I=c(()=>w.value.roles),P=c(()=>w.value.commands),L=c(()=>w.value.policies),B=c(()=>{var n;return(n=w.value.scopes)!=null&&n.length?w.value.scopes:[{id:"global",name:"全局默认"}]}),V=c(()=>P.value.filter(n=>n.status==="pending")),f=c(()=>I.value.find(n=>n.id===d.value));J(I,n=>{!d.value&&n.length&&(d.value=n[0].id)},{immediate:true});const M=c(()=>{const n=p.value.toLowerCase();return P.value.filter(l=>h.value!=="all"&&l.status!==h.value?false:n?[l.id,l.name,l.commandPath,l.plugin,l.description].some(e=>e==null?void 0:e.toLowerCase().includes(n)):true)}),F=c(()=>f.value?M.value.filter(n=>{var l,e;return $.value||((l=f.value)==null?void 0:l.id)==="bot-admin"||((e=f.value)==null?void 0:e.scopeType)==="global"?true:n.allowGuildOverride}):[]);function S(n){const l=L.value.find(e=>e.scope===u.value&&e.roleId===d.value&&e.commandId===n);return(l==null?void 0:l.state)||"inherit"}async function b(n){_.value=true,A.value="";try{await n}catch(l){A.value=l instanceof Error?l.message:String(l)}finally{_.value=false}}async function R(){return b((async()=>{const n=await k("newauth/getData");E.newauth=n})())}function O(n,l){return b(k("newauth/setPolicy",{scope:u.value,roleId:d.value,commandId:n,state:l}))}function N(n,l){return b(k("newauth/applySuggestion",{commandId:n,scope:u.value,mode:l}))}function q(){return b(k("newauth/autoAssignPending",{scope:u.value,mode:"advisor"}))}function z(n,l){const e=l.target;return b(k("newauth/setGuildOverride",{commandId:n.id,allowGuildOverride:e.checked}))}function j(n,l){const e=l.target;return b(k("newauth/setCommandStatus",{commandId:n.id,status:e.value}))}return(n,l)=>(o(),a("main",Q,[t("header",W,[t("div",null,[l[9]||(l[9]=t("h1",null,"新权限",-1)),t("p",null,s(I.value.length)+" 个角色 / "+s(P.value.length)+" 个指令 / "+s(V.value.length)+" 个待配置",1)]),t("div",X,[y(t("select",{"onUpdate:modelValue":l[0]||(l[0]=e=>u.value=e),class:"scope-select"},[(o(true),a(m,null,x(B.value,e=>(o(),a("option",{key:e.id,value:e.id},s(e.name),9,Y))),128))],512),[[U,u.value]]),t("button",{class:"primary",disabled:_.value||!V.value.length,onClick:q}," AI 自动分配待配置 ",8,Z),t("button",{disabled:_.value,onClick:R},"刷新",8,ee)])]),t("nav",te,[t("button",{class:g({active:i.value==="roles"}),onClick:l[1]||(l[1]=e=>i.value="roles")},"角色",2),t("button",{class:g({active:i.value==="pending"}),onClick:l[2]||(l[2]=e=>i.value="pending")},"待配置",2),t("button",{class:g({active:i.value==="commands"}),onClick:l[3]||(l[3]=e=>i.value="commands")},"指令",2)]),A.value?(o(),a("p",le,s(A.value),1)):G("v-if",true),i.value==="roles"?(o(),a("section",ne,[t("aside",se,[(o(true),a(m,null,x(I.value,e=>(o(),a("button",{key:e.id,class:g(["role-item",{active:e.id===d.value}]),onClick:r=>d.value=e.id},[t("span",null,[t("strong",null,s(e.name),1),t("small",null,s(e.builtin?"内置角色":"自定义角色")+" / "+s(e.scopeType),1)]),t("em",null,s(e.allowCount),1)],10,oe))),128))]),f.value?(o(),a("section",ae,[t("div",ie,[t("div",null,[t("h2",null,s(f.value.name),1),t("p",null,s(f.value.id),1)]),t("label",ue,[y(t("input",{type:"checkbox","onUpdate:modelValue":l[4]||(l[4]=e=>$.value=e)},null,512),[[K,$.value]]),l[10]||(l[10]=T(" 审计模式 ",-1))])]),t("div",de,[y(t("input",{"onUpdate:modelValue":l[5]||(l[5]=e=>p.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[D,p.value,void 0,{trim:true}]]),y(t("select",{"onUpdate:modelValue":l[6]||(l[6]=e=>h.value=e)},[...l[11]||(l[11]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[U,h.value]])]),t("div",re,[(o(true),a(m,null,x(F.value,e=>(o(),a("article",{key:e.id,class:"command-row"},[t("div",ce,[t("strong",null,s(e.name),1),t("span",null,s(e.commandPath),1),t("small",null,s(e.plugin)+" / legacy "+s(e.legacyAuthority)+" / "+s(e.suggestion.label),1)]),t("div",ve,[t("button",{class:g({active:S(e.id)==="inherit"}),onClick:r=>O(e.id,"inherit")}," 继承 ",10,pe),t("button",{class:g({active:S(e.id)==="allow"}),onClick:r=>O(e.id,"allow")}," 开 ",10,ge),t("button",{class:g({active:S(e.id)==="deny"}),onClick:r=>O(e.id,"deny")}," 关 ",10,he)])]))),128))])])):G("v-if",true)])):i.value==="pending"?(o(),a("section",_e,[(o(true),a(m,null,x(V.value,e=>(o(),a("article",{key:e.id,class:"pending-item"},[t("div",null,[t("strong",null,s(e.name),1),t("span",null,s(e.commandPath),1),t("small",null,s(e.plugin)+" / legacy "+s(e.legacyAuthority),1)]),t("dl",null,[l[12]||(l[12]=t("dt",null,"旧建议",-1)),t("dd",null,s(e.suggestion.label),1),l[13]||(l[13]=t("dt",null,"AI 建议",-1)),t("dd",null,s(e.autoAssign.label)+":"+s(e.autoAssign.reason),1)]),t("div",fe,[t("button",{disabled:_.value,onClick:r=>N(e.id,"legacy")},"采用旧建议",8,be),t("button",{class:"primary",disabled:_.value,onClick:r=>N(e.id,"advisor")},"采用 AI 建议",8,ye)])]))),128)),V.value.length?G("v-if",true):(o(),a("p",ke,"没有待配置指令。"))])):(o(),a("section",Ce,[t("div",we,[y(t("input",{"onUpdate:modelValue":l[7]||(l[7]=e=>p.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[D,p.value,void 0,{trim:true}]]),y(t("select",{"onUpdate:modelValue":l[8]||(l[8]=e=>h.value=e)},[...l[14]||(l[14]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[U,h.value]])]),(o(true),a(m,null,x(M.value,e=>(o(),a("article",{key:e.id,class:"command-row"},[t("div",me,[t("strong",null,s(e.name),1),t("span",null,s(e.id),1),t("small",null,s(e.plugin)+" / "+s(e.commandPath)+" / "+s(e.autoAssign.label),1)]),t("div",xe,[t("label",Ae,[t("input",{type:"checkbox",checked:e.allowGuildOverride,onChange:r=>z(e,r)},null,40,Ie),l[15]||(l[15]=T(" 群内自治 ",-1))]),t("select",{value:e.status,onChange:r=>j(e,r)},[...l[16]||(l[16]=[t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],40,Ve)])]))),128))]))]))}}),Pe=(C,i)=>{const u=C.__vccOpts||C;for(const[d,p]of i)u[d]=p;return u},Se=Pe($e,[["__scopeId","data-v-b49c6f52"]]),Ge=C=>{C.page({name:"新权限",path:"/new-auth",icon:"⌗",fields:["newauth"],authority:4,component:Se})};export{Ge as default};
|
|
1
|
+
import{defineComponent as _e,ref as d,computed as c,watch as W,openBlock as s,createElementBlock as u,createElementVNode as t,toDisplayString as o,withDirectives as p,Fragment as k,renderList as y,vModelSelect as C,normalizeClass as w,createCommentVNode as L,withModifiers as ee,vModelText as T,vModelCheckbox as Ce,createTextVNode as F}from"vue";import{store as le,send as b}from"@koishijs/client";const we={class:"new-auth-page"},Ie={class:"topbar"},$e={class:"top-actions"},Se=["value"],Pe=["disabled"],Re=["disabled"],Ve={class:"tabs"},Ae={key:0,class:"error"},Ue={key:1,class:"role-layout"},xe={class:"role-list"},Me=["onClick"],Te=["disabled"],Oe={key:0,class:"detail"},Ge={class:"detail-head"},Ne={class:"detail-tools"},Be=["value"],De=["disabled"],Ee={class:"check"},Le={class:"member-panel"},Fe={class:"member-list"},Ke=["disabled","onClick"],qe=["value"],ze=["disabled"],je={class:"filters"},He={class:"command-list"},Je={class:"command-main"},Qe={class:"state-buttons"},We=["onClick"],Xe=["onClick"],Ye=["onClick"],Ze={key:2,class:"pending-grid"},el={class:"role-picker"},ll=["checked","onChange"],tl={class:"row-actions"},nl=["disabled","onClick"],ol=["disabled","onClick"],sl=["disabled","onClick"],ul={key:0,class:"empty"},il={key:3,class:"commands-table"},al={class:"filters"},dl={class:"plugin-bulk"},rl=["value"],vl=["disabled"],cl=["disabled"],pl=["disabled"],gl=["disabled"],bl={class:"command-main"},fl={class:"command-controls"},kl={class:"check"},yl=["checked","onChange"],hl=["value","onChange"],ml=_e({__name:"new-auth",setup(A){const f=d("roles"),a=d("global"),v=d(""),h=d(""),I=d("all"),K=d(false),r=d(false),O=d(""),$=d(""),U=d(""),q=d("guild"),x=d(""),G=d("global"),N=d(""),m=d(""),_=d({}),S=c(()=>le.newauth||{roles:[],commands:[],policies:[],members:[],scopes:[{id:"global",name:"全局默认"}],pendingCount:0}),P=c(()=>S.value.roles),B=c(()=>S.value.commands),z=c(()=>S.value.policies),te=c(()=>S.value.members),ne=c(()=>{var n;return(n=S.value.scopes)!=null&&n.length?S.value.scopes:[{id:"global",name:"全局默认"}]}),R=c(()=>B.value.filter(n=>n.status==="pending")),V=c(()=>P.value.find(n=>n.id===v.value)),X=c(()=>te.value.filter(n=>n.roleId===v.value)),oe=c(()=>P.value.filter(n=>n.id!==v.value)),se=c(()=>[...new Set(B.value.map(n=>n.plugin))].sort());W(P,n=>{!v.value&&n.length&&(v.value=n[0].id)},{immediate:true}),W(a,n=>{G.value=n==="global"?"global":n}),W([R,z,a],()=>{for(const n of R.value)D(n)},{immediate:true});const Y=c(()=>{const n=h.value.toLowerCase();return B.value.filter(l=>I.value!=="all"&&l.status!==I.value?false:n?[l.id,l.name,l.commandPath,l.plugin,l.description].some(e=>e==null?void 0:e.toLowerCase().includes(n)):true)}),ue=c(()=>V.value?Y.value.filter(n=>{var l,e;return K.value||((l=V.value)==null?void 0:l.id)==="bot-admin"||((e=V.value)==null?void 0:e.scopeType)==="global"?true:n.allowGuildOverride}):[]);function j(n){const l=z.value.find(e=>e.scope===a.value&&e.roleId===v.value&&e.commandId===n);return(l==null?void 0:l.state)||"inherit"}async function g(n){r.value=true,O.value="";try{await n}catch(l){O.value=l instanceof Error?l.message:String(l)}finally{r.value=false}}async function ie(){return g((async()=>{const n=await b("newauth/getData");le.newauth=n})())}function H(n,l){return g(b("newauth/setPolicy",{scope:a.value,roleId:v.value,commandId:n,state:l}))}function M(n){return`${a.value}:${n}`}function ae(n){const l=z.value.filter(e=>e.scope===a.value&&e.commandId===n.id&&e.state==="allow").map(e=>e.roleId);return l.length?l:[...n.autoAssign.roles]}function D(n){const l=M(n.id);_.value[l]||(_.value[l]=ae(n))}function de(n,l){return D(n),_.value[M(n.id)].includes(l)}function re(n,l,e){D(n);const i=M(n.id),J=e.target.checked,Q=new Set(_.value[i]);J?Q.add(l):Q.delete(l),_.value[i]=[...Q]}function Z(n,l){_.value[M(n.id)]=[...l==="legacy"?n.suggestion.roles:n.autoAssign.roles]}function ve(n){return D(n),g(b("newauth/configureCommandRoles",{commandId:n.id,scope:a.value,roleIds:_.value[M(n.id)]}))}function ce(){return g(b("newauth/autoAssignPending",{scope:a.value,mode:"advisor"}))}function pe(n,l){const e=l.target;return g(b("newauth/setGuildOverride",{commandId:n.id,allowGuildOverride:e.checked}))}function ge(n,l){const e=l.target;return g(b("newauth/setCommandStatus",{commandId:n.id,status:e.value}))}function be(){return g((async()=>{await b("newauth/createRole",{id:$.value,name:U.value,scopeType:q.value}),v.value=$.value,$.value="",U.value=""})())}function fe(){return g((async()=>{await b("newauth/addMember",{roleId:v.value,uid:x.value,scope:G.value}),x.value=""})())}function ke(n){return g(b("newauth/removeMember",{roleId:n.roleId,uid:`${n.platform}:${n.userId}`,scope:n.scope}))}function ye(){return g(b("newauth/copyRolePolicies",{sourceRoleId:N.value,targetRoleId:v.value,scope:a.value}))}function he(n){return`${n.roleId}:${n.platform}:${n.userId}:${n.scope}`}function E(n){return g(b("newauth/assignPluginCommands",{plugin:m.value,scope:a.value,target:n}))}function me(n){return n==="global"?"全局":"群内"}return(n,l)=>(s(),u("main",we,[t("header",Ie,[t("div",null,[l[20]||(l[20]=t("h1",null,"新权限",-1)),t("p",null,o(P.value.length)+" 个角色 / "+o(B.value.length)+" 个指令 / "+o(R.value.length)+" 个待配置",1)]),t("div",$e,[p(t("select",{"onUpdate:modelValue":l[0]||(l[0]=e=>a.value=e),class:"scope-select"},[(s(true),u(k,null,y(ne.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,Se))),128))],512),[[C,a.value]]),t("button",{class:"primary",disabled:r.value||!R.value.length,onClick:ce}," AI 自动分配待配置 ",8,Pe),t("button",{disabled:r.value,onClick:ie},"刷新",8,Re)])]),t("nav",Ve,[t("button",{class:w({active:f.value==="roles"}),onClick:l[1]||(l[1]=e=>f.value="roles")},"角色",2),t("button",{class:w({active:f.value==="pending"}),onClick:l[2]||(l[2]=e=>f.value="pending")},"待配置",2),t("button",{class:w({active:f.value==="commands"}),onClick:l[3]||(l[3]=e=>f.value="commands")},"指令",2)]),O.value?(s(),u("p",Ae,o(O.value),1)):L("v-if",true),f.value==="roles"?(s(),u("section",Ue,[t("aside",xe,[(s(true),u(k,null,y(P.value,e=>(s(),u("button",{key:e.id,class:w(["role-item",{active:e.id===v.value}]),onClick:i=>v.value=e.id},[t("span",null,[t("strong",null,o(e.name),1),t("small",null,o(e.builtin?"内置角色":"自定义角色")+" / "+o(me(e.scopeType)),1)]),t("em",null,o(e.allowCount),1)],10,Me))),128)),t("form",{class:"role-create",onSubmit:ee(be,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[4]||(l[4]=e=>$.value=e),placeholder:"custom:music-admin"},null,512),[[T,$.value,void 0,{trim:true}]]),p(t("input",{"onUpdate:modelValue":l[5]||(l[5]=e=>U.value=e),placeholder:"角色名称"},null,512),[[T,U.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[6]||(l[6]=e=>q.value=e)},[...l[21]||(l[21]=[t("option",{value:"guild"},"群内角色",-1),t("option",{value:"global"},"全局角色",-1)])],512),[[C,q.value]]),t("button",{class:"primary",disabled:r.value||!$.value||!U.value},"创建角色",8,Te)],32)]),V.value?(s(),u("section",Oe,[t("div",Ge,[t("div",null,[t("h2",null,o(V.value.name),1),t("p",null,o(V.value.id),1)]),t("div",Ne,[p(t("select",{"onUpdate:modelValue":l[7]||(l[7]=e=>N.value=e)},[l[22]||(l[22]=t("option",{disabled:"",value:""},"复制来源",-1)),(s(true),u(k,null,y(oe.value,e=>(s(),u("option",{key:e.id,value:e.id},o(e.name),9,Be))),128))],512),[[C,N.value]]),t("button",{disabled:r.value||!N.value,onClick:ye},"复制权限",8,De),t("label",Ee,[p(t("input",{type:"checkbox","onUpdate:modelValue":l[8]||(l[8]=e=>K.value=e)},null,512),[[Ce,K.value]]),l[23]||(l[23]=F(" 审计模式 ",-1))])])]),t("section",Le,[t("div",null,[l[24]||(l[24]=t("strong",null,"成员",-1)),t("small",null,o(X.value.length)+" 个显式成员",1)]),t("div",Fe,[(s(true),u(k,null,y(X.value,e=>(s(),u("span",{key:he(e),class:"member-chip"},[F(o(e.platform)+":"+o(e.userId)+" / "+o(e.scope)+" ",1),t("button",{disabled:r.value,onClick:i=>ke(e)},"移除",8,Ke)]))),128))]),t("form",{class:"member-add",onSubmit:ee(fe,["prevent"])},[p(t("input",{"onUpdate:modelValue":l[9]||(l[9]=e=>x.value=e),placeholder:"platform:userId"},null,512),[[T,x.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[10]||(l[10]=e=>G.value=e)},[l[25]||(l[25]=t("option",{value:"global"},"global",-1)),a.value!=="global"?(s(),u("option",{key:0,value:a.value},o(a.value),9,qe)):L("v-if",true)],512),[[C,G.value]]),t("button",{class:"primary",disabled:r.value||!x.value},"添加成员",8,ze)],32)]),t("div",je,[p(t("input",{"onUpdate:modelValue":l[11]||(l[11]=e=>h.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[T,h.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[12]||(l[12]=e=>I.value=e)},[...l[26]||(l[26]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[C,I.value]])]),t("div",He,[(s(true),u(k,null,y(ue.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",Je,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority)+" / "+o(e.suggestion.label),1)]),t("div",Qe,[t("button",{class:w({active:j(e.id)==="inherit"}),onClick:i=>H(e.id,"inherit")}," 继承 ",10,We),t("button",{class:w({active:j(e.id)==="allow"}),onClick:i=>H(e.id,"allow")}," 开 ",10,Xe),t("button",{class:w({active:j(e.id)==="deny"}),onClick:i=>H(e.id,"deny")}," 关 ",10,Ye)])]))),128))])])):L("v-if",true)])):f.value==="pending"?(s(),u("section",Ze,[(s(true),u(k,null,y(R.value,e=>(s(),u("article",{key:e.id,class:"pending-item"},[t("div",null,[t("strong",null,o(e.name),1),t("span",null,o(e.commandPath),1),t("small",null,o(e.plugin)+" / legacy "+o(e.legacyAuthority),1)]),t("dl",null,[l[27]||(l[27]=t("dt",null,"旧建议",-1)),t("dd",null,o(e.suggestion.label),1),l[28]||(l[28]=t("dt",null,"AI 建议",-1)),t("dd",null,o(e.autoAssign.label)+":"+o(e.autoAssign.reason),1)]),t("div",el,[(s(true),u(k,null,y(P.value,i=>(s(),u("label",{key:`${e.id}:${i.id}`,class:"check"},[t("input",{type:"checkbox",checked:de(e,i.id),onChange:J=>re(e,i.id,J)},null,40,ll),F(" "+o(i.name),1)]))),128))]),t("div",tl,[t("button",{disabled:r.value,onClick:i=>Z(e,"legacy")},"填入旧建议",8,nl),t("button",{disabled:r.value,onClick:i=>Z(e,"advisor")},"填入 AI 建议",8,ol),t("button",{class:"primary",disabled:r.value,onClick:i=>ve(e)},"保存分配",8,sl)])]))),128)),R.value.length?L("v-if",true):(s(),u("p",ul,"没有待配置指令。"))])):(s(),u("section",il,[t("div",al,[p(t("input",{"onUpdate:modelValue":l[13]||(l[13]=e=>h.value=e),placeholder:"搜索指令、插件、描述"},null,512),[[T,h.value,void 0,{trim:true}]]),p(t("select",{"onUpdate:modelValue":l[14]||(l[14]=e=>I.value=e)},[...l[29]||(l[29]=[t("option",{value:"all"},"全部状态",-1),t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],512),[[C,I.value]])]),t("div",dl,[p(t("select",{"onUpdate:modelValue":l[15]||(l[15]=e=>m.value=e)},[l[30]||(l[30]=t("option",{disabled:"",value:""},"选择插件",-1)),(s(true),u(k,null,y(se.value,e=>(s(),u("option",{key:e,value:e},o(e),9,rl))),128))],512),[[C,m.value]]),t("button",{disabled:r.value||!m.value,onClick:l[16]||(l[16]=e=>E("legacy"))},"采用插件建议",8,vl),t("button",{disabled:r.value||!m.value,onClick:l[17]||(l[17]=e=>E("guild-member"))},"给群成员",8,cl),t("button",{disabled:r.value||!m.value,onClick:l[18]||(l[18]=e=>E("guild-admin"))},"给群管理员",8,pl),t("button",{disabled:r.value||!m.value,onClick:l[19]||(l[19]=e=>E("bot-admin"))},"仅 Bot 管理员",8,gl)]),(s(true),u(k,null,y(Y.value,e=>(s(),u("article",{key:e.id,class:"command-row"},[t("div",bl,[t("strong",null,o(e.name),1),t("span",null,o(e.id),1),t("small",null,o(e.plugin)+" / "+o(e.commandPath)+" / "+o(e.autoAssign.label),1)]),t("div",fl,[t("label",kl,[t("input",{type:"checkbox",checked:e.allowGuildOverride,onChange:i=>pe(e,i)},null,40,yl),l[31]||(l[31]=F(" 群内自治 ",-1))]),t("select",{value:e.status,onChange:i=>ge(e,i)},[...l[32]||(l[32]=[t("option",{value:"pending"},"待配置",-1),t("option",{value:"configured"},"已配置",-1),t("option",{value:"disabled"},"已禁用",-1)])],40,hl)])]))),128))]))]))}}),_l=(A,f)=>{const a=A.__vccOpts||A;for(const[v,h]of f)a[v]=h;return a},Cl=_l(ml,[["__scopeId","data-v-6014eba6"]]),$l=A=>{A.page({name:"新权限",path:"/new-auth",icon:"⌗",fields:["newauth"],authority:4,component:Cl})};export{$l as default};
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.new-auth-page[data-v-
|
|
1
|
+
.new-auth-page[data-v-6014eba6]{min-height:100vh;background:#eef2f6;color:#18222d;padding:20px}.topbar[data-v-6014eba6],.detail-head[data-v-6014eba6],.filters[data-v-6014eba6],.row-actions[data-v-6014eba6],.command-controls[data-v-6014eba6],.top-actions[data-v-6014eba6],.detail-tools[data-v-6014eba6],.member-add[data-v-6014eba6],.plugin-bulk[data-v-6014eba6]{display:flex;align-items:center;gap:12px}.topbar[data-v-6014eba6]{justify-content:space-between;border:1px solid #d7dee8;border-radius:8px;background:#fff;box-shadow:0 8px 24px #20304814;padding:16px}h1[data-v-6014eba6],h2[data-v-6014eba6],p[data-v-6014eba6]{margin:0}h1[data-v-6014eba6]{font-size:26px;line-height:1.2}h2[data-v-6014eba6]{font-size:20px}p[data-v-6014eba6],small[data-v-6014eba6],span[data-v-6014eba6],dd[data-v-6014eba6],dt[data-v-6014eba6]{color:#607083}button[data-v-6014eba6],select[data-v-6014eba6],input[data-v-6014eba6]{border:1px solid #c9d3df;border-radius:6px;background:#fff;color:#18222d;min-height:34px;padding:0 10px;font:inherit}button[data-v-6014eba6]{cursor:pointer;transition:border-color .16s ease,box-shadow .16s ease,background .16s ease}button[data-v-6014eba6]:not(:disabled):hover,select[data-v-6014eba6]:focus,input[data-v-6014eba6]:focus{border-color:#2d6cdf;box-shadow:0 0 0 3px #2d6cdf1f;outline:none}button[data-v-6014eba6]:disabled{cursor:not-allowed;opacity:.55}.primary[data-v-6014eba6]{background:#1d6f68;border-color:#1d6f68;color:#fff}.tabs[data-v-6014eba6]{display:flex;gap:6px;margin:16px 0}.tabs button[data-v-6014eba6]{background:#f8fafc}.tabs button.active[data-v-6014eba6],.state-buttons button.active[data-v-6014eba6]{background:#17202b;border-color:#17202b;color:#fff}.role-layout[data-v-6014eba6]{display:grid;grid-template-columns:minmax(220px,280px) 1fr;gap:16px}.role-list[data-v-6014eba6]{display:grid;gap:8px;align-content:start}.role-create[data-v-6014eba6]{display:grid;gap:8px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;box-shadow:0 4px 14px #2030480d}.role-item[data-v-6014eba6]{display:flex;justify-content:space-between;align-items:center;min-height:58px;text-align:left}.role-item span[data-v-6014eba6],.command-main[data-v-6014eba6]{display:grid;gap:3px;min-width:0}.role-item.active[data-v-6014eba6]{border-color:#2d6cdf;box-shadow:inset 3px 0 #2d6cdf,0 4px 14px #2d6cdf1f}.role-item em[data-v-6014eba6]{font-style:normal;color:#9a5a00}.detail[data-v-6014eba6]{min-width:0}.detail-head[data-v-6014eba6]{justify-content:space-between;margin-bottom:12px}.detail-tools[data-v-6014eba6]{flex-wrap:wrap;justify-content:flex-end}.member-panel[data-v-6014eba6]{display:grid;gap:10px;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:12px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.member-panel>div[data-v-6014eba6]:first-child{display:flex;justify-content:space-between;gap:12px}.member-list[data-v-6014eba6]{display:flex;flex-wrap:wrap;gap:6px;min-height:28px}.member-chip[data-v-6014eba6]{display:inline-flex;align-items:center;gap:6px;border:1px solid #c9d3df;border-radius:999px;background:#f8fafc;padding:3px 4px 3px 10px}.member-chip button[data-v-6014eba6]{min-height:24px;padding:0 8px;border-radius:999px}.filters[data-v-6014eba6]{margin-bottom:12px}.plugin-bulk[data-v-6014eba6]{flex-wrap:wrap;border:1px solid #d7dee8;border-radius:8px;background:#fff;padding:10px;margin-bottom:12px;box-shadow:0 4px 14px #2030480d}.filters input[data-v-6014eba6]{flex:1;min-width:180px}.command-list[data-v-6014eba6],.pending-grid[data-v-6014eba6],.commands-table[data-v-6014eba6]{display:grid;gap:8px}.command-row[data-v-6014eba6],.pending-item[data-v-6014eba6]{background:#fff;border:1px solid #d7dee8;border-radius:8px;padding:12px;box-shadow:0 4px 14px #2030480d}.command-row[data-v-6014eba6]{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center}.command-main strong[data-v-6014eba6],.command-main span[data-v-6014eba6],.command-main small[data-v-6014eba6]{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.state-buttons[data-v-6014eba6]{display:grid;grid-template-columns:repeat(3,54px);gap:6px}.pending-grid[data-v-6014eba6]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}.pending-item[data-v-6014eba6]{display:grid;gap:10px}.role-picker[data-v-6014eba6]{display:grid;grid-template-columns:repeat(auto-fill,minmax(128px,1fr));gap:8px}.role-picker .check[data-v-6014eba6]{border:1px solid #d7dee8;border-radius:6px;background:#f8fafc;min-height:32px;padding:0 8px}dl[data-v-6014eba6]{display:grid;grid-template-columns:58px 1fr;gap:4px 8px;margin:0}dd[data-v-6014eba6]{margin:0}.check[data-v-6014eba6]{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}.check input[data-v-6014eba6]{min-height:0}.error[data-v-6014eba6]{border:1px solid #e2a15d;background:#fff6e9;color:#85510d;border-radius:6px;padding:10px;margin-bottom:12px}.empty[data-v-6014eba6]{padding:24px 0}@media (max-width: 760px){.new-auth-page[data-v-6014eba6]{padding:12px}.topbar[data-v-6014eba6],.detail-head[data-v-6014eba6],.command-row[data-v-6014eba6]{grid-template-columns:1fr;display:grid}.top-actions[data-v-6014eba6],.filters[data-v-6014eba6],.command-controls[data-v-6014eba6],.detail-tools[data-v-6014eba6],.member-add[data-v-6014eba6],.plugin-bulk[data-v-6014eba6]{flex-wrap:wrap}.role-layout[data-v-6014eba6]{grid-template-columns:1fr}.state-buttons[data-v-6014eba6]{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Command, Context, Schema, Session } from 'koishi';
|
|
1
|
+
import { Command, Context, Schema, Service, Session } from 'koishi';
|
|
2
2
|
import type { Argv } from 'koishi';
|
|
3
3
|
export declare const name = "new-auth";
|
|
4
4
|
export declare const inject: {
|
|
@@ -10,6 +10,7 @@ type ScopeType = 'global' | 'guild';
|
|
|
10
10
|
type CommandStatus = 'pending' | 'configured' | 'disabled';
|
|
11
11
|
type PolicyState = 'inherit' | 'allow' | 'deny';
|
|
12
12
|
type AutoAssignKind = 'public' | 'moderation' | 'owner' | 'admin' | 'legacy';
|
|
13
|
+
type PluginAssignTarget = 'legacy' | 'bot-admin' | 'guild-owner' | 'guild-admin' | 'guild-member';
|
|
13
14
|
export interface NewAuthCommandRecord {
|
|
14
15
|
id: string;
|
|
15
16
|
name: string;
|
|
@@ -81,6 +82,9 @@ declare module '@koishijs/plugin-console' {
|
|
|
81
82
|
success: true;
|
|
82
83
|
count: number;
|
|
83
84
|
}>;
|
|
85
|
+
'newauth/configureCommandRoles'(payload: ConfigureCommandRolesPayload): Promise<{
|
|
86
|
+
success: true;
|
|
87
|
+
}>;
|
|
84
88
|
'newauth/createRole'(payload: CreateRolePayload): Promise<{
|
|
85
89
|
success: true;
|
|
86
90
|
}>;
|
|
@@ -94,6 +98,10 @@ declare module '@koishijs/plugin-console' {
|
|
|
94
98
|
success: true;
|
|
95
99
|
count: number;
|
|
96
100
|
}>;
|
|
101
|
+
'newauth/assignPluginCommands'(payload: AssignPluginCommandsPayload): Promise<{
|
|
102
|
+
success: true;
|
|
103
|
+
count: number;
|
|
104
|
+
}>;
|
|
97
105
|
}
|
|
98
106
|
}
|
|
99
107
|
export interface NewAuthConsoleData {
|
|
@@ -148,6 +156,11 @@ interface AutoAssignPayload {
|
|
|
148
156
|
scope?: string;
|
|
149
157
|
mode?: 'legacy' | 'advisor';
|
|
150
158
|
}
|
|
159
|
+
interface ConfigureCommandRolesPayload {
|
|
160
|
+
commandId: string;
|
|
161
|
+
scope: string;
|
|
162
|
+
roleIds: string[];
|
|
163
|
+
}
|
|
151
164
|
interface CreateRolePayload {
|
|
152
165
|
id: string;
|
|
153
166
|
name: string;
|
|
@@ -163,22 +176,21 @@ interface CopyRolePoliciesPayload {
|
|
|
163
176
|
targetRoleId: string;
|
|
164
177
|
scope: string;
|
|
165
178
|
}
|
|
179
|
+
interface AssignPluginCommandsPayload {
|
|
180
|
+
plugin: string;
|
|
181
|
+
scope: string;
|
|
182
|
+
target: PluginAssignTarget;
|
|
183
|
+
}
|
|
166
184
|
export interface Config {
|
|
167
185
|
botAdmins: string[];
|
|
168
186
|
trustLegacyAuthorityAsAdmin: boolean;
|
|
169
187
|
legacyAdminAuthority: number;
|
|
170
|
-
ownerRoleNames: string[];
|
|
171
|
-
adminRoleNames: string[];
|
|
172
|
-
allowGuildOverrideAuthorityMax: number;
|
|
173
188
|
deniedMessage: string;
|
|
174
|
-
grantRuntimeCommandPermission: boolean;
|
|
175
|
-
raiseLegacyAuthority: boolean;
|
|
176
189
|
}
|
|
177
190
|
export declare const Config: Schema<Config>;
|
|
178
191
|
export declare function apply(ctx: Context, config: Config): void;
|
|
179
|
-
export declare class NewAuthService {
|
|
180
|
-
|
|
181
|
-
private config;
|
|
192
|
+
export declare class NewAuthService extends Service {
|
|
193
|
+
config: Config;
|
|
182
194
|
private commandCache;
|
|
183
195
|
private adminSet;
|
|
184
196
|
private ownerRoleNames;
|
|
@@ -252,7 +264,9 @@ export declare class NewAuthService {
|
|
|
252
264
|
setCommandGuildOverride(input: string, allowGuildOverride: boolean): Promise<NewAuthCommandRecord>;
|
|
253
265
|
setCommandPolicy(scope: string, roleId: string, input: string, state: PolicyState): Promise<NewAuthCommandRecord>;
|
|
254
266
|
applySuggestedPolicy(input: string, scope?: string, mode?: 'legacy' | 'advisor'): Promise<NewAuthCommandRecord>;
|
|
267
|
+
configureCommandRoles(input: string, scope?: string, roleIds?: string[]): Promise<NewAuthCommandRecord>;
|
|
255
268
|
autoAssignPending(scope?: string, mode?: 'legacy' | 'advisor'): Promise<number>;
|
|
269
|
+
assignPluginCommands(plugin: string, scope?: string, target?: PluginAssignTarget): Promise<number>;
|
|
256
270
|
getCommand(input: string): Promise<NewAuthCommandRecord>;
|
|
257
271
|
private ensureBuiltinRoles;
|
|
258
272
|
private applyRolesToCommand;
|
package/lib/index.js
CHANGED
|
@@ -38,6 +38,11 @@ const BUILTIN_ROLES = [
|
|
|
38
38
|
scopeType: 'global',
|
|
39
39
|
},
|
|
40
40
|
];
|
|
41
|
+
const OWNER_ROLE_NAMES = ['owner', 'guild-owner'];
|
|
42
|
+
const ADMIN_ROLE_NAMES = ['admin', 'administrator', 'guild-admin', 'moderator'];
|
|
43
|
+
const DEFAULT_ALLOW_GUILD_OVERRIDE_AUTHORITY_MAX = 3;
|
|
44
|
+
const GRANT_RUNTIME_COMMAND_PERMISSION = true;
|
|
45
|
+
const RAISE_LEGACY_AUTHORITY = false;
|
|
41
46
|
exports.Config = koishi_1.Schema.object({
|
|
42
47
|
botAdmins: koishi_1.Schema.array(String)
|
|
43
48
|
.role('table')
|
|
@@ -51,26 +56,9 @@ exports.Config = koishi_1.Schema.object({
|
|
|
51
56
|
.max(10)
|
|
52
57
|
.description('旧 authority 管理员阈值。')
|
|
53
58
|
.default(4),
|
|
54
|
-
ownerRoleNames: koishi_1.Schema.array(String)
|
|
55
|
-
.description('平台角色名/ID 命中这些值时识别为群主。')
|
|
56
|
-
.default(['owner', 'guild-owner', 'administrator']),
|
|
57
|
-
adminRoleNames: koishi_1.Schema.array(String)
|
|
58
|
-
.description('平台角色名/ID 命中这些值时识别为群管理员。')
|
|
59
|
-
.default(['admin', 'administrator', 'guild-admin', 'moderator']),
|
|
60
|
-
allowGuildOverrideAuthorityMax: koishi_1.Schema.number()
|
|
61
|
-
.min(0)
|
|
62
|
-
.max(10)
|
|
63
|
-
.description('旧 authority 建议值不高于该值时,默认认为允许群内自治。')
|
|
64
|
-
.default(3),
|
|
65
59
|
deniedMessage: koishi_1.Schema.string()
|
|
66
60
|
.description('权限不足时返回的提示。')
|
|
67
61
|
.default('你没有权限使用该指令。'),
|
|
68
|
-
grantRuntimeCommandPermission: koishi_1.Schema.boolean()
|
|
69
|
-
.description('新权限系统放行后,临时授予本次 Koishi command 权限,绕过旧 command authority 校验。')
|
|
70
|
-
.default(true),
|
|
71
|
-
raiseLegacyAuthority: koishi_1.Schema.boolean()
|
|
72
|
-
.description('新权限系统放行后,临时提升 session.user.authority 以兼容插件内部硬编码判断。可能影响依赖旧 authority 的插件行为。')
|
|
73
|
-
.default(false),
|
|
74
62
|
});
|
|
75
63
|
function apply(ctx, config) {
|
|
76
64
|
ctx.model.extend('new_auth_command', {
|
|
@@ -118,8 +106,6 @@ function apply(ctx, config) {
|
|
|
118
106
|
primary: ['scope', 'roleId', 'commandId'],
|
|
119
107
|
});
|
|
120
108
|
const service = new NewAuthService(ctx, config);
|
|
121
|
-
ctx.provide('newauth', service);
|
|
122
|
-
ctx.on('ready', () => service.start());
|
|
123
109
|
ctx.on('command-added', command => service.registerCommand(command));
|
|
124
110
|
ctx.on('command-updated', command => service.registerCommand(command));
|
|
125
111
|
ctx.before('command/execute', async (argv) => {
|
|
@@ -130,14 +116,14 @@ function apply(ctx, config) {
|
|
|
130
116
|
registerConsole(ctx, service);
|
|
131
117
|
});
|
|
132
118
|
}
|
|
133
|
-
class NewAuthService {
|
|
119
|
+
class NewAuthService extends koishi_1.Service {
|
|
134
120
|
constructor(ctx, config) {
|
|
135
|
-
|
|
136
|
-
this.config = config;
|
|
121
|
+
super(ctx, 'newauth', true);
|
|
137
122
|
this.commandCache = new Map();
|
|
123
|
+
this.config = config;
|
|
138
124
|
this.adminSet = new Set(config.botAdmins.map(normalizeKey));
|
|
139
|
-
this.ownerRoleNames = new Set(
|
|
140
|
-
this.adminRoleNames = new Set(
|
|
125
|
+
this.ownerRoleNames = new Set(OWNER_ROLE_NAMES.map(normalizeKey));
|
|
126
|
+
this.adminRoleNames = new Set(ADMIN_ROLE_NAMES.map(normalizeKey));
|
|
141
127
|
}
|
|
142
128
|
async start() {
|
|
143
129
|
await this.ensureBuiltinRoles();
|
|
@@ -183,10 +169,10 @@ class NewAuthService {
|
|
|
183
169
|
if (!result.allowed) {
|
|
184
170
|
return command.config.showWarning ? this.config.deniedMessage : '';
|
|
185
171
|
}
|
|
186
|
-
if (
|
|
172
|
+
if (GRANT_RUNTIME_COMMAND_PERMISSION) {
|
|
187
173
|
this.grantRuntimeCommandPermission(session, command, options || {});
|
|
188
174
|
}
|
|
189
|
-
if (
|
|
175
|
+
if (RAISE_LEGACY_AUTHORITY && session.user) {
|
|
190
176
|
const user = session.user;
|
|
191
177
|
const legacyAuthority = result.command?.legacyAuthority ?? command.config.authority ?? 0;
|
|
192
178
|
if ((user.authority ?? 0) < legacyAuthority) {
|
|
@@ -292,7 +278,7 @@ class NewAuthService {
|
|
|
292
278
|
}
|
|
293
279
|
async listRoles() {
|
|
294
280
|
const records = await this.ctx.database.get('new_auth_role', {});
|
|
295
|
-
return records.sort((a, b) => Number(
|
|
281
|
+
return records.sort((a, b) => Number(b.builtin) - Number(a.builtin) || a.id.localeCompare(b.id));
|
|
296
282
|
}
|
|
297
283
|
async listPolicies() {
|
|
298
284
|
return this.ctx.database.get('new_auth_policy', {});
|
|
@@ -365,7 +351,7 @@ class NewAuthService {
|
|
|
365
351
|
}
|
|
366
352
|
async createCustomRole(id, name, scopeType = 'guild') {
|
|
367
353
|
if (BUILTIN_ROLES.some(role => role.id === id)) {
|
|
368
|
-
throw new Error(
|
|
354
|
+
throw new Error(`不能覆盖内置角色:${id}`);
|
|
369
355
|
}
|
|
370
356
|
const now = new Date();
|
|
371
357
|
const [existing] = await this.ctx.database.get('new_auth_role', { id });
|
|
@@ -379,7 +365,7 @@ class NewAuthService {
|
|
|
379
365
|
updatedAt: now,
|
|
380
366
|
};
|
|
381
367
|
if (existing) {
|
|
382
|
-
await this.ctx.database.set('new_auth_role', id, record);
|
|
368
|
+
await this.ctx.database.set('new_auth_role', id, omitPrimary(record, 'id'));
|
|
383
369
|
}
|
|
384
370
|
else {
|
|
385
371
|
await this.ctx.database.create('new_auth_role', record);
|
|
@@ -412,6 +398,9 @@ class NewAuthService {
|
|
|
412
398
|
}
|
|
413
399
|
async setCommandStatus(input, status) {
|
|
414
400
|
const command = await this.resolveCommandInput(input);
|
|
401
|
+
if (status === 'disabled' && isSelfCommandPath(command.commandPath)) {
|
|
402
|
+
throw new Error('不能禁用 new-auth 管理指令');
|
|
403
|
+
}
|
|
415
404
|
await this.ctx.database.set('new_auth_command', command.id, {
|
|
416
405
|
status,
|
|
417
406
|
updatedAt: new Date(),
|
|
@@ -429,6 +418,7 @@ class NewAuthService {
|
|
|
429
418
|
return command;
|
|
430
419
|
}
|
|
431
420
|
async setCommandPolicy(scope, roleId, input, state) {
|
|
421
|
+
await this.ensureRoleExists(roleId);
|
|
432
422
|
const command = await this.resolveCommandInput(input);
|
|
433
423
|
await this.setPolicy(scope, roleId, command.id, state);
|
|
434
424
|
if (state !== 'inherit' && command.status === 'pending') {
|
|
@@ -447,6 +437,14 @@ class NewAuthService {
|
|
|
447
437
|
}
|
|
448
438
|
return command;
|
|
449
439
|
}
|
|
440
|
+
async configureCommandRoles(input, scope = 'global', roleIds = []) {
|
|
441
|
+
const command = await this.resolveCommandInput(input);
|
|
442
|
+
await this.applyRolesToCommand(scope, command.id, roleIds);
|
|
443
|
+
if (command.status === 'pending') {
|
|
444
|
+
await this.setCommandStatus(command.id, 'configured');
|
|
445
|
+
}
|
|
446
|
+
return command;
|
|
447
|
+
}
|
|
450
448
|
async autoAssignPending(scope = 'global', mode = 'advisor') {
|
|
451
449
|
const commands = await this.listCommands({ pending: true });
|
|
452
450
|
for (const command of commands) {
|
|
@@ -454,6 +452,22 @@ class NewAuthService {
|
|
|
454
452
|
}
|
|
455
453
|
return commands.length;
|
|
456
454
|
}
|
|
455
|
+
async assignPluginCommands(plugin, scope = 'global', target = 'legacy') {
|
|
456
|
+
const commands = (await this.listCommands({ all: true }))
|
|
457
|
+
.filter(command => command.plugin === plugin);
|
|
458
|
+
for (const command of commands) {
|
|
459
|
+
if (target === 'legacy') {
|
|
460
|
+
await this.applySuggestedPolicy(command.id, scope, 'legacy');
|
|
461
|
+
}
|
|
462
|
+
else {
|
|
463
|
+
await this.applyRolesToCommand(scope, command.id, getAssignTargetRoles(target));
|
|
464
|
+
if (command.status === 'pending') {
|
|
465
|
+
await this.setCommandStatus(command.id, 'configured');
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return commands.length;
|
|
470
|
+
}
|
|
457
471
|
async getCommand(input) {
|
|
458
472
|
const cached = this.commandCache.get(input);
|
|
459
473
|
if (cached)
|
|
@@ -474,7 +488,7 @@ class NewAuthService {
|
|
|
474
488
|
updatedAt: now,
|
|
475
489
|
};
|
|
476
490
|
if (existing) {
|
|
477
|
-
await this.ctx.database.set('new_auth_role', role.id, record);
|
|
491
|
+
await this.ctx.database.set('new_auth_role', role.id, omitPrimary(record, 'id'));
|
|
478
492
|
}
|
|
479
493
|
else {
|
|
480
494
|
await this.ctx.database.create('new_auth_role', record);
|
|
@@ -484,10 +498,13 @@ class NewAuthService {
|
|
|
484
498
|
async applyRolesToCommand(scope, commandId, roleIds) {
|
|
485
499
|
const roles = await this.listRoles();
|
|
486
500
|
const knownRoles = new Set(roles.map(role => role.id));
|
|
487
|
-
|
|
501
|
+
const selectedRoles = new Set(roleIds);
|
|
502
|
+
for (const roleId of selectedRoles) {
|
|
488
503
|
if (!knownRoles.has(roleId))
|
|
489
|
-
|
|
490
|
-
|
|
504
|
+
throw new Error(`未找到角色:${roleId}`);
|
|
505
|
+
}
|
|
506
|
+
for (const role of roles) {
|
|
507
|
+
await this.setPolicy(scope, role.id, commandId, selectedRoles.has(role.id) ? 'allow' : 'deny');
|
|
491
508
|
}
|
|
492
509
|
}
|
|
493
510
|
createCommandRecord(command, now) {
|
|
@@ -496,7 +513,7 @@ class NewAuthService {
|
|
|
496
513
|
const commandPath = command.name;
|
|
497
514
|
const aliases = Object.keys(command._aliases || {});
|
|
498
515
|
const id = makeCommandId(plugin, commandPath);
|
|
499
|
-
|
|
516
|
+
const record = {
|
|
500
517
|
id,
|
|
501
518
|
name: command.displayName || commandPath,
|
|
502
519
|
commandPath,
|
|
@@ -505,10 +522,14 @@ class NewAuthService {
|
|
|
505
522
|
description: this.getDescription(command),
|
|
506
523
|
legacyAuthority,
|
|
507
524
|
status: this.isSelfCommand(command) ? 'configured' : 'pending',
|
|
508
|
-
allowGuildOverride: legacyAuthority <=
|
|
525
|
+
allowGuildOverride: legacyAuthority <= DEFAULT_ALLOW_GUILD_OVERRIDE_AUTHORITY_MAX,
|
|
509
526
|
createdAt: now,
|
|
510
527
|
updatedAt: now,
|
|
511
528
|
};
|
|
529
|
+
if (this.getAutoAssignSuggestion(record).kind === 'admin') {
|
|
530
|
+
record.allowGuildOverride = false;
|
|
531
|
+
}
|
|
532
|
+
return record;
|
|
512
533
|
}
|
|
513
534
|
getDescription(command) {
|
|
514
535
|
const key = `commands.${command.name}.description`;
|
|
@@ -525,7 +546,7 @@ class NewAuthService {
|
|
|
525
546
|
|| record.name === normalized
|
|
526
547
|
|| record.aliases.includes(normalized));
|
|
527
548
|
if (!match)
|
|
528
|
-
throw new Error(
|
|
549
|
+
throw new Error(`未找到指令:${input}`);
|
|
529
550
|
return match;
|
|
530
551
|
}
|
|
531
552
|
async setPolicy(scope, roleId, commandId, state) {
|
|
@@ -627,7 +648,7 @@ class NewAuthService {
|
|
|
627
648
|
async ensureRoleExists(roleId) {
|
|
628
649
|
const [role] = await this.ctx.database.get('new_auth_role', { id: roleId });
|
|
629
650
|
if (!role)
|
|
630
|
-
throw new Error(
|
|
651
|
+
throw new Error(`未找到角色:${roleId}`);
|
|
631
652
|
return role;
|
|
632
653
|
}
|
|
633
654
|
hasPlatformRole(session, roleNames) {
|
|
@@ -658,11 +679,11 @@ class NewAuthService {
|
|
|
658
679
|
return [...(this.ctx.$commander?._commandList || [])];
|
|
659
680
|
}
|
|
660
681
|
isSelfCommand(command) {
|
|
661
|
-
return command.name
|
|
682
|
+
return isSelfCommandPath(command.name);
|
|
662
683
|
}
|
|
663
684
|
}
|
|
664
685
|
exports.NewAuthService = NewAuthService;
|
|
665
|
-
function createConsoleServiceClass() {
|
|
686
|
+
function createConsoleServiceClass(service) {
|
|
666
687
|
const { DataService } = require('@koishijs/plugin-console');
|
|
667
688
|
return class NewAuthConsoleDataService extends DataService {
|
|
668
689
|
constructor(ctx) {
|
|
@@ -672,7 +693,7 @@ function createConsoleServiceClass() {
|
|
|
672
693
|
});
|
|
673
694
|
}
|
|
674
695
|
async get(_forced, _client) {
|
|
675
|
-
return
|
|
696
|
+
return service.getConsoleData();
|
|
676
697
|
}
|
|
677
698
|
};
|
|
678
699
|
}
|
|
@@ -681,7 +702,7 @@ function registerConsole(ctx, service) {
|
|
|
681
702
|
dev: (0, path_1.resolve)(__dirname, '../client/index.ts'),
|
|
682
703
|
prod: (0, path_1.resolve)(__dirname, '../dist'),
|
|
683
704
|
});
|
|
684
|
-
ctx.plugin(createConsoleServiceClass());
|
|
705
|
+
ctx.plugin(createConsoleServiceClass(service));
|
|
685
706
|
const ok = async (task) => {
|
|
686
707
|
await task;
|
|
687
708
|
ctx.console.refresh('newauth');
|
|
@@ -707,6 +728,9 @@ function registerConsole(ctx, service) {
|
|
|
707
728
|
ctx.console.refresh('newauth');
|
|
708
729
|
return { success: true, count };
|
|
709
730
|
}, { authority: 4 });
|
|
731
|
+
ctx.console.addListener('newauth/configureCommandRoles', async (payload) => {
|
|
732
|
+
return ok(service.configureCommandRoles(payload.commandId, payload.scope, payload.roleIds));
|
|
733
|
+
}, { authority: 4 });
|
|
710
734
|
ctx.console.addListener('newauth/createRole', async (payload) => {
|
|
711
735
|
return ok(service.createCustomRole(payload.id, payload.name, payload.scopeType));
|
|
712
736
|
}, { authority: 4 });
|
|
@@ -721,6 +745,11 @@ function registerConsole(ctx, service) {
|
|
|
721
745
|
ctx.console.refresh('newauth');
|
|
722
746
|
return { success: true, count };
|
|
723
747
|
}, { authority: 4 });
|
|
748
|
+
ctx.console.addListener('newauth/assignPluginCommands', async (payload) => {
|
|
749
|
+
const count = await service.assignPluginCommands(payload.plugin, payload.scope, payload.target);
|
|
750
|
+
ctx.console.refresh('newauth');
|
|
751
|
+
return { success: true, count };
|
|
752
|
+
}, { authority: 4 });
|
|
724
753
|
}
|
|
725
754
|
function registerManagementCommands(ctx, config, service) {
|
|
726
755
|
const authority = config.legacyAdminAuthority;
|
|
@@ -773,6 +802,15 @@ function registerManagementCommands(ctx, config, service) {
|
|
|
773
802
|
const record = await service.setCommandPolicy(scope, roleId, command, 'inherit');
|
|
774
803
|
return `已恢复继承:${scope} ${roleId} -> ${record.commandPath}`;
|
|
775
804
|
});
|
|
805
|
+
ctx.command('newauth.assign <command> <roleIds> [scope]', '按角色配置单个指令,roleIds 用逗号分隔', { authority })
|
|
806
|
+
.action(async (_, command, roleIds, scope = 'global') => {
|
|
807
|
+
const roles = String(roleIds || '')
|
|
808
|
+
.split(',')
|
|
809
|
+
.map(role => role.trim())
|
|
810
|
+
.filter(Boolean);
|
|
811
|
+
const record = await service.configureCommandRoles(command, scope, roles);
|
|
812
|
+
return `已配置:${record.commandPath} -> ${roles.join(', ') || '无角色'} (${scope})`;
|
|
813
|
+
});
|
|
776
814
|
ctx.command('newauth.disable <command>', '禁用指令', { authority })
|
|
777
815
|
.action(async (_, command) => {
|
|
778
816
|
const record = await service.setCommandStatus(command, 'disabled');
|
|
@@ -816,6 +854,14 @@ function registerManagementCommands(ctx, config, service) {
|
|
|
816
854
|
const count = await service.copyRolePolicies(sourceRoleId, targetRoleId, scope);
|
|
817
855
|
return `已复制 ${count} 条策略:${sourceRoleId} -> ${targetRoleId} (${scope})`;
|
|
818
856
|
});
|
|
857
|
+
ctx.command('newauth.plugin.assign <plugin> <target> [scope]', '批量配置插件指令', { authority })
|
|
858
|
+
.action(async (_, plugin, target, scope = 'global') => {
|
|
859
|
+
if (!['legacy', 'bot-admin', 'guild-owner', 'guild-admin', 'guild-member'].includes(target)) {
|
|
860
|
+
return 'target 只能是 legacy、bot-admin、guild-owner、guild-admin 或 guild-member。';
|
|
861
|
+
}
|
|
862
|
+
const count = await service.assignPluginCommands(plugin, scope, target);
|
|
863
|
+
return `已批量配置 ${count} 个指令:${plugin} -> ${target} (${scope})`;
|
|
864
|
+
});
|
|
819
865
|
}
|
|
820
866
|
function inferLegacyAuthority(command) {
|
|
821
867
|
if (typeof command.config.authority === 'number')
|
|
@@ -857,6 +903,22 @@ function getLegacySuggestion(authority) {
|
|
|
857
903
|
label: '仅 Bot 管理员',
|
|
858
904
|
};
|
|
859
905
|
}
|
|
906
|
+
function getAssignTargetRoles(target) {
|
|
907
|
+
if (target === 'guild-member')
|
|
908
|
+
return ['guild-member', 'guild-admin', 'guild-owner'];
|
|
909
|
+
if (target === 'guild-admin')
|
|
910
|
+
return ['guild-admin', 'guild-owner'];
|
|
911
|
+
if (target === 'guild-owner')
|
|
912
|
+
return ['guild-owner'];
|
|
913
|
+
return ['bot-admin'];
|
|
914
|
+
}
|
|
915
|
+
function omitPrimary(record, key) {
|
|
916
|
+
const { [key]: _value, ...rest } = record;
|
|
917
|
+
return rest;
|
|
918
|
+
}
|
|
919
|
+
function isSelfCommandPath(commandPath) {
|
|
920
|
+
return commandPath === 'newauth' || commandPath.startsWith('newauth/');
|
|
921
|
+
}
|
|
860
922
|
function inferPlugin(command) {
|
|
861
923
|
const source = command.caller?.scope || command.ctx?.scope;
|
|
862
924
|
const plugin = source?.plugin?.name || source?.uid || source?.id || 'unknown';
|
|
@@ -877,7 +939,7 @@ function getScope(session) {
|
|
|
877
939
|
function parseUid(uid) {
|
|
878
940
|
const index = uid.indexOf(':');
|
|
879
941
|
if (index <= 0 || index === uid.length - 1) {
|
|
880
|
-
throw new Error('
|
|
942
|
+
throw new Error('UID 必须为 platform:userId 格式');
|
|
881
943
|
}
|
|
882
944
|
return {
|
|
883
945
|
platform: uid.slice(0, index),
|
package/newauth.md
CHANGED
|
@@ -488,7 +488,7 @@ Bot 管理
|
|
|
488
488
|
|
|
489
489
|
## 9.3 当前范围切换
|
|
490
490
|
|
|
491
|
-
|
|
491
|
+
页面内有范围选择:
|
|
492
492
|
|
|
493
493
|
```txt
|
|
494
494
|
配置范围:
|
|
@@ -586,7 +586,7 @@ Bot 管理
|
|
|
586
586
|
数据库管理:不允许群内自治
|
|
587
587
|
```
|
|
588
588
|
|
|
589
|
-
|
|
589
|
+
群主打开自己群的配置页时(通过指令列出 而没有webui),只能看到:
|
|
590
590
|
|
|
591
591
|
```txt
|
|
592
592
|
允许群内自治的指令
|
|
@@ -1214,7 +1214,7 @@ deny:
|
|
|
1214
1214
|
|
|
1215
1215
|
## 20.5 Platform Role Provider
|
|
1216
1216
|
|
|
1217
|
-
|
|
1217
|
+
负责把平台身份转为角色。(Onebot平台会自动按QQ号来解析用户平台名称)
|
|
1218
1218
|
|
|
1219
1219
|
例如:
|
|
1220
1220
|
|
|
@@ -1454,5 +1454,5 @@ authority 只做建议,不直接授权;
|
|
|
1454
1454
|
|
|
1455
1455
|
---
|
|
1456
1456
|
|
|
1457
|
-
KOISHI侧边栏的图标不使用默认的
|
|
1457
|
+
KOISHI侧边栏的图标不使用默认的 使用“⌗”
|
|
1458
1458
|
同时根据F:\chatluna-1.4.0-alpha.17 为指令权限限制制作一个AI自动分配
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-new-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Koishi 的角色与作用域指令权限系统。",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"files": [
|