191 lines
7.3 KiB
TypeScript
191 lines
7.3 KiB
TypeScript
import { PlanFeatureUsageDto } from "~/application/dtos/subscriptions/PlanFeatureUsageDto";
|
|
import { EntityWithDetails, findEntityByName, findEntityBySlug, getEntityByIdOrName, getEntityByPrefix } from "../../db/entities/entities.db.server";
|
|
import { getPlanFeatureUsage } from "../../services/.server/subscriptionService";
|
|
import { Entity } from "@toolctrl/db/prisma-browser";
|
|
import { Params } from "react-router";
|
|
import { getAbsoluteUrl } from "../../url.server";
|
|
import { adminPath, appPath } from "~/utils/app/CanonicalUrlUtils";
|
|
import { getHostRoutingContextFromRequest } from "~/utils/app/HostRouting.server";
|
|
import { isReservedTenantChildRouteSlug } from "~/utils/services/tenantService";
|
|
|
|
export namespace EntitiesApi {
|
|
export type GetEntityData = {
|
|
entity: EntityWithDetails;
|
|
featureUsageEntity?: PlanFeatureUsageDto | undefined;
|
|
};
|
|
export type Routes = {
|
|
list?: string;
|
|
new?: string;
|
|
overview?: string;
|
|
edit?: string;
|
|
publicUrl?: string;
|
|
import?: string;
|
|
export?: string;
|
|
group?: string;
|
|
};
|
|
export async function get({ entity, tenantId, userId }: { entity: { id?: string; name?: string }; tenantId?: string | null; userId?: string }) {
|
|
const item = await getEntityByIdOrName({ tenantId, ...entity });
|
|
|
|
const featureUsageEntity = tenantId ? await getPlanFeatureUsage(tenantId, item.name) : undefined;
|
|
await fillSystemProperties({ entity: item, tenantId, userId });
|
|
const data: GetEntityData = {
|
|
entity: item,
|
|
featureUsageEntity,
|
|
};
|
|
return data;
|
|
}
|
|
export async function validateEntity({
|
|
tenantId,
|
|
name,
|
|
slug,
|
|
order,
|
|
prefix,
|
|
entity,
|
|
}: {
|
|
tenantId: string | null;
|
|
name: string;
|
|
slug: string;
|
|
order: number | null;
|
|
prefix: string;
|
|
entity?: Entity;
|
|
}) {
|
|
const errors: string[] = [];
|
|
if (isReservedTenantChildRouteSlug(slug)) {
|
|
errors.push(`Reserved entity slug '${slug}' conflicts with a workspace route`);
|
|
}
|
|
|
|
if (!entity || entity?.name !== name) {
|
|
const existingName = await findEntityByName({ tenantId, name });
|
|
if (existingName) {
|
|
errors.push(`Existing entity with name '${name}': ${existingName.slug}`);
|
|
}
|
|
}
|
|
|
|
if (!entity || entity?.slug !== slug) {
|
|
const existingSlug = await findEntityBySlug(slug);
|
|
if (existingSlug) {
|
|
errors.push(`Existing entity with slug '${slug}': ${existingSlug.slug}`);
|
|
}
|
|
}
|
|
|
|
// if (order) {
|
|
// if (!entity || entity?.order !== order) {
|
|
// const existingOrder = await getEntityByOrder(order);
|
|
// if (existingOrder) {
|
|
// errors.push(`Existing entity with order '${order}': ${existingOrder.slug}`);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
if (!entity || entity?.prefix !== prefix) {
|
|
const existingPrefix = await getEntityByPrefix(prefix);
|
|
if (existingPrefix) {
|
|
errors.push(`Existing entity with prefix '${prefix}': ${existingPrefix.slug}`);
|
|
}
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
export function getNoCodeRoutes({ request, params }: { request: Request; params: Params }): EntitiesApi.Routes {
|
|
const url = new URL(request.url);
|
|
const hostContext = getHostRoutingContextFromRequest(request);
|
|
const isAdminRequest = hostContext === "admin";
|
|
const isAppRequest = hostContext === "app";
|
|
const adminRoute = (path: string) => adminPath(path, { mode: "host-aware" });
|
|
const tenantRoute = (path: string) => appPath({ tenantSlug: params?.tenant ?? "", path, mode: "host-aware" });
|
|
|
|
if (params.group) {
|
|
if (isAdminRequest) {
|
|
const routes: EntitiesApi.Routes = {
|
|
list: adminRoute("entities/:entity/no-code/:entity"),
|
|
new: adminRoute("entities/:entity/no-code/:entity/new"),
|
|
overview: adminRoute("entities/:entity/no-code/:entity/:id"),
|
|
edit: adminRoute("entities/:entity/no-code/:entity/:id/edit"),
|
|
import: adminRoute("entities/:entity/no-code/:entity/import"),
|
|
export: adminRoute("entities/:entity/no-code/:entity/export"),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
group: adminRoute("entities/groups"),
|
|
};
|
|
return routes;
|
|
} else if (isAppRequest) {
|
|
const routes: EntitiesApi.Routes = {
|
|
list: tenantRoute(`g/${params.group}/:entity`),
|
|
new: tenantRoute(`g/${params.group}/:entity/new`),
|
|
overview: tenantRoute(`g/${params.group}/:entity/:id`),
|
|
edit: tenantRoute(`g/${params.group}/:entity/:id/edit`),
|
|
import: tenantRoute(`g/${params.group}/:entity/import`),
|
|
export: tenantRoute(`g/${params.group}/:entity/export`),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
group: tenantRoute(`g/${params.group}`),
|
|
};
|
|
return routes;
|
|
}
|
|
} else if (isAdminRequest && isPathOrChild(url.pathname, "/crm")) {
|
|
const routes: EntitiesApi.Routes = {
|
|
list: adminRoute("crm/:entity"),
|
|
new: adminRoute("crm/:entity/new"),
|
|
overview: adminRoute("crm/:entity/:id"),
|
|
edit: adminRoute("crm/:entity/:id/edit"),
|
|
import: adminRoute("crm/:entity/import"),
|
|
export: adminRoute("crm/:entity/export"),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
};
|
|
return routes;
|
|
} else if (isAdminRequest) {
|
|
const routes: EntitiesApi.Routes = {
|
|
list: adminRoute("entities/:entity/no-code/:entity"),
|
|
new: adminRoute("entities/:entity/no-code/:entity/new"),
|
|
overview: adminRoute("entities/:entity/no-code/:entity/:id"),
|
|
edit: adminRoute("entities/:entity/no-code/:entity/:id/edit"),
|
|
import: adminRoute("entities/:entity/no-code/:entity/import"),
|
|
export: adminRoute("entities/:entity/no-code/:entity/export"),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
};
|
|
return routes;
|
|
} else if (params?.tenant && isPathOrChild(url.pathname, `/${params.tenant}/crm`)) {
|
|
const routes: EntitiesApi.Routes = {
|
|
list: tenantRoute("crm/:entity"),
|
|
new: tenantRoute("crm/:entity/new"),
|
|
overview: tenantRoute("crm/:entity/:id"),
|
|
edit: tenantRoute("crm/:entity/:id/edit"),
|
|
import: tenantRoute("crm/:entity/import"),
|
|
export: tenantRoute("crm/:entity/export"),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
};
|
|
return routes;
|
|
}
|
|
const routes: EntitiesApi.Routes = {
|
|
list: tenantRoute(":entity"),
|
|
new: tenantRoute(":entity/new"),
|
|
overview: tenantRoute(":entity/:id"),
|
|
edit: tenantRoute(":entity/:id/edit"),
|
|
import: tenantRoute(":entity/import"),
|
|
export: tenantRoute(":entity/export"),
|
|
publicUrl: getAbsoluteUrl(request, `/public/:entity/:id`),
|
|
};
|
|
return routes;
|
|
}
|
|
export async function fillSystemProperties({
|
|
entity,
|
|
tenantId,
|
|
userId,
|
|
}: {
|
|
entity: EntityWithDetails;
|
|
tenantId: string | null | undefined;
|
|
userId: string | undefined;
|
|
}) {
|
|
// const userProp = entity.properties.find((f) => f.name === "user");
|
|
// if (userProp) {
|
|
// const users = await adminGetAllUsersNames();
|
|
// userProp.options = users.map((f) => ({
|
|
// value: f.email,
|
|
// name: f.email,
|
|
// })) as PropertyOption[];
|
|
// }
|
|
}
|
|
}
|
|
|
|
function isPathOrChild(pathname: string, prefix: string) {
|
|
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
}
|