mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-06 04:19:37 +00:00
feat: add react tour
This commit is contained in:
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"recommendations": ["biomejs.biome"]
|
||||
}
|
||||
26
.vscode/settings.json
vendored
26
.vscode/settings.json
vendored
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "biomejs.biome"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"quickfix.biome": "explicit",
|
||||
"source.organizeImports.biome": "explicit"
|
||||
},
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.formatOnSave": true,
|
||||
"emmet.showExpandedAbbreviation": "never",
|
||||
"prettier.enable": false
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { CodeEditor } from "@/components/shared/code-editor";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { FileTerminal } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
|
||||
interface Props {
|
||||
serverId: string;
|
||||
}
|
||||
|
||||
const schema = z.object({
|
||||
command: z.string().min(1, {
|
||||
message: "Command is required",
|
||||
}),
|
||||
});
|
||||
|
||||
type Schema = z.infer<typeof schema>;
|
||||
|
||||
export const EditScript = ({ serverId }: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const { data: server } = api.server.one.useQuery(
|
||||
{
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!serverId,
|
||||
},
|
||||
);
|
||||
|
||||
const { mutateAsync, isLoading } = api.server.update.useMutation();
|
||||
|
||||
const { data: defaultCommand } = api.server.getDefaultCommand.useQuery(
|
||||
{
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!serverId,
|
||||
},
|
||||
);
|
||||
|
||||
const form = useForm<Schema>({
|
||||
defaultValues: {
|
||||
command: "",
|
||||
},
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (server) {
|
||||
form.reset({
|
||||
command: server.command || defaultCommand,
|
||||
});
|
||||
}
|
||||
}, [server, defaultCommand]);
|
||||
|
||||
const onSubmit = async (formData: Schema) => {
|
||||
if (server) {
|
||||
await mutateAsync({
|
||||
...server,
|
||||
command: formData.command || "",
|
||||
serverId,
|
||||
})
|
||||
.then((data) => {
|
||||
toast.success("Script modified successfully");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error modifying the script");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline">
|
||||
Modify Script
|
||||
<FileTerminal className="size-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-5xl overflow-x-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Modify Script</DialogTitle>
|
||||
<DialogDescription>
|
||||
Modify the script which install everything necessary to deploy
|
||||
applications on your server,
|
||||
</DialogDescription>
|
||||
|
||||
<AlertBlock type="warning">
|
||||
We suggest to don't modify the script if you don't know what you are
|
||||
doing
|
||||
</AlertBlock>
|
||||
</DialogHeader>
|
||||
<div className="grid gap-4">
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
id="hook-form-delete-application"
|
||||
className="grid w-full gap-4"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="command"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Command</FormLabel>
|
||||
<FormControl className="max-h-[75vh] max-w-[60rem] overflow-y-scroll overflow-x-hidden">
|
||||
<CodeEditor
|
||||
language="shell"
|
||||
{...field}
|
||||
placeholder={`
|
||||
set -e
|
||||
echo "Hello world"
|
||||
`}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</div>
|
||||
<DialogFooter className="flex justify-between w-full">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
form.reset({
|
||||
command: defaultCommand || "",
|
||||
});
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={isLoading}
|
||||
form="hook-form-delete-application"
|
||||
type="submit"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -32,6 +32,7 @@ import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { ShowDeployment } from "../../application/deployments/show-deployment";
|
||||
import { EditScript } from "./edit-script";
|
||||
import { GPUSupport } from "./gpu-support";
|
||||
import { ValidateServer } from "./validate-server";
|
||||
|
||||
@@ -89,7 +90,12 @@ export const SetupServer = ({ serverId }: Props) => {
|
||||
</AlertBlock>
|
||||
</div>
|
||||
) : (
|
||||
<div id="hook-form-add-gitlab" className="grid w-full gap-1">
|
||||
<div id="hook-form-add-gitlab" className="grid w-full gap-4">
|
||||
<AlertBlock type="warning">
|
||||
Using a root user is required to ensure everything works as
|
||||
expected.
|
||||
</AlertBlock>
|
||||
|
||||
<Tabs defaultValue="ssh-keys">
|
||||
<TabsList className="grid grid-cols-4 w-[600px]">
|
||||
<TabsTrigger value="ssh-keys">SSH Keys</TabsTrigger>
|
||||
@@ -198,6 +204,28 @@ export const SetupServer = ({ serverId }: Props) => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 w-full border rounded-lg p-4">
|
||||
<span className="text-base font-semibold text-primary">
|
||||
Supported Distros:
|
||||
</span>
|
||||
<p>
|
||||
We strongly recommend to use the following distros to
|
||||
ensure the best experience:
|
||||
</p>
|
||||
<ul>
|
||||
<li>1. Ubuntu 24.04 LTS</li>
|
||||
<li>2. Ubuntu 23.10 LTS </li>
|
||||
<li>3. Ubuntu 22.04 LTS</li>
|
||||
<li>4. Ubuntu 20.04 LTS</li>
|
||||
<li>5. Ubuntu 18.04 LTS</li>
|
||||
<li>6. Debian 12</li>
|
||||
<li>7. Debian 11</li>
|
||||
<li>8. Debian 10</li>
|
||||
<li>9. Fedora 40</li>
|
||||
<li>10. Centos 9</li>
|
||||
<li>11. Centos 8</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="deployments">
|
||||
@@ -214,24 +242,29 @@ export const SetupServer = ({ serverId }: Props) => {
|
||||
See all the 5 Server Setup
|
||||
</CardDescription>
|
||||
</div>
|
||||
<DialogAction
|
||||
title={"Setup Server?"}
|
||||
description="This will setup the server and all associated data"
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
serverId: server?.serverId || "",
|
||||
})
|
||||
.then(async () => {
|
||||
refetch();
|
||||
toast.success("Server setup successfully");
|
||||
<div className="flex flex-row gap-2">
|
||||
<EditScript serverId={server?.serverId || ""} />
|
||||
<DialogAction
|
||||
title={"Setup Server?"}
|
||||
description="This will setup the server and all associated data"
|
||||
onClick={async () => {
|
||||
await mutateAsync({
|
||||
serverId: server?.serverId || "",
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error configuring server");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button isLoading={isLoading}>Setup Server</Button>
|
||||
</DialogAction>
|
||||
.then(async () => {
|
||||
refetch();
|
||||
toast.success("Server setup successfully");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error configuring server");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button isLoading={isLoading}>
|
||||
Setup Server
|
||||
</Button>
|
||||
</DialogAction>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-4">
|
||||
|
||||
@@ -2,7 +2,9 @@ import { cn } from "@/lib/utils";
|
||||
import { json } from "@codemirror/lang-json";
|
||||
import { yaml } from "@codemirror/lang-yaml";
|
||||
import { StreamLanguage } from "@codemirror/language";
|
||||
|
||||
import { properties } from "@codemirror/legacy-modes/mode/properties";
|
||||
import { shell } from "@codemirror/legacy-modes/mode/shell";
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { githubDark, githubLight } from "@uiw/codemirror-theme-github";
|
||||
import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror";
|
||||
@@ -10,7 +12,7 @@ import { useTheme } from "next-themes";
|
||||
interface Props extends ReactCodeMirrorProps {
|
||||
wrapperClassName?: string;
|
||||
disabled?: boolean;
|
||||
language?: "yaml" | "json" | "properties";
|
||||
language?: "yaml" | "json" | "properties" | "shell";
|
||||
lineWrapping?: boolean;
|
||||
lineNumbers?: boolean;
|
||||
}
|
||||
@@ -39,7 +41,9 @@ export const CodeEditor = ({
|
||||
? yaml()
|
||||
: language === "json"
|
||||
? json()
|
||||
: StreamLanguage.define(properties),
|
||||
: language === "shell"
|
||||
? StreamLanguage.define(shell)
|
||||
: StreamLanguage.define(properties),
|
||||
props.lineWrapping ? EditorView.lineWrapping : [],
|
||||
]}
|
||||
{...props}
|
||||
|
||||
1
apps/dokploy/drizzle/0051_hard_gorgon.sql
Normal file
1
apps/dokploy/drizzle/0051_hard_gorgon.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "server" ADD COLUMN "command" text DEFAULT '' NOT NULL;
|
||||
4240
apps/dokploy/drizzle/meta/0051_snapshot.json
Normal file
4240
apps/dokploy/drizzle/meta/0051_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -358,6 +358,13 @@
|
||||
"when": 1733889104203,
|
||||
"tag": "0050_nappy_wrecker",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 51,
|
||||
"version": "6",
|
||||
"when": 1734241482851,
|
||||
"tag": "0051_hard_gorgon",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -35,6 +35,7 @@
|
||||
"test": "vitest --config __test__/vitest.config.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reactour/tour": "3.7.0",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/lang-yaml": "^6.1.1",
|
||||
"@codemirror/language": "^6.10.1",
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import "@/styles/globals.css";
|
||||
|
||||
import { SearchCommand } from "@/components/dashboard/search-command";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { Languages } from "@/lib/languages";
|
||||
import { api } from "@/utils/api";
|
||||
import { TourProvider } from "@reactour/tour";
|
||||
import type { NextPage } from "next";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
@@ -11,74 +13,83 @@ import { Inter } from "next/font/google";
|
||||
import Head from "next/head";
|
||||
import Script from "next/script";
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import { SearchCommand } from "@/components/dashboard/search-command";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const steps = [
|
||||
{
|
||||
selector: ".first-step",
|
||||
content: "This is the first Step",
|
||||
},
|
||||
{
|
||||
selector: ".second-step",
|
||||
content: "This is the second Step",
|
||||
},
|
||||
{
|
||||
selector: ".third-step",
|
||||
content: "This is the third Step",
|
||||
},
|
||||
];
|
||||
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
getLayout?: (page: ReactElement) => ReactNode;
|
||||
// session: Session | null;
|
||||
theme?: string;
|
||||
getLayout?: (page: ReactElement) => ReactNode;
|
||||
// session: Session | null;
|
||||
theme?: string;
|
||||
};
|
||||
|
||||
type AppPropsWithLayout = AppProps & {
|
||||
Component: NextPageWithLayout;
|
||||
Component: NextPageWithLayout;
|
||||
};
|
||||
|
||||
const MyApp = ({
|
||||
Component,
|
||||
pageProps: { ...pageProps },
|
||||
Component,
|
||||
pageProps: { ...pageProps },
|
||||
}: AppPropsWithLayout) => {
|
||||
const getLayout = Component.getLayout ?? ((page) => page);
|
||||
const getLayout = Component.getLayout ?? ((page) => page);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style jsx global>{`
|
||||
return (
|
||||
<>
|
||||
<style jsx global>{`
|
||||
:root {
|
||||
--font-inter: ${inter.style.fontFamily};
|
||||
}
|
||||
`}</style>
|
||||
<Head>
|
||||
<title>Dokploy</title>
|
||||
</Head>
|
||||
{process.env.NEXT_PUBLIC_UMAMI_HOST &&
|
||||
process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID && (
|
||||
<Script
|
||||
src={process.env.NEXT_PUBLIC_UMAMI_HOST}
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
|
||||
/>
|
||||
)}
|
||||
<Head>
|
||||
<title>Dokploy</title>
|
||||
</Head>
|
||||
{process.env.NEXT_PUBLIC_UMAMI_HOST &&
|
||||
process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID && (
|
||||
<Script
|
||||
src={process.env.NEXT_PUBLIC_UMAMI_HOST}
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
forcedTheme={Component.theme}
|
||||
>
|
||||
<Toaster richColors />
|
||||
<SearchCommand />
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
<TourProvider steps={steps}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
forcedTheme={Component.theme}
|
||||
>
|
||||
<Toaster richColors />
|
||||
<SearchCommand />
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ThemeProvider>
|
||||
</TourProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default api.withTRPC(
|
||||
appWithTranslation(
|
||||
MyApp,
|
||||
// keep this in sync with next-i18next.config.js
|
||||
// if you want to know why don't just import the config file, this because next-i18next.config.js must be a CJS, but the rest of the code is ESM.
|
||||
// Add the config here is due to the issue: https://github.com/i18next/next-i18next/issues/2259
|
||||
// if one day every page is translated, we can safely remove this config.
|
||||
{
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: Object.values(Languages),
|
||||
localeDetection: false,
|
||||
},
|
||||
fallbackLng: "en",
|
||||
keySeparator: false,
|
||||
}
|
||||
)
|
||||
appWithTranslation(MyApp, {
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: Object.values(Languages),
|
||||
localeDetection: false,
|
||||
},
|
||||
fallbackLng: "en",
|
||||
keySeparator: false,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
import {
|
||||
IS_CLOUD,
|
||||
createServer,
|
||||
defaultCommand,
|
||||
deleteServer,
|
||||
findAdminById,
|
||||
findServerById,
|
||||
@@ -69,6 +70,11 @@ export const serverRouter = createTRPCRouter({
|
||||
|
||||
return server;
|
||||
}),
|
||||
getDefaultCommand: protectedProcedure
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input, ctx }) => {
|
||||
return defaultCommand();
|
||||
}),
|
||||
all: protectedProcedure.query(async ({ ctx }) => {
|
||||
const result = await db
|
||||
.select({
|
||||
|
||||
@@ -102,7 +102,6 @@ export const setupDockerContainerTerminalWebSocketServer = (
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: server.sshKey?.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
} else {
|
||||
const shell = getShell();
|
||||
|
||||
@@ -124,7 +124,6 @@ export const setupTerminalWebSocketServer = (
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: server.sshKey?.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ export const server = pgTable("server", {
|
||||
.notNull()
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
serverStatus: serverStatus("serverStatus").notNull().default("active"),
|
||||
|
||||
command: text("command").notNull().default(""),
|
||||
sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
@@ -105,4 +105,7 @@ export const apiUpdateServer = createSchema
|
||||
username: true,
|
||||
sshKeyId: true,
|
||||
})
|
||||
.required();
|
||||
.required()
|
||||
.extend({
|
||||
command: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -60,6 +60,116 @@ export const serverSetup = async (serverId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const defaultCommand = () => {
|
||||
const bashCommand = `
|
||||
set -e;
|
||||
DOCKER_VERSION=27.0.3
|
||||
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
SYS_ARCH=$(uname -m)
|
||||
CURRENT_USER=$USER
|
||||
|
||||
echo "Installing requirements for: OS: $OS_TYPE"
|
||||
if [ $EUID != 0 ]; then
|
||||
echo "Please run this script as root or with sudo ❌"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Check if the OS is manjaro, if so, change it to arch
|
||||
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
|
||||
OS_TYPE="arch"
|
||||
fi
|
||||
|
||||
# Check if the OS is Asahi Linux, if so, change it to fedora
|
||||
if [ "$OS_TYPE" = "fedora-asahi-remix" ]; then
|
||||
OS_TYPE="fedora"
|
||||
fi
|
||||
|
||||
# Check if the OS is popOS, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "pop" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
# Check if the OS is linuxmint, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "linuxmint" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
#Check if the OS is zorin, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "zorin" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
if [ "$OS_TYPE" = "arch" ] || [ "$OS_TYPE" = "archarm" ]; then
|
||||
OS_VERSION="rolling"
|
||||
else
|
||||
OS_VERSION=$(grep -w "VERSION_ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
fi
|
||||
|
||||
if [ "$OS_TYPE" = 'amzn' ]; then
|
||||
dnf install -y findutils >/dev/null
|
||||
fi
|
||||
|
||||
case "$OS_TYPE" in
|
||||
arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine) ;;
|
||||
*)
|
||||
echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "---------------------------------------------"
|
||||
echo "| CPU Architecture | $SYS_ARCH"
|
||||
echo "| Operating System | $OS_TYPE $OS_VERSION"
|
||||
echo "| Docker | $DOCKER_VERSION"
|
||||
echo -e "---------------------------------------------\n"
|
||||
echo -e "1. Installing required packages (curl, wget, git, jq, openssl). "
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
${installUtilities()}
|
||||
|
||||
echo -e "2. Validating ports. "
|
||||
${validatePorts()}
|
||||
|
||||
|
||||
|
||||
echo -e "3. Installing RClone. "
|
||||
${installRClone()}
|
||||
|
||||
echo -e "4. Installing Docker. "
|
||||
${installDocker()}
|
||||
|
||||
echo -e "5. Setting up Docker Swarm"
|
||||
${setupSwarm()}
|
||||
|
||||
echo -e "6. Setting up Network"
|
||||
${setupNetwork()}
|
||||
|
||||
echo -e "7. Setting up Directories"
|
||||
${setupMainDirectory()}
|
||||
${setupDirectories()}
|
||||
|
||||
echo -e "8. Setting up Traefik"
|
||||
${createTraefikConfig()}
|
||||
|
||||
echo -e "9. Setting up Middlewares"
|
||||
${createDefaultMiddlewares()}
|
||||
|
||||
echo -e "10. Setting up Traefik Instance"
|
||||
${createTraefikInstance()}
|
||||
|
||||
echo -e "11. Installing Nixpacks"
|
||||
${installNixpacks()}
|
||||
|
||||
echo -e "12. Installing Buildpacks"
|
||||
${installBuildpacks()}
|
||||
`;
|
||||
|
||||
return bashCommand;
|
||||
};
|
||||
|
||||
const installRequirements = async (serverId: string, logPath: string) => {
|
||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||
const client = new Client();
|
||||
@@ -73,110 +183,8 @@ const installRequirements = async (serverId: string, logPath: string) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
client
|
||||
.once("ready", () => {
|
||||
const bashCommand = `
|
||||
set -e;
|
||||
# Thanks to coolify <3
|
||||
|
||||
DOCKER_VERSION=27.0.3
|
||||
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
SYS_ARCH=$(uname -m)
|
||||
CURRENT_USER=$USER
|
||||
|
||||
echo "Installing requirements for: OS: $OS_TYPE"
|
||||
if [ $EUID != 0 ]; then
|
||||
echo "Please run this script as root or with sudo ❌"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Check if the OS is manjaro, if so, change it to arch
|
||||
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
|
||||
OS_TYPE="arch"
|
||||
fi
|
||||
|
||||
# Check if the OS is Asahi Linux, if so, change it to fedora
|
||||
if [ "$OS_TYPE" = "fedora-asahi-remix" ]; then
|
||||
OS_TYPE="fedora"
|
||||
fi
|
||||
|
||||
# Check if the OS is popOS, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "pop" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
# Check if the OS is linuxmint, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "linuxmint" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
#Check if the OS is zorin, if so, change it to ubuntu
|
||||
if [ "$OS_TYPE" = "zorin" ]; then
|
||||
OS_TYPE="ubuntu"
|
||||
fi
|
||||
|
||||
if [ "$OS_TYPE" = "arch" ] || [ "$OS_TYPE" = "archarm" ]; then
|
||||
OS_VERSION="rolling"
|
||||
else
|
||||
OS_VERSION=$(grep -w "VERSION_ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
|
||||
fi
|
||||
|
||||
case "$OS_TYPE" in
|
||||
arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | amzn | alpine) ;;
|
||||
*)
|
||||
echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
echo -e "---------------------------------------------"
|
||||
echo "| CPU Architecture | $SYS_ARCH"
|
||||
echo "| Operating System | $OS_TYPE $OS_VERSION"
|
||||
echo "| Docker | $DOCKER_VERSION"
|
||||
echo -e "---------------------------------------------\n"
|
||||
echo -e "1. Installing required packages (curl, wget, git, jq, openssl). "
|
||||
|
||||
command_exists() {
|
||||
command -v "$@" > /dev/null 2>&1
|
||||
}
|
||||
|
||||
${installUtilities()}
|
||||
|
||||
echo -e "2. Validating ports. "
|
||||
${validatePorts()}
|
||||
|
||||
|
||||
|
||||
echo -e "3. Installing RClone. "
|
||||
${installRClone()}
|
||||
|
||||
echo -e "4. Installing Docker. "
|
||||
${installDocker()}
|
||||
|
||||
echo -e "5. Setting up Docker Swarm"
|
||||
${setupSwarm()}
|
||||
|
||||
echo -e "6. Setting up Network"
|
||||
${setupNetwork()}
|
||||
|
||||
echo -e "7. Setting up Directories"
|
||||
${setupMainDirectory()}
|
||||
${setupDirectories()}
|
||||
|
||||
echo -e "8. Setting up Traefik"
|
||||
${createTraefikConfig()}
|
||||
|
||||
echo -e "9. Setting up Middlewares"
|
||||
${createDefaultMiddlewares()}
|
||||
|
||||
echo -e "10. Setting up Traefik Instance"
|
||||
${createTraefikInstance()}
|
||||
|
||||
echo -e "11. Installing Nixpacks"
|
||||
${installNixpacks()}
|
||||
|
||||
echo -e "12. Installing Buildpacks"
|
||||
${installBuildpacks()}
|
||||
`;
|
||||
client.exec(bashCommand, (err, stream) => {
|
||||
const command = server.command || defaultCommand();
|
||||
client.exec(command, (err, stream) => {
|
||||
if (err) {
|
||||
writeStream.write(err);
|
||||
reject(err);
|
||||
@@ -218,7 +226,6 @@ const installRequirements = async (serverId: string, logPath: string) => {
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: server.sshKey?.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -148,7 +148,6 @@ export const serverValidate = async (serverId: string) => {
|
||||
port: server.port,
|
||||
username: server.username,
|
||||
privateKey: server.sshKey?.privateKey,
|
||||
timeout: 99999,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
60
pnpm-lock.yaml
generated
60
pnpm-lock.yaml
generated
@@ -178,6 +178,9 @@ importers:
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.0.7
|
||||
version: 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@reactour/tour':
|
||||
specifier: 3.7.0
|
||||
version: 3.7.0(react@18.2.0)
|
||||
'@stripe/stripe-js':
|
||||
specifier: 4.8.0
|
||||
version: 4.8.0
|
||||
@@ -2871,6 +2874,26 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
|
||||
'@reactour/mask@1.1.0':
|
||||
resolution: {integrity: sha512-GkJMLuTs3vTsm4Ryq2uXcE4sMzRP1p4xSd6juSOMqbHa7IVD/UiLCLqJWHR9xGSQPbYhpZAZAORUG5cS0U5tBA==}
|
||||
peerDependencies:
|
||||
react: 16.x || 17.x || 18.x
|
||||
|
||||
'@reactour/popover@1.2.0':
|
||||
resolution: {integrity: sha512-1JMykZ+MmDmRlEVC5+DwlvK1exwV5bFHtiSFwVXnoPZmsSfwId6SLyjo9H6bybeuNtNEzviKdsF/ZBC1UQbDqg==}
|
||||
peerDependencies:
|
||||
react: 16.x || 17.x || 18.x
|
||||
|
||||
'@reactour/tour@3.7.0':
|
||||
resolution: {integrity: sha512-p0USaOBc5fcNBS5ZiQ2lsmztAhIGCUfx913Zw14FbEM8bhSXpR1F2JD0alVj9Ya1N+pnTNYatf14rSNGJsEnCg==}
|
||||
peerDependencies:
|
||||
react: 16.x || 17.x || 18.x
|
||||
|
||||
'@reactour/utils@0.5.0':
|
||||
resolution: {integrity: sha512-yQs5Nm/Dg1xRM7d/S/UILBV5OInrTgrjGzgc81/RP5khqdO5KnpOaC46yF83kDtCalte8X3RCwp+F2YA509k1w==}
|
||||
peerDependencies:
|
||||
react: 16.x || 17.x || 18.x
|
||||
|
||||
'@redis/bloom@1.2.0':
|
||||
resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==}
|
||||
peerDependencies:
|
||||
@@ -2980,6 +3003,11 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rooks/use-mutation-observer@4.11.2':
|
||||
resolution: {integrity: sha512-vpsdrZdr6TkB1zZJcHx+fR1YC/pHs2BaqcuYiEGjBVbwY5xcC49+h0hAUtQKHth3oJqXfIX/Ng8S7s5HFHdM/A==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||
|
||||
@@ -6012,6 +6040,9 @@ packages:
|
||||
reselect@5.1.1:
|
||||
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
resolve-alpn@1.2.1:
|
||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||
|
||||
@@ -8828,6 +8859,29 @@ snapshots:
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
|
||||
'@reactour/mask@1.1.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@reactour/utils': 0.5.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
|
||||
'@reactour/popover@1.2.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@reactour/utils': 0.5.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
|
||||
'@reactour/tour@3.7.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@reactour/mask': 1.1.0(react@18.2.0)
|
||||
'@reactour/popover': 1.2.0(react@18.2.0)
|
||||
'@reactour/utils': 0.5.0(react@18.2.0)
|
||||
react: 18.2.0
|
||||
|
||||
'@reactour/utils@0.5.0(react@18.2.0)':
|
||||
dependencies:
|
||||
'@rooks/use-mutation-observer': 4.11.2(react@18.2.0)
|
||||
react: 18.2.0
|
||||
resize-observer-polyfill: 1.5.1
|
||||
|
||||
'@redis/bloom@1.2.0(@redis/client@1.6.0)':
|
||||
dependencies:
|
||||
'@redis/client': 1.6.0
|
||||
@@ -8902,6 +8956,10 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.19.1':
|
||||
optional: true
|
||||
|
||||
'@rooks/use-mutation-observer@4.11.2(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
dependencies:
|
||||
domhandler: 5.0.3
|
||||
@@ -12238,6 +12296,8 @@ snapshots:
|
||||
|
||||
reselect@5.1.1: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
resolve-alpn@1.2.1: {}
|
||||
|
||||
resolve-from@4.0.0: {}
|
||||
|
||||
Reference in New Issue
Block a user