Compare commits

...

21 Commits

Author SHA1 Message Date
Mauricio Siu
385fbf4af5 Merge pull request #355 from Dokploy/canary
v0.6.3
2024-08-16 22:26:35 -06:00
Mauricio Siu
44e75ee7e1 refactor: update deps 2024-08-16 22:10:23 -06:00
Mauricio Siu
6b4d6eac1d chore: bump version 2024-08-16 22:07:02 -06:00
Mauricio Siu
175e84f50e refactor: update container id 2024-08-13 23:29:51 -06:00
Mauricio Siu
efb646c43d Merge pull request #346 from Dokploy/282-add-option-to-revert-dokploy-version-opt-in-based-auto-updates
282 add option to revert dokploy version opt in based auto updates
2024-08-13 23:12:57 -06:00
Mauricio Siu
fa950dae39 fix(settings): prevent to download the latest image on reload 2024-08-13 23:04:21 -06:00
Mauricio Siu
712ad25e7a feat(permission): add permission to access to ssh key section 2024-08-13 22:19:04 -06:00
Mauricio Siu
35a41e774e Merge pull request #343 from Tuluobo/bugfix/delete_service_with_container
fix(ui): close dialog after templete selected & add config editor line wrapping
2024-08-13 22:02:33 -06:00
Mauricio Siu
c2ac193fbe Merge pull request #344 from Dokploy/340-dokploy-postgres-and-redis-are-exposed
fix(services): set published port 0 to prevent swarm assign random po…
2024-08-13 21:56:12 -06:00
Mauricio Siu
ce3c89a715 Merge pull request #342 from Dokploy/326-dokploy-doesnt-persist-registry-tokens
fix(docker): add root docker to prevent registry delete in each resta…
2024-08-13 21:52:07 -06:00
Mauricio Siu
96f7206a1d fix(services): set published port 0 to prevent swarm assign random ports #340 2024-08-13 21:49:21 -06:00
Mauricio Siu
b7ace886f3 fix(docker): add root docker to prevent registry delete in each restart/update dokploy server #326 2024-08-13 20:40:34 -06:00
Mauricio Siu
5dc330eaa3 Merge pull request #341 from Dokploy/337-incorrect-github-apps-install-link-when-app-name-contain-special-characters
fix(github): use github url to install the application #337
2024-08-13 20:04:46 -06:00
Mauricio Siu
b7f5bee2f8 fix(github): use github url to install the application #337 2024-08-13 19:57:10 -06:00
Tuluobo
19ee5f073b feat: add line wrapping for traefik config editor 2024-08-13 20:51:50 +08:00
Tuluobo
1fd4a6ae80 refactor: close dialog after selected template 2024-08-13 19:38:09 +08:00
Mauricio Siu
3c8a412014 Merge pull request #339 from Vladislav-CS/patch-1
fix: responsive design in the project settings page
2024-08-10 16:00:06 -06:00
Vladislav Popovič
eee617719b Update show-deployments.tsx 2024-08-10 22:19:15 +03:00
Mauricio Siu
fc611946a6 Merge pull request #334 from Vladislav-CS/fix-typos
fix: typos
2024-08-08 10:59:22 -06:00
Vladislav Popovič
af13c84968 Update add-template.tsx 2024-08-08 18:21:35 +03:00
Vladislav Popovič
ddb78ef8dd Update show-ssh-keys.tsx 2024-08-08 18:17:12 +03:00
31 changed files with 3186 additions and 83 deletions

View File

@@ -46,6 +46,7 @@ export const ShowTraefikConfig = ({ applicationId }: Props) => {
<div className="flex flex-col pt-2 relative"> <div className="flex flex-col pt-2 relative">
<div className="flex flex-col gap-6 max-h-[35rem] min-h-[10rem] overflow-y-auto"> <div className="flex flex-col gap-6 max-h-[35rem] min-h-[10rem] overflow-y-auto">
<CodeEditor <CodeEditor
lineWrapping
value={data || "Empty"} value={data || "Empty"}
disabled disabled
className="font-mono" className="font-mono"

View File

@@ -144,6 +144,7 @@ export const UpdateTraefikConfig = ({ applicationId }: Props) => {
<FormLabel>Traefik config</FormLabel> <FormLabel>Traefik config</FormLabel>
<FormControl> <FormControl>
<CodeEditor <CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono" wrapperClassName="h-[35rem] font-mono"
placeholder={`http: placeholder={`http:
routers: routers:

View File

@@ -53,7 +53,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
<div className="flex flex-row items-center gap-2 flex-wrap"> <div className="flex flex-row items-center gap-2 flex-wrap">
<span>Webhook URL: </span> <span>Webhook URL: </span>
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
<span className="text-muted-foreground"> <span className="break-all text-muted-foreground">
{`${url}/api/deploy/${data?.refreshToken}`} {`${url}/api/deploy/${data?.refreshToken}`}
</span> </span>
<RefreshToken applicationId={applicationId} /> <RefreshToken applicationId={applicationId} />
@@ -72,7 +72,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
{deployments?.map((deployment) => ( {deployments?.map((deployment) => (
<div <div
key={deployment.deploymentId} key={deployment.deploymentId}
className="flex items-center justify-between rounded-lg border p-4" className="flex items-center justify-between rounded-lg border p-4 gap-2"
> >
<div className="flex flex-col"> <div className="flex flex-col">
<span className="flex items-center gap-4 font-medium capitalize text-foreground"> <span className="flex items-center gap-4 font-medium capitalize text-foreground">
@@ -87,7 +87,7 @@ export const ShowDeployments = ({ applicationId }: Props) => {
{deployment.title} {deployment.title}
</span> </span>
{deployment.description && ( {deployment.description && (
<span className="text-sm text-muted-foreground"> <span className="break-all text-sm text-muted-foreground">
{deployment.description} {deployment.description}
</span> </span>
)} )}

View File

@@ -104,6 +104,7 @@ export const ShowTraefikFile = ({ path }: Props) => {
</FormDescription> </FormDescription>
<FormControl> <FormControl>
<CodeEditor <CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono" wrapperClassName="h-[35rem] font-mono"
placeholder={`http: placeholder={`http:
routers: routers:

View File

@@ -55,6 +55,7 @@ interface Props {
export const AddTemplate = ({ projectId }: Props) => { export const AddTemplate = ({ projectId }: Props) => {
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const [open, setOpen] = useState(false);
const { data } = api.compose.templates.useQuery(); const { data } = api.compose.templates.useQuery();
const [selectedTags, setSelectedTags] = useState<string[]>([]); const [selectedTags, setSelectedTags] = useState<string[]>([]);
const { data: tags, isLoading: isLoadingTags } = const { data: tags, isLoading: isLoadingTags } =
@@ -75,14 +76,14 @@ export const AddTemplate = ({ projectId }: Props) => {
}) || []; }) || [];
return ( return (
<Dialog> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger className="w-full"> <DialogTrigger className="w-full">
<DropdownMenuItem <DropdownMenuItem
className="w-full cursor-pointer space-x-3" className="w-full cursor-pointer space-x-3"
onSelect={(e) => e.preventDefault()} onSelect={(e) => e.preventDefault()}
> >
<PuzzleIcon className="size-4 text-muted-foreground" /> <PuzzleIcon className="size-4 text-muted-foreground" />
<span>Templates</span> <span>Template</span>
</DropdownMenuItem> </DropdownMenuItem>
</DialogTrigger> </DialogTrigger>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl p-0"> <DialogContent className="max-h-screen overflow-y-auto sm:max-w-7xl p-0">
@@ -283,6 +284,7 @@ export const AddTemplate = ({ projectId }: Props) => {
utils.project.one.invalidate({ utils.project.one.invalidate({
projectId, projectId,
}); });
setOpen(false);
}) })
.catch(() => { .catch(() => {
toast.error( toast.error(

View File

@@ -9,36 +9,11 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { format } from "date-fns";
import { BadgeCheck } from "lucide-react"; import { BadgeCheck } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { RemoveGithubApp } from "./remove-github-app"; import { RemoveGithubApp } from "./remove-github-app";
export const generateName = () => {
const n1 = ["Blue", "Green", "Red", "Orange", "Violet", "Indigo", "Yellow"];
const n2 = [
"One",
"Two",
"Three",
"Four",
"Five",
"Six",
"Seven",
"Eight",
"Nine",
"Zero",
];
return `Dokploy-${n1[Math.round(Math.random() * (n1.length - 1))]}-${
n2[Math.round(Math.random() * (n2.length - 1))]
}`;
};
function slugify(text: string) {
return text
.toLowerCase()
.replace(/[\s\^&*()+=!]+/g, "-")
.replace(/[\$.,*+~()'"!:@^&]+/g, "")
.replace(/-+/g, "-")
.replace(/^-+|-+$/g, "");
}
export const GithubSetup = () => { export const GithubSetup = () => {
const [isOrganization, setIsOrganization] = useState(false); const [isOrganization, setIsOrganization] = useState(false);
@@ -52,10 +27,9 @@ export const GithubSetup = () => {
const manifest = JSON.stringify( const manifest = JSON.stringify(
{ {
redirect_url: `${origin}/api/redirect?authId=${data?.authId}`, redirect_url: `${origin}/api/redirect?authId=${data?.authId}`,
name: generateName(), name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`,
url: origin, url: origin,
hook_attributes: { hook_attributes: {
// JUST FOR TESTING
url: `${url}/api/deploy/github`, url: `${url}/api/deploy/github`,
// url: `${origin}/api/webhook`, // Aquí especificas la URL del endpoint de tu webhook // url: `${origin}/api/webhook`, // Aquí especificas la URL del endpoint de tu webhook
}, },
@@ -95,8 +69,8 @@ export const GithubSetup = () => {
</div> </div>
<div className="flex items-end gap-4 flex-wrap"> <div className="flex items-end gap-4 flex-wrap">
<RemoveGithubApp /> <RemoveGithubApp />
{/* <Link <Link
href={`https://github.com/settings/apps/${data?.githubAppName}`} href={`${data?.githubAppName}`}
target="_blank" target="_blank"
className={buttonVariants({ className={buttonVariants({
className: "w-fit", className: "w-fit",
@@ -104,7 +78,7 @@ export const GithubSetup = () => {
})} })}
> >
<span className="text-sm">Manage Github App</span> <span className="text-sm">Manage Github App</span>
</Link> */} </Link>
</div> </div>
</div> </div>
) : ( ) : (
@@ -119,9 +93,9 @@ export const GithubSetup = () => {
<div className="flex flex-row gap-4"> <div className="flex flex-row gap-4">
<Link <Link
href={`https://github.com/apps/${slugify( href={`${
data.githubAppName, data.githubAppName
)}/installations/new?state=gh_setup:${data?.authId}`} }/installations/new?state=gh_setup:${data?.authId}`}
className={buttonVariants({ className: "w-fit" })} className={buttonVariants({ className: "w-fit" })}
> >
Install Github App Install Github App

View File

@@ -22,7 +22,7 @@ export const ShowDestinations = () => {
<CardHeader> <CardHeader>
<CardTitle className="text-xl">SSH Keys</CardTitle> <CardTitle className="text-xl">SSH Keys</CardTitle>
<CardDescription> <CardDescription>
Use SSH to beeing able cloning from private repositories. Use SSH to be able to clone from private repositories.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-2 pt-4"> <CardContent className="space-y-2 pt-4">

View File

@@ -39,6 +39,7 @@ const addPermissions = z.object({
canAccessToTraefikFiles: z.boolean().optional().default(false), canAccessToTraefikFiles: z.boolean().optional().default(false),
canAccessToDocker: z.boolean().optional().default(false), canAccessToDocker: z.boolean().optional().default(false),
canAccessToAPI: z.boolean().optional().default(false), canAccessToAPI: z.boolean().optional().default(false),
canAccessToSSHKeys: z.boolean().optional().default(false),
}); });
type AddPermissions = z.infer<typeof addPermissions>; type AddPermissions = z.infer<typeof addPermissions>;
@@ -82,6 +83,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
canAccessToTraefikFiles: data.canAccessToTraefikFiles, canAccessToTraefikFiles: data.canAccessToTraefikFiles,
canAccessToDocker: data.canAccessToDocker, canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI, canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
}); });
} }
}, [form, form.formState.isSubmitSuccessful, form.reset, data]); }, [form, form.formState.isSubmitSuccessful, form.reset, data]);
@@ -98,6 +100,7 @@ export const AddUserPermissions = ({ userId }: Props) => {
accesedServices: data.accesedServices || [], accesedServices: data.accesedServices || [],
canAccessToDocker: data.canAccessToDocker, canAccessToDocker: data.canAccessToDocker,
canAccessToAPI: data.canAccessToAPI, canAccessToAPI: data.canAccessToAPI,
canAccessToSSHKeys: data.canAccessToSSHKeys,
}) })
.then(async () => { .then(async () => {
toast.success("Permissions updated"); toast.success("Permissions updated");
@@ -270,6 +273,26 @@ export const AddUserPermissions = ({ userId }: Props) => {
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="canAccessToSSHKeys"
render={({ field }) => (
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
<div className="space-y-0.5">
<FormLabel>Access to SSH Keys</FormLabel>
<FormDescription>
Allow to users to access to the SSH Keys section
</FormDescription>
</div>
<FormControl>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="accesedProjects" name="accesedProjects"

View File

@@ -106,6 +106,7 @@ export const ShowMainTraefikConfig = ({ children }: Props) => {
<FormLabel>Traefik config</FormLabel> <FormLabel>Traefik config</FormLabel>
<FormControl> <FormControl>
<CodeEditor <CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono" wrapperClassName="h-[35rem] font-mono"
placeholder={`providers: placeholder={`providers:
docker: docker:

View File

@@ -109,6 +109,7 @@ export const ShowServerTraefikConfig = ({ children }: Props) => {
<FormLabel>Traefik config</FormLabel> <FormLabel>Traefik config</FormLabel>
<FormControl> <FormControl>
<CodeEditor <CodeEditor
lineWrapping
wrapperClassName="h-[35rem] font-mono" wrapperClassName="h-[35rem] font-mono"
placeholder={`http: placeholder={`http:
routers: routers:

View File

@@ -79,6 +79,16 @@ export const SettingsLayout = ({ children }: Props) => {
}, },
] ]
: []), : []),
...(user?.canAccessToSSHKeys
? [
{
title: "SSH Keys",
label: "",
icon: KeyRound,
href: "/dashboard/settings/ssh-keys",
},
]
: []),
]} ]}
/> />
</div> </div>

View File

@@ -3,6 +3,7 @@ import { json } from "@codemirror/lang-json";
import { yaml } from "@codemirror/lang-yaml"; import { yaml } from "@codemirror/lang-yaml";
import { StreamLanguage } from "@codemirror/language"; import { StreamLanguage } from "@codemirror/language";
import { properties } from "@codemirror/legacy-modes/mode/properties"; import { properties } from "@codemirror/legacy-modes/mode/properties";
import { EditorView } from "@codemirror/view";
import { githubDark, githubLight } from "@uiw/codemirror-theme-github"; import { githubDark, githubLight } from "@uiw/codemirror-theme-github";
import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror"; import CodeMirror, { type ReactCodeMirrorProps } from "@uiw/react-codemirror";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
@@ -10,6 +11,7 @@ interface Props extends ReactCodeMirrorProps {
wrapperClassName?: string; wrapperClassName?: string;
disabled?: boolean; disabled?: boolean;
language?: "yaml" | "json" | "properties"; language?: "yaml" | "json" | "properties";
lineWrapping?: boolean;
} }
export const CodeEditor = ({ export const CodeEditor = ({
@@ -36,6 +38,7 @@ export const CodeEditor = ({
: language === "json" : language === "json"
? json() ? json()
: StreamLanguage.define(properties), : StreamLanguage.define(properties),
props.lineWrapping ? EditorView.lineWrapping : [],
]} ]}
{...props} {...props}
editable={!props.disabled} editable={!props.disabled}

View File

@@ -0,0 +1 @@
ALTER TABLE "user" ADD COLUMN "canAccessToSSHKeys" boolean DEFAULT false NOT NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -211,6 +211,13 @@
"when": 1722578386823, "when": 1722578386823,
"tag": "0029_colossal_zodiak", "tag": "0029_colossal_zodiak",
"breakpoints": true "breakpoints": true
},
{
"idx": 30,
"version": "6",
"when": 1723608499147,
"tag": "0030_little_kabuki",
"breakpoints": true
} }
] ]
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "dokploy", "name": "dokploy",
"version": "v0.6.2", "version": "v0.6.3",
"private": true, "private": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"type": "module", "type": "module",
@@ -39,6 +39,7 @@
"@codemirror/lang-yaml": "^6.1.1", "@codemirror/lang-yaml": "^6.1.1",
"@codemirror/language": "^6.10.1", "@codemirror/language": "^6.10.1",
"@codemirror/legacy-modes": "6.4.0", "@codemirror/legacy-modes": "6.4.0",
"@codemirror/view": "6.29.0",
"@dokploy/trpc-openapi": "0.0.4", "@dokploy/trpc-openapi": "0.0.4",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^8.4.1",
"@hookform/resolvers": "^3.3.4", "@hookform/resolvers": "^3.3.4",

View File

@@ -35,7 +35,7 @@ export default async function handler(
.update(admins) .update(admins)
.set({ .set({
githubAppId: data.id, githubAppId: data.id,
githubAppName: data.name, githubAppName: data.html_url,
githubClientId: data.client_id, githubClientId: data.client_id,
githubClientSecret: data.client_secret, githubClientSecret: data.client_secret,
githubWebhookSecret: data.webhook_secret, githubWebhookSecret: data.webhook_secret,

View File

@@ -1,9 +1,12 @@
import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show-ssh-keys"; import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show-ssh-keys";
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { SettingsLayout } from "@/components/layouts/settings-layout"; import { SettingsLayout } from "@/components/layouts/settings-layout";
import { appRouter } from "@/server/api/root";
import { validateRequest } from "@/server/auth/auth"; import { validateRequest } from "@/server/auth/auth";
import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
import React, { type ReactElement } from "react"; import React, { type ReactElement } from "react";
import superjson from "superjson";
const Page = () => { const Page = () => {
return ( return (
@@ -26,7 +29,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { user, session } = await validateRequest(ctx.req, ctx.res); const { user, session } = await validateRequest(ctx.req, ctx.res);
if (!user || user.rol === "user") { if (!user) {
return { return {
redirect: { redirect: {
permanent: true, permanent: true,
@@ -34,8 +37,45 @@ export async function getServerSideProps(
}, },
}; };
} }
const { req, res, resolvedUrl } = ctx;
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
},
transformer: superjson,
});
return { try {
props: {}, await helpers.project.all.prefetch();
}; const auth = await helpers.auth.get.fetch();
if (auth.rol === "user") {
const user = await helpers.user.byAuthId.fetch({
authId: auth.id,
});
if (!user.canAccessToSSHKeys) {
return {
redirect: {
permanent: true,
destination: "/",
},
};
}
}
return {
props: {
trpcState: helpers.dehydrate(),
},
};
} catch (error) {
return {
props: {},
};
}
} }

View File

@@ -20,6 +20,7 @@ import {
} from "@/server/utils/docker/utils"; } from "@/server/utils/docker/utils";
import { recreateDirectory } from "@/server/utils/filesystem/directory"; import { recreateDirectory } from "@/server/utils/filesystem/directory";
import { sendDockerCleanupNotifications } from "@/server/utils/notifications/docker-cleanup"; import { sendDockerCleanupNotifications } from "@/server/utils/notifications/docker-cleanup";
import { execAsync } from "@/server/utils/process/execAsync";
import { spawnAsync } from "@/server/utils/process/spawnAsync"; import { spawnAsync } from "@/server/utils/process/spawnAsync";
import { import {
readConfig, readConfig,
@@ -49,14 +50,10 @@ import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const settingsRouter = createTRPCRouter({ export const settingsRouter = createTRPCRouter({
reloadServer: adminProcedure.mutation(async () => { reloadServer: adminProcedure.mutation(async () => {
await spawnAsync("docker", [ const { stdout } = await execAsync(
"service", "docker service inspect dokploy --format '{{.ID}}'",
"update", );
"--force", await execAsync(`docker service update --force ${stdout.trim()}`);
"--image",
getDokployImage(),
"dokploy",
]);
return true; return true;
}), }),
reloadTraefik: adminProcedure.mutation(async () => { reloadTraefik: adminProcedure.mutation(async () => {

View File

@@ -34,21 +34,23 @@ export const sshRouter = createTRPCRouter({
}); });
} }
}), }),
remove: adminProcedure.input(apiRemoveSshKey).mutation(async ({ input }) => { remove: protectedProcedure
try { .input(apiRemoveSshKey)
return await removeSSHKeyById(input.sshKeyId); .mutation(async ({ input }) => {
} catch (error) { try {
throw new TRPCError({ return await removeSSHKeyById(input.sshKeyId);
code: "BAD_REQUEST", } catch (error) {
message: "Error to delete this ssh key", throw new TRPCError({
}); code: "BAD_REQUEST",
} message: "Error to delete this ssh key",
}), });
}
}),
one: protectedProcedure.input(apiFindOneSshKey).query(async ({ input }) => { one: protectedProcedure.input(apiFindOneSshKey).query(async ({ input }) => {
const sshKey = await findSSHKeyById(input.sshKeyId); const sshKey = await findSSHKeyById(input.sshKeyId);
return sshKey; return sshKey;
}), }),
all: adminProcedure.query(async () => { all: protectedProcedure.query(async () => {
return await db.query.sshKeys.findMany({}); return await db.query.sshKeys.findMany({});
}), }),
generate: protectedProcedure generate: protectedProcedure
@@ -56,15 +58,17 @@ export const sshRouter = createTRPCRouter({
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
return await generateSSHKey(input.type); return await generateSSHKey(input.type);
}), }),
update: adminProcedure.input(apiUpdateSshKey).mutation(async ({ input }) => { update: protectedProcedure
try { .input(apiUpdateSshKey)
return await updateSSHKeyById(input); .mutation(async ({ input }) => {
} catch (error) { try {
throw new TRPCError({ return await updateSSHKeyById(input);
code: "BAD_REQUEST", } catch (error) {
message: "Error to update this ssh key", throw new TRPCError({
cause: error, code: "BAD_REQUEST",
}); message: "Error to update this ssh key",
} cause: error,
}), });
}
}),
}); });

View File

@@ -28,6 +28,7 @@ export const users = pgTable("user", {
.notNull() .notNull()
.$defaultFn(() => new Date().toISOString()), .$defaultFn(() => new Date().toISOString()),
canCreateProjects: boolean("canCreateProjects").notNull().default(false), canCreateProjects: boolean("canCreateProjects").notNull().default(false),
canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false),
canCreateServices: boolean("canCreateServices").notNull().default(false), canCreateServices: boolean("canCreateServices").notNull().default(false),
canDeleteProjects: boolean("canDeleteProjects").notNull().default(false), canDeleteProjects: boolean("canDeleteProjects").notNull().default(false),
canDeleteServices: boolean("canDeleteServices").notNull().default(false), canDeleteServices: boolean("canDeleteServices").notNull().default(false),
@@ -107,6 +108,7 @@ export const apiAssignPermissions = createSchema
canAccessToTraefikFiles: true, canAccessToTraefikFiles: true,
canAccessToDocker: true, canAccessToDocker: true,
canAccessToAPI: true, canAccessToAPI: true,
canAccessToSSHKeys: true,
}) })
.required(); .required();

View File

@@ -36,10 +36,9 @@ export const initializePostgres = async () => {
Ports: [ Ports: [
{ {
TargetPort: 5432, TargetPort: 5432,
...(process.env.NODE_ENV === "development" PublishedPort: process.env.NODE_ENV === "development" ? 5432 : 0,
? { PublishedPort: 5432 }
: {}),
Protocol: "tcp", Protocol: "tcp",
PublishMode: "host",
}, },
], ],
}, },

View File

@@ -33,10 +33,9 @@ export const initializeRedis = async () => {
Ports: [ Ports: [
{ {
TargetPort: 6379, TargetPort: 6379,
...(process.env.NODE_ENV === "development" PublishedPort: process.env.NODE_ENV === "development" ? 6379 : 0,
? { PublishedPort: 6379 }
: {}),
Protocol: "tcp", Protocol: "tcp",
PublishMode: "host",
}, },
], ],
}, },

View File

@@ -81,6 +81,7 @@ docker service create \
--network dokploy-network \ --network dokploy-network \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \
--mount type=volume,source=dokploy-docker-config,target=/root/.docker \
--publish published=3000,target=3000,mode=host \ --publish published=3000,target=3000,mode=host \
--update-parallelism 1 \ --update-parallelism 1 \
--update-order stop-first \ --update-order stop-first \

View File

@@ -65,6 +65,7 @@ docker service create \
--network dokploy-network \ --network dokploy-network \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \
--mount type=volume,source=dokploy-docker-config,target=/root/.docker \
--publish published=3000,target=3000,mode=host \ --publish published=3000,target=3000,mode=host \
--update-parallelism 1 \ --update-parallelism 1 \
--update-order stop-first \ --update-order stop-first \

View File

@@ -81,6 +81,7 @@ docker service create \
--network dokploy-network \ --network dokploy-network \
--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
--mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \
--mount type=volume,source=dokploy-docker-config,target=/root/.docker \
--publish published=3000,target=3000,mode=host \ --publish published=3000,target=3000,mode=host \
--update-parallelism 1 \ --update-parallelism 1 \
--update-order stop-first \ --update-order stop-first \

View File

@@ -20,7 +20,7 @@
"format-and-lint": "biome check .", "format-and-lint": "biome check .",
"check": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true", "check": "biome check --write --no-errors-on-unmatched --files-ignore-unknown=true",
"format-and-lint:fix": "biome check . --write", "format-and-lint:fix": "biome check . --write",
"prepare": "node .husky/install.mjs" "prepare": "node ./.config/.husky/install.mjs"
}, },
"devDependencies": { "devDependencies": {
"dotenv": "16.4.5", "dotenv": "16.4.5",

3
pnpm-lock.yaml generated
View File

@@ -114,6 +114,9 @@ importers:
'@codemirror/legacy-modes': '@codemirror/legacy-modes':
specifier: 6.4.0 specifier: 6.4.0
version: 6.4.0 version: 6.4.0
'@codemirror/view':
specifier: 6.29.0
version: 6.29.0
'@dokploy/trpc-openapi': '@dokploy/trpc-openapi':
specifier: 0.0.4 specifier: 0.0.4
version: 0.0.4(@trpc/server@10.45.2)(zod@3.23.8) version: 0.0.4(@trpc/server@10.45.2)(zod@3.23.8)