feat(mod): lite /mod panel + clarify ACL vs housekeeping legacy
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 1m31s

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-22 21:29:32 +02:00
1 parent a384c9a8bb
commit 084cca6ea4
18 files changed
+931 -77

No files matched your search

+10 -9
View File
@@ -43,7 +43,8 @@ function fail(
export { fail as actionError, ok as actionOk };
interface AdminOpts<TSchema extends z.ZodType | undefined> {
permission?: string;
/** Single slug, or any-of list (e.g. admin.moderation.edit OR mod.actions). */
permission?: string | readonly string[];
schema?: TSchema;
rateLimitKey?: string;
rateLimitMax?: number;
@@ -75,19 +76,19 @@ export function adminAction<TSchema extends z.ZodType | undefined = undefined>(
setContextUserId(Number(apiCtx.session.user.id) as never);
if (opts.permission) {
if (
!canAccess(
apiCtx.permissions,
opts.permission,
apiCtx.session.user.rank,
)
) {
const needed = Array.isArray(opts.permission)
? opts.permission
: [opts.permission];
const allowed = needed.some((slug) =>
canAccess(apiCtx.permissions, slug, apiCtx.session.user.rank),
);
if (!allowed) {
await logAuthorizationEvent({
kind: "permission.denied",
userId: Number(apiCtx.session.user.id),
username: apiCtx.session.user.name ?? undefined,
rank: apiCtx.session.user.rank,
permission: opts.permission,
permission: needed.join("|"),
source: "adminAction",
reason: "Permission check denied",
});