Compare commits

..

36 Commits

Author SHA1 Message Date
Mauricio Siu
6edd2a81e5 refactor: lint 2025-01-22 23:45:03 -06:00
Mauricio Siu
fe5b8782e9 chore: bump version 2025-01-22 23:44:43 -06:00
Mauricio Siu
71f28fae70 Merge pull request #1176 from wish-oss/feat/refactor-templates
feat:update templates glance and homarr
2025-01-22 23:43:53 -06:00
Mauricio Siu
c44618aa95 Merge pull request #1169 from SHABIN-K/add-malayalam-language
style(i18n) add malayalamlanguage
2025-01-22 23:39:37 -06:00
Mauricio Siu
c7d86dd430 Merge pull request #1178 from Dokploy/1175-preview-deployment-not-found-correctly
fix: set right branch in preview remote deployments
2025-01-22 23:33:29 -06:00
Mauricio Siu
e50bbd1a6a fix: set right branch in preview remote deployments 2025-01-22 23:33:05 -06:00
vishalkadam47
d5ff91563a fix: docker-compose.yaml to docker-compose.yml 2025-01-23 10:08:50 +05:30
vishalkadam47
210fe0759c feat:update templates glance and homarr 2025-01-23 03:43:04 +05:30
Shabin k
edff66900e add malayalam support 2025-01-22 12:26:05 +05:30
Shabin k
4cf2177928 add malayalam support 2025-01-22 12:23:38 +05:30
Shabin k
4a8306b015 Merge branch 'Dokploy:canary' into add-malayalam-language 2025-01-22 12:22:54 +05:30
Shabin k
f92d6693c3 add malayalam support 2025-01-22 12:22:27 +05:30
Mauricio Siu
81248ed03f fix: add continue to process all applications 2025-01-22 00:39:28 -06:00
Mauricio Siu
c7d5900e11 chore: bump version 2025-01-22 00:25:25 -06:00
Mauricio Siu
d0608f43a9 Merge pull request #1166 from rahadi23/bugfix/1165-navigation-items-permissions
fix: filter navigation items based on user's permissions and role
2025-01-22 00:24:55 -06:00
Mauricio Siu
adaf12a9a4 refactor: update 2025-01-22 00:16:00 -06:00
Mauricio Siu
baa2ca20f4 Merge branch 'canary' into bugfix/1165-navigation-items-permissions 2025-01-22 00:15:31 -06:00
Mauricio Siu
537caf02e5 Merge pull request #1167 from Dokploy/fix/creation-users
refactor: make protected instead of admin
2025-01-22 00:14:49 -06:00
Mauricio Siu
02ff507094 refactor: update lint 2025-01-22 00:14:30 -06:00
Mauricio Siu
53df7d969e refactor: make protected instead of admin 2025-01-22 00:13:22 -06:00
Rahadi Jalu
9e6e68558a fix: adjust dialog title based on add/update condition 2025-01-22 11:26:18 +07:00
Rahadi Jalu
a2e9ea2986 fix: add condition to show create project button 2025-01-22 11:26:18 +07:00
Rahadi Jalu
026e1bece6 fix: filter navigation items based on user's permissions and role 2025-01-22 11:26:18 +07:00
Mauricio Siu
51f6e08e16 Merge pull request #1158 from nktnet1/superset-unofficial-template
feat(template): added apache superset (unofficial)
2025-01-21 21:59:04 -06:00
Mauricio Siu
c0f8218ad9 Merge pull request #1161 from nktnet1/volume-file-mount-content-line-clamp
fix(ui): volume file mount content, line clamp and preserve whitespace
2025-01-21 21:51:07 -06:00
Mauricio Siu
35dd6bff7a Merge pull request #1160 from nktnet1/fix-invisible-mount-path
fix(ui): volume mountPath undefined for file mount - meant to display filePath instead?
2025-01-21 21:50:33 -06:00
Mauricio Siu
8dad8fd008 Merge pull request #1162 from mikield/add-ukrainian-language
style(i18n) add ukrainian language
2025-01-21 21:31:33 -06:00
Tam Nguyen
52dbc0d8f1 fix(template): superset healthchecks 2025-01-22 09:14:21 +11:00
Vladyslav G
692f883064 style(i18n) add ukrainian language 2025-01-21 16:21:29 +01:00
Khiet Tam Nguyen
6d90e268f7 fix(ui): volume file mount content, line clamp and preserve whitespace 2025-01-22 01:09:44 +11:00
Khiet Tam Nguyen
1d86f1a0fc fix(template): added analytics tag to superset 2025-01-22 00:05:55 +11:00
Khiet Tam Nguyen
c7338983b8 refactor(ui): clearer ui display condition for volume mount display 2025-01-21 23:45:54 +11:00
Khiet Tam Nguyen
ce06cd42b3 fix(ui): show filePath instead of mountPath for file mounts 2025-01-21 23:41:05 +11:00
Khiet Tam Nguyen
1a44a0ea5a refactor(template): use dokploy mount volume for superset_config.py 2025-01-21 23:14:28 +11:00
Khiet Tam Nguyen
444121f8d8 fix(template): more appropriate tags for superset 2025-01-21 20:40:46 +11:00
Khiet Tam Nguyen
05a75edbec feat(template): added apache superset (unofficial) 2025-01-21 19:03:35 +11:00
24 changed files with 945 additions and 356 deletions

View File

@@ -100,7 +100,7 @@ export const ShowVolumes = ({ id, type }: Props) => {
{mount.type === "file" && (
<div className="flex flex-col gap-1">
<span className="font-medium">Content</span>
<span className="text-sm text-muted-foreground">
<span className="text-sm text-muted-foreground line-clamp-[10] whitespace-break-spaces">
{mount.content}
</span>
</div>
@@ -113,12 +113,21 @@ export const ShowVolumes = ({ id, type }: Props) => {
</span>
</div>
)}
<div className="flex flex-col gap-1">
<span className="font-medium">Mount Path</span>
<span className="text-sm text-muted-foreground">
{mount.mountPath}
</span>
</div>
{mount.type === "file" ? (
<div className="flex flex-col gap-1">
<span className="font-medium">File Path</span>
<span className="text-sm text-muted-foreground">
{mount.filePath}
</span>
</div>
) : (
<div className="flex flex-col gap-1">
<span className="font-medium">Mount Path</span>
<span className="text-sm text-muted-foreground">
{mount.mountPath}
</span>
</div>
)}
</div>
<div className="flex flex-row gap-1">
<UpdateVolume

View File

@@ -118,7 +118,7 @@ export const HandleProject = ({ projectId }: Props) => {
</DialogTrigger>
<DialogContent className="sm:m:max-w-lg ">
<DialogHeader>
<DialogTitle>Add a project</DialogTitle>
<DialogTitle>{projectId ? "Update" : "Add a"} project</DialogTitle>
<DialogDescription>The home of something big!</DialogDescription>
</DialogHeader>
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}

View File

@@ -87,9 +87,12 @@ export const ShowProjects = () => {
Create and manage your projects
</CardDescription>
</CardHeader>
<div className="">
<HandleProject />
</div>
{(auth?.rol === "admin" || user?.canCreateProjects) && (
<div className="">
<HandleProject />
</div>
)}
</div>
<CardContent className="space-y-2 py-8 border-t gap-4 flex flex-col min-h-[60vh]">

View File

@@ -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<AppRouter>["auth"]["get"];
type UserQueryOutput = inferRouterOutputs<AppRouter>["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 (
<SidebarProvider
@@ -486,173 +588,185 @@ export default function Page({ children }: Props) {
<SidebarGroup>
<SidebarGroupLabel>Home</SidebarGroupLabel>
<SidebarMenu>
{filteredHome.map((item) => (
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
{item.isSingle ? (
<SidebarMenuButton
asChild
tooltip={item.title}
className={cn(isActiveRoute(item.url) && "bg-border")}
>
<Link
href={item.url}
className="flex w-full items-center gap-2"
>
<item.icon
className={cn(
isActiveRoute(item.url) && "text-primary",
)}
/>
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
) : (
<>
<CollapsibleTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={item.isActive}
>
{item.icon && <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 }),
);
<span>{item.title}</span>
{item.items?.length && (
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
return (
<Collapsible
key={item.title}
asChild
defaultOpen={isActive}
className="group/collapsible"
>
<SidebarMenuItem>
{isSingle ? (
<SidebarMenuButton
asChild
tooltip={item.title}
className={cn(isActive && "bg-border")}
>
<Link
href={item.url}
className="flex w-full items-center gap-2"
>
{item.icon && (
<item.icon
className={cn(isActive && "text-primary")}
/>
)}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
className={cn(
isActiveRoute(subItem.url) && "bg-border",
)}
>
<Link
href={subItem.url}
className="flex w-full items-center"
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
) : (
<>
<CollapsibleTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={isActive}
>
{item.icon && <item.icon />}
<span>{item.title}</span>
{item.items?.length && (
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
)}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
className={cn(isActive && "bg-border")}
>
{subItem.icon && (
<span className="mr-2">
<subItem.icon
className={cn(
"h-4 w-4 text-muted-foreground",
isActiveRoute(subItem.url) &&
"text-primary",
)}
/>
</span>
)}
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
)}
</SidebarMenuItem>
</Collapsible>
))}
<Link
href={subItem.url}
className="flex w-full items-center"
>
{subItem.icon && (
<span className="mr-2">
<subItem.icon
className={cn(
"h-4 w-4 text-muted-foreground",
isActive && "text-primary",
)}
/>
</span>
)}
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
)}
</SidebarMenuItem>
</Collapsible>
);
})}
</SidebarMenu>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Settings</SidebarGroupLabel>
<SidebarMenu className="gap-2">
{filteredSettings.map((item) => (
<Collapsible
key={item.title}
asChild
defaultOpen={item.isActive}
className="group/collapsible"
>
<SidebarMenuItem>
{item.isSingle ? (
<SidebarMenuButton
asChild
tooltip={item.title}
className={cn(isActiveRoute(item.url) && "bg-border")}
>
<Link
href={item.url}
className="flex w-full items-center gap-2"
>
<item.icon
className={cn(
isActiveRoute(item.url) && "text-primary",
)}
/>
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
) : (
<>
<CollapsibleTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={item.isActive}
>
{item.icon && <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 }),
);
<span>{item.title}</span>
{item.items?.length && (
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
return (
<Collapsible
key={item.title}
asChild
defaultOpen={isActive}
className="group/collapsible"
>
<SidebarMenuItem>
{isSingle ? (
<SidebarMenuButton
asChild
tooltip={item.title}
className={cn(isActive && "bg-border")}
>
<Link
href={item.url}
className="flex w-full items-center gap-2"
>
{item.icon && (
<item.icon
className={cn(isActive && "text-primary")}
/>
)}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
className={cn(
isActiveRoute(subItem.url) && "bg-border",
)}
>
<Link
href={subItem.url}
className="flex w-full items-center"
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
) : (
<>
<CollapsibleTrigger asChild>
<SidebarMenuButton
tooltip={item.title}
isActive={isActive}
>
{item.icon && <item.icon />}
<span>{item.title}</span>
{item.items?.length && (
<ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
)}
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton
asChild
className={cn(isActive && "bg-border")}
>
{subItem.icon && (
<span className="mr-2">
<subItem.icon
className={cn(
"h-4 w-4 text-muted-foreground",
isActiveRoute(subItem.url) &&
"text-primary",
)}
/>
</span>
)}
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
)}
</SidebarMenuItem>
</Collapsible>
))}
<Link
href={subItem.url}
className="flex w-full items-center"
>
{subItem.icon && (
<span className="mr-2">
<subItem.icon
className={cn(
"h-4 w-4 text-muted-foreground",
isActive && "text-primary",
)}
/>
</span>
)}
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</>
)}
</SidebarMenuItem>
</Collapsible>
);
})}
</SidebarMenu>
</SidebarGroup>
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Extra</SidebarGroupLabel>
<SidebarMenu>
{data.help.map((item: ExternalLink) => (
{help.map((item: ExternalLink) => (
<SidebarMenuItem key={item.name}>
<SidebarMenuButton asChild>
<a

View File

@@ -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" },
@@ -17,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;

View File

@@ -1,6 +1,6 @@
{
"name": "dokploy",
"version": "v0.17.5",
"version": "v0.17.7",
"private": true,
"license": "Apache-2.0",
"type": "module",

View File

@@ -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(

View File

@@ -0,0 +1 @@
{}

View File

@@ -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": "ഉപയോക്തൃനാമം"
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -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": "Ім'я користувача"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="256px" height="128px" viewBox="0 0 256 128" version="1.1" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid">
<title>Superset</title>
<g>
<path d="M190.218924,0 C168.269282,0 148.049828,12.3487941 128.508879,33.9252584 C109.307183,12.0095415 88.748476,0 65.7810761,0 C27.7508614,0 0,27.1402067 0,63.67771 C0,100.215213 27.7508614,127.016168 65.7810761,127.016168 C89.1555791,127.016168 107.271667,116.058309 127.491121,94.2104426 C147.03207,116.12616 166.912271,127.084018 190.218924,127.084018 C228.249139,127.016168 256,100.316989 256,63.67771 C256,27.038431 228.249139,0 190.218924,0 Z M66.0524781,88.6806255 C49.9379804,88.6806255 40.3371323,78.0620196 40.3371323,64.0169626 C40.3371323,49.9719056 49.9379804,39.0479724 66.0524781,39.0479724 C79.6225815,39.0479724 90.716141,49.9719056 102.725682,64.6954678 C91.3946462,78.4012722 79.4190299,88.6806255 66.0524781,88.6806255 Z M189.065465,88.6806255 C175.698913,88.6806255 164.401802,78.0620196 152.392261,64.0169626 C164.741055,49.2934005 175.359661,39.0479724 189.065465,39.0479724 C205.179963,39.0479724 214.679035,50.1076067 214.679035,64.0169626 C214.679035,77.9263186 205.179963,88.6806255 189.065465,88.6806255 Z" fill="#484848"></path>
<path d="M156.124039,117.958124 L181.703684,87.4253909 C171.526107,84.3721177 162.12881,75.2122979 152.392261,63.8473363 L127.491121,94.2104426 C135.643361,103.668805 145.322237,111.695521 156.124039,117.958124 Z" fill="#20A7C9"></path>
<path d="M128.508879,33.8913332 C120.41092,24.2972701 110.793109,16.0907501 100.045587,9.60084813 L74.432017,40.4728333 C84.1685661,43.8653591 92.7855818,52.6180758 101.945402,63.7794858 L102.963159,64.4919162 L128.508879,33.8913332 Z" fill="#20A7C9"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,6 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 40 40">
<g id="ss11151339769_1">
<path d="M 0 40 L 0 0 L 40 0 L 40 40 Z" fill="transparent"></path>
<path d="M 34.95 0 L 5.05 0 C 2.262 0 0 2.262 0 5.05 L 0 34.95 C 0 37.738 2.262 40 5.05 40 L 34.95 40 C 37.738 40 40 37.738 40 34.95 L 40 5.05 C 40 2.262 37.738 0 34.95 0 Z M 8.021 14.894 C 8.021 12.709 9.794 10.935 11.979 10.935 L 19.6 10.935 C 19.712 10.935 19.815 11.003 19.862 11.106 C 19.909 11.209 19.888 11.329 19.812 11.415 L 18.141 13.229 C 17.85 13.544 17.441 13.726 17.012 13.726 L 12 13.726 C 11.344 13.726 10.812 14.259 10.812 14.915 L 10.812 17.909 C 10.812 18.294 10.5 18.606 10.115 18.606 L 8.721 18.606 C 8.335 18.606 8.024 18.294 8.024 17.909 L 8.024 14.894 Z M 31.729 25.106 C 31.729 27.291 29.956 29.065 27.771 29.065 L 24.532 29.065 C 22.347 29.065 20.574 27.291 20.574 25.106 L 20.574 19.438 C 20.574 19.053 20.718 18.682 20.979 18.397 L 22.868 16.347 C 22.947 16.262 23.071 16.232 23.182 16.274 C 23.291 16.318 23.365 16.421 23.365 16.538 L 23.365 25.088 C 23.365 25.744 23.897 26.276 24.553 26.276 L 27.753 26.276 C 28.409 26.276 28.941 25.744 28.941 25.088 L 28.941 14.915 C 28.941 14.259 28.409 13.726 27.753 13.726 L 24.032 13.726 C 23.606 13.726 23.2 13.906 22.909 14.218 L 11.812 26.276 L 18.479 26.276 C 18.865 26.276 19.176 26.588 19.176 26.974 L 19.176 28.368 C 19.176 28.753 18.865 29.065 18.479 29.065 L 9.494 29.065 C 8.679 29.065 8.018 28.403 8.018 27.588 L 8.018 26.85 C 8.018 26.479 8.156 26.124 8.409 25.85 L 20.85 12.335 C 21.674 11.441 22.829 10.935 24.044 10.935 L 27.768 10.935 C 29.953 10.935 31.726 12.709 31.726 14.894 L 31.726 25.106 Z" fill="rgb(0,0,0)"></path>
</g>
<svg width="136" height="136" viewBox="0 0 136 136" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2343_96406)">
<path d="M136 2.28882e-05H0L0.000144482 136H136V2.28882e-05ZM27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27ZM107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z" fill="black"/>
<path d="M27.27 50.6401C27.27 43.2101 33.3 37.1801 40.73 37.1801H66.64C67.02 37.1801 67.37 37.4101 67.53 37.7601C67.69 38.1101 67.62 38.5201 67.36 38.8101L61.68 44.9801C60.69 46.0501 59.3 46.6701 57.84 46.6701H40.8C38.57 46.6701 36.76 48.4801 36.76 50.7101V60.8901C36.76 62.2001 35.7 63.2601 34.39 63.2601H29.65C28.34 63.2601 27.28 62.2001 27.28 60.8901V50.6401H27.27Z" fill="white"/>
<path d="M107.88 85.3601C107.88 92.7901 101.85 98.82 94.42 98.82H83.41C75.98 98.82 69.95 92.7901 69.95 85.3601V66.0901C69.95 64.7801 70.44 63.5201 71.33 62.5501L77.75 55.5801C78.02 55.2901 78.44 55.1901 78.82 55.3301C79.19 55.4801 79.44 55.83 79.44 56.23V85.3001C79.44 87.5301 81.25 89.3401 83.48 89.3401H94.36C96.59 89.3401 98.4 87.5301 98.4 85.3001V50.7101C98.4 48.4801 96.59 46.6701 94.36 46.6701H81.71C80.26 46.6701 78.88 47.2801 77.89 48.3401L40.16 89.3401H62.83C64.14 89.3401 65.2 90.4001 65.2 91.7101V96.4501C65.2 97.7601 64.14 98.82 62.83 98.82H32.28C29.51 98.82 27.26 96.5701 27.26 93.8001V91.29C27.26 90.03 27.73 88.8201 28.59 87.8901L70.89 41.9401C73.69 38.9001 77.62 37.1801 81.75 37.1801H94.41C101.84 37.1801 107.87 43.2101 107.87 50.6401V85.3601H107.88Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0_2343_96406">
<rect width="136" height="136" rx="16" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -345,7 +345,7 @@ export const settingsRouter = createTRPCRouter({
writeConfig("middlewares", input.traefikConfig);
return true;
}),
getUpdateData: adminProcedure.mutation(async () => {
getUpdateData: protectedProcedure.mutation(async () => {
if (IS_CLOUD) {
return DEFAULT_UPDATE_DATA;
}
@@ -373,10 +373,10 @@ export const settingsRouter = createTRPCRouter({
return true;
}),
getDokployVersion: adminProcedure.query(() => {
getDokployVersion: protectedProcedure.query(() => {
return packageInfo.version;
}),
getReleaseTag: adminProcedure.query(() => {
getReleaseTag: protectedProcedure.query(() => {
return getDokployImageTag();
}),
readDirectories: protectedProcedure

View File

@@ -0,0 +1,8 @@
services:
glance:
image: glanceapp/glance
volumes:
- ../files/app/glance.yml:/app/glance.yml
ports:
- 8080
restart: unless-stopped

View File

@@ -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,
};
}

View File

@@ -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

View File

@@ -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,
};
}

View File

@@ -0,0 +1,62 @@
# Note: this is an UNOFFICIAL production docker image build for Superset:
# - https://github.com/amancevice/docker-superset
#
# After deploying this image, you will need to run one of the two
# commands below in a terminal within the superset container:
# $ superset-demo # Initialise database + load demo charts/datasets
# $ superset-init # Initialise database only
#
# You will be prompted to enter the credentials for the admin user.
services:
superset:
image: amancevice/superset
restart: always
depends_on:
- db
- redis
environment:
SECRET_KEY: ${SECRET_KEY}
MAPBOX_API_KEY: ${MAPBOX_API_KEY}
POSTGRES_USER: ${POSTGRES_USER}
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
db:
image: postgres
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 30s
timeout: 10s
retries: 3
networks:
- dokploy-network
redis:
image: redis
restart: always
volumes:
- redis:/data
command: redis-server --requirepass ${REDIS_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
interval: 30s
timeout: 10s
retries: 3
networks:
- dokploy-network
volumes:
postgres:
redis:

View File

@@ -0,0 +1,67 @@
import {
type DomainSchema,
type Schema,
type Template,
generatePassword,
generateRandomDomain,
} from "../utils";
export function generate(schema: Schema): Template {
const mapboxApiKey = "";
const secretKey = generatePassword(30);
const postgresDb = "superset";
const postgresUser = "superset";
const postgresPassword = generatePassword(30);
const redisPassword = generatePassword(30);
const domains: DomainSchema[] = [
{
host: generateRandomDomain(schema),
port: 8088,
serviceName: "superset",
},
];
const envs = [
`SECRET_KEY=${secretKey}`,
`MAPBOX_API_KEY=${mapboxApiKey}`,
`POSTGRES_DB=${postgresDb}`,
`POSTGRES_USER=${postgresUser}`,
`POSTGRES_PASSWORD=${postgresPassword}`,
`REDIS_PASSWORD=${redisPassword}`,
];
const mounts: Template["mounts"] = [
{
filePath: "./superset/superset_config.py",
content: `
import os
SECRET_KEY = os.getenv("SECRET_KEY")
MAPBOX_API_KEY = os.getenv("MAPBOX_API_KEY", "")
CACHE_CONFIG = {
"CACHE_TYPE": "RedisCache",
"CACHE_DEFAULT_TIMEOUT": 300,
"CACHE_KEY_PREFIX": "superset_",
"CACHE_REDIS_HOST": "redis",
"CACHE_REDIS_PORT": 6379,
"CACHE_REDIS_DB": 1,
"CACHE_REDIS_URL": f"redis://:{os.getenv('REDIS_PASSWORD')}@redis: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_TRACK_MODIFICATIONS = True
`.trim(),
},
];
return {
envs,
domains,
mounts,
};
}

View File

@@ -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),
@@ -1298,4 +1298,47 @@ export const templates: TemplateData[] = [
tags: ["developer", "tools"],
load: () => import("./it-tools/index").then((m) => m.generate),
},
{
id: "superset",
name: "Superset (Unofficial)",
version: "latest",
description: "Data visualization and data exploration platform.",
logo: "superset.svg",
links: {
github: "https://github.com/amancevice/docker-superset",
website: "https://superset.apache.org",
docs: "https://superset.apache.org/docs/intro",
},
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),
},
];

View File

@@ -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,
});