From ce06cd42b335ad12d4f013520d22392c06079a94 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Tue, 21 Jan 2025 23:41:05 +1100 Subject: [PATCH 01/61] fix(ui): show filePath instead of mountPath for file mounts --- .../advanced/volumes/show-volumes.tsx | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index 9575c59c..c84ed594 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -98,12 +98,20 @@ export const ShowVolumes = ({ id, type }: Props) => { )} {mount.type === "file" && ( -
- Content - - {mount.content} - -
+ <> +
+ Content + + {mount.content} + +
+
+ File Path + + {mount.filePath} + +
+ )} {mount.type === "bind" && (
@@ -113,12 +121,14 @@ export const ShowVolumes = ({ id, type }: Props) => {
)} -
- Mount Path - - {mount.mountPath} - -
+ {mount.type !== "file" && ( +
+ Mount Path + + {mount.mountPath} + +
+ )}
Date: Tue, 21 Jan 2025 23:45:54 +1100 Subject: [PATCH 02/61] refactor(ui): clearer ui display condition for volume mount display --- .../advanced/volumes/show-volumes.tsx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index c84ed594..bbfe4fa6 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -98,20 +98,12 @@ export const ShowVolumes = ({ id, type }: Props) => { )} {mount.type === "file" && ( - <> -
- Content - - {mount.content} - -
-
- File Path - - {mount.filePath} - -
- +
+ Content + + {mount.content} + +
)} {mount.type === "bind" && (
@@ -121,7 +113,14 @@ export const ShowVolumes = ({ id, type }: Props) => {
)} - {mount.type !== "file" && ( + {mount.type === "file" ? ( +
+ File Path + + {mount.filePath} + +
+ ) : (
Mount Path From 6d90e268f78f5f6faa78791360073eeb1de54bec Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Wed, 22 Jan 2025 01:09:44 +1100 Subject: [PATCH 03/61] fix(ui): volume file mount content, line clamp and preserve whitespace --- .../dashboard/application/advanced/volumes/show-volumes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index 9575c59c..9bbc3944 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -100,7 +100,7 @@ export const ShowVolumes = ({ id, type }: Props) => { {mount.type === "file" && (
Content - + {mount.content}
From 692f8830649cd4286d66ac2ccd6169504b9828b8 Mon Sep 17 00:00:00 2001 From: Vladyslav G Date: Tue, 21 Jan 2025 16:21:29 +0100 Subject: [PATCH 04/61] style(i18n) add ukrainian language --- apps/dokploy/lib/languages.ts | 1 + apps/dokploy/public/locales/uk/common.json | 1 + apps/dokploy/public/locales/uk/settings.json | 58 ++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 apps/dokploy/public/locales/uk/common.json create mode 100644 apps/dokploy/public/locales/uk/settings.json diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts index f83b3de6..883570bd 100644 --- a/apps/dokploy/lib/languages.ts +++ b/apps/dokploy/lib/languages.ts @@ -1,6 +1,7 @@ export const Languages = { english: { code: "en", name: "English" }, polish: { code: "pl", name: "Polski" }, + ukrainian: {code: 'uk', name: "Українська"}, russian: { code: "ru", name: "Русский" }, french: { code: "fr", name: "Français" }, german: { code: "de", name: "Deutsch" }, diff --git a/apps/dokploy/public/locales/uk/common.json b/apps/dokploy/public/locales/uk/common.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/apps/dokploy/public/locales/uk/common.json @@ -0,0 +1 @@ +{} diff --git a/apps/dokploy/public/locales/uk/settings.json b/apps/dokploy/public/locales/uk/settings.json new file mode 100644 index 00000000..d917f326 --- /dev/null +++ b/apps/dokploy/public/locales/uk/settings.json @@ -0,0 +1,58 @@ +{ + "settings.common.save": "Зберегти", + "settings.common.enterTerminal": "Увійти в термінал", + "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.traefik.managePorts": "Додаткові порти", + "settings.server.webServer.traefik.managePortsDescription": "Додайте або видаліть порти для Traefik", + "settings.server.webServer.traefik.targetPort": "Цільовий порт", + "settings.server.webServer.traefik.publishedPort": "Опублікований порт", + "settings.server.webServer.traefik.addPort": "Додати порт", + "settings.server.webServer.traefik.portsUpdated": "Порти успішно оновлено", + "settings.server.webServer.traefik.portsUpdateError": "Не вдалося оновити порти", + "settings.server.webServer.traefik.publishMode": "Режим публікації", + "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": "Налаштуйте тему вашої панелі керування.", + "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.terminal.connectionSettings": "Налаштування з'єднання", + "settings.terminal.ipAddress": "IP-адреса", + "settings.terminal.port": "Порт", + "settings.terminal.username": "Ім'я користувача" +} \ No newline at end of file From 026e1bece679305f9ea0f06e47cb1df4268ecb29 Mon Sep 17 00:00:00 2001 From: Rahadi Jalu Date: Wed, 22 Jan 2025 11:22:30 +0700 Subject: [PATCH 05/61] fix: filter navigation items based on user's permissions and role --- apps/dokploy/components/layouts/side.tsx | 770 +++++++++++++---------- 1 file changed, 442 insertions(+), 328 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index b2f87e41..c711b862 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,7 +1,6 @@ "use client"; import { Activity, - AudioWaveform, BarChartHorizontalBigIcon, Bell, BlocksIcon, @@ -9,7 +8,6 @@ import { Boxes, ChevronRight, CircleHelp, - Command, CreditCard, Database, Folder, @@ -27,8 +25,8 @@ import { Users, } from "lucide-react"; import { usePathname } from "next/navigation"; -import { useEffect, useState } from "react"; import type * as React from "react"; +import { useEffect, useState } from "react"; import { Breadcrumb, @@ -65,243 +63,290 @@ import { useSidebar, } from "@/components/ui/sidebar"; import { cn } from "@/lib/utils"; +import type { AppRouter } from "@/server/api/root"; import { api } from "@/utils/api"; +import type { inferRouterOutputs } from "@trpc/server"; import Link from "next/link"; import { useRouter } from "next/router"; import { Logo } from "../shared/logo"; import { UpdateServerButton } from "./update-server"; import { UserNav } from "./user-nav"; -// This is sample data. -interface NavItem { + +// The types of the queries we are going to use +type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; +type UserQueryOutput = inferRouterOutputs["user"]["byAuthId"]; + +type SingleNavItem = { + isSingle?: true; title: string; url: string; - icon: LucideIcon; - isSingle: boolean; - isActive: boolean; - items?: { - title: string; - url: string; - icon?: LucideIcon; - }[]; -} + icon?: LucideIcon; + isEnabled?: (opts: { + auth?: AuthQueryOutput; + user?: UserQueryOutput; + isCloud: boolean; + }) => boolean; +}; -interface ExternalLink { +// NavItem type +// Consists of a single item or a group of items +// If `isSingle` is true or undefined, the item is a single item +// If `isSingle` is false, the item is a group of items +type NavItem = + | SingleNavItem + | { + isSingle: false; + title: string; + icon: LucideIcon; + items: SingleNavItem[]; + isEnabled?: (opts: { + auth?: AuthQueryOutput; + user?: UserQueryOutput; + isCloud: boolean; + }) => boolean; + }; + +// ExternalLink type +// Represents an external link item (used for the help section) +type ExternalLink = { name: string; url: string; icon: React.ComponentType<{ className?: string }>; -} + isEnabled?: (opts: { + auth?: AuthQueryOutput; + user?: UserQueryOutput; + isCloud: boolean; + }) => boolean; +}; -const data = { - user: { - name: "shadcn", - email: "m@example.com", - avatar: "/avatars/shadcn.jpg", - }, - teams: [ - { - name: "Dokploy", - logo: Logo, - plan: "Enterprise", - }, - { - name: "Acme Corp.", - logo: AudioWaveform, - plan: "Startup", - }, - { - name: "Evil Corp.", - logo: Command, - plan: "Free", - }, - ], +// Menu type +// Consists of home, settings, and help items +type Menu = { + home: NavItem[]; + settings: NavItem[]; + help: ExternalLink[]; +}; + +// Menu items +// Consists of unfiltered home, settings, and help items +// The items are filtered based on the user's role and permissions +// The `isEnabled` function is called to determine if the item should be displayed +const MENU: Menu = { home: [ { + isSingle: true, title: "Projects", url: "/dashboard/projects", icon: Folder, - isSingle: true, - isActive: false, }, { + isSingle: true, title: "Monitoring", url: "/dashboard/monitoring", icon: BarChartHorizontalBigIcon, - isSingle: true, - isActive: false, + // Only enabled in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => !isCloud, }, { + isSingle: true, title: "Traefik File System", url: "/dashboard/traefik", icon: GalleryVerticalEnd, - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to Traefik files in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!( + (auth?.rol === "admin" || user?.canAccessToTraefikFiles) && + !isCloud + ), }, { + isSingle: true, title: "Docker", url: "/dashboard/docker", icon: BlocksIcon, - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to Docker in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), }, { + isSingle: true, title: "Swarm", url: "/dashboard/swarm", icon: PieChart, - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to Docker in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), }, { + isSingle: true, title: "Requests", url: "/dashboard/requests", icon: Forward, - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to Docker in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), }, + // Legacy unused menu, adjusted to the new structure // { + // isSingle: true, // title: "Projects", // url: "/dashboard/projects", // icon: Folder, - // isSingle: true, // }, // { + // isSingle: true, // title: "Monitoring", // icon: BarChartHorizontalBigIcon, // url: "/dashboard/settings/monitoring", - // isSingle: true, // }, - // { - // title: "Settings", - // url: "#", - // icon: Settings2, - // isActive: true, - // items: [ - // { - // title: "Profile", - // url: "/dashboard/settings/profile", - // }, - // { - // title: "Users", - // url: "/dashboard/settings/users", - // }, - // { - // title: "SSH Key", - // url: "/dashboard/settings/ssh-keys", - // }, - // { - // title: "Git", - // url: "/dashboard/settings/git-providers", - // }, - // ], + // isSingle: false, + // title: "Settings", + // icon: Settings2, + // items: [ + // { + // title: "Profile", + // url: "/dashboard/settings/profile", + // }, + // { + // title: "Users", + // url: "/dashboard/settings/users", + // }, + // { + // title: "SSH Key", + // url: "/dashboard/settings/ssh-keys", + // }, + // { + // title: "Git", + // url: "/dashboard/settings/git-providers", + // }, + // ], // }, - // { - // title: "Integrations", - // icon: BlocksIcon, - // items: [ - // { - // title: "S3 Destinations", - // url: "/dashboard/settings/destinations", - // }, - // { - // title: "Registry", - // url: "/dashboard/settings/registry", - // }, - // { - // title: "Notifications", - // url: "/dashboard/settings/notifications", - // }, - // ], - ] as NavItem[], + // isSingle: false, + // title: "Integrations", + // icon: BlocksIcon, + // items: [ + // { + // title: "S3 Destinations", + // url: "/dashboard/settings/destinations", + // }, + // { + // title: "Registry", + // url: "/dashboard/settings/registry", + // }, + // { + // title: "Notifications", + // url: "/dashboard/settings/notifications", + // }, + // ], + // }, + ], + settings: [ { + isSingle: true, title: "Server", url: "/dashboard/settings/server", icon: Activity, - isSingle: true, - isActive: false, + // Only enabled for admins in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!(auth?.rol === "admin" && !isCloud), }, { + isSingle: true, title: "Profile", url: "/dashboard/settings/profile", icon: User, - isSingle: true, - isActive: false, }, { + isSingle: true, title: "Servers", url: "/dashboard/settings/servers", icon: Server, - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "Users", icon: Users, url: "/dashboard/settings/users", - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "SSH Keys", icon: KeyRound, url: "/dashboard/settings/ssh-keys", - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to SSH keys + isEnabled: ({ auth, user }) => + !!(auth?.rol === "admin" || user?.canAccessToSSHKeys), }, - { + isSingle: true, title: "Git", url: "/dashboard/settings/git-providers", icon: GitBranch, - isSingle: true, - isActive: false, + // Only enabled for admins and users with access to Git providers + isEnabled: ({ auth, user }) => + !!(auth?.rol === "admin" || user?.canAccessToGitProviders), }, { + isSingle: true, title: "Registry", url: "/dashboard/settings/registry", icon: Package, - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "S3 Destinations", url: "/dashboard/settings/destinations", icon: Database, - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "Certificates", url: "/dashboard/settings/certificates", icon: ShieldCheck, - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "Cluster", url: "/dashboard/settings/cluster", icon: Boxes, - isSingle: true, - isActive: false, + // Only enabled for admins in non-cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!(auth?.rol === "admin" && !isCloud), }, { + isSingle: true, title: "Notifications", url: "/dashboard/settings/notifications", icon: Bell, - isSingle: true, - isActive: false, + // Only enabled for admins + isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), }, { + isSingle: true, title: "Billing", url: "/dashboard/settings/billing", icon: CreditCard, - isSingle: true, - isActive: false, + // Only enabled for admins in cloud environments + isEnabled: ({ auth, user, isCloud }) => + !!(auth?.rol === "admin" && isCloud), }, - ] as NavItem[], + ], + help: [ { name: "Documentation", @@ -325,8 +370,108 @@ const data = { /> ), }, - ] as ExternalLink[], -}; + ], +} as const; + +/** + * Creates a menu based on the current user's role and permissions + * @returns a menu object with the home, settings, and help items + */ +function createMenuForAuthUser(opts: { + auth?: AuthQueryOutput; + user?: UserQueryOutput; + isCloud: boolean; +}): Menu { + return { + // Filter the home items based on the user's role and permissions + // Calls the `isEnabled` function if it exists to determine if the item should be displayed + home: MENU.home.filter((item) => + !item.isEnabled + ? true + : item.isEnabled({ + auth: opts.auth, + user: opts.user, + isCloud: opts.isCloud, + }), + ), + // Filter the settings items based on the user's role and permissions + // Calls the `isEnabled` function if it exists to determine if the item should be displayed + settings: MENU.settings.filter((item) => + !item.isEnabled + ? true + : item.isEnabled({ + auth: opts.auth, + user: opts.user, + isCloud: opts.isCloud, + }), + ), + // Filter the help items based on the user's role and permissions + // Calls the `isEnabled` function if it exists to determine if the item should be displayed + help: MENU.help.filter((item) => + !item.isEnabled + ? true + : item.isEnabled({ + auth: opts.auth, + user: opts.user, + isCloud: opts.isCloud, + }), + ), + }; +} + +/** + * Determines if an item url is active based on the current pathname + * @returns true if the item url is active, false otherwise + */ +function isActiveRoute(opts: { + /** The url of the item. Usually obtained from `item.url` */ + itemUrl: string; + /** The current pathname. Usually obtained from `usePathname()` */ + pathname: string; +}): boolean { + const normalizedItemUrl = opts.itemUrl?.replace("/projects", "/project"); + const normalizedPathname = opts.pathname?.replace("/projects", "/project"); + + if (!normalizedPathname) return false; + + if (normalizedPathname === normalizedItemUrl) return true; + + if (normalizedPathname.startsWith(normalizedItemUrl)) { + const nextChar = normalizedPathname.charAt(normalizedItemUrl.length); + return nextChar === "/"; + } + + return false; +} + +/** + * Finds the active nav item based on the current pathname + * @returns the active nav item with `SingleNavItem` type or undefined if none is active + */ +function findActiveNavItem( + navItems: NavItem[], + pathname: string, +): SingleNavItem | undefined { + const found = navItems.find((item) => + item.isSingle !== false + ? // The current item is single, so check if the item url is active + isActiveRoute({ itemUrl: item.url, pathname }) + : // The current item is not single, so check if any of the sub items are active + item.items.some((item) => + isActiveRoute({ itemUrl: item.url, pathname }), + ), + ); + + if (found?.isSingle !== false) { + // The found item is single, so return it + return found; + } + + // The found item is not single, so find the active sub item + return found?.items.find((item) => + isActiveRoute({ itemUrl: item.url, pathname }), + ); +} interface Props { children: React.ReactNode; @@ -398,64 +543,21 @@ export default function Page({ children }: Props) { const includesProjects = pathname?.includes("/dashboard/project"); const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); - const isActiveRoute = (itemUrl: string) => { - const normalizedItemUrl = itemUrl?.replace("/projects", "/project"); - const normalizedPathname = pathname?.replace("/projects", "/project"); - if (!normalizedPathname) return false; + const { + home: filteredHome, + settings: filteredSettings, + help, + } = createMenuForAuthUser({ auth, user, isCloud: !!isCloud }); - if (normalizedPathname === normalizedItemUrl) return true; + const activeItem = findActiveNavItem( + [...filteredHome, ...filteredSettings], + pathname, + ); - if (normalizedPathname.startsWith(normalizedItemUrl)) { - const nextChar = normalizedPathname.charAt(normalizedItemUrl.length); - return nextChar === "/"; - } - - return false; - }; - - let filteredHome = isCloud - ? data.home.filter( - (item) => - ![ - "/dashboard/monitoring", - "/dashboard/traefik", - "/dashboard/docker", - "/dashboard/swarm", - "/dashboard/requests", - ].includes(item.url), - ) - : data.home; - - let filteredSettings = isCloud - ? data.settings.filter( - (item) => - ![ - "/dashboard/settings/server", - "/dashboard/settings/cluster", - ].includes(item.url), - ) - : data.settings.filter( - (item) => !["/dashboard/settings/billing"].includes(item.url), - ); - - filteredHome = filteredHome.map((item) => ({ - ...item, - isActive: isActiveRoute(item.url), - })); - - filteredSettings = filteredSettings.map((item) => ({ - ...item, - isActive: isActiveRoute(item.url), - })); - - const activeItem = - filteredHome.find((item) => item.isActive) || - filteredSettings.find((item) => item.isActive); - - const showProjectsButton = - currentPath === "/dashboard/projects" && - (auth?.rol === "admin" || user?.canCreateProjects); + // const showProjectsButton = + // currentPath === "/dashboard/projects" && + // (auth?.rol === "admin" || user?.canCreateProjects); return ( Home - {filteredHome.map((item) => ( - - - {item.isSingle ? ( - - - - {item.title} - - - ) : ( - <> - - - {item.icon && } + {filteredHome.map((item) => { + const isSingle = item.isSingle !== false; + const isActive = isSingle + ? isActiveRoute({ itemUrl: item.url, pathname }) + : item.items.some((item) => + isActiveRoute({ itemUrl: item.url, pathname }), + ); - {item.title} - {item.items?.length && ( - + return ( + + + {isSingle ? ( + + + {item.icon && ( + )} - - - - - {item.items?.map((subItem) => ( - - - {item.title}
+ + + ) : ( + <> + + + {item.icon && } + + {item.title} + {item.items?.length && ( + + )} + + + + + {item.items?.map((subItem) => ( + + - {subItem.icon && ( - - - - )} - {subItem.title} - - - - ))} - - - - )} - - - ))} + + {subItem.icon && ( + + + + )} + {subItem.title} + + + + ))} + + + + )} + + + ); + })} Settings - {filteredSettings.map((item) => ( - - - {item.isSingle ? ( - - - - {item.title} - - - ) : ( - <> - - - {item.icon && } + {filteredSettings.map((item) => { + const isSingle = item.isSingle !== false; + const isActive = isSingle + ? isActiveRoute({ itemUrl: item.url, pathname }) + : item.items.some((item) => + isActiveRoute({ itemUrl: item.url, pathname }), + ); - {item.title} - {item.items?.length && ( - + return ( + + + {isSingle ? ( + + + {item.icon && ( + )} - - - - - {item.items?.map((subItem) => ( - - - {item.title} + + + ) : ( + <> + + + {item.icon && } + + {item.title} + {item.items?.length && ( + + )} + + + + + {item.items?.map((subItem) => ( + + - {subItem.icon && ( - - - - )} - {subItem.title} - - - - ))} - - - - )} - - - ))} + + {subItem.icon && ( + + + + )} + {subItem.title} + + + + ))} + + + + )} + + + ); + })} Extra - {data.help.map((item: ExternalLink) => ( + {help.map((item: ExternalLink) => ( Date: Wed, 22 Jan 2025 11:23:18 +0700 Subject: [PATCH 06/61] fix: add condition to show create project button --- apps/dokploy/components/dashboard/projects/show.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index c4b2f672..afeabf30 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -87,9 +87,12 @@ export const ShowProjects = () => { Create and manage your projects -
- -
+ + {(auth?.rol === "admin" || user?.canCreateProjects) && ( +
+ +
+ )}
From 9e6e68558aeb56908e10c50be94198d14293f1a0 Mon Sep 17 00:00:00 2001 From: Rahadi Jalu Date: Wed, 22 Jan 2025 11:23:58 +0700 Subject: [PATCH 07/61] fix: adjust dialog title based on add/update condition --- apps/dokploy/components/dashboard/projects/handle-project.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/dashboard/projects/handle-project.tsx b/apps/dokploy/components/dashboard/projects/handle-project.tsx index cf38c57c..08e3e0a8 100644 --- a/apps/dokploy/components/dashboard/projects/handle-project.tsx +++ b/apps/dokploy/components/dashboard/projects/handle-project.tsx @@ -118,7 +118,7 @@ export const HandleProject = ({ projectId }: Props) => { - Add a project + {projectId ? "Update" : "Add a"} project The home of something big! {isError && {error?.message}} From 53df7d969e9192ebc05a66f948dd0af9a04ca095 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:13:22 -0600 Subject: [PATCH 08/61] refactor: make protected instead of admin --- apps/dokploy/server/api/routers/settings.ts | 1450 +++++++++---------- 1 file changed, 725 insertions(+), 725 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 449a2233..0224e343 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -1,59 +1,59 @@ import { db } from "@/server/db"; import { - apiAssignDomain, - apiEnableDashboard, - apiModifyTraefikConfig, - apiReadStatsLogs, - apiReadTraefikConfig, - apiSaveSSHKey, - apiServerSchema, - apiTraefikConfig, - apiUpdateDockerCleanup, + apiAssignDomain, + apiEnableDashboard, + apiModifyTraefikConfig, + apiReadStatsLogs, + apiReadTraefikConfig, + apiSaveSSHKey, + apiServerSchema, + apiTraefikConfig, + apiUpdateDockerCleanup, } from "@/server/db/schema"; import { removeJob, schedule } from "@/server/utils/backup"; import { - DEFAULT_UPDATE_DATA, - IS_CLOUD, - canAccessToTraefikFiles, - cleanStoppedContainers, - cleanUpDockerBuilder, - cleanUpSystemPrune, - cleanUpUnusedImages, - cleanUpUnusedVolumes, - execAsync, - execAsyncRemote, - findAdmin, - findAdminById, - findServerById, - getDokployImage, - getDokployImageTag, - getUpdateData, - initializeTraefik, - logRotationManager, - parseRawConfig, - paths, - prepareEnvironmentVariables, - processLogs, - pullLatestRelease, - readConfig, - readConfigInPath, - readDirectory, - readMainConfig, - readMonitoringConfig, - recreateDirectory, - sendDockerCleanupNotifications, - spawnAsync, - startService, - startServiceRemote, - stopService, - stopServiceRemote, - updateAdmin, - updateLetsEncryptEmail, - updateServerById, - updateServerTraefik, - writeConfig, - writeMainConfig, - writeTraefikConfigInPath, + DEFAULT_UPDATE_DATA, + IS_CLOUD, + canAccessToTraefikFiles, + cleanStoppedContainers, + cleanUpDockerBuilder, + cleanUpSystemPrune, + cleanUpUnusedImages, + cleanUpUnusedVolumes, + execAsync, + execAsyncRemote, + findAdmin, + findAdminById, + findServerById, + getDokployImage, + getDokployImageTag, + getUpdateData, + initializeTraefik, + logRotationManager, + parseRawConfig, + paths, + prepareEnvironmentVariables, + processLogs, + pullLatestRelease, + readConfig, + readConfigInPath, + readDirectory, + readMainConfig, + readMonitoringConfig, + recreateDirectory, + sendDockerCleanupNotifications, + spawnAsync, + startService, + startServiceRemote, + stopService, + stopServiceRemote, + updateAdmin, + updateLetsEncryptEmail, + updateServerById, + updateServerTraefik, + writeConfig, + writeMainConfig, + writeTraefikConfigInPath, } from "@dokploy/server"; import { checkGPUStatus, setupGPUSupport } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; @@ -65,736 +65,736 @@ import { z } from "zod"; import packageInfo from "../../../package.json"; import { appRouter } from "../root"; import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, + adminProcedure, + createTRPCRouter, + protectedProcedure, + publicProcedure, } from "../trpc"; export const settingsRouter = createTRPCRouter({ - reloadServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { stdout } = await execAsync( - "docker service inspect dokploy --format '{{.ID}}'", - ); - await execAsync(`docker service update --force ${stdout.trim()}`); - return true; - }), - reloadTraefik: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - try { - if (input?.serverId) { - await stopServiceRemote(input.serverId, "dokploy-traefik"); - await startServiceRemote(input.serverId, "dokploy-traefik"); - } else if (!IS_CLOUD) { - await stopService("dokploy-traefik"); - await startService("dokploy-traefik"); - } - } catch (err) { - console.error(err); - } + reloadServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { stdout } = await execAsync( + "docker service inspect dokploy --format '{{.ID}}'" + ); + await execAsync(`docker service update --force ${stdout.trim()}`); + return true; + }), + reloadTraefik: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + try { + if (input?.serverId) { + await stopServiceRemote(input.serverId, "dokploy-traefik"); + await startServiceRemote(input.serverId, "dokploy-traefik"); + } else if (!IS_CLOUD) { + await stopService("dokploy-traefik"); + await startService("dokploy-traefik"); + } + } catch (err) { + console.error(err); + } - return true; - }), - toggleDashboard: adminProcedure - .input(apiEnableDashboard) - .mutation(async ({ input }) => { - await initializeTraefik({ - enableDashboard: input.enableDashboard, - serverId: input.serverId, - }); - return true; - }), + return true; + }), + toggleDashboard: adminProcedure + .input(apiEnableDashboard) + .mutation(async ({ input }) => { + await initializeTraefik({ + enableDashboard: input.enableDashboard, + serverId: input.serverId, + }); + return true; + }), - cleanUnusedImages: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - return true; - }), - cleanUnusedVolumes: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedVolumes(input?.serverId); - return true; - }), - cleanStoppedContainers: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanStoppedContainers(input?.serverId); - return true; - }), - cleanDockerBuilder: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpDockerBuilder(input?.serverId); - }), - cleanDockerPrune: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpSystemPrune(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); + cleanUnusedImages: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + return true; + }), + cleanUnusedVolumes: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedVolumes(input?.serverId); + return true; + }), + cleanStoppedContainers: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanStoppedContainers(input?.serverId); + return true; + }), + cleanDockerBuilder: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpDockerBuilder(input?.serverId); + }), + cleanDockerPrune: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpSystemPrune(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); - return true; - }), - cleanAll: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - await cleanStoppedContainers(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); - await cleanUpSystemPrune(input?.serverId); + return true; + }), + cleanAll: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + await cleanStoppedContainers(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); + await cleanUpSystemPrune(input?.serverId); - return true; - }), - cleanMonitoring: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { MONITORING_PATH } = paths(); - await recreateDirectory(MONITORING_PATH); - return true; - }), - saveSSHPrivateKey: adminProcedure - .input(apiSaveSSHKey) - .mutation(async ({ input, ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: input.sshPrivateKey, - }); + return true; + }), + cleanMonitoring: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { MONITORING_PATH } = paths(); + await recreateDirectory(MONITORING_PATH); + return true; + }), + saveSSHPrivateKey: adminProcedure + .input(apiSaveSSHKey) + .mutation(async ({ input, ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: input.sshPrivateKey, + }); - return true; - }), - assignDomainServer: adminProcedure - .input(apiAssignDomain) - .mutation(async ({ ctx, input }) => { - if (IS_CLOUD) { - return true; - } - const admin = await updateAdmin(ctx.user.authId, { - host: input.host, - ...(input.letsEncryptEmail && { - letsEncryptEmail: input.letsEncryptEmail, - }), - certificateType: input.certificateType, - }); + return true; + }), + assignDomainServer: adminProcedure + .input(apiAssignDomain) + .mutation(async ({ ctx, input }) => { + if (IS_CLOUD) { + return true; + } + const admin = await updateAdmin(ctx.user.authId, { + host: input.host, + ...(input.letsEncryptEmail && { + letsEncryptEmail: input.letsEncryptEmail, + }), + certificateType: input.certificateType, + }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } - updateServerTraefik(admin, input.host); - if (input.letsEncryptEmail) { - updateLetsEncryptEmail(input.letsEncryptEmail); - } + updateServerTraefik(admin, input.host); + if (input.letsEncryptEmail) { + updateLetsEncryptEmail(input.letsEncryptEmail); + } - return admin; - }), - cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: null, - }); - return true; - }), - updateDockerCleanup: adminProcedure - .input(apiUpdateDockerCleanup) - .mutation(async ({ input, ctx }) => { - if (input.serverId) { - await updateServerById(input.serverId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + return admin; + }), + cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: null, + }); + return true; + }), + updateDockerCleanup: adminProcedure + .input(apiUpdateDockerCleanup) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + await updateServerById(input.serverId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - const server = await findServerById(input.serverId); + const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this server", - }); - } + if (server.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this server", + }); + } - if (server.enableDockerCleanup) { - const server = await findServerById(input.serverId); - if (server.serverStatus === "inactive") { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server is inactive", - }); - } - if (IS_CLOUD) { - await schedule({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - scheduleJob(server.serverId, "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...`, - ); - await cleanUpUnusedImages(server.serverId); - await cleanUpDockerBuilder(server.serverId); - await cleanUpSystemPrune(server.serverId); - await sendDockerCleanupNotifications(server.adminId); - }); - } - } else { - if (IS_CLOUD) { - await removeJob({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - const currentJob = scheduledJobs[server.serverId]; - currentJob?.cancel(); - } - } - } else if (!IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); + if (server.enableDockerCleanup) { + const server = await findServerById(input.serverId); + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } + if (IS_CLOUD) { + await schedule({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + scheduleJob(server.serverId, "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...` + ); + await cleanUpUnusedImages(server.serverId); + await cleanUpDockerBuilder(server.serverId); + await cleanUpSystemPrune(server.serverId); + await sendDockerCleanupNotifications(server.adminId); + }); + } + } else { + if (IS_CLOUD) { + await removeJob({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + const currentJob = scheduledJobs[server.serverId]; + currentJob?.cancel(); + } + } + } else if (!IS_CLOUD) { + const admin = await findAdminById(ctx.user.adminId); - if (admin.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this admin", - }); - } - const adminUpdated = await updateAdmin(ctx.user.authId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + if (admin.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this admin", + }); + } + const adminUpdated = await updateAdmin(ctx.user.authId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - if (adminUpdated?.enableDockerCleanup) { - scheduleJob("docker-cleanup", "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...`, - ); - await cleanUpUnusedImages(); - await cleanUpDockerBuilder(); - await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); - }); - } else { - const currentJob = scheduledJobs["docker-cleanup"]; - currentJob?.cancel(); - } - } + if (adminUpdated?.enableDockerCleanup) { + scheduleJob("docker-cleanup", "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...` + ); + await cleanUpUnusedImages(); + await cleanUpDockerBuilder(); + await cleanUpSystemPrune(); + await sendDockerCleanupNotifications(admin.adminId); + }); + } else { + const currentJob = scheduledJobs["docker-cleanup"]; + currentJob?.cancel(); + } + } - return true; - }), + return true; + }), - readTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readMainConfig(); - return traefikConfig; - }), + readTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readMainConfig(); + return traefikConfig; + }), - updateTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeMainConfig(input.traefikConfig); - return true; - }), + updateTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeMainConfig(input.traefikConfig); + return true; + }), - readWebServerTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("dokploy"); - return traefikConfig; - }), - updateWebServerTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("dokploy", input.traefikConfig); - return true; - }), + readWebServerTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("dokploy"); + return traefikConfig; + }), + updateWebServerTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("dokploy", input.traefikConfig); + return true; + }), - readMiddlewareTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("middlewares"); - return traefikConfig; - }), + readMiddlewareTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("middlewares"); + return traefikConfig; + }), - updateMiddlewareTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("middlewares", input.traefikConfig); - return true; - }), - getUpdateData: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return DEFAULT_UPDATE_DATA; - } + updateMiddlewareTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("middlewares", input.traefikConfig); + return true; + }), + getUpdateData: protectedProcedure.mutation(async () => { + if (IS_CLOUD) { + return DEFAULT_UPDATE_DATA; + } - return await getUpdateData(); - }), - updateServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } + return await getUpdateData(); + }), + updateServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } - await pullLatestRelease(); + await pullLatestRelease(); - // This causes restart of dokploy, thus it will not finish executing properly, so don't await it - // Status after restart is checked via frontend /api/health endpoint - void spawnAsync("docker", [ - "service", - "update", - "--force", - "--image", - getDokployImage(), - "dokploy", - ]); + // This causes restart of dokploy, thus it will not finish executing properly, so don't await it + // Status after restart is checked via frontend /api/health endpoint + void spawnAsync("docker", [ + "service", + "update", + "--force", + "--image", + getDokployImage(), + "dokploy", + ]); - return true; - }), + return true; + }), - getDokployVersion: adminProcedure.query(() => { - return packageInfo.version; - }), - getReleaseTag: adminProcedure.query(() => { - return getDokployImageTag(); - }), - readDirectories: protectedProcedure - .input(apiServerSchema) - .query(async ({ ctx, input }) => { - try { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + getDokployVersion: protectedProcedure.query(() => { + return packageInfo.version; + }), + getReleaseTag: protectedProcedure.query(() => { + return getDokployImageTag(); + }), + readDirectories: protectedProcedure + .input(apiServerSchema) + .query(async ({ ctx, input }) => { + try { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); - const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); - return result || []; - } catch (error) { - throw error; - } - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); + const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); + return result || []; + } catch (error) { + throw error; + } + }), - updateTraefikFile: protectedProcedure - .input(apiModifyTraefikConfig) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + updateTraefikFile: protectedProcedure + .input(apiModifyTraefikConfig) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - await writeTraefikConfigInPath( - input.path, - input.traefikConfig, - input?.serverId, - ); - return true; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await writeTraefikConfigInPath( + input.path, + input.traefikConfig, + input?.serverId + ); + return true; + }), - readTraefikFile: protectedProcedure - .input(apiReadTraefikConfig) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + readTraefikFile: protectedProcedure + .input(apiReadTraefikConfig) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - return readConfigInPath(input.path, input.serverId); - }), - getIp: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const admin = await findAdmin(); - return admin.serverIp; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + return readConfigInPath(input.path, input.serverId); + }), + getIp: protectedProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const admin = await findAdmin(); + return admin.serverIp; + }), - getOpenApiDocument: protectedProcedure.query( - async ({ ctx }): Promise => { - const protocol = ctx.req.headers["x-forwarded-proto"]; - const url = `${protocol}://${ctx.req.headers.host}/api`; - const openApiDocument = generateOpenApiDocument(appRouter, { - title: "tRPC OpenAPI", - version: "1.0.0", - baseUrl: url, - docsUrl: `${url}/settings.getOpenApiDocument`, - tags: [ - "admin", - "docker", - "compose", - "registry", - "cluster", - "user", - "domain", - "destination", - "backup", - "deployment", - "mounts", - "certificates", - "settings", - "security", - "redirects", - "port", - "project", - "application", - "mysql", - "postgres", - "redis", - "mongo", - "mariadb", - "sshRouter", - "gitProvider", - "bitbucket", - "github", - "gitlab", - ], - }); + getOpenApiDocument: protectedProcedure.query( + async ({ ctx }): Promise => { + const protocol = ctx.req.headers["x-forwarded-proto"]; + const url = `${protocol}://${ctx.req.headers.host}/api`; + const openApiDocument = generateOpenApiDocument(appRouter, { + title: "tRPC OpenAPI", + version: "1.0.0", + baseUrl: url, + docsUrl: `${url}/settings.getOpenApiDocument`, + tags: [ + "admin", + "docker", + "compose", + "registry", + "cluster", + "user", + "domain", + "destination", + "backup", + "deployment", + "mounts", + "certificates", + "settings", + "security", + "redirects", + "port", + "project", + "application", + "mysql", + "postgres", + "redis", + "mongo", + "mariadb", + "sshRouter", + "gitProvider", + "bitbucket", + "github", + "gitlab", + ], + }); - openApiDocument.info = { - title: "Dokploy API", - description: "Endpoints for dokploy", - // TODO: get version from package.json - version: "1.0.0", - }; + openApiDocument.info = { + title: "Dokploy API", + description: "Endpoints for dokploy", + // TODO: get version from package.json + version: "1.0.0", + }; - return openApiDocument; - }, - ), - readTraefikEnv: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = - "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; + return openApiDocument; + } + ), + readTraefikEnv: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = + "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - return result.stdout.trim(); - } - if (!IS_CLOUD) { - const result = await execAsync(command); - return result.stdout.trim(); - } - }), + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + return result.stdout.trim(); + } + if (!IS_CLOUD) { + const result = await execAsync(command); + return result.stdout.trim(); + } + }), - writeTraefikEnv: adminProcedure - .input(z.object({ env: z.string(), serverId: z.string().optional() })) - .mutation(async ({ input }) => { - const envs = prepareEnvironmentVariables(input.env); - await initializeTraefik({ - env: envs, - serverId: input.serverId, - }); + writeTraefikEnv: adminProcedure + .input(z.object({ env: z.string(), serverId: z.string().optional() })) + .mutation(async ({ input }) => { + const envs = prepareEnvironmentVariables(input.env); + await initializeTraefik({ + env: envs, + serverId: input.serverId, + }); - return true; - }), - haveTraefikDashboardPortEnabled: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + return true; + }), + haveTraefikDashboardPortEnabled: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync( - "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik", - ); - stdout = result.stdout; - } + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync( + "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik" + ); + stdout = result.stdout; + } - const parsed: any[] = JSON.parse(stdout.trim()); - for (const port of parsed) { - if (port.PublishedPort === 8080) { - return true; - } - } + const parsed: any[] = JSON.parse(stdout.trim()); + for (const port of parsed) { + if (port.PublishedPort === 8080) { + return true; + } + } - return false; - }), + return false; + }), - readStatsLogs: adminProcedure - .meta({ - openapi: { - path: "/read-stats-logs", - method: "POST", - override: true, - enabled: false, - }, - }) - .input(apiReadStatsLogs) - .query(({ input }) => { - if (IS_CLOUD) { - return { - data: [], - totalCount: 0, - }; - } - const rawConfig = readMonitoringConfig(); - const parsedConfig = parseRawConfig( - rawConfig as string, - input.page, - input.sort, - input.search, - input.status, - ); + readStatsLogs: adminProcedure + .meta({ + openapi: { + path: "/read-stats-logs", + method: "POST", + override: true, + enabled: false, + }, + }) + .input(apiReadStatsLogs) + .query(({ input }) => { + if (IS_CLOUD) { + return { + data: [], + totalCount: 0, + }; + } + const rawConfig = readMonitoringConfig(); + const parsedConfig = parseRawConfig( + rawConfig as string, + input.page, + input.sort, + input.search, + input.status + ); - return parsedConfig; - }), - readStats: adminProcedure.query(() => { - if (IS_CLOUD) { - return []; - } - const rawConfig = readMonitoringConfig(); - const processedLogs = processLogs(rawConfig as string); - return processedLogs || []; - }), - getLogRotateStatus: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - return await logRotationManager.getStatus(); - }), - toggleLogRotate: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - if (input.enable) { - await logRotationManager.activate(); - } else { - await logRotationManager.deactivate(); - } + return parsedConfig; + }), + readStats: adminProcedure.query(() => { + if (IS_CLOUD) { + return []; + } + const rawConfig = readMonitoringConfig(); + const processedLogs = processLogs(rawConfig as string); + return processedLogs || []; + }), + getLogRotateStatus: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + return await logRotationManager.getStatus(); + }), + toggleLogRotate: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + if (input.enable) { + await logRotationManager.activate(); + } else { + await logRotationManager.deactivate(); + } - return true; - }), - haveActivateRequests: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const config = readMainConfig(); + return true; + }), + haveActivateRequests: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const config = readMainConfig(); - if (!config) return false; - const parsedConfig = load(config) as { - accessLog?: { - filePath: string; - }; - }; + if (!config) return false; + const parsedConfig = load(config) as { + accessLog?: { + filePath: string; + }; + }; - return !!parsedConfig?.accessLog?.filePath; - }), - toggleRequests: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - const mainConfig = readMainConfig(); - if (!mainConfig) return false; + return !!parsedConfig?.accessLog?.filePath; + }), + toggleRequests: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + const mainConfig = readMainConfig(); + if (!mainConfig) return false; - const currentConfig = load(mainConfig) as { - accessLog?: { - filePath: string; - }; - }; + const currentConfig = load(mainConfig) as { + accessLog?: { + filePath: string; + }; + }; - if (input.enable) { - const config = { - accessLog: { - filePath: "/etc/dokploy/traefik/dynamic/access.log", - format: "json", - bufferingSize: 100, - filters: { - retryAttempts: true, - minDuration: "10ms", - }, - }, - }; - currentConfig.accessLog = config.accessLog; - } else { - currentConfig.accessLog = undefined; - } + if (input.enable) { + const config = { + accessLog: { + filePath: "/etc/dokploy/traefik/dynamic/access.log", + format: "json", + bufferingSize: 100, + filters: { + retryAttempts: true, + minDuration: "10ms", + }, + }, + }; + currentConfig.accessLog = config.accessLog; + } else { + currentConfig.accessLog = undefined; + } - writeMainConfig(dump(currentConfig)); + writeMainConfig(dump(currentConfig)); - return true; - }), - isCloud: protectedProcedure.query(async () => { - return IS_CLOUD; - }), - health: publicProcedure.query(async () => { - if (IS_CLOUD) { - try { - await db.execute(sql`SELECT 1`); - return { status: "ok" }; - } catch (error) { - console.error("Database connection error:", error); - throw error; - } - } - return { status: "not_cloud" }; - }), - setupGPU: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }), - ) - .mutation(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - throw new Error("Select a server to enable the GPU Setup"); - } + return true; + }), + isCloud: protectedProcedure.query(async () => { + return IS_CLOUD; + }), + health: publicProcedure.query(async () => { + if (IS_CLOUD) { + try { + await db.execute(sql`SELECT 1`); + return { status: "ok" }; + } catch (error) { + console.error("Database connection error:", error); + throw error; + } + } + return { status: "not_cloud" }; + }), + setupGPU: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }) + ) + .mutation(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + throw new Error("Select a server to enable the GPU Setup"); + } - try { - await setupGPUSupport(input.serverId); - return { success: true }; - } catch (error) { - console.error("GPU Setup Error:", error); - throw error; - } - }), - checkGPUStatus: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }), - ) - .query(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - return { - driverInstalled: false, - driverVersion: undefined, - gpuModel: undefined, - runtimeInstalled: false, - runtimeConfigured: false, - cudaSupport: undefined, - cudaVersion: undefined, - memoryInfo: undefined, - availableGPUs: 0, - swarmEnabled: false, - gpuResources: 0, - }; - } + try { + await setupGPUSupport(input.serverId); + return { success: true }; + } catch (error) { + console.error("GPU Setup Error:", error); + throw error; + } + }), + checkGPUStatus: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }) + ) + .query(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + return { + driverInstalled: false, + driverVersion: undefined, + gpuModel: undefined, + runtimeInstalled: false, + runtimeConfigured: false, + cudaSupport: undefined, + cudaVersion: undefined, + memoryInfo: undefined, + availableGPUs: 0, + swarmEnabled: false, + gpuResources: 0, + }; + } - try { - return await checkGPUStatus(input.serverId || ""); - } catch (error) { - throw new Error("Failed to check GPU status"); - } - }), - updateTraefikPorts: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - additionalPorts: z.array( - z.object({ - targetPort: z.number(), - publishedPort: z.number(), - publishMode: z.enum(["ingress", "host"]).default("host"), - }), - ), - }), - ) - .mutation(async ({ input }) => { - try { - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Please set a serverId to update Traefik ports", - }); - } - await initializeTraefik({ - serverId: input.serverId, - additionalPorts: input.additionalPorts, - }); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - error instanceof Error - ? error.message - : "Error updating Traefik ports", - cause: error, - }); - } - }), - getTraefikPorts: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + try { + return await checkGPUStatus(input.serverId || ""); + } catch (error) { + throw new Error("Failed to check GPU status"); + } + }), + updateTraefikPorts: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + additionalPorts: z.array( + z.object({ + targetPort: z.number(), + publishedPort: z.number(), + publishMode: z.enum(["ingress", "host"]).default("host"), + }) + ), + }) + ) + .mutation(async ({ input }) => { + try { + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Please set a serverId to update Traefik ports", + }); + } + await initializeTraefik({ + serverId: input.serverId, + additionalPorts: input.additionalPorts, + }); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Error updating Traefik ports", + cause: error, + }); + } + }), + getTraefikPorts: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - try { - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync(command); - stdout = result.stdout; - } + try { + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync(command); + stdout = result.stdout; + } - const ports: { - Protocol: string; - TargetPort: number; - PublishedPort: number; - PublishMode: string; - }[] = JSON.parse(stdout.trim()); + const ports: { + Protocol: string; + TargetPort: number; + PublishedPort: number; + PublishMode: string; + }[] = JSON.parse(stdout.trim()); - // Filter out the default ports (80, 443, and optionally 8080) - const additionalPorts = ports - .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) - .map((port) => ({ - targetPort: port.TargetPort, - publishedPort: port.PublishedPort, - publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", - })); + // Filter out the default ports (80, 443, and optionally 8080) + const additionalPorts = ports + .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) + .map((port) => ({ + targetPort: port.TargetPort, + publishedPort: port.PublishedPort, + publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", + })); - return additionalPorts; - } catch (error) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "Failed to get Traefik ports", - cause: error, - }); - } - }), + return additionalPorts; + } catch (error) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to get Traefik ports", + cause: error, + }); + } + }), }); // { // "Parallelism": 1, From 02ff507094dcb5e5d7ae819278b74fbc681a75b5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:14:30 -0600 Subject: [PATCH 09/61] refactor: update lint --- apps/dokploy/server/api/routers/settings.ts | 1450 +++++++++---------- 1 file changed, 725 insertions(+), 725 deletions(-) diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 0224e343..cb0e32d9 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -1,59 +1,59 @@ import { db } from "@/server/db"; import { - apiAssignDomain, - apiEnableDashboard, - apiModifyTraefikConfig, - apiReadStatsLogs, - apiReadTraefikConfig, - apiSaveSSHKey, - apiServerSchema, - apiTraefikConfig, - apiUpdateDockerCleanup, + apiAssignDomain, + apiEnableDashboard, + apiModifyTraefikConfig, + apiReadStatsLogs, + apiReadTraefikConfig, + apiSaveSSHKey, + apiServerSchema, + apiTraefikConfig, + apiUpdateDockerCleanup, } from "@/server/db/schema"; import { removeJob, schedule } from "@/server/utils/backup"; import { - DEFAULT_UPDATE_DATA, - IS_CLOUD, - canAccessToTraefikFiles, - cleanStoppedContainers, - cleanUpDockerBuilder, - cleanUpSystemPrune, - cleanUpUnusedImages, - cleanUpUnusedVolumes, - execAsync, - execAsyncRemote, - findAdmin, - findAdminById, - findServerById, - getDokployImage, - getDokployImageTag, - getUpdateData, - initializeTraefik, - logRotationManager, - parseRawConfig, - paths, - prepareEnvironmentVariables, - processLogs, - pullLatestRelease, - readConfig, - readConfigInPath, - readDirectory, - readMainConfig, - readMonitoringConfig, - recreateDirectory, - sendDockerCleanupNotifications, - spawnAsync, - startService, - startServiceRemote, - stopService, - stopServiceRemote, - updateAdmin, - updateLetsEncryptEmail, - updateServerById, - updateServerTraefik, - writeConfig, - writeMainConfig, - writeTraefikConfigInPath, + DEFAULT_UPDATE_DATA, + IS_CLOUD, + canAccessToTraefikFiles, + cleanStoppedContainers, + cleanUpDockerBuilder, + cleanUpSystemPrune, + cleanUpUnusedImages, + cleanUpUnusedVolumes, + execAsync, + execAsyncRemote, + findAdmin, + findAdminById, + findServerById, + getDokployImage, + getDokployImageTag, + getUpdateData, + initializeTraefik, + logRotationManager, + parseRawConfig, + paths, + prepareEnvironmentVariables, + processLogs, + pullLatestRelease, + readConfig, + readConfigInPath, + readDirectory, + readMainConfig, + readMonitoringConfig, + recreateDirectory, + sendDockerCleanupNotifications, + spawnAsync, + startService, + startServiceRemote, + stopService, + stopServiceRemote, + updateAdmin, + updateLetsEncryptEmail, + updateServerById, + updateServerTraefik, + writeConfig, + writeMainConfig, + writeTraefikConfigInPath, } from "@dokploy/server"; import { checkGPUStatus, setupGPUSupport } from "@dokploy/server"; import { generateOpenApiDocument } from "@dokploy/trpc-openapi"; @@ -65,736 +65,736 @@ import { z } from "zod"; import packageInfo from "../../../package.json"; import { appRouter } from "../root"; import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, + adminProcedure, + createTRPCRouter, + protectedProcedure, + publicProcedure, } from "../trpc"; export const settingsRouter = createTRPCRouter({ - reloadServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { stdout } = await execAsync( - "docker service inspect dokploy --format '{{.ID}}'" - ); - await execAsync(`docker service update --force ${stdout.trim()}`); - return true; - }), - reloadTraefik: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - try { - if (input?.serverId) { - await stopServiceRemote(input.serverId, "dokploy-traefik"); - await startServiceRemote(input.serverId, "dokploy-traefik"); - } else if (!IS_CLOUD) { - await stopService("dokploy-traefik"); - await startService("dokploy-traefik"); - } - } catch (err) { - console.error(err); - } + reloadServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { stdout } = await execAsync( + "docker service inspect dokploy --format '{{.ID}}'", + ); + await execAsync(`docker service update --force ${stdout.trim()}`); + return true; + }), + reloadTraefik: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + try { + if (input?.serverId) { + await stopServiceRemote(input.serverId, "dokploy-traefik"); + await startServiceRemote(input.serverId, "dokploy-traefik"); + } else if (!IS_CLOUD) { + await stopService("dokploy-traefik"); + await startService("dokploy-traefik"); + } + } catch (err) { + console.error(err); + } - return true; - }), - toggleDashboard: adminProcedure - .input(apiEnableDashboard) - .mutation(async ({ input }) => { - await initializeTraefik({ - enableDashboard: input.enableDashboard, - serverId: input.serverId, - }); - return true; - }), + return true; + }), + toggleDashboard: adminProcedure + .input(apiEnableDashboard) + .mutation(async ({ input }) => { + await initializeTraefik({ + enableDashboard: input.enableDashboard, + serverId: input.serverId, + }); + return true; + }), - cleanUnusedImages: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - return true; - }), - cleanUnusedVolumes: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedVolumes(input?.serverId); - return true; - }), - cleanStoppedContainers: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanStoppedContainers(input?.serverId); - return true; - }), - cleanDockerBuilder: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpDockerBuilder(input?.serverId); - }), - cleanDockerPrune: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpSystemPrune(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); + cleanUnusedImages: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + return true; + }), + cleanUnusedVolumes: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedVolumes(input?.serverId); + return true; + }), + cleanStoppedContainers: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanStoppedContainers(input?.serverId); + return true; + }), + cleanDockerBuilder: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpDockerBuilder(input?.serverId); + }), + cleanDockerPrune: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpSystemPrune(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); - return true; - }), - cleanAll: adminProcedure - .input(apiServerSchema) - .mutation(async ({ input }) => { - await cleanUpUnusedImages(input?.serverId); - await cleanStoppedContainers(input?.serverId); - await cleanUpDockerBuilder(input?.serverId); - await cleanUpSystemPrune(input?.serverId); + return true; + }), + cleanAll: adminProcedure + .input(apiServerSchema) + .mutation(async ({ input }) => { + await cleanUpUnusedImages(input?.serverId); + await cleanStoppedContainers(input?.serverId); + await cleanUpDockerBuilder(input?.serverId); + await cleanUpSystemPrune(input?.serverId); - return true; - }), - cleanMonitoring: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } - const { MONITORING_PATH } = paths(); - await recreateDirectory(MONITORING_PATH); - return true; - }), - saveSSHPrivateKey: adminProcedure - .input(apiSaveSSHKey) - .mutation(async ({ input, ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: input.sshPrivateKey, - }); + return true; + }), + cleanMonitoring: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } + const { MONITORING_PATH } = paths(); + await recreateDirectory(MONITORING_PATH); + return true; + }), + saveSSHPrivateKey: adminProcedure + .input(apiSaveSSHKey) + .mutation(async ({ input, ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: input.sshPrivateKey, + }); - return true; - }), - assignDomainServer: adminProcedure - .input(apiAssignDomain) - .mutation(async ({ ctx, input }) => { - if (IS_CLOUD) { - return true; - } - const admin = await updateAdmin(ctx.user.authId, { - host: input.host, - ...(input.letsEncryptEmail && { - letsEncryptEmail: input.letsEncryptEmail, - }), - certificateType: input.certificateType, - }); + return true; + }), + assignDomainServer: adminProcedure + .input(apiAssignDomain) + .mutation(async ({ ctx, input }) => { + if (IS_CLOUD) { + return true; + } + const admin = await updateAdmin(ctx.user.authId, { + host: input.host, + ...(input.letsEncryptEmail && { + letsEncryptEmail: input.letsEncryptEmail, + }), + certificateType: input.certificateType, + }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } - updateServerTraefik(admin, input.host); - if (input.letsEncryptEmail) { - updateLetsEncryptEmail(input.letsEncryptEmail); - } + updateServerTraefik(admin, input.host); + if (input.letsEncryptEmail) { + updateLetsEncryptEmail(input.letsEncryptEmail); + } - return admin; - }), - cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { - if (IS_CLOUD) { - return true; - } - await updateAdmin(ctx.user.authId, { - sshPrivateKey: null, - }); - return true; - }), - updateDockerCleanup: adminProcedure - .input(apiUpdateDockerCleanup) - .mutation(async ({ input, ctx }) => { - if (input.serverId) { - await updateServerById(input.serverId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + return admin; + }), + cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { + if (IS_CLOUD) { + return true; + } + await updateAdmin(ctx.user.authId, { + sshPrivateKey: null, + }); + return true; + }), + updateDockerCleanup: adminProcedure + .input(apiUpdateDockerCleanup) + .mutation(async ({ input, ctx }) => { + if (input.serverId) { + await updateServerById(input.serverId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - const server = await findServerById(input.serverId); + const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this server", - }); - } + if (server.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this server", + }); + } - if (server.enableDockerCleanup) { - const server = await findServerById(input.serverId); - if (server.serverStatus === "inactive") { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Server is inactive", - }); - } - if (IS_CLOUD) { - await schedule({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - scheduleJob(server.serverId, "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...` - ); - await cleanUpUnusedImages(server.serverId); - await cleanUpDockerBuilder(server.serverId); - await cleanUpSystemPrune(server.serverId); - await sendDockerCleanupNotifications(server.adminId); - }); - } - } else { - if (IS_CLOUD) { - await removeJob({ - cronSchedule: "0 0 * * *", - serverId: input.serverId, - type: "server", - }); - } else { - const currentJob = scheduledJobs[server.serverId]; - currentJob?.cancel(); - } - } - } else if (!IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); + if (server.enableDockerCleanup) { + const server = await findServerById(input.serverId); + if (server.serverStatus === "inactive") { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Server is inactive", + }); + } + if (IS_CLOUD) { + await schedule({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + scheduleJob(server.serverId, "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...`, + ); + await cleanUpUnusedImages(server.serverId); + await cleanUpDockerBuilder(server.serverId); + await cleanUpSystemPrune(server.serverId); + await sendDockerCleanupNotifications(server.adminId); + }); + } + } else { + if (IS_CLOUD) { + await removeJob({ + cronSchedule: "0 0 * * *", + serverId: input.serverId, + type: "server", + }); + } else { + const currentJob = scheduledJobs[server.serverId]; + currentJob?.cancel(); + } + } + } else if (!IS_CLOUD) { + const admin = await findAdminById(ctx.user.adminId); - if (admin.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this admin", - }); - } - const adminUpdated = await updateAdmin(ctx.user.authId, { - enableDockerCleanup: input.enableDockerCleanup, - }); + if (admin.adminId !== ctx.user.adminId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to access this admin", + }); + } + const adminUpdated = await updateAdmin(ctx.user.authId, { + enableDockerCleanup: input.enableDockerCleanup, + }); - if (adminUpdated?.enableDockerCleanup) { - scheduleJob("docker-cleanup", "0 0 * * *", async () => { - console.log( - `Docker Cleanup ${new Date().toLocaleString()}] Running...` - ); - await cleanUpUnusedImages(); - await cleanUpDockerBuilder(); - await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); - }); - } else { - const currentJob = scheduledJobs["docker-cleanup"]; - currentJob?.cancel(); - } - } + if (adminUpdated?.enableDockerCleanup) { + scheduleJob("docker-cleanup", "0 0 * * *", async () => { + console.log( + `Docker Cleanup ${new Date().toLocaleString()}] Running...`, + ); + await cleanUpUnusedImages(); + await cleanUpDockerBuilder(); + await cleanUpSystemPrune(); + await sendDockerCleanupNotifications(admin.adminId); + }); + } else { + const currentJob = scheduledJobs["docker-cleanup"]; + currentJob?.cancel(); + } + } - return true; - }), + return true; + }), - readTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readMainConfig(); - return traefikConfig; - }), + readTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readMainConfig(); + return traefikConfig; + }), - updateTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeMainConfig(input.traefikConfig); - return true; - }), + updateTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeMainConfig(input.traefikConfig); + return true; + }), - readWebServerTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("dokploy"); - return traefikConfig; - }), - updateWebServerTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("dokploy", input.traefikConfig); - return true; - }), + readWebServerTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("dokploy"); + return traefikConfig; + }), + updateWebServerTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("dokploy", input.traefikConfig); + return true; + }), - readMiddlewareTraefikConfig: adminProcedure.query(() => { - if (IS_CLOUD) { - return true; - } - const traefikConfig = readConfig("middlewares"); - return traefikConfig; - }), + readMiddlewareTraefikConfig: adminProcedure.query(() => { + if (IS_CLOUD) { + return true; + } + const traefikConfig = readConfig("middlewares"); + return traefikConfig; + }), - updateMiddlewareTraefikConfig: adminProcedure - .input(apiTraefikConfig) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - writeConfig("middlewares", input.traefikConfig); - return true; - }), - getUpdateData: protectedProcedure.mutation(async () => { - if (IS_CLOUD) { - return DEFAULT_UPDATE_DATA; - } + updateMiddlewareTraefikConfig: adminProcedure + .input(apiTraefikConfig) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + writeConfig("middlewares", input.traefikConfig); + return true; + }), + getUpdateData: protectedProcedure.mutation(async () => { + if (IS_CLOUD) { + return DEFAULT_UPDATE_DATA; + } - return await getUpdateData(); - }), - updateServer: adminProcedure.mutation(async () => { - if (IS_CLOUD) { - return true; - } + return await getUpdateData(); + }), + updateServer: adminProcedure.mutation(async () => { + if (IS_CLOUD) { + return true; + } - await pullLatestRelease(); + await pullLatestRelease(); - // This causes restart of dokploy, thus it will not finish executing properly, so don't await it - // Status after restart is checked via frontend /api/health endpoint - void spawnAsync("docker", [ - "service", - "update", - "--force", - "--image", - getDokployImage(), - "dokploy", - ]); + // This causes restart of dokploy, thus it will not finish executing properly, so don't await it + // Status after restart is checked via frontend /api/health endpoint + void spawnAsync("docker", [ + "service", + "update", + "--force", + "--image", + getDokployImage(), + "dokploy", + ]); - return true; - }), + return true; + }), - getDokployVersion: protectedProcedure.query(() => { - return packageInfo.version; - }), - getReleaseTag: protectedProcedure.query(() => { - return getDokployImageTag(); - }), - readDirectories: protectedProcedure - .input(apiServerSchema) - .query(async ({ ctx, input }) => { - try { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + getDokployVersion: protectedProcedure.query(() => { + return packageInfo.version; + }), + getReleaseTag: protectedProcedure.query(() => { + return getDokployImageTag(); + }), + readDirectories: protectedProcedure + .input(apiServerSchema) + .query(async ({ ctx, input }) => { + try { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); - const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); - return result || []; - } catch (error) { - throw error; - } - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + const { MAIN_TRAEFIK_PATH } = paths(!!input?.serverId); + const result = await readDirectory(MAIN_TRAEFIK_PATH, input?.serverId); + return result || []; + } catch (error) { + throw error; + } + }), - updateTraefikFile: protectedProcedure - .input(apiModifyTraefikConfig) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + updateTraefikFile: protectedProcedure + .input(apiModifyTraefikConfig) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - await writeTraefikConfigInPath( - input.path, - input.traefikConfig, - input?.serverId - ); - return true; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + await writeTraefikConfigInPath( + input.path, + input.traefikConfig, + input?.serverId, + ); + return true; + }), - readTraefikFile: protectedProcedure - .input(apiReadTraefikConfig) - .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + readTraefikFile: protectedProcedure + .input(apiReadTraefikConfig) + .query(async ({ input, ctx }) => { + if (ctx.user.rol === "user") { + const canAccess = await canAccessToTraefikFiles(ctx.user.authId); - if (!canAccess) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - } - return readConfigInPath(input.path, input.serverId); - }), - getIp: protectedProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const admin = await findAdmin(); - return admin.serverIp; - }), + if (!canAccess) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + } + return readConfigInPath(input.path, input.serverId); + }), + getIp: protectedProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const admin = await findAdmin(); + return admin.serverIp; + }), - getOpenApiDocument: protectedProcedure.query( - async ({ ctx }): Promise => { - const protocol = ctx.req.headers["x-forwarded-proto"]; - const url = `${protocol}://${ctx.req.headers.host}/api`; - const openApiDocument = generateOpenApiDocument(appRouter, { - title: "tRPC OpenAPI", - version: "1.0.0", - baseUrl: url, - docsUrl: `${url}/settings.getOpenApiDocument`, - tags: [ - "admin", - "docker", - "compose", - "registry", - "cluster", - "user", - "domain", - "destination", - "backup", - "deployment", - "mounts", - "certificates", - "settings", - "security", - "redirects", - "port", - "project", - "application", - "mysql", - "postgres", - "redis", - "mongo", - "mariadb", - "sshRouter", - "gitProvider", - "bitbucket", - "github", - "gitlab", - ], - }); + getOpenApiDocument: protectedProcedure.query( + async ({ ctx }): Promise => { + const protocol = ctx.req.headers["x-forwarded-proto"]; + const url = `${protocol}://${ctx.req.headers.host}/api`; + const openApiDocument = generateOpenApiDocument(appRouter, { + title: "tRPC OpenAPI", + version: "1.0.0", + baseUrl: url, + docsUrl: `${url}/settings.getOpenApiDocument`, + tags: [ + "admin", + "docker", + "compose", + "registry", + "cluster", + "user", + "domain", + "destination", + "backup", + "deployment", + "mounts", + "certificates", + "settings", + "security", + "redirects", + "port", + "project", + "application", + "mysql", + "postgres", + "redis", + "mongo", + "mariadb", + "sshRouter", + "gitProvider", + "bitbucket", + "github", + "gitlab", + ], + }); - openApiDocument.info = { - title: "Dokploy API", - description: "Endpoints for dokploy", - // TODO: get version from package.json - version: "1.0.0", - }; + openApiDocument.info = { + title: "Dokploy API", + description: "Endpoints for dokploy", + // TODO: get version from package.json + version: "1.0.0", + }; - return openApiDocument; - } - ), - readTraefikEnv: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = - "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; + return openApiDocument; + }, + ), + readTraefikEnv: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = + "docker service inspect --format='{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' dokploy-traefik"; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - return result.stdout.trim(); - } - if (!IS_CLOUD) { - const result = await execAsync(command); - return result.stdout.trim(); - } - }), + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + return result.stdout.trim(); + } + if (!IS_CLOUD) { + const result = await execAsync(command); + return result.stdout.trim(); + } + }), - writeTraefikEnv: adminProcedure - .input(z.object({ env: z.string(), serverId: z.string().optional() })) - .mutation(async ({ input }) => { - const envs = prepareEnvironmentVariables(input.env); - await initializeTraefik({ - env: envs, - serverId: input.serverId, - }); + writeTraefikEnv: adminProcedure + .input(z.object({ env: z.string(), serverId: z.string().optional() })) + .mutation(async ({ input }) => { + const envs = prepareEnvironmentVariables(input.env); + await initializeTraefik({ + env: envs, + serverId: input.serverId, + }); - return true; - }), - haveTraefikDashboardPortEnabled: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + return true; + }), + haveTraefikDashboardPortEnabled: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync( - "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik" - ); - stdout = result.stdout; - } + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync( + "docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik", + ); + stdout = result.stdout; + } - const parsed: any[] = JSON.parse(stdout.trim()); - for (const port of parsed) { - if (port.PublishedPort === 8080) { - return true; - } - } + const parsed: any[] = JSON.parse(stdout.trim()); + for (const port of parsed) { + if (port.PublishedPort === 8080) { + return true; + } + } - return false; - }), + return false; + }), - readStatsLogs: adminProcedure - .meta({ - openapi: { - path: "/read-stats-logs", - method: "POST", - override: true, - enabled: false, - }, - }) - .input(apiReadStatsLogs) - .query(({ input }) => { - if (IS_CLOUD) { - return { - data: [], - totalCount: 0, - }; - } - const rawConfig = readMonitoringConfig(); - const parsedConfig = parseRawConfig( - rawConfig as string, - input.page, - input.sort, - input.search, - input.status - ); + readStatsLogs: adminProcedure + .meta({ + openapi: { + path: "/read-stats-logs", + method: "POST", + override: true, + enabled: false, + }, + }) + .input(apiReadStatsLogs) + .query(({ input }) => { + if (IS_CLOUD) { + return { + data: [], + totalCount: 0, + }; + } + const rawConfig = readMonitoringConfig(); + const parsedConfig = parseRawConfig( + rawConfig as string, + input.page, + input.sort, + input.search, + input.status, + ); - return parsedConfig; - }), - readStats: adminProcedure.query(() => { - if (IS_CLOUD) { - return []; - } - const rawConfig = readMonitoringConfig(); - const processedLogs = processLogs(rawConfig as string); - return processedLogs || []; - }), - getLogRotateStatus: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - return await logRotationManager.getStatus(); - }), - toggleLogRotate: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - if (input.enable) { - await logRotationManager.activate(); - } else { - await logRotationManager.deactivate(); - } + return parsedConfig; + }), + readStats: adminProcedure.query(() => { + if (IS_CLOUD) { + return []; + } + const rawConfig = readMonitoringConfig(); + const processedLogs = processLogs(rawConfig as string); + return processedLogs || []; + }), + getLogRotateStatus: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + return await logRotationManager.getStatus(); + }), + toggleLogRotate: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + if (input.enable) { + await logRotationManager.activate(); + } else { + await logRotationManager.deactivate(); + } - return true; - }), - haveActivateRequests: adminProcedure.query(async () => { - if (IS_CLOUD) { - return true; - } - const config = readMainConfig(); + return true; + }), + haveActivateRequests: adminProcedure.query(async () => { + if (IS_CLOUD) { + return true; + } + const config = readMainConfig(); - if (!config) return false; - const parsedConfig = load(config) as { - accessLog?: { - filePath: string; - }; - }; + if (!config) return false; + const parsedConfig = load(config) as { + accessLog?: { + filePath: string; + }; + }; - return !!parsedConfig?.accessLog?.filePath; - }), - toggleRequests: adminProcedure - .input( - z.object({ - enable: z.boolean(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD) { - return true; - } - const mainConfig = readMainConfig(); - if (!mainConfig) return false; + return !!parsedConfig?.accessLog?.filePath; + }), + toggleRequests: adminProcedure + .input( + z.object({ + enable: z.boolean(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD) { + return true; + } + const mainConfig = readMainConfig(); + if (!mainConfig) return false; - const currentConfig = load(mainConfig) as { - accessLog?: { - filePath: string; - }; - }; + const currentConfig = load(mainConfig) as { + accessLog?: { + filePath: string; + }; + }; - if (input.enable) { - const config = { - accessLog: { - filePath: "/etc/dokploy/traefik/dynamic/access.log", - format: "json", - bufferingSize: 100, - filters: { - retryAttempts: true, - minDuration: "10ms", - }, - }, - }; - currentConfig.accessLog = config.accessLog; - } else { - currentConfig.accessLog = undefined; - } + if (input.enable) { + const config = { + accessLog: { + filePath: "/etc/dokploy/traefik/dynamic/access.log", + format: "json", + bufferingSize: 100, + filters: { + retryAttempts: true, + minDuration: "10ms", + }, + }, + }; + currentConfig.accessLog = config.accessLog; + } else { + currentConfig.accessLog = undefined; + } - writeMainConfig(dump(currentConfig)); + writeMainConfig(dump(currentConfig)); - return true; - }), - isCloud: protectedProcedure.query(async () => { - return IS_CLOUD; - }), - health: publicProcedure.query(async () => { - if (IS_CLOUD) { - try { - await db.execute(sql`SELECT 1`); - return { status: "ok" }; - } catch (error) { - console.error("Database connection error:", error); - throw error; - } - } - return { status: "not_cloud" }; - }), - setupGPU: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }) - ) - .mutation(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - throw new Error("Select a server to enable the GPU Setup"); - } + return true; + }), + isCloud: protectedProcedure.query(async () => { + return IS_CLOUD; + }), + health: publicProcedure.query(async () => { + if (IS_CLOUD) { + try { + await db.execute(sql`SELECT 1`); + return { status: "ok" }; + } catch (error) { + console.error("Database connection error:", error); + throw error; + } + } + return { status: "not_cloud" }; + }), + setupGPU: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .mutation(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + throw new Error("Select a server to enable the GPU Setup"); + } - try { - await setupGPUSupport(input.serverId); - return { success: true }; - } catch (error) { - console.error("GPU Setup Error:", error); - throw error; - } - }), - checkGPUStatus: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - }) - ) - .query(async ({ input }) => { - if (IS_CLOUD && !input.serverId) { - return { - driverInstalled: false, - driverVersion: undefined, - gpuModel: undefined, - runtimeInstalled: false, - runtimeConfigured: false, - cudaSupport: undefined, - cudaVersion: undefined, - memoryInfo: undefined, - availableGPUs: 0, - swarmEnabled: false, - gpuResources: 0, - }; - } + try { + await setupGPUSupport(input.serverId); + return { success: true }; + } catch (error) { + console.error("GPU Setup Error:", error); + throw error; + } + }), + checkGPUStatus: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + }), + ) + .query(async ({ input }) => { + if (IS_CLOUD && !input.serverId) { + return { + driverInstalled: false, + driverVersion: undefined, + gpuModel: undefined, + runtimeInstalled: false, + runtimeConfigured: false, + cudaSupport: undefined, + cudaVersion: undefined, + memoryInfo: undefined, + availableGPUs: 0, + swarmEnabled: false, + gpuResources: 0, + }; + } - try { - return await checkGPUStatus(input.serverId || ""); - } catch (error) { - throw new Error("Failed to check GPU status"); - } - }), - updateTraefikPorts: adminProcedure - .input( - z.object({ - serverId: z.string().optional(), - additionalPorts: z.array( - z.object({ - targetPort: z.number(), - publishedPort: z.number(), - publishMode: z.enum(["ingress", "host"]).default("host"), - }) - ), - }) - ) - .mutation(async ({ input }) => { - try { - if (IS_CLOUD && !input.serverId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Please set a serverId to update Traefik ports", - }); - } - await initializeTraefik({ - serverId: input.serverId, - additionalPorts: input.additionalPorts, - }); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - error instanceof Error - ? error.message - : "Error updating Traefik ports", - cause: error, - }); - } - }), - getTraefikPorts: adminProcedure - .input(apiServerSchema) - .query(async ({ input }) => { - const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; + try { + return await checkGPUStatus(input.serverId || ""); + } catch (error) { + throw new Error("Failed to check GPU status"); + } + }), + updateTraefikPorts: adminProcedure + .input( + z.object({ + serverId: z.string().optional(), + additionalPorts: z.array( + z.object({ + targetPort: z.number(), + publishedPort: z.number(), + publishMode: z.enum(["ingress", "host"]).default("host"), + }), + ), + }), + ) + .mutation(async ({ input }) => { + try { + if (IS_CLOUD && !input.serverId) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Please set a serverId to update Traefik ports", + }); + } + await initializeTraefik({ + serverId: input.serverId, + additionalPorts: input.additionalPorts, + }); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: + error instanceof Error + ? error.message + : "Error updating Traefik ports", + cause: error, + }); + } + }), + getTraefikPorts: adminProcedure + .input(apiServerSchema) + .query(async ({ input }) => { + const command = `docker service inspect --format='{{json .Endpoint.Ports}}' dokploy-traefik`; - try { - let stdout = ""; - if (input?.serverId) { - const result = await execAsyncRemote(input.serverId, command); - stdout = result.stdout; - } else if (!IS_CLOUD) { - const result = await execAsync(command); - stdout = result.stdout; - } + try { + let stdout = ""; + if (input?.serverId) { + const result = await execAsyncRemote(input.serverId, command); + stdout = result.stdout; + } else if (!IS_CLOUD) { + const result = await execAsync(command); + stdout = result.stdout; + } - const ports: { - Protocol: string; - TargetPort: number; - PublishedPort: number; - PublishMode: string; - }[] = JSON.parse(stdout.trim()); + const ports: { + Protocol: string; + TargetPort: number; + PublishedPort: number; + PublishMode: string; + }[] = JSON.parse(stdout.trim()); - // Filter out the default ports (80, 443, and optionally 8080) - const additionalPorts = ports - .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) - .map((port) => ({ - targetPort: port.TargetPort, - publishedPort: port.PublishedPort, - publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", - })); + // Filter out the default ports (80, 443, and optionally 8080) + const additionalPorts = ports + .filter((port) => ![80, 443, 8080].includes(port.PublishedPort)) + .map((port) => ({ + targetPort: port.TargetPort, + publishedPort: port.PublishedPort, + publishMode: port.PublishMode.toLowerCase() as "host" | "ingress", + })); - return additionalPorts; - } catch (error) { - throw new TRPCError({ - code: "INTERNAL_SERVER_ERROR", - message: "Failed to get Traefik ports", - cause: error, - }); - } - }), + return additionalPorts; + } catch (error) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to get Traefik ports", + cause: error, + }); + } + }), }); // { // "Parallelism": 1, From adaf12a9a4221661a2ed49b231917f2f88bd890d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:16:00 -0600 Subject: [PATCH 10/61] refactor: update --- apps/dokploy/lib/languages.ts | 2 +- apps/dokploy/public/locales/uk/settings.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts index 883570bd..de08a9fb 100644 --- a/apps/dokploy/lib/languages.ts +++ b/apps/dokploy/lib/languages.ts @@ -1,7 +1,7 @@ export const Languages = { english: { code: "en", name: "English" }, polish: { code: "pl", name: "Polski" }, - ukrainian: {code: 'uk', name: "Українська"}, + ukrainian: { code: "uk", name: "Українська" }, russian: { code: "ru", name: "Русский" }, french: { code: "fr", name: "Français" }, german: { code: "de", name: "Deutsch" }, diff --git a/apps/dokploy/public/locales/uk/settings.json b/apps/dokploy/public/locales/uk/settings.json index d917f326..766a1bff 100644 --- a/apps/dokploy/public/locales/uk/settings.json +++ b/apps/dokploy/public/locales/uk/settings.json @@ -55,4 +55,4 @@ "settings.terminal.ipAddress": "IP-адреса", "settings.terminal.port": "Порт", "settings.terminal.username": "Ім'я користувача" -} \ No newline at end of file +} From c7d5900e111dd0f52918de357675d97fdeb8b161 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:25:25 -0600 Subject: [PATCH 11/61] chore: bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 094823af..f1123794 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.5", + "version": "v0.17.6", "private": true, "license": "Apache-2.0", "type": "module", From 81248ed03f0501d8f126665341bb74f40481e8a0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 00:39:28 -0600 Subject: [PATCH 12/61] fix: add continue to process all applications --- apps/dokploy/pages/api/deploy/github.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 2ee17394..ff7a9221 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -121,7 +121,7 @@ export default async function handler( if (IS_CLOUD && app.serverId) { jobData.serverId = app.serverId; await deploy(jobData); - return true; + continue; } await myQueue.add( "deployments", @@ -156,7 +156,7 @@ export default async function handler( if (IS_CLOUD && composeApp.serverId) { jobData.serverId = composeApp.serverId; await deploy(jobData); - return true; + continue; } await myQueue.add( From f92d6693c3e388890f1a3e5dbdcb9c66986da458 Mon Sep 17 00:00:00 2001 From: Shabin k <73272797+SHABIN-K@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:22:27 +0530 Subject: [PATCH 13/61] add malayalam support --- apps/dokploy/lib/languages.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts index de08a9fb..fa527ed6 100644 --- a/apps/dokploy/lib/languages.ts +++ b/apps/dokploy/lib/languages.ts @@ -18,6 +18,7 @@ export const Languages = { norwegian: { code: "no", name: "Norsk" }, azerbaijani: { code: "az", name: "Azərbaycan" }, indonesian: { code: "id", name: "Bahasa Indonesia" }, + malayalam: { code: "ml", name: "മലയാളം" } }; export type Language = keyof typeof Languages; From 4cf2177928477783b8e2d82812c1ccaae6e88675 Mon Sep 17 00:00:00 2001 From: Shabin k <73272797+SHABIN-K@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:23:38 +0530 Subject: [PATCH 14/61] add malayalam support --- apps/dokploy/public/locales/ml/common.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/dokploy/public/locales/ml/common.json diff --git a/apps/dokploy/public/locales/ml/common.json b/apps/dokploy/public/locales/ml/common.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/apps/dokploy/public/locales/ml/common.json @@ -0,0 +1 @@ +{} From edff66900ee73f1adb61f828fc884a797f167d13 Mon Sep 17 00:00:00 2001 From: Shabin k <73272797+SHABIN-K@users.noreply.github.com> Date: Wed, 22 Jan 2025 12:26:05 +0530 Subject: [PATCH 15/61] add malayalam support --- apps/dokploy/public/locales/ml/settings.json | 58 ++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 apps/dokploy/public/locales/ml/settings.json diff --git a/apps/dokploy/public/locales/ml/settings.json b/apps/dokploy/public/locales/ml/settings.json new file mode 100644 index 00000000..cb62b6ec --- /dev/null +++ b/apps/dokploy/public/locales/ml/settings.json @@ -0,0 +1,58 @@ +{ + "settings.common.save": "സേവ് ചെയ്യുക", + "settings.common.enterTerminal": "ടർമിനലിൽ പ്രവേശിക്കുക", + "settings.server.domain.title": "സർവർ ഡോമെയ്ൻ", + "settings.server.domain.description": "നിങ്ങളുടെ സർവർ അപ്ലിക്കേഷനിൽ ഒരു ഡോമെയ്ൻ ചേർക്കുക.", + "settings.server.domain.form.domain": "ഡോമെയ്ൻ", + "settings.server.domain.form.letsEncryptEmail": "ലെറ്റ്സ് എൻക്രിപ്റ്റ് ഇമെയിൽ", + "settings.server.domain.form.certificate.label": "സർട്ടിഫിക്കറ്റ് പ്രൊവൈഡർ", + "settings.server.domain.form.certificate.placeholder": "ഒരു സർട്ടിഫിക്കറ്റ് തിരഞ്ഞെടുക്കുക", + "settings.server.domain.form.certificateOptions.none": "ഒന്നുമില്ല", + "settings.server.domain.form.certificateOptions.letsencrypt": "ലെറ്റ്സ് എൻക്രിപ്റ്റ്", + + "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": "ട്രാഫിക്", + "settings.server.webServer.traefik.modifyEnv": "ചുറ്റുപാടുകൾ മാറ്റുക", + "settings.server.webServer.traefik.managePorts": "അധിക പോർട്ട് മാപ്പിംഗ്", + "settings.server.webServer.traefik.managePortsDescription": "ട്രാഫിക്കിനായി അധിക പോർട്ടുകൾ ചേർക്കുക അല്ലെങ്കിൽ നീക്കം ചെയ്യുക", + "settings.server.webServer.traefik.targetPort": "ടാർഗറ്റ് പോർട്ട്", + "settings.server.webServer.traefik.publishedPort": "പ്രസിദ്ധീകരിച്ച പോർട്ട്", + "settings.server.webServer.traefik.addPort": "പോർട്ട് ചേർക്കുക", + "settings.server.webServer.traefik.portsUpdated": "പോർട്ടുകൾ വിജയകരമായി അപ്ഡേറ്റ് ചെയ്തു", + "settings.server.webServer.traefik.portsUpdateError": "പോർട്ടുകൾ അപ്ഡേറ്റ് ചെയ്യാൻ പരാജയപ്പെട്ടു", + "settings.server.webServer.traefik.publishMode": "പ്രസിദ്ധീകരണ മോഡ്", + "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.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.terminal.connectionSettings": "കണക്ഷൻ ക്രമീകരണങ്ങൾ", + "settings.terminal.ipAddress": "IP വിലാസം", + "settings.terminal.port": "പോർട്ട്", + "settings.terminal.username": "ഉപയോക്തൃനാമം" +} From 210fe0759c0722cd847d6e593f6009b6cf1f14d3 Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Thu, 23 Jan 2025 03:43:04 +0530 Subject: [PATCH 16/61] feat:update templates glance and homarr --- apps/dokploy/public/templates/glance.png | Bin 0 -> 7946 bytes apps/dokploy/public/templates/homarr.png | Bin 0 -> 3408 bytes apps/dokploy/public/templates/twenty.svg | 16 ++- .../templates/glance/docker-compose.yaml | 8 ++ apps/dokploy/templates/glance/index.ts | 108 ++++++++++++++++++ .../templates/homarr/docker-compose.yaml | 11 ++ apps/dokploy/templates/homarr/index.ts | 27 +++++ apps/dokploy/templates/templates.ts | 40 ++++++- 8 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 apps/dokploy/public/templates/glance.png create mode 100644 apps/dokploy/public/templates/homarr.png create mode 100644 apps/dokploy/templates/glance/docker-compose.yaml create mode 100644 apps/dokploy/templates/glance/index.ts create mode 100644 apps/dokploy/templates/homarr/docker-compose.yaml create mode 100644 apps/dokploy/templates/homarr/index.ts diff --git a/apps/dokploy/public/templates/glance.png b/apps/dokploy/public/templates/glance.png new file mode 100644 index 0000000000000000000000000000000000000000..54fc4131137d09784f3057b58cc18afdaab13ba4 GIT binary patch literal 7946 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 CNyW7Q literal 0 HcmV?d00001 diff --git a/apps/dokploy/public/templates/twenty.svg b/apps/dokploy/public/templates/twenty.svg index cf5223b9..bad18fab 100644 --- a/apps/dokploy/public/templates/twenty.svg +++ b/apps/dokploy/public/templates/twenty.svg @@ -1,6 +1,12 @@ - - - - - + + + + + + + + + + + diff --git a/apps/dokploy/templates/glance/docker-compose.yaml b/apps/dokploy/templates/glance/docker-compose.yaml new file mode 100644 index 00000000..e931d6e4 --- /dev/null +++ b/apps/dokploy/templates/glance/docker-compose.yaml @@ -0,0 +1,8 @@ +services: + glance: + image: glanceapp/glance + volumes: + - ../files/app/glance.yml:/app/glance.yml + ports: + - 8080 + restart: unless-stopped diff --git a/apps/dokploy/templates/glance/index.ts b/apps/dokploy/templates/glance/index.ts new file mode 100644 index 00000000..4b229786 --- /dev/null +++ b/apps/dokploy/templates/glance/index.ts @@ -0,0 +1,108 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8080, + serviceName: "glance", + }, + ]; + + const mounts: Template["mounts"] = [ + { + filePath: "/app/glance.yml", + content: ` +branding: + hide-footer: true + logo-text: P + +pages: + - name: Home + columns: + - size: small + widgets: + - type: calendar + + - type: releases + show-source-icon: true + repositories: + - Dokploy/dokploy + - n8n-io/n8n + - Budibase/budibase + - home-assistant/core + - tidbyt/pixlet + + - type: twitch-channels + channels: + - nmplol + - extraemily + - qtcinderella + - ludwig + - timthetatman + - mizkif + + - size: full + widgets: + - type: hacker-news + + - type: videos + style: grid-cards + channels: + - UC3GzdWYwUYI1ACxuP9Nm-eg + - UCGbg3DjQdcqWwqOLHpYHXIg + - UC24RSoLcjiNZbQcT54j5l7Q + limit: 3 + + - type: rss + limit: 10 + collapse-after: 3 + cache: 3h + feeds: + - url: https://daringfireball.net/feeds/main + title: Daring Fireball + + - size: small + widgets: + - type: weather + location: Gansevoort, New York, United States + show-area-name: false + units: imperial + hour-format: 12h + + - type: markets + markets: + - symbol: SPY + name: S&P 500 + - symbol: VOO + name: Vanguard + - symbol: BTC-USD + name: Bitcoin + - symbol: ETH-USD + name: Etherium + - symbol: NVDA + name: NVIDIA + - symbol: AAPL + name: Apple + - symbol: MSFT + name: Microsoft + - symbol: GOOGL + name: Google + - symbol: AMD + name: AMD + - symbol: TSLA + name: Tesla`, + }, + ]; + + return { + domains, + mounts, + }; +} diff --git a/apps/dokploy/templates/homarr/docker-compose.yaml b/apps/dokploy/templates/homarr/docker-compose.yaml new file mode 100644 index 00000000..876ea3f6 --- /dev/null +++ b/apps/dokploy/templates/homarr/docker-compose.yaml @@ -0,0 +1,11 @@ +services: + homarr: + image: ghcr.io/homarr-labs/homarr:latest + restart: unless-stopped + volumes: + # - /var/run/docker.sock:/var/run/docker.sock # Optional, only if you want docker integration + - ../homarr/appdata:/appdata + environment: + - SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY} + ports: + - 7575 diff --git a/apps/dokploy/templates/homarr/index.ts b/apps/dokploy/templates/homarr/index.ts new file mode 100644 index 00000000..eb5a9f82 --- /dev/null +++ b/apps/dokploy/templates/homarr/index.ts @@ -0,0 +1,27 @@ +import { + type DomainSchema, + type Schema, + type Template, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const secretKey = generatePassword(64); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 7575, + serviceName: "homarr", + }, + ]; + + const envs = [`SECRET_ENCRYPTION_KEY=${secretKey}`]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 4cd167a5..0f93e69d 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1074,7 +1074,7 @@ export const templates: TemplateData[] = [ website: "https://penpot.app/", docs: "https://docs.penpot.app/", }, - tags: ["desing", "collaboration"], + tags: ["design", "collaboration"], load: () => import("./penpot/index").then((m) => m.generate), }, { @@ -1097,7 +1097,7 @@ export const templates: TemplateData[] = [ name: "Unsend", version: "v1.2.4", description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", - logo: "unsend.png", // we defined the name and the extension of the logo + logo: "unsend.png", links: { github: "https://github.com/unsend-dev/unsend", website: "https://unsend.dev/", @@ -1276,11 +1276,11 @@ export const templates: TemplateData[] = [ version: "latest", description: "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", - logo: "couchdb.png", // we defined the name and the extension of the logo + logo: "couchdb.png", links: { - github: "lorem", - website: "lorem", - docs: "lorem", + github: "https://github.com/apache/couchdb", + website: "https://couchdb.apache.org/", + docs: "https://docs.couchdb.org/en/stable/", }, tags: ["database", "storage"], load: () => import("./couchdb/index").then((m) => m.generate), @@ -1312,4 +1312,32 @@ export const templates: TemplateData[] = [ tags: ["analytics", "bi", "dashboard", "database", "sql"], load: () => import("./superset/index").then((m) => m.generate), }, + { + id: "glance", + name: "Glance", + version: "latest", + description: "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + logo: "glance.png", + links: { + github: "https://github.com/glanceapp/glance", + docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", + }, + tags: ["dashboard", "monitoring", "widgets", "rss"], + load: () => import("./glance/index").then((m) => m.generate), + }, + { + id: "homarr", + name: "Homarr", + version: "latest", + description: "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + logo: "homarr.png", + links: { + github: "https://github.com/homarr-labs/homarr", + docs: "https://homarr.dev/docs/getting-started/installation/docker", + website: "https://homarr.dev/", + }, + tags: ["dashboard", "monitoring"], + load: () => import("./homarr/index").then((m) => m.generate), + }, + ]; From d5ff91563a42b97737003b2faaf45ca42940d53d Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Thu, 23 Jan 2025 10:08:50 +0530 Subject: [PATCH 17/61] fix: docker-compose.yaml to docker-compose.yml --- .../templates/glance/{docker-compose.yaml => docker-compose.yml} | 0 .../templates/homarr/{docker-compose.yaml => docker-compose.yml} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename apps/dokploy/templates/glance/{docker-compose.yaml => docker-compose.yml} (100%) rename apps/dokploy/templates/homarr/{docker-compose.yaml => docker-compose.yml} (100%) diff --git a/apps/dokploy/templates/glance/docker-compose.yaml b/apps/dokploy/templates/glance/docker-compose.yml similarity index 100% rename from apps/dokploy/templates/glance/docker-compose.yaml rename to apps/dokploy/templates/glance/docker-compose.yml diff --git a/apps/dokploy/templates/homarr/docker-compose.yaml b/apps/dokploy/templates/homarr/docker-compose.yml similarity index 100% rename from apps/dokploy/templates/homarr/docker-compose.yaml rename to apps/dokploy/templates/homarr/docker-compose.yml From e50bbd1a6a163eae99146508db2abe68356dfdb9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:33:05 -0600 Subject: [PATCH 18/61] fix: set right branch in preview remote deployments --- packages/server/src/services/application.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index e297124d..4c194e0f 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -577,6 +577,8 @@ export const deployRemotePreviewApplication = async ({ if (application.sourceType === "github") { command += await getGithubCloneCommand({ ...application, + appName: previewDeployment.appName, + branch: previewDeployment.branch, serverId: application.serverId, logPath: deployment.logPath, }); From fe5b8782e9dec8e34369caeafdcacf464baf3ceb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:44:43 -0600 Subject: [PATCH 19/61] chore: bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f1123794..a467d5f6 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.6", + "version": "v0.17.7", "private": true, "license": "Apache-2.0", "type": "module", From 6edd2a81e5797e2b973912c9487ae46b91e9ff33 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:45:03 -0600 Subject: [PATCH 20/61] refactor: lint --- apps/dokploy/lib/languages.ts | 2 +- apps/dokploy/templates/templates.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/lib/languages.ts b/apps/dokploy/lib/languages.ts index fa527ed6..a19c9589 100644 --- a/apps/dokploy/lib/languages.ts +++ b/apps/dokploy/lib/languages.ts @@ -18,7 +18,7 @@ export const Languages = { norwegian: { code: "no", name: "Norsk" }, azerbaijani: { code: "az", name: "Azərbaycan" }, indonesian: { code: "id", name: "Bahasa Indonesia" }, - malayalam: { code: "ml", name: "മലയാളം" } + malayalam: { code: "ml", name: "മലയാളം" }, }; export type Language = keyof typeof Languages; diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 0f93e69d..1bdb1ec1 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1316,7 +1316,8 @@ export const templates: TemplateData[] = [ id: "glance", name: "Glance", version: "latest", - description: "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + description: + "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", logo: "glance.png", links: { github: "https://github.com/glanceapp/glance", @@ -1329,7 +1330,8 @@ export const templates: TemplateData[] = [ id: "homarr", name: "Homarr", version: "latest", - description: "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + description: + "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", logo: "homarr.png", links: { github: "https://github.com/homarr-labs/homarr", @@ -1339,5 +1341,4 @@ export const templates: TemplateData[] = [ tags: ["dashboard", "monitoring"], load: () => import("./homarr/index").then((m) => m.generate), }, - ]; From 03e1c17675b50a6dd640ea38ef3cb1be50360945 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 23 Jan 2025 00:44:31 -0600 Subject: [PATCH 21/61] feat: add remote logs error when is not reachable --- .../deployments/show-deployment.tsx | 25 +- .../deployments/show-deployments.tsx | 14 +- .../show-preview-builds.tsx | 11 +- .../deployments/show-deployment-compose.tsx | 18 +- .../deployments/show-deployments-compose.tsx | 13 +- .../dashboard/project/add-template.tsx | 6 +- .../drizzle/0058_brown_sharon_carter.sql | 1 + apps/dokploy/drizzle/meta/0058_snapshot.json | 4341 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + packages/server/src/db/schema/deployment.ts | 1 + packages/server/src/services/deployment.ts | 33 + packages/server/src/services/mount.ts | 4 +- 12 files changed, 4450 insertions(+), 24 deletions(-) create mode 100644 apps/dokploy/drizzle/0058_brown_sharon_carter.sql create mode 100644 apps/dokploy/drizzle/meta/0058_snapshot.json diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index 39b09d0c..e6fdb38b 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -17,8 +17,15 @@ interface Props { open: boolean; onClose: () => void; serverId?: string; + errorMessage?: string; } -export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { +export const ShowDeployment = ({ + logPath, + open, + onClose, + serverId, + errorMessage, +}: Props) => { const [data, setData] = useState(""); const [showExtraLogs, setShowExtraLogs] = useState(false); const [filteredLogs, setFilteredLogs] = useState([]); @@ -99,6 +106,8 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { } }, [filteredLogs, autoScroll]); + const optionalErrors = parseLogs(errorMessage || ""); + return ( { )) ) : ( -
- -
+ <> + {optionalErrors.length > 0 ? ( + optionalErrors.map((log: LogLine, index: number) => ( + + )) + ) : ( +
+ +
+ )} + )}
diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx index a767350f..b5ece6e7 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployments.tsx @@ -8,7 +8,7 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { api } from "@/utils/api"; +import { api, type RouterOutputs } from "@/utils/api"; import { RocketIcon } from "lucide-react"; import React, { useEffect, useState } from "react"; import { CancelQueues } from "./cancel-queues"; @@ -18,8 +18,11 @@ import { ShowDeployment } from "./show-deployment"; interface Props { applicationId: string; } + export const ShowDeployments = ({ applicationId }: Props) => { - const [activeLog, setActiveLog] = useState(null); + const [activeLog, setActiveLog] = useState< + RouterOutputs["deployment"]["all"][number] | null + >(null); const { data } = api.application.one.useQuery({ applicationId }); const { data: deployments } = api.deployment.all.useQuery( { applicationId }, @@ -100,7 +103,7 @@ export const ShowDeployments = ({ applicationId }: Props) => { - + {project.applications.length > 0 || + project.compose.length > 0 ? ( + + + + + e.stopPropagation()} + > + {project.applications.length > 0 && ( + + + Applications + + {project.applications.map((app) => ( +
+ + + + {app.name} + + + {app.domains.map((domain) => ( + + + {domain.host} + + + + ))} + +
+ ))} +
+ )} + {project.compose.length > 0 && ( + + + Compose + + {project.compose.map((comp) => ( +
+ + + + {comp.name} + + + {comp.domains.map((domain) => ( + + + {domain.host} + + + + ))} + +
+ ))} +
+ )} +
+
+ ) : null} @@ -182,7 +261,10 @@ export const ShowProjects = () => { - + e.stopPropagation()} + > Actions diff --git a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx index f100979e..fc48134a 100644 --- a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx +++ b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx @@ -22,7 +22,7 @@ import { Textarea } from "@/components/ui/textarea"; import { sshKeyCreate, type sshKeyType } from "@/server/db/validations"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon, PlusIcon } from "lucide-react"; +import { DownloadIcon, PenBoxIcon, PlusIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -111,6 +111,26 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => { toast.error("Error generating the SSH Key"); }); + const downloadKey = ( + content: string, + defaultFilename: string, + keyType: "private" | "public", + ) => { + const keyName = form.watch("name"); + const filename = keyName + ? `${keyName}${sshKeyId ? `_${sshKeyId}` : ""}_${keyType}_${defaultFilename}` + : `${keyType}_${defaultFilename}`; + const blob = new Blob([content], { type: "text/plain" }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(url); + }; + return ( @@ -245,7 +265,41 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => { )} /> - + +
+ {form.watch("privateKey") && ( + + )} + {form.watch("publicKey") && ( + + )} +
From 130567dd78f833347d45491ebfc53ecc923d896b Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Fri, 24 Jan 2025 08:17:39 +0530 Subject: [PATCH 28/61] feat: add Spacedrive file manager template --- apps/dokploy/public/templates/spacedrive.png | Bin 0 -> 326880 bytes .../templates/spacedrive/docker-compose.yml | 9 +++++ apps/dokploy/templates/spacedrive/index.ts | 31 ++++++++++++++++++ apps/dokploy/templates/templates.ts | 19 +++++++++-- 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 apps/dokploy/public/templates/spacedrive.png create mode 100644 apps/dokploy/templates/spacedrive/docker-compose.yml create mode 100644 apps/dokploy/templates/spacedrive/index.ts diff --git a/apps/dokploy/public/templates/spacedrive.png b/apps/dokploy/public/templates/spacedrive.png new file mode 100644 index 0000000000000000000000000000000000000000..a10fffd526c657f7efa794fb53dbb1654c9d8478 GIT binary patch literal 326880 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 )L6Ejfy zRq?g^@)kW_<2gISQnRq3lS3%xDY{v|-1)K>2>D}`^BfJQ4XSSN!UBbT_MkPzMHODh zM;$J*g|qw^GjQpgC_z!WqxwF7XA;5X;U5ov8^Sk50iGiS3lbec2b=nOo~8lu?C^( zx4M9HD^_le+gT(5tOsXR9^y!*R8B{c7C4GtH9h)Et&n0-qn$o)s`_G4CCBWEF0;+r z5}2^+=cQSJt_8NJMc9cTBJ+ViEi1wXeCY%MD}-ftkoV>sbSqjg1In>EuH;WxLVo^v zb}Z3>KUu21@E6~afB7GOLw@O3pOCAc?gbZx@;VKnH=cru z)8!-yjKG2|d;?ikPs-{$)e4V7Osv~0&cfGSGz}g`bv3Bp>PjYSYCU(y z&lCbU&}#0cUmEI2jE;*ttuc;9nZ5?}My;HS@4Hr4Z_dH0RE9tcm8F?AKX}lM>;O8J zLVHGp$QXpTm7W-e0)}35+Nz0C*6vgGaX@(OZXkKi{4ag$Bs1U8VK1c_x)@xiM%TMF zD(h>K1iGZSrdl7-z|V;Tc*X;A->Vhpz*0LTH=I4(L=!loA5d7WXCi$nw_gACp~%j= zZDdHTi%#VA+`en*_Zq5yzw%c|y->7sdalSNy$7ZefN~{*UlQ5>5#;Lmh3eiDemg5m zC_#)Hyt6>Ge%S>Y6n1P>*T2!+x9#x-=#L7!zZ)`JXI9SXEO9Ud9xjz}6c%1E&W(8Q|0mA4hSirf=;Ji_}H7Ej=$Q+Z>0$fz&p{mVZ#za>q81m?p zFk96BOm#JI7xfpY>Y- zszFZy?E}p?H{o9Lpt9#N?)xdK_ryQd10+Hejz5^vGCw73+qT_4fZi1Wx~1U!HdN2oh`||wRame#ywPpIVuSyo zONqd_7c=+_ZF3(KADnv%@w9+_0kD_szFpOG!8u#dA7=c;7O^`WpA^~cAk&q|AAR_F z`Tm#Qlqdh{g-rL$zVl;~I#^JeIn&+!>bWSu-iAoauW1G-b8XQWd$xZrqgtnmx={CG z7e{Stnx5QM0bf~H-L-XB^_6249sASIttvYSlfOu}x&w2JSM3Bw4}1OoArM>$ItN=x z?^5T#escR&ty9;41RN zo(G|?e-e==MfgLJ`~Pw$+!rjgk>boKyM_bf8g)#?;MgZHUggedJm%|U32fW8Jv4y+ zD7JS)-#M@EyIq|OuG3q|29wc(JRCd>OvDVl)uY5gql8_2nFTPiK)zmNEde;49HCZ( zr;|t^~Vv&z<{NTQK=65RCqSvNyY-o6`!m z(0`zX8@r!5QUHCnnL=S_2RU`D&(GBxT2t}TT&dfLB5^a{ET~whZ7Qs-3-#*n37FYH zmI-uIRaXH2wl)8b`zVDC!WXeXe)9eGywR!E*Wt&*_Q~RCpX5;6(F5g)#*1bSK@MwX zC_bNm2l!@VE6{k^!)HvftT5rN z=WwPUwDZ9{jb4f110vI>MXtVduwLu3B*lrroDZ6<0PI`0mDjB+;iNsDPD!K4BYmT| zZ`%gYZ#y{WqrZj>6_HtR9_XZnwD+>IB$X* z2V+kY<<6Z8`N4Nymyi4(pOg3hYnL+JqsX*;kuw;r`^ecTXOT3)y9V$i!7KaW+~ny)O5=AR@2wmgL&7q7G$r91sDU5GljEiYkgfYOgnB6w!4LGtd8bBFkCSitO3?)jbn3KC9^V#$kRuv`}ag} z$9|qst5OuRMg^X9l7@?0X10%<^gp+W#ckW;1<)VnfTi5N9uyQ2yXIo|89z z^p4zldMEpPlVEoWW{QV~dM8={jtaEs-f@-bhy0**5*~bb8U1aGj~DES6ZHHuvky)1Fn3Iv#?62mv@BwL_D#dN(I=qOYL+`n9f4 z#!i7M`fj|{(awF>Ug^OKAejNoYu3WG)b7+A6(k{gJ@-8@oCVp|_p#!7B)RXAM2Gjk zX#FqsUevl>w)&u1FPQ^o9iScpM8j_zRE{5}lZNBfNXhlkSLk<54T;*x z#Av}CzwTYuC{W*b(IC}VWj8G958t8#{^(WR4JzPH4q*K>4o)^~W<}{>&qnwW55u1E zS{Y#Zd`V^2UcE5*lb)a+2HqtYbB9W9oUGqqBsl0tQtTx8nkOC6eXdcA3yB7d>eI8R z5^}Wi;r`{Lzd6vnLuC>{V@JTB_q*qKlj|Z^es-i{J?fL*aTn==62y+ZR!PGt$5NoT zXznEtk3jjhZQJ7s&>t4qy$#@;x8(Tf?;7G{nqg0PNS%Z#uo0mX% z@&g1Ghm$AGb`hebfb!ct@#>J@}+mFqvB>6(7IOS zj2E?faD&n{9(ia*AYAPXm=xh^HDXuOiml?HoYXVkwUFJUK7b0t{Kw~2tKg8 zPII%72vrwGNlk)cOohQ`5e8w+8i~%(rOM<%x=fCW;ZW>to(d)rJR@@TG06VA(x|#$ z$8@lEN+>~jkGgKDwrhd8=x^J$ZMO@cKThEMaXyDYGbI~iv;nP}*GH}q0}J`u#j<0o z*4t;;167H4Y4`2|lqJA_^(zOQTh;d&oKGSb7mx?{C;7wQ{F?mi)qC>92M((324}4T z-EdTcH1Qh>D0|fnIHRWq-fdWghT0c}Dln*kz7Rqf^i-=cOe#F)qy!o^WCy#?i{nF|dtE13akZh!MkY%6->xGm^L@LssK zjph3Q#6c}pKwP2ML&dp?AJLnDkihSCs|1FVLW%ht%KS++N@}1EB!ERU$I?Ujo!2pw zut7hux;g?M?PDK(9vr*&v;5s9kyu!fwBQJvpa-5@^bET@t(ocr<6x;jN0RAZk=T;!VV;}Ox z#ie}tGvAg^{+F-H2Y&6YJa~OyMA=H%O?jcCI0$ubS_MnB$yU@dK+!@6RQo-QGrKm9 zx!>GQVxTy06k~QF@!PXU3#+WCqWm8X(mqwMc#*xBWKndVj{fH&!b$Zbs;X~wABfP} zN6hia`Fn7J_)-<#V#kn)M3oqoYB*Qi)Xr?;hcJuJwmO zW0nEOic@DKb;lOtI1Y(PrlNrtYh3f0E0Nu^BGYFVRd*wd$R$p=A_tam$MF2jPau1a z8tB`z0mgZ6wD@hibpZXQ@#$>==Z})w?qvoMtg~!^3=G#0*S~rsdp@ePwgT|Bk5sz(vye2^UKQ%`Qdk8m*4rnJtxopYjLN~Sc<;c zyyvz8zR=y#2m!M4us*>rlbTK%4%SK}j%^5|t;H;tm{!0I6@k5^_|X@Yen5TJ?SFQ{T3CEkJ){WBPHWp9C~1cywh)pv7C;;Q?hhhqk~j6Ii{M)y{zM zWB}8%@~9U)R26=Di=E|VJ~{p8Vk@|7!8jhdRMTGM6CZtEUjObrx%2c+rU%opob2!r z*{*nn1Gp1q=mJ?O7T2bOPwMG@YttEz`lI|XYnUL}&)f>=oa>qXEHVa#nx-?6O_ zLm)Cal=iaifoN&aPC}@>-?RWR9XhB$2o&PLp6|K8PXT;-y?%WHbz_)GGjv|~8eGQ= zf;M)pX{I){9ML(jO@e5(=qy+Yd%j1iyY4S-d93o2UGL;`7vxc4xqEPTaR_jf_GC`^5 zC_?P&J=IF?2#qpGS5_V2WrDyU4ME)_7{BuqC!CX;)E9H2z}*qy>mm>SC4~E0n}vx% zWA)1qf%M>=XsX``#0nkaYgT*~u*@v8j!KX^sn z|F`bS{;h+hng-vtXL(&&Z{kcEmS!OU=?GwrY1G>Owrc;Vmi-!dFS|iV%P#lag7xTU zT6q5u2%akE`x3092{hZ&L5-*sC~wEF0X(6isfI;T?idCQ4{jxp$8kYbt*=j^l?ya` zjU84xRbz#(J*=RpA*5K1PNy{h-LVIIUhK|uaPH9c%=a*v3&t4XS#9EZppG-I>0AZ- zH997vfVF4Q7FE&L^ogI-Z{AB#WW?UA&tdRbYZ4af=Sjwi8*@t!>Cn2Db%NepcTUKr zGGcYI5MqmYu+F4!p2tODFM{_|_Ma5F z`U(Vh&6+~@9ni&{p`LUvr#P5k(%+5x(KoEVZMO%Y-y}}GEhGIFf^%#3DYMiW+N0%V zTuwqv3a74Bdv-y&H}Nvas_I2~O56u7J+hX7n7p;AS`>iuRX_!j`T#p;0go#J_FZ_P7A_hdzKG z$G&sM0W77arR!=mymeVEG^FZ7&)BIr;5k}kGoW3JMVXn&x5cvV#N8_jgrM9whQ9xF zj>lp#ok~`~k!tnlAA3<={q)b}eIL9$^qwCduC_}n0XY<%9aWWGK`pIHOFERU1ulTK zpj_kJZQN+PMp}nvGdO2a_h-zWZI!9v5VVaMh8E~Ionl*Ty+7Knfk!p>BM_fZSGB8m z1GVTPtrxOX-L--{n3ia>a7|OS(NL7tzC^Th0gU8EG-tsHRFRzKJYV__P-kXJMSt$9 zB32AIRT!nKYf}-oOPbw6b_OJlp6(i&yP6kqF34Q7R&wPt8Er#IS)-gg9?slP?|E?j z{@i=$fO%%v9;|8a=)8h}gzpEncpQ`WovG={k5j67RIf3`JYPtLGtkfp4IP!>a<~9?GFH*uZi6MQ>5;H8Jk9N<5{ngqPUOvpX*+AQ8C`OZQJbt z=qpF#bt>RKwEf(Ign}jJ1_y+)jj3DE!(jcw_-Rr)C^!P`j1vSsfjO7jMqjOd`PrZG zt184~98)?z=<3=k&+{X-{grS0Q2y{gd0BqxZ{C#$Ki`Y&>gb1!T$tY|sC{ECn>FjX zLHn7qGaQS6gk8Zk1u%=+LkLD34zek%rh48vCoH_(qN+|?DTMj1?s@P;cfoeud%iHw z;f!T8z0{`o5!+yCP7r}!(?Wety;z%m63^VXmDPcjAVkVjmun^)>dFtttG-qS2==TC zZ6BAJue;}i^h66cKsQ9>j0hdQ!wiVzxgX2gszvuJ(-S@NeAiIldyCtsK%1O#?irxm zd!(aPa*-7p3ToZ$tlw@Ty-C8_aRJSBNSaCw!aa+mK`r{W_;{BD zXq|CjVYjAXp-pXv7{E${EvaqLoRp7g-F7%{MUE${>Un#VqCz0;SS8`~O)qE7pjUqB zdDq`J)OPp9yR@&P6GHkbXXW1~6;C$rjJ9bjdn{`~x+)Q8^;%nzmJ@xYtas6GfptaW zocFZJD==O$^!KO|Jt+`|oRwrX*KkF^7|zuCtT7Txn%`iCHhqrL_b9;50#Hc+^N41h zrAM8+MrV=@ly#L`ujf7c+^*ZYHPfHZ9Jtr90*W;#17vj}q4G|jXKmxnp!99#j3iV_ zW`V7K&oXC2f!X6aH`Oa8v>u6@{K9e{X!w+?#gPU$k7DnV(#|ARyDSOdcyi_pR^}lu zMBe#5$aGJHJ#X|NDd|3|p00w9i($l?MZ1B?Oenu=2*qt;aoct~0QyZ1#M@9kzb$>| zPUcb!pqRFi?buoLsFjQ+PUjb{52yA?Pwbl};EomHt%QQ*QQlD8z4LMwek$Naop?BX zW)iu)xR5XW*~{{^-~OSz?^o~0{_UeM9BDvB!}ENGq3z9f>)NDAO1A9J=maKpHAH)y z_pH3iwX_CT`s$Y2rE*@U6_h!z4B{k z>GhmGI1fR2HSxDAR|w9<={MKO5#^&PR^s8LWV@%_y?ZG?`0nfSFaN=d^2}ekly4Apn2 zW9iQQHi5vb*NceARq@+N(b)C)F>KbT%h2=p4cQ?Uy8M;;J4^sf!im={)pe}1s#1!? z^hDdx?`N-{1v-a>l^GDTc7_NTF}7k;Kcl!DSf9hdoZbYDFvjv>pJ1=u+&(r z0>rh-l)dvFWRAuDpd&D zsl+TuXCh8%_RiLNltG07UdADOZ0P#N!dxrsgdegre>78V6F=6mK_O3z`c4QAVx%&O z?4A*M@cSbB?~35A%vASo9w<|=@vbMDb5K{qJsUMEX3#zBmVn&0ZQH{K=#N9jh!6RD zH+jBm0vl`cXTsiv z75IrFyB%bIMfsDDeocP*%G+}113Te^sg=Dw3BjpyKDEJ#q5{Cph6ID8%>Flver<14 zsXz(!I z2o}%{!zQSX+L(U#OEq~m)OQ*pLuSyddgxiwQ#H+k9XtdHBh;R^EgdRqZ73Yy6HC?J z`z%xvhmz=L?asr?_}10<5CCZE<(jqQVXu#8*l=a9h0|by1UGn3yz}QZ-}i_DpG|5+ z=bEu`bDkYD?}o7^V8A=x1M@K1LPmc|=X{u5HtEMwEaoPUUu4tqVGh;$JRQ$>Ae>YY zJ(`Y&1v|Od`ktdy>}XGP5|O(UzAp04Cyw6eJNr41>zw^O!9TYngEr`{lCzQn;2Pv_ z+qOM?fd07no0bb9?8+!Gr>dH2z9;yH(!FigR6oB*0?`5*>SHp{3@_}EuEBiO53XoSfX(|Dc;n!VOxv9au zUW9FWdkgCA+Gbswu8|WBKmsQ~@H0qWuz(qiK}NE&z4$1nDHt(%7j1iAZBr0AYe9Y&rLOL<<(cpv0PeF04Ux z87R9(d{{Zxi|ylHy$Zp#A-gDKz#$Ug_)Nht_uNLr(2RW}v91iAs|S9)zUz_10|%~i zjVN9g80$*>x&HH=b7Dp}z}San4aOjLut3-R)&lu);JswM3b!hNz|#EaB1nchLS_0e zpd{IT>G6@n`-@<1@mye<6hs+FT^$7D9{jn;gBK5iaZJu(saFW+&0(b9e=eN_&P7g5g=b@a^NNt46rB7u4<)4A@UaTo}J8yTCcYN2Ozu~U$I|BvcPgl)~2E% zTm9zj1=Jl?UklK4qU4Oo~Z29zBUc)p{t(l z4lyM_*7&v-chV#cTMK;YkKXhOdS{0u9`e-6q0Ti|TbE*-D5QB1h&!G0z5v9hi<`s{ zkSlw{Q8EG5b)l^y1m|^PA%wVrs;^)7%Im<6=qA<}vTzK~0JloTHZTg_@g?Yl@8RTT z4kbTXW`X~z7AEv#N;-p`9sG;|urZRL9bNk(sq2yNiR3Bll(eqFs%u#zx|dB_++;OB zdM3RDLRRQbI*E;JGQ?Q6nieXyNw%u}%BmW3Q8S5>7ro@ZOAB`@rVFXcTAwKGe=v~9 zX;eWMgxComOR77VOMC&HB&?A_Xu&%V_u}--o)EeJame)M0aXce&opqsHB%}DF2{MBoLB}RKJSUgh z-q7FC?pFf468h&si-OCb%3l2a^5R1N;xpfr7e4-DdEW=`$kkhWk)0?{^F_6N@psee z?Xi$l8K9u8r-~&^)qWpcD<7*u4c3b6EWFs8zGCZVF8$XQC6bw}5zEk^{h3B>w2hCn zJnk`}0;y>3oQ_ppYy2o~`wT=URMfyalnMBKWXjG^0(=??znRa)=smRhs%zE>;yD43 zzUvRVwr>%8W_iwHt47lt@qw1Y=sb@m8PoP|8gp|-P^kz2(Kvcw@i+b#h|I*XQVSGGT>r!EHK?{9$oEzMP4|Del z#{B5Ly@h67PH|<{U8tL%e@HRC$fJ@hJ4a!--~Z2^muLRU9ofIN7hFiw zd)^4Lp@gC>pUvvQL-YLG2of%pE#Ws~ARSxass`(*o*ydBcNTCSO@xFw1c2JJ9^I;u zp#@>=$N26gUll{@1k$HcKn^wK-4bPMEOClf0eT4xC@`Tx~pOBTDQ*Mwkju9%Yp0?_vndpS`J&l)5%T+4C47$|^60Ga6z&Y&iMS&Ev#=E_^g~2|f{vxL$ zVuEwelfB9a)3#I#DibttD!t$-qP7>=RPIGS`TH-*PhPtxci+F0Y0pZvy}SW14g{$c z-qA!COSRlmz_t3)mmr}|5G8qpX(?)Us}E-m|E4uDt}O zN@P0O1%7IH?X!ywWCN=%bwca*EFXC-$v8}yDG1mphfC?L1%RO=-cA?}Hdra?MW zkAK#Mg#qfzWCZXK3X|Km0rbZe3*Yz&U3X@m_L{~-##y3C(fclJ<;=4JBHCcr{7RDc z9-dOOz_$`lWGm`s0&j``X4+(<-O(y%UByG8{*Fu^u3LGB0-XiOWufj`eZTz7@!S}JW;VdSJ}ngXWxkD< zEgoml4p~{X%~f=$#e6D&>QtVu1+Y=sw9!jC5q92*#x>958|E|8V7=|HM0%Ri9^|CY z3uTk+f*)+FbW)oHU1iIO<2(`e|x$}8bNJ2GYA-33?5txgNBdYpMd~@GKAwIyPAsD(En<^g6|r z<1)+sGYbr&Qp(kQP7yyuhGg;6;o!x39ErjB@HVLwA+xDmcdRm3{aY6q#;7@C=uZ-it z5I{MTTs6s$1U75|z8NpDQ{{@{pg`%D9dNFz=K5OcBoEjF<&vn{Njtgkc93`8ejuOt z@bfasUM}v6Ob^-q2x1| zJOZn7X_yvp0?&?DwW+@DdXlrACz$Bx484SP8J_VN?e(1ZO6oVcJ~0V8N7T)k0G?}r zZKj%=rG00u$YkIgvfm$NE{yodC;ZIAzmHzJcV8hnCIS6-1_kJ}*n?>Pjd<-aTHnlf z9i6{trwMnJVHb2Vl_ga3Gz(X@aHzAI>wOKzntNA**eIT;Scbd9cklfng!e?`Le65= z(%TBKLw6htoWllg z5(T^P7e=0WZ@SfUFXPPq#M@-fgZ?;o|CZPr>sIk}^?agSUS7%zUwKVF_fNhf&wlW( zT)n;*fUP_kmHI4drFN8Ez@(w!ZB@Tj#hIOsUD||l6omJ1>?p8ib>LFNt%Ck7E)-vW z&u&Dwd=6p(O6v~Q!I9R)uf9ecEZ24-oS>@cs;`{V0Kl1+u1gfOsep9F(%N%u=Ml8_ zY#o&6(FQGwYkPlbf?PAz#PMm=Q+KDA++h5EmW>jjt=b4xyD7II9jqg$>|t~b8dVDK zJqXD4(ZJ88WXs^SjR5|&7GxRVy9SJBzO5i>Iu+RNrwl#^{i6;L$l8V|4fMnDnQ7U2(?Z--y92H1=7+*8tw$-J`2e zXsGW;m2((9hkf69z1KjFwsJ&>#ie8q%!JQmv?)&4cSXRy_O=(8} zuvq~3nnBQiu2swLwx#vJc{IittoN1$t@pmMOa{}QCleX0Zv2D(W5XJ+VP@Sb7M@$SPEsf4 z<9DBGQnS+LG*@Nkj8C>}Pl`<67J27$5L_gk09GovXNPyw4uAlk8_Mk)IN!F13eY23 zwzn2#rj&;cm>=Rx!`h)mU#*&K zRlJ!1=TZFF3c?-!yFc2h{pbfjmp}Z6FUm9j>ZMHYOoE*p>FEl|s~f>U;LVI)$cE~N zFp54duGNT0cT;WEW=nO3hIc-a6E!orpXrzB?IN z1*?~BVnMYmy~F8@XEezWH|B;@kZ~SA2WDa#{WYy1ozti8oVgV*H#rnC47S{NlYVYN z1Ap}9uTjvVPJ{&r&_g6;gBgLgwN&!8O6png=u>CHQbLE(WyJ^j=_vf8d^uqozVHQjJ%>AfE#!nSFlJui-?1ahgq z@TY54sg`9(o7~m+t@r3HEsZAmezINrW05OuyOxYcrFWbx7=g2nuuX8IxIv{bTzP*t zFZ6ADC;@*QKwAm^*;PnaAdt4bT6xwij%YDR+9RO?K>D~ekKqf}(M&xkTQE{& zCxZQAuU3ssw*n1E*_Ea+4JksDXM_L*!A*XJwuKbi?#%(_3@haAJKHF$o0SEnE#OtR zXxgZoW8a^5IvzS}Y0>OQepcda$PF%{snbu?UV9e#T)@bI^U4aKU8h?6IqU_Vd7m?) zd87T@o!)q6eb*-mE83}qPn}afZ){nwg%$T?{J`fq6mf#m!SdO72c2tVS)$JRzng0I z?77LAzlVw`=KW9hhnuD}-&?av1j)sP-mz z!$QUb<<)ghH5Hyb2*Z8v)gQ|r|Hm)M2mXz_bMHBwh>4{T97Gy*cjM$?1{?~&vzB&) z&WeO~H05th+r@`r%7U zHPY5*sBPCg8@JA!oS7TlegvKTaezX0?O5aaJ-TpZ#m3fB*B^#>+Buc)>~;1$)mQXn z_s4tU=j1hUeic%k(b6~S=zM^miacNkL9e%-HOJFv(L5`4ud2Wv$E&H!G z?0-}oF~FWv+MAM+xq46F7H(wlhCAlZ|JE0m)hVSU@#(O>68gtSo%i z>WpG~MxP*Vx5vVvwx2*YMZ!K>>fZE8P5L}U3Hz%_KKbz%<(@o{%lF{WdoEJ>lb?1{ z%9qiLyp+N_f|ce3{J=6DU_jimox<)0PJpNr^wy;rT2xjo8mg3DG_*7Fuddyq#&+Sd4U_te|7bnGb0d>uXa zSz7kmXO85v7zOv)=bK3YOvxmR7{aM*D%vaAfom!8rCpf&8FRnBD@Yq}VO6q=8PUp; z0>rU?ch5!@QiH#)<^|3Cz;cO&43244>FgX9m#0djb8679b;S|I2L{2@4xf?Vb^ zl*3kMbP5rbzxh%@GcC{pby9ltckQ390t`!VYnmu`?p(^ZU;cso>Hqhe@=Jg7u3Y{6 z*n93awAB^#mh!GYS6>*4@4*4&&>d-NF)zOh3&$bb^*W8@N3x}A(E6?AvLHGUTl&sKo1YK^nn4OqClJrLP_K;*%v zL>|0+1niR^r=`HV*G4%qpY+}vsj2K_9^7w2a@%%00s6ZYY@Z_*;{X(TU>j{s(X$~* zSI;x^7T4X5rs7=w?DU4WU_}E2%r;V{c$+5(#~>l$hzaDg+%AQ*YSqJpM0`uY$c3v1u(0`D4{FRJQx8omMN#6qEWl<0HC zDY&1cP7a-_=nsmcO@>~A@=34ICWD};%E+-E9rKt&Y#*ratxFGuTV3`<88yhKS5XOq ze7cDNOH;slkUn+(G&jI&>A@KRTSg1OXt<}5kiw8f#{jS#og1!OBrOBTkX%oC%K0OJ zd0Mfa^^Tdbpv!oNXA>O5z|9A&>G8%6IKM_PVlnxcU>jIeWAkwt73Y(+7`52-p^l!+Z-G+J4Tt*r~lj;lTz;e5rpp?*I4$azR`HQ7a!Ak zU$oEp$h%;!lUo^m+io2|=R^MPZO&3Tj9p4V=r#Oy!z6X0GCmZPCMGuh4?3!x7OmVA zhN`~D8l0Ei$cz_f>wZ|ma*V*a<%>Q|a_8=)y!`xkOIHpOv`>y0uV2NFk)u#CMs$ryws|uw8cMM@qDMpFVxvPA%v@ zdh4lJ72hTH;1WC>>3C%VYQbmAHKOoLZ_+bF%?PR95hMYkfd#Dh!3tyz@LPR;{*|wS zObxyXo4U?iyH5N%6e{dJpYJtZ#=Xj9*dS~7ylWArCV0h4K(@ADJtM+40=ORr;EX`< z+9X0I`X8}Jlfmm+b3H4PC_IN%A&wneuLnPCIb~?T*r2u_LiPR~{me?&0&P+?kr-q2 zPQYH7L-452HDdpCv^yAqMGD>y&1+P+4lB)jlMzh_4A6Lyt71=cfJZ6iRL1!T>N zyCKMBFvv|`^In$Q0_S4)`yTd7*?V5s1$Je~(M*OxQOr6O z+H()o_&qASmrAsyM&03OG{C>9tfTHzhXux|E86G=kOH{<(2Rl}woknAV>glmws4(c z%jPvN_L7URl5>cGopf4(M(1@aDrYg;NweJm86bIGYtcFOYuV3@R%Zu%<Snfq5EHDp!00Q)&HybDl%zJ5UsNsB^B?Ioj|4%z`s-SgG`-5(=>QR2h}`=Gg!e?`LgF?>xkdpt z=5m$2gXl{8=8e8>j|V`1oCfG40QU{8NK`Mc41jvo3p!E+u_m0ZqPr8snF1|s zA_yknEKNA;1rkW5j#^OIs<^_eW|!Av(e)$*^pf%kZ}swvyXAnjSbOVuEfW{9xD%kB z(4Kl(O)qKTkZ^F#cwLLV6$utAP|^MGr2p=i)Sbvtp%9#lVm63lc{mb>dzEHw1LT}@ zS}^F`wxr4d00y6SiIRI(OAl>bT2nbbmrmc?7Y335BUJb6^idD9b^`!3(_0;Zsex*C zw{p(RdoQbc$(z_A_&$4y0fxEf@EOu+Bb<|;;t8xFu3((i0sz;x&buBYfpvI%<3nCW z7p89=^E=SXpwbR8Erj${7c+4rqW$E3ng@(hnOtxymbP|v3f&aC>w`h+WB{sbTTo+u0!8TRW1FbrH zpudsGsRa)q0l0fr*OA18rShJozh|#?4G15-rcqV#(SGwx|9W=9T#>Y3_BlG|elCb$ zvdx0M1Vm@tAi~haT)+UpQzew!I1Ra}K!Su5#7?~@-E*S#fg)oG6dn5oom8czW^Vs% zbCjKBzuDz`MDG1DWO`G&RrDdi8zr;?by|xRCg>a+=^VzkZQCOR=v&`;3}(gd9j2P( ztS@5ZatAYz6gjJZF_YP5qRQR&)57&8?+VaG36-GV+yYe-DVLWQ^3wC)kuQDtd-DDd z-jS;}_9`N`sO^-c!@F1Z>v$>Ab|Qy*h%iOMT2VDPQ1z53fHh*-+F5~%6`L1{b`0Kh znquhtWclspjWYH{H@2@+D21u`B*F_ebkAzsp-vE?8MmnD?Mr1HapB1lX;YPR7Z9Un z5STP{T1goRpShKEY==0U#6a*0sPlo7xJv<;9*`ZLH=#m!8hx$Br;Dr?&R91d`R6l0 zcumzcYge|0YRH#=z*DQiKS&I`pFNzb113 zc@bPX4nRDAdgH+R<;LLf#qi3%bMQ6ZwrzVj0R3^>_z*wa>C0xvY9=EJH+fZAb4J43lH5O~XChiHgW&iF0A1y*>DZ}@m zIjV13ESB<`iKAcZY5#j^s^pw|$qS5IjnSgY4%Xv!GU68nQV9dRQd+~la_mp`{WYz~ z9A=C=#ZX+2-zdQt#--HBCb*-1hR9^UdMCLgVm|=qY!^f&gjY!siO5qT zZx>Z`&!)E*OZT1;li?1cv1EPTxxU#OeZ!X9_Q(PHRz1(1SLTJ~HChPKrS-QL2tMY9xU2?R;676@Xu+I%gYP-`g55?m*Z3V=872V!1;Q zF;Tb^;nh-QnT7EteYjd3n3d>ZasPE7Qz)XN@L8{_@DeA}p!(h$$|voSIRjrN0@mp7 zh5%$UBCYuY_BgVNQ8e_2yB2c2Im9|4W861w1k(0hx`E)?!gK@Q&Gb!YKyP;cJo6m0 zYZeDT%N57(_2aK$6J-0?dx?uIIPN^3b7KoKScj!=RmBX0p;zH_D)bEQ9aG~3OV%|r zRuJDOb|Ic>+wra)?L00_fJyjsqpr*^vnqmxhO-E1vzqku5Xz6@*o*#Nk*lvCd(khQ z*mH2MJhOGZ4P~U$I#K*@qq%R}?E~ny2?XDk^IN;uAY?pSE;?6+T-@R^=`Oy(KcO<^pKl6?HlO-I@pBwkFs}z2`a+ z(dSq7dpGxk%^a)K0ufV3lJiaq0A@Io*~)#Fk~;vnv%T|~^XZMxwQB4GYjk(KWGMOB zM21(H?mQz?G|{`36O)DxxR35%R5_pQ`{uF%oB4Kd{CCE%*Y4jFpL3hB9H)#7W590% z>Q^vmF7Pq?XKnioGxu2woSsU;T#~=_x_*TAu#&FQu&HnavzSEiUXgo$0-4@O3dUt@ z`ri5^G=pR;{FvwM-<%lPhm=MRUh{M6O;Gx%ah06in zL#t|T=jiFu+)ymUa9{*nnA!sSIBNokM6KosP2QKu1-gAu?f_?=Rq6yntXVv$;_G$o5@o;4#ZkZD{~yCN>N zU{J&rcoQpexiXn0HDlfS96Tl=V9p`dQr)ac>R4@x62U-3wI1|VcLXu={0yErRMGcu zh{$EV#?na)xVLtIAuxiW;oK4^OSTP|Z`-XGKDb&& zRH-Pi^=PgkIM4X4R)0`E(wPNWT98zMbFq}+?W&&cQ71(%E-vJy=U+ z{oDKI!J&b2#=pIjrU#Sblr(zxslUt&Elei>XC;nm$+cq4&lGNSfO9{(rP(uWk_Q{N z_)-|oKT64Ks6WZ#Lb*Q*lbyi26hVZemacov>&iI%cLv2+ge~S{rGQQqY_s>A_4{m2 zHd1?`Hwmu*dr5Ik%oM`2fh928!h z5nKzL`wq51l?HgM|BZ!JmKiTt@h*1k`{2Z9&`C`la0bt$WUteq8`K&#C&87IYK{@V zB%C^2hS6RGPm5fAP2`>DJ5>7fni}3@Yggctbm%9J*4lcw1t27S&w?dQxP! z+sVDR@5`Tm^aXiBb_Y80RD*J9V4qfQABaW^I(EI7*$O|N#)Ik4KMc<CKr9v)2F?s(w&I;}pAFThjw))Z6-LBn#?)1GxWN^nB2eqyj=(^NvW#L( zwQok0kVpWbOfogADxOGWbOFGKc-p8hcXt14EY8-P*P8V)du~0jJwoGmKpNL3B%*b7 z&`+J2-t^$!aZMWq`w5D$eNKG)TClZTs}h&&4B-Jk^J=wpB)vUnH+v@?(95wfP%{mn zok5z(tv}ZKnlEhlUYH`F<%)V99x9!mY^v5+b z#D}xLGCK>kPyvH&ZXGXl>KQk2n(XzVOH6ka4|GT1zy$7Sl*N0rdDH&H=?S&^&71yS zomiMCmv=7Y8!voEzVus%>iL7&_nd_-lbQWhRp;G88DO@OI%nYY@bUN z)ORLWWC3F)kub8awZL!$e9OSkBMA$UakjyAh~@Qw^{Dk%1YW1)b14G-RNI5|xe;Mn zI_K=!Ff#c(c?K|Vk^|w~QG@Z*8cR*zR-e`J8KQSsln7@gfz)|Fm~D^fuIC8Z3RM!I zo8aN%HpF`O)9hH%G|@H3hJ;Id8~a>|?2c9R`!9>&PW{eUEmv-$00VP|H3e$A@_cPq};MA4zpWqNo#(Kd7PG(JxQK{J>jE$b$+ zk_a7T>hKul=P7~<$o+d)^67v1b$O5Mg!iZ80(GqO(Smyvm*cFvpi^qBR;8OAYqQo{ z-S?7K-(}s?U0QDG1VeWvO7yC?AE%M((07<&DucyYYS7qv#U~Wtr6tglM{Ahf5tyH% zfyv0ARRVG_+by~i2U>4j_5wRc+=uME1Ht%Q#Y|Q&9i8M_nPhR_Ya?xC5kr`$43j}Z zsOMr32`-6GM@@lQSqX^G0g;(=S$n+EiuXCa^5^us_kj7iGSP`DbCu}k!1Lw+uGDD-I2hZXBRL z%u#krRNgswZcqVRH{<4re3djTckbu}5X^vcu6zdcGn<*7ce)l&VynkSf=Y~&0*#mZ z)Sl}!O>*bXrF{Dv-fBlZ^?^7jEA}YJD)Pp0qM@5ro4H+3A0ZK(cqbC>_ zEH>y2!}c=JfT&=kcJe}fpL3a2-eG7mESzu#S724jY5Z-eXfuq1n;3Fm_lC2iw=1WE zQ?z8dAJFLcCOSX}W)g(T8cpAF7V}J1uy&F-H2cWW2JY3nW&vI#wZ!_G3O#!!T7g@r z>U*l1*K~HR){X>V(-hjF%J?YVJPX>gz*Gi4%QHT=M!B9VkCU+yTN8V*bFOE`D?@gy z4x5DoEC!3d&TLED$aOF|-YnUcNX0#>uy@YY($cZCBw*f0<%701p>s}BODw$EL;;A_SUUv#d$gln`>XeP&z{#OpeEEKTe|=zfjc zBL2ZRJ*}v2=)!UV;N>SS^6@dUPQA|m%b1K|Uys_56{c^~@C<#O$@ACW zojS<@k-oS1G`ObsddNFtg%04R12nn`DsOJeCQ>9wZaI!Csj6~EX`hUTFtxwBg2Xf~ zn(WOzg-sdZPM6;J!9e0Mt=eYXBUsSiNNnhbKf9dhhyW6#?T^iD?oo-tLG1!k`I5u3JRfdBvYc-mRI8k-!@(wU!`-T%ZyMF#hHJ3!z>O^vLy%4V%FtAZ_mYdg=EXv3r}FeI`#h;E)zVe;?E1qSTz~p6Y955I={Fw* z%$cZqjX>SndoxPm9|ffzQ0^Tg>q8zSF>Y#zas-S!KsanmN!}+$Nge0Wo7xef2R!-@ z!g)tfBx@m^_(YsUOmOIi31OWr9FR#r0gHxJHWruALcHFTFdeAvd`sl*PaoG>IIPYH zg!j$?;_I&%VFOeQj-U<_`K8ZF?;Gaawrd0EzbNUR>+qKKdUt|gm8Z;FuzzOzAfukE zo{DJ3i`4`VZ7>mir|J7m(%YD+G$a5{Oc+^;juk6KBk2$IQzue21DVz)tzP3W!yZp;4aQyZA03SA zj|XT*aN1wgYp_lYf<^cG9na>0%@6qmRZW|_JyE&FPi?isTf^N zf{SK=M#jOL7sOfY7dlD8Qos%;iaZ_Xh*O56vYH#52aB6X)tIS8ks9|6F$5k56}5Ad zo8Y#=oZWka!F#;|R1YN{f&3jMm`rBALmeSOLF5C5HjY>45j#m~A_`t?>tgO0138 z5Jtnxh_~v`RM6h4%#)g@aQYkU;KF=SkO1z!&-xsaAh@@CES2zyOGTVVakNRHZ5Z@! zqlcV2=%VUF*Etmi0mbq7o=$@Wf+?1dFv7V-SD*AaBID#5!{&{CFwG254|O$QA14xt zTO$N_j+Gb^$Lf(sm;jXL0`Adk96gTr`7BV3`qbsjd(eqJObT~G(2jJ#c_Ohw@$Jga zwRamy66k%8`epa-Ymd@Ck}#r~9H4z}nGOEALR+LIW4dgxibReB=Dln=Z4m?0YS)Ve zKil+w}tU$1&5x8~P4AK@pO| zu5_rQ!Av?IW(tDMUKQC}&>Y*eo}HR^uAVY&=&o+R~=&FbDSs_mlyK0pS&rb z`e!f8v;XQ{xq9m)5GNsTo^G^K+nphqHPQZU68;lIh;0auQb5PsHY*U6x zBcMzhv>7AxUI;Tm&}tCS9R&y;ab5jJ7e}2c2F&EyjQ6al7?8qaFS<-%>!1wgjmO`J*)U_-Op323)rUa+9a{_=mP6YoYq+$E6GIy6&4dJU?kcg zzMb!VW+vQ~SBzG%CJ#(ISx@nr%QT>85f2m`Hc;fjv5NjF$kk6pu^RIIn zu^S>%p#_|yA+=S`x9xfX`ojdvx5QR$tQ1%6nB*w)gcMKsf?;)WI`Ldb`-#69+I4TMxpUe{^ufC5WQfdJSe@w<(B2 zHvs85DVROwlJgw)=CNJ^kkwp+D#@gqCL(aaYyjSyuSY7fYkILq*0n5%&7RvR4P4GV z?=?bd=fnlpq~b}FM6Doa`qzg-u9jcQ z?i3su<2-jXg(xC-MW*kH zy!{s_P!@ja(pUhd?rH{OxY{{GAIUb&F{ z{X_M<1kn&XazU{}Si0b8h3T3qA0=y`VDZ>I54%cV{gv_B#Rx^fNB|&!+%P{w*Z3xi zH~VEsD*8R#I|w_`C3TA)ry7Kp74xNve*ESnjpf?n@d|f5X8|NhCsBuncH#Ni+j9^^ zl(&q7TN>`}?-pF@)wsKAR>@{IRaR?|{_;K6>o0Cep_Fj7Sy?QQP? z%1m-%T5+6&=+=<>eTMeF=6VCleAAaa;+Y+5gt}`#2ku#SsD)x_XNF=Hh|KizGvODX z=&3K|KoE>4947=C!BeWIxnbs%a}V5S0Aol&?4$~cc^9xOOFQQx!Lx1r-xGk>vF@dM zC}q5XV@nyzolF#YO5~kSL-s$HZWXnM z*16kY+pm7I0>+u)O%raz_#DjVT7z(e$tfE0bM0~AsJb6-?)~D|ds&3x<}48Aig-<& zU;|1mpvW?s41(uLjKG_St-@#n;z|j=46C#~QO_ws+N4=2ZpaWkpiW#!zszwR_$hh)YugBVz`SRjCdu>Y0kV-L zBT_IQY!Azv`x<$GYx%sVW}kTWWMaSUoQH}ZAQB2x4qMaqa@rM3DW>ZbMj(ECz=Tb1 z7+*rI)zLVibGfkRe6^EGH3zO9-SQkJ5j-h!^>vYVUOE7Dj*1z}84RfCeM<-&(ramx z%@2Lst^=SyP8%Qc_ku*52h=r?CoQFDC&23^F^!5J6a9{iCOA*%rkBw+gTiB2JUIe% z`l}Y5m5wc3w%S%qiqk~dUF_tYcdq1%fBv%UL}Yhy=t*x+uCAoh%TO3gI}9E0zEin4 zG4%PX*;LP>rSqXV^WM)obznc5MJxyA`%){O>+9}Q)pg3C{VbZl zS;ttwqb&6GD-rHw@j@>v<&%-gD*fL6;$kU>pRwTmH=c!|) z5}8nNL3#T#BD}Xy-BZG+*)V_P9a8`H#Nb4VDtfslGqz~kwr$G+^vBir{JPIE@tH`4 z-JC#F-#MZTX(u~))^$hyS`0kWgDj~H_?R=U8BVh($-J`fhHf^3t zbL9f(DCvu(#7xz@9vJ%7X)7D;FZN3itJR=94=YGxp_#zKL%j;P9iZS=V0vY=k!o&s zec_q=Ey2qOz^&}>g70Oj*1g}4P}eg|fs{h)AYf;3PgOFf>q(xlP@@B?LrVK7SnMg^ zGpO*A4yRBd%%-@5C(_&N7kuwkpZ@M)ceg7@b?AWtO5nB_f&N9)m|i< zYebHEdT!z%f8 z%Excp2GAd?jgM}35ZegFQUt7=)3A#=2>AzEB7(GV!Mo8Jxk!d#zohuYr! z*Hb_Bk_pFqH%*kUe&Jj4hTNAs@7u|Ae=;uC_CL4nNbIF3r*kT8iPBMC1<}bEV@@2P z@fG*ew>Fg>-SdgSE{$Y>>1$rmp3&S%2B5yAwEHozZ#dJ?+F^4ZbPv0bhsybMu!i9d zi&)&TIzqvL=5B#&908>Q_w{?zsnM616g#lve+BC90kzT85 zGcWe4K$#?v7r=5fqZ7UQYsAn-Jk~{Wy_Q~;dW3R+&S%$v^--Xm1@fcxeJ1XybZFND z*+P2jWJYnlb8i`dd=Efc&w`R@FY7#GGPb~C^iB@guIgBYo%>X+S!~nGO~SS-24|o5 z5+5#NxuB2u(YrqS_>oaU*JzC^(YX8CGI36wajpXbU3#dJ7L$nF5#d`R_r7E#$D9-4 za1h^duMCa{Boh|Jxt)aKHmdu!J$!)vFstV`No`MNq#^z!Z@#mRNDGw@F_{#78Hq(0 zgY}dhT7vq=pqJF8bb89rl;K`bx3~0|y}r1(ke~hd4f))MzA4ZCrK7bP;^>iK)XK%8 z#e)ZAwV1X5+6TG*1MH{r*+i6Z_n1wN+wTBPV9fO1GS|T%7NGlgZAnHKz%A8p+c#aS zfb%eWK9D7&exkZ7FZ71ePSlocwEMc-^QZb8j6@xp;H;vavkf+BMJ+~vYf{&p=WVlK zV0t2ZAjQlVl!-YO&w}>6NqxRiGO+xf_CoIo!;SV-cN^;y2xcvi&H&@5eMh3bm*`z`s0vA@W2NEc0tF-_;my8^g0+?BadIskr31UU6`rwGhX5LdN zD$%BU3i3VY33YimF^(skOP!z$>>@KUkvl!9`;5-2jl{AJGf`gr2v$jalSz6dvil{G zcRnq0^4u$+o#?Dv*G_BwsiJX?ey!_5^LSfr}!2$Z?GFToJIENkBl`p6} z*{nH7Uc+ULDBg;%{vO#Knc^WvOLy09N&H~7S4}s+V~E)8u#*?R`ii{v_1ERef8|o9 zccz(Ni-KTIWwcfHfJ3bM@$xx1z_A`Tw&HuKZ`wX43XGh2EeI!4>b(0xZ?#;Vx4-j- zDm)z+^RxZ_xt4a$K%~w~STK?P(8PACyG$znZQ9?I$t9(Tl6ZsZynP5G^vS`3_k4he$qh2{O!enBv z!Kq=_Q~K}ihn-j=N|%v9jFZQJBJE9keq z_ngK- zdvD*DFMQ$~@|0Z4{@x_A^Bdb2f^#S8WDrqM!B6;rRDT}^*q{k8PmNJ<^GRk?w@7Ct zf(xReKK&8*C+{3iYUwTI{lr0t6%;McbnT!%pv~Xx>9j)gsOVk^xM88AFE(sO5)5sy zb*d`Z3=&k|heacmwimteRu2FmEkF`Y0qk^q1QkJD-6q+0aZt${qP{OyUpYNMqJEbX zz3lvb0ALJRoMnZ}GRL08S1p&swT^hDk9l~7z0 z&j-gSE9vBU=euniK)-!}n~!kcY;AbZbCNMB<^(uT#T=RHc|y>Foy=>kpu)yZ z$*jb$sXnQm^Pb9SIq^-Lg~WpT$9riOG((>!- zCK`J&l{3a5CG!$42!=|;hE5d4exTFro%RE!9-`6ugIoeqTdl=fIQ5)?_li7tUgVu` ziQvwmg^by`GMTRl2R;1vS3hr9^h2m5odM3bYWlX_3_#}%fBxL>o@>!!fbDr=hht;Z z%hWmm-kYtMm~gzHya{NHpV!=Tvv<8%J?T;0jR{W2*Fq}@mlW0A%lHD2{T1b_pZ}&z z!UH844E$uo@Coget$J!l^4=VH)F zTfoH?QDr^!TsQmA#RTh8sIwEtD*Bc7`aS}8NUdr+DIi%SB1r$SS`~P^_Y%!_w4;sg zdG~xua;k^^49Ylr$z+ifmEF>kp;j}Go>^xN4s8fC!gjpA@W_3C2QA@TU>@n{ z3}`WE0+W}(bfy|XzHks_ zt=h>($M8T>kjeLOj^mM-w@wKQ-NU|B(6=oF=(l}<@D}XfpsZQY0h!ETa>DSyc_hfh zUhq)_Lm_A%>FcQ|>*!8)9$nU4U@YZ$Pt{$l>N(}k<)!@Shd+}q{`M>K)L*(3UeyQl z2+yY~YbgR3$)9P)S_&wnz z7}r&DbTj0rl-R{glJo#PqXYs)Fp0>;p%1+Q$7_0BU?EMDwnVY`=kc~R;(eZePl(@yklg9 zdGtds^h3&x<_xo`_u9ZAme^^O`V0FR2%dWewoBs`<9%0s6xL%?~>X zz?(a@R+!6}XP5`+VH-L-F+#5gYi;J7F^H~zF7AR?A-DQV**W}VtQ{6ucaAyJ)c|?< zh40AszV)Wu{iO?;uBO6-+HS1FNhr=!qU$tXS>$idp6D$MY}sP$+p0AH^zb`KKPzhq zzMvV=P>S;`gy=GmJ@c-?1?HJOaPYrYLcymo!mQ4oVzgPY9S}ayzURkB2$;R-r?EXb ziKg-$nNDdKr$1X7uwGPsEP*RiksZyuMg{cge8&VKC{tWP^~RZJVC|Fk0A`$Ks`ddl z>E#5%YrMEc#;c;fWa-MJ?~m32uLWM)WL9 zM!=j05dc^d{EI=oXXjP!y=O?q-Q~Pr%qO@jGJRj<-is>uW`012oteq$g%UGD;01mP zl+Udc*ueU>-4sB-g+Tkp_U~m7lM?}lXAKo}dzI&mK#H}Fv9mC66sWVU;5j$Jt|B@M zu>{|vxN7aowNZAvoxJttJMzU(d_$g;oq!Zhw!aCDND0OCj4*0DexJ3loHbwf#t?!P z5kVYp+5OoGZKl|=9$YqUXvET4hc74Ra2EBQtE=EJ5()ZxlxKNoSX-2LU3std{nbwP zi=w+s$JgNaUut@L3F<4Pzg!OJ#Kn-R?|-hsl#UXXg_)($E@IHS*#=`&NjGt<*PJ;0$1ngGsy zb!q*2ee5vJ+PJIG+98KuFW3E?5o^vrDR3aSMq@2!8_6IOCmYs9$jm{Gk~u89`O|0L zD{}91BGa1(T(~RI)`h&=DlqSX-`bR%_dq1L$jeyB`L~e%Rj|*fkS@-D%Rn z;5kyH8?a^ryl4Vrkge|>&!m9*bfh^vjcLH0R-I@NP}tntl?e*Q|o^V zqG|rUyc@Qkd1ftlYvxsN{!&F>GIzX^h33vBm=yzDF9LF9j>{4Eo1FIsc7=Hf(xHN0 zHdbg0h=={RvI1TOzDOkfP^Gx6SIw(_voi-&$(cD=_5*aX?UVOFMx>dMqJL*VPY>8< zK>9hlEF;!sy$K{G`7gQ3c9s<|qLgp^9E!9J+)Gz?W<_7QnNSKC$NCt|0(|eDlslj7 z^_-5=0!I@!z2U|Hp+H{01TgPdTDeJo>BJdgT$d6(V-+8uTS;cH7Fw$yv;FK%RUVDM zJDS9)oebf~d(f!dN3r7y&tNV##&RXHdq(8`S47@<<*4Q^WAD;x*q~TjVPe<$qxM24 zH(|JCep?>Mm0VmxrYlyE0bK~|J80X_zI~cykFbU&2%36hGqLDIN*!7& z5P&FcRaK96UjYNK#|>v_s?Y4ddO>93s|A#}m0|CqwI9h~Di6#}JXF!I3P3&;VsfW5 zEGz7%yK@@vs(*V1@F+k%&@f&oc9Kjd`I_oAZI4_xan~qDvvDh^RB8zmz}PwkXu-2M zUhU>HL6a_{5tPGux4h)uwXR873U}6aEdsP7-rw2ZO+kVolcUV4z>DN7=_^$6-74}V zajQaos%}OYXYT+&8qqS|nE$4ph@<^#)puQ0R0t!uT@%3ewAF#WBzP-RX!{_9Acpg|w}Y z{aawJZA=_r+o7g%0uDofQd8SEkiKo#572LO^?U_5PXh(r7b)$(bufuu0Jc(OBy;VI zt+|^Z+^kwL0OdUzyu^!?sk|Qa?nQm(7C7fbxwyEHpZ)X=`QpdEEzkbtyYk?z>7W8C zwH7#?YBxf|*;;aoPMLykDo1xcvy)`O_&9(Ku`^{Th#mX+Aac$udJ03S{IW<{K`6#hwa{lfzZaOtRAds+>Y z{MZ_5tLr3MM)F9j!WIU}g@J=aKJ$@okJZi`iO<>n8faqLQD_W_Z2J|}YZW7`uwtHnEH6{rGv zAnqBr0626zV`XnMWp96W!M?iJDBtHmns$sC;v zjlT12Alg(5c~EcP?RN6bm%b~nz3{p`@$98cS9K&?VJXuEz89Lz_Z{@Ro+ShgxXLKW|U{ZYF2Xf<1d z-l$5Q0qfV*W6oSgu-j2KS{%^t+axYMow-b*FWj~=U7zK3gV|9(JX zJsb&IBG7ZL={W_Qs^<{^PquW}-@!q9#|RQnS|%JxkR%g1P!s7Ki9lunUK-$B|Kr3a z!xJLYcSP>JqyRc&u+2b6iCK`Y;#5<8z9Y9|RnE66`nLH1{icBG+Rr}>5bY-`!$r_m zy!KZv7gTdyJQQNHj?Ef7LMoXUFPhj=3t?oH=J=d9LAKQsC#|nDDT3Wj-nn-rfAOhr z%6sKPrU#Q?SMTUVy+UlL)n9du$D!-TXj97Og4qS(-Y3F<65YOc>t`CKf!6OJ9U6FW zk;yynevi#}>^=5X-r=6JB@$;<(dWPJj{tgKRFJ7E>l(C(_|?2ZCA+BvPqszRoHnY! zDQ7x;MU90tfLl)vma4n{eVu@yNx84zf0}))AvkCf4$J6c(7FKiu4IDWjIKsFP!l4c z!yxw@;5uSYmPxR3@cgXLPZA5pjd+oJ0Pu|a8Yx|e_a$q|=CMSgb(Q`2WL1pBgy;B* zeTGQ{mGB=SP|Tk47APBe7VKEI(SH`ztV3ILw=_7YfU9MBr|sLExQ?AfMHJWR7z++C z_P}OireQFsN_WlWnph+?ta!se2OQT91#q|yxO|48-Vnv?ch{BXFyrh z{pj1aZPy3TZzI@V@%hw3MjHsZaf8!SUQU)S-3nI5F*tV%ye1|gw<^?XpSe`{X3gb_ zXW&sqX)hqwBy3g{XYcb#?p$8V4}b7edH$b%Pu}}iFNF`{Xt{)8)=w)xItD zb{3>~9v@uQD>oI#o2=;R_@7By>HVr-940`ZbLn+pT7&DVO4B6Bu&Bfv!D3t0ZX-Lh zQsH>>b}NrGz`vO}EXEgM+yrR?XA}FKOLgz9ADL?T8e1xne82f9P#v_y>L!@7YwjfL zz0#QjTiIS%S4ljgb7UD~(Pj4fx;+K?OpAp_iFc^f^eo$Y=V#fAG1)60oqoq^Z3vi4 z@ZL|7YJ+mPYC29bxQr zkG$)F-U~dFD#0n?q*x5yucAxb>u!og1-EJ8XML(I=&5WsvmXjD}Dud((ymk#i@S{WLC!2JK|K=v*(Lm|%o`NV>6 zo%K0*$Jt!Bf1V1=@1+L1rPUfSSFX$cTb}6ZgyCb(_C40Y9omFiDN4v9dMIhMmf#!V z2P>8jI*mT@IuJ))byvc04Xx9#P1FIOHa#pU_h5Z)Ru3q=L}!$Ym(taG_v-zxB9SI#1H6H#wX)EAqkfP z!*Xq&E71>GLvjm?ByC8y44#uQAL3GkKNq?8ytw7}j6j^em}uP_iRUdPcS|%5r=hZe z^KH8pfId7cKP<$310M}U-XcYI6U4)|yz(Z@v|{FHA}(eBdUS6n zr7v1{5Qp2zn{VEeuYT$kd5>I}D>|L*1X#<2!G+G8_v4R~T1kb@3gq2;xCfQll9kela+9#^ExedBa~NqZc99t?6@bP^48NcI`ZU=-1_NF>Y{agxOs{YqMH zV--lkOk&m5Wz>LJT3}YuE4_Q^^|qH_-bhT6fs%R`WWi3Tu^wj@cxNV|$~M637L_fv z`|`h29Kd=>$L{=^=`qh#rK1=&+si)Xz-|xlWfKUiea;8e<~h>0?DZ=%M}~0Ervrpc z?{LPVj;UNwXW&~Eb_Vy}RH)AgB3hL7l@;@H?KhJMUc#PzjAuUUQL)|=D5s%XW3Yyx zgvWyJgCpryqH1f}lrJpCT)UEZm>d7RPvqX0MD{-y zkqg5UU4l3|NNx_bh(N|#Nef`E6Ehp#ecR3h=#OcZhm!OZH?9>>FOeALRVwue4@mdG zIqXTLYX#H;`@IT#Hd)X)u2o5MCoQMv&I3=UpQm0BR1WNm3;FJAKa#J1?1%Ecf9q1F zci4$$dfAHtkUMJ%IuD6l0KbOH2=1*7{&kAhHb9d(l@J-K!8r~ZSWopAns8lvVC$-Q z1pHfKx)yY!1H5w>K^eZ8EESlzixs!R`rZ+&Ulo7c*H!fD%L=s5w+7NygiwR^dR)N_ zJeq#)Q$@eygG+C+sK7qL_#7u0EE32_R*g$y`@ zm@rC|ahlN#9ax}U4r?mjEArquk^8S5D(b>~jC?MX zVIuJe1eFAD-r2ToTMf`xkK!BtZcovBbia&)#;$Dxl2vzW@)_toAEugH+ca=rt#WN3 zD#YmiWv+o8Eg}ZM9wO63dFi=VJU`1-C3&+Ihsd50cdy6eI zQafsmtXV7_`Ey@JtJ+WTMdav!`F2=BG=+U^A!Pu(J?L-0c&EmOvZ;Zg*SeJ+V4Z21 zj@7bL72Oa5YWm+MRRx0mo@jvRGBBEe8LnL!%RYDNxViQx_pZT=SuFMRGB^>%f@V$zj>x_yeBG`RUgfIa`%9*N1Vx2ml-4tdUKPd zdfw|{mrj68@u|`)k|AE@mr;;!oV!`g9$ESm_m>?IOh*x$2Eca#xY#XqU50{@)ELT} za1q5!y^o!RsOf<}X8K6_Yu;oRFia17pt?{Lz1%K)u-c)`QqzL8etO-x{iD9_;(S^9 zbAiXQ!zpgP5Rv;L_r7)j>`~B>F3e@4C3d{hrIQ4r4dgelzHRFO`eQV3uEj^ZZLe)6 zM+TF*wGaTEV-8k7*hwge1Qf23@0p*c8L)=*)CBd1)}qh08W{Q3@@TF0)*EljSO4r4 zdFJwtOz%u$mNgUz=SUjX7%=RnTAc-lrrW4^O6w5cKQt3*XY|_VeX0YYmLeZf|4J`*T3}o~aaw{_f{EBdCj9^)LSH;+jc zr1uoavw!OW=%n~j2-dy*APMq&wB|KQfq_JWZ&8)se^j~Z*=&tiewx4cS`K;@_QXNf z*q`-C4=%HWK2Uc7_!|4Wfuuw#2@`yQo$(sy-sm2`EQFvp9=c=7KMMw8!7Obk+0Rdy zF{}kJ11qyWt4Qh01R~3#G5B5t&x+jpoXFMd2g|i+{!ZBxuY7l0K|k7wY46_~&3)U} z0rZE7l2=yG)AX;={aQjE$^Dh!iwQ-@Fen}=F;d1AD|`EEPTCej5l;**F=<-CbgHLF z?f>H9Lca6r59Q@Q|B1Zs-?}6Fx2LKnfo84r*z;uoSy+N`ve0;Kc+GCC>ffBD4vhdr z&!B}GTDRN)G#o3adOO^~>8iX}0JmJlwU`|1pD7O2-SR|d5Cox}{8G}KPo)m=YN1Hj&&{guz zpu(g8Txglb-2NFbBhr>;!E;Gf%oE8I(;_TtyI`UModla}+W6OkfAbHM#69uOzF^&x zssE0oDD`6ZxYuoIXvga$F7hWCo>zajF@D^l65MWTw=23Z2FhtW#7rSQIO+jcVm{Sgc@UdK5(FI-&H zj4?Nv_+CcTBqLxl(gPU*c!>gTX-e7jD5AzWF%Voz_b{?wxt}J=b6@<{vXrp};`wN) zjp$%5%k}qduwU0~OQAPZ@LUUDS@dn6OwM4$p<0^Z+;(l}rmxr&wnOs$r_$1g3&Fi1 z4b`R2M0$kcndY^3jOM+!Y~1u+Esp-6enbaf^jhV8k|X`nusEZ>pHq-<>mh~ zd5-{&6p0P1QeRfrYyUNa{_f!^FKKal&h!(sOeQ3IOq^ceD^Yc%*I%9$KN|(;e=#Lt3^M*b< zx}kXA;d>iqkvj&jg=b78)`XI#x=X&Fva)faKM6J7rA`unUdCDys?i0#{o?XMe*UvJ z<+)G1CQtvRL-iaxQ3j-C)!RBTt76WxCT;7}46@{zaYM!(>R_8*nkqg3!?+GZFWiZ| zj^-L@#2!$M)(nXEejbW0_-{YE5bXOoApJQ87>@pv-XAOJ7b3DpeKQ~~P1vNRBWU;N z{LTzwt8q*dxL8W=7Ok}j6lo+kSkv2SL{nx`&l(kF-ePhRc5J)0*b)UQg88IYN7fHZYs9Wj2UA=hjcj-NC1pTfp1|uJnW09t$9< zv`5wRlfBw&-~NGo_bac<6Yswe?%N0G8#8g}rote9(~-VsKkVqn|YhR_pJj!ADwG^ivDgq6zI>Urc~8? zzE#V&Z5*KA)78-@P_bbqqlajrhCwM}`clI`q45^$pUg$;7`cbK^;Lmy7c(QMvSsOwML+AKt zE!I34IAmWK4&}4DKi2>l!E>Nd5%+EfQ{vJ3w-#u(tLQK#_sll%{SjdAL{Rvm^F)7` z43F7qshBSSzD*R+t%w)k-uV0J946zPZYtpx9o|Sx+9FVDj9Gr~PS3f1_o-THOg9M2 z*}(whqV^Gv7<|?)q|C~s}d!D%z?kDjo z)80)g`_|jkgg<5w_;;KB;?$kjY}DD|Ere!pSm&uJkcn{C;2fP?1Lzif!346DR&Cn? z)--(OX~@-C0(FtunICJ@d~ImU;ke^vDxSF6tFB%M9;@h_ihlFSsP0Z}K*FMZL%jFQ zn4`>KS^?Dg`O+4Iqo`i-Rdvz_2&A$m2l&74WPQVb6#IA+TWRNI#I7W z6M~i*yR_Ccynf%6()^&SY%fZox4lw)iXzmo3Jy|aLC5|`2)=^MC$gHr&slOWGq zM>80EC=oNTuaNx^Bvn$^(bIP*|B0YlKH&XcJ~xzM9Q_{QuI_-xzF{zTTXNxy;tTa# z$9ue<>+P6@y0_nYaE``9CLS%XA3^K~Z+)i7?!A=zUp>;?FUJKt(_@zXpvipyoUu|? z&OJ5vjpn`q^v80$DPcHoJt!w=SeRQ)F+9mU(@VT2<8(V=DsZjj>5Rzck$&BC!eu?HrarOI%tIxd}2wasEW(tpH8=eNhe6Qs`F)^ zyelFX;a(=*Zm&A((dw;vkHK7>6<_O7;)BF2MgevIqFIl3f{KPG_vqoH!D| zJhJ^u=f4cwO34WL$imz@Ei-H*9IL8LXHL{1iq>YKQB;DWl3Jk+ql!6}Yd)<#cv9r* z+mw5+$U$#t6hI6(9x*lObN%FHrM&#_CJ?u6eSrQj1>0_mIGmBq(`uXL1m-EcXZwb) z<<@2Bj`^f!=NOnRnZy79P3BkpY;pLVB!y23aTx4&a&jldR$y>;mhYHPD<4%KD1rh?`hAF>frPI7uWCvPl-&?-047VxAFLqmy`$ z#5iQ~Rp^Q3Il#P(?-=Sl9FyE^5Tx%mK_AG~6o7Uv>Bg9>9aY!>X}IUo+@Hz0W_S|; z+IO(A)?_S}=>F}LNM_B+BWH`Xd(VsTN*V#yj6E;Y7Q@#LH5ytwuvN~ttq0J5!M9$k zpcQ<@-jZB;UpG`L3s-ZJK)SYH!onNPog~CPaox1{YqITH-dqwTa-TMH6aqvkBZ|qH-c}x}i^^qk2$rAr3`? z!=}gg$3H7rEFt|p`o1ls7Tp({>h1&l`8-d)=+k1gR?;v}LU7H2CIzzf?~21uI%uul z8`$)66roNDJg2e>ZUj)2P;`)GrKZQoVT?8C2s(s^k|ZP7+V+eUlnJLj72GmolUlMmF)btY3K?j;nQ1Px1SjmRq1dx|fn7WS%J z;&>fD?2#4*6KF+#?-uL8>LaJ*#Oi|Q!xhW`voaq5RXB+xPYll=uy#?QoF|YB`p{b! zdtj8}iuRiCJEDcP^)g{6ejqyuPe%!!j?wJAO(J+&xN}pDyoj$0UA9dQ>T8F)EbOsT)o2Y`hb{5GFDDzi#hV;@`kKo;ED&B0L3>|d<1^~YP zHJsQU{pvrCgZb)EMVCoLPVA0?-hQINH^OX?*g~zWu$YwtF0}b7iN=;DD@rlg!e^1T z1?%zVf_hI;zId;v3ZzqHVhRgdnjtnl)d~26DmbGlApnK!fGhWJlk`R9Ml92=tE!#> z`4*iyd+r*;YGi98ElN^8TA#eN${oZ&xBw!Ab87k*6#}j3?qO<@$;fi&d3o`AfM#q_StTtWn z8My7V=j;~>%F{)eY$(_O@P-`^$#1+@WdE|rJFm)IQ6G(yuWN%*-N`BJFF<{(qHmiI z&~FvmzJZr(1?WX)(f3vWtUUjLrms}>JTZCNK`KIZ_B3sfox^0R7f#SPO_Y~k_>Syl zFS`pI6g|a2`kk^AX^F-e9^+OiAl$QFEG>aFFAMAIzqfR=7T&l1=1HIRFqu%N3F!VE zGf1zK6Lj~TbEpcG)WNV8PZON8_`O#P|Npc1CtH$bNs=JOENW&x#gLhmr~@Q`!Uk*n zKGx_(FB*Lg7UA3@bK^mAIwZuE@2_l9f+9uiHKUbo2lf9bTt{?aNm9R zF%dHpxhcK)KA=`p(bWZ^Lak5`vvl`&f>8$(T3eO6zAoa;&6T z>+ky|t zlyi_<^#H%H&cYmaekMTQ*ZtF*Sj|QE7v|sY4s-Fx|9W`kdWV=!IYhnZTS?6o)$@l<1%mQ8y54 zw)ug*wbUMF_VBQ?AHIKU|M`FZC;Qc_*Io7;Q}NXzW1^1K7O6F00p(R;vP(AG!|N)Q zN+)QT%>h5M@BOblepCorz{5GZXv9OCpRFQ#`tgln`yAY_0NG`M-fcFSav1V6D z(>ms<5mtYQzD%OvPBV3V=G{r3rzO^F3g|QYD3g5Lq8os?U?&FY?BDH*NV5XWal`La zFYgV6L@FSr6!fo+!V_C5Y^glRfIk8-y~Ph*taG?E;eod?S?i|?nhIYXs(xq@!QgMWE$LC37>0dom_U=I%u_VsW7 zVE_0(eq&$!-(FjLhe;{46{0JgD}iU|)k9rX?&<^rj-^~q0IY>n%t0mFyL~u`7f>L0 za!vQj^VY?gjr(;$G0b7ddQo*zhLf=gunwWIv0elRR5z0 zIB?^75e*VD;GVoU^s185t%}uK|8Qr0tjN^LkdPC{fBeIL{TF+0d)qzqW$Fl^9t6Og z>2wsXi7rL+(#3MC6`ZRHaoH2@8ZL1V$_v|^h7w4(HQpQy)Q?$ek0&1uY>*HO%xX>b zudnAeA2a5^`>gk=)ZM$@P{$dVqbqqI1muKpfF!z<2l_nwA;>zR{JvQ7DzPV8Dhhh6 zVaYfW>ojI{MnDCMYj4=50HPir^qB(%ke-2A#NZ?ZqE{++bB09k83Xk}d=aYV&AL zs=1jxn!WuyBgPYvW7F`wKw-urEBr_i^X_3NKS23CuR^Q{vo;;2YUL zierFyt(_Kc7l0)3Sr2+dO|i?8?%t5{V-P=d|+yCXC?Mr*G_8vzsOr@>LBVNFS z5D9YC*Z{`$89-KK?UQjIw^9pMXt;c0`LYihW^B4CE}iAGMI56YJPh1=13A*(EaGhx z?AWgV4Ac0mARJ`6qf^iaX@WSu*N&5HfABOvsk>k5vb$3E`M!j&KzQ;lHv()mvBn4c z5;)>Q7#LBQwe@|oslT6Oll#CE(4;2=_XupMW}pcPKQ6aDQ1{sLT-lt7<8L!D$SQVhw1{KkschZw z@K&0i7rw@%768ynyxa?niuhYDV@G!s|E1ZF|C`zVhl85?{46MIVlYl(%MCr1Ql8_> zpe2YUtrlSa^7=#p`cGB49nZK+Xf`g}pW8;vgPfGi7LPOwUM+PG9_C0;KQg~__Rv!# zM*%?bK>f5G*sF&J`~BD7+CTiies5p=Utd|0BKHvA3-^W>ry2tI-*=pQ;153nwy~Vgm+BW>!RBGfNp zbz5~jl;s&BF*ri1GGWTFWddXyEc1kQyE7P(!E09T-nO8B@LRXU&pm#G>3s;%xh)F#q2*ctyoWyatz%OZB%F0L z`g=%voM)FhDi1%mEv%JVKwRP-#cV2*Ze)%0=^h*6IZt>xsu-_>L|EX?V4`KHa_gcH zH;W(p7iN$D&FuZxX8vlUzj;+CuZkI%%Zk?`rx~Qbygqe+{(%Aa2jAv~axX9U9Icof zSDtIuE2H|?OP-F17h%2zpE8I}7u!g69R-3R5`)V;)%!r(&FnY-{kQh*_iydhXS*Zt z+#)5RY*ZjvCK{T8B3pRMg% zKzb6ao&j~UJo{JxZWVBkV#c;qDvpBSS)`MaEb|J`Qf2bM-VEZot}?HY_!c9RknN)i z#*^4XUa}T|Y;43 z@)eK+A2uKQ^7<(P^beIi$1{Kb4j|tx?;Va}9pBPi9*Dh#{XK};&yu=(v``A<&P3!+ z3uBuCXl%7xE0liD+4G(6?D4VL-~a8u*sE^vv3QtcfT7$Ey`oBE6b?rZ`2M7%Wtop~ zwFebZCpRRl#%RHn%w(!qTj-vL0Mh)H(;{(HbhqYX7TZWB&7In>ErGm`qC4kI`2vQ(J#Uc*2NaZa^M-13Ok@Sk zT$1C7z#Ge?Gf=&d0pQ;5K)C#^03HdEC<0&!=CdAdrR1KXmEe0tQ8<~s-Qe%7{IauC zkYq@-HbqL}5imy%a3y)rR+VZXs2oYvtn1fD4WO!6*wpuZZN}WTku|AOdNRj=qi7oO z++G}G#5H#DZzO~RCIrdW9w%UxRTF{{?XxxBT-n&h<8VR08_B9mhawq|fxm@0ZXwr# zv`Kk2MyxnI6(i2ziwu(Hu!9g zO5S?W_8V@VsLvb+gvuFH%ae%_96>0Wh2 zfr|v`iq=>mTSXUZ>*pYq0SL!5$i@cy94ZAonspZtein<{+qr-4d?~t;fb;il%Aykh z>umysZic2+*Ugx9+jMV2XR<*^Icjf)OaHkAfMxkFRw@ZZzY!pTSaw3qctt ztq3x)UJ60I5Kvc`s>(wS(`}#hGM9n*hEi^oDK3l8F`yxUV5aV!+Duh^x)XuKs!-o1 zA9P!VXcwE;Dq!yjh6pTg1uTmlo8@3h4in;Rd3c?RYWa)hD}oA}us_~uaHs_5G*^bj z*35^8ub*3_WWt~)>WixU^myFEXQI5HO($dYd{}G8Gp%0l*R(Iq-u)M|$3Jvs?%+Bt zH@!Tc$4q^`QnLPHwf6El0Q8@D_8iZ3JQLD(2|T#^_-2-`D$>2dx4aTKg}VxS)`h?q z2%BppNVTBMQ(bFtR#~B!&6!I+D{4V{(|q97D7DS+YWlO21Lao<|G7us|NOiptI?+s~Yc z7A3Li_7n&@0*ZhB$KTqcJ=w#Xqt`SA$)YNt z@nFs_Kuz?rn*g-{Iv{;v>e)4STdVPal}sMno~GDiI`-Z_ByyclL|MN+wFtE$Q1x+( z6K(U_>T|*(&~#EQpfIxsGd!BT{d=6`NJQ!o*WAX9GlBO*1WZ%mc=A*sZ z+aKTAfBj#7V_(>VVGkh?C!$lI{qj~WJzC5?ooqfWzOKLP(`X}xz}^P|$qaJic4rqM z!04{1N~lJofeDa-Ky@ET2>DHCL7^8Z7EHjIXW}U#bj+-;Y6QZvPx!dAV{B$-@4K?~ zGthQzhsZ3?*nN2tcXQo~O#@N#9!;DPVYpKnIu%!f4MKCOJQZ}1E^87~c4zCO?DZsT zQV7zU-kvHsRzi6EKD-j7S3!4$(XyZyJ0M{~V2&Ud&-QCMNkE4|mB??24JxJf;5rDE zgaA+!WrHT0qUY$DL=gS(Wdf(dkz_)AMbSAqglwH7WD+(W`|M#Z$JoXs4ka1p&7PIq zojw<$TW7p)d^zt~B-Kq@Y-(b|sh&bi_`4=aoUGju;emimV-ZiFTu0tS`j0Z395~lsG2Qjq`*rC{RNo?4s;G`J&J7v3m=XkNTkKJA(%Yp-p2mwaAA$d;C&Mw26k2S)WV;i4(tyRx@KjCLlS=KtEfGr}D zD+ALc&P3(A!F9nRxf%%EMfcj#5B>drF?;__r{-S7Do7?&x$2}EF?zh8mkjy~K)*Z4 zepWDl4_mdV4@dQMEhWsJ5M|rhSLnm1@4GIl#3aE73Vrok7tfu$ z{oB8MZGW)0_Ug+AYfs0eeTt}DWjTS-mztc6Q?Q@QjcuNoqDLW~HVD4wi5B_@o?T|Z zU3JZeBQ@SN3`Cf&3(SG^e?#}unu&6E@!gs5 z=*JYQpBf?yPn5%%xS0t+c9q3ebfK=WP1{n+PJuZAUnMlBzULd1+AGhh2=r$FCU`!F z>g73ptO?7BjC>@Vb`8pa5|IPRZWjn5h~F{rkK~>hX;%UMMEgwGA^>sShk5Ul?buu~ zHg1qz6K88{TK(dk9!r#P;R7@DrbpEY$$cZ|I2&iNcFkm@6C$JHbaeh_J1DugZ_M8P z69y%BPh#Q_gx{D&m!fh>N&HI&{RN=^c>%X=VBYRESv>)}u7j$O#dG~IMDd1A7Z6c8 zXdd763^elvyi=m8UJoRFO_?^kO7JZL zfRzlcO~4v~9zy(tvaw`6At}NPDLLnpj*RpuAFkuE^=VM^9D#g9q%2??9)MD4a!9Rf zYDyIds`-A%?0=EP`Lop;sh)~_;;DFC|Ap6PZ~xn|Lai8UM@g}$imFr@OOkaSC1P*^ z>(7FLUS1yoK)-e}{5dDlRia$e{oN@a&-Ycidu8#a&?N@8~LhnXop)YIV__Q3JtgJc*iKE3t zEc7C_{qv(7|Cxm@4H*eVqNVZk=|VC~Waguer$d?h+rKq?`XT8pHfy zABV`!4M=*&P1p=sY`-Q+jKHFeWzreYiAv~)^JCAQW@*VTl5+Rd7hC|PG@Dy_4rD+L zfm--PLP@DhV%J55)R;(|ZLbQes<+RAc2I!Xf@_B2*hf}csGrJAGNdAuq5{GT#7Ezs z4R(){3yJI;cZD}An%_}$hjrAxUt0pneXy9CFDNM@}{@t9Czp^Fu= z+X1>?FCt|_l1fTBlS-~GT_Mv}f_VkZtkH0(mp|@fTQmhKNhdGr>-ner@_r)Lm z1)$#z?A{feKiBS|?GH8$%{|G3Y7bo?S`;oQMa#7i&?8V@h6rx6&r5Ry=5F?{|NJ}q z!QR{M)rn;ucjO|R@RS-|w4zO?ZpW@#rzK}d;IL0VY)pi0h0Q)~$`eAjhoO}oYz^}v zO5%MOV?Q6K^9SrQH+`Waq6cSI{lT`qnyM)}WZkZx0eI{Yum3ARyR?#`oUQHOrQhN#}uzD#QFF7|7{IbmS1(bPs4pdz^0KUw& zj)5c)!w0CqX&x8EAf2s$y;YPF=}f)CgXaKU+OpnZ1x8p;eXCoVsh(rW)v~Grwmx86 z_KQ|@sBzoUT41KD3sAJH>-BPy(rtiS9YXYeBT&=6v1>qn9|-0^l-0L@6iR&Fdte7L zZ=I5u9pxNl%b^I!HTykpfO=xHeUV4cxmrq9BPo&#vF?((U%V$tO*m;7A4BDp?g{{V zEZ(=wcZ}fH?H~@bDDMfrTZ=N+`yCXHQSf{`=qk zo4r0}8r|c}`TB5Q3zW0q12X?PUn~AFaH6^j8bvz^D_jSw;aupm*>nH){F%#23I+F9 z8=1KZ07+|NY>LLh9Q}UOwSIxeEm(UHR9A3cbDxXWyR-IbT-MzBu$TR@%)Qy67u|_^ z`w7g?)8q76U*0Z6;z}ZGthYz0w=diB)fTABDxFo_S>UEXdMD<{%j#2n%1HxofJ#BS zi#5;!5lm=T3kuSMbySuVH<6K5fwBm4BXB4M*%a80Z*6LSwzOKXl4`{rWEE{nk=%lc zvunV3rfQy*i5HI7O2BaXZL_#?E&%iluvcB#V03_7LPUre*PY-tjnf>^8$CCG*rFv53klr`I4-b+aE<`i~VD7&4U|(JzA3%TR-gEr9Cm69s3&BLh zHlt3iUCXkSX=)aDA?X3f2s$hf9Z`L(n>Z8*=2(sA@!t)whlid0@TVW`AOE-C+2>!q zIySH|2*n468*fc1GOt|-x4Z|ul$VAt6T>OUw+>uqPlB0F@a}jODdAIG2P)#SjzSOC zS3&_LI>Q1&0#dbIUu}?M9C0UWcxcm>$62z1bzhLI4Zs`=v}CuJUos*OX7=dk^7eCC zUeouo45q>RvWGE2T;4eYeqa==TQfBf^(QOMCGFT`ueBR`G03RYHL)w3fquGfi#3aS z!wYet;oQ_Z7nG%EO2V6fpCuW1ON8DcuucR2q_dzc7@z2wwk^m2u5FCY$+RE|lw1aqByt;l)wyD#lV?L;@#=&unTe;} z7qIt#2YdX}p${ExdwlJcyhXIVjd8v^zc%yp=f!^QQwHcirPl}OJ+}gjbrZm@)Po^d z98m0ouO5byloy$yNUePdLM>sf#K zDBZ(z@#{SIZokoYnPA*%#Wo(BpG(;Xk+}WQbqz+d;{(w2ITG_FIP34n76r3&8KiUZ zr;)hvLzz7heOr!!^o6qTjiN{x0y`7jo)(!)tkR74`YqcOYVWj95W$)mWzr+SR;q?F zneL>d1p#K+<1Xv}HC6tff!j&=4XE|lhGc|o^X>*#UuOh%QJb^n(bEUIWg@JID*$o1 zA6w^ZBTpvTPC(ApqZ0Pw_>``1LF+;)rWd0Epx|eJTGP+xGb*`TB=RUI>U^prEHQ_0 zkf7&+(L%JTvU_MkG`wFJ>Cnls&oZ@4tKd8bfpcr)IT|<~*m*0>%-@(j{(*`Kjg+NBcFHt=VQWKtp!ixUd?8uPx@_ zd@4a-19=~;foPvQ14edf$zZ;@b4H3~vzfYd1!z`nn%H@RC=g2zXj_R12|83pOL2`& z@P{n;HsK{7RA*&s;8w6!V}LNV@hUuqWB{K)V815b60h?jIj=IlNGc^})$XbgnE==llZcJgLt?&S%~US z52!M06-y#73%}TJu6D7hp-(0`poY;Su0!WR z;(g9Oo0pF$&*7Bz0U-+py66cnxJ`qo8R#S7 z=%lB4poX2=rQQ0xpT>1j^Ugs1Oz0$FVg{N^+eV>GyyT_^4L+Gab;)O(>|+K!K+*LS z!Q4iysUWiwNv7OLsXT}Xo^KJ~3rK=+Gg&4*a5Pv=IKAsv@0>1%CUNJWFd)V}E9-{_ z(aV~&ggDO0KSrwhI1Ubv=T+ny5=J8~d)9Rh2py>poxpf)_Vypm@ObG%j}sz6T;}>%|Pp8GwIzeQE&xPDnZvaoC1lpBboI)lb@J@A|c1x^z<~eLsN3BI76!0jj(a zIYV1HEmaoB0#?Ovp8U_>zI|u^{9nJe&+XyxKm-Gbd2|=ampKKIYG%W z9L*24g*!8QT1Dbcpk20~umEXlu#d8_hu(MRR#POpm@Rrv0DQ4e1GzhcEPYfWNeKd# zDL1Zs?P42?XkI~GUJ{xXsJj~>G)eEb5?y22!dWm*K7-X-E7ZFq-|G;-BL?LVq)Gh1 zBjB#^yrinz*0qzQL5qHF0&&#?DnXIzjke;otb$YZGt#=TwXfeSIqs@EO(m{ODmU0M zhuL!2Sk_%Q#1mNM_r_@b8fgeb+DA(^dALvgV!=sBSVZ{8x2sZ;o2PBX@O?wv;ywZO7jXUw1N0lgZu)$?H|V}a zXswI;Qvh4pC9}dX{cqJvw$&Q#mLUW6@F2&xMfC<6H~__9k(~2xx3h2m@F)A{|KShz z#eaHb?Xh)Dut8JW;z!MN%>(d`8O~)hJzhaG7XVM*!N49ffAclhYiAhV|8L$)0_5p+ zLUM2Tk|{^v4ZkYQcjJysFPA?NQCut<4utQILCL;rWo&-_ zd;sufQmJ?KUY|1Ay~f}1lh%Xz4tSd=gP-m3PRF-^WCS3+2eJKckK@(^*fRpO83<$m zSAlfPvfALP#*_#ORP`B1w*p9K!(eSw(y-5TuLR6I8}8 zCCv*8@DU*($@4&bc5{*gl8FrYjF5INc%x56iC0%mM#IkR4o&Fs6RR%d$pk;@Rdg@0$d<`+fNsspy5@kB>-~IbH_N~3OS6}R` zJs~Nz1i1!8LmNtKJvJWm@TtqOty7Q}Wubi~k&1LJXi>Z#BTx-)3&6fssrRJRe(kEq zKq9D`i2iSml!yeztlHOh@fRkpOPkA694VyRV-!;21%RufS}!RU6*( zU>!Vtuhsq2N09Eizxx?r!{A zsq@N&-(-J0E2&kA?wB&m>e*Q-c@Hw<*t(w-^_#9P$oaie+PadF-jvKJfZvpR&XoPl zXv+$e*Ln|Dg{r#*5gQ)&%9lLsC$~;~PLM58wSih2Tw~^y44CG<1YWbe7RX(dM&-qi7gNW5MJ&dQDO-p z`o%nqscjcHu;W5^ai6<#wvfaD!ljmvRWhM5m%A(FdgJGj3CU&0I1?B1b`aTqim}d? zLRCslNq~w9d~|+U-*rYdn6{8ZvpmIJs9KYS6ELYiFetE_)@WcJ>|m7}m3bPRm{JiF@RezO+?LuLdmEmA&RTg8KzXNQ9Xpul zK-{Ma(67O}f3C@L4-awAzwerKtnbU?Kp+aNBLstDIjeeeb@R4Rh52q|OTq^*FXY{B zXYb!V+Hd~;Yx~k34%u^8kaL7m7|fcqSXri`UOh>KAUK|M*WFGorQ@nnC@#SKAipAh z-WKiinzTQb(*3&Ga8|(1uC!8t*Lz!G`|R~Se9L_hsY5U(rz(LJpdI&cvYKL9bhkA- z+X47X;OD%eXSQpb3AH)H(GzJ+KLgNL;}{c=b8T~Gu~1pB?yLG|D{CiA1A0aPC@ZQE zq^o{&alDHa9Y`Pv87r69V@XG5zUTpfSN-a=uRY0xV-jtvl%LZ~s|j!tr2HiD1MLMb zwFMxw5Jccj1^lW_*l+4d3 z*o>>dPuJYd2SxZO1z-^Y*4fi}g*TeVx7oH*4yxmUrE+52q+gy7y=7-x*9CViVaqFh zd=*2pv8qF=@<(yylbQd*?Cn3m+S?=QxiT3^!MhmO^t}QM>A9F*IPOKs{Zj+zxPG$U z^#>nv(jo4%s*+%vZ|+w3a#G$7Bc-YdAV#p-E8Co{B7ZdCW?A$s6EGdzZnv}VzyHDh z@qhl>zWSTjwtu&`c!U@W!W3?*O_Kqj!`7KF`)T_$pM1mZ;Dzs8e^d2G0@vGbriIfY z5ME7YDV=^A@djg<0rwG(GlGR@@gc|_-jm-Qqbp^ELtxQo%+I~(Caeg}#%%j+;WzZG zW6iwVWVJH~{ie&nJBxCWIgjnnK)~u6AYPi= z9_tIpJXKSu66vh1Dax)E`@ggDYzm?a8F}^HSwo6ubj5c|<===^l;s$Rb^!)}H(83A zMx_VV!z^PiO|$GYR2Yh8=;hZs4oV2oAQk zjL(4hSyUC>Tei|4OVgQh{ebnb>^d!(@6UU#_;B&OoPjrdeBP3t^3dkeOscUjhrT!4 zeP#Ck@5~;*J1DtV-GQrF^s3{nB37#)y+5XxQuj{1VXY$*-#(k&8qtn+WFLV9fM1qpld5&f!G%D&zMNPs&pdgs}A zP>{d&_$y^&jO5$i4yEq9oNsaMWq7+`p87;0o|UsJJ>;hS#;FLR7z-z$m$i1N zEI3o_cJ@fc*i&Aqwn!ZeEU+#VPxh!kzGTLN)wv;KkJqZI)APtl$qoT)kh*^Bd~28g zF`+a$|AsjWDQvmcop;YA@OyBzc+?a6wEqT9frmOHK%75OzVOY=-k3f8&g|X4<6^Z| z#5JgZ_v{Crb0&0(WeyiFuTKD=ZvX^W&5?U%&+j!!F+OPA;t~}14|MEBAsZAI3?x+F z(zCn~?1vQyRk>Kj;!c16?swnVTYI$KE5C?^L=jOq@Rixzcg>1<0hGWl^&l-A;eDsS z7QOS0KW9K1VGBX^9H5>=}4DA^?e0K8i#M2iyJUPG+MMNYAD1z`(vA72Po@y0^ra zd@6$dJ=p{nM+2J5$SD?V|V(xnqMo=(Wkg1ug45D#gpYa|(^_dQc z2ZY2xteo!EX1pxCLY77LePk^UucfV%M_LLR=-T1SGXzvGW-&$*?Ko*Si<{wXAQTr| zaNin#c_G|Heoof)o%@OJc>(F47(l;Ki0p2!4=4;b1p8$1SN(zgm;9D<@H+Tby^qy> z`}|bffH$( zDXf;$o(;*W6G$7y%_G!AY+9eUTJhW|B!(4?>=vS^R#}F%8{OAEfL#5ajgQRHA|?1t zP;UJHPOnX`AF$fC9l`k1kxFgd#MqP4-;)&@^Eq!_a^$!TCrY>=Z9g6;g^yqpYcDYY zjndi-QBO0@J??5d^V$Lupen1h;l6{;=+nvo1(1UEoR+%>6$K2?G7wnFBqLa0V2OZf z5M)+LZTl>sv=S>Q!Xsk>qbz6y%md|i)cD+*c-*!7Fi!G>=L|VK>-;Vx4ID&)U9^{M zu&bBELZv&xEg(an$qh;rW!E$dgF27N#m*EX@TTXntXWt{<;&X$NfcEjT53_DS`vvY zZ$xIX&w~5^B=FgKO_k^#ibBD?nSW*WQ=yB)W+T*oW-TQwPb!zs#sLuRvV!N@+eb;WdI{dS@R`c9k6ZSH3h;22%%XqQ;&+z;5z=qx z26ZmSO2^j-gCH!0n-zvh5OnDsh$+hrYRNMa1m5ZQj@#iq>n8Q+;dUrKnltK6nh6&-km*5WFqP<^iJJK>>3FYU|J- zv{Dv5A3qR}RrZ)xU?_Yx7lAT;e&cri*b1OzbA`DMR0Yyb$L+4kD0Pj)T*gB7_$qAH z-Qz8?9bCi-eVva&>mU-7qh#P>MF>vN`ALJumy6kB~tVBq{iWX~Z2f#|^pk!TFnHB8SQN(so!Cuc{cTu%ogOarRGCWa@rvQKv3YicbGzow}>~Je@J5fwz?WCu3TVIOu{a$|80Q7>eEDH*+@e_j!s6_FlOLB51-X5W7M zC;Q_MZ|&jrjP?S8Kk?aJeVEKYfAardW}u{v5yX9MlM_Mj7G8{iw--0bn{E*@*K46 zNN_&~M!lE1#R}#FXs~HtGpI*5)}ohDs=-?ziYA1;V431OCj%)dV7fniCOl@LE$Y!u zK(6vz2O}l}VK~;aiwZ}XfENoq=}~EJX3wr>X-1y;S|bprL#4kQB(rZ8Ta#H#qR8_u z+MvmKsfZ2%_`$NqYAgaPIO;jx;{Ej-a1$9_=>G4lild(kjjaJnqzrt&smPm{hp1w& z;eK4xqiAlxte0b2aU}1|jCsFUvnev`M?f#LH(Sa) zO@nk=vaiZ~1xW8Ra}Yss1kz1`bn!hiZXrfz>D<3(cC zH+(2i3I-Ik-zjSI=rjMw@;ISu0%$Dk9N_@zx~D@h7>NZQG;zMe~(n!<9_B%d1&&`^ALo30?yU+M*3?VVfPt#Up|}1 zbMBz`QxHL*;WG5(|bDm zJ~#ofEV&SCB>`Q^^IewVVc^&YkOnr_LI z1EM#)+gPLf>2u&%EW$jw7gV)O_FP?_)YEnFiR@ei?1Vj~9NRF(Pzqts6%)LDVA8v( zsJBN|nQzSA{R<5559kNQ1eg4%7@I4=4bnz?0_}58I#3SXUtS+GK>uKD(ucBDn`}G! z1-LH|_B)-2v~w{XSf*@qb!;l-cXncUxxHFtA(ZD?xMo}6<8HUJw?DqK-~8?G?X%s( z)Z?3%Id}mgf)P2VIRP&LO7_`!eK{cC*6Z$+P)LP!NESJCwNg!Pj-3o%_usmI5gR?ss2{4bYg^BEb;H@6@@Um+b+U*fi zp=7~kvGtoFvDb^n$^@BU7csL;p*;o3HfH4&m_LiB1-;tC{htXUUK_^+WQ|X40by_V zY3)Tm55s=)f@8$#ht1fv2B8f2b!t<;_?(Ct?-!?W>{7X8Ms)>PYmRpd29)aD1b|uP z1&teE=3kh-`|oB?KbrZ2)dc2}>rvK@)CB{fFgqCZ+0WIVI{GfMxqCULVz)=f1P=zyHC0{}12U>%V?5 zv`UWl5@k^6SZX*tzUx?+>2dt~+^Uxs^^6RPy*IgbdS5QsBsh1eqCHYpmn|!wR|PS1 zkZuLq#FySaq*TeOvyK9_0t(_UR|?SQ2OC1Z-V*VnDAPL;0O@%Z$c#1( z-drYb(9cLmKr~(BiJlR#6|q3G)!s{G|AWvbvN2;sfQxfotiIYf*7H4#z^ndVF(b2t zWalk4B5bVR>skL(O&f?74=k=&p{?7VUpGn6*;p%=iXYmf2>?QQla>jnwKR#57viaZ zP9mrrnPXy`tyB*m`Wv&S-t^3kZTXdoAhVYm_fHX^{}h1q)j+@6 z&rR4Xzw!|1Jy-Y3_*PQOFCtP#OE4YDi{A)DpfJk3~pIR8$7eBGvBbz8#wqjDahcSol| za8=74IjJTH2@CdI1>-IZN+F2{822w{z#ZgzfqrALZ0iYw9u&Vn5=VWI-9{D(-Tza{ z?CW;n#=XXEwDF-?a$!qmofrB7k_EUWu^uN4jF)pVaC|Dooe9B&>&oD_q3m2uYxVZ(6#oh!B+p5=6rc&MokfR%A3&qeltVccOC{&_?{M%Cgxt z53}^KNXh2v^K=kO69R}5NQ+u|SL8yJ{TKI|7q9{Y*8Lo8l_UkOx(qdvJj=8bG6IoO zIVBOg6$|s;F@v6`T2gs2WHw$k1wo?AJ%H1B zo0y>Cwpoi1_;a(knt_~}k$vV}69!H}x(7Y!{NG%FEz;XZKszD4X6kRydAQe< zpypw3CR28Y(CNVBk1#*>puDzzXUcDA>UfAS-*!k(9h| zdbsd$lzsBS5?2&jzufLwcAJ1WOe=1b)dpg#GM2fAaPLe+-%G^jkgQ+=a?HWNc&WK< z=#}n2YqxPDX(C2~TH~PIEbjZ})hXU((S0ga@>JVmyuZ97j(sI9gE*E0EcLV46fZwS;;QJt(V3w%k*Zs=lO2i|sxv%5{--;|7H2YX(5Lr7w<_&z! z(4ES7_xoo5{+n;?)zOc7thIvi?&jQc2tHmLz^?G+^LK1TxE-JCF_;i{_vnRET#(XnqkJqy=M=C?f2N?#%gQX`wHlz z?DqAGKz9s6w67On!$!+FuI7%#8$eM5?DvrK36p4?QPO4YRX>4rTr%hF;C=4_w>E&VX3bI+UF{O0c3gTr=Digjf-f_Ss0%Kc3ZRYQYqd7 zQMhGng}1vBgd=6iN>O?dxQlw&ktn5SHk(2~?JS-pg7xef8ui<&_hGTk+ET7;+TR=r z(xH9sQH6mO5GV2~3*~(trJPzp@7#w#~JjyM6!NkM^(s`ycF!zj|f+_eh@GtVH|z zat%-Zjmm|lDM_7Gtto@>O#Qa?+6xbrUj7a%@S3;c+ouP6&n4iu8vJ)vWq^%;b2=ry z4Itn-x>02P>vL0tb3wTi$aXV(e|WzggV4oxjd`V?2Dq(RZz?3#1Li4@8PH=n%vXeFh6J@n!A=pgy!r3$B;5b8V(=&4T`7 zMPQ_YeT3uzxo!{@1wLQjZYmSDlGiha%P}z{Yv)>r0+WpgbY1 zAWpve`^&WkofC?T%JhSzaHp*;MJdT+yrRUHIMQ|HCO~6i;2^TMX?vK$II*E0_YzMcz*zJUQu{-EA-xZF$r1qCcHKikHWUIeZUr_aS+x! z+9s0H`Xy5rVR{4$eJ@7iJA3!;oqheEzqdEPda$-{!%B^;PI7a094P?z)GRDs$e|cy z0SJ|LxLe#^zV^oU4=p#ipSPm>uI5i#R|??6d@=%JUA#a4n~$Z_PsXGIxSl=YD+6(H zK(2DH(cd2z0-6B6jszAu@UoA_)+FJ^e!jRzt2LZ@(0g>;Rbu=h2n zX27;t5TqIJS+)dNn|Dgc#s!`RD<=kk6i6pPnyq`P-#k;F2lIp`pqKTPo9I!NLV6Yy z2vz$(n1z?cWPnn1kAQyUv#hM~s#+~fF*~(N6NEK_?OImFVWe-{w7R}SfXJXZ8}qdl zHfkWEaZo~N1gKMGb=wANlmI1+^zNGwpjC4<@PHo#_X0ex2pA*)YX#qKy$cDF->qcT z!3m>v8SiC1@I+ey7u}9=PJ`de=!jKI+DU9(OJ%}`fmvvk{NcMEXe%xXS2NC*Q7R|KkhgBQW-r0vO?aWpaa$V%lASRP2q%)0wZi ziym^d6+b`G5eoCo7Isdvu^eps@R^5%MVB==0qdQcKX%}`32lf;IBxV!pMa>yqMwO} zg1}sxz`+6-j0=?MviGBP8=U()M}RvJqg%k`H24iBOY5a@T9S3AW6TJLtS`LszL@pE^PVLw0XkqFI2^R~r+ob|Xf5lN*LFvO0b_}VCovOJTk+4AB1og(n1 z{9SDR0q)V&OzHA2^WZsb0i)#3p&dobJoVD|oZ zx5eUBaBf;!=klkEwb*LaM=!6B6rg{wJw1~!Tt!R-5u2CaLv)|F5*S|CAF>4S6=RCX zif!v9NUUjSHa${MofdXCv)_O7tv%Y_cCUQQ5^fs!xIa7s*_GdG!7;tH+{!C@3b>TV ztI!8@UR1{hgsnOO)1tJ2inkOcVeR|~lnEc!58v|ldxBlhQ zlfK{6GL!c(+Z{yW?5R_9?*<4&9rog1js%sey^Ix4s!WP*L#XoV{#Y9k1Qeh|ZDUy5+cd^j$Z#uA zln?{-r`@qPcI;LI#+DHPkso!o-(bP3WUnm~1}g;bWrcN^?p1HXRUmoVMlEt`UVF^> z(rXb3+%4y51;Ct%`$eAUFmw0;z(SMGjNq6{<@G~U+fABS%UFU2nUEs^+!%zN#xdmI z6RrltqkPt6fhfTm$Vupg3>68*F$4Y9E>>$n>*%%E7ZGk?lRn6uxB~9yo{$$H`{@Dn zKS%cbIY@!YuEe`PJiBph6^)KWleP|(7snD=f6O(SW|mlfm|1`vLhObBq*HQ5c)slS zuy20@hJYdN(!_1S^ZMko87ELHA-Xw(Pj1!AYxtf*g zX3F+Vk$nbvN_E}>uW{8Q4l-w0236ksB4bL|@Jf`gMA`zdv5*l;LxyDfDq}^#68UXQ zGPSJz!UJN537*aCb_5cwQ!Td|Cy{<8?^jVFu*`j||5a+?vSJ|9O0Za9vHxvD(0UM% zA4=pk0<^#yd1T;TRVAFV=)4$(JEkZHj(OgzHqhBh69rN~1NbxV5stkZ}f6{p$JKx#+_mB4P|L_O<(q3D8-)ce_ zh;qtYzzYc+*6`VlbIE*LtAQK;u9FEoYN=Q1FScHs{FcPgmp)=ObkK+HJD^aTebVH0 z$<6|1LfCFR77qPuLU(J|Iaz}#@9r!4dwAU5C374bYEfRV)Zmq!Br7|VrK8b4Awa*G z^sh$&RfXlN5tv3C>_do~Mb9^ges-!mAe7seGFN4JMg*liIj1PQ%&g2jAxvnDB81Y^ z>PV);@E#> z_U@lx?R^}#JO@s%Zirnw_jRtgUmpp+ync=V{imKiuYhv`E|sVj%ci;o+4F2DGV6~3 zyJIUN&NR`MCxFWg0k(`M*`FuZhpI-4<2+fcee>Ht+3R2LtnEQPu$OmSz_~((+(>3R z_J)Op9p_2SCyJB3lJQvn^5|19g=3z+$&U;h&X>!rWkf;r83W}#MdHie*1vtB3lPk^ zCGhZm|DfUG0@xjir|?`}f9!Q{Fzn3isoVd46oKo}J%(Ze@bY%EbH6uRAgG9(y6+W8 zohc0^`etJm{aiW^q{3Cg01y_J$15*y zASrjZ#N9-wZigP=fruRK|0eIbAhK2FHtkOrDKw?46Bj#TrN6(o?C=u40Bp8Z9v%Mex57$FHQAR|Ii zL`6Ui*GMb?l;m(8Be46_;-piQ)n7n9AVoJ%&$VG&ZeuF?NMieIv&XN^_V15M?nNw< zTloebZy*B9wHO?ZW%>-HLS(2e93J?8Swng3Kj=$g&H;_O~e3 z4kAv1FgOLp<%K2cAs2o42(}X{W0FN^=R5n;x8K_z{(nE(>tF2*`)0X(t998q1V9&Q z5ph=fT%Vh`Vovi6vG>e+c6{e?@4d^N2$W&-eFp1An&^Kr70`Bf!el}tAC(F6UY=stnh<`~M`feG}Ue&3hO8HO_W1?U?uQ696U zSk}mM&oBb}GWvO!4Z^7E-i()Cn?>cE1Plt0E_<|13)C&g0b%78kQ?OtHaf(*08pc`pJzO7YHf9wGLLo2dx1?N{PE5hPTx+?kHbeNT1DGSPhlz(}3>=aviK5KAa z*5JP-@N39Jo9Xd}sFE_utu{et2gOpX~;J-8d_|#Q-gc1a7<$^ieo@==$=o zj6@Bc$bc@-Hy^3vxfnYu@z*wkUWD0mSP@^j%2kUglo~A8j^}yaUn{f6KI|C@!=&rx zK6GD*o5SC&9ip!GVCGL#k9nuw4nLH8>1ZHPa0Fy8L5)Z1Lbm7??*_=sVXd}Jqnc}?YOGnf~q_UA=lq3*efP>~XDK|i*c%(zRkpnDD=68R%!o#Zs8Ofkrf&IlY}eF{cd z?8@hdbnWP$o;WkE&lP$d$42v>;E>sL*P^P?S~gdd_=8#d+U)(GIxqBV@X3}ex|*nN zWi?&a3iOga|C9jwy@2;Kyl|&4g?1TRUtT{B1cA$4hSj4Fa26d&_r2lR=g&&5dBtA* z@BZ|IJ=&AGdm8=Ws!73V+Zrc=GWb-mwwDgGjKnFy&+JK{p)UnW#Yu;t;1E~erYK~x z^&F1kz$W&R>hvRr_u9*R&Zw{O9L_ZodcnM9Oxz4lemSqBqB|x*xFwitwjnQ6sRt|4m ziP$VC!#7JUzf=itlv%|FYc?ak@6lKcO5x4I5?N$|XmhIo$)-kPdezLntBkx@;KEXA zO9b5lm3vh-PDvW39T1dAGsSg?3ITNzNB~MytrBJD89A_!dW+vzAby7eC|6{`xri)E z;c5ZGXq-TP8;OBIkxvnzX4M3OB%7(_K`(#-@+R~lY<;F=Vuly)M+cyY^iW7SVtX`u z|LwIg^%b$L)hlEB{f8gFWXnGxfc_KkGyk(}6Qo!-6A)oHq>G@(wy8tniUclHAeg>?|te1C3|?uve=qgc1YiJ8B`h9AS|(z zSYNuhp_t)h%ZDh@btYIRe9t3-#|kvF0%q#7(3`!HSmabWpt9u0|23h#APF0AawjTl z51)xbe330QVY&5&Qhs2)=`v8RH9Nd~@s&kmNK~u-e@YMqqO4>tf%M#9($eXV+51+g z{yuh6t&f0QQ|pW}4&eoqCUb(|IA;m+YUbpg#67$r6A2?H_G-XsQ)C+R0uG z#T|C6%(l#XWfopm)+~>mRc)X$*<4NGY!8W9uxlW^MFg&T%Y>laV$hz&$m$%FA3goO zN8cwxc?+S`hxE+hd>%;`W_pKxwVBj-ItI;t1nD-Ft@mJES*`gD9&yf&D@9`kGU$7a ze~$uap`5-3cvHD&9|(#78zzLz=8|yJCHVbSX^2(gXLtOhR;Jna4eR?*aj+%!SF zEn|ROnZCP^d`d8l+jdT(1@WqCAy`{nCK22qTtU>HiS^k#6VXf6!-LuTZ#K4(-UA=Z zf2#t-L?~_s=Py=kA2UF|XTSI}*{WTAQ$(KUN%gdEIKE{wIr`dWTXxmh73$6tkrozE z8vAdA&5Nzz0L*=7kMADs+i$)T@7(yQ-Xjt%N5Vk=V~jh4DEP=e^QC;4i;h6T>%W$^hCftG1YQU$Efnpy3wRguga zKpVPsLEI@5u)k36!#&!%@^`<0^aVUO4n$7dYXT6h0jz5+!LE_5lDUpf!B< zu@e00S(I5V4WG5~7(94pF}u8wJMa9sWf`&hOzfwkJ7&TCq$YdjLC58-OAjItX`9?G(mg)-8q7#nMd>$URd6?<4 z_*9!?%W~t>x^_a0sti3(-p@UdLUW?H;#q=yp6O(2Zh?_;@ z-0cOSf0O|IC;GZeU-l#d!vOqM2Q;x@e#HU%oyiE4;bHJ354M|nPF7Vnp?|-7*x3(1 zytUu|<9GJvue3m1#lU_2%L`;orfiOzE?1;d?i;3pmlVo_i|RQzpT`-dZO1N~sVKuo z_G*YnxL`)m%m{>!bL$W^LCo$W$i_Pl4iZwr#q5qm=5WfQpR8t{eDphaEFc_iqd-q% z*$19VZf3HKT4jE;U|eu#Pd$wkOdn4ay;r&b!J&|mX>qs=^t&p-HqlFM{5uP%>E0g* zz1cQS1nZ&qd4~4-^Jpe5o-$Fl(9Wu`&?&1o>P2q_O%zrJ8vti0k+=vHO`pxzF7!4R z8QloPDtCfw;;m~400`?VAhB`_tm&)(;w1?cf%1$b5Mxj8(M2e(6ix{s>=*G%Ag~`3 zkqYK*iPnMOese;+g53DSxHf(dxQ*x0PY4G1m(~UrFNn31gWkyl1Q^zP|J>~H-^?C= z7_HXsolw68-yi_#RAdw{W$qszK>vv=x8JIy7%rHeXBFHkp3oZ(2{+vETe2PsS!8|a zjOE4lOIT{_Sr%v5&fFfK9_^3+{G&a5w#!2J<>-BZc>-8Y=qZEU3TXRAmco;n$$A_; z+2Y_Aeg3y^2plS7%j)SN#|q_{fl@$-zAm*`>K-4d^|A?Ro|vQFb?*_0ibx zlr$aJH|Jn|{l!Tn&RPe!XP3P)uqBtDr<20;h|xeANWK4QdzewR-2|+P?x6nbvcqzK-?N&X$VK2B^ zN>FfJ5*jhC#Lz_6e_6HzZ}J{&E6o~dmxiEX@yBg9VQq<7 z%-`jHm31V`WP_;4(|gzA)EGQ%#vTE%CAtELRM#0WW_`|<^xy_%_i3520_&!;=K}y@VAa+N1$Jyc9g8y+ zakegCBoEjrIT<4;%hRk(-h{nW0C+Q?S_R~CT@2Lzqu(ku`m|SE5lA@;O9dG-Ag-(e zAxN;{UTBvCJU5ADD&z#PMXe&j9~aQRv<*PzkE`P-EM? zq@O;RwI9qLzZD&n(D(8^L3yNyg<|Fl_Tq#7aRT(`$avp@NGk8U@VR#n;Arl8vMo>c z?Q6_guUU4xTcvBPiPKF?-0GCGcB~p)a2BdV8rUT6eWC@3fB2 z$n>z5Bk*}!m$7asrGo^-V>(`L$u_#~P9(tTx;%C_Sew1gZRjz_WWBid^MV9WSc6BK zf<&wV+6r>92E{?pKPb|-m4-QIS%3*3``QQ3+JV5c*sN@QDR*zH@~|Wfz}oOn=_kU}t9&m~AHKh$#@)1D}>JzDxl-B?zJ(Y$VB9 z#Lpe+>ueg7Q$UIQf1apBr+xB??SyK7fQS=hg+~Q=F)Ol4IzKs|SgfvF!K3O7MZ92* z7sASY0Zb7toU0Ohs2hY;#otYnBAE8E9EOi*ULC8txBCcyuAc)*fkmL#OxRR93_rr& z|6vU>p1uFvVlpoTD{B3My?CL2bO8NXQ1zVv<~@$7Qkq@pX-)m6@C@UJdu`9d@wrO- z5BEFQxoE)6?1#5M+M_k|-IzhglJf;~@!oh4*nB$5*W48Fb{&q@1XY+4zZbxFO*I4? z`szJ|pw!`8j8cD2O^zt&b)P@4(H-+MaC!JorSDG2pc!7XGXR0tS!p~vQU$Dh-MIbE z(l5R@Lpu@*Pu&+=SN?tuV6m2KGv(zei+<|qyNFcUTl`R!{h(m?>A`!$Tn~j(V z$^6<80Y))rl!AO{v!=Ysi$J{cyR?1|x(SU|jEMC|COitR32UVVZ<%Y3Gs)@d+L1+O z^dVn=GZL`S6`K9`8z;EdrRAU}(txu55uM*;z79&>2Qu@N zHGj#VzX0^-n!KT<#jX3z0Qj}66AI@L9q$G#$uiXO1AequQ*3qkPzhM|@N}Z`_u##g z3Ug~_-+%wZ834-dlyR{pMXAZFB?D?3f zy&SKqBN%5d)+yA3DJ-Op3BcWF*yFPHQ^9&Omq(0{79ycIsb`#(6tMK&Ur3?K)PM}r1x|R3ZR<^gKl77X+}TAdPc}6iiaj<%;k6Om-+sF=%p~fz=2M&)_oh zMDMM^qHJMm^Ck9sR&y7iUuDLvU|%JMS}*3R9RV89S`+t0f@0;^1@j~mtIMiEP{__^ z+YQUst#hJa8R}&2bD==2`yBoa+Dp!ot<1uafn`j>Z^j7ENaQk0jR^e>1*+FB9$$%| z{%H;ITxV+|i%DOZJ^ta~g+3^tSJd1$h+NX&ToKI9wrZ5jd3k+80R3)_;2VJXJ-|6E z1U=UrJK{+DrXg8wwyoQNcmu&XL=W@IXUdn-B%bB}?G{+yxjjBU*&lxUoxL7h0B3`$ zF^cj8(n?tBl>^N$ImF&GP#jIud@A&GIMN%y<;4-IupUtvb!YR+HemD9(F+&W9AocsRZhmK<}`kZCyy}P}U zK)a1VxJ~yvL`zJ6vD%pp5Iwc3&ldV_1RF`L4J=H=P5EP2GU>+10|B0uJsQ+DF!qrv z@MaeKtoLnPw4_sH4x1&O-WF$%wc4~Gnxh_KVKpc1zM6j2DF5>a`7J%`&B9)u%xjuv z(3Sn1@fbj=q?^tfmJ1r8pXqG;+yaro$){e~1xg~w3O)6~3(tB%ntif+mQqhKqn3)QL9>d)awa309IK7*{|LC5pULB+p_U=K(pzk?Bx%C8nN# zZub7SX6^kE4OOaqpQBnfi>k;ts!aKd7y73H(4YA#f3W@z!~mi|UBB%d*jnQ>t6~dF!H(bA9y$1B{cP9VZ%*DBecP2;=XLu-Fsh3(zq9MiwyDJOu$Ua= zB5)9z>(JnlKypn1c_$X6=#1D%arp1Mt|$8GP~tdv%CZt_=8-;w8^2_a7Qnp(U!zA@ z)Eh`u&;<|~etS$2sh-WqHft$87r%|NfGXHWvPJ`1X+8^{shFQltr;wXWW*jR8V{yE zA5}*&ut!Q-l`{w;K+imqje~}{`3FgMU=ZwTWR8N92&G5sd=AaxzIID`yv29Ve&H4; zL&E#Sv>$p#+{iw7@zA8b;>Pd8`2$L}730k;^R5ICmrA1}C5wKY%S!eWLJJlNHo9z| znV*^SwQ_Lp7>H8)$ZMU)s#skFA}dA&u?En!KH*q}g)n|+1ndw#fnVoS2E83K=wbX@ zaXq>h_F-cch{N^c=zgEP5@T2W)*D0FL(pfgap;Z(6T9yRCf%h3zmG$;i zW^H_sG63c&b%NyCF@z51ciIp$9Lu1u?AzZ8xARY#6r5ji`#Xax$uW&t`+0<$+`m&Y z0JdiE=OX~C5%6464+cWOtoe2{m_M4?gAIalzJNg=)ZGoEAlsnCdkz*3U&6GwqKPta zvgWX|;IITx^VSE7>%n!Y<&FAtUX!Da=Bcg za)m8%_FW|6aOyl?UY{yJ-w4j{X`Ob>%d^7{)LtPrgsbm1eFK(!nKK(Sj(Bu6S8@9ks=Z`PcbWBU{coxu9wk>1e=UEj9>gG#&+@CgF&8Ca*;;Higl1?hea zmT?J^mss2-K&Iv5(>7XPw#(yuNv0shrcZ#ekz0*Sjs8AgEL_X9K_4c?h!RIg&uqATfdr!Q)PJiyl_pRu`$F-CPmjn zwW39C1dWkb%RqZ@eIa9ET7|+vuYa|h zdJH2l9hJZ%gkGTn>5Z31&c6`4&RchzEx7bAP8Omj+ct%O%x?yp7PpvVI+Q82?O zB4OG<+XB`rG53h8zIJ4Q9l9rd)p&P0k0D4E4(2b%2ZPz}SknGzLu2ybhYlOwJI8;G z%!|ICZ_1p^nfEbX^t8v;TH=2W?ixvj)yRBq#(|+KOP6AGNNn1Sm#n9t6S^#4^?E`D z(MVd21ZYpyqD}aiv$0GB+nQ;0`WfT{0lA*^32zYxOF_9HUzDvK13PpL0~fJt8JM3l zXb`^YQJaSvKj`xE2wBT=c;xXDB!^iKhx-ve>u&fxso-xnAqSGdSlb{$oK9|*AAWJ^~T$n(-JkgH&3u>)mIwq(ISnTFQpSYfq+ahfmeIC z_(c1$ZO3wWSxE0&tj)CPn))5mts}j{7&=iENJj8mqhqZU?Xig7xoK&-Som$c9tbEM zr~bL;d+Wi>-jAi1@G1D*cgNHFrnAZ$0>YP!n_FDUE=sOx%Z<3JFToxr!8cI#bu(cf zXF~jF0>6!^r)R(G_t7|!ihvYg`=B7biQ_QgyId@Jj{Kh~$<4gT!Jjpb?(Ucc!$?U_ zBv7{i;aR>z^aH0#=+HU=Qh>0(9$htqBklobB6c3A&a=!S$eb{uA7DZ$epA-hI8hNQ z#jBP9Q5w#OC@Dr)@qlMQHaKPpjD*8OG+tH#^s=RS)>K7M+K<@dzpVKY(e*fZ>bhxy`OSy7m>x>i(OwS zB$RbR8!Zn|uClHm^SWj9)rf*R8f!PWq+v7`u}B;m%y!2I{rfI+j^oPT`tKeWrba+G z#?sCaghK=F)2m_0{-ce)@%@^)9i*?eK$iG8!?Y@~DxGcM>gx!~MS;ZE9JkXO&P46H9v(2l zi;?n7ICyfgXvE!OZGZ7V><}eeJk^$BSgY)!_JQ&IFcD=a%{Q~St!O62;AZVdv!@>s zhe=leUnHBU=txETW?(#0cYk?(ECBr;K>4SVJ+G5n^dV6OoEawAa${aI9!OS{q=c!m z@<~8kJO{P7_X`nV_NQ;ZKU%I?A_>a}-b?E$^aQXqQr0RG1t?y{@z*XWRJUr^`~0x0XFzRk~1$cmuU(~~G zJd@r)e9{r$2T*0N3b-Q)B8B%k=FF{lyexK6;ybK_I@4-dY}$-x><8~`6Q0O{%DrWx zUP-E6*@>}bTM7sYPxFv)7pnDAA)5d>?@HXOLc3uVAZkxA>NRg7Bd@9jz^(O6S;)t}o56*FChl zyJVhMIXESBclrM_NY5H1aL$4c#yahom2YNtbVaaEsU5w_J6LcwiuSNR>i#!vQHehV zp}q#flZf28KfZtkl-l?u!-65peNy*sCQ8mBRM{=at_MJi_JbEe`T}HZwQge>QWMny zEh<0n_S5F7fGI<)1f$N13z`41b>N->1oqu2JMICvP=a@CVNjmQvq!voE{5|lh$k4~ z?I#?;Q^g(ld-!Hec!60LXrSg(ie4bMhV40nkG-oI1g`NT4`T_c5sc5 z{=^Wnf7%!RoH-YkiVIN#TglRe$U17!1SE*GYz)ff8+27zBrGYTW)xyEVI_M4xB%?& zhZ|$_fY96$`+MQdizV^pnllCZm)EBV&_4j)zUu~|nXY~za9nu|3N!~&-?-O&8!T{qid@Of{Qx5lOp{2I~Py&%UHD` z58sXOnFMe#fAs%9D64nDW$ViMo^14&b!Dq&ROoHatS{nj>hmLdD*kC-Yv1Kf;QK;+FV%Kh21iz?Ww z_J$cOAt7p+Z}Ks#>L$eREbmK~>f%^M-IxHJ3m}?h)FrvW#J`vTS7M7=dAODR$5l2Q zu^kw@Nh5!8DePvV0)%DJt=^MvmWcGp^<*WB%02cL+ye@bf;a6^(YkhNwK{#rdr`zW zb`Z7ewD(>RS~Cb#q!j|RRNDoCh~D8bSBi5(2Sp2KI-%x5%5X=Y?QI&SSsFp`^VX zRNGI${rtPu{Tk!vGZ9=!UShF}Y`4rX6N0mepjDTx`x+#6)^W;$iIpN#6?l+f zos@P{aF3vTJ+przz2$SDE)|St02qAVg0YE&muB0vnaq(T;z~dZCVj=tVxM-PQd_lN zvt|1P7%EWP5Fh>Wlkw#RY^?CDD4ZxQKaxpoecA+CV;k#z8m zxPh!JkR*X}76|49Cz&wpr?rmEJZDiwnK_{Q?@hY{=olC4+KuemK}x_gV_2VEo!aSb**G-6KwGEB zyuIqd1t{m^$w7KobncM|9smE3-|P0^vCSK=LU5Vs;VmrF{(K??PT9eonLpW(T|a}@ zDR{RY#ACGbSw-DQ`FoI2z4YkYnpv~h(>yFv!c1_W3!Wi?3?mF^d1?l}jr*8`#EjHF zEhu3wd#6upwCvec<+n4i0w)*bJ<$KQnJhmK%W;)h7t2N~V2IoU+*sc{vjXRU z{j}1AAacH57I>6rz0Oit&TNxim?D=Gi}vk`=Sdi1Vm*{kUsVwUZePZ#S}2Z8`lyvs zJ#|tL=X2;E2NjQ0#7%EwSkf+-*QjY>f(o7sc?{Jqu~s4>T-&3FOttQ5!V(LCVV|2l zeQVYpFJKfbe9zu1jgy~Pz88!sFriE-#F)}_|@ex3~&GrxMs0xSXq0pJBQunsMiqg(M>-4~s-= zl9<-o1z5;eGoisjW6kaf-U6P3;>8(+ zyWc1I|ML2&0`wcvH-1Xlb6$nY)e-Rw$635tQ9&{u`$=JH@8(3_DLBaz?%_$MpDvAq54 zfey2F>O1!iWcab?*DqO63bMOHIRIXdd)1q^(SbX)YBMBW=;F%unf;+s0U}5}{oX~% zdljTx;K4k{tq@{cC}h*y8Urgob!WgtRRw^9cqW8*?t82}uLyxK1m>!Wf&p<8e&E6L zk!I8i08@Ii3l(x#z1WD=-Yo&G^Y|)ffwQl@(l&^)x_#+%_FPxnjLsXJL4aCXx5{ewvXGc_5R9>l3PQww|{)J-K*W$Ph?dWYz~NM z^mz58gXFDaOk~CHKk*^bu(UJVC2eSjOB7L_u~nG7K4>)!z#?d9AbKkF4Y>B3T;d{ZH=YmZ~B1A zd2Z_3-MqYhjsX2u#^|S#Jy#+$+i>UY$LJfkp%ikFEDF*G+5-iN0(t>o8=^H)?aqV6 zWL^l}-QK@@Z|}c;G=J5x+Z!fBCXD7AE==6-s*T1t?7H=KfXE~|h0dyX7jy)$iR)QM zRA2mNK~+aMH7lO-NT{g(X-4x3>vw$x$SAb?$q;W~k16^p^LU zn{iRNW?I+`6In3>o#h@DCF`@jTIfk`e7p0wMb%j0akH^%8bC#*x#4j@1G~y|TV|42 zRe%v2Jk&^WN;*c_axq`1$20TkZW^#>rQ8gxN~^G%d#NtHk9^b_fni$dTV>y=`n`#< zh{ho~A9PIVEVgEr%k?Zo8J#b#ZSN+*OMUt64Ed~Z+IZ1>71i?vGk zX$7L=is=?X@9`5SiXTkO?HYx7?q@#b%U$f%jXct7T^`X@M`6+;xGL=op2c1$Ie-q- zf>urxDm0VNkpS_iu$%zu;5;r@V+uT+5i&Lb^088*sX(#+?$dGG4)c9yuXf6qH}Lxx zV`hB>+1-_B-G2gLzHDhMr&lKhX4q9g= zclH|!IAtoS!b}sP%o4bL#1SKZGhzu}pH2`_ zdU0&z0tN8JXyf>zx*!x5$O;kRZuaiodwaCK`Oa@>s?kd7xtI2uxm?@N;i)nK7^A(G}q_!&j7D&=CUTeya#z_Y9ur zts*6a#{4%s?ndTu8?G5?I)EQU;!eHj?V#>%dy(;uyv^sC=2!!uIo9i~dKNGX8hj~1 zHCpZ}zQ5j;PM4!50%4lXhqnBZ8Cs#>tW=o+?e?L1&sjEq3G_u-xB~R4YPfN)WK)?? z+h)b6%Jo^Pbh;iv+gu@Au2q>h`$?o858-RidbmZ#JDT%y3>kQ~r{Mc!^U#DU{{;Mz%sn@KkA{MGmfNVM%I+k?Z-j&# z#%H*%K;{>8RC3<0pG8M$Bo~n}h}{vKE%*oN_Z+!ivl-s*S%IBL=_-UWOI|Z-4?KrmhtB0*CxAd7H0OElv@YZqP3ijE!vY)%C+wt+MH@WZ$;D>igjmh`2wUU%mGtjeRQZCL>D<`T}iM7CLAk4CcG zl*F*HTznxWl+Sl&@77G(4YAT%dfWmuEAMy6z&!2)Z)P-I-hU3Okrk(BdTSo{{}hfinbU=pu;Uq>L^s4JDzV#x-cWU=;#GrAM{QUS&_!^+L)i zq(DE6+gORcCH8I0_(9)`^O`h~1nj9qNij1jHm69n%e8^jddP{oj%Y_oXU@x^-dVzKrq0ra1dayzS7?|}|*J>ab#qhAYH ztMDliXaj<}m_lEfva!iDIh62R+RHa=_wbLWr$^gcJGarpDn?egad@$ucMiNd_=sBD zo@QNq@pTqHHv64GBOTooQgWz}&kU^j!J8Ac?Ed#&XoH&1#VrxQOF+c{0Gg$CY$`yPK-9EziK=R#-s}Qkg8*9T z)1GZF1F}gOd8y<*%92|mR%bzfd`5;8h>OPVufbjom(1=wtiqCR_4=OA2Xd^Um|(30 z^z-k}vLQey%D0SryPOPDYH!9XEW&j$A_FX_{F!q(9<(QfagcO-8|T(3gZ^l?e=PQK zBZ{8IzyADSw;%i}QKfKb--(a?g%Ib}mUD>2GAp&>BXfHVMkC32IEuCSo_vAw<3U zpxJt-PaT}S(@oTrL1x?6vVJ2a?6`56i9619$-I!4Lte9i5X|RBX<64lOgN=?fm@s5 z{Jsl1d)uxyZV1!o2jG49ph1h9osZFtiiz=zIhMZld7Ih&OMAQ*kUm=n`6!S?l+GIi zMV6G7l7KMkr8PkSs6bzqt_q+o%WqZS+&X>5W}$7wNsZk>eq$?ebt1w?gKL5UBJ8Yy zoJY1;9<)oS{$Bw+7%@OC!Ax6Bg&F2x!KB%4OO(?K0&FRWFM#A*ywFkjpIz~rIVQQ> z<9_M4?$&u~p%^{US^2uKbTdl621L+_WW-7=$n{6WqLe^1US%nD9eU4mL9(cebRw{O zantqwz@*uMw2}ubcAf{}RN}se4C(G(DIC}hqY%{1bLIt{zX0@Gu-UWX{ysb9wq5_t z6)?>V!Exs?!uGF3zOMyLw!Kuna=54+GgElqxBU^mIn6~%miFrLM4V)bqjz z&w2fQ{r?qcY1UVSOeOSBxB%!*(fzFIJ^}L~=scFPHzUGbcKJQEE$h{NJuYRJ!g8jp z1vzM4LHdcr;Oh!R|9vRJ)_v9(u(P5sy5%bKL%`WIP^VT?w)Qxyu|!sW41P^$FseMo zS#TW)t1&XdDqi!dEWKcd7KyDbV5kKdXI0Ky0lds3+X|v-nU$Hc5rsg&Ol zuy3WDS({9#3t*Fl*HV=Loh44tKZ+-#%iV)HyR0vrm>mLZF;@A*h-ZkoYnoV!NY%^! zu?n?`g?b%Q53}PYo&yLfNdP;SxMQQ0@FrZbC9~qT$^Q1G|NK(|=*dE|Y34pt_FOSf zz7b0+PtWw2{EBnL^*4HH7gYf`Tu&xcdWkkcV;m?z!0i3o_qH=%JkV#b#93sat|T^`8JGc|7wKh zfg!PiV|zrBhtCcBPW#m)K?V3%fqVt36H7aXjTOe@T=hpYochl9-Tk}oz6lOq`?a8* zm{C9et`Sx9sg!*P!sb$VfCj?I0$LWJi|4^bH^Hgc+Y-zPV5TzHEO43uX%j&$l=u^b zPqheY)qW{B4>G1K7_53~^nk*r{V=(Ya}`IBEjI;}3n+2RV_5EsDUr{9@TSYoOPS0f zSht|UVGwa^9O$t)ik7A$e2nU>Jez}oon8i}8Ce2CjG+UA>K+*Pf04^)0;O&vk(s&m zpjd?v-DC1TL?^qt%q?hZxn@f>jWMblx_yXU&-sIa^ql6#?D>9n1wNeYR2H(>1#@nTBO z^P1@u@ao0)U^P|pjmKWebR!df+jxVQfqPIYxCz7;(4pHw_~-2p@9mZCMiHt2WL-Zi zQJ+`|I2xynPs7I_GoXFjPKMsaS!$vphQyb>v=oX$IK5-j)JN!V;9>&8od8A`Xr?k`m zb03do21cYWeGUwbsiO;@Y9xSp{mePg7FKCW-0duYX1N#}?SUp9&eY<{bcFx~fsuds zEM5h!-cAV#1errYY?vTv==VW@c|asfk<-XGe#7^a@Sd3fqy=y${kv_gAPGql(Q`h` zn>H|-qAYoxziKgK0%%d6dD;^nd7)<_Y9<5`DOup!;Q;Hi7PR+)yBbi?9nL)@*A#1s zwuy0pg^cAoCXh}op=B8(bwmw4yH+xpGi!|~VjT$~D%XT{Aq1;51I*sQ_CFSc=A7Rb z79_3ijiExj#}{z^2>|qKd#JCSjT^J)S!wm6% z^6>)Z58nfx4J+GwXX{Jxvf4lP`nmAWq_;>HA;e4)?~ z0CNTo%!Gg)v;sOK0GQV}qO$EMa1HvFhlDGHU>)M@9UTLqUL9s>&3HCXfkQ^BSUdru z^5$qC@<9-l%p(vxQ~mYKm)e5u2Qf?3R*+el&11vnaQSH(5IL2;cWMQgDBRCg3mD$tV!!zxwer{}w^-9|k$8pY}Z~W}#^|1i- zXJCCkBsiy86d?fTQB+O5LZh3n3;{Pp@e=kx0C6D3SvOuE7Zz}+5F3$whq*Pt-oJgc z-J;S}lZV^#i@XQ92sUx$9zBBFaaNmw_gFTLU$pIge*q2PeZZhq5M#~s0F09xwO!Ly z?@6is={P;=VV^|a96|sN0#G2i=DsrgY`+@Pn++=E^9&-C%A;NU(0j%0xflJY&OVf3 z%mGy6e(FQeIfH%h-te=UG6A=(6zoP|9>bgk15^wUE01#H;35PECalFQwnn2p>&mk| zDTO!Tx2?eX{0|1+&61M&PE;7#8fZH*zYvhl>kS5 zR$1}q{+Z(mJw81h1mckCs@K49UGIDbWWKy#&4%hg4%_GTre^^r&+qLM*0+G&0_m}^ zI!j4AN+3Pgp2ykeoXNRmn)H#KT;=oV7Fe#5YuK+u_*Q55n!3AkLRgv@a0KZ+@OB%m z)QCvjFu2VIu^%SxHmqAyWq`_|E~|(bR=MN2sYaappq@_sr4dwbG1CnQl<1|ObJL~!s*F1$O^^`S%N$^R%c_(-;Wwo{TIPiwD5|3X)~UTz z)1cNe_3R?SUvUm;Y>&h$G z{%s*tO8V0gw5eY8@)fkJ>eiR?_D=zzX8_`! zUIUQ3o~dim;LRd{zTvQ^51$_Q;iU9vNiPTlAY5U6!>K9=R0OTWWBfWd_i8!DH5U}! z|7BbAWONEgGx&tB1>I`vYw2DEq^rscq(Gkj+7(F7;dFGgT+tl~ABF<8h4u5tV_@xe zuwXlVulGgol->`^4c6y_6F_Z&>T_pfw)UX=d8ZK=SLdrd#TU?D4djexw_6Y(FF@RQ zS$FD#t}M|+=9vLzC3t2eXg9V0Ci>Qyu$^g;WR_Qm!2+SZsNXhwoQocOX*(sV5K4k> znV6gD((X`n%>w&A%QMy~kWbjINJ-uV2n^3#^-!A-jWb=^-2dIgweKT|);;HMg7iWvU}A?7T>g|*}GrL_H{2k7^hPdM3+YJMXk}@(F!)&To zdHmZc*sYRvO1e&0FF;E4Qx zx+ft8`5@VFQ*0iZ4N9%`?c*ACs&y^6<^WLqs`JHc20%|w`$=jo+CEYUS;^#j;Zo^7 zca`_XBpgQ|wI?*GjRg@)w%F^p+6ve`C`s@Cm}o5zN~wLlSwPtC6^J7o$IAKg46!fU zuV@coSxJ28R$eI)OZ?3VNJm7U)U4U!zsEW_5D>oz$GI7v%xrIF56AzWK=QsYac_`K(&8<_4XP~VSHGU{DrZ{jPJa~yv93!hh%D7=`FFnnyGva}O<7OlqhZyvR z)dEO>IJqvl&$0lLGf)w=zj8igE+%~{)QWwuOkq7x8)txmX805+jfCWq#hHTgN(NnQ zgO+CZ)emMelZ{#zm?jMZ&{zTF5s=QvG_$~7aH%#J2MmC;!i%&TLW27|zJ4Y((K>JW zVSA&sWuyvk0ny%DP&qS*vp8{zAo=braQ;cV2yjSgs6?ElttUOw^Y8g-fTW&dk8xvr9^-zDdU3z*@( zT{8S9LAYac^jRcs0M3`_sd*>%H}4NyIBx!LRsJPp7v01s|bdpB^S^>@FAc= z4A#SJdqGIAB;zu%a zIBEwKeP+#xIj}}ji6Z6<^-K3diF>&P^aFh-Y_Sdg{qp*l06N0CaDUW}=h;qJ-ct1m z$)Z}^_qZXpuY$>3{t@~9u053uh%+IS@;Dz#upS?uY&VF+DKNR^kWni6&q~`7jw`5* z$~fgboLKiJU7;?JtqSGXc%zOP*j!nq)|1Yx+>~t{h}fLdvU#@07M?AR-|^|_TxPO4 z6xZQa)LX6%^qh>12lq7w`@9PgA{1@?@I=7=m`OkWeb|jJ!2A*~`7o$WfHtR~Z&l9a zs_SD!j_|uYh#v2N6DWrO?FEo7dg@_X_1z%AswIl;je{Jz592{71?r}aC>GH=vDSzE z-$em8s+=EWi&;u6wC8G;_AOH@IiVb`{JwisLd(G;QYQiHk*|EJ7|$~Gk^g&CvTbRY z!O0CF)Cb|Qu6)twd5%B_piD3DECeiGkaGd*jc!00uccFM%4f5LcNHRjGn=Mw4da7_LlS!|~PY6`U)O zY@QI;2!MXu>2D$+DUfb=mfMPk-Z$PDk+|Ge-bm8eWN(=k`?fxMC51*h%RD;I zZ1lYQPwbzLb7}sp%8%_J-F?}gqK?@4@xFTmAf1ouMWtRJF9fYIW@tTFYGBwM|9YHy z%^g%d{qQ-KYy|qi4zXaIp%ZcvpP$bU|BAQH4A_XCyrB(Zvn!G>W{`jvlL6q zYPKx~+z_S3)<*U73TQ%BT5mxH+XAA(YPM=-@%SSMi)4mf&V$n*J`rS?k^#lcIO4u* zw_@AC26BG(C+Azy&rYVJ)c0|NJTESb92Q$TD!RF}yg*lRbd%sRmRT&UQ5e<^qWhyeO#2I!f=J8s;L^8fBxr!{;hH!n8c zd?*kMDwDm@>%F%!8ZjtpgvSOi$7;2|--~cTfnr3JYrs`gDVN&ny~yi)inHxI%b&Ug zj5XK}>utws9e!(%ugrD3_)TfVk!ag*UrZLO73X4hLw4QQ_gsq0xn;@+(fO?+bKc#@ ztCOMCAb4nZ?Q7G zB4uYHpsYT|EXtCe`>;3NxNw&$gSH^soQbkU=s7&yf*4u|q>Yrdt9FD;xj(2NFxA?? zDm|h(DTs}xgw^`>gY-5lo5JBhyx^H`vGw907$&L?nrT73Vf{R9X9L+Dk^si7PANEhEVQ5^5Q&csPZ%Ce@YwaTW zjTdnKkpc9dlJ7Zi#iXx%4BR+>0noJ-|1e z5@1j5sNzmf!Ud?r1f*(RRf_=|YKr72%~zR6Hm(96XwG`hl1CzCo0zOH)&?V<$K&^F zr+Wuuolz4ALItNgMj^NW>1SUA)qLGnn?}6J^HVOb71edtUw&!iIThlM{qef-pfCBv zLRuWZbBs{&`dH?UBS?Sh#-X=ev*YLOWL0E?pc_EWvynAl^kdVKN|uoi0IgZnkM12k z6I-KEuxtXTC&F$+KtF+$NNn!B?UsnZDK&YLF$eKkH;aY$s9JsYx5Y}>v~1pTzw*py zU2SWNZCXeWs1kBpf$BBj%$RlqGP+d&Qz2wmQdAE-()+(p{@@MqajDor5+_ygO2@?q z#nmR}o*NNvwBULQvaEl$$Y=9O1PvNMF3$r5ef-lsv|IxO-sqV#02MuqW!dMR`K8~T z3@T2SX%O;Ufi@*&Cn7~UFHF=luqN?tjoUrVT$O@1nFWJSVEm=5{UZhF_XLhVm5EYN zGoO{sc?^m9KVjHerV1i!w08a(|Mht{!sgTOSVuB)GG?=1?e-lRnR{+d! zycHBIn1FR>3$o^X9Py8YyMBPRL2w5DkF& z=oA2EYXz<3tU)@K)g-ijBY-FBhi4#zX4oz0z1|=o7xuDe;FkuKx@{DD0|$3X^*!uW7Jl&TcO^x2(F-1VqyuKHjigah zuCkIm>YSr85VCDV|7*Qi8-`RZEYMOwPG<0@>X`B2| zxZAAcJ|X0_7kylz+I$ji27(x~bHl!{D!sR)M>PZ4!-#p|djv2u;9&)EITJ+0)brg3 zp){Kw90v+2>r3Xokrsi1)H5om5S-{R;xT`|zKz5S1n`vvZ_TvLTLq+|;8o55R@1gr z$|qfA&!aNVwt?HXhXSk5&ixK~&Aw>+Gi6TxtQ& zCCZHSDCSl+C4@LUNgyo!$h|QWpDLjO+KVonh^Fh?5IyV>&QBwW2L634Zjhc#}t9XdC$1A(Te(fv!tI{(t|62LI_lP5MsPEoPt%1 z*(2*n40;?Q`(%W;n3d({47mbe3fNIKeMI^e7t+&vawaGrr@;IQ`!1D@MuuitB}cGL zCDNw`QT3l&QWj2ar4nJhMJJ(5k!#iPN@|t0RfJ{w7PMS|KWACFxl+1C#eiKTnJ%_! zT@SXh=(D=7x+?9C)}JsQgwBl5Hf;3151fHo1?cym`{jVzL=^M{1t(!}Xv^yU&CVIo zjarRoH^jVm!^)zPfE~|^K-{MW(C-YOqlN8e0>CHV^JEhd?Am-3GWfr15 zR1qK~7Ci@7BdA?}OVvWzIWa%e1aiiUB1+eK+oh=JxO32Gf?Tdiwvni{1U9!#5EzqM zW3^OcIkiPGi?5@PtHwRz#r1e|5S^71&ROVbYaRjL%Wr(}Ue};Gwrg`z>OM=zW!qq5 z(WbxGS$)>DI0Zfrb`Xg>mb%*>=OEtOn&qaz!luA!7FC+#CSU=nEMpF{G{dMsx&#)s zR#YtN!h9K{z2ugGe9}|OO6Hr$nhx3Y7W4-PAPt#KU1AB>jF<4v!Soh#m-Wlj65Sw< zQ02-?*?cqV*BuGLLD{Gkdb*{EogyBPZ^ulF4ALPF%AA+pYVdnCQvSE-9yH$Howf}W zF*B^!lo*N5QN7&Pvh|{i4r`s5gQ6jW%`6SO39d;b44NB#=6VF4;;_L(%BTWktkQ8B zpUoNW)e;VT?jA{?^YM56<|C;{>@NlRwA71DzVow6{#aFrE=!M_fcd7Jm2d1*pMUN# z7=I~s|JVWgPc3_1iST{(LeGBhYC(kQH z$98Up<`NRG5U8RCU+H^aJKYpat8JC$Zyi?eX$S(s@}y&B^f_?Wd0X7Hgn-`lTG4!k zu)cwK;nxV$y|MbMAcgoiT90! zh*MH*PJ#1zuuekS9>9)gATl~q;QSuMoWXEttq1&ApOXWj5w%i8iLt4bs!hY)l30B8 z&bd8?z8f5h6yC*m=S=zwSpV1n`n|#VJ+kMOW0;gUqkWaL+R2^k!HpBTJmXI1d<<4A z-%Wd|g5dM2HAO3z<&_6FWr#fm12Z7sHg#cfUbhM=y*R0njDk}DnZk63BmvrnF=vps z{LrD0bUi*05X?z}h)^H^*hc`S3&x|~{fnMDCaX6Dfq+){oy##rR$qYZ-bPK4PL=im zz|5Xtcy-J&o^02S5m$2__??5yZb%39cN;%HmpM*->GQfhX4FCV%ly5~>V4^q=)-hP z`T*5T>sOfx8#Z+;!mh}sV{Ihk*LoaEEYuhfin8LeUw#sVn*lIj?&S@6Si73pHUlL_}ppF9@`Lj0f$%5 zC8JAJeL=;l0h>BRkdP?w46x7S5ajGsa|J;NudjM!2zWRLpO3&Jjs33f<7Bn=V21a0 za&d1-S8TymvEo|F)}gc3;i`+E zH$6~dw@7IoK^ECYCF*&*v}e90M1ZIgI18dhtpvs^K^RcZYPhzPtXFg*Bw#)ZJ(j%J z=(w071!i)j5n_dSj8g^o)B4e7K)%Lad|a?%af~J8&2;6ES-k>go*!+{DsUZ=%ws{? zm{bDka2|92yG~Y6($V!AW~2@2#I0Kt|#D+Is0z2#oC-XJ(WiiZ*-OkG66K@=RVvKOLo0j@_s(xvVlV*$7x(} zZV@s}gbP{Bl-d5t@h1yYv&0H7AhOKFbz|gDx4mXuBriHQ3CKIYY!g{*>%`h3l7)`7 z386sL=8p#1D3BGfCSr%UMFo_2=&NiRa_hdlwu7Pb_F;$VW{Z)8xh2z2^(*=19$j;G6 z0M3PYRQ@#@?_Q(a%a;@-S-<$aa0buV{<{vuuJ9h=)vV_5qyG7ota0(n4#b9Y!_E+K zmh)Q)f^{EJzdFQmpWlBvCX055Ec)46XFrL=P3rDvv7RYdLW3@|^h=o|j_JJtwE87W zezsjZjSSYcF3OOzj53=|ap`-U<*n>15@wTS`=ZpBo$GlGXj9oUDGg^m?@Sa-+HhG? z;!GxgWN8(a$}?hsW%0p@1O~iRW_t+>SVr5DvUqVlhxZkMgFGCO{qX8KZ58Vy;8d!C zn@L@Rr98dchg*=vwx9w7Qzil{)ehsPtaEk8BKj8SE{xm32pR=Zv8;O&W#1EyW=RzS z^xQ;HSu;NgXv;bX>1vT}oSJh^`ri3E&|kY=WRH=m6+B>q=$TuE05jjoT-Wt*Xsf=I zB{z7N&ymFVOL_Z83eZ2`MZJr1JGl|6y&q-V?J=mW9ye|{N8D?SHjGDr=Oja^2nyeD z5P;IJR?t!zvU>rFSKzl^_Zf&{Ms3aN{qCXsm{+vm$-6!)+j`iCzLvnZ;mBGDkVn!$ zmh?LVyOp`wZOyUi`i7j5#};f%?jeKrYt~~aG@s5V%A~LVia|Q33bxp;4WI1I@ai~B z@6B2-5;p_$*7?Ct1J-|6shp2wq_4oyr{Eu0>6aE~VN3yN=sxv6tB%>p0Cu&2+;Z+V zD>ZAIGZWwk3aEGXZfTjjQslRwyt$G!7a*5`Aq7BWM%)5`Exg8=z#A=B50vy3KvK3@ zR2Z)(6h!|#t1yUEN@TSaYUE0I4U+R=V`vSeHC z2VWbrAjr?c?)yMZt~$?EoMO%Mv8=RLzaA1Vp?77)NoO-|Ix+cSGH3p~v8e0St`o`Ld< zO&yXp2bS|6yPn(fT_1@nuLF7t(mev~-T&Vo@B7zgzPCeG-49l4Oa#u4ApNYI+zZC} z#iDKQ8}Iw78(R}4Sa9yM?rUI5fw?8wNR0QbQZddz`a(!#LTyA)O&wp>AI!@7K>)Gp zM>oWo<4TK3+T(12T6p)vHUSa|%qP$va*9j)S-7yO!Ht@=igB|4Qxr>I zm+-(H(LXC=KVAD%Z2IDn{&NQCAMkaT?0L{jqWV$Xba{|hkE7bB;u{FF4MyF_h=nR> z#uaOaA_tOkgmMPm+;+Y@+ep<8r62_6QEow(kw;(zZ@^?q^`=@=ve zbLux22EKNJwoKq$3*(Gnf$9SqaNQ zv>MC=f)xQ8#IZB7!uUR;5_MWWjP2w}W@}~>IbcXEvJ$6&N}T6X)Xlra#|J7IvlU}- zyeu{vToJ5xKZg|z!I!CkA%ZOa+{)1|pgHN$O9?e2_jrT2uf=n2wD!ZM3PON9wb=dF zpYOCznZ-IJ%R14_{FT)$i@g?`JP&~4W8Y`Y`~LGDoWCT|KLvn(i!j{1_7Pzawk_8c zGfNPiv1w|uXX;M(9?xD7rD!@16jaj57f?K=)Uz@f)`!;*!B;)F7%SlR(y(R)f);}o z>a4xyERjOV01(E;Ho;}%`8yBFL*|lAKH9U+^;`sRB^VuF8J5{68F0r{w-Gd?znMNuzj6nn%iPy#li7HblC8`louc0XLDotA}@GU8=@p91~Cip>gH z^~{1z5CSdvH;`w;79q8!mql7;Z?%5#S^O$el9&5rK*l%-ls0L}b5vCroc3vv*1|kz znr;nH$IficQV?&k*qucJZN}R(czXxB`*J-o)sCUG8ZzSa@_p_HJ}8|dw)W%T7bzgG z*x>PN1WpXuBqT>%ALa`fw(1B4cm~9^fezv%oQje_wmo^q+q_nsdeHko=N^drnjZ9o z7;u$Aw^bN!CLv#v=s!n*zRke-?(EN9w&60tbq)-}M73LHIlbJ}TW&a1_I=HGdCuQO zex%j?`BlfX{aQZkb_IP?eIitX3nL7=u9Lg=kl02DFhFLel;}|&+j|-Fd@7(Z+Gr<2d_4a;+V{eEIr>qg+BYACHGSA#oeor zn*#i%=Qbij>#PiaS(vD`BD?OwYPC)L|<%!XFk>h^{?Hsz;g1@|7nzm&ND90B^h zYaZ^AJ%2{x%Cq;9FI-a^j%yKNd4bT`b5#~uFk^!;vHaww3sF+-%RRgU!fiD0S8?Hs9)E@lO zqn>m%#z7;6BWXb3O7sZJ=<_@_%aHq0*8p++oxi!5YoLTjH^*RxJq)``Bn-z0+`9~& zx3LTzPSzvvb!o|n23hhE3nl4oiD-sZ>V>e!2uaF%r=SCtpXQD(NE0Lgp)B^g&w%NJ$$K4Rq%nIa4jRG4p z_NH_Rh&R2=h=Z&Avnq{jyuxfRMo>ZbV}S*qVVQ@0)xun@k!7C4F}AhWe`X3Wd3Dn{ z(rpx?s?oYWbE=tOnFS)!DvELcUCiVH7~5(UXaroZ#{~(dRXYlIDIo2G)A;mC@93|G z=?Vf6F%9fViTh9C6$ru2gENT2xxJLLe-r@yPT;!)&Y!9Gy!^TI;@Olv?#nZ`-oVds zmtehvJ=?7()ixc@2?>j>WWt8B8h5u>uMYryE(^v57lo@BLT)u+T_bQ>K4Dm`3?pE= zX|@>S!^lDRbcx5D*A}>f?Ox(sK&gg{??=Ct)+t@HF+)gkkn7|qUI9!1l#jdAk9Vys zj}W$F)7*-F!xh>udu-D+YbQ%GSsKsv-djf&VIst$Y=!TSf&A{+i+%#$`!)MKfb`zu zyc3smQHE}FfIEo|SsR7qF2S@@;I2S=spOpj(DPXNB<-haHh30z*>B((F3a96E`N_a zvKvH3odox2iMqS#5^-2j3@_S_utzfms72rUJjZ0&KiO`Z=TTdkAX@s1;&_>;&=q*<7mmnz!@Pj|BdnIb@;&G#Dooc(S{904pQ6xcPVsrZQCFm7?hv(S*= zqm?(4KwL)HYqDh4+QEANizD}~*ucy8!RG?0z_Dq4WdeBUCO>UIBue(! zn?1a_U&vl{le*@|b0&ONY(IOVM{T?>ua69%{}f8wvt0xa2zR6Yf$>wZyJ7OQXnNYX z5x_=!sCYyIwPVGduUkd&9mo2dO9tK3Z6bubH6QdG_SJmVL9)_CZ3x z$|z)7>Z*es=1~CQ@fF-6g(rXONUm9IDyDnHklZ_(I_lJ3g5S48R3th$kw z087fN37BD^LcR~J)l#)dW83Ed#Hcl!WuRU3IHz`bN%ov-5vcofiCJ7!zj!yMz7is= z&s&MDS)mf;?04?5&VWt*&&l`D`H})jj2Ar7#`_VpvVb`cMI?ZZ+xk*+trMUox|&bT zqAWfr@T9_kwrVFVQG%P{$?V~?3r=`;h;u`-Qt4Cl;GJE{mlF4n8KB=V;qEqZlh3>Z zMm~{IK!KqOJkOyRt}Vxl4ZvL;X5#yrVKAs0{ocJY-8Q|#6vAxhZl8bt+VO*|(FJSSO@ zkRzOKV0Hp1>?0Z-PmwL%#|n*R z+})ua_Xw`)-WPMs>blkkx?=LTVU2SR(x(!8Bj7U2o*M;iLzo)KLk41W(e_)d0$|Q` zD{pfJY+J$h&7dvXGSN4cJ*5FTR? z8@czG{tU9~VM*3JP$EyO(NxA+m6D5b>y^CEKI0hsjOBjCd{X)iBf+uO7gPO*bM`s5 zshb%zE`S$cU2zZ2>#Y&OM!6p_cY%eBYPJYbD%(DmMQ`2s7+EtF$)zfS`o$gvV&ME= zhvB{1?sLBzh(#ed_3TY2|28cW*I#2N+BYv5^p6#wf2h*-JHAv74Vl74L519lw%W)5 zbMjf}D{5ml40l;giUV2seG(wB-?^K;`QnW|Su@|o%Jg0s#`?+6<=G=Mqc~Fpg9YMa7x47LaLq1)adXeO_abo17Wc{0bGO8Mdsbu@eY2`J-y)knk%d+j3A!CZ1Jik4w;;nCWgw&VqzaT3pB?HJ zs1kc|V6-G%DV>{1)$zmeF0!SX+(?c!t3*`J3EcGnW9C|dSBz!b7OUSg0X24k7$J26 z)xL}+#CTrGYQ)v#q(kSu1)$7 z&Xw4oe6@62ByrCFYU~`A&sM6Z7yfIGb00bOJs8I#5_jqoKliSm7UH&MWaS)~%Gk|U z;J$)grz*)qJG*o67_GPZ?=djN?8%&g{G{C1jP$1?2kK0K%Z!6qWWZe6h*=CIS(&}c zCMxp9f{b;=QjLm+8MpG2W$S?@*xZ*cd&r@C^7}WO%ojIZ@sN6J2Tu*8Adrw0LIlvF zGJi8#K{E>mvP?be$-eBfyZ7LsjzPsH&cs{-Nkuk@YiN19LND_f#d)kGUFsY3p3ZR)j05~6w+9|lz=^O(7cbleQTHGB2d!D{WU zc-HR#cM?~H{=Riz%ia3~*vso<1L#*~&v9?|rnHL2SUN4Ob8-C80X&Mw>SVE_U)`Ej zA6BcJytrxXvoiRKMOza1?))JV_O|RqYZ-MErxB9waI#I02?LZ^llp|%<~rVIKoJB5 z5Z5rGCVT?6kn}@KCP|oM_ROMlku=|DV)f#a-XH!fS}Z3+~CBLU~e`=OWYxADGnRpM?U+ijv}l?D1OvuP6oYScg6 zw7NIjMg~k5wQ>S%7I50eg-ZFXgFFHk+ce8d^ZWg`f|VPf_n|;LHq7Y=R2n%CW5f%1 zseRTPpH*7S;A8}cs0SP5zz`|E3;I1H@|m@m1(g0Whis~#=`-(Qv52Zvcr1+cnrCI> zjO4ozSH6zpux^1W!Oy^*l6{PT5%ZoAqUYH2mD`7uKXgDxQ z2k0LPoZ~*n;6}u`P_$h`@mNqI$hME0vJgT9>0nFg>4$z*+ouC&`J|HnuzO{%Y)3n9 zYl~i{94@vC3jodt+BmTF)RjFe{vOWRBkC0$sIFTQ_)7J83xJV{|B`GrEwwhS%ud&WNSS;B zO0Do*WS5SEZStn4^8&I($sw?%^fH>mz6f;^uARxLV z3AXpCcR+a9w+BJVdPQ99Ymt?6y65FuHnsc!9noS-)k8aG(a-DUlU;hd_s1`v{_eS; zGr-2V5(26}wfh||J6?dG1=~1zN2)SjgH~4$s;pDx>Hcb+0_E(!&BWuPGW#?0Zl$ zkKl88Et=?^pGbquKRg1ciab#M*VIytRAH>W)fi2mJ9Ri#X^^$8gjE9B_a?%k0bDJ8 z9@_@#J#WE0ZU$%8x||F-lT^Bn7w}e+FqGYmLE*w85BOpRro7#3HwRI;R?jf z+&TcJLIM%Unf5oY%`?9!lP(>|5wxxLp?k&@bq`fHWz42Qc-Dn(lUKYdG!2LxXj%jU z>}$xaD4Iwmp=#Y${ml%-ohZ!P1l5DRpRb`0uJS`4`Z=;W{^qIM#=Z;T8d>*9IpzsD zoymm>NGv|>5cg=C)Xz`9Afla-NSr0Tjq2WAgHP*}iV+$Bs$(!KWYK#9G*JAGGP?tC zqyaU_x-$S}YZ)!eAU0gqw#bJ(%Gz6WoY1kkn-;dS`TQ7pB3cTJW_xpRN5@_uVO1Ir zCCnz6Ud&lr1Yr(UTKSv|{&hgf(jx%F*4Eb~u~VJ0gS@zFY$iW!6kWRALK!=W`| zFWG0nWd#Xz7W{ZmwW)kOdiHW#xvsC@bK0L)&CE4!#1o$HAG!t9gc}Vh-47il`tR+{ z?DK=o+AV$IsVaLl44=n5Y0Xtm49jOP2>l}h=r>^HyZ&$kNc4ogI*Gwig3a$RfpIm4 z%?XcN5b`xYi&w@?2Z{~fd-LYCy|RZxkne8V`_3(Gez)yP+&KVZz*cn&M!23ZoB#_nmtCEE0!KLA?XgeE>qiIQ#pw5A(@_jrXUX``nF) zf=%D0*Bv$DOmn%u>~l*QyP3|2(_;nkQ3ol570T!~^^ltdec=`e##sRNiJ~)%d%K7G zGob`7MBZ4rzMIYf&c|p7fb|4WTavkzD)u4?Ko!^#XzLi)0jQ8FkuN+;YOrbsMjAxQ z>d}4};^?bYXgQ-wRqZ*KdHl8nAF ziT3jPbOAcHOuT!6_kwS=D$#cJ;HzQ2>rOSOe`B^Uxa-b0g77E}on-8t1Zd@BfZ69? zd~UC8XV^2RyD0w-0 zcP&qSvknRVGf}uwPrLwK%kM>B`2wC95DkEHgIQ}D2(sTxZ+8oNUJ=`vMP>1bl(wU6 zH8>H{_YN=jYf&;h(ie~=`@*lA9S7|Rw?r5&P*Pur5X(VW1ojnk!HAlr0tXm-wvG4R zQ_;SpuX_;4pul}C>88E#rmHX5J=ZL$A`t?K74SRv{0}V!Z3OLYxjvu*lDZ8bN*Z8_ zeG#~6bj}>zwMGHihV4Sl%s(@G_1SIVG;M{iH3W|{qnyTtp2G0^bMfu+5ES! z0G5j-i6CI-8^YIl={?@fzLElZzGLV~KmTHX z1m?R7Si#Py)zwmtI5xRcf^Pwjor>|2NhE6U{c~av$wL6njbzhZ_avKDvZTJ2#P(w&!9{Aw^Nb=NzgBj#ZdGc2PY^RSb~twZ^ykZaMGz9XrA0c3O{)%Qa!rTK~EgL1pc^>oaigFUsne)|S(r;Ae+ z#;HYXZ)RVaz5dFFlK#8AaKraJ$Ljr@Wq^MWhQ zYZ4+$zaGF>#=#?|bL#+X=}Es<&@QPmfG58Tkn_$T?2BK2X8R{x%6P7V;ag7KLwW%V z^vwL`tN;=#W??q3}OX9~v%-Le$%yP3T|uC2QxU`MC$Zfk~{ z0&sL9Z>CGvhk&b}$G)!jV~ObTnqll$w+NiiL8!%|MMnQS?-vrNM#mBPN-Ikts{mjm zfF~%8SKB-Tj<)tZ&z5Ye4M43HO}L(fCDkg}MNHa7s`Et->uCx_YMqXbZ@Jcgq5j#ZgJArVbT!|@}>2cY7 z2T>g=z9wsbjmpoIR~SFKK4zorLJF|oIRZcBV3w=C zbt*g*{8x!kR(sOTbQ!(^b__VqfpD{2EDfTsJt}jaGs_KuUw5C%Cd%~E9w7d`pb2`pHpoa1IoVg$lYIv1YssD!&Uzgg7xSsV30e2JqF2;4_Fxu+kR0mH@WE27ulT^D9kS=U z&fj^uq~}sXAA>i?rG(rYxwpH((ub^km+d;1R5v#&2q(cWwu9qJ7J3b6PvnSCzlHdI z?Y%6U&w6DeMREny&0-()te>16Tcg=L-79Yacnt;BOsl>R;L&1>F=~ls+7~|ktva8C z{Ges+0)$GiZd`>vQXw}&NJO?`AX(>%RQsr?-dmDEx-C#WhK%?)uMfTeQF`B>?*k8Y z6;`mks?96WNF~a~Vl7FOCBzHUQ~->&;H(F*mcKKAjBZU=dP~c`+ym_svL*-G0*K<& zof+Q2-u#8x!|VGsa$l1=@--u)hn*zDUdq@%1%O_dQ9sq}d4vP!#iKNjvwrZbbUPWd z`!QP>`+oh3u|{!F@5H$*R`Y(aR(t*WmHqOszO;9?x7~wZwcWhL+Y3OyURW=O&NY3& zp%2n5=3IVv*bl5Gp+@rgu749bm`5`JeaW+$NM4~EE-K=R*oe#XUF6UixDHg`%?8mj z4)zgVWst=rAAvb*KB3x>ieYnDRff1A5+Fc2=v9E@0=OSXCGm4$(K=9m@=gcLnvVgs z2SReo-;llL%F1#qktcz7NR{OlL$UCi?y9VmtZz9MOM>T0sWBCJGv1q>f_$q9*HIDx znna;K;uZh^6QJv7bN)N<@G_(Gq1i@Kv0E3m7iUC@36R*$n_*q6<1WcEsE1N-j zPtS`gbas3=UXq!g$+~FYwA9}1qP}jr!t`wACKdxJqS$Y9XSV;&?A2emJ-m8`Z1Xip zJ*nMieg2WEd*6rt^7=Ug^jpfu?~y&f!_5H{veF2Y=lzc76j-HPEk+Tg{^9Wrf5W&h zCF@&y-CYM*^FCg^dSzez;&Xd%d-I*kYbjA-D2+Js8&&+?hf;A-n&I(YPFYOzxrf_>>!xNK=2&X^ZCfnocpC+1#{YQJ zc||#X^>fcFKi2uQUTLJ)7Q5q9<(Ngs?x@@jbSmw3wqrXzg|I}nL67u6;{4`b@=U-DBG_c&aSKqY z0966VJpkr_2BH=zzAMnzw{3tEBC`^?%Kj;(c?5p!OOo-){sq(k&|a_cE7_~*WW!}li1h1R9e0OcaUp#(d&?NBpUDe zXW87eehmR3|3t+b`iCQEjMs!vrqohU*JWq6|97*`{?hE>jXiVr@rszSYQ+YQDb50N zFW~$m0O)rhgFfu++l5K)qaDCSBDBq0D^J4^mITf+K>kEKJHoq+rkHY|N z_^7TS7E{;N^g|B#<34p|`NqL==BJ*O{2B2JSgSRZD(*BFT?@(e;PUKI{Ob5*d2i?W zcd%E(MeY3*gS8PPGcex&-AHgatG8o9RP6gv0}1o4z6!8s;%#PBUa<%174f(*TV6N@ z)dsKtbzzmZ0Mj|^XmS6q*(&%ZfjDxo-loU4>S3o~t`YGsi~D_1I6r3F#6;2q9PWe#I#q@g7`F zbq?Yxz0TT6+~v8j9PJFN`z*9e@iI_QEpx{1oY*ie6qtIV`wY@yw2a1RI8`~Kd?wqE+^TFfQc^6AZi zaXV$g%~v3X6s-GoZM5*0M91EJ>wcpvy2Yhk4**>4(VaE?IHeY`1I`92k<3`u^C zw0kQ!BQ7AUnvhjSy!q;ObbI#%rdljKSdW4d_h_77UO#n!jvFQ)>?bp`m57Ox$=t=! zhe_;rIxbw%3UG@c`BVe#eusS~ARPC+e3K&OS6_X3yha#$e?zjvD`4L5$p=}@2|brh zi}#C7zhrQbv(%B@8A`M+2rO7K)y}{pN@r}&zu)FQY}cULBn6_x-4Yo;S99QU{#*dg zjg>1iSRDHas6zW_9wKlZz@-FH91SLKc}6Sw`Tut*)1ye-x%8oR$2fuO^Agq$;%=D0 zI8$Ut_uEE*$jM`OUh_}3Un`kf??<-+IA?v|W>GfXxxa6rKRqK8*zXw-Wk8z>NeO^V zEs~UvbOB(R1;F2UdAnlLJbPkMux^Rcyw%j(MQ^8EPBIX<60 z`<36d47?Km%mt@>r`n-J0w3xTsc(vvm$c2UT#gy0U}bqTa9fG-2;rIPpV_#0FZ&I5N!0GKWxmp#qO!VaT!Vx@;x zsg*M@!ph>CATx&FG?O0KhRo+#gHcKdWa)Ycu|3o3?LZ>p7tJ9ihL&XCFa6bPn^JYG zLec|CUv;5$e(vLD9_SXi3Lri{c^s$lX=F}f)o)P3k6Aw=yGM0j>&=_b?2Wy$*08oBl*iB$ zsN)F)TEP!C1K6x=JCe970{0$a^pdL*A2LZEOV&sA?ONhYk44KMBr95X{`;u(TZdcq4`Q;no_H8S8-X1+gy{TZYW8E{_%55#K1XP|vt%SWH|u3kd*nm4k|`ER>Eo8F(#(3?(r1YxUY z1}&w}MBA!B-3lt}V!ulk8iBXCdJl2-eF5~1D4{Q9v9?xbm@Z?d-qMNU`>FKU3M%5u zwhKT!1dl?QJxdJD*i)brf5zj*7|vasZSIYy^dW)%@c<)FaFVXB`}Uh~7eI;x)w z-*N;YMpOYJf%D+?4lcAvtOtfZcN6{J#Kv%m2*8fThRT7Zf(8)cnk3?3B;DyehttA& zfn;lkWw|uO4mNNzJej@z8@JbQ{CRQ8J+RYO_oNR5!Cqd^4bcBw!23Rp!1Y6M2G6>T zPNr0m?eE@qK>=_N9~SJ@aK*8PRROr~9D%_;|NL|N>i_6Z> zZ5Twco(}0JJ5LahMJSx(zJ-!eAe$e3m{)g8<>`90c03%x@6mSc(D#Q$Fm42W z;K01orSJ)8_c$CQCLe{3j99k$s_4(u;FSPbl+9LuLjl(XOnICoN4?iv{*8ELP>Sso zJVTerhVNk|FtX~ssEl_|o)_B;h2EgQ*Q>oNm1tSIAC&V`A7^EkrU0JuPdD8c9#$@h zGI?%&=G0dlQY{`F0NYeuBC_VxmM&GKU$fUsnYm52jhhJzVrgcR_qRpb0a(_)2y{0Q zc__Qd)>krcY<$~& zZn6n~>ork;o_l%SwxCI6T-fKIe`de@4`0~h@0x}5+J4|7s##jPM{halU&!3wV1MiVqZmJOQk$=q4~ z1e{OAz%0=g5F{IX(bo*2cClB)f+fIw39gZuA-`=UW4}oJtZgLSCZf~?~m00LgrA}w^`wD1$1YM3*yyv*0H!TbWzRC?ReLiNEY7G`xasU^MUkBXpasn1 zQX&lwu3O}ZPC&)HouP7qd@mFFx$pPaBWs7!$%#cFQCeDLAl8HeFpHbT*2MQwQhm(bO_QdJ_iQCjg-in5)D%; z6$F?^pd4@7yPJBqC&yBXYXpE_?zarA4+`hURR(iQklsH8=#5I)BPD%ld6s=|__{B> z^c79MW|I&4FyB$L#|z0gC-WRMi=U+qA`7(W8Jt&bbz z))zNx?_qC#;kJ8qdj|bOm|4xOtV?q7<@J#P^yhrxd4CfD>JS@drR>hJp3I1EB^xre zNbl_5(rwN)ad*6Y!(c5nz4s*>{j$^D?!kWXtFP?o80u!$X(&MQco@fQr@nlkpeB0| zW`Ggp3_FfsFJfZiZuP3^TL$Xs**Om>AfbDF-RAiLc~vn?N(7d1@PvUQucqAK!xD4m z5&#(}&1^T5Gi>Twd*-h^(<|AW^;+=d<2xLzz_6dh<4(6*r}pmqjt>O++qhuT!Xq}( zCv7uG$EEDuN5HtrhO8WGCn0hLG0q(x^ z^RYQR2ep_yb}b0tGoW4u$KARI3LuaJ^&~60sT3YMcxC{k2Yiiz-$jrr$g~&y;Fop* zVWCFL`jsv~PS}4Nvnq)E(;_T%}BA&rQcM{P&37SUmxA$8Qkmzmdm+g-|$d7szf6bbjw6 z_+KESE0C^^W$oF|W@Ol7XqQs_apaFX+1pvU-Jgy?%@3aGXIr(t)O{AD@=I#^yq$L1 zr<)&7X3n~+zVzNF9Su~+WGgWaKtW%Da1sDF_e@fecl$)=2h5Iu)JyT>8 zvEjlLEX6?HsRKrMtd`rBX>!&F)dpEX0e-(V6O{sKX}Y?1T*pmViH&Ajk$xm_AcO{? zRsEqWK-fhH%lZSK@%4CO}wrv26PM_ZDzG0{>Y&&SP+HMGthbx`dxa{^m$p%S-@#;oDv5 z7tc@VDix_bo)c5qu|%z8yevO>T2BiJEj`ERDsaBG%|`EVAq2O0!?#me zKCc7{Df{U@8RsX4`@2o&vh?r!NSZXma9quJ2|fmrrMGXJ*6e2He{J^J7yj%9Ot<_- zRsHsLv*^0DT-ZXtygmYeo<2zbOxg2I$Cap&&!z5eJb4Nvch0pX0PJy>+> z1uQqiHSUK# z1C*$rTUoL(ki5)w)lWWWxm&RB0F)}ST=iQ6NxA;%m`~aJW;%O1wHjC>0M5pWjb4y+ zz0MecA7N0GwnRzr4g>%iN+Mw+0fe!%o!RP!^JuD$V|H#tFi??G*3Ip9`me2-eIQeU zSGo(j3?0y}NOKvwA|BiPJ`RfVtq0r4H3y=~6a=wj9Hw;7sJ)s02eU8#%I_wweAiIf z>!GiElH_}NeH;M&SuZS}|7Y%rcnrI*w*qnBa8MbOGfE)vZueMLZz$n&Ou&5v?aVkajW_fw}sP-NUJ*jAdpLKqt6m*W3(P<_Z`gn zdnA1C=GY5AAb2<_0>aO^UPe2>drfd^K0LKWO9ZgF(g2`tRCop?~9hh z{JQi|Pram_U0GXx<5Nkcld-@s1|jA0gfYwAI$!jYzd5FLdJ&K7g@y=7CxUTZf?lfL z&d-O%Z+ZC}IUzG&W7NdrvJ&`9zkiUqx1tE0T+@^afsxshJ3v;hI z!rzYfieEtMbj-5v99&g?9Op?>L)zwnR*snIc9HN&HW4LffZbUYDjPUi%~K$QW-5CG z%#bSVOe=)N5LnFSNp(}OrmJUBdo=s(uiak1c5ChS9__6v?!IArQu#Uo=BK}3`puSQHT#dn7H|gL&GPerk@Z3zYezd!O$AKvBY&m?a(x^c z1gW?rmi1V*5J|FT2it!S`{MuN_Ssi{AlB{(VsFi5N=+${+?=oA9M2?N{qp)V0CarF zS$XeGfwM>MCbLukNvOI>|MOA{Zk4tpcZ&PQ-_rM8-h}Yjf0MDmw*n+=M)sG%0AO$4 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 literal 0 HcmV?d00001 diff --git a/apps/dokploy/templates/spacedrive/docker-compose.yml b/apps/dokploy/templates/spacedrive/docker-compose.yml new file mode 100644 index 00000000..b98d55ab --- /dev/null +++ b/apps/dokploy/templates/spacedrive/docker-compose.yml @@ -0,0 +1,9 @@ +services: + server: + image: ghcr.io/spacedriveapp/spacedrive/server:latest + ports: + - 8080 + environment: + - SD_AUTH=${SD_USERNAME}:${SD_PASSWORD} + volumes: + - /var/spacedrive:/var/spacedrive diff --git a/apps/dokploy/templates/spacedrive/index.ts b/apps/dokploy/templates/spacedrive/index.ts new file mode 100644 index 00000000..b599ee0e --- /dev/null +++ b/apps/dokploy/templates/spacedrive/index.ts @@ -0,0 +1,31 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, + generatePassword, +} from "../utils"; + +export function generate(schema: Schema): Template { + const randomDomain = generateRandomDomain(schema); + const secretKey = generatePassword(); + const randomUsername = "admin"; // Default username + + const domains: DomainSchema[] = [ + { + host: randomDomain, + port: 8080, + serviceName: "server", + }, + ]; + + const envs = [ + `SD_USERNAME=${randomUsername}`, + `SD_PASSWORD=${secretKey}`, + ]; + + return { + envs, + domains, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 1bdb1ec1..e89aa524 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -538,7 +538,7 @@ export const templates: TemplateData[] = [ website: "https://filebrowser.org/", docs: "https://filebrowser.org/", }, - tags: ["file", "manager"], + tags: ["file-manager","storage"], load: () => import("./filebrowser/index").then((m) => m.generate), }, { @@ -834,7 +834,7 @@ export const templates: TemplateData[] = [ website: "https://nextcloud.com/", docs: "https://docs.nextcloud.com/", }, - tags: ["file", "sync"], + tags: ["file-manager", "sync"], load: () => import("./nextcloud-aio/index").then((m) => m.generate), }, { @@ -1341,4 +1341,19 @@ export const templates: TemplateData[] = [ tags: ["dashboard", "monitoring"], load: () => import("./homarr/index").then((m) => m.generate), }, + { + id: "spacedrive", + name: "Spacedrive", + version: "latest", + description: + "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", + links: { + github: "https://github.com/spacedriveapp/spacedrive", + website: "https://spacedrive.com/", + docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", + }, + logo: "spacedrive.png", + tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), + }, ]; From fd570ff861e6e76b6053148234606b6b2a005f5b Mon Sep 17 00:00:00 2001 From: Freilyn Bernabe Date: Thu, 23 Jan 2025 23:41:58 -0400 Subject: [PATCH 29/61] feat(template): added maybe finance --- apps/dokploy/public/templates/maybe.svg | 17 ++++++++ .../templates/maybe/docker-compose.yml | 37 ++++++++++++++++ apps/dokploy/templates/maybe/index.ts | 43 +++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 +++++++ 4 files changed, 112 insertions(+) create mode 100644 apps/dokploy/public/templates/maybe.svg create mode 100644 apps/dokploy/templates/maybe/docker-compose.yml create mode 100644 apps/dokploy/templates/maybe/index.ts diff --git a/apps/dokploy/public/templates/maybe.svg b/apps/dokploy/public/templates/maybe.svg new file mode 100644 index 00000000..a4a87736 --- /dev/null +++ b/apps/dokploy/public/templates/maybe.svg @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/maybe/docker-compose.yml b/apps/dokploy/templates/maybe/docker-compose.yml new file mode 100644 index 00000000..84ec297e --- /dev/null +++ b/apps/dokploy/templates/maybe/docker-compose.yml @@ -0,0 +1,37 @@ +services: + app: + image: ghcr.io/maybe-finance/maybe:sha-68c570eed8810fd59b5b33cca51bbad5eabb4cb4 + restart: unless-stopped + volumes: + - ./uploads:/app/uploads + environment: + DATABASE_URL: postgresql://maybe:maybe@db:5432/maybe + SECRET_KEY_BASE: ${SECRET_KEY_BASE} + SELF_HOSTED: true + SYNTH_API_KEY: ${SYNTH_API_KEY} + RAILS_FORCE_SSL: "false" + RAILS_ASSUME_SSL: "false" + GOOD_JOB_EXECUTION_MODE: async + depends_on: + db: + condition: service_healthy + + db: + image: postgres:16 + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - dokploy-network + volumes: + - db-data:/var/lib/postgresql/data + environment: + POSTGRES_DB: maybe + POSTGRES_USER: maybe + POSTGRES_PASSWORD: maybe + +volumes: + db-data: diff --git a/apps/dokploy/templates/maybe/index.ts b/apps/dokploy/templates/maybe/index.ts new file mode 100644 index 00000000..5eaf7a81 --- /dev/null +++ b/apps/dokploy/templates/maybe/index.ts @@ -0,0 +1,43 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const secretKeyBase = generateBase64(64); + const synthApiKey = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "app", + }, + ]; + + const envs = [ + `SECRET_KEY_BASE=${secretKeyBase}`, + "SELF_HOSTED=true", + `SYNTH_API_KEY=${synthApiKey}`, + "RAILS_FORCE_SSL=false", + "RAILS_ASSUME_SSL=false", + "GOOD_JOB_EXECUTION_MODE=async", + ]; + + const mounts: Template["mounts"] = [ + { + filePath: "./uploads", + content: "This is where user uploads will be stored", + }, + ]; + + return { + envs, + mounts, + domains, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 1bdb1ec1..8291eb66 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1341,4 +1341,19 @@ export const templates: TemplateData[] = [ tags: ["dashboard", "monitoring"], load: () => import("./homarr/index").then((m) => m.generate), }, + { + id: "maybe", + name: "Maybe", + version: "latest", + description: + "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", + logo: "maybe.svg", + links: { + github: "https://github.com/maybe-finance/maybe", + website: "https://maybe.finance/", + docs: "https://docs.maybe.finance/", + }, + tags: ["finance", "self-hosted"], + load: () => import("./maybe/index").then((m) => m.generate), + }, ]; From 52ea9ffa670cd4a799c39a23c496dc2e77c8abe6 Mon Sep 17 00:00:00 2001 From: Rahadi Jalu Date: Fri, 24 Jan 2025 15:05:02 +0700 Subject: [PATCH 30/61] fix(ui): add condition to show update server button to admin only --- apps/dokploy/components/layouts/side.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index c711b862..559fe182 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -783,7 +783,7 @@ export default function Page({ children }: Props) { ))} - {!isCloud && ( + {!isCloud && auth?.rol === "admin" && ( From d7210e9d7be93d0c64642ace370d18242a8b348e Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Sat, 25 Jan 2025 00:42:46 +0530 Subject: [PATCH 31/61] fix: terminal paste visibility --- .../dashboard/settings/web-server/terminal.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx index e45b73d2..402ce57d 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx @@ -32,10 +32,14 @@ export const Terminal: React.FC = ({ id, serverId }) => { lineHeight: 1.4, convertEol: true, theme: { - cursor: resolvedTheme === "light" ? "#000000" : "transparent", - background: "rgba(0, 0, 0, 0)", - foreground: "currentColor", + cursor: resolvedTheme === "light" ? "#000000" : "#FFFFFF", + background: resolvedTheme === "light" ? "#FFFFFF" : "rgba(0, 0, 0, 0.9)", + foreground: resolvedTheme === "light" ? "#000000" : "#FFFFFF", + selectionForeground: resolvedTheme === "light" ? "#000000" : "#FFFFFF", }, + allowTransparency: true, + screenReaderMode: true, + scrollback: 1000, }); const addonFit = new FitAddon(); @@ -68,7 +72,7 @@ export const Terminal: React.FC = ({ id, serverId }) => { return (

-
+
From ac49235916937252c1e22b4e97e9c3430c9ad337 Mon Sep 17 00:00:00 2001 From: Vyacheslav Shcherbinin Date: Sat, 25 Jan 2025 10:51:22 +0700 Subject: [PATCH 32/61] Pull Request actions filter --- apps/dokploy/pages/api/deploy/github.ts | 141 ++++++++++++------------ 1 file changed, 72 insertions(+), 69 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index ff7a9221..0cb7cf36 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -201,79 +201,82 @@ export default async function handler( res.status(200).json({ message: "Preview Deployment Closed" }); return; } + // opened or synchronize or reopened - const repository = githubBody?.repository?.name; - const deploymentHash = githubBody?.pull_request?.head?.sha; - const branch = githubBody?.pull_request?.base?.ref; - const owner = githubBody?.repository?.owner?.login; + if (githubBody?.action === "opened" || githubBody?.action === "synchronize" || githubBody?.action === "reopened") { + const repository = githubBody?.repository?.name; + const deploymentHash = githubBody?.pull_request?.head?.sha; + const branch = githubBody?.pull_request?.base?.ref; + const owner = githubBody?.repository?.owner?.login; - const apps = await db.query.applications.findMany({ - where: and( - eq(applications.sourceType, "github"), - eq(applications.repository, repository), - eq(applications.branch, branch), - eq(applications.isPreviewDeploymentsActive, true), - eq(applications.owner, owner), - ), - with: { - previewDeployments: true, - }, - }); - - const prBranch = githubBody?.pull_request?.head?.ref; - - const prNumber = githubBody?.pull_request?.number; - const prTitle = githubBody?.pull_request?.title; - const prURL = githubBody?.pull_request?.html_url; - - for (const app of apps) { - const previewLimit = app?.previewLimit || 0; - if (app?.previewDeployments?.length > previewLimit) { - continue; - } - const previewDeploymentResult = - await findPreviewDeploymentByApplicationId(app.applicationId, prId); - - let previewDeploymentId = - previewDeploymentResult?.previewDeploymentId || ""; - - if (!previewDeploymentResult) { - const previewDeployment = await createPreviewDeployment({ - applicationId: app.applicationId as string, - branch: prBranch, - pullRequestId: prId, - pullRequestNumber: prNumber, - pullRequestTitle: prTitle, - pullRequestURL: prURL, - }); - previewDeploymentId = previewDeployment.previewDeploymentId; - } - - const jobData: DeploymentJob = { - applicationId: app.applicationId as string, - titleLog: "Preview Deployment", - descriptionLog: `Hash: ${deploymentHash}`, - type: "deploy", - applicationType: "application-preview", - server: !!app.serverId, - previewDeploymentId, - }; - - if (IS_CLOUD && app.serverId) { - jobData.serverId = app.serverId; - await deploy(jobData); - return true; - } - await myQueue.add( - "deployments", - { ...jobData }, - { - removeOnComplete: true, - removeOnFail: true, + const apps = await db.query.applications.findMany({ + where: and( + eq(applications.sourceType, "github"), + eq(applications.repository, repository), + eq(applications.branch, branch), + eq(applications.isPreviewDeploymentsActive, true), + eq(applications.owner, owner), + ), + with: { + previewDeployments: true, }, - ); + }); + + const prBranch = githubBody?.pull_request?.head?.ref; + + const prNumber = githubBody?.pull_request?.number; + const prTitle = githubBody?.pull_request?.title; + const prURL = githubBody?.pull_request?.html_url; + + for (const app of apps) { + const previewLimit = app?.previewLimit || 0; + if (app?.previewDeployments?.length > previewLimit) { + continue; + } + const previewDeploymentResult = + await findPreviewDeploymentByApplicationId(app.applicationId, prId); + + let previewDeploymentId = + previewDeploymentResult?.previewDeploymentId || ""; + + if (!previewDeploymentResult) { + const previewDeployment = await createPreviewDeployment({ + applicationId: app.applicationId as string, + branch: prBranch, + pullRequestId: prId, + pullRequestNumber: prNumber, + pullRequestTitle: prTitle, + pullRequestURL: prURL, + }); + previewDeploymentId = previewDeployment.previewDeploymentId; + } + + const jobData: DeploymentJob = { + applicationId: app.applicationId as string, + titleLog: "Preview Deployment", + descriptionLog: `Hash: ${deploymentHash}`, + type: "deploy", + applicationType: "application-preview", + server: !!app.serverId, + previewDeploymentId, + }; + + if (IS_CLOUD && app.serverId) { + jobData.serverId = app.serverId; + await deploy(jobData); + return true; + } + await myQueue.add( + "deployments", + { ...jobData }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); + } + return res.status(200).json({ message: "Apps Deployed" }); } - return res.status(200).json({ message: "Apps Deployed" }); } return res.status(400).json({ message: "No Actions matched" }); From adfe5986711445f9d425303a345f138cd055b4a8 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Sat, 25 Jan 2025 19:14:58 +1100 Subject: [PATCH 33/61] fix(template): prepend superset_ to postgres/redis --- .../templates/superset/docker-compose.yml | 25 +++++++++++-------- apps/dokploy/templates/superset/index.ts | 4 +-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/apps/dokploy/templates/superset/docker-compose.yml b/apps/dokploy/templates/superset/docker-compose.yml index 1766b86b..e3fb3454 100644 --- a/apps/dokploy/templates/superset/docker-compose.yml +++ b/apps/dokploy/templates/superset/docker-compose.yml @@ -13,8 +13,11 @@ services: image: amancevice/superset restart: always depends_on: - - db - - redis + - superset_postgres + - superset_redis + volumes: + # This superset_config.py can be edited in Dokploy's UI Advanced -> Volume Mount + - ../files/superset/superset_config.py:/etc/superset/superset_config.py environment: SECRET_KEY: ${SECRET_KEY} MAPBOX_API_KEY: ${MAPBOX_API_KEY} @@ -22,11 +25,11 @@ services: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} REDIS_PASSWORD: ${REDIS_PASSWORD} - volumes: - # Note: superset_config.py can be edited in Dokploy's UI Volume Mount - - ../files/superset/superset_config.py:/etc/superset/superset_config.py + # Ensure the hosts matches your service names below. + POSTGRES_HOST: superset_postgres + REDIS_HOST: superset_redis - db: + superset_postgres: image: postgres restart: always environment: @@ -34,7 +37,7 @@ services: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} volumes: - - postgres:/var/lib/postgresql/data + - superset_postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] interval: 30s @@ -43,11 +46,11 @@ services: networks: - dokploy-network - redis: + superset_redis: image: redis restart: always volumes: - - redis:/data + - superset_redis_data:/data command: redis-server --requirepass ${REDIS_PASSWORD} healthcheck: test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] @@ -58,5 +61,5 @@ services: - dokploy-network volumes: - postgres: - redis: + superset_postgres_data: + superset_redis_data: diff --git a/apps/dokploy/templates/superset/index.ts b/apps/dokploy/templates/superset/index.ts index 6132f978..4994b639 100644 --- a/apps/dokploy/templates/superset/index.ts +++ b/apps/dokploy/templates/superset/index.ts @@ -47,13 +47,13 @@ CACHE_CONFIG = { "CACHE_REDIS_HOST": "redis", "CACHE_REDIS_PORT": 6379, "CACHE_REDIS_DB": 1, - "CACHE_REDIS_URL": f"redis://:{os.getenv('REDIS_PASSWORD')}@redis:6379/1", + "CACHE_REDIS_URL": f"redis://:{os.getenv('REDIS_PASSWORD')}@{os.getenv('REDIS_HOST')}:6379/1", } FILTER_STATE_CACHE_CONFIG = {**CACHE_CONFIG, "CACHE_KEY_PREFIX": "superset_filter_"} EXPLORE_FORM_DATA_CACHE_CONFIG = {**CACHE_CONFIG, "CACHE_KEY_PREFIX": "superset_explore_form_"} -SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{os.getenv('POSTGRES_USER')}:{os.getenv('POSTGRES_PASSWORD')}@db:5432/{os.getenv('POSTGRES_DB')}" +SQLALCHEMY_DATABASE_URI = f"postgresql+psycopg2://{os.getenv('POSTGRES_USER')}:{os.getenv('POSTGRES_PASSWORD')}@{os.getenv('POSTGRES_HOST')}:5432/{os.getenv('POSTGRES_DB')}" SQLALCHEMY_TRACK_MODIFICATIONS = True `.trim(), }, From 829e77a6b835d48334d5f856bbc2a5128ce6d1ac Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Sun, 26 Jan 2025 21:50:10 +0530 Subject: [PATCH 34/61] feat: add erpnext template Co-authored-by: Revant --- apps/dokploy/public/templates/erpnext.svg | 5 + .../templates/erpnext/docker-compose.yml | 346 ++++++++++++++++++ apps/dokploy/templates/erpnext/index.ts | 37 ++ apps/dokploy/templates/templates.ts | 22 ++ 4 files changed, 410 insertions(+) create mode 100644 apps/dokploy/public/templates/erpnext.svg create mode 100644 apps/dokploy/templates/erpnext/docker-compose.yml create mode 100644 apps/dokploy/templates/erpnext/index.ts diff --git a/apps/dokploy/public/templates/erpnext.svg b/apps/dokploy/public/templates/erpnext.svg new file mode 100644 index 00000000..d699ea2a --- /dev/null +++ b/apps/dokploy/public/templates/erpnext.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/dokploy/templates/erpnext/docker-compose.yml b/apps/dokploy/templates/erpnext/docker-compose.yml new file mode 100644 index 00000000..def916be --- /dev/null +++ b/apps/dokploy/templates/erpnext/docker-compose.yml @@ -0,0 +1,346 @@ +x-custom-image: &custom_image + image: ${IMAGE_NAME:-docker.io/frappe/erpnext}:${VERSION:-version-15} + pull_policy: ${PULL_POLICY:-always} + deploy: + restart_policy: + condition: always + +services: + backend: + <<: *custom_image + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + healthcheck: + test: + - CMD + - wait-for-it + - '0.0.0.0:8000' + interval: 2s + timeout: 10s + retries: 30 + + frontend: + <<: *custom_image + command: + - nginx-entrypoint.sh + depends_on: + backend: + condition: service_started + required: true + websocket: + condition: service_started + required: true + environment: + BACKEND: backend:8000 + FRAPPE_SITE_NAME_HEADER: ${FRAPPE_SITE_NAME_HEADER:-$$host} + SOCKETIO: websocket:9000 + UPSTREAM_REAL_IP_ADDRESS: 127.0.0.1 + UPSTREAM_REAL_IP_HEADER: X-Forwarded-For + UPSTREAM_REAL_IP_RECURSIVE: "off" + volumes: + - sites:/home/frappe/frappe-bench/sites + + networks: + - bench-network + + healthcheck: + test: + - CMD + - wait-for-it + - '0.0.0.0:8080' + interval: 2s + timeout: 30s + retries: 30 + + queue-default: + <<: *custom_image + command: + - bench + - worker + - --queue + - default + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + queue-long: + <<: *custom_image + command: + - bench + - worker + - --queue + - long + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + queue-short: + <<: *custom_image + command: + - bench + - worker + - --queue + - short + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + depends_on: + configurator: + condition: service_completed_successfully + required: true + + scheduler: + <<: *custom_image + healthcheck: + test: + - CMD + - wait-for-it + - 'redis-queue:6379' + interval: 2s + timeout: 10s + retries: 30 + command: + - bench + - schedule + depends_on: + configurator: + condition: service_completed_successfully + required: true + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + + websocket: + <<: *custom_image + healthcheck: + test: + - CMD + - wait-for-it + - '0.0.0.0:9000' + interval: 2s + timeout: 10s + retries: 30 + command: + - node + - /home/frappe/frappe-bench/apps/frappe/socketio.js + depends_on: + configurator: + condition: service_completed_successfully + required: true + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + + configurator: + <<: *custom_image + deploy: + mode: replicated + replicas: ${CONFIGURE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + [[ $${REGENERATE_APPS_TXT} == "1" ]] && ls -1 apps > sites/apps.txt; + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && exit 0; + bench set-config -g db_host $$DB_HOST; + bench set-config -gp db_port $$DB_PORT; + bench set-config -g redis_cache "redis://$$REDIS_CACHE"; + bench set-config -g redis_queue "redis://$$REDIS_QUEUE"; + bench set-config -g redis_socketio "redis://$$REDIS_QUEUE"; + bench set-config -gp socketio_port $$SOCKETIO_PORT; + environment: + DB_HOST: db + DB_PORT: "3306" + REDIS_CACHE: redis-cache:6379 + REDIS_QUEUE: redis-queue:6379 + SOCKETIO_PORT: "9000" + REGENERATE_APPS_TXT: "${REGENERATE_APPS_TXT:-0}" + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + + create-site: + <<: *custom_image + deploy: + mode: replicated + replicas: ${CREATE_SITE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + wait-for-it -t 120 db:3306; + wait-for-it -t 120 redis-cache:6379; + wait-for-it -t 120 redis-queue:6379; + export start=`date +%s`; + until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \ + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \ + [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]]; + do + echo "Waiting for sites/common_site_config.json to be created"; + sleep 5; + if (( `date +%s`-start > 120 )); then + echo "could not find sites/common_site_config.json with required keys"; + exit 1 + fi + done; + echo "sites/common_site_config.json found"; + [[ -d "sites/${SITE_NAME}" ]] && echo "${SITE_NAME} already exists" && exit 0; + bench new-site --mariadb-user-host-login-scope='%' --admin-password=$${ADMIN_PASSWORD} --db-root-username=root --db-root-password=$${DB_ROOT_PASSWORD} $${INSTALL_APP_ARGS} $${SITE_NAME}; + volumes: + - sites:/home/frappe/frappe-bench/sites + environment: + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + DB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + INSTALL_APP_ARGS: ${INSTALL_APP_ARGS} + SITE_NAME: ${SITE_NAME} + networks: + - bench-network + + migration: + <<: *custom_image + deploy: + mode: replicated + replicas: ${MIGRATE:-0} + restart_policy: + condition: none + entrypoint: ["bash", "-c"] + command: + - > + curl -f http://${SITE_NAME}:8080/api/method/ping || echo "Site busy" && exit 0; + bench --site all set-config -p maintenance_mode 1; + bench --site all set-config -p pause_scheduler 1; + bench --site all migrate; + bench --site all set-config -p maintenance_mode 0; + bench --site all set-config -p pause_scheduler 0; + volumes: + - sites:/home/frappe/frappe-bench/sites + networks: + - bench-network + + db: + image: mariadb:10.6 + deploy: + restart_policy: + condition: always + healthcheck: + test: mysqladmin ping -h localhost --password=${DB_ROOT_PASSWORD} + interval: 1s + retries: 20 + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --skip-character-set-client-handshake + - --skip-innodb-read-only-compressed + environment: + - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + - MARIADB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} + volumes: + - db-data:/var/lib/mysql + networks: + - bench-network + + redis-cache: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-cache-data:/data + networks: + - bench-network + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + + redis-queue: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-queue-data:/data + networks: + - bench-network + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + + redis-socketio: + deploy: + restart_policy: + condition: always + image: redis:6.2-alpine + volumes: + - redis-socketio-data:/data + networks: + - bench-network + healthcheck: + test: + - CMD + - redis-cli + - ping + interval: 5s + timeout: 5s + retries: 3 + +volumes: + db-data: + redis-cache-data: + redis-queue-data: + redis-socketio-data: + sites: + +networks: + bench-network: \ No newline at end of file diff --git a/apps/dokploy/templates/erpnext/index.ts b/apps/dokploy/templates/erpnext/index.ts new file mode 100644 index 00000000..0be46c44 --- /dev/null +++ b/apps/dokploy/templates/erpnext/index.ts @@ -0,0 +1,37 @@ +import { + type DomainSchema, + type Schema, + type Template, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const dbRootPassword = generatePassword(32); + const adminPassword = generatePassword(32); + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 8080, + serviceName: "frontend", + }, + ]; + + const envs = [ + `SITE_NAME=${mainDomain}`, + `ADMIN_PASSWORD=${adminPassword}`, + `DB_ROOT_PASSWORD=${dbRootPassword}`, + "MIGRATE=1", + "CREATE_SITE=1", + "CONFIGURE=1", + "REGENERATE_APPS_TXT=1", + "INSTALL_APP_ARGS=--install-app erpnext", + "IMAGE_NAME=docker.io/frappe/erpnext", + "VERSION=version-15", + "FRAPPE_SITE_NAME_HEADER=", + ]; + + return { envs, domains }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 1bdb1ec1..86867532 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1341,4 +1341,26 @@ export const templates: TemplateData[] = [ tags: ["dashboard", "monitoring"], load: () => import("./homarr/index").then((m) => m.generate), }, + { + id: "erpnext", + name: "ERPNext", + version: "version-15", + description: "100% Open Source and highly customizable ERP software.", + logo: "erpnext.svg", + links: { + github: "https://github.com/frappe/erpnext", + docs: "https://docs.frappe.io/erpnext", + website: "https://erpnext.com", + }, + tags: [ + "erp", + "accounts", + "manufacturing", + "retail", + "sales", + "pos", + "hrms", + ], + load: () => import("./erpnext/index").then((m) => m.generate), + }, ]; From 87697147da35dac31682b7fe6c1241deec0990c9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:29:51 -0600 Subject: [PATCH 35/61] Update apps/dokploy/templates/maybe/docker-compose.yml --- apps/dokploy/templates/maybe/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/maybe/docker-compose.yml b/apps/dokploy/templates/maybe/docker-compose.yml index 84ec297e..017ca29b 100644 --- a/apps/dokploy/templates/maybe/docker-compose.yml +++ b/apps/dokploy/templates/maybe/docker-compose.yml @@ -3,7 +3,7 @@ services: image: ghcr.io/maybe-finance/maybe:sha-68c570eed8810fd59b5b33cca51bbad5eabb4cb4 restart: unless-stopped volumes: - - ./uploads:/app/uploads + - ../files/uploads:/app/uploads environment: DATABASE_URL: postgresql://maybe:maybe@db:5432/maybe SECRET_KEY_BASE: ${SECRET_KEY_BASE} From b1beb7b71b3e0bc8e9338a8a8dcab87bffaefbe5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:55:22 -0600 Subject: [PATCH 36/61] fix: adjust color paste on terminal --- .../settings/web-server/terminal.tsx | 11 +- apps/dokploy/package.json | 1 + apps/dokploy/styles/globals.css | 317 +++++++++--------- pnpm-lock.yaml | 18 + 4 files changed, 188 insertions(+), 159 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx index 402ce57d..a82afa28 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx @@ -6,6 +6,7 @@ import "@xterm/xterm/css/xterm.css"; import { AttachAddon } from "@xterm/addon-attach"; import { useTheme } from "next-themes"; import { getLocalServerData } from "./local-server-config"; +import { ClipboardAddon } from "@xterm/addon-clipboard"; interface Props { id: string; @@ -32,15 +33,15 @@ export const Terminal: React.FC = ({ id, serverId }) => { lineHeight: 1.4, convertEol: true, theme: { - cursor: resolvedTheme === "light" ? "#000000" : "#FFFFFF", - background: resolvedTheme === "light" ? "#FFFFFF" : "rgba(0, 0, 0, 0.9)", - foreground: resolvedTheme === "light" ? "#000000" : "#FFFFFF", - selectionForeground: resolvedTheme === "light" ? "#000000" : "#FFFFFF", + cursor: resolvedTheme === "light" ? "#000000" : "transparent", + background: "rgba(0, 0, 0, 0)", + foreground: "currentColor", }, allowTransparency: true, screenReaderMode: true, scrollback: 1000, }); + const addonFit = new FitAddon(); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; @@ -58,6 +59,8 @@ export const Terminal: React.FC = ({ id, serverId }) => { const ws = new WebSocket(wsUrl); const addonAttach = new AttachAddon(ws); + const clipboardAddon = new ClipboardAddon(); + term.loadAddon(clipboardAddon); // @ts-ignore term.open(termRef.current); diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 5166c2f9..23be9df3 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -75,6 +75,7 @@ "@uiw/react-codemirror": "^4.22.1", "@xterm/addon-attach": "0.10.0", "@xterm/xterm": "^5.4.0", + "@xterm/addon-clipboard": "0.1.0", "adm-zip": "^0.5.14", "bcrypt": "5.1.1", "bullmq": "5.4.2", diff --git a/apps/dokploy/styles/globals.css b/apps/dokploy/styles/globals.css index 7b7977b9..860c0632 100644 --- a/apps/dokploy/styles/globals.css +++ b/apps/dokploy/styles/globals.css @@ -3,235 +3,242 @@ @tailwind utilities; @layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; + :root { + --terminal-paste: rgba(0, 0, 0, 0.2); + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 50.2%; - --destructive-foreground: 0 0% 98%; + --destructive: 0 84.2% 50.2%; + --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; - --radius: 0.5rem; - --overlay: rgba(0, 0, 0, 0.2); + --radius: 0.5rem; + --overlay: rgba(0, 0, 0, 0.2); - --chart-1: 173 58% 39%; - --chart-2: 12 76% 61%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } + --chart-1: 173 58% 39%; + --chart-2: 12 76% 61%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } - .dark { - --background: 0 0% 0%; - --foreground: 0 0% 98%; + .dark { + --terminal-paste: rgba(255, 255, 255, 0.2); + --background: 0 0% 0%; + --foreground: 0 0% 98%; - --card: 240 4% 10%; - --card-foreground: 0 0% 98%; + --card: 240 4% 10%; + --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; - --muted: 240 4% 10%; - --muted-foreground: 240 5% 64.9%; + --muted: 240 4% 10%; + --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; - --destructive: 0 84.2% 50.2%; - --destructive-foreground: 0 0% 98%; + --destructive: 0 84.2% 50.2%; + --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 4% 10%; - --ring: 240 4.9% 83.9%; + --border: 240 3.7% 15.9%; + --input: 240 4% 10%; + --ring: 240 4.9% 83.9%; - --overlay: rgba(0, 0, 0, 0.5); + --overlay: rgba(0, 0, 0, 0.5); - --chart-1: 220 70% 50%; - --chart-5: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-2: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } + --chart-1: 220 70% 50%; + --chart-5: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-2: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } } @layer base { - * { - @apply border-border; - } + * { + @apply border-border; + } - body { - @apply bg-background text-foreground; - } + body { + @apply bg-background text-foreground; + } - /* Custom scrollbar styling */ - ::-webkit-scrollbar { - width: 0.3125rem; - } + /* Custom scrollbar styling */ + ::-webkit-scrollbar { + width: 0.3125rem; + } - ::-webkit-scrollbar-track { - background: transparent; - } + ::-webkit-scrollbar-track { + background: transparent; + } - ::-webkit-scrollbar-thumb { - background: hsl(var(--border)); - border-radius: 0.3125rem; - } + ::-webkit-scrollbar-thumb { + background: hsl(var(--border)); + border-radius: 0.3125rem; + } - * { - scrollbar-width: thin; - scrollbar-color: hsl(var(--border)) transparent; - } + * { + scrollbar-width: thin; + scrollbar-color: hsl(var(--border)) transparent; + } } .xterm-viewport { - border-radius: 0.75rem /* 12px */ !important; + border-radius: 0.75rem /* 12px */ !important; } .xterm .xterm-viewport { - overflow-y: auto !important; + overflow-y: auto !important; } .xterm .xterm-screen { - overflow: hidden; + overflow: hidden; } @layer utilities { - /* Chrome, Safari and Opera */ - .no-scrollbar::-webkit-scrollbar { - display: none; - } + /* Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } - .no-scrollbar { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } } /* Codemirror */ .cm-editor { - @apply w-full h-full rounded-md overflow-hidden border border-solid border-border outline-none; + @apply w-full h-full rounded-md overflow-hidden border border-solid border-border outline-none; } .cm-editor .cm-scroller { - font-family: inherit; - line-height: inherit; + font-family: inherit; + line-height: inherit; } .cm-editor.cm-focused { - @apply outline-none; + @apply outline-none; } /* fix: placeholder bg */ .cm-editor .cm-activeLine:has(.cm-placeholder) { - background-color: transparent; + background-color: transparent; } .compose-file-editor .cm-editor { - @apply min-h-[25rem]; + @apply min-h-[25rem]; } @keyframes heartbeat { - 0% { - transform: scale(1); - opacity: 0.7; - } - 25% { - transform: scale(1.1); - opacity: 1; - } - 50% { - transform: scale(1); - opacity: 0.7; - } - 75% { - transform: scale(1.1); - opacity: 1; - } - 100% { - transform: scale(1); - opacity: 0.7; - } + 0% { + transform: scale(1); + opacity: 0.7; + } + 25% { + transform: scale(1.1); + opacity: 1; + } + 50% { + transform: scale(1); + opacity: 0.7; + } + 75% { + transform: scale(1.1); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 0.7; + } } .animate-heartbeat { - animation: heartbeat 2.5s infinite; + animation: heartbeat 2.5s infinite; } @media (prefers-color-scheme: dark) { - .swagger-ui { - background-color: white; - } + .swagger-ui { + background-color: white; + } - .swagger-ui .info { - margin: 0px !important; - padding-top: 1rem !important; - } + .swagger-ui .info { + margin: 0px !important; + padding-top: 1rem !important; + } } /* Docker Logs Scrollbar */ @layer utilities { - .custom-logs-scrollbar { - scrollbar-width: thin; - scrollbar-color: hsl(var(--muted-foreground)) transparent; - } + .custom-logs-scrollbar { + scrollbar-width: thin; + scrollbar-color: hsl(var(--muted-foreground)) transparent; + } - .custom-logs-scrollbar::-webkit-scrollbar { - width: 8px; - height: 8px; - } + .custom-logs-scrollbar::-webkit-scrollbar { + width: 8px; + height: 8px; + } - .custom-logs-scrollbar::-webkit-scrollbar-track { - background: transparent; - } + .custom-logs-scrollbar::-webkit-scrollbar-track { + background: transparent; + } - .custom-logs-scrollbar::-webkit-scrollbar-thumb { - background-color: hsl(var(--muted-foreground) / 0.3); - border-radius: 20px; - } + .custom-logs-scrollbar::-webkit-scrollbar-thumb { + background-color: hsl(var(--muted-foreground) / 0.3); + border-radius: 20px; + } - .custom-logs-scrollbar::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--muted-foreground) / 0.5); - } + .custom-logs-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: hsl(var(--muted-foreground) / 0.5); + } +} + +.xterm-bg-257.xterm-fg-257 { + background-color: var(--terminal-paste) !important; + color: currentColor !important; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6341be34..c2167677 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -217,6 +217,9 @@ importers: '@xterm/addon-attach': specifier: 0.10.0 version: 0.10.0(@xterm/xterm@5.5.0) + '@xterm/addon-clipboard': + specifier: 0.1.0 + version: 0.1.0(@xterm/xterm@5.5.0) '@xterm/xterm': specifier: ^5.4.0 version: 5.5.0 @@ -3503,6 +3506,11 @@ packages: peerDependencies: '@xterm/xterm': ^5.0.0 + '@xterm/addon-clipboard@0.1.0': + resolution: {integrity: sha512-zdoM7p53T5sv/HbRTyp4hY0kKmEQ3MZvAvEtiXqNIHc/JdpqwByCtsTaQF5DX2n4hYdXRPO4P/eOS0QEhX1nPw==} + peerDependencies: + '@xterm/xterm': ^5.4.0 + '@xterm/xterm@5.5.0': resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==} @@ -5011,6 +5019,9 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + js-base64@3.7.7: + resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-beautify@1.15.1: resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} engines: {node: '>=14'} @@ -9894,6 +9905,11 @@ snapshots: dependencies: '@xterm/xterm': 5.5.0 + '@xterm/addon-clipboard@0.1.0(@xterm/xterm@5.5.0)': + dependencies: + '@xterm/xterm': 5.5.0 + js-base64: 3.7.7 + '@xterm/xterm@5.5.0': {} '@xtuc/ieee754@1.2.0': {} @@ -11410,6 +11426,8 @@ snapshots: joycon@3.1.1: {} + js-base64@3.7.7: {} + js-beautify@1.15.1: dependencies: config-chain: 1.1.13 From e7a6247297f42fcbc3a1310c9ffb318766fd819f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:56:01 -0600 Subject: [PATCH 37/61] refactor: lint --- .../settings/web-server/terminal.tsx | 2 +- apps/dokploy/styles/globals.css | 318 +++++++++--------- 2 files changed, 160 insertions(+), 160 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx index a82afa28..c858a8be 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx @@ -4,9 +4,9 @@ import { useEffect, useRef } from "react"; import { FitAddon } from "xterm-addon-fit"; import "@xterm/xterm/css/xterm.css"; import { AttachAddon } from "@xterm/addon-attach"; +import { ClipboardAddon } from "@xterm/addon-clipboard"; import { useTheme } from "next-themes"; import { getLocalServerData } from "./local-server-config"; -import { ClipboardAddon } from "@xterm/addon-clipboard"; interface Props { id: string; diff --git a/apps/dokploy/styles/globals.css b/apps/dokploy/styles/globals.css index 860c0632..74a1d276 100644 --- a/apps/dokploy/styles/globals.css +++ b/apps/dokploy/styles/globals.css @@ -3,242 +3,242 @@ @tailwind utilities; @layer base { - :root { - --terminal-paste: rgba(0, 0, 0, 0.2); - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; + :root { + --terminal-paste: rgba(0, 0, 0, 0.2); + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 50.2%; - --destructive-foreground: 0 0% 98%; + --destructive: 0 84.2% 50.2%; + --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 10% 3.9%; - --radius: 0.5rem; - --overlay: rgba(0, 0, 0, 0.2); + --radius: 0.5rem; + --overlay: rgba(0, 0, 0, 0.2); - --chart-1: 173 58% 39%; - --chart-2: 12 76% 61%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } + --chart-1: 173 58% 39%; + --chart-2: 12 76% 61%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } - .dark { - --terminal-paste: rgba(255, 255, 255, 0.2); - --background: 0 0% 0%; - --foreground: 0 0% 98%; + .dark { + --terminal-paste: rgba(255, 255, 255, 0.2); + --background: 0 0% 0%; + --foreground: 0 0% 98%; - --card: 240 4% 10%; - --card-foreground: 0 0% 98%; + --card: 240 4% 10%; + --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; - --muted: 240 4% 10%; - --muted-foreground: 240 5% 64.9%; + --muted: 240 4% 10%; + --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; - --destructive: 0 84.2% 50.2%; - --destructive-foreground: 0 0% 98%; + --destructive: 0 84.2% 50.2%; + --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 4% 10%; - --ring: 240 4.9% 83.9%; + --border: 240 3.7% 15.9%; + --input: 240 4% 10%; + --ring: 240 4.9% 83.9%; - --overlay: rgba(0, 0, 0, 0.5); + --overlay: rgba(0, 0, 0, 0.5); - --chart-1: 220 70% 50%; - --chart-5: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-2: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } + --chart-1: 220 70% 50%; + --chart-5: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-2: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } } @layer base { - * { - @apply border-border; - } + * { + @apply border-border; + } - body { - @apply bg-background text-foreground; - } + body { + @apply bg-background text-foreground; + } - /* Custom scrollbar styling */ - ::-webkit-scrollbar { - width: 0.3125rem; - } + /* Custom scrollbar styling */ + ::-webkit-scrollbar { + width: 0.3125rem; + } - ::-webkit-scrollbar-track { - background: transparent; - } + ::-webkit-scrollbar-track { + background: transparent; + } - ::-webkit-scrollbar-thumb { - background: hsl(var(--border)); - border-radius: 0.3125rem; - } + ::-webkit-scrollbar-thumb { + background: hsl(var(--border)); + border-radius: 0.3125rem; + } - * { - scrollbar-width: thin; - scrollbar-color: hsl(var(--border)) transparent; - } + * { + scrollbar-width: thin; + scrollbar-color: hsl(var(--border)) transparent; + } } .xterm-viewport { - border-radius: 0.75rem /* 12px */ !important; + border-radius: 0.75rem /* 12px */ !important; } .xterm .xterm-viewport { - overflow-y: auto !important; + overflow-y: auto !important; } .xterm .xterm-screen { - overflow: hidden; + overflow: hidden; } @layer utilities { - /* Chrome, Safari and Opera */ - .no-scrollbar::-webkit-scrollbar { - display: none; - } + /* Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } - .no-scrollbar { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } } /* Codemirror */ .cm-editor { - @apply w-full h-full rounded-md overflow-hidden border border-solid border-border outline-none; + @apply w-full h-full rounded-md overflow-hidden border border-solid border-border outline-none; } .cm-editor .cm-scroller { - font-family: inherit; - line-height: inherit; + font-family: inherit; + line-height: inherit; } .cm-editor.cm-focused { - @apply outline-none; + @apply outline-none; } /* fix: placeholder bg */ .cm-editor .cm-activeLine:has(.cm-placeholder) { - background-color: transparent; + background-color: transparent; } .compose-file-editor .cm-editor { - @apply min-h-[25rem]; + @apply min-h-[25rem]; } @keyframes heartbeat { - 0% { - transform: scale(1); - opacity: 0.7; - } - 25% { - transform: scale(1.1); - opacity: 1; - } - 50% { - transform: scale(1); - opacity: 0.7; - } - 75% { - transform: scale(1.1); - opacity: 1; - } - 100% { - transform: scale(1); - opacity: 0.7; - } + 0% { + transform: scale(1); + opacity: 0.7; + } + 25% { + transform: scale(1.1); + opacity: 1; + } + 50% { + transform: scale(1); + opacity: 0.7; + } + 75% { + transform: scale(1.1); + opacity: 1; + } + 100% { + transform: scale(1); + opacity: 0.7; + } } .animate-heartbeat { - animation: heartbeat 2.5s infinite; + animation: heartbeat 2.5s infinite; } @media (prefers-color-scheme: dark) { - .swagger-ui { - background-color: white; - } + .swagger-ui { + background-color: white; + } - .swagger-ui .info { - margin: 0px !important; - padding-top: 1rem !important; - } + .swagger-ui .info { + margin: 0px !important; + padding-top: 1rem !important; + } } /* Docker Logs Scrollbar */ @layer utilities { - .custom-logs-scrollbar { - scrollbar-width: thin; - scrollbar-color: hsl(var(--muted-foreground)) transparent; - } + .custom-logs-scrollbar { + scrollbar-width: thin; + scrollbar-color: hsl(var(--muted-foreground)) transparent; + } - .custom-logs-scrollbar::-webkit-scrollbar { - width: 8px; - height: 8px; - } + .custom-logs-scrollbar::-webkit-scrollbar { + width: 8px; + height: 8px; + } - .custom-logs-scrollbar::-webkit-scrollbar-track { - background: transparent; - } + .custom-logs-scrollbar::-webkit-scrollbar-track { + background: transparent; + } - .custom-logs-scrollbar::-webkit-scrollbar-thumb { - background-color: hsl(var(--muted-foreground) / 0.3); - border-radius: 20px; - } + .custom-logs-scrollbar::-webkit-scrollbar-thumb { + background-color: hsl(var(--muted-foreground) / 0.3); + border-radius: 20px; + } - .custom-logs-scrollbar::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--muted-foreground) / 0.5); - } + .custom-logs-scrollbar::-webkit-scrollbar-thumb:hover { + background-color: hsl(var(--muted-foreground) / 0.5); + } } .xterm-bg-257.xterm-fg-257 { - background-color: var(--terminal-paste) !important; - color: currentColor !important; + background-color: var(--terminal-paste) !important; + color: currentColor !important; } From 772341fb1e1e4dbc34a895646c7799f72cc0a04a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:57:33 -0600 Subject: [PATCH 38/61] refactor: remove unused settings --- .../components/dashboard/settings/web-server/terminal.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx index c858a8be..dccdec00 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/terminal.tsx @@ -37,9 +37,6 @@ export const Terminal: React.FC = ({ id, serverId }) => { background: "rgba(0, 0, 0, 0)", foreground: "currentColor", }, - allowTransparency: true, - screenReaderMode: true, - scrollback: 1000, }); const addonFit = new FitAddon(); From 1aed53e6fe4bf74c0e57486a1788ec1244a74203 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:01:49 -0600 Subject: [PATCH 39/61] refactor: add missing load --- apps/dokploy/templates/templates.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index e513a6e9..f932e7ef 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1369,6 +1369,7 @@ export const templates: TemplateData[] = [ }, logo: "spacedrive.png", tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), }, { id: "alist", From 8d31574e5f0739b086722706b7f38b81de25577f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:17:26 -0600 Subject: [PATCH 40/61] refactor: add continue to is cloud --- apps/dokploy/pages/api/deploy/github.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 0cb7cf36..761c3866 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -182,8 +182,9 @@ export default async function handler( } } else if (req.headers["x-github-event"] === "pull_request") { const prId = githubBody?.pull_request?.id; + const action = githubBody?.action; - if (githubBody?.action === "closed") { + if (action === "closed") { const previewDeploymentResult = await findPreviewDeploymentsByPullRequestId(prId); @@ -203,7 +204,11 @@ export default async function handler( } // opened or synchronize or reopened - if (githubBody?.action === "opened" || githubBody?.action === "synchronize" || githubBody?.action === "reopened") { + if ( + action === "opened" || + action === "synchronize" || + action === "reopened" + ) { const repository = githubBody?.repository?.name; const deploymentHash = githubBody?.pull_request?.head?.sha; const branch = githubBody?.pull_request?.base?.ref; @@ -264,7 +269,7 @@ export default async function handler( if (IS_CLOUD && app.serverId) { jobData.serverId = app.serverId; await deploy(jobData); - return true; + continue; } await myQueue.add( "deployments", From 11c4101dc342beaa14ff0c5d965f0ed151c9d132 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:45:53 -0600 Subject: [PATCH 41/61] fix: search by name and not on url --- .../general/generic/save-bitbucket-provider.tsx | 9 +++++++-- .../application/general/generic/save-github-provider.tsx | 9 +++++++-- .../application/general/generic/save-gitlab-provider.tsx | 9 +++++++-- .../general/generic/save-bitbucket-provider-compose.tsx | 9 +++++++-- .../general/generic/save-github-provider-compose.tsx | 9 +++++++-- .../general/generic/save-gitlab-provider-compose.tsx | 9 +++++++-- 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx index a487452b..9b207d63 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx @@ -235,7 +235,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { {repositories?.map((repo) => ( { form.setValue("repository", { @@ -245,7 +245,12 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.username} + + { {repositories?.map((repo) => ( { form.setValue("repository", { @@ -236,7 +236,12 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.login} + + { {repositories?.map((repo) => { return ( { form.setValue("repository", { @@ -260,7 +260,12 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.username} + + { {repositories?.map((repo) => ( { form.setValue("repository", { @@ -247,7 +247,12 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.username} + + { {repositories?.map((repo) => ( { form.setValue("repository", { @@ -238,7 +238,12 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.login} + + { {repositories?.map((repo) => { return ( { form.setValue("repository", { @@ -262,7 +262,12 @@ export const SaveGitlabProviderCompose = ({ composeId }: Props) => { form.setValue("branch", ""); }} > - {repo.name} + + {repo.name} + + {repo.owner.username} + + Date: Sun, 26 Jan 2025 15:53:50 -0600 Subject: [PATCH 42/61] refactor: add dokploy-network on new installations to prevent gateway 504 errors randomly --- packages/server/src/setup/traefik-setup.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 270a4447..6a2a0133 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -189,10 +189,12 @@ export const getDefaultTraefikConfig = () => { : { swarm: { exposedByDefault: false, - watch: false, + watch: true, }, docker: { exposedByDefault: false, + watch: true, + network: "dokploy-network", }, }), file: { @@ -243,10 +245,12 @@ export const getDefaultServerTraefikConfig = () => { providers: { swarm: { exposedByDefault: false, - watch: false, + watch: true, }, docker: { exposedByDefault: false, + watch: true, + network: "dokploy-network", }, file: { directory: "/etc/dokploy/traefik/dynamic", From 95e642e63a98512863bb31db7c583505c69cdc0a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:20:02 -0600 Subject: [PATCH 43/61] refactor: make less aggressive cleanups --- .../drizzle/0060_disable-aggressive-cache.sql | 3 + apps/dokploy/drizzle/meta/0060_snapshot.json | 4341 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + packages/server/src/services/settings.ts | 4 +- packages/server/src/utils/docker/utils.ts | 28 +- 5 files changed, 4368 insertions(+), 15 deletions(-) create mode 100644 apps/dokploy/drizzle/0060_disable-aggressive-cache.sql create mode 100644 apps/dokploy/drizzle/meta/0060_snapshot.json diff --git a/apps/dokploy/drizzle/0060_disable-aggressive-cache.sql b/apps/dokploy/drizzle/0060_disable-aggressive-cache.sql new file mode 100644 index 00000000..33828cec --- /dev/null +++ b/apps/dokploy/drizzle/0060_disable-aggressive-cache.sql @@ -0,0 +1,3 @@ +-- Custom SQL migration file, put you code below! + +UPDATE "admin" SET "cleanupCacheApplications" = false; diff --git a/apps/dokploy/drizzle/meta/0060_snapshot.json b/apps/dokploy/drizzle/meta/0060_snapshot.json new file mode 100644 index 00000000..5e5719f7 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0060_snapshot.json @@ -0,0 +1,4341 @@ +{ + "id": "1f4eada1-a120-490d-a152-2fc7a81eee7a", + "prevId": "8492fecb-69a3-4efb-86b1-7bbc62e1eba8", + "version": "6", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "columnsFrom": [ + "registryId" + ], + "tableTo": "registry", + "columnsTo": [ + "registryId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + } + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_adminId_admin_adminId_fk": { + "name": "project_adminId_admin_adminId_fk", + "tableFrom": "project", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "destinationId" + ], + "tableTo": "destination", + "columnsTo": [ + "destinationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_adminId_admin_adminId_fk": { + "name": "destination_adminId_admin_adminId_fk", + "tableFrom": "destination", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "redisId" + ], + "tableTo": "redis", + "columnsTo": [ + "redisId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_adminId_admin_adminId_fk": { + "name": "certificate_adminId_admin_adminId_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "columns": [ + "certificatePath" + ], + "nullsNotDistinct": false + } + } + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "columnsFrom": [ + "user_id" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "columns": [ + "username", + "applicationId" + ], + "nullsNotDistinct": false + } + } + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_adminId_admin_adminId_fk": { + "name": "registry_adminId_admin_adminId_fk", + "tableFrom": "registry", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "slackId" + ], + "tableTo": "slack", + "columnsTo": [ + "slackId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "telegramId" + ], + "tableTo": "telegram", + "columnsTo": [ + "telegramId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "discordId" + ], + "tableTo": "discord", + "columnsTo": [ + "discordId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "emailId" + ], + "tableTo": "email", + "columnsTo": [ + "emailId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "gotifyId" + ], + "tableTo": "gotify", + "columnsTo": [ + "gotifyId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_adminId_admin_adminId_fk": { + "name": "notification_adminId_admin_adminId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_adminId_admin_adminId_fk": { + "name": "ssh-key_adminId_admin_adminId_fk", + "tableFrom": "ssh-key", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_adminId_admin_adminId_fk": { + "name": "git_provider_adminId_admin_adminId_fk", + "tableFrom": "git_provider", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "server_adminId_admin_adminId_fk": { + "name": "server_adminId_admin_adminId_fk", + "tableFrom": "server", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "columnsFrom": [ + "sshKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "domainId" + ], + "tableTo": "domain", + "columnsTo": [ + "domainId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + } + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index ccf4a066..25dd0a13 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -421,6 +421,13 @@ "when": 1737615160768, "tag": "0059_striped_bill_hollister", "breakpoints": true + }, + { + "idx": 60, + "version": "6", + "when": 1737929896838, + "tag": "0060_disable-aggressive-cache", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index d22780c9..d37793ea 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -216,8 +216,8 @@ echo "$json_output" }; export const cleanupFullDocker = async (serverId?: string | null) => { - const cleanupImages = "docker image prune --all --force"; - const cleanupVolumes = "docker volume prune --all --force"; + const cleanupImages = "docker image prune --force"; + const cleanupVolumes = "docker volume prune --force"; const cleanupContainers = "docker container prune --force"; const cleanupSystem = "docker system prune --all --force --volumes"; const cleanupBuilder = "docker builder prune --all --force"; diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 654d8330..64e98306 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -144,10 +144,11 @@ export const getContainerByName = (name: string): Promise => { }; export const cleanUpUnusedImages = async (serverId?: string) => { try { + const command = "docker image prune --force"; if (serverId) { - await execAsyncRemote(serverId, "docker image prune --all --force"); + await execAsyncRemote(serverId, command); } else { - await execAsync("docker image prune --all --force"); + await execAsync(command); } } catch (error) { console.error(error); @@ -157,10 +158,11 @@ export const cleanUpUnusedImages = async (serverId?: string) => { export const cleanStoppedContainers = async (serverId?: string) => { try { + const command = "docker container prune --force"; if (serverId) { - await execAsyncRemote(serverId, "docker container prune --force"); + await execAsyncRemote(serverId, command); } else { - await execAsync("docker container prune --force"); + await execAsync(command); } } catch (error) { console.error(error); @@ -170,10 +172,11 @@ export const cleanStoppedContainers = async (serverId?: string) => { export const cleanUpUnusedVolumes = async (serverId?: string) => { try { + const command = "docker volume prune --force"; if (serverId) { - await execAsyncRemote(serverId, "docker volume prune --all --force"); + await execAsyncRemote(serverId, command); } else { - await execAsync("docker volume prune --all --force"); + await execAsync(command); } } catch (error) { console.error(error); @@ -199,21 +202,20 @@ export const cleanUpInactiveContainers = async () => { }; export const cleanUpDockerBuilder = async (serverId?: string) => { + const command = "docker builder prune --all --force"; if (serverId) { - await execAsyncRemote(serverId, "docker builder prune --all --force"); + await execAsyncRemote(serverId, command); } else { - await execAsync("docker builder prune --all --force"); + await execAsync(command); } }; export const cleanUpSystemPrune = async (serverId?: string) => { + const command = "docker system prune --all --force --volumes"; if (serverId) { - await execAsyncRemote( - serverId, - "docker system prune --all --force --volumes", - ); + await execAsyncRemote(serverId, command); } else { - await execAsync("docker system prune --all --force --volumes"); + await execAsync(command); } }; From 8e51dedb78b21f08d1b7829e87cfbca3a851bfb0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:28:00 -0600 Subject: [PATCH 44/61] refactor: adjust order --- .../application/advanced/show-resources.tsx | 63 +++++++++---------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx index bcf0ccbd..227bca55 100644 --- a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx @@ -144,38 +144,6 @@ export const ShowResources = ({ id, type }: Props) => { className="grid w-full gap-8 " >
- ( - -
- Memory Reservation - - - - - - -

- Memory soft limit in bytes. Example: 256MB = - 268435456 bytes -

-
-
-
-
- - - - -
- )} - /> - { ); }} /> + ( + +
+ Memory Reservation + + + + + + +

+ Memory soft limit in bytes. Example: 256MB = + 268435456 bytes +

+
+
+
+
+ + + + +
+ )} + /> Date: Sun, 26 Jan 2025 17:20:33 -0600 Subject: [PATCH 45/61] fix: set extracted port on ssh connection --- packages/server/src/utils/providers/git.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/server/src/utils/providers/git.ts b/packages/server/src/utils/providers/git.ts index 6377b557..8f8a3830 100644 --- a/packages/server/src/utils/providers/git.ts +++ b/packages/server/src/utils/providers/git.ts @@ -69,6 +69,7 @@ export const cloneGitRepository = async ( }); } + const { port } = sanitizeRepoPathSSH(customGitUrl); await spawnAsync( "git", [ @@ -91,7 +92,7 @@ export const cloneGitRepository = async ( env: { ...process.env, ...(customGitSSHKeyId && { - GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`, + GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`, }), }, }, @@ -168,7 +169,8 @@ export const getCustomGitCloneCommand = async ( ); if (customGitSSHKeyId) { const sshKey = await findSSHKeyById(customGitSSHKeyId); - const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`; + const { port } = sanitizeRepoPathSSH(customGitUrl); + const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; command.push( ` echo "${sshKey.privateKey}" > /tmp/id_rsa @@ -304,6 +306,7 @@ export const cloneGitRawRepository = async (entity: { }); } + const { port } = sanitizeRepoPathSSH(customGitUrl); await spawnAsync( "git", [ @@ -322,7 +325,7 @@ export const cloneGitRawRepository = async (entity: { env: { ...process.env, ...(customGitSSHKeyId && { - GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`, + GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`, }), }, }, @@ -381,7 +384,8 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => { command.push(`mkdir -p ${outputPath};`); if (customGitSSHKeyId) { const sshKey = await findSSHKeyById(customGitSSHKeyId); - const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`; + const { port } = sanitizeRepoPathSSH(customGitUrl); + const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`; command.push( ` echo "${sshKey.privateKey}" > /tmp/id_rsa From 165cdd27da5799ee150d5313826f1dc4a95fdfb9 Mon Sep 17 00:00:00 2001 From: Freilyn Bernabe Date: Sun, 26 Jan 2025 19:19:14 -0400 Subject: [PATCH 46/61] feat(template): added answer template --- apps/dokploy/public/templates/answer.png | Bin 0 -> 3775 bytes .../templates/answer/docker-compose.yml | 31 ++++++++++++++++ apps/dokploy/templates/answer/index.ts | 33 ++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 ++++++++ 4 files changed, 79 insertions(+) create mode 100644 apps/dokploy/public/templates/answer.png create mode 100644 apps/dokploy/templates/answer/docker-compose.yml create mode 100644 apps/dokploy/templates/answer/index.ts diff --git a/apps/dokploy/public/templates/answer.png b/apps/dokploy/public/templates/answer.png new file mode 100644 index 0000000000000000000000000000000000000000..3fca604d4895423acefc7a4d9d203592d4a81ae5 GIT binary patch literal 3775 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 Da`o^h literal 0 HcmV?d00001 diff --git a/apps/dokploy/templates/answer/docker-compose.yml b/apps/dokploy/templates/answer/docker-compose.yml new file mode 100644 index 00000000..75d8177a --- /dev/null +++ b/apps/dokploy/templates/answer/docker-compose.yml @@ -0,0 +1,31 @@ +services: + answer: + image: apache/answer:1.4.1 + ports: + - '9080:80' + restart: on-failure + volumes: + - answer-data:/data + depends_on: + db: + condition: service_healthy + db: + image: postgres:16 + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - dokploy-network + volumes: + - db-data:/var/lib/postgresql/data + environment: + POSTGRES_DB: answer + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + +volumes: + answer-data: + db-data: diff --git a/apps/dokploy/templates/answer/index.ts b/apps/dokploy/templates/answer/index.ts new file mode 100644 index 00000000..36d48cb3 --- /dev/null +++ b/apps/dokploy/templates/answer/index.ts @@ -0,0 +1,33 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateHash, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainServiceHash = generateHash(schema.projectName); + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 9080, + serviceName: "answer", + }, + ]; + + const envs = [ + `ANSWER_HOST=http://${mainDomain}`, + `SERVICE_HASH=${mainServiceHash}`, + ]; + + const mounts: Template["mounts"] = []; + + return { + envs, + mounts, + domains, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index be04033d..7ba3829f 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1408,4 +1408,19 @@ export const templates: TemplateData[] = [ tags: ["file", "webdav", "storage"], load: () => import("./alist/index").then((m) => m.generate), }, + { + id: "answer", + name: "Answer", + version: "v1.4.1", + description: + "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", + logo: "answer.png", + links: { + github: "https://github.com/apache/answer", + website: "https://answer.apache.org/", + docs: "https://answer.apache.org/docs", + }, + tags: ["q&a", "self-hosted"], + load: () => import("./answer/index").then((m) => m.generate), + }, ]; From eeb97645b5c809187e7612455de42ec9e4d2a546 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 17:40:28 -0600 Subject: [PATCH 47/61] refactor: add back delete with confirmation --- ...{delete-compose.tsx => delete-service.tsx} | 102 ++++++++++++------ .../services/application/[applicationId].tsx | 32 +----- .../services/compose/[composeId].tsx | 4 +- .../services/mariadb/[mariadbId].tsx | 33 +----- .../[projectId]/services/mongo/[mongoId].tsx | 30 +----- .../[projectId]/services/mysql/[mysqlId].tsx | 30 +----- .../services/postgres/[postgresId].tsx | 31 +----- .../[projectId]/services/redis/[redisId].tsx | 30 +----- 8 files changed, 86 insertions(+), 206 deletions(-) rename apps/dokploy/components/dashboard/compose/{delete-compose.tsx => delete-service.tsx} (63%) diff --git a/apps/dokploy/components/dashboard/compose/delete-compose.tsx b/apps/dokploy/components/dashboard/compose/delete-service.tsx similarity index 63% rename from apps/dokploy/components/dashboard/compose/delete-compose.tsx rename to apps/dokploy/components/dashboard/compose/delete-service.tsx index 764de95e..a91ae63f 100644 --- a/apps/dokploy/components/dashboard/compose/delete-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/delete-service.tsx @@ -20,6 +20,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; +import type { ServiceType } from "@dokploy/server/db/schema"; import { zodResolver } from "@hookform/resolvers/zod"; import { Copy, Trash2 } from "lucide-react"; import { TrashIcon } from "lucide-react"; @@ -39,16 +40,42 @@ const deleteComposeSchema = z.object({ type DeleteCompose = z.infer; interface Props { - composeId: string; + id: string; + type: ServiceType | "application"; } -export const DeleteCompose = ({ composeId }: Props) => { +export const DeleteService = ({ id, type }: Props) => { const [isOpen, setIsOpen] = useState(false); - const { mutateAsync, isLoading } = api.compose.delete.useMutation(); - const { data } = api.compose.one.useQuery( - { composeId }, - { enabled: !!composeId }, - ); + + const queryMap = { + postgres: () => + api.postgres.one.useQuery({ postgresId: id }, { enabled: !!id }), + redis: () => api.redis.one.useQuery({ redisId: id }, { enabled: !!id }), + mysql: () => api.mysql.one.useQuery({ mysqlId: id }, { enabled: !!id }), + mariadb: () => + api.mariadb.one.useQuery({ mariadbId: id }, { enabled: !!id }), + application: () => + api.application.one.useQuery({ applicationId: id }, { enabled: !!id }), + mongo: () => api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id }), + compose: () => + api.compose.one.useQuery({ composeId: id }, { enabled: !!id }), + }; + const { data, refetch } = queryMap[type] + ? queryMap[type]() + : api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id }); + + const mutationMap = { + postgres: () => api.postgres.remove.useMutation(), + redis: () => api.redis.remove.useMutation(), + mysql: () => api.mysql.remove.useMutation(), + mariadb: () => api.mariadb.remove.useMutation(), + application: () => api.application.delete.useMutation(), + mongo: () => api.mongo.remove.useMutation(), + compose: () => api.compose.delete.useMutation(), + }; + const { mutateAsync, isLoading } = mutationMap[type] + ? mutationMap[type]() + : api.mongo.remove.useMutation(); const { push } = useRouter(); const form = useForm({ defaultValues: { @@ -62,14 +89,23 @@ export const DeleteCompose = ({ composeId }: Props) => { const expectedName = `${data?.name}/${data?.appName}`; if (formData.projectName === expectedName) { const { deleteVolumes } = formData; - await mutateAsync({ composeId, deleteVolumes }) + await mutateAsync({ + mongoId: id || "", + postgresId: id || "", + redisId: id || "", + mysqlId: id || "", + mariadbId: id || "", + applicationId: id || "", + composeId: id || "", + deleteVolumes, + }) .then((result) => { push(`/dashboard/project/${result?.projectId}`); - toast.success("Compose deleted successfully"); + toast.success("deleted successfully"); setIsOpen(false); }) .catch(() => { - toast.error("Error deleting the compose"); + toast.error("Error deleting the service"); }); } else { form.setError("projectName", { @@ -95,8 +131,8 @@ export const DeleteCompose = ({ composeId }: Props) => { Are you absolutely sure? This action cannot be undone. This will permanently delete the - compose. If you are sure please enter the compose name to delete - this compose. + service. If you are sure please enter the service name to delete + this service.
@@ -142,27 +178,29 @@ export const DeleteCompose = ({ composeId }: Props) => { )} /> - ( - -
- - - + {type === "compose" && ( + ( + +
+ + + - - Delete volumes associated with this compose - -
- -
- )} - /> + + Delete volumes associated with this compose + +
+ +
+ )} + /> + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index bdde2af5..3ef433ed 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -13,6 +13,7 @@ import { ShowGeneralApplication } from "@/components/dashboard/application/gener import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; import { ShowPreviewDeployments } from "@/components/dashboard/application/preview-deployments/show-preview-deployments"; import { UpdateApplication } from "@/components/dashboard/application/update-application"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { ProjectLayout } from "@/components/layouts/project-layout"; import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar"; @@ -82,8 +83,6 @@ const Service = ( }, ); - const { mutateAsync, isLoading: isRemoving } = - api.application.delete.useMutation(); const { data: auth } = api.auth.get.useQuery(); const { data: user } = api.user.byAuthId.useQuery( { @@ -177,34 +176,7 @@ const Service = (
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await mutateAsync({ - applicationId: applicationId, - }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Application deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting application"); - }); - }} - > - - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index dba3aea8..66c3ef53 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -1,7 +1,7 @@ import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { AddCommandCompose } from "@/components/dashboard/compose/advanced/add-command"; -import { DeleteCompose } from "@/components/dashboard/compose/delete-compose"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowDeploymentsCompose } from "@/components/dashboard/compose/deployments/show-deployments-compose"; import { ShowDomainsCompose } from "@/components/dashboard/compose/domains/show-domains"; import { ShowGeneralCompose } from "@/components/dashboard/compose/general/show"; @@ -168,7 +168,7 @@ const Service = ( {(auth?.rol === "admin" || user?.canDeleteServices) && ( - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index fed8faa3..4edf9f14 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show- import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowExternalMariadbCredentials } from "@/components/dashboard/mariadb/general/show-external-mariadb-credentials"; import { ShowGeneralMariadb } from "@/components/dashboard/mariadb/general/show-general-mariadb"; @@ -67,8 +68,7 @@ const Mariadb = ( enabled: !!auth?.id && auth?.rol === "user", }, ); - const { mutateAsync: remove, isLoading: isRemoving } = - api.mariadb.remove.useMutation(); + return (
)}
-
+
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await remove({ mariadbId }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Mariadb deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting the mariadb"); - }); - }} - > - - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 49dcfa65..ecd42841 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show- import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { ShowExternalMongoCredentials } from "@/components/dashboard/mongo/general/show-external-mongo-credentials"; import { ShowGeneralMongo } from "@/components/dashboard/mongo/general/show-general-mongo"; @@ -69,8 +70,6 @@ const Mongo = ( enabled: !!auth?.id && auth?.rol === "user", }, ); - const { mutateAsync: remove, isLoading: isRemoving } = - api.mongo.remove.useMutation(); return (
@@ -155,32 +154,7 @@ const Mongo = (
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await remove({ mongoId }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Mongo deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting the mongo"); - }); - }} - > - - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index a6117a3a..5b851015 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show- import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { ShowExternalMysqlCredentials } from "@/components/dashboard/mysql/general/show-external-mysql-credentials"; @@ -68,8 +69,6 @@ const MySql = ( }, ); - const { mutateAsync: remove, isLoading: isRemoving } = - api.mysql.remove.useMutation(); return (
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await remove({ mysqlId }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Mysql deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting the mysql"); - }); - }} - > - - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index d7b4b8d3..78637254 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show- import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { ShowBackups } from "@/components/dashboard/database/backups/show-backups"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; @@ -70,9 +71,6 @@ const Postgresql = ( }, ); - const { mutateAsync: remove, isLoading: isRemoving } = - api.postgres.remove.useMutation(); - return (
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await remove({ postgresId }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Postgres deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting the postgres"); - }); - }} - > - - + )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 4a85eac0..1eef98e4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -2,6 +2,7 @@ import { ShowResources } from "@/components/dashboard/application/advanced/show- import { ShowVolumes } from "@/components/dashboard/application/advanced/volumes/show-volumes"; import { ShowEnvironment } from "@/components/dashboard/application/environment/show-enviroment"; import { ShowDockerLogs } from "@/components/dashboard/application/logs/show"; +import { DeleteService } from "@/components/dashboard/compose/delete-service"; import { DockerMonitoring } from "@/components/dashboard/monitoring/docker/show"; import { ShowCustomCommand } from "@/components/dashboard/postgres/advanced/show-custom-command"; import { ShowExternalRedisCredentials } from "@/components/dashboard/redis/general/show-external-redis-credentials"; @@ -68,8 +69,6 @@ const Redis = ( }, ); - const { mutateAsync: remove, isLoading: isRemoving } = - api.redis.remove.useMutation(); return (
{(auth?.rol === "admin" || user?.canDeleteServices) && ( - { - await remove({ redisId }) - .then(() => { - router.push( - `/dashboard/project/${data?.projectId}`, - ); - toast.success("Redis deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting the redis"); - }); - }} - > - - + )}
From 36c1a615c6a51eebaaca9c412a4a6c82f7f11c54 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 17:42:04 -0600 Subject: [PATCH 48/61] refactor: use copy package --- .../dokploy/components/dashboard/compose/delete-service.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/compose/delete-service.tsx b/apps/dokploy/components/dashboard/compose/delete-service.tsx index a91ae63f..a2a2d9c0 100644 --- a/apps/dokploy/components/dashboard/compose/delete-service.tsx +++ b/apps/dokploy/components/dashboard/compose/delete-service.tsx @@ -23,12 +23,12 @@ import { api } from "@/utils/api"; import type { ServiceType } from "@dokploy/server/db/schema"; import { zodResolver } from "@hookform/resolvers/zod"; import { Copy, Trash2 } from "lucide-react"; -import { TrashIcon } from "lucide-react"; import { useRouter } from "next/router"; import { useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import copy from "copy-to-clipboard"; const deleteComposeSchema = z.object({ projectName: z.string().min(1, { @@ -155,9 +155,7 @@ export const DeleteService = ({ id, type }: Props) => { variant="outline" onClick={() => { if (data?.name && data?.appName) { - navigator.clipboard.writeText( - `${data.name}/${data.appName}`, - ); + copy(`${data.name}/${data.appName}`); toast.success("Copied to clipboard. Be careful!"); } }} From 64e68cfde1ffbfd1e212ccd4f30c7f04910a04fb Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 17:44:31 -0600 Subject: [PATCH 49/61] refactor: update styles --- .../dashboard/application/domains/show-domains.tsx | 8 ++++++-- .../components/dashboard/compose/delete-service.tsx | 2 +- .../components/dashboard/compose/domains/show-domains.tsx | 8 ++++++-- apps/dokploy/components/layouts/side.tsx | 4 ++-- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index 0b775c22..21b7a9f5 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -98,8 +98,12 @@ export const ShowDomains = ({ applicationId }: Props) => { applicationId={applicationId} domainId={item.domainId} > - { composeId={composeId} domainId={item.domainId} > - Date: Sun, 26 Jan 2025 17:49:51 -0600 Subject: [PATCH 50/61] refactor: add server IP address --- .../components/dashboard/project/add-application.tsx | 11 ++++++++--- .../components/dashboard/project/add-compose.tsx | 10 ++++++++-- .../components/dashboard/project/add-template.tsx | 11 +++++++++-- .../settings/certificates/add-certificate.tsx | 10 ++++++++-- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-application.tsx b/apps/dokploy/components/dashboard/project/add-application.tsx index 2c30df8d..da30cfee 100644 --- a/apps/dokploy/components/dashboard/project/add-application.tsx +++ b/apps/dokploy/components/dashboard/project/add-application.tsx @@ -70,7 +70,7 @@ interface Props { export const AddApplication = ({ projectId, projectName }: Props) => { const utils = api.useUtils(); - + const { data: isCloud } = api.settings.isCloud.useQuery(); const [visible, setVisible] = useState(false); const slug = slugify(projectName); const { data: servers } = api.server.withSSHKey.useQuery(); @@ -166,7 +166,7 @@ export const AddApplication = ({ projectId, projectName }: Props) => { - Select a Server (Optional) + Select a Server {!isCloud ? "(Optional)" : ""} @@ -197,7 +197,12 @@ export const AddApplication = ({ projectId, projectName }: Props) => { key={server.serverId} value={server.serverId} > - {server.name} + + {server.name} + + {server.ipAddress} + + ))} Servers ({servers?.length}) diff --git a/apps/dokploy/components/dashboard/project/add-compose.tsx b/apps/dokploy/components/dashboard/project/add-compose.tsx index dc753e94..ea8690a8 100644 --- a/apps/dokploy/components/dashboard/project/add-compose.tsx +++ b/apps/dokploy/components/dashboard/project/add-compose.tsx @@ -73,6 +73,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => { const utils = api.useUtils(); const [visible, setVisible] = useState(false); const slug = slugify(projectName); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery(); const { mutateAsync, isLoading, error, isError } = api.compose.create.useMutation(); @@ -173,7 +174,7 @@ export const AddCompose = ({ projectId, projectName }: Props) => { - Select a Server (Optional) + Select a Server {!isCloud ? "(Optional)" : ""} @@ -204,7 +205,12 @@ export const AddCompose = ({ projectId, projectName }: Props) => { key={server.serverId} value={server.serverId} > - {server.name} + + {server.name} + + {server.ipAddress} + + ))} Servers ({servers?.length}) diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index bc8929b2..cc6962aa 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -80,6 +80,7 @@ export const AddTemplate = ({ projectId }: Props) => { const [viewMode, setViewMode] = useState<"detailed" | "icon">("detailed"); const [selectedTags, setSelectedTags] = useState([]); const { data } = api.compose.templates.useQuery(); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: servers } = api.server.withSSHKey.useQuery(); const { data: tags, isLoading: isLoadingTags } = api.compose.getTags.useQuery(); @@ -372,7 +373,8 @@ export const AddTemplate = ({ projectId }: Props) => { @@ -405,7 +407,12 @@ export const AddTemplate = ({ projectId }: Props) => { key={server.serverId} value={server.serverId} > - {server.name} + + {server.name} + + {server.ipAddress} + + ))} diff --git a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx index 3d03d7c6..c6546f2d 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx @@ -61,6 +61,7 @@ export const AddCertificate = () => { const [open, setOpen] = useState(false); const utils = api.useUtils(); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { mutateAsync, isError, error, isLoading } = api.certificates.create.useMutation(); const { data: servers } = api.server.withSSHKey.useQuery(); @@ -181,7 +182,7 @@ export const AddCertificate = () => { - Select a Server (Optional) + Select a Server {!isCloud && "(Optional)"} @@ -202,7 +203,12 @@ export const AddCertificate = () => { key={server.serverId} value={server.serverId} > - {server.name} + + {server.name} + + {server.ipAddress} + + ))} Servers ({servers?.length}) From 3209550edc62b43cfd55f367af83be76bd2579ba Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 17:57:31 -0600 Subject: [PATCH 51/61] Update apps/dokploy/templates/answer/docker-compose.yml --- apps/dokploy/templates/answer/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/answer/docker-compose.yml b/apps/dokploy/templates/answer/docker-compose.yml index 75d8177a..e17a6d1e 100644 --- a/apps/dokploy/templates/answer/docker-compose.yml +++ b/apps/dokploy/templates/answer/docker-compose.yml @@ -2,7 +2,7 @@ services: answer: image: apache/answer:1.4.1 ports: - - '9080:80' + - '80' restart: on-failure volumes: - answer-data:/data From 1e56364f93fae4db3fc32035e11d3ce3d9cdcbb5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 18:26:49 -0600 Subject: [PATCH 52/61] refactor: update typo --- .../dashboard/application/advanced/volumes/show-volumes.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index 5444ee1b..e0f842ce 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -45,8 +45,8 @@ export const ShowVolumes = ({ id, type }: Props) => {
Volumes - If you want to persist data in this postgres database use the - following config to setup the volumes + If you want to persist data in this service use the following config + to setup the volumes
From 0fb67ced5df0cd2d3cdf4482818dc00f10f17680 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 18:59:27 -0600 Subject: [PATCH 53/61] fix: remove cron jobs after delete service --- .../dashboard/project/add-database.tsx | 18 ++++++++++----- apps/dokploy/server/api/routers/mariadb.ts | 4 ++++ apps/dokploy/server/api/routers/mongo.ts | 4 ++++ apps/dokploy/server/api/routers/mysql.ts | 4 ++++ apps/dokploy/server/api/routers/postgres.ts | 5 ++++ apps/dokploy/server/api/routers/redis.ts | 1 - apps/dokploy/server/utils/backup.ts | 23 +++++++++++++++++++ packages/server/src/services/backup.ts | 21 ++++++++++++++++- 8 files changed, 72 insertions(+), 8 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index fc86d253..2ba3eb6b 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -89,7 +89,7 @@ const mySchema = z.discriminatedUnion("type", [ z .object({ type: z.literal("postgres"), - databaseName: z.string().min(1, "Database name required"), + databaseName: z.string().default("postgres"), databaseUser: z.string().default("postgres"), }) .merge(baseDatabaseSchema), @@ -110,7 +110,10 @@ const mySchema = z.discriminatedUnion("type", [ type: z.literal("mysql"), databaseRootPassword: z.string().default(""), databaseUser: z.string().default("mysql"), - databaseName: z.string().min(1, "Database name required"), + databaseName: z + .string() + .min(1, "Database name required") + .default("mysql"), }) .merge(baseDatabaseSchema), z @@ -119,7 +122,10 @@ const mySchema = z.discriminatedUnion("type", [ dockerImage: z.string().default("mariadb:4"), databaseRootPassword: z.string().default(""), databaseUser: z.string().default("mariadb"), - databaseName: z.string().min(1, "Database name required"), + databaseName: z + .string() + .min(1, "Database name required") + .default("mariadb"), }) .merge(baseDatabaseSchema), ]); @@ -206,7 +212,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => { promise = postgresMutation.mutateAsync({ ...commonParams, databasePassword: data.databasePassword, - databaseName: data.databaseName, + databaseName: data.databaseName || "postgres", databaseUser: data.databaseUser || databasesUserDefaultPlaceholder[data.type], @@ -233,7 +239,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => { ...commonParams, databasePassword: data.databasePassword, databaseRootPassword: data.databaseRootPassword, - databaseName: data.databaseName, + databaseName: data.databaseName || "mariadb", databaseUser: data.databaseUser || databasesUserDefaultPlaceholder[data.type], serverId: data.serverId, @@ -242,7 +248,7 @@ export const AddDatabase = ({ projectId, projectName }: Props) => { promise = mysqlMutation.mutateAsync({ ...commonParams, databasePassword: data.databasePassword, - databaseName: data.databaseName, + databaseName: data.databaseName || "mysql", databaseUser: data.databaseUser || databasesUserDefaultPlaceholder[data.type], databaseRootPassword: data.databaseRootPassword, diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 09f4d675..6e85d274 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -9,6 +9,7 @@ import { apiSaveExternalPortMariaDB, apiUpdateMariaDB, } from "@/server/db/schema"; +import { cancelJobs } from "@/server/utils/backup"; import { IS_CLOUD, addNewService, @@ -16,6 +17,7 @@ import { createMariadb, createMount, deployMariadb, + findBackupsByDbId, findMariadbById, findProjectById, findServerById, @@ -211,8 +213,10 @@ export const mariadbRouter = createTRPCRouter({ }); } + const backups = await findBackupsByDbId(input.mariadbId, "mariadb"); const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), + async () => await cancelJobs(backups), async () => await removeMariadbById(input.mariadbId), ]; diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index b114b8d8..2bca3ec5 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -9,6 +9,7 @@ import { apiSaveExternalPortMongo, apiUpdateMongo, } from "@/server/db/schema"; +import { cancelJobs } from "@/server/utils/backup"; import { IS_CLOUD, addNewService, @@ -16,6 +17,7 @@ import { createMongo, createMount, deployMongo, + findBackupsByDbId, findMongoById, findProjectById, removeMongoById, @@ -252,9 +254,11 @@ export const mongoRouter = createTRPCRouter({ message: "You are not authorized to delete this mongo", }); } + const backups = await findBackupsByDbId(input.mongoId, "mongo"); const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), + async () => await cancelJobs(backups), async () => await removeMongoById(input.mongoId), ]; diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 44d2537a..7022d7e1 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -19,6 +19,7 @@ import { createMount, createMysql, deployMySql, + findBackupsByDbId, findMySqlById, findProjectById, removeMySqlById, @@ -30,6 +31,7 @@ import { updateMySqlById, } from "@dokploy/server"; import { observable } from "@trpc/server/observable"; +import { cancelJobs } from "@/server/utils/backup"; export const mysqlRouter = createTRPCRouter({ create: protectedProcedure @@ -249,8 +251,10 @@ export const mysqlRouter = createTRPCRouter({ }); } + const backups = await findBackupsByDbId(input.mysqlId, "mysql"); const cleanupOperations = [ async () => await removeService(mongo?.appName, mongo.serverId), + async () => await cancelJobs(backups), async () => await removeMySqlById(input.mysqlId), ]; diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 7d178943..c7ac462f 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -21,6 +21,7 @@ import { createMount, createPostgres, deployPostgres, + findBackupsByDbId, findPostgresById, findProjectById, removePostgresById, @@ -34,6 +35,7 @@ import { import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; import { z } from "zod"; +import { cancelJobs } from "@/server/utils/backup"; const ee = new EventEmitter(); @@ -231,8 +233,11 @@ export const postgresRouter = createTRPCRouter({ }); } + const backups = await findBackupsByDbId(input.postgresId, "postgres"); + const cleanupOperations = [ removeService(postgres.appName, postgres.serverId), + cancelJobs(backups), removePostgresById(input.postgresId), ]; diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index 5883e50b..967fb51a 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -244,7 +244,6 @@ export const redisRouter = createTRPCRouter({ message: "You are not authorized to delete this Redis", }); } - const cleanupOperations = [ async () => await removeService(redis?.appName, redis.serverId), async () => await removeRedisById(input.redisId), diff --git a/apps/dokploy/server/utils/backup.ts b/apps/dokploy/server/utils/backup.ts index a178063f..2f141971 100644 --- a/apps/dokploy/server/utils/backup.ts +++ b/apps/dokploy/server/utils/backup.ts @@ -1,3 +1,10 @@ +import { + type BackupScheduleList, + IS_CLOUD, + removeScheduleBackup, + scheduleBackup, +} from "@dokploy/server/index"; + type QueueJob = | { type: "backup"; @@ -59,3 +66,19 @@ export const updateJob = async (job: QueueJob) => { throw error; } }; + +export const cancelJobs = async (backups: BackupScheduleList) => { + for (const backup of backups) { + if (backup.enabled) { + if (IS_CLOUD) { + await removeJob({ + cronSchedule: backup.schedule, + backupId: backup.backupId, + type: "backup", + }); + } else { + removeScheduleBackup(backup.backupId); + } + } + } +}; diff --git a/packages/server/src/services/backup.ts b/packages/server/src/services/backup.ts index 70e37af4..ef3d0446 100644 --- a/packages/server/src/services/backup.ts +++ b/packages/server/src/services/backup.ts @@ -2,11 +2,13 @@ import { db } from "@dokploy/server/db"; import { type apiCreateBackup, backups } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; +import { IS_CLOUD } from "../constants"; +import { removeScheduleBackup, scheduleBackup } from "../utils/backups/utils"; export type Backup = typeof backups.$inferSelect; export type BackupSchedule = Awaited>; - +export type BackupScheduleList = Awaited>; export const createBackup = async (input: typeof apiCreateBackup._type) => { const newBackup = await db .insert(backups) @@ -69,3 +71,20 @@ export const removeBackupById = async (backupId: string) => { return result[0]; }; + +export const findBackupsByDbId = async ( + id: string, + type: "postgres" | "mysql" | "mariadb" | "mongo", +) => { + const result = await db.query.backups.findMany({ + where: eq(backups[`${type}Id`], id), + with: { + postgres: true, + mysql: true, + mariadb: true, + mongo: true, + destination: true, + }, + }); + return result || []; +}; From 24327139b829857a666dea36d80ead978a250a2b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:00:58 -0600 Subject: [PATCH 54/61] refactor: make optional field name --- .../components/dashboard/project/add-database.tsx | 10 ++-------- apps/dokploy/server/api/routers/mysql.ts | 2 +- apps/dokploy/server/api/routers/postgres.ts | 2 +- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index 2ba3eb6b..1ca0d6a5 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -110,10 +110,7 @@ const mySchema = z.discriminatedUnion("type", [ type: z.literal("mysql"), databaseRootPassword: z.string().default(""), databaseUser: z.string().default("mysql"), - databaseName: z - .string() - .min(1, "Database name required") - .default("mysql"), + databaseName: z.string().default("mysql"), }) .merge(baseDatabaseSchema), z @@ -122,10 +119,7 @@ const mySchema = z.discriminatedUnion("type", [ dockerImage: z.string().default("mariadb:4"), databaseRootPassword: z.string().default(""), databaseUser: z.string().default("mariadb"), - databaseName: z - .string() - .min(1, "Database name required") - .default("mariadb"), + databaseName: z.string().default("mariadb"), }) .merge(baseDatabaseSchema), ]); diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 7022d7e1..7ebf4623 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -12,6 +12,7 @@ import { import { TRPCError } from "@trpc/server"; +import { cancelJobs } from "@/server/utils/backup"; import { IS_CLOUD, addNewService, @@ -31,7 +32,6 @@ import { updateMySqlById, } from "@dokploy/server"; import { observable } from "@trpc/server/observable"; -import { cancelJobs } from "@/server/utils/backup"; export const mysqlRouter = createTRPCRouter({ create: protectedProcedure diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index c7ac462f..92603a61 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -14,6 +14,7 @@ import { apiSaveExternalPortPostgres, apiUpdatePostgres, } from "@/server/db/schema"; +import { cancelJobs } from "@/server/utils/backup"; import { IS_CLOUD, addNewService, @@ -35,7 +36,6 @@ import { import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; import { z } from "zod"; -import { cancelJobs } from "@/server/utils/backup"; const ee = new EventEmitter(); From 34b495663077c3b24e5fb5732e135316969ab990 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:13:43 -0600 Subject: [PATCH 55/61] feat: add DOKPLOY_DEPLOY_URL in enviroment variables for previews --- packages/server/src/services/application.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index 4c194e0f..da1b50af 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -451,7 +451,7 @@ export const deployPreviewApplication = async ({ body: `### Dokploy Preview Deployment\n\n${buildingComment}`, }); application.appName = previewDeployment.appName; - application.env = application.previewEnv; + application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`; application.buildArgs = application.previewBuildArgs; const admin = await findAdminById(application.project.adminId); @@ -564,7 +564,7 @@ export const deployRemotePreviewApplication = async ({ body: `### Dokploy Preview Deployment\n\n${buildingComment}`, }); application.appName = previewDeployment.appName; - application.env = application.previewEnv; + application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`; application.buildArgs = application.previewBuildArgs; if (application.serverId) { From 23de14f0b4ce40d1662e685e44c777b4de5d9925 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:15:11 -0600 Subject: [PATCH 56/61] chore: bump version --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 23be9df3..f4127bb1 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.17.8", + "version": "v0.17.9", "private": true, "license": "Apache-2.0", "type": "module", From 3d5bed0915baa9e7d6d83078fad56c1c484ccdc5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:30:21 -0600 Subject: [PATCH 57/61] chore: add missing dependency --- apps/dokploy/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f4127bb1..9e2eb6dd 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,6 +35,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { + "dockerode": "4.0.2", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-yaml": "^6.1.1", "@codemirror/language": "^6.10.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c2167677..b5f0a8f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -250,6 +250,9 @@ importers: date-fns: specifier: 3.6.0 version: 3.6.0 + dockerode: + specifier: 4.0.2 + version: 4.0.2 dotenv: specifier: 16.4.5 version: 16.4.5 From 094491ecbf0e4d183a4539e5c5abf2248b5ac6c6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:47:26 -0600 Subject: [PATCH 58/61] refactor: add missing deps --- apps/dokploy/package.json | 13 +++++++++++++ pnpm-lock.yaml | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 9e2eb6dd..f08400ff 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,6 +35,16 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { + "rotating-file-stream": "3.2.3", + "qrcode": "^1.5.3", + "otpauth": "^9.2.3", + "hi-base32": "^0.5.1", + "boxen": "^7.1.1", + "@octokit/auth-app": "^6.0.4", + "nodemailer": "6.9.14", + "@react-email/components": "^0.0.21", + "node-os-utils": "1.3.7", + "@lucia-auth/adapter-drizzle": "1.0.7", "dockerode": "4.0.2", "@codemirror/lang-json": "^6.0.1", "@codemirror/lang-yaml": "^6.1.1", @@ -129,6 +139,9 @@ "@faker-js/faker": "^8.4.1" }, "devDependencies": { + "@types/qrcode": "^1.5.5", + "@types/nodemailer": "^6.4.15", + "@types/node-os-utils": "1.3.4", "@types/adm-zip": "^0.5.5", "@types/bcrypt": "5.0.2", "@types/js-cookie": "^3.0.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5f0a8f8..91b15544 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,12 @@ importers: '@hookform/resolvers': specifier: ^3.3.4 version: 3.9.0(react-hook-form@7.52.1(react@18.2.0)) + '@lucia-auth/adapter-drizzle': + specifier: 1.0.7 + version: 1.0.7(lucia@3.2.0) + '@octokit/auth-app': + specifier: ^6.0.4 + version: 6.1.1 '@octokit/webhooks': specifier: ^13.2.7 version: 13.3.0 @@ -184,6 +190,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) + '@react-email/components': + specifier: ^0.0.21 + version: 0.0.21(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@stepperize/react': specifier: 4.0.1 version: 4.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -229,6 +238,9 @@ importers: bcrypt: specifier: 5.1.1 version: 5.1.1 + boxen: + specifier: ^7.1.1 + version: 7.1.1 bullmq: specifier: 5.4.2 version: 5.4.2 @@ -265,6 +277,9 @@ importers: fancy-ansi: specifier: ^0.1.3 version: 0.1.3 + hi-base32: + specifier: ^0.5.1 + version: 0.5.1 i18next: specifier: ^23.16.4 version: 23.16.5 @@ -298,21 +313,33 @@ importers: next-themes: specifier: ^0.2.1 version: 0.2.1(next@15.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + node-os-utils: + specifier: 1.3.7 + version: 1.3.7 node-pty: specifier: 1.0.0 version: 1.0.0 node-schedule: specifier: 2.1.1 version: 2.1.1 + nodemailer: + specifier: 6.9.14 + version: 6.9.14 octokit: specifier: 3.1.2 version: 3.1.2 + otpauth: + specifier: ^9.2.3 + version: 9.3.4 postgres: specifier: 3.4.4 version: 3.4.4 public-ip: specifier: 6.0.2 version: 6.0.2 + qrcode: + specifier: ^1.5.3 + version: 1.5.4 react: specifier: 18.2.0 version: 18.2.0 @@ -331,6 +358,9 @@ importers: recharts: specifier: ^2.12.7 version: 2.12.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + rotating-file-stream: + specifier: 3.2.3 + version: 3.2.3 slugify: specifier: ^1.6.6 version: 1.6.6 @@ -392,9 +422,18 @@ importers: '@types/node': specifier: ^18.17.0 version: 18.19.42 + '@types/node-os-utils': + specifier: 1.3.4 + version: 1.3.4 '@types/node-schedule': specifier: 2.1.6 version: 2.1.6 + '@types/nodemailer': + specifier: ^6.4.15 + version: 6.4.16 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.5 '@types/react': specifier: 18.3.5 version: 18.3.5 From d43cd52762bb85fd5ad599180477d45489b9ecba Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:58:35 -0600 Subject: [PATCH 59/61] refactor: update --- apps/dokploy/package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index f08400ff..1839c0b2 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,6 +35,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { + "bl": "6.0.11", "rotating-file-stream": "3.2.3", "qrcode": "^1.5.3", "otpauth": "^9.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91b15544..6f2cd6bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,6 +238,9 @@ importers: bcrypt: specifier: 5.1.1 version: 5.1.1 + bl: + specifier: 6.0.11 + version: 6.0.11 boxen: specifier: ^7.1.1 version: 7.1.1 From 55fae23ce3e8f289cc9314db6cb44168380c035a Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Mon, 27 Jan 2025 14:36:07 +1100 Subject: [PATCH 60/61] feat(template): added shlink, a url shortener service --- apps/dokploy/public/templates/shlink.svg | 8 +++++ .../templates/shlink/docker-compose.yml | 29 +++++++++++++++ apps/dokploy/templates/shlink/index.ts | 35 +++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 ++++++++ 4 files changed, 87 insertions(+) create mode 100644 apps/dokploy/public/templates/shlink.svg create mode 100644 apps/dokploy/templates/shlink/docker-compose.yml create mode 100644 apps/dokploy/templates/shlink/index.ts diff --git a/apps/dokploy/public/templates/shlink.svg b/apps/dokploy/public/templates/shlink.svg new file mode 100644 index 00000000..6253cd36 --- /dev/null +++ b/apps/dokploy/public/templates/shlink.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/shlink/docker-compose.yml b/apps/dokploy/templates/shlink/docker-compose.yml new file mode 100644 index 00000000..53f48216 --- /dev/null +++ b/apps/dokploy/templates/shlink/docker-compose.yml @@ -0,0 +1,29 @@ +services: + shlink: + image: shlinkio/shlink:stable + environment: + - INITIAL_API_KEY=${INITIAL_API_KEY} + - DEFAULT_DOMAIN=${DEFAULT_DOMAIN} + # Note: you should also update SHLINK_SERVER_URL in the shlink-web service. + - IS_HTTPS_ENABLED=false + volumes: + - shlink-data:/etc/shlink/data + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/rest/v3/health"] + interval: 30s + timeout: 10s + retries: 3 + shlink-web: + image: shlinkio/shlink-web-client + environment: + - SHLINK_SERVER_API_KEY=${INITIAL_API_KEY} + # Note: if you've set IS_HTTPS_ENABLED=true, change http to https. + - SHLINK_SERVER_URL=http://${DEFAULT_DOMAIN} + healthcheck: + test: ["CMD", "curl", "-f", "http://127.0.0.1:8080"] + interval: 30s + timeout: 10s + retries: 3 + +volumes: + shlink-data: \ No newline at end of file diff --git a/apps/dokploy/templates/shlink/index.ts b/apps/dokploy/templates/shlink/index.ts new file mode 100644 index 00000000..1e456e1c --- /dev/null +++ b/apps/dokploy/templates/shlink/index.ts @@ -0,0 +1,35 @@ +import { + type DomainSchema, + type Schema, + type Template, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const defaultDomain = generateRandomDomain(schema); + const initialApiKey = generatePassword(30); + + const domains: DomainSchema[] = [ + { + host: `web-${defaultDomain}`, + port: 8080, + serviceName: "shlink-web", + }, + { + host: defaultDomain, + port: 8080, + serviceName: "shlink", + }, + ]; + + const envs = [ + `INITIAL_API_KEY=${initialApiKey}`, + `DEFAULT_DOMAIN=${defaultDomain}`, + ]; + + return { + envs, + domains, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 7ba3829f..57698af3 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1423,4 +1423,19 @@ export const templates: TemplateData[] = [ tags: ["q&a", "self-hosted"], load: () => import("./answer/index").then((m) => m.generate), }, + { + id: "shlink", + name: "Shlink", + version: "v1.4.1", + description: + "URL shortener that can be used to serve shortened URLs under your own domain.", + logo: "shlink.svg", + links: { + github: "https://github.com/shlinkio/shlink", + website: "https://shlink.io", + docs: "https://shlink.io/documentation", + }, + tags: ["sharing", "shortener", "url"], + load: () => import("./shlink/index").then((m) => m.generate), + }, ]; From 27738d253e6cd134709a9dde357318a7e0fe9ea9 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Tue, 28 Jan 2025 01:47:14 +1100 Subject: [PATCH 61/61] fix(template): shlink version stable in index.ts --- apps/dokploy/templates/shlink/docker-compose.yml | 2 +- apps/dokploy/templates/templates.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/templates/shlink/docker-compose.yml b/apps/dokploy/templates/shlink/docker-compose.yml index 53f48216..6d15a26d 100644 --- a/apps/dokploy/templates/shlink/docker-compose.yml +++ b/apps/dokploy/templates/shlink/docker-compose.yml @@ -26,4 +26,4 @@ services: retries: 3 volumes: - shlink-data: \ No newline at end of file + shlink-data: diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 57698af3..98bbdbc5 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1426,7 +1426,7 @@ export const templates: TemplateData[] = [ { id: "shlink", name: "Shlink", - version: "v1.4.1", + version: "stable", description: "URL shortener that can be used to serve shortened URLs under your own domain.", logo: "shlink.svg",