style: format code biome
Local Build and Deploy / deploy (push) Failing after 46s

This commit is contained in:
openhands committed 2026-07-13 21:57:41 +02:00
1 parent 8efd032cc6
commit df38dccbf1
735 files changed
+128321 -120870

No files matched your search

+169 -119
View File
@@ -1,12 +1,12 @@
import { unstable_cache } from "next/cache";
import { cache } from "react";
import { auth } from "./auth";
import { sessionUserId } from "./auth/session-user";
import { prisma } from "./prisma";
import { logAuthorizationEvent } from "./admin/authorization-events";
import { isDynamicSuperAdmin } from "./admin/authorization-policy";
import { resolveAuthorizationState } from "./admin/rank-authority";
import { auth } from "./auth";
import { sessionUserId } from "./auth/session-user";
import { redirectSafe } from "./foundation/security";
import { prisma } from "./prisma";
// Re-export PERMS from the standalone file (safe for client components)
export { PERMS } from "./permission-slugs";
@@ -20,12 +20,12 @@ export type { PermissionSet } from "@/types/admin";
import type { PermissionSet } from "@/types/admin";
function createEmptySet(): PermissionSet {
return {
has: () => false,
hasAny: () => false,
hasAll: () => false,
isSuperAdmin: false,
};
return {
has: () => false,
hasAny: () => false,
hasAll: () => false,
isSuperAdmin: false,
};
}
/**
@@ -33,8 +33,8 @@ function createEmptySet(): PermissionSet {
* Cached via unstable_cache with 60s TTL — invalidated via revalidateTag('permissions').
*/
const getCachedPermissionSlugs = unstable_cache(
async (userId: number, rank: number): Promise<string[]> => {
const rows = await prisma.$queryRaw<{ slug: string }[]>`
async (userId: number, rank: number): Promise<string[]> => {
const rows = await prisma.$queryRaw<{ slug: string }[]>`
SELECT DISTINCT p.slug
FROM acl_model_permissions mp
JOIN acl_permissions p ON p.id = mp.permission_id
@@ -49,10 +49,10 @@ const getCachedPermissionSlugs = unstable_cache(
WHERE ar.slug = ${`rank_${rank}`}
)
`;
return rows.map((r) => r.slug);
},
["user-permissions"],
{ revalidate: 60, tags: ["permissions"] },
return rows.map((r) => r.slug);
},
["user-permissions"],
{ revalidate: 60, tags: ["permissions"] },
);
/**
@@ -61,55 +61,59 @@ const getCachedPermissionSlugs = unstable_cache(
* Wrapped with React cache() to de-duplicate within the same request.
*/
export const loadUserPermissions = cache(async function loadUserPermissions(
userId: number,
rank: number,
highestRank: number | null,
userId: number,
rank: number,
highestRank: number | null,
): Promise<PermissionSet> {
try {
// Super admin bypasses all permission checks — zero DB queries
if (isDynamicSuperAdmin(rank, highestRank)) {
return {
has: () => true,
hasAny: () => true,
hasAll: () => true,
isSuperAdmin: true,
};
}
try {
// Super admin bypasses all permission checks — zero DB queries
if (isDynamicSuperAdmin(rank, highestRank)) {
return {
has: () => true,
hasAny: () => true,
hasAll: () => true,
isSuperAdmin: true,
};
}
const slugArray = await getCachedPermissionSlugs(userId, rank);
if (slugArray.length === 0) return createEmptySet();
const slugArray = await getCachedPermissionSlugs(userId, rank);
if (slugArray.length === 0) return createEmptySet();
const slugs = new Set(slugArray);
return {
has: (perm: string) => slugs.has(perm),
hasAny: (...perms: string[]) => perms.some((p) => slugs.has(p)),
hasAll: (...perms: string[]) => perms.every((p) => slugs.has(p)),
isSuperAdmin: false,
};
} catch (error) {
await logAuthorizationEvent({
kind: "permission.load_error",
userId,
rank,
source: "loadUserPermissions",
reason: "ACL query failed",
error,
});
// Fail-closed: return empty set on any error
return createEmptySet();
}
const slugs = new Set(slugArray);
return {
has: (perm: string) => slugs.has(perm),
hasAny: (...perms: string[]) => perms.some((p) => slugs.has(p)),
hasAll: (...perms: string[]) => perms.every((p) => slugs.has(p)),
isSuperAdmin: false,
};
} catch (error) {
await logAuthorizationEvent({
kind: "permission.load_error",
userId,
rank,
source: "loadUserPermissions",
reason: "ACL query failed",
error,
});
// Fail-closed: return empty set on any error
return createEmptySet();
}
});
const getCurrentAuthorizationState = cache(async (userId: number) =>
resolveAuthorizationState(userId, {
user: prisma.user,
highestRank: async () => {
const rows = await prisma.$queryRaw<{ highest_rank: number | bigint | null }[]>`
resolveAuthorizationState(userId, {
user: prisma.user,
highestRank: async () => {
const rows = await prisma.$queryRaw<
{ highest_rank: number | bigint | null }[]
>`
SELECT MAX(id) AS highest_rank FROM permission_ranks
`;
return rows[0]?.highest_rank == null ? null : Number(rows[0].highest_rank);
},
}),
return rows[0]?.highest_rank == null
? null
: Number(rows[0].highest_rank);
},
}),
);
// ── Context Helpers ─────────────────────────────────────────────────
@@ -119,23 +123,32 @@ const getCurrentAuthorizationState = cache(async (userId: number) =>
* Redirects to login if not authenticated.
*/
export async function getAdminContext() {
const session = await auth();
if (!session?.user) {
redirectSafe("/login", "/login");
}
const session = await auth();
if (!session?.user) {
redirectSafe("/login", "/login");
}
const userId = sessionUserId(session.user.id);
if (!userId) redirectSafe("/login", "/login");
const state = await getCurrentAuthorizationState(userId);
if (!state) redirectSafe("/login", "/login");
const permissions = await loadUserPermissions(userId, state.actor.rank, state.highestRank);
return {
session: {
...session,
user: { ...session.user, id: userId, username: state.actor.username, rank: state.actor.rank },
},
permissions,
};
const userId = sessionUserId(session.user.id);
if (!userId) redirectSafe("/login", "/login");
const state = await getCurrentAuthorizationState(userId);
if (!state) redirectSafe("/login", "/login");
const permissions = await loadUserPermissions(
userId,
state.actor.rank,
state.highestRank,
);
return {
session: {
...session,
user: {
...session.user,
id: userId,
username: state.actor.username,
rank: state.actor.rank,
},
},
permissions,
};
}
/**
@@ -143,29 +156,42 @@ export async function getAdminContext() {
* Returns null if not authenticated (caller handles 401).
*/
export async function getApiAdminContext() {
const session = await auth();
if (!session?.user) return null;
const session = await auth();
if (!session?.user) return null;
const userId = sessionUserId(session.user.id);
if (!userId) return null;
const state = await getCurrentAuthorizationState(userId);
if (!state) return null;
const permissions = await loadUserPermissions(userId, state.actor.rank, state.highestRank);
return {
session: {
...session,
user: { ...session.user, id: userId, username: state.actor.username, rank: state.actor.rank },
},
permissions,
};
const userId = sessionUserId(session.user.id);
if (!userId) return null;
const state = await getCurrentAuthorizationState(userId);
if (!state) return null;
const permissions = await loadUserPermissions(
userId,
state.actor.rank,
state.highestRank,
);
return {
session: {
...session,
user: {
...session.user,
id: userId,
username: state.actor.username,
rank: state.actor.rank,
},
},
permissions,
};
}
/**
* Check if user has a specific permission.
* All fallbacks are represented as ACL role permissions by migration 0011.
*/
export function canAccess(permissions: PermissionSet, slug: string, _rank?: number): boolean {
return permissions.has(slug);
export function canAccess(
permissions: PermissionSet,
slug: string,
_rank?: number,
): boolean {
return permissions.has(slug);
}
/**
@@ -173,49 +199,73 @@ export function canAccess(permissions: PermissionSet, slug: string, _rank?: numb
* Redirects to login if unauthenticated and to / without moderator ACL access.
*/
export async function getModContext() {
const session = await auth();
if (!session?.user) {
redirectSafe("/login", "/login");
}
const session = await auth();
if (!session?.user) {
redirectSafe("/login", "/login");
}
const userId = sessionUserId(session.user.id);
if (!userId) redirectSafe("/login", "/login");
const state = await getCurrentAuthorizationState(userId);
if (!state) redirectSafe("/", "/");
const permissions = await loadUserPermissions(userId, state.actor.rank, state.highestRank);
if (!canAccess(permissions, PERMS.MOD_DASHBOARD)) redirectSafe("/", "/");
return {
session: {
...session,
user: { ...session.user, id: userId, username: state.actor.username, rank: state.actor.rank },
},
permissions,
};
const userId = sessionUserId(session.user.id);
if (!userId) redirectSafe("/login", "/login");
const state = await getCurrentAuthorizationState(userId);
if (!state) redirectSafe("/", "/");
const permissions = await loadUserPermissions(
userId,
state.actor.rank,
state.highestRank,
);
if (!canAccess(permissions, PERMS.MOD_DASHBOARD)) redirectSafe("/", "/");
return {
session: {
...session,
user: {
...session.user,
id: userId,
username: state.actor.username,
rank: state.actor.rank,
},
},
permissions,
};
}
// ── Legacy single-check functions (kept for backward compatibility) ──
/** Check if a user has a CMS permission using their current database rank. */
export async function checkPermission(userId: number, _rank: number, permission: string): Promise<boolean> {
const state = await getCurrentAuthorizationState(userId);
if (!state) return false;
const perms = await loadUserPermissions(userId, state.actor.rank, state.highestRank);
return perms.has(permission);
export async function checkPermission(
userId: number,
_rank: number,
permission: string,
): Promise<boolean> {
const state = await getCurrentAuthorizationState(userId);
if (!state) return false;
const perms = await loadUserPermissions(
userId,
state.actor.rank,
state.highestRank,
);
return perms.has(permission);
}
/** Check multiple permissions (user needs ALL of them) */
export async function checkAllPermissions(
userId: number,
_rank: number,
permissions: string[],
userId: number,
_rank: number,
permissions: string[],
): Promise<boolean> {
const state = await getCurrentAuthorizationState(userId);
if (!state) return false;
const perms = await loadUserPermissions(userId, state.actor.rank, state.highestRank);
return perms.hasAll(...permissions);
const state = await getCurrentAuthorizationState(userId);
if (!state) return false;
const perms = await loadUserPermissions(
userId,
state.actor.rank,
state.highestRank,
);
return perms.hasAll(...permissions);
}
/** Check if user has admin access */
export async function hasAdminAccess(userId: number, rank: number): Promise<boolean> {
return checkPermission(userId, rank, PERMS.ADMIN_DASHBOARD);
export async function hasAdminAccess(
userId: number,
rank: number,
): Promise<boolean> {
return checkPermission(userId, rank, PERMS.ADMIN_DASHBOARD);
}