diff --git a/apps/web/src/config/pricing-table.tsx b/apps/web/src/config/pricing-table.tsx index ca03a21d..b6b2e415 100644 --- a/apps/web/src/config/pricing-table.tsx +++ b/apps/web/src/config/pricing-table.tsx @@ -1,8 +1,4 @@ -import type { - LimitsV1, - LimitsV2, - LimitsV3, -} from "@openstatus/db/src/schema/plan/schema"; +import type { Limits } from "@openstatus/db/src/schema/plan/schema"; import Link from "next/link"; import type React from "react"; @@ -30,7 +26,7 @@ export const pricingTableConfig: Record< { label: string; features: { - value: keyof LimitsV1 | keyof LimitsV2 | keyof LimitsV3; + value: keyof Limits; label: string; description?: React.ReactNode; // tooltip informations badge?: string; diff --git a/packages/db/src/schema/plan/schema.ts b/packages/db/src/schema/plan/schema.ts index 46f2baec..ac10c924 100644 --- a/packages/db/src/schema/plan/schema.ts +++ b/packages/db/src/schema/plan/schema.ts @@ -1,90 +1,49 @@ import { z } from "zod"; import { monitorFlyRegionSchema, monitorPeriodicitySchema } from "../constants"; -// This is not a database table but just a schema for the limits of the plan +// REMINDER: this is not a database table but just a schema for the limits of the plan +// default values are set to the free plan limits -export const limitsV1 = z.object({ +export const limitsSchema = z.object({ version: z.undefined(), - monitors: z.number(), - "synthetic-checks": z.number(), - periodicity: monitorPeriodicitySchema.array(), - "multi-region": z.boolean(), - "max-regions": z.number(), - "data-retention": z.enum(["14 days", "3 months", "12 months", "24 months"]), - // status pages - "status-pages": z.number(), - maintenance: z.boolean(), - "status-subscribers": z.boolean(), - "custom-domain": z.boolean(), - "password-protection": z.boolean(), - "white-label": z.boolean(), - // alerts - notifications: z.boolean(), - pagerduty: z.boolean(), - sms: z.boolean(), - "notification-channels": z.number(), - // collaboration - members: z.literal("Unlimited").or(z.number()), - "audit-log": z.boolean(), - regions: monitorFlyRegionSchema.array(), + /** + * Monitor limits + */ + monitors: z.number().default(1), + "synthetic-checks": z.number().default(30), + periodicity: monitorPeriodicitySchema.array().default(["10m", "30m", "1h"]), + "multi-region": z.boolean().default(true), + "max-regions": z.number().default(6), + "data-retention": z + .enum(["14 days", "3 months", "12 months", "24 months"]) + .default("14 days"), + regions: monitorFlyRegionSchema + .array() + .default(["ams", "gru", "iad", "jnb", "hkg", "syd"]), + "private-locations": z.boolean().default(false), + screenshots: z.boolean().default(false), + /** + * Status page limits + */ + "status-pages": z.number().default(1), + maintenance: z.boolean().default(true), + "monitor-values-visibility": z.boolean().default(true), + "status-subscribers": z.boolean().default(false), + "custom-domain": z.boolean().default(false), + "password-protection": z.boolean().default(false), + "white-label": z.boolean().default(false), + /** + * Notification limits + */ + notifications: z.boolean().default(true), + pagerduty: z.boolean().default(false), + sms: z.boolean().default(false), + "notification-channels": z.number().default(1), + /** + * Collaboration limits + */ + members: z.literal("Unlimited").or(z.number()).default(1), + "audit-log": z.boolean().default(false), }); -export type LimitsV1 = z.infer; -export const limitsV2 = limitsV1.extend({ - version: z.literal("v2"), - "private-locations": z.boolean(), - "monitor-values-visibility": z.boolean(), -}); - -export const limitsV3 = limitsV2.extend({ - version: z.literal("v3"), - screenshots: z.boolean(), -}); - -export type LimitsV2 = z.infer; -export type LimitsV3 = z.infer; - -const unknownLimit = z.discriminatedUnion("version", [ - limitsV1, - limitsV2, - limitsV3, -]); - -export function migrateFromV1ToV2({ data }: { data: LimitsV1 }) { - return { - version: "v2", - ...data, - "private-locations": true, - "monitor-values-visibility": true, - }; -} - -export function migrateFromV2ToV3({ data }: { data: LimitsV2 }) { - return { - ...data, - version: "v3", - screenshots: true, - }; -} - -export function migrateFromV1ToV3({ data }: { data: LimitsV1 }) { - return { - ...data, - version: "v3", - screenshots: true, - "private-locations": true, - "monitor-values-visibility": true, - }; -} - -export const limitSchema = unknownLimit.transform((val) => { - if (!val.version) { - return migrateFromV1ToV3({ data: val }); - } - if (val.version === "v2") { - return migrateFromV2ToV3({ data: val }); - } - return val; -}); - -export type Limits = z.infer; +export type Limits = z.infer; diff --git a/packages/db/src/schema/workspaces/validation.ts b/packages/db/src/schema/workspaces/validation.ts index 97a91332..8c4c7485 100644 --- a/packages/db/src/schema/workspaces/validation.ts +++ b/packages/db/src/schema/workspaces/validation.ts @@ -2,7 +2,7 @@ import { createSelectSchema } from "drizzle-zod"; import { z } from "zod"; import { allPlans } from "../plan/config"; -import { limitsV1 } from "../plan/schema"; +import { limitsSchema } from "../plan/schema"; import { workspacePlans, workspaceRole } from "./constants"; import { workspace } from "./workspace"; @@ -11,13 +11,12 @@ export const workspaceRoleSchema = z.enum(workspaceRole); /** * Workspace schema with limits and plan - * If not available in the db, the limits will be taken from the workspace plan */ -const selectWorkspaceSchemaDevelopment = createSelectSchema(workspace) +export const selectWorkspaceSchema = createSelectSchema(workspace) .extend({ limits: z.string().transform((val) => { const parsed = JSON.parse(val); - const result = limitsV1.partial().safeParse(parsed); + const result = limitsSchema.partial().safeParse(parsed); if (result.error) return {}; return result.data; }), @@ -30,39 +29,17 @@ const selectWorkspaceSchemaDevelopment = createSelectSchema(workspace) .transform((val) => { return { ...val, - limits: limitsV1.parse({ ...allPlans[val.plan].limits, ...val.limits }), + limits: limitsSchema.parse({ + ...allPlans[val.plan].limits, + /** + * override the default plan limits + * allows us to set custom limits for a workspace + */ + ...val.limits, + }), }; }); -/** - * Workspace schema with limits and plan - * The limits for paid users have to be defined within the db otherwise, fallbacks to free plan limits - */ -const selectWorkspaceSchemaProduction = createSelectSchema(workspace).extend({ - limits: z.string().transform((val) => { - const parsed = JSON.parse(val); - const result = limitsV1.safeParse(parsed); - if (result.error) { - // Fallback to default limits - return limitsV1.parse({ - ...allPlans.free.limits, - }); - } - - return result.data; - }), - plan: z - .enum(workspacePlans) - .nullable() - .default("free") - .transform((val) => val ?? "free"), -}); - -export const selectWorkspaceSchema = - process.env.NODE_ENV === "development" - ? selectWorkspaceSchemaDevelopment - : selectWorkspaceSchemaProduction; - export const insertWorkspaceSchema = createSelectSchema(workspace); export type Workspace = z.infer;