138 lines
4.0 KiB
TypeScript
138 lines
4.0 KiB
TypeScript
import { JsonPropertyDto } from "~/modules/jsonProperties/dtos/JsonPropertyTypeDto";
|
|
import { TFunction } from "i18next";
|
|
import { JsonPropertiesValuesDto } from "~/modules/jsonProperties/dtos/JsonPropertiesValuesDto";
|
|
import { Portal } from "@toolctrl/db/prisma-browser";
|
|
|
|
export type PortalConfiguration = {
|
|
enabled: boolean;
|
|
forTenants: boolean;
|
|
pricing: boolean;
|
|
analytics: boolean;
|
|
domains?: {
|
|
enabled: boolean;
|
|
provider: "fly";
|
|
portalAppId: string;
|
|
records?: { A: string; AAAA: string };
|
|
};
|
|
metadata: Array<JsonPropertyDto> | undefined;
|
|
pages: Array<PortalPageConfigDto>;
|
|
};
|
|
|
|
export type PortalPageConfigDto = {
|
|
name: string;
|
|
title: string;
|
|
slug: string;
|
|
properties: Array<JsonPropertyDto>;
|
|
errors?: (params: { portal: Portal; page: { attributes: JsonPropertiesValuesDto } | null }) => string[];
|
|
};
|
|
|
|
export function getAppPortalConfiguration({ t }: { t: TFunction }): PortalConfiguration {
|
|
const titleProperty: JsonPropertyDto = {
|
|
name: "title",
|
|
title: t("models.portal.title"),
|
|
type: "string",
|
|
required: true,
|
|
};
|
|
const descriptionProperty: JsonPropertyDto = {
|
|
name: "description",
|
|
title: t("models.portal.description"),
|
|
type: "string",
|
|
required: false,
|
|
};
|
|
const htmlProperty: JsonPropertyDto = {
|
|
name: "html",
|
|
title: t("shared.content"),
|
|
type: "wysiwyg",
|
|
required: true,
|
|
};
|
|
|
|
const seoProperties: JsonPropertyDto[] = [
|
|
{
|
|
name: "seoTitle",
|
|
title: t("shared.title"),
|
|
type: "string",
|
|
required: false,
|
|
group: t("models.portal.seo"),
|
|
},
|
|
{
|
|
name: "seoDescription",
|
|
title: t("shared.description"),
|
|
type: "string",
|
|
required: false,
|
|
group: t("models.portal.seo"),
|
|
},
|
|
{
|
|
name: "seoImage",
|
|
title: t("shared.image"),
|
|
type: "image",
|
|
required: false,
|
|
group: t("models.portal.seo"),
|
|
},
|
|
];
|
|
|
|
const conf: PortalConfiguration = {
|
|
// Portals ship gated OFF by default so the boilerplate stays lean; a
|
|
// deployment opts in with PORTALS_ENABLED=true (e.g. the e2e/readiness
|
|
// target). The route files are always mounted but the loaders fail closed
|
|
// (400 "disabled") until this flag is set, so flipping the env is the only
|
|
// switch needed at runtime.
|
|
enabled: process.env.PORTALS_ENABLED === "true",
|
|
forTenants: true,
|
|
pricing: true,
|
|
analytics: true,
|
|
domains: {
|
|
enabled: true,
|
|
provider: "fly",
|
|
portalAppId: "app-portal",
|
|
records: {
|
|
A: "66.241.125.25",
|
|
AAAA: "2a09:8280:1::31:5dc2:0",
|
|
},
|
|
},
|
|
metadata: [],
|
|
pages: [
|
|
{
|
|
name: "pricing",
|
|
title: t("models.portal.pages.pricing"),
|
|
slug: "/plans",
|
|
properties: [titleProperty, descriptionProperty, ...seoProperties],
|
|
errors: (params) => {
|
|
let errors: string[] = [];
|
|
if (!params.portal.stripeAccountId) {
|
|
errors.push(t("models.portal.pages.errors.stripeAccountMissing"));
|
|
}
|
|
return errors;
|
|
},
|
|
},
|
|
{
|
|
name: "privacy-policy",
|
|
title: t("models.portal.pages.privacyPolicy"),
|
|
slug: "/privacy-policy",
|
|
properties: [titleProperty, descriptionProperty, htmlProperty, ...seoProperties],
|
|
errors: (params) => {
|
|
const errors: string[] = [];
|
|
if (!params.page?.attributes?.html) {
|
|
errors.push(t("models.portal.pages.errors.privacyPolicyContentMissing"));
|
|
}
|
|
return errors;
|
|
},
|
|
},
|
|
{
|
|
name: "terms-and-conditions",
|
|
title: t("models.portal.pages.termsAndConditions"),
|
|
slug: "/terms-and-conditions",
|
|
properties: [titleProperty, descriptionProperty, htmlProperty, ...seoProperties],
|
|
errors: (params) => {
|
|
const errors: string[] = [];
|
|
if (!params.page?.attributes?.html) {
|
|
errors.push(t("models.portal.pages.errors.termsContentMissing"));
|
|
}
|
|
return errors;
|
|
},
|
|
},
|
|
],
|
|
};
|
|
|
|
return conf;
|
|
}
|