142 lines
5.4 KiB
TypeScript
142 lines
5.4 KiB
TypeScript
import { Tenant } from "@toolctrl/db/prisma-browser";
|
|
import { LoaderFunctionArgs, redirect, ActionFunction } from "react-router";
|
|
import type { TFunction } from "i18next";
|
|
import { MetaTagsDto } from "~/application/dtos/seo/MetaTagsDto";
|
|
import { RowAccess } from "~/application/enums/entities/RowAccess";
|
|
import { EntitiesApi } from "~/utils/api/.server/EntitiesApi";
|
|
import { RowPermissionsApi } from "~/utils/api/.server/RowPermissionsApi";
|
|
import { RowsApi } from "~/utils/api/.server/RowsApi";
|
|
import UrlUtils from "~/utils/app/UrlUtils";
|
|
import { adminGetAllTenants, getTenant } from "~/utils/db/tenants.db.server";
|
|
import { UserWithDetails, getUser, getUsersByTenant } from "~/utils/db/users.db.server";
|
|
import RowHelper from "~/utils/helpers/RowHelper";
|
|
import RowsRequestUtils from "../utils/RowsRequestUtils";
|
|
import EventsService from "~/modules/events/services/.server/EventsService";
|
|
import { RowSharedDto } from "~/modules/events/dtos/RowSharedDto";
|
|
import { getRowsRouteErrorCode, getRowsRouteErrorStatus, translateRowsRouteError } from "./Rows_Error.server";
|
|
|
|
const allowedShareTypes = ["tenant", "user", "role", "group"] as const;
|
|
type ShareType = (typeof allowedShareTypes)[number];
|
|
|
|
function getShareType(value: FormDataEntryValue | null): ShareType | null {
|
|
const type = value?.toString();
|
|
return allowedShareTypes.includes(type as ShareType) ? (type as ShareType) : null;
|
|
}
|
|
|
|
export namespace Rows_Share {
|
|
export type ActionData = {
|
|
success?: string;
|
|
error?: string;
|
|
errorCode?: import("~/utils/api/.server/RowsApi").RowApiErrorCode;
|
|
};
|
|
|
|
export type LoaderData = {
|
|
meta: MetaTagsDto;
|
|
rowData: RowsApi.GetRowData;
|
|
routes: EntitiesApi.Routes;
|
|
tenants: Tenant[];
|
|
users: UserWithDetails[];
|
|
};
|
|
export const loader = async ({ request, params }: LoaderFunctionArgs) => {
|
|
const { t, userId, tenantId, entity } = await RowsRequestUtils.getLoader({ request, params });
|
|
if (!entity.isAutogenerated || entity.type === "system") {
|
|
throw redirect(tenantId ? UrlUtils.currentTenantUrl(params, "404") : "/404");
|
|
}
|
|
const rowData = await RowsApi.get(params.id!, {
|
|
entity,
|
|
tenantId,
|
|
userId,
|
|
});
|
|
if (!rowData.rowPermissions.canUpdate) {
|
|
throw Error(t("shared.unauthorized"));
|
|
}
|
|
let tenants: Tenant[] = [];
|
|
if (tenantId) {
|
|
tenants.push((await getTenant(tenantId))!);
|
|
} else {
|
|
tenants = await adminGetAllTenants();
|
|
}
|
|
const data: LoaderData = {
|
|
meta: [{ title: `${t("shared.share")} | ${RowHelper.getTextDescription({ entity, item: rowData.item, t })} | ${process.env.APP_NAME}` }],
|
|
rowData,
|
|
routes: EntitiesApi.getNoCodeRoutes({ request, params }),
|
|
tenants,
|
|
users: (await getUsersByTenant(tenantId)).filter((f) => f.id !== userId),
|
|
};
|
|
return Response.json(data);
|
|
};
|
|
|
|
export const action: ActionFunction = async ({ request, params }) => {
|
|
const { t, entity, tenantId, userId, form } = await RowsRequestUtils.getAction({ request, params });
|
|
const user = await getUser(userId);
|
|
if (!user) {
|
|
throw redirect(tenantId ? UrlUtils.currentTenantUrl(params, "404") : "/404");
|
|
}
|
|
|
|
const action = form.get("action");
|
|
if (!entity.isAutogenerated || entity.type === "system") {
|
|
throw redirect(tenantId ? UrlUtils.currentTenantUrl(params, "404") : "/404");
|
|
}
|
|
const rowData = await RowsApi.get(params.id!, {
|
|
entity,
|
|
tenantId,
|
|
userId,
|
|
});
|
|
if (!rowData.rowPermissions.canUpdate) {
|
|
throw Error(t("shared.unauthorized"));
|
|
}
|
|
|
|
if (action === "share") {
|
|
const type = getShareType(form.get("type"));
|
|
if (!type) {
|
|
return Response.json({ error: t("shared.invalidForm") }, { status: 400 });
|
|
}
|
|
const id = form.get("id")?.toString() ?? "";
|
|
const access = form.get("access")?.toString() as RowAccess;
|
|
try {
|
|
await RowPermissionsApi.share(rowData.item, {
|
|
type,
|
|
id,
|
|
access,
|
|
}, { currentTenantId: tenantId });
|
|
await EventsService.create({
|
|
request,
|
|
event: "row.shared",
|
|
tenantId,
|
|
userId,
|
|
data: {
|
|
id: rowData.item.id,
|
|
title: RowHelper.getTextDescription({ entity, item: rowData.item, t }),
|
|
entity: { id: entity.id, name: entity.name, slug: entity.slug, title: entity.title },
|
|
type,
|
|
to: id,
|
|
access,
|
|
user: { id: user.id, email: user.email },
|
|
} satisfies RowSharedDto,
|
|
});
|
|
return Response.json({ success: t("shared.saved") });
|
|
} catch (error: unknown) {
|
|
return rowActionError(t, error);
|
|
}
|
|
} else if (action === "set-access") {
|
|
const id = form.get("id")?.toString() ?? "";
|
|
const access = form.get("access")?.toString() as RowAccess;
|
|
await RowPermissionsApi.setAccessForRow(rowData.item.id, id, access);
|
|
return Response.json({ success: t("shared.saved") });
|
|
} else if (action === "remove") {
|
|
const id = form.get("id")?.toString() ?? "";
|
|
await RowPermissionsApi.delForRow(rowData.item.id, id);
|
|
return Response.json({ success: t("shared.deleted") });
|
|
} else {
|
|
return Response.json({ error: t("shared.invalidForm") }, { status: 400 });
|
|
}
|
|
};
|
|
}
|
|
|
|
function rowActionError(t: TFunction, error: unknown) {
|
|
return Response.json(
|
|
{ error: translateRowsRouteError(t, error), errorCode: getRowsRouteErrorCode(error) } satisfies Rows_Share.ActionData,
|
|
{ status: getRowsRouteErrorStatus(error) }
|
|
);
|
|
}
|