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
This commit is contained in:
Mauricio Siu
2024-08-13 23:12:57 -06:00
committed by GitHub
9 changed files with 3148 additions and 34 deletions

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

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

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