Merge remote-tracking branch 'origin/release/v2' into feat/pr-6691-hardening

# Conflicts:
#	apps/nextjs/src/app/[locale]/layout.tsx
#	packages/api/src/router/widgets/anchor-notes.ts
#	packages/api/src/router/widgets/archive-team-warrior.ts
#	packages/api/src/router/widgets/audio-stats.ts
#	packages/api/src/router/widgets/bazarr.ts
#	packages/api/src/router/widgets/coolify.ts
#	packages/api/src/router/widgets/firewall.ts
#	packages/api/src/router/widgets/immich.ts
#	packages/api/src/router/widgets/paperless-ngx.ts
#	packages/api/src/router/widgets/patchmon.ts
#	packages/api/src/router/widgets/speedtest-tracker.ts
#	packages/api/src/router/widgets/tracearr.ts
#	packages/api/src/router/widgets/traefik.ts
#	packages/api/src/router/widgets/umami.ts
#	packages/api/src/router/widgets/uptime-kuma.ts
#	packages/api/src/router/widgets/vpn.ts
#	packages/api/src/router/widgets/wud.ts
#	packages/definitions/src/widget-integration-map.ts
#	packages/widgets/src/anchor-note/index.ts
#	packages/widgets/src/archive-team-warrior/index.ts
#	packages/widgets/src/audio-stats/index.ts
#	packages/widgets/src/bazarr/index.ts
#	packages/widgets/src/coolify/index.ts
#	packages/widgets/src/firewall/index.ts
#	packages/widgets/src/immich/album-carousel/index.ts
#	packages/widgets/src/immich/server-stats/index.ts
#	packages/widgets/src/paperless-ngx/index.ts
#	packages/widgets/src/patchmon/index.ts
#	packages/widgets/src/speedtest-tracker/index.ts
#	packages/widgets/src/system-disks/index.ts
#	packages/widgets/src/system-resources/index.ts
#	packages/widgets/src/tracearr/index.ts
#	packages/widgets/src/traefik/index.ts
#	packages/widgets/src/umami/index.ts
#	packages/widgets/src/uptime-kuma/index.ts
#	packages/widgets/src/vpn/index.ts
#	packages/widgets/src/wud/index.ts
This commit is contained in:
ajnart committed 2026-08-26 15:12:49 +02:00
commit ac7e3f2ef4
201 files changed
+13795 -981

No files matched your search

+30 -3
View File
@@ -85,7 +85,9 @@ interface BoardItemPlacementRectangle {
const boardItemPlacementTails = new Map<string, Promise<void>>();
const maxManageOverviewPreviewRows = 12;
const manageOverviewInputSchema = z.object({ fullPreview: z.boolean().default(false) }).optional();
const manageOverviewInputSchema = z
.object({ fullPreview: z.boolean().default(false), userId: z.string().optional() })
.optional();
const serializeBoardItemPlacementAsync = async <T>(boardId: string, operation: () => Promise<T>) => {
const previous = boardItemPlacementTails.get(boardId) ?? Promise.resolve();
@@ -207,6 +209,7 @@ const searchAccessibleBoardsAsync = async (
group: {
with: {
boardPermissions: {},
permissions: {},
},
},
},
@@ -363,9 +366,14 @@ export const boardRouter = createTRPCRouter({
}));
}),
getManageOverview: publicProcedure.input(manageOverviewInputSchema).query(async ({ ctx, input }) => {
const userId = ctx.session?.user.id;
const sessionUserId = ctx.session?.user.id;
const userId = input?.userId ?? sessionUserId;
if (userId !== sessionUserId && !ctx.session?.user.permissions.includes("admin")) {
throw new TRPCError({ code: "FORBIDDEN", message: "You are not allowed to view boards for this user" });
}
const { boardIds, currentUser, groupMemberships } = await getBoardAccessContextAsync(ctx.db, userId);
const groupPermissionWhere = getBoardGroupPermissionWhere(groupMemberships);
const canViewAllBoards = getCanViewAllBoards(groupMemberships);
const dbBoards = await ctx.db.query.boards.findMany({
columns: {
@@ -415,7 +423,7 @@ export const boardRouter = createTRPCRouter({
orderBy: (section, { asc }) => [asc(section.xOffset), asc(section.id)],
},
},
where: getAccessibleBoardsWhere(ctx.session?.user.permissions.includes("board-view-all"), userId, boardIds),
where: getAccessibleBoardsWhere(canViewAllBoards, userId, boardIds),
});
const previewLayoutIds = dbBoards.flatMap((board) => board.layouts.map((layout) => layout.id));
@@ -2615,6 +2623,7 @@ const getBoardAccessContextAsync = async (db: Database, userId: string | undefin
group: {
with: {
boardPermissions: {},
permissions: {},
},
},
},
@@ -2638,6 +2647,24 @@ const getBoardAccessContextAsync = async (db: Database, userId: string | undefin
return { boardIds, currentUser, groupMemberships };
};
const getCanViewAllBoards = (
groupMemberships: Awaited<ReturnType<typeof getBoardAccessContextAsync>>["groupMemberships"],
) => {
const permissions = new Set(
groupMemberships.flatMap((membership) => membership.group.permissions.map(({ permission }) => permission)),
);
return getPermissionsWithChildren([...permissions]).includes("board-view-all");
};
export const getAccessibleBoardIdsForUserAsync = async (db: Database, userId: string) => {
const { boardIds, groupMemberships } = await getBoardAccessContextAsync(db, userId);
const accessibleBoards = await db.query.boards.findMany({
columns: { id: true },
where: getAccessibleBoardsWhere(getCanViewAllBoards(groupMemberships), userId, boardIds),
});
return new Set(accessibleBoards.map(({ id }) => id));
};
const getBoardGroupPermissionWhere = (
groupMemberships: Awaited<ReturnType<typeof getBoardAccessContextAsync>>["groupMemberships"],
) =>