Openstatus
www.openstatus.dev
1import { FREE_FLY_REGIONS } from "@openstatus/regions";
2import { z } from "zod";
3import { monitorPeriodicitySchema, monitorRegionSchema } from "../constants";
4
5// REMINDER: this is not a database table but just a schema for the limits of the plan
6// default values are set to the free plan limits
7
8export const limitsSchema = z.object({
9 version: z.undefined(),
10 /**
11 * Monitor limits
12 */
13 monitors: z.number().prefault(1),
14 "synthetic-checks": z.number().prefault(30), // monthly limits
15 periodicity: monitorPeriodicitySchema.array().prefault(["10m", "30m", "1h"]),
16 "multi-region": z.boolean().prefault(true),
17 "max-regions": z.number().prefault(6),
18 "data-retention": z
19 .enum(["14 days", "3 months", "12 months", "24 months"])
20 .prefault("14 days"),
21 regions: monitorRegionSchema.array().prefault(FREE_FLY_REGIONS),
22 "private-locations": z.boolean().prefault(false),
23 screenshots: z.boolean().prefault(false),
24 "response-logs": z.boolean().prefault(false),
25 otel: z.boolean().prefault(false),
26 /**
27 * Status page limits
28 */
29 "status-pages": z.number().prefault(1),
30 "page-components": z.number().prefault(3),
31 maintenance: z.boolean().prefault(true),
32 "monitor-values-visibility": z.boolean().prefault(true),
33 "status-subscribers": z.boolean().prefault(false),
34 "custom-domain": z.boolean().prefault(false),
35 "password-protection": z.boolean().prefault(false),
36 "email-domain-protection": z.boolean().prefault(false), // add-on but required in limits
37 "white-label": z.boolean().prefault(false),
38 /**
39 * Notification limits
40 */
41
42 notifications: z.boolean().prefault(true),
43 pagerduty: z.boolean().prefault(false),
44 opsgenie: z.boolean().prefault(false),
45 whatsapp: z.boolean().prefault(false),
46 sms: z.boolean().prefault(false),
47 "sms-limit": z.number().prefault(0),
48 "notification-channels": z.number().prefault(1),
49
50 /**
51 * Collaboration limits
52 */
53 members: z.literal("Unlimited").or(z.number()).prefault(1),
54 "audit-log": z.boolean().prefault(false),
55});
56
57export type Limits = z.infer<typeof limitsSchema>;
58
59//
60
61const priceSchema = z.object({
62 USD: z.number(),
63 EUR: z.number(),
64 INR: z.number(),
65});
66
67export type Price = z.infer<typeof priceSchema>;
68
69export const addons = [
70 "email-domain-protection",
71 "white-label",
72 "status-pages",
73] as const satisfies Partial<keyof Limits>[];
74
75export const addonsSchema = z.partialRecord(
76 z.enum(addons),
77 z.object({
78 price: priceSchema,
79 }),
80) satisfies z.ZodType<Partial<Record<keyof Limits, { price: Price }>>>;
81
82export type Addons = z.infer<typeof addonsSchema>;
83
84/**
85 * Enforces that addon keys in Limits must be set to false in plan configs
86 * (since addons can only be enabled by purchasing them)
87 */
88export type PlanLimits = {
89 [K in keyof Limits]: K extends keyof Addons
90 ? Limits[K] extends boolean
91 ? false // Force addon boolean fields to false
92 : Limits[K] // Non-boolean fields stay as-is
93 : Limits[K]; // Non-addon fields stay as-is
94};