Merge branch 'canary' into feature/delete-docker-volumes

This commit is contained in:
djknaeckebrot
2024-12-23 08:13:12 +01:00
76 changed files with 3638 additions and 861 deletions

View File

@@ -21,6 +21,7 @@ import { mysqlRouter } from "./routers/mysql";
import { notificationRouter } from "./routers/notification";
import { portRouter } from "./routers/port";
import { postgresRouter } from "./routers/postgres";
import { previewDeploymentRouter } from "./routers/preview-deployment";
import { projectRouter } from "./routers/project";
import { redirectsRouter } from "./routers/redirects";
import { redisRouter } from "./routers/redis";
@@ -30,8 +31,8 @@ import { serverRouter } from "./routers/server";
import { settingsRouter } from "./routers/settings";
import { sshRouter } from "./routers/ssh-key";
import { stripeRouter } from "./routers/stripe";
import { swarmRouter } from "./routers/swarm";
import { userRouter } from "./routers/user";
import { previewDeploymentRouter } from "./routers/preview-deployment";
/**
* This is the primary router for your server.
@@ -73,6 +74,7 @@ export const appRouter = createTRPCRouter({
github: githubRouter,
server: serverRouter,
stripe: stripeRouter,
swarm: swarmRouter,
});
// export type definition of API

View File

@@ -188,9 +188,9 @@ export const authRouter = createTRPCRouter({
.mutation(async ({ ctx, input }) => {
const currentAuth = await findAuthByEmail(ctx.user.email);
if (input.password) {
if (input.currentPassword || input.password) {
const correctPassword = bcrypt.compareSync(
input.password,
input.currentPassword || "",
currentAuth?.password || "",
);
if (!correctPassword) {
@@ -268,7 +268,9 @@ export const authRouter = createTRPCRouter({
return auth;
}),
verifyToken: protectedProcedure.mutation(async () => {
return true;
}),
one: adminProcedure.input(apiFindOneAuth).query(async ({ input }) => {
const auth = await findAuthById(input.id);
return auth;

View File

@@ -12,6 +12,7 @@ import {
} from "@/server/db/schema";
import { removeJob, schedule } from "@/server/utils/backup";
import {
DEFAULT_UPDATE_DATA,
IS_CLOUD,
canAccessToTraefikFiles,
cleanStoppedContainers,
@@ -25,6 +26,8 @@ import {
findAdminById,
findServerById,
getDokployImage,
getDokployImageTag,
getUpdateData,
initializeTraefik,
logRotationManager,
parseRawConfig,
@@ -267,11 +270,11 @@ export const settingsRouter = createTRPCRouter({
message: "You are not authorized to access this admin",
});
}
await updateAdmin(ctx.user.authId, {
const adminUpdated = await updateAdmin(ctx.user.authId, {
enableDockerCleanup: input.enableDockerCleanup,
});
if (admin.enableDockerCleanup) {
if (adminUpdated?.enableDockerCleanup) {
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
console.log(
`Docker Cleanup ${new Date().toLocaleString()}] Running...`,
@@ -342,17 +345,20 @@ export const settingsRouter = createTRPCRouter({
writeConfig("middlewares", input.traefikConfig);
return true;
}),
checkAndUpdateImage: adminProcedure.mutation(async () => {
getUpdateData: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
return DEFAULT_UPDATE_DATA;
}
return await pullLatestRelease();
return await getUpdateData();
}),
updateServer: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
}
await pullLatestRelease();
await spawnAsync("docker", [
"service",
"update",
@@ -361,12 +367,16 @@ export const settingsRouter = createTRPCRouter({
getDokployImage(),
"dokploy",
]);
return true;
}),
getDokployVersion: adminProcedure.query(() => {
return packageInfo.version;
}),
getReleaseTag: adminProcedure.query(() => {
return getDokployImageTag();
}),
readDirectories: protectedProcedure
.input(apiServerSchema)
.query(async ({ ctx, input }) => {
@@ -706,6 +716,83 @@ export const settingsRouter = createTRPCRouter({
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 to update 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;
}
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",
}));
return additionalPorts;
} catch (error) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Failed to get Traefik ports",
cause: error,
});
}
}),
});
// {
// "Parallelism": 1,

View File

@@ -0,0 +1,44 @@
import {
getApplicationInfo,
getNodeApplications,
getNodeInfo,
getSwarmNodes,
} from "@dokploy/server";
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
export const swarmRouter = createTRPCRouter({
getNodes: protectedProcedure
.input(
z.object({
serverId: z.string().optional(),
}),
)
.query(async ({ input }) => {
return await getSwarmNodes(input.serverId);
}),
getNodeInfo: protectedProcedure
.input(z.object({ nodeId: z.string(), serverId: z.string().optional() }))
.query(async ({ input }) => {
return await getNodeInfo(input.nodeId, input.serverId);
}),
getNodeApps: protectedProcedure
.input(
z.object({
serverId: z.string().optional(),
}),
)
.query(async ({ input }) => {
return getNodeApplications(input.serverId);
}),
getAppInfos: protectedProcedure
.input(
z.object({
appName: z.string(),
serverId: z.string().optional(),
}),
)
.query(async ({ input }) => {
return await getApplicationInfo(input.appName, input.serverId);
}),
});