mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Feat/add sidebar (#1084)
* refactor: add sidebar * chore: add deps * refactor: update sidebar * refactor: another layout * refactor: update variant * refactor: change layout * refactor: change variant * refactor: enhance sidebar navigation with active state management * feat: add project button to dashboard * Merge branch 'canary' into feat/add-sidebar * refactor: add loader * refactor: update destinations and refactor * refactor: ui refactor certificates * refactor: delete unused files * refactor: remove unused files and duplicate registry * refactor: update style registry * refactor: add new design registry * refactor: enhance git providers * refactor: remove duplicate files * refactor: update * refactor: update users * refactor: delete unused files * refactor: update profile * refactor: apply changes * refactor: update UI * refactor: enhance Docker monitoring UI layout * refactor: add theme toggle and language selection to user navigation (#1083) * refactor: remove unused files * feat: add filter to services * refactor: add active items * refactor: remove tab prop * refactor: remove unused files * refactor: remove duplicated files * refactor: remove unused files * refactor: remove duplicate files * refactor: remove unused files * refactor: delete unused files * refactor: remove unsued files * refactor: delete unused files * refactor: lint * refactor: remove unused secuirty * refactor: delete unused files * refactor: delete unused files * remove imports * refactor: add update button * refactor: delete unused files * refactor: remove unused code * refactor: remove unused files * refactor: update login page * refactor: update login UI * refactor: update ui reset password * refactor: add justify end * feat: add suscriptions * feat: add sheet * feat: add logs for postgres * feat: add logs for all databases * feat: add server logs with drawer logs * refactor: remove unused files * refactor: add refetch when closing * refactor: fix linter * chore: bump node-20 * revert * refactor: fix conflicts * refactor: update * refactor: add missing deps * refactor: delete duplicate files * refactor: delete unsued files * chore: lint * refactor: remove unsued file * refactor: add refetch * refactor: remove duplicated files * refactor: delete unused files * refactor: update setup onboarding * refactor: add breadcrumb * refactor: apply updates * refactor: add faker * refactor: use 0 in validation * refactor: show correct state * refactor: update --------- Co-authored-by: vishalkadam47 <vishal@jeevops.com> Co-authored-by: Vishal kadam <107353260+vishalkadam47@users.noreply.github.com>
This commit is contained in:
@@ -28,6 +28,7 @@ import {
|
||||
updateMariadbById,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
|
||||
export const mariadbRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -155,6 +156,31 @@ export const mariadbRouter = createTRPCRouter({
|
||||
|
||||
return deployMariadb(input.mariadbId);
|
||||
}),
|
||||
deployWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/mariadb-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiDeployMariaDB)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
const mariadb = await findMariadbById(input.mariadbId);
|
||||
if (mariadb.project.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to deploy this Mariadb",
|
||||
});
|
||||
}
|
||||
|
||||
return observable<string>((emit) => {
|
||||
deployMariadb(input.mariadbId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.input(apiChangeMariaDBStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
updateMongoById,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
|
||||
export const mongoRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -167,6 +168,31 @@ export const mongoRouter = createTRPCRouter({
|
||||
}
|
||||
return deployMongo(input.mongoId);
|
||||
}),
|
||||
deployWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/mongo-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiDeployMongo)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
const mongo = await findMongoById(input.mongoId);
|
||||
if (mongo.project.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to deploy this mongo",
|
||||
});
|
||||
}
|
||||
return observable<string>((emit) => {
|
||||
deployMongo(input.mongoId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
}),
|
||||
|
||||
changeStatus: protectedProcedure
|
||||
.input(apiChangeMongoStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
stopServiceRemote,
|
||||
updateMySqlById,
|
||||
} from "@dokploy/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
|
||||
export const mysqlRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -166,6 +167,31 @@ export const mysqlRouter = createTRPCRouter({
|
||||
}
|
||||
return deployMySql(input.mysqlId);
|
||||
}),
|
||||
deployWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/mysql-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiDeployMySql)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
const mysql = await findMySqlById(input.mysqlId);
|
||||
if (mysql.project.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to deploy this MySQL",
|
||||
});
|
||||
}
|
||||
|
||||
return observable<string>((emit) => {
|
||||
deployMySql(input.mysqlId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.input(apiChangeMySqlStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
|
||||
import { EventEmitter } from "node:events";
|
||||
import {
|
||||
createTRPCRouter,
|
||||
protectedProcedure,
|
||||
publicProcedure,
|
||||
} from "@/server/api/trpc";
|
||||
import {
|
||||
apiChangePostgresStatus,
|
||||
apiCreatePostgres,
|
||||
@@ -27,6 +32,10 @@ import {
|
||||
updatePostgresById,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { z } from "zod";
|
||||
|
||||
const ee = new EventEmitter();
|
||||
|
||||
export const postgresRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -166,6 +175,32 @@ export const postgresRouter = createTRPCRouter({
|
||||
}
|
||||
return deployPostgres(input.postgresId);
|
||||
}),
|
||||
|
||||
deployWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/postgres-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiDeployPostgres)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
const postgres = await findPostgresById(input.postgresId);
|
||||
if (postgres.project.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to deploy this Postgres",
|
||||
});
|
||||
}
|
||||
return observable<string>((emit) => {
|
||||
deployPostgres(input.postgresId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
}),
|
||||
|
||||
changeStatus: protectedProcedure
|
||||
.input(apiChangePostgresStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
stopServiceRemote,
|
||||
updateRedisById,
|
||||
} from "@dokploy/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
|
||||
export const redisRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
@@ -189,6 +190,30 @@ export const redisRouter = createTRPCRouter({
|
||||
}
|
||||
return deployRedis(input.redisId);
|
||||
}),
|
||||
deployWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/redis-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiDeployRedis)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
const redis = await findRedisById(input.redisId);
|
||||
if (redis.project.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to deploy this Redis",
|
||||
});
|
||||
}
|
||||
return observable<string>((emit) => {
|
||||
deployRedis(input.redisId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
}),
|
||||
changeStatus: protectedProcedure
|
||||
.input(apiChangeRedisStatus)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -96,8 +96,14 @@ export const registryRouter = createTRPCRouter({
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log("Error Registry:", error);
|
||||
return false;
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Error testing the registry",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
updateServerById,
|
||||
} from "@dokploy/server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { observable } from "@trpc/server/observable";
|
||||
import { and, desc, eq, getTableColumns, isNotNull, sql } from "drizzle-orm";
|
||||
|
||||
export const serverRouter = createTRPCRouter({
|
||||
@@ -126,6 +127,34 @@ export const serverRouter = createTRPCRouter({
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
setupWithLogs: protectedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
path: "/deploy/server-with-logs",
|
||||
method: "POST",
|
||||
override: true,
|
||||
enabled: false,
|
||||
},
|
||||
})
|
||||
.input(apiFindOneServer)
|
||||
.subscription(async ({ input, ctx }) => {
|
||||
try {
|
||||
const server = await findServerById(input.serverId);
|
||||
if (server.adminId !== ctx.user.adminId) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "You are not authorized to setup this server",
|
||||
});
|
||||
}
|
||||
return observable<string>((emit) => {
|
||||
serverSetup(input.serverId, (log) => {
|
||||
emit.next(log);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
validate: protectedProcedure
|
||||
.input(apiFindOneServer)
|
||||
.query(async ({ input, ctx }) => {
|
||||
|
||||
@@ -18,6 +18,7 @@ import next from "next";
|
||||
import { setupDockerContainerLogsWebSocketServer } from "./wss/docker-container-logs";
|
||||
import { setupDockerContainerTerminalWebSocketServer } from "./wss/docker-container-terminal";
|
||||
import { setupDockerStatsMonitoringSocketServer } from "./wss/docker-stats";
|
||||
import { setupDrawerLogsWebSocketServer } from "./wss/drawer-logs";
|
||||
import { setupDeploymentLogsWebSocketServer } from "./wss/listen-deployment";
|
||||
import { setupTerminalWebSocketServer } from "./wss/terminal";
|
||||
|
||||
@@ -33,6 +34,7 @@ void app.prepare().then(async () => {
|
||||
});
|
||||
|
||||
// WEBSOCKET
|
||||
setupDrawerLogsWebSocketServer(server);
|
||||
setupDeploymentLogsWebSocketServer(server);
|
||||
setupDockerContainerLogsWebSocketServer(server);
|
||||
setupDockerContainerTerminalWebSocketServer(server);
|
||||
|
||||
36
apps/dokploy/server/wss/drawer-logs.ts
Normal file
36
apps/dokploy/server/wss/drawer-logs.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type http from "node:http";
|
||||
import { applyWSSHandler } from "@trpc/server/adapters/ws";
|
||||
import { WebSocketServer } from "ws";
|
||||
import { appRouter } from "../api/root";
|
||||
import { createTRPCContext } from "../api/trpc";
|
||||
|
||||
export const setupDrawerLogsWebSocketServer = (
|
||||
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
|
||||
) => {
|
||||
const wssTerm = new WebSocketServer({
|
||||
noServer: true,
|
||||
path: "/drawer-logs",
|
||||
});
|
||||
|
||||
applyWSSHandler({
|
||||
wss: wssTerm,
|
||||
router: appRouter,
|
||||
createContext: createTRPCContext as any,
|
||||
});
|
||||
server.on("upgrade", (req, socket, head) => {
|
||||
const { pathname } = new URL(req.url || "", `http://${req.headers.host}`);
|
||||
|
||||
if (pathname === "/_next/webpack-hmr") {
|
||||
return;
|
||||
}
|
||||
if (pathname === "/drawer-logs") {
|
||||
wssTerm.handleUpgrade(req, socket, head, function done(ws) {
|
||||
wssTerm.emit("connection", ws, req);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
wssTerm.on("connection", async (ws, req) => {
|
||||
const url = new URL(req.url || "", `http://${req.headers.host}`);
|
||||
});
|
||||
};
|
||||
@@ -1,14 +1,14 @@
|
||||
import type http from "node:http";
|
||||
import {
|
||||
findServerById,
|
||||
IS_CLOUD,
|
||||
findServerById,
|
||||
validateWebSocketRequest,
|
||||
} from "@dokploy/server";
|
||||
import { publicIpv4, publicIpv6 } from "public-ip";
|
||||
import { Client, type ConnectConfig } from "ssh2";
|
||||
import { WebSocketServer } from "ws";
|
||||
import { setupLocalServerSSHKey } from "./utils";
|
||||
import { getDockerHost } from "../utils/docker";
|
||||
import { setupLocalServerSSHKey } from "./utils";
|
||||
|
||||
const COMMAND_TO_ALLOW_LOCAL_ACCESS = `
|
||||
# ----------------------------------------
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { execAsync, paths } from "@dokploy/server";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import { execAsync, paths } from "@dokploy/server";
|
||||
|
||||
export const getShell = () => {
|
||||
switch (os.platform()) {
|
||||
@@ -15,14 +15,16 @@ export const getShell = () => {
|
||||
};
|
||||
|
||||
/** Returns private SSH key for dokploy local server terminal. Uses already created SSH key or generates a new SSH key.
|
||||
*/
|
||||
*/
|
||||
export const setupLocalServerSSHKey = async () => {
|
||||
const { SSH_PATH } = paths(true);
|
||||
const sshKeyPath = path.join(SSH_PATH, "auto_generated-dokploy-local");
|
||||
|
||||
if (!fs.existsSync(sshKeyPath)) {
|
||||
// Generate new SSH key if it hasn't been created yet
|
||||
await execAsync(`ssh-keygen -t rsa -b 4096 -f ${sshKeyPath} -N "" -C "dokploy-local-access"`);
|
||||
await execAsync(
|
||||
`ssh-keygen -t rsa -b 4096 -f ${sshKeyPath} -N "" -C "dokploy-local-access"`,
|
||||
);
|
||||
}
|
||||
|
||||
const privateKey = fs.readFileSync(sshKeyPath, "utf8");
|
||||
|
||||
Reference in New Issue
Block a user