mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
feat: added cron job to remove healthcheck logs older than 12h
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
import {useMemo} from "react"
|
import {useMemo} from "react"
|
||||||
import {Card} from "@/components/ui/card"
|
import {Card} from "@/components/ui/card"
|
||||||
import {HealthcheckLog} from "@/db/schema/15_healthcheck-log"
|
import {HealthcheckLog} from "@/db/schema/15_healthcheck-log"
|
||||||
|
import {useIsMobile} from "@/hooks/use-mobile";
|
||||||
|
|
||||||
type HealthStatus = "healthy" | "degraded" | "down" | "unknown"
|
type HealthStatus = "healthy" | "degraded" | "down" | "unknown"
|
||||||
|
|
||||||
@@ -105,11 +106,12 @@ export const HealthCheckGraph = ({logs}: Props) => {
|
|||||||
return buildTimeSeries(logs)
|
return buildTimeSeries(logs)
|
||||||
}, [logs])
|
}, [logs])
|
||||||
|
|
||||||
|
const isMobile = useIsMobile()
|
||||||
|
|
||||||
|
|
||||||
const hourLabels = useMemo(() => {
|
const hourLabels = useMemo(() => {
|
||||||
if (data.length === 0) return []
|
if (data.length === 0) return []
|
||||||
|
|
||||||
const isMobile = window.innerWidth <= 768
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
.map((item, index) => ({ item, index }))
|
.map((item, index) => ({ item, index }))
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {db} from "@/db";
|
||||||
|
import * as drizzleDb from "@/db";
|
||||||
|
import {and, eq, gte, lt} from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function getHealthLast12hLogs({id}: { id: string }) {
|
||||||
|
const now = new Date()
|
||||||
|
const since = new Date(now.getTime() - 12 * 60 * 60 * 1000)
|
||||||
|
|
||||||
|
return db
|
||||||
|
.select()
|
||||||
|
.from(drizzleDb.schemas.healthcheckLog)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(drizzleDb.schemas.healthcheckLog.objectId, id),
|
||||||
|
gte(drizzleDb.schemas.healthcheckLog.date, since)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function deleteHealthLogsOlderThan12h() {
|
||||||
|
const now = new Date()
|
||||||
|
const threshold = new Date(now.getTime() - 12 * 60 * 60 * 1000)
|
||||||
|
|
||||||
|
const logsToDelete = await db
|
||||||
|
.select()
|
||||||
|
.from(drizzleDb.schemas.healthcheckLog)
|
||||||
|
.where(
|
||||||
|
lt(drizzleDb.schemas.healthcheckLog.date, threshold)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log(`Number of logs found to delete: ${logsToDelete.length}`)
|
||||||
|
|
||||||
|
await db
|
||||||
|
.delete(drizzleDb.schemas.healthcheckLog)
|
||||||
|
.where(
|
||||||
|
lt(drizzleDb.schemas.healthcheckLog.date, threshold)
|
||||||
|
)
|
||||||
|
|
||||||
|
return logsToDelete.length
|
||||||
|
}
|
||||||
@@ -39,6 +39,14 @@ export const env = createEnv({
|
|||||||
process.env.NODE_ENV === "production" ? "0 7 * * *" : "* * * * *",
|
process.env.NODE_ENV === "production" ? "0 7 * * *" : "* * * * *",
|
||||||
),
|
),
|
||||||
|
|
||||||
|
CLEANING_HEALTHCHECK_LOGS_CRON: z
|
||||||
|
.string()
|
||||||
|
.default(
|
||||||
|
process.env.NODE_ENV === "production" ? "0 * * * *" : "* * * * *",
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AUTH_OIDC_ID: z.string().optional().default("oidc"),
|
AUTH_OIDC_ID: z.string().optional().default("oidc"),
|
||||||
AUTH_OIDC_TITLE: z.string().optional(),
|
AUTH_OIDC_TITLE: z.string().optional(),
|
||||||
AUTH_OIDC_DESC: z.string().optional(),
|
AUTH_OIDC_DESC: z.string().optional(),
|
||||||
@@ -95,6 +103,7 @@ export const env = createEnv({
|
|||||||
SMTP_SECURE: process.env.SMTP_SECURE,
|
SMTP_SECURE: process.env.SMTP_SECURE,
|
||||||
|
|
||||||
RETENTION_CRON: process.env.RETENTION_CRON,
|
RETENTION_CRON: process.env.RETENTION_CRON,
|
||||||
|
CLEANING_HEALTHCHECK_LOGS_CRON: process.env.CLEANING_HEALTHCHECK_LOGS_CRON,
|
||||||
|
|
||||||
AUTH_OIDC_ID: process.env.AUTH_OIDC_ID,
|
AUTH_OIDC_ID: process.env.AUTH_OIDC_ID,
|
||||||
AUTH_OIDC_TITLE: process.env.AUTH_OIDC_TITLE,
|
AUTH_OIDC_TITLE: process.env.AUTH_OIDC_TITLE,
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
import {ActionError, userAction} from "@/lib/safe-actions/actions";
|
import {ActionError, userAction} from "@/lib/safe-actions/actions";
|
||||||
import {AgentSchema} from "@/features/agents/agents.schema";
|
import {AgentSchema} from "@/features/agents/agents.schema";
|
||||||
import {z} from "zod";
|
import {z} from "zod";
|
||||||
import {eq, and, ne, count, gte} from "drizzle-orm";
|
import {eq, and, ne, count, gte, lt} from "drizzle-orm";
|
||||||
import {db} from "@/db";
|
import {db} from "@/db";
|
||||||
import * as drizzleDb from "@/db";
|
import * as drizzleDb from "@/db";
|
||||||
import {slugify} from "@/utils/slugify";
|
import {slugify} from "@/utils/slugify";
|
||||||
|
import {getHealthLast12hLogs} from "@/db/services/healthcheck";
|
||||||
|
|
||||||
const verifySlugUniqueness = async (slug: string, agentId?: string) => {
|
const verifySlugUniqueness = async (slug: string, agentId?: string) => {
|
||||||
const conditions = agentId ? and(eq(drizzleDb.schemas.agent.slug, slug), ne(drizzleDb.schemas.agent.id, agentId)) : eq(drizzleDb.schemas.agent.slug, slug);
|
const conditions = agentId ? and(eq(drizzleDb.schemas.agent.slug, slug), ne(drizzleDb.schemas.agent.id, agentId)) : eq(drizzleDb.schemas.agent.slug, slug);
|
||||||
@@ -57,22 +58,7 @@ export const getAgentAction = userAction.schema(z.string()).action(async ({parse
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
data: agent,
|
data: agent,
|
||||||
health: agent ? await getLast12hLogs({ id: agent.id }) : []
|
health: agent ? await getHealthLast12hLogs({ id: agent.id }) : []
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export async function getLast12hLogs({id}: { id: string }) {
|
|
||||||
const now = new Date()
|
|
||||||
const since = new Date(now.getTime() - 12 * 60 * 60 * 1000)
|
|
||||||
|
|
||||||
return db
|
|
||||||
.select()
|
|
||||||
.from(drizzleDb.schemas.healthcheckLog)
|
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(drizzleDb.schemas.healthcheckLog.objectId, id),
|
|
||||||
gte(drizzleDb.schemas.healthcheckLog.date, since)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
+10
-1
@@ -2,7 +2,7 @@ import cron from "node-cron";
|
|||||||
import {retentionCleanTask} from "@/lib/tasks/database";
|
import {retentionCleanTask} from "@/lib/tasks/database";
|
||||||
import {env} from "@/env.mjs";
|
import {env} from "@/env.mjs";
|
||||||
import {backupCleanTask} from "@/lib/tasks/cleaning";
|
import {backupCleanTask} from "@/lib/tasks/cleaning";
|
||||||
|
import {deleteHealthLogsOlderThan12h} from "@/db/services/healthcheck";
|
||||||
|
|
||||||
export const retentionJob = cron.schedule(env.RETENTION_CRON, async () => {
|
export const retentionJob = cron.schedule(env.RETENTION_CRON, async () => {
|
||||||
try {
|
try {
|
||||||
@@ -21,3 +21,12 @@ export const cleaningJob = cron.schedule("* * * * *", async () => {
|
|||||||
console.error(`[CRON] Error:`, err);
|
console.error(`[CRON] Error:`, err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const cleaningHealthcheckLogsJob = cron.schedule(env.CLEANING_HEALTHCHECK_LOGS_CRON, async () => {
|
||||||
|
try {
|
||||||
|
console.log("Cleaning Healthcheck Logs Job : Starting task");
|
||||||
|
await deleteHealthLogsOlderThan12h();
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[CRON] Error:`, err);
|
||||||
|
}
|
||||||
|
});
|
||||||
+8
-1
@@ -2,7 +2,7 @@ import { env } from "@/env.mjs";
|
|||||||
import { db, makeMigration } from "@/db";
|
import { db, makeMigration } from "@/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import * as drizzleDb from "@/db";
|
import * as drizzleDb from "@/db";
|
||||||
import { cleaningJob, retentionJob } from "@/lib/tasks";
|
import {cleaningHealthcheckLogsJob, cleaningJob, retentionJob} from "@/lib/tasks";
|
||||||
import { generateRSAKeys, getOrCreateMasterKey } from "@/utils/rsa-keys";
|
import { generateRSAKeys, getOrCreateMasterKey } from "@/utils/rsa-keys";
|
||||||
import { StorageProviderKind } from "@/features/storages/types";
|
import { StorageProviderKind } from "@/features/storages/types";
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ export async function init() {
|
|||||||
console.log("====Initialization completed====");
|
console.log("====Initialization completed====");
|
||||||
await setupCronJobs();
|
await setupCronJobs();
|
||||||
await setupCleaningJobs();
|
await setupCleaningJobs();
|
||||||
|
await setupCleaningHealthLogsJobs();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(env.AUTH_GOOGLE_ID && env.AUTH_GOOGLE_SECRET) ||
|
(env.AUTH_GOOGLE_ID && env.AUTH_GOOGLE_SECRET) ||
|
||||||
@@ -40,6 +41,12 @@ async function setupCleaningJobs() {
|
|||||||
console.log("==== Cleaning job started ====");
|
console.log("==== Cleaning job started ====");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setupCleaningHealthLogsJobs() {
|
||||||
|
console.log("==== Setting up Cleaning Healthcheck Logs Jobs ====");
|
||||||
|
cleaningHealthcheckLogsJob.start();
|
||||||
|
console.log("==== Cleaning Healthcheck Logs job started ====");
|
||||||
|
}
|
||||||
|
|
||||||
async function createSettingsIfNotExist() {
|
async function createSettingsIfNotExist() {
|
||||||
await db.transaction(async (tx) => {
|
await db.transaction(async (tx) => {
|
||||||
const systemSettingsValues = {
|
const systemSettingsValues = {
|
||||||
|
|||||||
Reference in New Issue
Block a user