checks retention routes
This commit is contained in:
1 parent
ce97e201c9
commit
08887bbc58
6 files changed
+104
-3
No files matched your search
@@ -9,6 +9,11 @@ export interface ISettingsController {
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => void;
|
||||
updateRetentionPolicy: (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => void;
|
||||
testTransport: (req: Request, res: Response, next: NextFunction) => void;
|
||||
}
|
||||
|
||||
@@ -53,6 +58,23 @@ class SettingsController implements ISettingsController {
|
||||
}
|
||||
};
|
||||
|
||||
updateRetentionPolicy = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) => {
|
||||
try {
|
||||
const retentionDays = req.body.checksRetentionDays;
|
||||
const updatedRetentionPolicy =
|
||||
await this.settingsService.updateRetentionPolicy(retentionDays);
|
||||
res.status(200).json({
|
||||
message: `Retention policy set to ${updatedRetentionPolicy} days`,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
testTransport = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const testResult = await this.notificationService.testEmailTransport(
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface ISystemSettings extends Document {
|
||||
systemEmailIgnoreTLS: boolean;
|
||||
systemEmailRequireTLS: boolean;
|
||||
systemEmailRejectUnauthorized: boolean;
|
||||
dataRetentionDays: number;
|
||||
}
|
||||
|
||||
const SystemSettingsSchema = new Schema<ISystemSettings>(
|
||||
@@ -30,6 +31,7 @@ const SystemSettingsSchema = new Schema<ISystemSettings>(
|
||||
systemEmailIgnoreTLS: { type: Boolean, default: false },
|
||||
systemEmailRequireTLS: { type: Boolean, default: false },
|
||||
systemEmailRejectUnauthorized: { type: Boolean, default: true },
|
||||
dataRetentionDays: { type: Number, default: 90 },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
|
||||
@@ -5,8 +5,12 @@ import { verifyTeamPermission } from "@/middleware/VerifyPermission.js";
|
||||
import { addUserContext } from "@/middleware/AddUserContext.js";
|
||||
import { PERMISSIONS } from "@/services/business/AuthService.js";
|
||||
import { validateBody } from "@/middleware/validation.js";
|
||||
import { systemSettingsSchema } from "@/validation/index.js";
|
||||
import {
|
||||
systemSettingsSchema,
|
||||
updateRetentionPolicySchema,
|
||||
} from "@/validation/index.js";
|
||||
import { verify } from "node:crypto";
|
||||
import { FeatureFlags } from "@/utils/FeatureFlags.js";
|
||||
|
||||
class SettingsRoutes {
|
||||
private router;
|
||||
@@ -34,6 +38,14 @@ class SettingsRoutes {
|
||||
validateBody(systemSettingsSchema),
|
||||
this.controller.updateEmailSettings
|
||||
);
|
||||
this.router.patch(
|
||||
"/retention",
|
||||
verifyToken,
|
||||
addUserContext,
|
||||
verifyTeamPermission(FeatureFlags.getSetRetentionPermission()),
|
||||
validateBody(updateRetentionPolicySchema),
|
||||
this.controller.updateRetentionPolicy
|
||||
);
|
||||
this.router.post(
|
||||
"/test-transport",
|
||||
verifyToken,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ISystemSettings, SystemSettings } from "@/db/models/index.js";
|
||||
import mongoose from "mongoose";
|
||||
import ApiError from "@/utils/ApiError.js";
|
||||
import { EmailService } from "@/services/index.js";
|
||||
const SERVICE_NAME = "SettingsService";
|
||||
@@ -7,6 +8,7 @@ export interface ISettingsService {
|
||||
updateEmailSettings: (
|
||||
settings: Partial<ISystemSettings>
|
||||
) => Promise<ISystemSettings>;
|
||||
updateRetentionPolicy: (retentionDays: number) => Promise<number>;
|
||||
}
|
||||
|
||||
class SettingsService implements ISettingsService {
|
||||
@@ -93,6 +95,59 @@ class SettingsService implements ISettingsService {
|
||||
|
||||
return settings;
|
||||
};
|
||||
|
||||
updateRetentionPolicy = async (retentionDays: number) => {
|
||||
const settings = await SystemSettings.findOneAndUpdate(
|
||||
{ _id: "global" },
|
||||
{ $set: { checksRetentionDays: retentionDays } },
|
||||
{ new: true, upsert: true, setDefaultsOnInsert: true }
|
||||
);
|
||||
|
||||
if (!settings) {
|
||||
throw new ApiError("Unable to update system settings", 500);
|
||||
}
|
||||
|
||||
const db = mongoose.connection.db;
|
||||
if (!db) {
|
||||
throw new ApiError("Database not initialized", 500);
|
||||
}
|
||||
|
||||
const seconds = Math.floor(retentionDays * 24 * 60 * 60);
|
||||
|
||||
const cmdResult = await db.command({
|
||||
collMod: "checks",
|
||||
expireAfterSeconds: seconds,
|
||||
});
|
||||
|
||||
if (!cmdResult || (cmdResult as any).ok !== 1) {
|
||||
throw new ApiError("Failed to apply TTL to checks collection", 500);
|
||||
}
|
||||
|
||||
const list = (await db.command({
|
||||
listCollections: 1,
|
||||
filter: { name: "checks" },
|
||||
})) as unknown as {
|
||||
cursor?: {
|
||||
firstBatch?: Array<{
|
||||
options?: { expireAfterSeconds?: number | string };
|
||||
}>;
|
||||
};
|
||||
ok?: 1 | 0;
|
||||
};
|
||||
const expireOption =
|
||||
list?.cursor?.firstBatch?.[0]?.options?.expireAfterSeconds;
|
||||
const appliedSeconds =
|
||||
typeof expireOption === "number" ? expireOption : Number(expireOption);
|
||||
|
||||
if (!appliedSeconds || appliedSeconds !== seconds) {
|
||||
throw new ApiError(
|
||||
"TTL verification failed: expireAfterSeconds not applied",
|
||||
500
|
||||
);
|
||||
}
|
||||
|
||||
return retentionDays;
|
||||
};
|
||||
}
|
||||
|
||||
export default SettingsService;
|
||||
@@ -11,8 +11,11 @@ const FeatureFlags = {
|
||||
mode: CURRENT_MODE,
|
||||
isSaaS,
|
||||
isSelfHosted,
|
||||
getDiagnosticPermission(): string {
|
||||
return isSaaS() ? PERMISSIONS.master : PERMISSIONS.diagnostic.read;
|
||||
getDiagnosticPermission(): string[] {
|
||||
return isSaaS() ? [PERMISSIONS.master] : [PERMISSIONS.diagnostic.read];
|
||||
},
|
||||
getSetRetentionPermission(): string[] {
|
||||
return isSaaS() ? [PERMISSIONS.master] : [PERMISSIONS.checks.write];
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -646,3 +646,10 @@ export const systemSettingsSchema = z
|
||||
.transform((val) => (typeof val === "string" ? val === "true" : val)),
|
||||
})
|
||||
.strip();
|
||||
|
||||
export const updateRetentionPolicySchema = z.object({
|
||||
checksRetentionDays: z
|
||||
.number()
|
||||
.min(1, "Retention days must be at least 1")
|
||||
.max(365, "Retention days cannot exceed 365 days"),
|
||||
});
|
||||
Reference in new issue
Block a user