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

+123 -104
View File
@@ -7,47 +7,50 @@ import { prisma } from "./prisma";
type PrismaModel = any;
interface AdminListConfig<TRow> {
/** Permission slug required to view this page */
permission: string;
/** Prisma model name (e.g. 'user', 'ban', 'room') */
model: string;
/** Search field paths for OR filter. Use dot notation for nested relations (e.g. 'owner.username') */
searchFields: string[];
/** Default sort when no sort param provided. Default: { id: 'desc' } */
defaultSort?: Record<string, "asc" | "desc">;
/** Default items per page. Default: 20 */
defaultPerPage?: number;
/** Prisma include clause for relations */
include?: Record<string, unknown>;
/** Prisma select clause (mutually exclusive with include) */
select?: Record<string, unknown>;
/** Fixed WHERE conditions merged with search (e.g. { online: '1' }) */
baseWhere?: Record<string, unknown>;
/**
* Map URL ?filter_* params to Prisma WHERE conditions. Each entry receives
* the raw string value from the URL and returns the WHERE fragment to merge.
* Returning null/undefined drops the filter.
*
* @example
* filterMap: {
* filter_rank: (v) => ({ rank: { gte: Number(v) } }),
* filter_online: (v) => ({ online: v }),
* }
*/
filterMap?: Record<string, (value: string) => Record<string, unknown> | null | undefined>;
/** Transform raw Prisma row to the shape needed by the table component */
mapRow?: (row: PrismaModel) => TRow;
/** Permission slug required to view this page */
permission: string;
/** Prisma model name (e.g. 'user', 'ban', 'room') */
model: string;
/** Search field paths for OR filter. Use dot notation for nested relations (e.g. 'owner.username') */
searchFields: string[];
/** Default sort when no sort param provided. Default: { id: 'desc' } */
defaultSort?: Record<string, "asc" | "desc">;
/** Default items per page. Default: 20 */
defaultPerPage?: number;
/** Prisma include clause for relations */
include?: Record<string, unknown>;
/** Prisma select clause (mutually exclusive with include) */
select?: Record<string, unknown>;
/** Fixed WHERE conditions merged with search (e.g. { online: '1' }) */
baseWhere?: Record<string, unknown>;
/**
* Map URL ?filter_* params to Prisma WHERE conditions. Each entry receives
* the raw string value from the URL and returns the WHERE fragment to merge.
* Returning null/undefined drops the filter.
*
* @example
* filterMap: {
* filter_rank: (v) => ({ rank: { gte: Number(v) } }),
* filter_online: (v) => ({ online: v }),
* }
*/
filterMap?: Record<
string,
(value: string) => Record<string, unknown> | null | undefined
>;
/** Transform raw Prisma row to the shape needed by the table component */
mapRow?: (row: PrismaModel) => TRow;
}
interface AdminListResult<TRow> {
rows: TRow[];
total: number;
page: number;
perPage: number;
lastPage: number;
locale: string;
permissions: PermissionSet;
rank: number;
rows: TRow[];
total: number;
page: number;
perPage: number;
lastPage: number;
locale: string;
permissions: PermissionSet;
rank: number;
}
/**
@@ -55,17 +58,22 @@ interface AdminListResult<TRow> {
* e.g. 'owner.username' => { owner: { username: { contains: search } } }
* e.g. 'username' => { username: { contains: search } }
*/
function buildFieldCondition(fieldPath: string, search: string): Record<string, unknown> {
const parts = fieldPath.split(".");
if (parts.length === 1) {
return { [parts[0]]: { contains: search } };
}
// Build nested object from right to left
let result: Record<string, unknown> = { [parts[parts.length - 1]]: { contains: search } };
for (let i = parts.length - 2; i >= 0; i--) {
result = { [parts[i]]: result };
}
return result;
function buildFieldCondition(
fieldPath: string,
search: string,
): Record<string, unknown> {
const parts = fieldPath.split(".");
if (parts.length === 1) {
return { [parts[0]]: { contains: search } };
}
// Build nested object from right to left
let result: Record<string, unknown> = {
[parts[parts.length - 1]]: { contains: search },
};
for (let i = parts.length - 2; i >= 0; i--) {
result = { [parts[i]]: result };
}
return result;
}
/**
@@ -74,72 +82,83 @@ function buildFieldCondition(fieldPath: string, search: string): Record<string,
* Prisma query with pagination, and row mapping.
*/
export async function fetchAdminList<TRow = PrismaModel>(
config: AdminListConfig<TRow>,
paramsPromise: Promise<{ locale: string }>,
searchParamsPromise: Promise<Record<string, string>>,
config: AdminListConfig<TRow>,
paramsPromise: Promise<{ locale: string }>,
searchParamsPromise: Promise<Record<string, string>>,
): Promise<AdminListResult<TRow>> {
const { locale } = await paramsPromise;
const { session, permissions } = await getAdminContext();
const { locale } = await paramsPromise;
const { session, permissions } = await getAdminContext();
if (!canAccess(permissions, config.permission, session.user.rank)) {
redirect("/admin");
}
if (!canAccess(permissions, config.permission, session.user.rank)) {
redirect("/admin");
}
const rawParams = await searchParamsPromise;
const sp = new URLSearchParams(rawParams);
const parsed = parseListParams(sp);
const perPage = config.defaultPerPage ? Number(rawParams.perPage) || config.defaultPerPage : parsed.perPage;
const { search, page, sort, order } = parsed;
const rawParams = await searchParamsPromise;
const sp = new URLSearchParams(rawParams);
const parsed = parseListParams(sp);
const perPage = config.defaultPerPage
? Number(rawParams.perPage) || config.defaultPerPage
: parsed.perPage;
const { search, page, sort, order } = parsed;
// Build WHERE clause
const searchWhere =
search && config.searchFields.length > 0
? { OR: config.searchFields.map((field) => buildFieldCondition(field, search)) }
: {};
// Build WHERE clause
const searchWhere =
search && config.searchFields.length > 0
? {
OR: config.searchFields.map((field) =>
buildFieldCondition(field, search),
),
}
: {};
// Apply URL filter_* params via filterMap
const filterWhere: Record<string, unknown> = {};
if (config.filterMap) {
for (const [paramKey, build] of Object.entries(config.filterMap)) {
const value = rawParams[paramKey];
if (!value) continue;
const fragment = build(value);
if (fragment) Object.assign(filterWhere, fragment);
}
}
// Apply URL filter_* params via filterMap
const filterWhere: Record<string, unknown> = {};
if (config.filterMap) {
for (const [paramKey, build] of Object.entries(config.filterMap)) {
const value = rawParams[paramKey];
if (!value) continue;
const fragment = build(value);
if (fragment) Object.assign(filterWhere, fragment);
}
}
const where = {
...(config.baseWhere ?? {}),
...filterWhere,
...searchWhere,
};
const where = {
...(config.baseWhere ?? {}),
...filterWhere,
...searchWhere,
};
// Build orderBy
const orderBy = sort ? { [sort]: order } : (config.defaultSort ?? { id: "desc" as const });
// Build orderBy
const orderBy = sort
? { [sort]: order }
: (config.defaultSort ?? { id: "desc" as const });
// Access Prisma model dynamically
const delegate = (prisma as PrismaModel)[config.model];
// Access Prisma model dynamically
const delegate = (prisma as PrismaModel)[config.model];
const queryArgs: PrismaModel = {
where,
orderBy,
skip: (page - 1) * perPage,
take: perPage,
};
const queryArgs: PrismaModel = {
where,
orderBy,
skip: (page - 1) * perPage,
take: perPage,
};
if (config.include) queryArgs.include = config.include;
if (config.select) queryArgs.select = config.select;
if (config.include) queryArgs.include = config.include;
if (config.select) queryArgs.select = config.select;
const [rawRows, total] = await Promise.all([delegate.findMany(queryArgs), delegate.count({ where })]);
const [rawRows, total] = await Promise.all([
delegate.findMany(queryArgs),
delegate.count({ where }),
]);
const pagination = calcPagination(total, page, perPage);
const rows = config.mapRow ? rawRows.map(config.mapRow) : rawRows;
const pagination = calcPagination(total, page, perPage);
const rows = config.mapRow ? rawRows.map(config.mapRow) : rawRows;
return {
rows,
...pagination,
locale,
permissions,
rank: session.user.rank,
};
return {
rows,
...pagination,
locale,
permissions,
rank: session.user.rank,
};
}