mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
@@ -2,7 +2,7 @@ import { Label } from "@/components/ui/label";
|
|||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
export const ToggleAutoCheckUpdates = () => {
|
export const ToggleAutoCheckUpdates = ({ disabled }: { disabled: boolean }) => {
|
||||||
const [enabled, setEnabled] = useState<boolean>(
|
const [enabled, setEnabled] = useState<boolean>(
|
||||||
localStorage.getItem("enableAutoCheckUpdates") === "true",
|
localStorage.getItem("enableAutoCheckUpdates") === "true",
|
||||||
);
|
);
|
||||||
@@ -18,6 +18,7 @@ export const ToggleAutoCheckUpdates = () => {
|
|||||||
checked={enabled}
|
checked={enabled}
|
||||||
onCheckedChange={handleToggle}
|
onCheckedChange={handleToggle}
|
||||||
id="autoCheckUpdatesToggle"
|
id="autoCheckUpdatesToggle"
|
||||||
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
<Label className="text-primary" htmlFor="autoCheckUpdatesToggle">
|
<Label className="text-primary" htmlFor="autoCheckUpdatesToggle">
|
||||||
Automatically check for new updates
|
Automatically check for new updates
|
||||||
|
|||||||
@@ -3,38 +3,53 @@ import { Button } from "@/components/ui/button";
|
|||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogDescription,
|
|
||||||
DialogHeader,
|
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { RefreshCcw } from "lucide-react";
|
import {
|
||||||
|
Bug,
|
||||||
|
Download,
|
||||||
|
Info,
|
||||||
|
RefreshCcw,
|
||||||
|
Server,
|
||||||
|
ServerCrash,
|
||||||
|
Sparkles,
|
||||||
|
Stars,
|
||||||
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { UpdateWebServer } from "./update-webserver";
|
|
||||||
import { ToggleAutoCheckUpdates } from "./toggle-auto-check-updates";
|
import { ToggleAutoCheckUpdates } from "./toggle-auto-check-updates";
|
||||||
|
import { UpdateWebServer } from "./update-webserver";
|
||||||
|
|
||||||
export const UpdateServer = () => {
|
export const UpdateServer = () => {
|
||||||
const [isUpdateAvailable, setIsUpdateAvailable] = useState<null | boolean>(
|
const [hasCheckedUpdate, setHasCheckedUpdate] = useState(false);
|
||||||
null,
|
const [isUpdateAvailable, setIsUpdateAvailable] = useState(false);
|
||||||
);
|
|
||||||
const { mutateAsync: getUpdateData, isLoading } =
|
const { mutateAsync: getUpdateData, isLoading } =
|
||||||
api.settings.getUpdateData.useMutation();
|
api.settings.getUpdateData.useMutation();
|
||||||
|
const { data: dokployVersion } = api.settings.getDokployVersion.useQuery();
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [latestVersion, setLatestVersion] = useState("");
|
||||||
|
|
||||||
const handleCheckUpdates = async () => {
|
const handleCheckUpdates = async () => {
|
||||||
try {
|
try {
|
||||||
const { updateAvailable, latestVersion } = await getUpdateData();
|
const updateData = await getUpdateData();
|
||||||
setIsUpdateAvailable(updateAvailable);
|
const versionToUpdate = updateData.latestVersion || "";
|
||||||
if (updateAvailable) {
|
setHasCheckedUpdate(true);
|
||||||
toast.success(`${latestVersion} update is available!`);
|
setIsUpdateAvailable(updateData.updateAvailable);
|
||||||
|
setLatestVersion(versionToUpdate);
|
||||||
|
|
||||||
|
if (updateData.updateAvailable) {
|
||||||
|
toast.success(versionToUpdate, {
|
||||||
|
description: "New version available!",
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
toast.info("No updates available");
|
toast.info("No updates available");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error checking for updates:", error);
|
console.error("Error checking for updates:", error);
|
||||||
|
setHasCheckedUpdate(true);
|
||||||
setIsUpdateAvailable(false);
|
setIsUpdateAvailable(false);
|
||||||
toast.error(
|
toast.error(
|
||||||
"An error occurred while checking for updates, please try again.",
|
"An error occurred while checking for updates, please try again.",
|
||||||
@@ -45,59 +60,166 @@ export const UpdateServer = () => {
|
|||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button variant="secondary">
|
<Button variant="secondary" className="gap-2">
|
||||||
<RefreshCcw className="h-4 w-4" />
|
<Sparkles className="h-4 w-4" />
|
||||||
Updates
|
Updates
|
||||||
</Button>
|
</Button>
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
<DialogContent className="sm:m:max-w-lg ">
|
<DialogContent className="max-w-lg p-6">
|
||||||
<DialogHeader>
|
<div className="flex items-center justify-between mb-8">
|
||||||
<DialogTitle>Web Server Update</DialogTitle>
|
<DialogTitle className="text-2xl font-semibold">
|
||||||
<DialogDescription>
|
Web Server Update
|
||||||
Check new releases and update your dokploy
|
</DialogTitle>
|
||||||
</DialogDescription>
|
{dokployVersion && (
|
||||||
</DialogHeader>
|
<div className="flex items-center gap-1.5 rounded-full px-3 py-1 mr-2 bg-muted">
|
||||||
|
<Server className="h-4 w-4 text-muted-foreground" />
|
||||||
|
<span className="text-sm text-muted-foreground">
|
||||||
|
{dokployVersion}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
{/* Initial state */}
|
||||||
<span className="text-sm text-muted-foreground">
|
{!hasCheckedUpdate && (
|
||||||
We suggest to update your dokploy to the latest version only if you:
|
<div className="mb-8">
|
||||||
</span>
|
<p className="text text-muted-foreground">
|
||||||
<ul className="list-disc list-inside text-sm text-muted-foreground">
|
Check for new releases and update Dokploy.
|
||||||
<li>Want to try the latest features</li>
|
<br />
|
||||||
<li>Some bug that is blocking to use some features</li>
|
<br />
|
||||||
</ul>
|
We recommend checking for updates regularly to ensure you have the
|
||||||
<AlertBlock type="info">
|
latest features and security improvements.
|
||||||
We recommend checking the latest version for any breaking changes
|
</p>
|
||||||
before updating. Go to{" "}
|
</div>
|
||||||
<Link
|
)}
|
||||||
href="https://github.com/Dokploy/dokploy/releases"
|
|
||||||
target="_blank"
|
|
||||||
className="text-foreground"
|
|
||||||
>
|
|
||||||
Dokploy Releases
|
|
||||||
</Link>{" "}
|
|
||||||
to check the latest version.
|
|
||||||
</AlertBlock>
|
|
||||||
|
|
||||||
<div className="w-full flex flex-col gap-4">
|
{/* Update available state */}
|
||||||
<ToggleAutoCheckUpdates />
|
{isUpdateAvailable && latestVersion && (
|
||||||
{isUpdateAvailable === false && (
|
<div className="mb-8">
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="inline-flex items-center gap-2 rounded-lg px-3 py-2 border border-emerald-900 bg-emerald-900 dark:bg-emerald-900/40 mb-4 w-full">
|
||||||
<RefreshCcw className="size-6 self-center text-muted-foreground" />
|
<div className="flex items-center gap-1.5">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="flex h-2 w-2">
|
||||||
You are using the latest version
|
<span className="animate-ping absolute inline-flex h-2 w-2 rounded-full bg-emerald-400 opacity-75" />
|
||||||
|
<span className="relative inline-flex rounded-full h-2 w-2 bg-emerald-500" />
|
||||||
|
</span>
|
||||||
|
<Download className="h-4 w-4 text-emerald-400" />
|
||||||
|
<span className="text font-medium text-emerald-400 ">
|
||||||
|
New version available:
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
<span className="text font-semibold text-emerald-300">
|
||||||
|
{latestVersion}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4 text-muted-foreground">
|
||||||
|
<p className="text">
|
||||||
|
A new version of the server software is available. Consider
|
||||||
|
updating if you:
|
||||||
|
</p>
|
||||||
|
<ul className="space-y-3">
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Stars className="h-5 w-5 mt-0.5 text-[#5B9DFF]" />
|
||||||
|
<span className="text">
|
||||||
|
Want to access the latest features and improvements
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
|
<Bug className="h-5 w-5 mt-0.5 text-[#5B9DFF]" />
|
||||||
|
<span className="text">
|
||||||
|
Are experiencing issues that may be resolved in the new
|
||||||
|
version
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Up to date state */}
|
||||||
|
{hasCheckedUpdate && !isUpdateAvailable && !isLoading && (
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex flex-col items-center gap-6 mb-6">
|
||||||
|
<div className="rounded-full p-4 bg-emerald-400/40">
|
||||||
|
<Sparkles className="h-8 w-8 text-emerald-400" />
|
||||||
|
</div>
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<h3 className="text-lg font-medium">
|
||||||
|
You are using the latest version
|
||||||
|
</h3>
|
||||||
|
<p className="text text-muted-foreground">
|
||||||
|
Your server is up to date with all the latest features and
|
||||||
|
security improvements.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasCheckedUpdate && isLoading && (
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex flex-col items-center gap-6 mb-6">
|
||||||
|
<div className="rounded-full p-4 bg-[#5B9DFF]/40 text-foreground">
|
||||||
|
<RefreshCcw className="h-8 w-8 animate-spin" />
|
||||||
|
</div>
|
||||||
|
<div className="text-center space-y-2">
|
||||||
|
<h3 className="text-lg font-medium">Checking for updates...</h3>
|
||||||
|
<p className="text text-muted-foreground">
|
||||||
|
Please wait while we pull the latest version information from
|
||||||
|
Docker Hub.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isUpdateAvailable && (
|
||||||
|
<div className="rounded-lg bg-[#16254D] p-4 mb-8">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Info className="h-5 w-5 flex-shrink-0 text-[#5B9DFF]" />
|
||||||
|
<div className="text-[#5B9DFF]">
|
||||||
|
We recommend reviewing the{" "}
|
||||||
|
<Link
|
||||||
|
href="https://github.com/Dokploy/dokploy/releases"
|
||||||
|
target="_blank"
|
||||||
|
className="text-white underline hover:text-zinc-200"
|
||||||
|
>
|
||||||
|
release notes
|
||||||
|
</Link>{" "}
|
||||||
|
for any breaking changes before updating.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between pt-2">
|
||||||
|
<ToggleAutoCheckUpdates disabled={isLoading} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4 flex items-center justify-end">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button variant="outline" onClick={() => setIsOpen(false)}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
{isUpdateAvailable ? (
|
{isUpdateAvailable ? (
|
||||||
<UpdateWebServer />
|
<UpdateWebServer />
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
className="w-full"
|
variant="secondary"
|
||||||
onClick={handleCheckUpdates}
|
onClick={handleCheckUpdates}
|
||||||
isLoading={isLoading}
|
disabled={isLoading}
|
||||||
>
|
>
|
||||||
{isLoading ? "Checking for updates..." : "Check for updates"}
|
{isLoading ? (
|
||||||
|
<>
|
||||||
|
<RefreshCcw className="h-4 w-4 animate-spin" />
|
||||||
|
Checking for updates
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<RefreshCcw className="h-4 w-4" />
|
||||||
|
Check for updates
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -106,3 +228,5 @@ export const UpdateServer = () => {
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default UpdateServer;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { HardDriveDownload } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -21,7 +22,7 @@ export const UpdateWebServer = ({ isNavbar }: Props) => {
|
|||||||
const { mutateAsync: updateServer, isLoading } =
|
const { mutateAsync: updateServer, isLoading } =
|
||||||
api.settings.updateServer.useMutation();
|
api.settings.updateServer.useMutation();
|
||||||
|
|
||||||
const buttonLabel = isNavbar ? "Update available" : "Update server";
|
const buttonLabel = isNavbar ? "Update available" : "Update Server";
|
||||||
|
|
||||||
const handleConfirm = async () => {
|
const handleConfirm = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -49,6 +50,7 @@ export const UpdateWebServer = ({ isNavbar }: Props) => {
|
|||||||
variant={isNavbar ? "outline" : "secondary"}
|
variant={isNavbar ? "outline" : "secondary"}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
|
{!isLoading && <HardDriveDownload className="h-4 w-4" />}
|
||||||
{!isLoading && (
|
{!isLoading && (
|
||||||
<span className="absolute -right-1 -top-2 flex h-3 w-3">
|
<span className="absolute -right-1 -top-2 flex h-3 w-3">
|
||||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" />
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ import { api } from "@/utils/api";
|
|||||||
import { HeartIcon } from "lucide-react";
|
import { HeartIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
import { UpdateWebServer } from "../dashboard/settings/web-server/update-webserver";
|
||||||
import { Logo } from "../shared/logo";
|
import { Logo } from "../shared/logo";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
|
||||||
import { buttonVariants } from "../ui/button";
|
import { buttonVariants } from "../ui/button";
|
||||||
import { useEffect, useRef, useState } from "react";
|
|
||||||
import { UpdateWebServer } from "../dashboard/settings/web-server/update-webserver";
|
|
||||||
|
|
||||||
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 5;
|
const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 5;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { removeJob, schedule } from "@/server/utils/backup";
|
import { removeJob, schedule } from "@/server/utils/backup";
|
||||||
import {
|
import {
|
||||||
|
DEFAULT_UPDATE_DATA,
|
||||||
IS_CLOUD,
|
IS_CLOUD,
|
||||||
canAccessToTraefikFiles,
|
canAccessToTraefikFiles,
|
||||||
cleanStoppedContainers,
|
cleanStoppedContainers,
|
||||||
@@ -25,6 +26,7 @@ import {
|
|||||||
findAdminById,
|
findAdminById,
|
||||||
findServerById,
|
findServerById,
|
||||||
getDokployImage,
|
getDokployImage,
|
||||||
|
getUpdateData,
|
||||||
initializeTraefik,
|
initializeTraefik,
|
||||||
logRotationManager,
|
logRotationManager,
|
||||||
parseRawConfig,
|
parseRawConfig,
|
||||||
@@ -45,14 +47,12 @@ import {
|
|||||||
stopService,
|
stopService,
|
||||||
stopServiceRemote,
|
stopServiceRemote,
|
||||||
updateAdmin,
|
updateAdmin,
|
||||||
getUpdateData,
|
|
||||||
updateLetsEncryptEmail,
|
updateLetsEncryptEmail,
|
||||||
updateServerById,
|
updateServerById,
|
||||||
updateServerTraefik,
|
updateServerTraefik,
|
||||||
writeConfig,
|
writeConfig,
|
||||||
writeMainConfig,
|
writeMainConfig,
|
||||||
writeTraefikConfigInPath,
|
writeTraefikConfigInPath,
|
||||||
DEFAULT_UPDATE_DATA,
|
|
||||||
} from "@dokploy/server";
|
} from "@dokploy/server";
|
||||||
import { checkGPUStatus, setupGPUSupport } from "@dokploy/server";
|
import { checkGPUStatus, setupGPUSupport } from "@dokploy/server";
|
||||||
import { generateOpenApiDocument } from "@dokploy/trpc-openapi";
|
import { generateOpenApiDocument } from "@dokploy/trpc-openapi";
|
||||||
|
|||||||
Reference in New Issue
Block a user