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:
Mauricio Siu
2025-01-12 14:29:43 -06:00
committed by GitHub
parent 87f4c7b71b
commit a104867ed2
278 changed files with 18706 additions and 16959 deletions

View File

@@ -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 }) => {

View File

@@ -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 }) => {

View File

@@ -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 }) => {

View File

@@ -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 }) => {

View File

@@ -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 }) => {

View File

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

View File

@@ -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 }) => {

View File

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

View 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}`);
});
};

View File

@@ -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 = `
# ----------------------------------------

View File

@@ -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");