mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge remote-tracking branch 'dokploy/canary' into check-for-updates
This commit is contained in:
commit
8f05f06259
@ -6,13 +6,144 @@ import {
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { api } from "@/utils/api";
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
import { AlertCircle, Link, ShieldCheck } from "lucide-react";
|
||||
import { AddCertificate } from "./add-certificate";
|
||||
import { DeleteCertificate } from "./delete-certificate";
|
||||
|
||||
export const ShowCertificates = () => {
|
||||
const { data } = api.certificates.all.useQuery();
|
||||
|
||||
const extractExpirationDate = (certData: string): Date | null => {
|
||||
try {
|
||||
const match = certData.match(
|
||||
/-----BEGIN CERTIFICATE-----\s*([^-]+)\s*-----END CERTIFICATE-----/,
|
||||
);
|
||||
if (!match?.[1]) return null;
|
||||
|
||||
const base64Cert = match[1].replace(/\s/g, "");
|
||||
const binaryStr = window.atob(base64Cert);
|
||||
const bytes = new Uint8Array(binaryStr.length);
|
||||
|
||||
for (let i = 0; i < binaryStr.length; i++) {
|
||||
bytes[i] = binaryStr.charCodeAt(i);
|
||||
}
|
||||
|
||||
let dateFound = 0;
|
||||
for (let i = 0; i < bytes.length - 2; i++) {
|
||||
if (bytes[i] === 0x17 || bytes[i] === 0x18) {
|
||||
const dateType = bytes[i];
|
||||
const dateLength = bytes[i + 1];
|
||||
if (typeof dateLength === "undefined") continue;
|
||||
|
||||
if (dateFound === 0) {
|
||||
dateFound++;
|
||||
i += dateLength + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let dateStr = "";
|
||||
for (let j = 0; j < dateLength; j++) {
|
||||
const charCode = bytes[i + 2 + j];
|
||||
if (typeof charCode === "undefined") continue;
|
||||
dateStr += String.fromCharCode(charCode);
|
||||
}
|
||||
|
||||
if (dateType === 0x17) {
|
||||
// UTCTime (YYMMDDhhmmssZ)
|
||||
const year = Number.parseInt(dateStr.slice(0, 2));
|
||||
const fullYear = year >= 50 ? 1900 + year : 2000 + year;
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
fullYear,
|
||||
Number.parseInt(dateStr.slice(2, 4)) - 1,
|
||||
Number.parseInt(dateStr.slice(4, 6)),
|
||||
Number.parseInt(dateStr.slice(6, 8)),
|
||||
Number.parseInt(dateStr.slice(8, 10)),
|
||||
Number.parseInt(dateStr.slice(10, 12)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// GeneralizedTime (YYYYMMDDhhmmssZ)
|
||||
return new Date(
|
||||
Date.UTC(
|
||||
Number.parseInt(dateStr.slice(0, 4)),
|
||||
Number.parseInt(dateStr.slice(4, 6)) - 1,
|
||||
Number.parseInt(dateStr.slice(6, 8)),
|
||||
Number.parseInt(dateStr.slice(8, 10)),
|
||||
Number.parseInt(dateStr.slice(10, 12)),
|
||||
Number.parseInt(dateStr.slice(12, 14)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error parsing certificate:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const getExpirationStatus = (certData: string) => {
|
||||
const expirationDate = extractExpirationDate(certData);
|
||||
|
||||
if (!expirationDate)
|
||||
return {
|
||||
status: "unknown" as const,
|
||||
className: "text-muted-foreground",
|
||||
message: "Could not determine expiration",
|
||||
};
|
||||
|
||||
const now = new Date();
|
||||
const daysUntilExpiration = Math.ceil(
|
||||
(expirationDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24),
|
||||
);
|
||||
|
||||
if (daysUntilExpiration < 0) {
|
||||
return {
|
||||
status: "expired" as const,
|
||||
className: "text-red-500",
|
||||
message: `Expired on ${expirationDate.toLocaleDateString([], {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (daysUntilExpiration <= 30) {
|
||||
return {
|
||||
status: "warning" as const,
|
||||
className: "text-yellow-500",
|
||||
message: `Expires in ${daysUntilExpiration} days`,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: "valid" as const,
|
||||
className: "text-muted-foreground",
|
||||
message: `Expires ${expirationDate.toLocaleDateString([], {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}`,
|
||||
};
|
||||
};
|
||||
|
||||
const getCertificateChainInfo = (certData: string) => {
|
||||
const certCount = (certData.match(/-----BEGIN CERTIFICATE-----/g) || [])
|
||||
.length;
|
||||
return certCount > 1
|
||||
? {
|
||||
isChain: true,
|
||||
count: certCount,
|
||||
}
|
||||
: {
|
||||
isChain: false,
|
||||
count: 1,
|
||||
};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full">
|
||||
<Card className="bg-transparent h-full">
|
||||
@ -23,7 +154,7 @@ export const ShowCertificates = () => {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 pt-4 h-full">
|
||||
{data?.length === 0 ? (
|
||||
{!data?.length ? (
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<ShieldCheck className="size-8 self-center text-muted-foreground" />
|
||||
<span className="text-base text-muted-foreground">
|
||||
@ -35,21 +166,53 @@ export const ShowCertificates = () => {
|
||||
) : (
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
{data?.map((destination, index) => (
|
||||
<div
|
||||
key={destination.certificateId}
|
||||
className="flex items-center justify-between border p-4 rounded-lg"
|
||||
>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{index + 1}. {destination.name}
|
||||
</span>
|
||||
<div className="flex flex-row gap-3">
|
||||
<DeleteCertificate
|
||||
certificateId={destination.certificateId}
|
||||
/>
|
||||
{data.map((certificate, index) => {
|
||||
const expiration = getExpirationStatus(
|
||||
certificate.certificateData,
|
||||
);
|
||||
const chainInfo = getCertificateChainInfo(
|
||||
certificate.certificateData,
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={certificate.certificateId}
|
||||
className="flex flex-col border p-4 rounded-lg space-y-2"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">
|
||||
{index + 1}. {certificate.name}
|
||||
</span>
|
||||
{chainInfo.isChain && (
|
||||
<div className="flex items-center gap-1 px-1.5 py-0.5 rounded bg-muted/50">
|
||||
<Link className="size-3 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Chain ({chainInfo.count})
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<DeleteCertificate
|
||||
certificateId={certificate.certificateId}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={`text-xs flex items-center gap-1.5 ${expiration.className}`}
|
||||
>
|
||||
{expiration.status !== "valid" && (
|
||||
<AlertCircle className="size-3" />
|
||||
)}
|
||||
{expiration.message}
|
||||
{certificate.autoRenew &&
|
||||
expiration.status !== "valid" && (
|
||||
<span className="text-xs text-emerald-500 ml-1">
|
||||
(Auto-renewal enabled)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>
|
||||
<AddCertificate />
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { api } from "@/utils/api";
|
||||
import { TrashIcon } from "lucide-react";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import React from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
@ -24,8 +24,13 @@ export const DeleteNotification = ({ notificationId }: Props) => {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="ghost" isLoading={isLoading}>
|
||||
<TrashIcon className="size-4 text-muted-foreground" />
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-9 w-9 group hover:bg-red-500/10"
|
||||
isLoading={isLoading}
|
||||
>
|
||||
<Trash2 className="size-4 text-muted-foreground group-hover:text-red-500" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
|
@ -40,48 +40,58 @@ export const ShowNotifications = () => {
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid lg:grid-cols-2 xl:grid-cols-3 gap-4">
|
||||
{data?.map((notification, index) => (
|
||||
<div
|
||||
key={notification.notificationId}
|
||||
className="flex items-center justify-between border gap-2 p-3.5 rounded-lg"
|
||||
>
|
||||
<div className="flex flex-row gap-2 items-center w-full ">
|
||||
{notification.notificationType === "slack" && (
|
||||
<SlackIcon className="text-muted-foreground size-6 flex-shrink-0" />
|
||||
)}
|
||||
{notification.notificationType === "telegram" && (
|
||||
<TelegramIcon className="text-muted-foreground size-8 flex-shrink-0" />
|
||||
)}
|
||||
{notification.notificationType === "discord" && (
|
||||
<DiscordIcon className="text-muted-foreground size-7 flex-shrink-0" />
|
||||
)}
|
||||
{notification.notificationType === "email" && (
|
||||
<Mail
|
||||
size={29}
|
||||
className="text-muted-foreground size-6 flex-shrink-0"
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{notification.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row gap-1 w-fit">
|
||||
<UpdateNotification
|
||||
notificationId={notification.notificationId}
|
||||
/>
|
||||
<DeleteNotification
|
||||
notificationId={notification.notificationId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 justify-end w-full items-end">
|
||||
<AddNotification />
|
||||
<div className="grid lg:grid-cols-1 xl:grid-cols-2 gap-4">
|
||||
{data?.map((notification, index) => (
|
||||
<div
|
||||
key={notification.notificationId}
|
||||
className="flex items-center justify-between rounded-xl p-4 transition-colors dark:bg-zinc-900/50 hover:bg-zinc-900 border border-zinc-800/50"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
{notification.notificationType === "slack" && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-indigo-500/10">
|
||||
<SlackIcon className="h-6 w-6 text-indigo-400" />
|
||||
</div>
|
||||
)}
|
||||
{notification.notificationType === "telegram" && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-cyan-500/10">
|
||||
<TelegramIcon className="h-6 w-6 text-indigo-400" />
|
||||
</div>
|
||||
)}
|
||||
{notification.notificationType === "discord" && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-indigo-500/10">
|
||||
<DiscordIcon className="h-6 w-6 text-indigo-400" />
|
||||
</div>
|
||||
)}
|
||||
{notification.notificationType === "email" && (
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-zinc-500/10">
|
||||
<Mail className="h-6 w-6 text-indigo-400" />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-zinc-300">
|
||||
{notification.name}
|
||||
</span>
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{notification.notificationType?.[0]?.toUpperCase() + notification.notificationType?.slice(1)} notification
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<UpdateNotification
|
||||
notificationId={notification.notificationId}
|
||||
/>
|
||||
<DeleteNotification
|
||||
notificationId={notification.notificationId}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-4 justify-end w-full items-end">
|
||||
<AddNotification />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
@ -26,7 +26,7 @@ import { Input } from "@/components/ui/input";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Mail, PenBoxIcon } from "lucide-react";
|
||||
import { Mail, Pen } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FieldErrors, useFieldArray, useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
@ -218,8 +218,10 @@ export const UpdateNotification = ({ notificationId }: Props) => {
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger className="" asChild>
|
||||
<Button variant="ghost">
|
||||
<PenBoxIcon className="size-4 text-muted-foreground" />
|
||||
<Button variant="ghost"
|
||||
size="icon"
|
||||
className="h-9 w-9">
|
||||
<Pen className="size-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-2xl">
|
||||
|
@ -104,6 +104,7 @@ export const ProfileForm = () => {
|
||||
.then(async () => {
|
||||
await refetch();
|
||||
toast.success("Profile Updated");
|
||||
form.reset();
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error to Update the profile");
|
||||
|
@ -18,7 +18,7 @@ import { Logo } from "../shared/logo";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
|
||||
import { buttonVariants } from "../ui/button";
|
||||
|
||||
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 15;
|
||||
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 5;
|
||||
|
||||
export const Navbar = () => {
|
||||
const [isUpdateAvailable, setIsUpdateAvailable] = useState<boolean>(false);
|
||||
|
@ -188,9 +188,9 @@ export const authRouter = createTRPCRouter({
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const currentAuth = await findAuthByEmail(ctx.user.email);
|
||||
|
||||
if (input.password) {
|
||||
if (input.currentPassword || input.password) {
|
||||
const correctPassword = bcrypt.compareSync(
|
||||
input.password,
|
||||
input.currentPassword || "",
|
||||
currentAuth?.password || "",
|
||||
);
|
||||
if (!correctPassword) {
|
||||
@ -268,7 +268,9 @@ export const authRouter = createTRPCRouter({
|
||||
|
||||
return auth;
|
||||
}),
|
||||
|
||||
verifyToken: protectedProcedure.mutation(async () => {
|
||||
return true;
|
||||
}),
|
||||
one: adminProcedure.input(apiFindOneAuth).query(async ({ input }) => {
|
||||
const auth = await findAuthById(input.id);
|
||||
return auth;
|
||||
|
@ -269,11 +269,11 @@ export const settingsRouter = createTRPCRouter({
|
||||
message: "You are not authorized to access this admin",
|
||||
});
|
||||
}
|
||||
await updateAdmin(ctx.user.authId, {
|
||||
const adminUpdated = await updateAdmin(ctx.user.authId, {
|
||||
enableDockerCleanup: input.enableDockerCleanup,
|
||||
});
|
||||
|
||||
if (admin.enableDockerCleanup) {
|
||||
if (adminUpdated?.enableDockerCleanup) {
|
||||
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
||||
console.log(
|
||||
`Docker Cleanup ${new Date().toLocaleString()}] Running...`,
|
||||
|
@ -26,7 +26,7 @@ services:
|
||||
hard: 262144
|
||||
|
||||
plausible:
|
||||
image: ghcr.io/plausible/community-edition:v2.1.0
|
||||
image: ghcr.io/plausible/community-edition:v2.1.4
|
||||
restart: always
|
||||
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
|
||||
depends_on:
|
||||
|
@ -34,7 +34,7 @@ export const templates: TemplateData[] = [
|
||||
{
|
||||
id: "plausible",
|
||||
name: "Plausible",
|
||||
version: "v2.1.0",
|
||||
version: "v2.1.4",
|
||||
description:
|
||||
"Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.",
|
||||
logo: "plausible.svg",
|
||||
|
@ -11,6 +11,8 @@ import { runMariadbBackup } from "./mariadb";
|
||||
import { runMongoBackup } from "./mongo";
|
||||
import { runMySqlBackup } from "./mysql";
|
||||
import { runPostgresBackup } from "./postgres";
|
||||
import { sendDockerCleanupNotifications } from "../notifications/docker-cleanup";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
|
||||
export const initCronJobs = async () => {
|
||||
console.log("Setting up cron jobs....");
|
||||
@ -25,14 +27,15 @@ export const initCronJobs = async () => {
|
||||
await cleanUpUnusedImages();
|
||||
await cleanUpDockerBuilder();
|
||||
await cleanUpSystemPrune();
|
||||
await sendDockerCleanupNotifications(admin.adminId);
|
||||
});
|
||||
}
|
||||
|
||||
const servers = await getAllServers();
|
||||
|
||||
for (const server of servers) {
|
||||
const { appName, serverId } = server;
|
||||
if (serverId) {
|
||||
const { appName, serverId, enableDockerCleanup } = server;
|
||||
if (enableDockerCleanup) {
|
||||
scheduleJob(serverId, "0 0 * * *", async () => {
|
||||
console.log(
|
||||
`SERVER-BACKUP[${new Date().toLocaleString()}] Running Cleanup ${appName}`,
|
||||
@ -40,12 +43,17 @@ export const initCronJobs = async () => {
|
||||
await cleanUpUnusedImages(serverId);
|
||||
await cleanUpDockerBuilder(serverId);
|
||||
await cleanUpSystemPrune(serverId);
|
||||
await sendDockerCleanupNotifications(
|
||||
admin.adminId,
|
||||
`Docker cleanup for Server ${appName}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const pgs = await db.query.postgres.findMany({
|
||||
with: {
|
||||
project: true,
|
||||
backups: {
|
||||
with: {
|
||||
destination: true,
|
||||
@ -61,18 +69,39 @@ export const initCronJobs = async () => {
|
||||
for (const backup of pg.backups) {
|
||||
const { schedule, backupId, enabled } = backup;
|
||||
if (enabled) {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`PG-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
runPostgresBackup(pg, backup);
|
||||
});
|
||||
try {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`PG-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
runPostgresBackup(pg, backup);
|
||||
});
|
||||
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: pg.name,
|
||||
projectName: pg.project.name,
|
||||
databaseType: "postgres",
|
||||
type: "success",
|
||||
adminId: pg.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: pg.name,
|
||||
projectName: pg.project.name,
|
||||
databaseType: "postgres",
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: pg.project.adminId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mariadbs = await db.query.mariadb.findMany({
|
||||
with: {
|
||||
project: true,
|
||||
backups: {
|
||||
with: {
|
||||
destination: true,
|
||||
@ -89,18 +118,38 @@ export const initCronJobs = async () => {
|
||||
for (const backup of maria.backups) {
|
||||
const { schedule, backupId, enabled } = backup;
|
||||
if (enabled) {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MARIADB-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMariadbBackup(maria, backup);
|
||||
});
|
||||
try {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MARIADB-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMariadbBackup(maria, backup);
|
||||
});
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: maria.name,
|
||||
projectName: maria.project.name,
|
||||
databaseType: "mariadb",
|
||||
type: "success",
|
||||
adminId: maria.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: maria.name,
|
||||
projectName: maria.project.name,
|
||||
databaseType: "mariadb",
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: maria.project.adminId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mongodbs = await db.query.mongo.findMany({
|
||||
with: {
|
||||
project: true,
|
||||
backups: {
|
||||
with: {
|
||||
destination: true,
|
||||
@ -117,18 +166,38 @@ export const initCronJobs = async () => {
|
||||
for (const backup of mongo.backups) {
|
||||
const { schedule, backupId, enabled } = backup;
|
||||
if (enabled) {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MONGO-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMongoBackup(mongo, backup);
|
||||
});
|
||||
try {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MONGO-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMongoBackup(mongo, backup);
|
||||
});
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: mongo.name,
|
||||
projectName: mongo.project.name,
|
||||
databaseType: "mongodb",
|
||||
type: "success",
|
||||
adminId: mongo.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: mongo.name,
|
||||
projectName: mongo.project.name,
|
||||
databaseType: "mongodb",
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: mongo.project.adminId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mysqls = await db.query.mysql.findMany({
|
||||
with: {
|
||||
project: true,
|
||||
backups: {
|
||||
with: {
|
||||
destination: true,
|
||||
@ -145,12 +214,31 @@ export const initCronJobs = async () => {
|
||||
for (const backup of mysql.backups) {
|
||||
const { schedule, backupId, enabled } = backup;
|
||||
if (enabled) {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MYSQL-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMySqlBackup(mysql, backup);
|
||||
});
|
||||
try {
|
||||
scheduleJob(backupId, schedule, async () => {
|
||||
console.log(
|
||||
`MYSQL-SERVER[${new Date().toLocaleString()}] Running Backup ${backupId}`,
|
||||
);
|
||||
await runMySqlBackup(mysql, backup);
|
||||
});
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: mysql.name,
|
||||
projectName: mysql.project.name,
|
||||
databaseType: "mysql",
|
||||
type: "success",
|
||||
adminId: mysql.project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
applicationName: mysql.name,
|
||||
projectName: mysql.project.name,
|
||||
databaseType: "mysql",
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
adminId: mysql.project.adminId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user