mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #829 from Dokploy/refactor/enhancement-languages
refactor: improve I18N
This commit is contained in:
commit
5f71a393be
@ -99,14 +99,14 @@ workflows:
|
||||
only:
|
||||
- main
|
||||
- canary
|
||||
- fix/build-i18n
|
||||
- refactor/enhancement-languages
|
||||
- build-arm64:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
- canary
|
||||
- fix/build-i18n
|
||||
- refactor/enhancement-languages
|
||||
- combine-manifests:
|
||||
requires:
|
||||
- build-amd64
|
||||
@ -116,4 +116,4 @@ workflows:
|
||||
only:
|
||||
- main
|
||||
- canary
|
||||
- fix/build-i18n
|
||||
- refactor/enhancement-languages
|
||||
|
@ -35,7 +35,7 @@ RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var
|
||||
COPY --from=build /prod/dokploy/.next ./.next
|
||||
COPY --from=build /prod/dokploy/dist ./dist
|
||||
COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs
|
||||
COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs
|
||||
# COPY --from=build /prod/dokploy/next-i18next.config.cjs ./next-i18next.config.cjs
|
||||
COPY --from=build /prod/dokploy/public ./public
|
||||
COPY --from=build /prod/dokploy/package.json ./package.json
|
||||
COPY --from=build /prod/dokploy/drizzle ./drizzle
|
||||
|
@ -26,6 +26,7 @@ if (typeof window === "undefined") {
|
||||
|
||||
const baseApp: ApplicationNested = {
|
||||
applicationId: "",
|
||||
herokuVersion: "",
|
||||
applicationStatus: "done",
|
||||
appName: "",
|
||||
autoDeploy: true,
|
||||
|
@ -6,6 +6,7 @@ import { expect, test } from "vitest";
|
||||
|
||||
const baseApp: ApplicationNested = {
|
||||
applicationId: "",
|
||||
herokuVersion: "",
|
||||
applicationStatus: "done",
|
||||
appName: "",
|
||||
autoDeploy: true,
|
||||
|
@ -68,7 +68,7 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
Open Terminal
|
||||
</Button>
|
||||
</DockerTerminalModal>
|
||||
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
|
||||
<div className="flex flex-row items-center gap-2 rounded-md px-4 py-2 border">
|
||||
<span className="text-sm font-medium">Autodeploy</span>
|
||||
<Switch
|
||||
aria-label="Toggle italic"
|
||||
|
@ -1,94 +1,96 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useState } from "react";
|
||||
|
||||
const Terminal = dynamic(
|
||||
() => import("./docker-terminal").then((e) => e.DockerTerminal),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
() => import("./docker-terminal").then((e) => e.DockerTerminal),
|
||||
{
|
||||
ssr: false,
|
||||
},
|
||||
);
|
||||
|
||||
interface Props {
|
||||
containerId: string;
|
||||
serverId?: string;
|
||||
children?: React.ReactNode;
|
||||
containerId: string;
|
||||
serverId?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const DockerTerminalModal = ({
|
||||
children,
|
||||
containerId,
|
||||
serverId,
|
||||
children,
|
||||
containerId,
|
||||
serverId,
|
||||
}: Props) => {
|
||||
const [mainDialogOpen, setMainDialogOpen] = useState(false);
|
||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||
const [mainDialogOpen, setMainDialogOpen] = useState(false);
|
||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||
|
||||
const handleMainDialogOpenChange = (open: boolean) => {
|
||||
if (!open) {
|
||||
setConfirmDialogOpen(true);
|
||||
} else {
|
||||
setMainDialogOpen(true);
|
||||
}
|
||||
};
|
||||
const handleMainDialogOpenChange = (open: boolean) => {
|
||||
if (!open) {
|
||||
setConfirmDialogOpen(true);
|
||||
} else {
|
||||
setMainDialogOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
setMainDialogOpen(false);
|
||||
};
|
||||
const handleConfirm = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
setMainDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
};
|
||||
return (
|
||||
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
{children}
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Docker Terminal</DialogTitle>
|
||||
<DialogDescription>
|
||||
Easy way to access to docker container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
const handleCancel = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
};
|
||||
return (
|
||||
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem
|
||||
className="w-full cursor-pointer space-x-3"
|
||||
onSelect={(e) => e.preventDefault()}
|
||||
>
|
||||
{children}
|
||||
</DropdownMenuItem>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Docker Terminal</DialogTitle>
|
||||
<DialogDescription>
|
||||
Easy way to access to docker container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Terminal
|
||||
id="terminal"
|
||||
containerId={containerId}
|
||||
serverId={serverId || ""}
|
||||
/>
|
||||
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure you want to close the terminal?</DialogTitle>
|
||||
<DialogDescription>
|
||||
By clicking the confirm button, the terminal will be closed.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm}>Confirm</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
<Terminal
|
||||
id="terminal"
|
||||
containerId={containerId}
|
||||
serverId={serverId || ""}
|
||||
/>
|
||||
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Are you sure you want to close the terminal?
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
By clicking the confirm button, the terminal will be closed.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm}>Confirm</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +27,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Languages } from "@/lib/languages";
|
||||
import useLocale from "@/utils/hooks/use-locale";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useTheme } from "next-themes";
|
||||
@ -37,25 +38,9 @@ const appearanceFormSchema = z.object({
|
||||
theme: z.enum(["light", "dark", "system"], {
|
||||
required_error: "Please select a theme.",
|
||||
}),
|
||||
language: z.enum(
|
||||
[
|
||||
"en",
|
||||
"pl",
|
||||
"ru",
|
||||
"fr",
|
||||
"de",
|
||||
"tr",
|
||||
"zh-Hant",
|
||||
"kz",
|
||||
"zh-Hans",
|
||||
"fa",
|
||||
"ko",
|
||||
"pt-br",
|
||||
],
|
||||
{
|
||||
required_error: "Please select a language.",
|
||||
},
|
||||
),
|
||||
language: z.nativeEnum(Languages, {
|
||||
required_error: "Please select a language.",
|
||||
}),
|
||||
});
|
||||
|
||||
type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;
|
||||
@ -63,7 +48,7 @@ type AppearanceFormValues = z.infer<typeof appearanceFormSchema>;
|
||||
// This can come from your database or API.
|
||||
const defaultValues: Partial<AppearanceFormValues> = {
|
||||
theme: "system",
|
||||
language: "en",
|
||||
language: Languages.English,
|
||||
};
|
||||
|
||||
export function AppearanceForm() {
|
||||
@ -188,25 +173,15 @@ export function AppearanceForm() {
|
||||
<SelectValue placeholder="No preset selected" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{[
|
||||
{ label: "English", value: "en" },
|
||||
{ label: "Polski", value: "pl" },
|
||||
{ label: "Русский", value: "ru" },
|
||||
{ label: "Français", value: "fr" },
|
||||
{ label: "Deutsch", value: "de" },
|
||||
{ label: "繁體中文", value: "zh-Hant" },
|
||||
{ label: "简体中文", value: "zh-Hans" },
|
||||
{ label: "Türkçe", value: "tr" },
|
||||
{ label: "Қазақ", value: "tr" },
|
||||
{ label: "Kazakh", value: "kz" },
|
||||
{ label: "Persian", value: "fa" },
|
||||
{ label: "한국어", value: "ko" },
|
||||
{ label: "Português", value: "pt-br" },
|
||||
].map((preset) => (
|
||||
<SelectItem key={preset.label} value={preset.value}>
|
||||
{preset.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
{Object.keys(Languages).map((preset) => {
|
||||
const value =
|
||||
Languages[preset as keyof typeof Languages];
|
||||
return (
|
||||
<SelectItem key={value} value={value}>
|
||||
{preset}
|
||||
</SelectItem>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormItem>
|
||||
|
@ -1,22 +1,22 @@
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { api } from "@/utils/api";
|
||||
import { Loader2 } from "lucide-react";
|
||||
@ -25,118 +25,118 @@ import type React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const Terminal = dynamic(
|
||||
() =>
|
||||
import("@/components/dashboard/docker/terminal/docker-terminal").then(
|
||||
(e) => e.DockerTerminal
|
||||
),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
() =>
|
||||
import("@/components/dashboard/docker/terminal/docker-terminal").then(
|
||||
(e) => e.DockerTerminal,
|
||||
),
|
||||
{
|
||||
ssr: false,
|
||||
},
|
||||
);
|
||||
|
||||
interface Props {
|
||||
appName: string;
|
||||
children?: React.ReactNode;
|
||||
serverId?: string;
|
||||
appName: string;
|
||||
children?: React.ReactNode;
|
||||
serverId?: string;
|
||||
}
|
||||
|
||||
export const DockerTerminalModal = ({ children, appName, serverId }: Props) => {
|
||||
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||
{
|
||||
appName,
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!appName,
|
||||
}
|
||||
);
|
||||
const [containerId, setContainerId] = useState<string | undefined>();
|
||||
const [mainDialogOpen, setMainDialogOpen] = useState(false);
|
||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||
const { data, isLoading } = api.docker.getContainersByAppNameMatch.useQuery(
|
||||
{
|
||||
appName,
|
||||
serverId,
|
||||
},
|
||||
{
|
||||
enabled: !!appName,
|
||||
},
|
||||
);
|
||||
const [containerId, setContainerId] = useState<string | undefined>();
|
||||
const [mainDialogOpen, setMainDialogOpen] = useState(false);
|
||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||
|
||||
const handleMainDialogOpenChange = (open: boolean) => {
|
||||
if (!open) {
|
||||
setConfirmDialogOpen(true);
|
||||
} else {
|
||||
setMainDialogOpen(true);
|
||||
}
|
||||
};
|
||||
const handleMainDialogOpenChange = (open: boolean) => {
|
||||
if (!open) {
|
||||
setConfirmDialogOpen(true);
|
||||
} else {
|
||||
setMainDialogOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
setMainDialogOpen(false);
|
||||
};
|
||||
const handleConfirm = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
setMainDialogOpen(false);
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
};
|
||||
const handleCancel = () => {
|
||||
setConfirmDialogOpen(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data?.length > 0) {
|
||||
setContainerId(data[0]?.containerId);
|
||||
}
|
||||
}, [data]);
|
||||
useEffect(() => {
|
||||
if (data && data?.length > 0) {
|
||||
setContainerId(data[0]?.containerId);
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
|
||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-7xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Docker Terminal</DialogTitle>
|
||||
<DialogDescription>
|
||||
Easy way to access to docker container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Label>Select a container to view logs</Label>
|
||||
<Select onValueChange={setContainerId} value={containerId}>
|
||||
<SelectTrigger>
|
||||
{isLoading ? (
|
||||
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||
<span>Loading...</span>
|
||||
<Loader2 className="animate-spin size-4" />
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue placeholder="Select a container" />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{data?.map((container) => (
|
||||
<SelectItem
|
||||
key={container.containerId}
|
||||
value={container.containerId}
|
||||
>
|
||||
{container.name} ({container.containerId}) {container.state}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectLabel>Containers ({data?.length})</SelectLabel>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Terminal
|
||||
serverId={serverId || ""}
|
||||
id="terminal"
|
||||
containerId={containerId || "select-a-container"}
|
||||
/>
|
||||
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Are you sure you want to close the terminal?
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
By clicking the confirm button, the terminal will be closed.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm}>Confirm</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
return (
|
||||
<Dialog open={mainDialogOpen} onOpenChange={handleMainDialogOpenChange}>
|
||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||
<DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-7xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Docker Terminal</DialogTitle>
|
||||
<DialogDescription>
|
||||
Easy way to access to docker container
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Label>Select a container to view logs</Label>
|
||||
<Select onValueChange={setContainerId} value={containerId}>
|
||||
<SelectTrigger>
|
||||
{isLoading ? (
|
||||
<div className="flex flex-row gap-2 items-center justify-center text-sm text-muted-foreground">
|
||||
<span>Loading...</span>
|
||||
<Loader2 className="animate-spin size-4" />
|
||||
</div>
|
||||
) : (
|
||||
<SelectValue placeholder="Select a container" />
|
||||
)}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
{data?.map((container) => (
|
||||
<SelectItem
|
||||
key={container.containerId}
|
||||
value={container.containerId}
|
||||
>
|
||||
{container.name} ({container.containerId}) {container.state}
|
||||
</SelectItem>
|
||||
))}
|
||||
<SelectLabel>Containers ({data?.length})</SelectLabel>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Terminal
|
||||
serverId={serverId || ""}
|
||||
id="terminal"
|
||||
containerId={containerId || "select-a-container"}
|
||||
/>
|
||||
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Are you sure you want to close the terminal?
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
By clicking the confirm button, the terminal will be closed.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={handleCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={handleConfirm}>Confirm</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
16
apps/dokploy/lib/languages.ts
Normal file
16
apps/dokploy/lib/languages.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export enum Languages {
|
||||
English = "en",
|
||||
Polish = "pl",
|
||||
Russian = "ru",
|
||||
French = "fr",
|
||||
German = "de",
|
||||
ChineseTraditional = "zh-Hant",
|
||||
ChineseSimplified = "zh-Hans",
|
||||
Turkish = "tr",
|
||||
Kazakh = "kz",
|
||||
Persian = "fa",
|
||||
Korean = "ko",
|
||||
Portuguese = "pt-br",
|
||||
}
|
||||
|
||||
export type Language = keyof typeof Languages;
|
@ -1,23 +1,23 @@
|
||||
/** @type {import('next-i18next').UserConfig} */
|
||||
module.exports = {
|
||||
fallbackLng: "en",
|
||||
keySeparator: false,
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: [
|
||||
"en",
|
||||
"pl",
|
||||
"ru",
|
||||
"fr",
|
||||
"de",
|
||||
"tr",
|
||||
"kz",
|
||||
"zh-Hant",
|
||||
"zh-Hans",
|
||||
"fa",
|
||||
"ko",
|
||||
"pt-br",
|
||||
],
|
||||
localeDetection: false,
|
||||
},
|
||||
fallbackLng: "en",
|
||||
keySeparator: false,
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: [
|
||||
"en",
|
||||
"pl",
|
||||
"ru",
|
||||
"fr",
|
||||
"de",
|
||||
"tr",
|
||||
"kz",
|
||||
"zh-Hant",
|
||||
"zh-Hans",
|
||||
"fa",
|
||||
"ko",
|
||||
"pt-br",
|
||||
],
|
||||
localeDetection: false,
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "@/styles/globals.css";
|
||||
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { Languages } from "@/lib/languages";
|
||||
import { api } from "@/utils/api";
|
||||
import type { NextPage } from "next";
|
||||
import { appWithTranslation } from "next-i18next";
|
||||
@ -71,20 +72,7 @@ export default api.withTRPC(
|
||||
{
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: [
|
||||
"en",
|
||||
"pl",
|
||||
"ru",
|
||||
"fr",
|
||||
"de",
|
||||
"tr",
|
||||
"kz",
|
||||
"zh-Hant",
|
||||
"zh-Hans",
|
||||
"fa",
|
||||
"ko",
|
||||
"pt-br",
|
||||
],
|
||||
locales: Object.values(Languages),
|
||||
localeDetection: false,
|
||||
},
|
||||
fallbackLng: "en",
|
||||
|
@ -10,142 +10,141 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { extractCommitMessage, extractHash } from "./[refreshToken]";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse,
|
||||
) {
|
||||
const signature = req.headers["x-hub-signature-256"];
|
||||
const githubBody = req.body;
|
||||
const signature = req.headers["x-hub-signature-256"];
|
||||
const githubBody = req.body;
|
||||
|
||||
if (!githubBody?.installation?.id) {
|
||||
res.status(400).json({ message: "Github Installation not found" });
|
||||
return;
|
||||
}
|
||||
if (!githubBody?.installation?.id) {
|
||||
res.status(400).json({ message: "Github Installation not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
const githubResult = await db.query.github.findFirst({
|
||||
where: eq(github.githubInstallationId, githubBody.installation.id),
|
||||
});
|
||||
const githubResult = await db.query.github.findFirst({
|
||||
where: eq(github.githubInstallationId, githubBody.installation.id),
|
||||
});
|
||||
|
||||
if (!githubResult) {
|
||||
res.status(400).json({ message: "Github Installation not found" });
|
||||
return;
|
||||
}
|
||||
if (!githubResult) {
|
||||
res.status(400).json({ message: "Github Installation not found" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!githubResult.githubWebhookSecret) {
|
||||
res.status(400).json({ message: "Github Webhook Secret not set" });
|
||||
return;
|
||||
}
|
||||
const webhooks = new Webhooks({
|
||||
secret: githubResult.githubWebhookSecret,
|
||||
});
|
||||
if (!githubResult.githubWebhookSecret) {
|
||||
res.status(400).json({ message: "Github Webhook Secret not set" });
|
||||
return;
|
||||
}
|
||||
const webhooks = new Webhooks({
|
||||
secret: githubResult.githubWebhookSecret,
|
||||
});
|
||||
|
||||
const verified = await webhooks.verify(
|
||||
JSON.stringify(githubBody),
|
||||
signature as string
|
||||
);
|
||||
const verified = await webhooks.verify(
|
||||
JSON.stringify(githubBody),
|
||||
signature as string,
|
||||
);
|
||||
|
||||
if (!verified) {
|
||||
res.status(401).json({ message: "Unauthorized" });
|
||||
return;
|
||||
}
|
||||
if (!verified) {
|
||||
res.status(401).json({ message: "Unauthorized" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.headers["x-github-event"] === "ping") {
|
||||
res.status(200).json({ message: "Ping received, webhook is active" });
|
||||
return;
|
||||
}
|
||||
if (req.headers["x-github-event"] === "ping") {
|
||||
res.status(200).json({ message: "Ping received, webhook is active" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (req.headers["x-github-event"] !== "push") {
|
||||
res.status(400).json({ message: "We only accept push events" });
|
||||
return;
|
||||
}
|
||||
if (req.headers["x-github-event"] !== "push") {
|
||||
res.status(400).json({ message: "We only accept push events" });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const branchName = githubBody?.ref?.replace("refs/heads/", "");
|
||||
const repository = githubBody?.repository?.name;
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
const owner = githubBody?.repository?.owner?.name;
|
||||
try {
|
||||
const branchName = githubBody?.ref?.replace("refs/heads/", "");
|
||||
const repository = githubBody?.repository?.name;
|
||||
const deploymentTitle = extractCommitMessage(req.headers, req.body);
|
||||
const deploymentHash = extractHash(req.headers, req.body);
|
||||
const owner = githubBody?.repository?.owner?.name;
|
||||
|
||||
const apps = await db.query.applications.findMany({
|
||||
where: and(
|
||||
eq(applications.sourceType, "github"),
|
||||
eq(applications.autoDeploy, true),
|
||||
eq(applications.branch, branchName),
|
||||
eq(applications.repository, repository),
|
||||
eq(applications.owner, owner),
|
||||
),
|
||||
});
|
||||
|
||||
const apps = await db.query.applications.findMany({
|
||||
where: and(
|
||||
eq(applications.sourceType, "github"),
|
||||
eq(applications.autoDeploy, true),
|
||||
eq(applications.branch, branchName),
|
||||
eq(applications.repository, repository),
|
||||
eq(applications.owner, owner)
|
||||
),
|
||||
});
|
||||
for (const app of apps) {
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: app.applicationId as string,
|
||||
titleLog: deploymentTitle,
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
server: !!app.serverId,
|
||||
};
|
||||
|
||||
for (const app of apps) {
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: app.applicationId as string,
|
||||
titleLog: deploymentTitle,
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
server: !!app.serverId,
|
||||
};
|
||||
if (IS_CLOUD && app.serverId) {
|
||||
jobData.serverId = app.serverId;
|
||||
await deploy(jobData);
|
||||
return true;
|
||||
}
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (IS_CLOUD && app.serverId) {
|
||||
jobData.serverId = app.serverId;
|
||||
await deploy(jobData);
|
||||
return true;
|
||||
}
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
const composeApps = await db.query.compose.findMany({
|
||||
where: and(
|
||||
eq(compose.sourceType, "github"),
|
||||
eq(compose.autoDeploy, true),
|
||||
eq(compose.branch, branchName),
|
||||
eq(compose.repository, repository),
|
||||
eq(compose.owner, owner),
|
||||
),
|
||||
});
|
||||
|
||||
const composeApps = await db.query.compose.findMany({
|
||||
where: and(
|
||||
eq(compose.sourceType, "github"),
|
||||
eq(compose.autoDeploy, true),
|
||||
eq(compose.branch, branchName),
|
||||
eq(compose.repository, repository),
|
||||
eq(compose.owner, owner)
|
||||
),
|
||||
});
|
||||
for (const composeApp of composeApps) {
|
||||
const jobData: DeploymentJob = {
|
||||
composeId: composeApp.composeId as string,
|
||||
titleLog: deploymentTitle,
|
||||
type: "deploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
server: !!composeApp.serverId,
|
||||
};
|
||||
|
||||
for (const composeApp of composeApps) {
|
||||
const jobData: DeploymentJob = {
|
||||
composeId: composeApp.composeId as string,
|
||||
titleLog: deploymentTitle,
|
||||
type: "deploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
server: !!composeApp.serverId,
|
||||
};
|
||||
if (IS_CLOUD && composeApp.serverId) {
|
||||
jobData.serverId = composeApp.serverId;
|
||||
await deploy(jobData);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IS_CLOUD && composeApp.serverId) {
|
||||
jobData.serverId = composeApp.serverId;
|
||||
await deploy(jobData);
|
||||
return true;
|
||||
}
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
const totalApps = apps.length + composeApps.length;
|
||||
const emptyApps = totalApps === 0;
|
||||
|
||||
const totalApps = apps.length + composeApps.length;
|
||||
const emptyApps = totalApps === 0;
|
||||
|
||||
if (emptyApps) {
|
||||
res.status(200).json({ message: "No apps to deploy" });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ message: `Deployed ${totalApps} apps` });
|
||||
} catch (error) {
|
||||
res.status(400).json({ message: "Error To Deploy Application", error });
|
||||
}
|
||||
if (emptyApps) {
|
||||
res.status(200).json({ message: "No apps to deploy" });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ message: `Deployed ${totalApps} apps` });
|
||||
} catch (error) {
|
||||
res.status(400).json({ message: "Error To Deploy Application", error });
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,44 @@
|
||||
{
|
||||
"settings.common.save": "저장",
|
||||
"settings.server.domain.title": "서버 도메인",
|
||||
"settings.server.domain.description": "서버 애플리케이션에 도메인을 추가합니다.",
|
||||
"settings.server.domain.form.domain": "도메인",
|
||||
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 이메일",
|
||||
"settings.server.domain.form.certificate.label": "인증서",
|
||||
"settings.server.domain.form.certificate.placeholder": "인증서 선택",
|
||||
"settings.server.domain.form.certificateOptions.none": "없음",
|
||||
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (기본)",
|
||||
"settings.common.save": "저장",
|
||||
"settings.server.domain.title": "서버 도메인",
|
||||
"settings.server.domain.description": "서버 애플리케이션에 도메인을 추가합니다.",
|
||||
"settings.server.domain.form.domain": "도메인",
|
||||
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt 이메일",
|
||||
"settings.server.domain.form.certificate.label": "인증서",
|
||||
"settings.server.domain.form.certificate.placeholder": "인증서 선택",
|
||||
"settings.server.domain.form.certificateOptions.none": "없음",
|
||||
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (기본)",
|
||||
|
||||
"settings.server.webServer.title": "웹 서버",
|
||||
"settings.server.webServer.description": "웹 서버를 재시작하거나 정리합니다.",
|
||||
"settings.server.webServer.actions": "작업",
|
||||
"settings.server.webServer.reload": "재시작",
|
||||
"settings.server.webServer.watchLogs": "로그 보기",
|
||||
"settings.server.webServer.updateServerIp": "서버 IP 갱신",
|
||||
"settings.server.webServer.server.label": "서버",
|
||||
"settings.server.webServer.traefik.label": "Traefik",
|
||||
"settings.server.webServer.traefik.modifyEnv": "환경 변수 수정",
|
||||
"settings.server.webServer.storage.label": "저장 공간",
|
||||
"settings.server.webServer.storage.cleanUnusedImages": "사용하지 않는 이미지 정리",
|
||||
"settings.server.webServer.storage.cleanUnusedVolumes": "사용하지 않는 볼륨 정리",
|
||||
"settings.server.webServer.storage.cleanStoppedContainers": "정지된 컨테이너 정리",
|
||||
"settings.server.webServer.storage.cleanDockerBuilder": "도커 빌더 & 시스템 정리",
|
||||
"settings.server.webServer.storage.cleanMonitoring": "모니터링 데이터 정리",
|
||||
"settings.server.webServer.storage.cleanAll": "전체 정리",
|
||||
"settings.server.webServer.title": "웹 서버",
|
||||
"settings.server.webServer.description": "웹 서버를 재시작하거나 정리합니다.",
|
||||
"settings.server.webServer.actions": "작업",
|
||||
"settings.server.webServer.reload": "재시작",
|
||||
"settings.server.webServer.watchLogs": "로그 보기",
|
||||
"settings.server.webServer.updateServerIp": "서버 IP 갱신",
|
||||
"settings.server.webServer.server.label": "서버",
|
||||
"settings.server.webServer.traefik.label": "Traefik",
|
||||
"settings.server.webServer.traefik.modifyEnv": "환경 변수 수정",
|
||||
"settings.server.webServer.storage.label": "저장 공간",
|
||||
"settings.server.webServer.storage.cleanUnusedImages": "사용하지 않는 이미지 정리",
|
||||
"settings.server.webServer.storage.cleanUnusedVolumes": "사용하지 않는 볼륨 정리",
|
||||
"settings.server.webServer.storage.cleanStoppedContainers": "정지된 컨테이너 정리",
|
||||
"settings.server.webServer.storage.cleanDockerBuilder": "도커 빌더 & 시스템 정리",
|
||||
"settings.server.webServer.storage.cleanMonitoring": "모니터링 데이터 정리",
|
||||
"settings.server.webServer.storage.cleanAll": "전체 정리",
|
||||
|
||||
"settings.profile.title": "계정",
|
||||
"settings.profile.description": "여기에서 프로필 세부 정보를 변경하세요.",
|
||||
"settings.profile.email": "이메일",
|
||||
"settings.profile.password": "비밀번호",
|
||||
"settings.profile.avatar": "아바타",
|
||||
"settings.profile.title": "계정",
|
||||
"settings.profile.description": "여기에서 프로필 세부 정보를 변경하세요.",
|
||||
"settings.profile.email": "이메일",
|
||||
"settings.profile.password": "비밀번호",
|
||||
"settings.profile.avatar": "아바타",
|
||||
|
||||
"settings.appearance.title": "외관",
|
||||
"settings.appearance.description": "대시보드의 테마를 사용자 설정합니다.",
|
||||
"settings.appearance.theme": "테마",
|
||||
"settings.appearance.themeDescription": "대시보드 테마 선택",
|
||||
"settings.appearance.themes.light": "라이트",
|
||||
"settings.appearance.themes.dark": "다크",
|
||||
"settings.appearance.themes.system": "시스템",
|
||||
"settings.appearance.language": "언어",
|
||||
"settings.appearance.languageDescription": "대시보드에서 사용할 언어 선택"
|
||||
"settings.appearance.title": "외관",
|
||||
"settings.appearance.description": "대시보드의 테마를 사용자 설정합니다.",
|
||||
"settings.appearance.theme": "테마",
|
||||
"settings.appearance.themeDescription": "대시보드 테마 선택",
|
||||
"settings.appearance.themes.light": "라이트",
|
||||
"settings.appearance.themes.dark": "다크",
|
||||
"settings.appearance.themes.system": "시스템",
|
||||
"settings.appearance.language": "언어",
|
||||
"settings.appearance.languageDescription": "대시보드에서 사용할 언어 선택"
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{}
|
||||
{}
|
||||
|
@ -1,41 +1,41 @@
|
||||
{
|
||||
"settings.common.save": "Сақтау",
|
||||
"settings.server.domain.title": "Сервер домені",
|
||||
"settings.server.domain.description": "Dokploy сервер қолданбасына домен енгізіңіз.",
|
||||
"settings.server.domain.form.domain": "Домен",
|
||||
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Эл. поштасы",
|
||||
"settings.server.domain.form.certificate.label": "Сертификат",
|
||||
"settings.server.domain.form.certificate.placeholder": "Сертификатты таңдаңыз",
|
||||
"settings.server.domain.form.certificateOptions.none": "Жоқ",
|
||||
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Стандартты)",
|
||||
"settings.server.webServer.title": "Веб-Сервер",
|
||||
"settings.server.webServer.description": "Веб-серверді қайта жүктеу немесе тазалау.",
|
||||
"settings.server.webServer.actions": "Әрекеттер",
|
||||
"settings.server.webServer.reload": "Қайта жүктеу",
|
||||
"settings.server.webServer.watchLogs": "Журналдарды қарау",
|
||||
"settings.server.webServer.updateServerIp": "Сервердің IP жаңарту",
|
||||
"settings.server.webServer.server.label": "Сервер",
|
||||
"settings.server.webServer.traefik.label": "Traefik",
|
||||
"settings.server.webServer.traefik.modifyEnv": "Env Өзгерту",
|
||||
"settings.server.webServer.storage.label": "Диск кеңістігі",
|
||||
"settings.server.webServer.storage.cleanUnusedImages": "Пайдаланылмаған образды тазалау",
|
||||
"settings.server.webServer.storage.cleanUnusedVolumes": "Пайдаланылмаған томды тазалау",
|
||||
"settings.server.webServer.storage.cleanStoppedContainers": "Тоқтатылған контейнерлерді тазалау",
|
||||
"settings.server.webServer.storage.cleanDockerBuilder": "Docker Builder & Системаны тазалау",
|
||||
"settings.server.webServer.storage.cleanMonitoring": "Мониторингті тазалау",
|
||||
"settings.server.webServer.storage.cleanAll": "Барлығын тазалау",
|
||||
"settings.profile.title": "Аккаунт",
|
||||
"settings.profile.description": "Профиль мәліметтерін осы жерден өзгертіңіз.",
|
||||
"settings.profile.email": "Эл. пошта",
|
||||
"settings.profile.password": "Құпия сөз",
|
||||
"settings.profile.avatar": "Аватар",
|
||||
"settings.appearance.title": "Сыртқы түрі",
|
||||
"settings.appearance.description": "Dokploy сыртқы келбетін өзгерту.",
|
||||
"settings.appearance.theme": "Келбеті",
|
||||
"settings.appearance.themeDescription": "Жүйе тақтасының келбетің таңдаңыз",
|
||||
"settings.appearance.themes.light": "Жарық",
|
||||
"settings.appearance.themes.dark": "Қараңғы",
|
||||
"settings.appearance.themes.system": "Жүйелік",
|
||||
"settings.appearance.language": "Тіл",
|
||||
"settings.appearance.languageDescription": "Жүйе тақтасының тілің таңдаңыз"
|
||||
"settings.common.save": "Сақтау",
|
||||
"settings.server.domain.title": "Сервер домені",
|
||||
"settings.server.domain.description": "Dokploy сервер қолданбасына домен енгізіңіз.",
|
||||
"settings.server.domain.form.domain": "Домен",
|
||||
"settings.server.domain.form.letsEncryptEmail": "Let's Encrypt Эл. поштасы",
|
||||
"settings.server.domain.form.certificate.label": "Сертификат",
|
||||
"settings.server.domain.form.certificate.placeholder": "Сертификатты таңдаңыз",
|
||||
"settings.server.domain.form.certificateOptions.none": "Жоқ",
|
||||
"settings.server.domain.form.certificateOptions.letsencrypt": "Let's Encrypt (Стандартты)",
|
||||
"settings.server.webServer.title": "Веб-Сервер",
|
||||
"settings.server.webServer.description": "Веб-серверді қайта жүктеу немесе тазалау.",
|
||||
"settings.server.webServer.actions": "Әрекеттер",
|
||||
"settings.server.webServer.reload": "Қайта жүктеу",
|
||||
"settings.server.webServer.watchLogs": "Журналдарды қарау",
|
||||
"settings.server.webServer.updateServerIp": "Сервердің IP жаңарту",
|
||||
"settings.server.webServer.server.label": "Сервер",
|
||||
"settings.server.webServer.traefik.label": "Traefik",
|
||||
"settings.server.webServer.traefik.modifyEnv": "Env Өзгерту",
|
||||
"settings.server.webServer.storage.label": "Диск кеңістігі",
|
||||
"settings.server.webServer.storage.cleanUnusedImages": "Пайдаланылмаған образды тазалау",
|
||||
"settings.server.webServer.storage.cleanUnusedVolumes": "Пайдаланылмаған томды тазалау",
|
||||
"settings.server.webServer.storage.cleanStoppedContainers": "Тоқтатылған контейнерлерді тазалау",
|
||||
"settings.server.webServer.storage.cleanDockerBuilder": "Docker Builder & Системаны тазалау",
|
||||
"settings.server.webServer.storage.cleanMonitoring": "Мониторингті тазалау",
|
||||
"settings.server.webServer.storage.cleanAll": "Барлығын тазалау",
|
||||
"settings.profile.title": "Аккаунт",
|
||||
"settings.profile.description": "Профиль мәліметтерін осы жерден өзгертіңіз.",
|
||||
"settings.profile.email": "Эл. пошта",
|
||||
"settings.profile.password": "Құпия сөз",
|
||||
"settings.profile.avatar": "Аватар",
|
||||
"settings.appearance.title": "Сыртқы түрі",
|
||||
"settings.appearance.description": "Dokploy сыртқы келбетін өзгерту.",
|
||||
"settings.appearance.theme": "Келбеті",
|
||||
"settings.appearance.themeDescription": "Жүйе тақтасының келбетің таңдаңыз",
|
||||
"settings.appearance.themes.light": "Жарық",
|
||||
"settings.appearance.themes.dark": "Қараңғы",
|
||||
"settings.appearance.themes.system": "Жүйелік",
|
||||
"settings.appearance.language": "Тіл",
|
||||
"settings.appearance.languageDescription": "Жүйе тақтасының тілің таңдаңыз"
|
||||
}
|
||||
|
@ -31,8 +31,8 @@
|
||||
"settings.profile.email": "Email",
|
||||
"settings.profile.password": "Senha",
|
||||
"settings.profile.avatar": "Avatar",
|
||||
|
||||
"settings.appearance.title": "Aparência",
|
||||
|
||||
"settings.appearance.title": "Aparencia",
|
||||
"settings.appearance.description": "Personalize o tema do seu dashboard.",
|
||||
"settings.appearance.theme": "Tema",
|
||||
"settings.appearance.themeDescription": "Selecione um tema para o dashboard",
|
||||
|
@ -190,7 +190,7 @@ export const notificationRouter = createTRPCRouter({
|
||||
await sendDiscordNotification(input, {
|
||||
title: "> `🤚` - Test Notification",
|
||||
description: "> Hi, From Dokploy 👋",
|
||||
color: 0xf3f7f4
|
||||
color: 0xf3f7f4,
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,93 +1,93 @@
|
||||
import { Secrets } from "@/components/ui/secrets";
|
||||
import {
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateBase64,
|
||||
generateRandomDomain,
|
||||
type DomainSchema,
|
||||
type Schema,
|
||||
type Template,
|
||||
generateBase64,
|
||||
generateRandomDomain,
|
||||
} from "../utils";
|
||||
|
||||
export function generate(schema: Schema): Template {
|
||||
const triggerDomain = generateRandomDomain(schema);
|
||||
const triggerDomain = generateRandomDomain(schema);
|
||||
|
||||
const magicLinkSecret = generateBase64(16);
|
||||
const sessionSecret = generateBase64(16);
|
||||
const encryptionKey = generateBase64(32);
|
||||
const providerSecret = generateBase64(32);
|
||||
const coordinatorSecret = generateBase64(32);
|
||||
const magicLinkSecret = generateBase64(16);
|
||||
const sessionSecret = generateBase64(16);
|
||||
const encryptionKey = generateBase64(32);
|
||||
const providerSecret = generateBase64(32);
|
||||
const coordinatorSecret = generateBase64(32);
|
||||
|
||||
const dbPassword = generateBase64(24);
|
||||
const dbUser = "triggeruser";
|
||||
const dbName = "triggerdb";
|
||||
const dbPassword = generateBase64(24);
|
||||
const dbUser = "triggeruser";
|
||||
const dbName = "triggerdb";
|
||||
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: triggerDomain,
|
||||
port: 3000,
|
||||
serviceName: "webapp",
|
||||
},
|
||||
];
|
||||
const domains: DomainSchema[] = [
|
||||
{
|
||||
host: triggerDomain,
|
||||
port: 3000,
|
||||
serviceName: "webapp",
|
||||
},
|
||||
];
|
||||
|
||||
const envs = [
|
||||
`NODE_ENV=production`,
|
||||
`RUNTIME_PLATFORM=docker-compose`,
|
||||
`V3_ENABLED=true`,
|
||||
const envs = [
|
||||
"NODE_ENV=production",
|
||||
"RUNTIME_PLATFORM=docker-compose",
|
||||
"V3_ENABLED=true",
|
||||
|
||||
`# Domain configuration`,
|
||||
`TRIGGER_DOMAIN=${triggerDomain}`,
|
||||
`TRIGGER_PROTOCOL=http`,
|
||||
"# Domain configuration",
|
||||
`TRIGGER_DOMAIN=${triggerDomain}`,
|
||||
"TRIGGER_PROTOCOL=http",
|
||||
|
||||
`# Database configuration with secure credentials`,
|
||||
`POSTGRES_USER=${dbUser}`,
|
||||
`POSTGRES_PASSWORD=${dbPassword}`,
|
||||
`POSTGRES_DB=${dbName}`,
|
||||
`DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`,
|
||||
"# Database configuration with secure credentials",
|
||||
`POSTGRES_USER=${dbUser}`,
|
||||
`POSTGRES_PASSWORD=${dbPassword}`,
|
||||
`POSTGRES_DB=${dbName}`,
|
||||
`DATABASE_URL=postgresql://${dbUser}:${dbPassword}@postgres:5432/${dbName}`,
|
||||
|
||||
`# Secrets`,
|
||||
`MAGIC_LINK_SECRET=${magicLinkSecret}`,
|
||||
`SESSION_SECRET=${sessionSecret}`,
|
||||
`ENCRYPTION_KEY=${encryptionKey}`,
|
||||
`PROVIDER_SECRET=${providerSecret}`,
|
||||
`COORDINATOR_SECRET=${coordinatorSecret}`,
|
||||
"# Secrets",
|
||||
`MAGIC_LINK_SECRET=${magicLinkSecret}`,
|
||||
`SESSION_SECRET=${sessionSecret}`,
|
||||
`ENCRYPTION_KEY=${encryptionKey}`,
|
||||
`PROVIDER_SECRET=${providerSecret}`,
|
||||
`COORDINATOR_SECRET=${coordinatorSecret}`,
|
||||
|
||||
`# TRIGGER_TELEMETRY_DISABLED=1`,
|
||||
`INTERNAL_OTEL_TRACE_DISABLED=1`,
|
||||
`INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0`,
|
||||
"# TRIGGER_TELEMETRY_DISABLED=1",
|
||||
"INTERNAL_OTEL_TRACE_DISABLED=1",
|
||||
"INTERNAL_OTEL_TRACE_LOGGING_ENABLED=0",
|
||||
|
||||
`DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300`,
|
||||
`DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100`,
|
||||
"DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT=300",
|
||||
"DEFAULT_ENV_EXECUTION_CONCURRENCY_LIMIT=100",
|
||||
|
||||
`DIRECT_URL=\${DATABASE_URL}`,
|
||||
`REDIS_HOST=redis`,
|
||||
`REDIS_PORT=6379`,
|
||||
`REDIS_TLS_DISABLED=true`,
|
||||
"DIRECT_URL=${DATABASE_URL}",
|
||||
"REDIS_HOST=redis",
|
||||
"REDIS_PORT=6379",
|
||||
"REDIS_TLS_DISABLED=true",
|
||||
|
||||
`# If this is set, emails that are not specified won't be able to log in`,
|
||||
`# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"`,
|
||||
`# Accounts with these emails will become admins when signing up and get access to the admin panel`,
|
||||
`# ADMIN_EMAILS="admin@example.com|another-admin@example.com"`,
|
||||
"# If this is set, emails that are not specified won't be able to log in",
|
||||
'# WHITELISTED_EMAILS="authorized@yahoo.com|authorized@gmail.com"',
|
||||
"# Accounts with these emails will become admins when signing up and get access to the admin panel",
|
||||
'# ADMIN_EMAILS="admin@example.com|another-admin@example.com"',
|
||||
|
||||
`# If this is set, your users will be able to log in via GitHub`,
|
||||
`# AUTH_GITHUB_CLIENT_ID=`,
|
||||
`# AUTH_GITHUB_CLIENT_SECRET=`,
|
||||
"# If this is set, your users will be able to log in via GitHub",
|
||||
"# AUTH_GITHUB_CLIENT_ID=",
|
||||
"# AUTH_GITHUB_CLIENT_SECRET=",
|
||||
|
||||
`# E-mail settings`,
|
||||
`# Ensure the FROM_EMAIL matches what you setup with Resend.com`,
|
||||
`# If these are not set, emails will be printed to the console`,
|
||||
`# FROM_EMAIL=`,
|
||||
`# REPLY_TO_EMAIL=`,
|
||||
`# RESEND_API_KEY=`,
|
||||
"# E-mail settings",
|
||||
"# Ensure the FROM_EMAIL matches what you setup with Resend.com",
|
||||
"# If these are not set, emails will be printed to the console",
|
||||
"# FROM_EMAIL=",
|
||||
"# REPLY_TO_EMAIL=",
|
||||
"# RESEND_API_KEY=",
|
||||
|
||||
`# Worker settings`,
|
||||
`HTTP_SERVER_PORT=9020`,
|
||||
`COORDINATOR_HOST=127.0.0.1`,
|
||||
`COORDINATOR_PORT=\${HTTP_SERVER_PORT}`,
|
||||
`# REGISTRY_HOST=\${DEPLOY_REGISTRY_HOST}`,
|
||||
`# REGISTRY_NAMESPACE=\${DEPLOY_REGISTRY_NAMESPACE}`,
|
||||
];
|
||||
"# Worker settings",
|
||||
"HTTP_SERVER_PORT=9020",
|
||||
"COORDINATOR_HOST=127.0.0.1",
|
||||
"COORDINATOR_PORT=${HTTP_SERVER_PORT}",
|
||||
"# REGISTRY_HOST=${DEPLOY_REGISTRY_HOST}",
|
||||
"# REGISTRY_NAMESPACE=${DEPLOY_REGISTRY_NAMESPACE}",
|
||||
];
|
||||
|
||||
return {
|
||||
envs,
|
||||
domains,
|
||||
};
|
||||
return {
|
||||
envs,
|
||||
domains,
|
||||
};
|
||||
}
|
||||
|
@ -1,26 +1,10 @@
|
||||
import type { Languages } from "@/lib/languages";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
const SUPPORTED_LOCALES = [
|
||||
"en",
|
||||
"pl",
|
||||
"ru",
|
||||
"fr",
|
||||
"de",
|
||||
"tr",
|
||||
"kz",
|
||||
"zh-Hant",
|
||||
"zh-Hans",
|
||||
"fa",
|
||||
"ko",
|
||||
"pt-br",
|
||||
] as const;
|
||||
|
||||
type Locale = (typeof SUPPORTED_LOCALES)[number];
|
||||
|
||||
export default function useLocale() {
|
||||
const currentLocale = (Cookies.get("DOKPLOY_LOCALE") ?? "en") as Locale;
|
||||
const currentLocale = (Cookies.get("DOKPLOY_LOCALE") ?? "en") as Languages;
|
||||
|
||||
const setLocale = (locale: Locale) => {
|
||||
const setLocale = (locale: Languages) => {
|
||||
Cookies.set("DOKPLOY_LOCALE", locale, { expires: 365 });
|
||||
window.location.reload();
|
||||
};
|
||||
|
@ -5,11 +5,19 @@ export function getLocale(cookies: NextApiRequestCookies) {
|
||||
return locale;
|
||||
}
|
||||
|
||||
// libs/i18n.js
|
||||
import { Languages } from "@/lib/languages";
|
||||
import { serverSideTranslations as originalServerSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import nextI18NextConfig from "../next-i18next.config.cjs";
|
||||
|
||||
export const serverSideTranslations = (
|
||||
locale: string,
|
||||
namespaces = ["common"],
|
||||
) => originalServerSideTranslations(locale, namespaces, nextI18NextConfig);
|
||||
) =>
|
||||
originalServerSideTranslations(locale, namespaces, {
|
||||
fallbackLng: "en",
|
||||
keySeparator: false,
|
||||
i18n: {
|
||||
defaultLocale: "en",
|
||||
locales: Object.values(Languages),
|
||||
localeDetection: false,
|
||||
},
|
||||
});
|
||||
|
@ -95,7 +95,9 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
},
|
||||
{
|
||||
name: "`❓`・Type",
|
||||
value: type.replace("error", "Failed").replace("success", "Successful"),
|
||||
value: type
|
||||
.replace("error", "Failed")
|
||||
.replace("success", "Successful"),
|
||||
inline: true,
|
||||
},
|
||||
...(type === "error" && errorMessage
|
||||
|
@ -48,7 +48,6 @@ export const sendDockerCleanupNotifications = async (
|
||||
title: "> `✅` - Docker Cleanup",
|
||||
color: 0x57f287,
|
||||
fields: [
|
||||
|
||||
{
|
||||
name: "`📅`・Date",
|
||||
value: date.toLocaleDateString(),
|
||||
|
Loading…
Reference in New Issue
Block a user