Merge pull request #3424 from bluewave-labs/feat/status-page-infra-section-be

feat: status-page infrastructure section BE
This commit is contained in:
Alexander Holliday authored and GitHub committed 2026-03-18 14:38:57 -07:00
commit 07f828a01a
6 files changed
+61 -6

No files matched your search

@@ -0,0 +1,45 @@
import mongoose from "mongoose";
import { logger } from "@/utils/logger.js";
import StatusPageModel from "../models/StatusPage.js";
export async function migrateStatusPageTypeToArray(): Promise<void> {
const SERVICE_NAME = "Migration:MigrateStatusPageTypeToArray";
try {
logger.info({ service: SERVICE_NAME, message: "Starting migration of StatusPage type field from string to array" });
const db = mongoose.connection.db;
if (!db) {
throw new Error("Database connection is not initialized");
}
const result = await StatusPageModel.updateMany(
{
$expr: {
$eq: [{ $type: "$type" }, "string"], // only scalar string, not existing array
},
},
[
{
$set: {
type: ["$type"],
},
},
]
);
if (result.modifiedCount === 0) {
logger.info({ service: SERVICE_NAME, message: "No StatusPage documents needed migration" });
return;
}
logger.info({
service: SERVICE_NAME,
message: `Migration complete. Converted ${result.modifiedCount} StatusPage document(s) from string type to array type`,
});
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error({ service: SERVICE_NAME, message: `Error during StatusPage type migration: ${errorMessage}` });
throw error;
}
}
+2
View File
@@ -3,6 +3,7 @@ import { convertChecksToTimeSeries } from "./0002_convertChecksToTimeSeries.js";
import { cleanupDuplicateMonitorStats } from "./0003_cleanupDuplicateMonitorStats.js";
import { fixInfrastructureThresholds } from "./0004_fixInfrastructureThresholds.js";
import MigrationModel from "../models/Migration.js";
import { migrateStatusPageTypeToArray } from "./0005_migrateStatusPageTypeToArray.js";
type MigrationEntry = {
name: string;
@@ -14,6 +15,7 @@ const migrations: MigrationEntry[] = [
{ name: "0002_convertChecksToTimeSeries", execute: convertChecksToTimeSeries },
{ name: "0003_cleanupDuplicateMonitorStats", execute: cleanupDuplicateMonitorStats },
{ name: "0004_fixInfrastructureThresholds", execute: fixInfrastructureThresholds },
{ name: "0005_migrateStatusPageTypeToArray", execute: migrateStatusPageTypeToArray },
];
const runMigrations = async (logger?: { info: Function; error: Function }) => {
+6 -2
View File
@@ -43,9 +43,9 @@ const StatusPageSchema = new Schema<StatusPageDocument>(
required: true,
},
type: {
type: String,
type: [String],
required: true,
default: "uptime",
default: ["uptime"],
enum: StatusPageTypes,
},
companyName: {
@@ -100,6 +100,10 @@ const StatusPageSchema = new Schema<StatusPageDocument>(
type: Boolean,
default: false,
},
showInfrastructure: {
type: Boolean,
default: false,
},
customCSS: {
type: String,
default: "",
@@ -57,6 +57,7 @@ class MongoStatusPagesRepository implements IStatusPagesRepository {
showCharts: doc.showCharts,
showUptimePercentage: doc.showUptimePercentage,
showAdminLoginLink: doc.showAdminLoginLink,
showInfrastructure: doc.showInfrastructure,
customCSS: doc.customCSS,
createdAt: this.toDateString(doc.createdAt),
updatedAt: this.toDateString(doc.updatedAt),
+3 -2
View File
@@ -1,4 +1,4 @@
export const StatusPageTypes = ["uptime"] as const;
export const StatusPageTypes = ["uptime", "infrastructure"] as const;
export type StatusPageType = (typeof StatusPageTypes)[number];
export interface StatusPageLogo {
@@ -15,7 +15,7 @@ export interface StatusPage {
id: string;
userId: string;
teamId: string;
type: StatusPageType;
type: StatusPageType[];
companyName: string;
url: string;
timezone?: string;
@@ -28,6 +28,7 @@ export interface StatusPage {
showCharts: boolean;
showUptimePercentage: boolean;
showAdminLoginLink: boolean;
showInfrastructure: boolean;
customCSS: string;
createdAt: string;
updatedAt: string;
@@ -1,5 +1,6 @@
import { z } from "zod";
import { booleanCoercion } from "./shared.js";
import { StatusPageTypes } from "@/types/statusPage.js";
//****************************************
// Status Page Validations
@@ -10,13 +11,13 @@ export const getStatusPageParamValidation = z.object({
});
export const getStatusPageQueryValidation = z.object({
type: z.literal("uptime"),
type: z.union([z.enum(StatusPageTypes), z.array(z.enum(StatusPageTypes))]).transform((val) => (Array.isArray(val) ? val : [val])),
timeFrame: z.coerce.number().optional(),
});
export const createStatusPageBodyValidation = z
.object({
type: z.literal("uptime"),
type: z.union([z.enum(StatusPageTypes), z.array(z.enum(StatusPageTypes))]).transform((val) => (Array.isArray(val) ? val : [val])),
companyName: z.string().min(1, "Company name is required"),
url: z.string().regex(/^[a-zA-Z0-9_-]+$/, {
message: "URL can only contain letters, numbers, underscores, and hyphens",
@@ -30,6 +31,7 @@ export const createStatusPageBodyValidation = z
showCharts: booleanCoercion.optional(),
showUptimePercentage: booleanCoercion,
showAdminLoginLink: booleanCoercion.optional(),
showInfrastructure: booleanCoercion.optional(),
removeLogo: z.union([z.literal("true"), z.literal("false")]).optional(),
})
.strip();