@@ -12,6 +12,7 @@ import {
1212 settingQueries ,
1313 zoneQueries ,
1414 tabQueries ,
15+ roleQueries ,
1516} from "../lib/db" ;
1617import {
1718 auditLogs ,
@@ -42,7 +43,7 @@ import { decrementScoreAndReplyCount } from "../lib/score";
4243import { isValidIpRule } from "../middleware/ip-ban" ;
4344import { applyProgressivePenalty } from "../lib/penalty" ;
4445import { userSummary } from "../lib/format" ;
45- import { createReportBodySchema , errorResponseSchema } from "@cnode/shared" ;
46+ import { createReportBodySchema , errorResponseSchema , roleAssignmentSchema , userRoleSchema } from "@cnode/shared" ;
4647
4748const admin = new OpenAPIHono < {
4849 Variables : AuthVars ;
@@ -202,7 +203,7 @@ admin.post("/topic/:tid/good", modRequired(), async (c) => {
202203 return c . json ( { success : true , message : topic . good ? "已取消加精" : "已加精" } ) ;
203204} ) ;
204205
205- admin . post ( "/topic/:tid/lock" , adminRequired ( ) , async ( c ) => {
206+ admin . post ( "/topic/:tid/lock" , modRequired ( ) , async ( c ) => {
206207 const tid = Number ( c . req . param ( "tid" ) ) ;
207208 const topic = await topicQueries . getById ( tid ) ;
208209 if ( ! topic ) return c . json ( { success : false , error_msg : "话题不存在" } , 404 ) ;
@@ -252,7 +253,42 @@ admin.get("/admin/users", adminRequired(), async (c) => {
252253 let totalQuery = db . select ( { c : count ( ) } ) . from ( users ) . $dynamic ( ) ;
253254 if ( where ) { listQuery = listQuery . where ( where ) as any ; totalQuery = totalQuery . where ( where ) as any ; }
254255 const [ list , totalResult ] = await Promise . all ( [ listQuery . orderBy ( desc ( users . createAt ) ) . limit ( pagination . limit ) . offset ( pagination . offset ) , totalQuery ] ) ;
255- return c . json ( paginated ( list . map ( ( u : any ) => ( { id : u . id , loginname : u . loginname , email : u . email , avatar_url : u . avatar , score : u . score , topic_count : u . topicCount , reply_count : u . replyCount , is_block : ! ! u . isBlock , is_muted : ! ! u . isMuted || ! ! u . isBlock , active : ! ! u . active , create_at : u . createAt } ) ) , Number ( totalResult [ 0 ] ?. c || 0 ) , pagination ) ) ;
256+ const rolesByUserId = await roleQueries . listByUserIds ( list . map ( ( u : any ) => u . id ) ) ;
257+ return c . json ( paginated ( list . map ( ( u : any ) => ( { id : u . id , loginname : u . loginname , email : u . email , avatar_url : u . avatar , score : u . score , topic_count : u . topicCount , reply_count : u . replyCount , roles : rolesByUserId . get ( u . id ) || [ ] , is_block : ! ! u . isBlock , is_muted : ! ! u . isMuted || ! ! u . isBlock , active : ! ! u . active , create_at : u . createAt } ) ) , Number ( totalResult [ 0 ] ?. c || 0 ) , pagination ) ) ;
258+ } ) ;
259+
260+ admin . get ( "/admin/users/:id/roles" , adminRequired ( ) , async ( c ) => {
261+ const id = Number ( c . req . param ( "id" ) ) ;
262+ if ( ! id || Number . isNaN ( id ) ) return c . json ( { success : false , error_msg : "用户不存在" } , 404 ) ;
263+ const target = await userQueries . getById ( id ) ;
264+ if ( ! target ) return c . json ( { success : false , error_msg : "用户不存在" } , 404 ) ;
265+ const roles = await roleQueries . listByUserId ( id ) ;
266+ return c . json ( { success : true , data : { user_id : id , roles } } ) ;
267+ } ) ;
268+
269+ admin . post ( "/admin/users/:id/roles" , adminRequired ( ) , async ( c ) => {
270+ const id = Number ( c . req . param ( "id" ) ) ;
271+ const target = id > 0 && ! Number . isNaN ( id ) ? await userQueries . getById ( id ) : null ;
272+ if ( ! target ) return c . json ( { success : false , error_msg : "用户不存在" } , 404 ) ;
273+ const parsed = roleAssignmentSchema . safeParse ( await c . req . json ( ) . catch ( ( ) => ( { } ) ) ) ;
274+ if ( ! parsed . success ) return c . json ( { success : false , error_msg : "角色无效" } , 422 ) ;
275+ const user = c . get ( "user" ) ! ;
276+ const roles = await roleQueries . grant ( id , parsed . data . role , user . id , parsed . data . reason ) ;
277+ await auditQueries . log ( user . id , user . loginname , "grant_role" , { type : "user" , id : String ( id ) , name : target . loginname } , "success" , JSON . stringify ( { role : parsed . data . role , reason : parsed . data . reason || null } ) ) ;
278+ return c . json ( { success : true , data : { user_id : id , roles } } ) ;
279+ } ) ;
280+
281+ admin . delete ( "/admin/users/:id/roles/:role" , adminRequired ( ) , async ( c ) => {
282+ const id = Number ( c . req . param ( "id" ) ) ;
283+ const target = id > 0 && ! Number . isNaN ( id ) ? await userQueries . getById ( id ) : null ;
284+ if ( ! target ) return c . json ( { success : false , error_msg : "用户不存在" } , 404 ) ;
285+ const role = c . req . param ( "role" ) ;
286+ const parsed = userRoleSchema . safeParse ( role ) ;
287+ if ( ! parsed . success ) return c . json ( { success : false , error_msg : "角色无效" } , 422 ) ;
288+ const user = c . get ( "user" ) ! ;
289+ const roles = await roleQueries . revoke ( id , parsed . data ) ;
290+ await auditQueries . log ( user . id , user . loginname , "revoke_role" , { type : "user" , id : String ( id ) , name : target . loginname } , "success" , JSON . stringify ( { role : parsed . data } ) ) ;
291+ return c . json ( { success : true , data : { user_id : id , roles } } ) ;
256292} ) ;
257293
258294admin . post ( "/user/:name/block" , adminRequired ( ) , async ( c ) => {
@@ -740,6 +776,7 @@ admin.get("/admin/tabs", adminRequired(), async (c) => {
740776 label : r . label ,
741777 visible : ! ! r . visible ,
742778 sort_order : r . sortOrder || 0 ,
779+ scope : r . scope || "public" ,
743780 } ) ) ;
744781 return c . json ( { success : true , data } ) ;
745782} ) ;
@@ -765,6 +802,7 @@ admin.patch("/admin/tabs/:id", adminRequired(), async (c) => {
765802 label : updated . label ,
766803 visible : ! ! updated . visible ,
767804 sort_order : updated . sortOrder || 0 ,
805+ scope : updated . scope || "public" ,
768806 } ,
769807 } ) ;
770808} ) ;
0 commit comments