Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/services/admin/admin-booking-workspace.server.ts
T

155 lines
4.7 KiB
TypeScript

import type { AdminPermissionName } from "@hivegrid/db/admin-permissions";
import { adminHasPermission } from "@hivegrid/db/admin-rbac.server";
import { writeAdminSalesAudit } from "@hivegrid/db/admin-sales-core.server";
import { runAsAdminIdentity } from "~/services/admin/admin-identity.server";
import {
extractWebActorIp,
extractWebActorUserAgent,
extractWebRequestId,
} from "~/utils/audit/withAuditContext";
import { db } from "~/utils/db.server";
import { NOT_FOUND_ENVELOPE } from "~/utils/errors.server";
export async function runAsAdminBookingOperator<T>(
request: Request,
permission:
| Extract<
AdminPermissionName,
| "admin_sales.booking.manage"
| "admin_sales.communication.read_all"
>
| readonly Extract<
AdminPermissionName,
| "admin_sales.booking.manage"
| "admin_sales.communication.read_all"
>[],
operation: (context: {
readonly adminUserId: string;
readonly email: string;
readonly canReadAllCommunication: boolean;
readonly actor: (reason: string) => {
readonly adminUserId: string;
readonly email: string;
readonly reason: string;
readonly requestId: string | null;
readonly ip: string | null;
readonly userAgent: string | null;
};
}) => Promise<T>
): Promise<T> {
return runAsAdminIdentity(request, permission, async (session) => {
const admin = await db.adminUser.findFirst({
where: { id: session.adminUserId, status: "active" },
select: { id: true, email: true },
});
if (!admin) {
throw Response.json(NOT_FOUND_ENVELOPE, { status: 404 });
}
const canReadAllCommunication = await adminHasPermission(
admin.id,
"admin_sales.communication.read_all",
db
);
const actorBase = {
adminUserId: admin.id,
email: admin.email,
requestId: extractWebRequestId(request),
ip: extractWebActorIp(request),
userAgent: extractWebActorUserAgent(request),
};
return operation({
adminUserId: admin.id,
email: admin.email,
canReadAllCommunication,
actor: (reason) => ({ ...actorBase, reason }),
});
});
}
export async function readAdminBookingWorkspace(context: {
readonly adminUserId: string;
readonly canReadAllCommunication: boolean;
readonly actor: (reason: string) => {
readonly adminUserId: string;
readonly email: string;
readonly reason: string;
readonly requestId: string | null;
readonly ip: string | null;
readonly userAgent: string | null;
};
}) {
const bookingWhere = context.canReadAllCommunication
? {}
: {
OR: [
{ adminUserId: context.adminUserId },
{
meetingType: {
hostAdminUserIds: { has: context.adminUserId },
},
},
],
};
const [meetingTypes, pages, bookings, connectedCalendars] =
await Promise.all([
db.adminMeetingType.findMany({
where: { archivedAt: null },
orderBy: [{ active: "desc" }, { name: "asc" }, { id: "asc" }],
}),
db.adminBookingPage.findMany({
where: { archivedAt: null },
orderBy: [
{ published: "desc" },
{ title: "asc" },
{ id: "asc" },
],
include: { meetingType: { select: { name: true } } },
}),
db.adminBooking.findMany({
where: bookingWhere,
orderBy: [{ startsAt: "asc" }, { id: "asc" }],
take: 200,
include: {
contact: {
select: { firstName: true, lastName: true },
},
meetingType: { select: { name: true } },
},
}),
db.adminCalendarConnection.count({
where: {
adminUserId: context.adminUserId,
archivedAt: null,
status: "active",
},
}),
]);
const foreignBookings = bookings.filter(
(booking) => booking.adminUserId !== context.adminUserId
);
if (foreignBookings.length > 0) {
await writeAdminSalesAudit(db, {
actor: context.actor("Fremde Team-Buchungen geöffnet"),
action: "admin.sales.booking.foreignBookings.read",
targetType: "AdminBooking",
targetId: foreignBookings[0]!.id,
after: {
foreignTargets: foreignBookings.map((booking) => ({
targetId: booking.id,
dataOwnerAdminUserId: booking.adminUserId,
})),
accessKind: "read",
},
});
}
return { meetingTypes, pages, bookings, connectedCalendars };
}
export function adminBookingOrigin(request: Request): string {
const configured = process.env.ADMIN_BOOKING_PUBLIC_ORIGIN?.trim();
if (configured) return configured.replace(/\/$/u, "");
const url = new URL(request.url);
return `${url.protocol}//meet.localhost:3000`;
}