This commit is contained in:
1 parent
e85e4d74ea
commit
2e4ed76121
378 files changed
+22404
-20686
No files matched your search
+44
-40
@@ -1,21 +1,21 @@
|
||||
import type { z } from 'zod'
|
||||
import { auth } from '@/lib/auth'
|
||||
import { canAccess, getApiAdminContext } from '@/lib/permissions'
|
||||
import { type ActionResult, actionError, handleActionError } from '@/lib/safe-action-shared'
|
||||
import { logAuthorizationEvent } from '@/lib/admin/authorization-events'
|
||||
import type { z } from "zod";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { canAccess, getApiAdminContext } from "@/lib/permissions";
|
||||
import { type ActionResult, actionError, handleActionError } from "@/lib/safe-action-shared";
|
||||
import { logAuthorizationEvent } from "@/lib/admin/authorization-events";
|
||||
|
||||
export type { ActionResult }
|
||||
export type { ActionResult };
|
||||
|
||||
// ── Admin action wrapper ─────────────────────────────────────────────
|
||||
|
||||
interface AdminActionOptions<TSchema extends z.ZodType | undefined = undefined> {
|
||||
permission?: string
|
||||
schema?: TSchema
|
||||
permission?: string;
|
||||
schema?: TSchema;
|
||||
}
|
||||
|
||||
type AdminActionContext<TSchema extends z.ZodType | undefined> = {
|
||||
session: { user: { id: number; username: string; rank: number; look: string; mail: string } }
|
||||
} & (TSchema extends z.ZodType ? { data: z.infer<TSchema> } : object)
|
||||
session: { user: { id: number; username: string; rank: number; look: string; mail: string } };
|
||||
} & (TSchema extends z.ZodType ? { data: z.infer<TSchema> } : object);
|
||||
|
||||
/**
|
||||
* Create a server action with admin auth + optional permission + optional Zod validation.
|
||||
@@ -38,54 +38,58 @@ export function adminAction<TSchema extends z.ZodType | undefined = undefined>(
|
||||
input: TSchema extends z.ZodType ? z.input<TSchema> : void,
|
||||
): Promise<ActionResult> => {
|
||||
try {
|
||||
const apiCtx = await getApiAdminContext()
|
||||
if (!apiCtx) return actionError('Unauthorized')
|
||||
const apiCtx = await getApiAdminContext();
|
||||
if (!apiCtx) return actionError("Unauthorized");
|
||||
|
||||
if (options.permission) {
|
||||
if (!canAccess(apiCtx.permissions, options.permission, apiCtx.session.user.rank)) {
|
||||
await logAuthorizationEvent({
|
||||
kind: 'permission.denied', userId: apiCtx.session.user.id,
|
||||
username: apiCtx.session.user.name ?? undefined, rank: apiCtx.session.user.rank,
|
||||
permission: options.permission, source: 'adminAction', reason: 'Permission check denied',
|
||||
})
|
||||
return actionError('Unauthorized')
|
||||
kind: "permission.denied",
|
||||
userId: apiCtx.session.user.id,
|
||||
username: apiCtx.session.user.name ?? undefined,
|
||||
rank: apiCtx.session.user.rank,
|
||||
permission: options.permission,
|
||||
source: "adminAction",
|
||||
reason: "Permission check denied",
|
||||
});
|
||||
return actionError("Unauthorized");
|
||||
}
|
||||
}
|
||||
|
||||
let data: unknown
|
||||
let data: unknown;
|
||||
if (options.schema) {
|
||||
const parsed = options.schema.safeParse(input)
|
||||
const parsed = options.schema.safeParse(input);
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
ok: false,
|
||||
error: 'Validation failed',
|
||||
error: "Validation failed",
|
||||
fieldErrors: parsed.error.flatten().fieldErrors as Record<string, string[]>,
|
||||
}
|
||||
};
|
||||
}
|
||||
data = parsed.data
|
||||
data = parsed.data;
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
session: apiCtx.session,
|
||||
...(options.schema ? { data } : {}),
|
||||
} as AdminActionContext<TSchema>
|
||||
} as AdminActionContext<TSchema>;
|
||||
|
||||
return await handler(ctx)
|
||||
return await handler(ctx);
|
||||
} catch (error) {
|
||||
return handleActionError(error)
|
||||
return handleActionError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ── Auth action wrapper (no permissions) ─────────────────────────────
|
||||
|
||||
interface AuthActionOptions<TSchema extends z.ZodType | undefined = undefined> {
|
||||
schema?: TSchema
|
||||
schema?: TSchema;
|
||||
}
|
||||
|
||||
type AuthActionContext<TSchema extends z.ZodType | undefined> = {
|
||||
session: { user: { id: number; username: string; rank: number; look: string; mail: string } }
|
||||
} & (TSchema extends z.ZodType ? { data: z.infer<TSchema> } : object)
|
||||
session: { user: { id: number; username: string; rank: number; look: string; mail: string } };
|
||||
} & (TSchema extends z.ZodType ? { data: z.infer<TSchema> } : object);
|
||||
|
||||
/**
|
||||
* Create a server action with auth only (no permission check).
|
||||
@@ -99,30 +103,30 @@ export function authAction<TSchema extends z.ZodType | undefined = undefined>(
|
||||
input: TSchema extends z.ZodType ? z.input<TSchema> : void,
|
||||
): Promise<ActionResult> => {
|
||||
try {
|
||||
const session = await auth()
|
||||
if (!session?.user) return actionError('Unauthorized')
|
||||
const session = await auth();
|
||||
if (!session?.user) return actionError("Unauthorized");
|
||||
|
||||
let data: unknown
|
||||
let data: unknown;
|
||||
if (options.schema) {
|
||||
const parsed = options.schema.safeParse(input)
|
||||
const parsed = options.schema.safeParse(input);
|
||||
if (!parsed.success) {
|
||||
return {
|
||||
ok: false,
|
||||
error: 'Validation failed',
|
||||
error: "Validation failed",
|
||||
fieldErrors: parsed.error.flatten().fieldErrors as Record<string, string[]>,
|
||||
}
|
||||
};
|
||||
}
|
||||
data = parsed.data
|
||||
data = parsed.data;
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
session,
|
||||
...(options.schema ? { data } : {}),
|
||||
} as AuthActionContext<TSchema>
|
||||
} as AuthActionContext<TSchema>;
|
||||
|
||||
return await handler(ctx)
|
||||
return await handler(ctx);
|
||||
} catch (error) {
|
||||
return handleActionError(error)
|
||||
return handleActionError(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user