mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-09-14 11:06:15 +05:00
- Removed the `index.html` file as part of the transition to a new routing system. - Updated `package.json` scripts to streamline development commands using `concurrently`. - Enhanced dependency management by adding new packages and updating existing ones in `package.json`. - Introduced a new server function for branding and session management, improving API interactions. - Refactored routing to utilize TanStack Start, ensuring better integration with the existing Dokploy architecture. - Improved static file handling and server response management in the custom Node.js server. - Updated README to reflect changes in architecture and development processes.
233 lines
7.3 KiB
TypeScript
233 lines
7.3 KiB
TypeScript
import fs from "node:fs";
|
|
import http from "node:http";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import {
|
|
auth,
|
|
createDefaultMiddlewares,
|
|
createDefaultServerTraefikConfig,
|
|
createDefaultTraefikConfig,
|
|
getPublicWhitelabelingConfig,
|
|
IS_CLOUD,
|
|
initCancelDeployments,
|
|
initCronJobs,
|
|
initEnterpriseBackupCronJobs,
|
|
initializeNetwork,
|
|
initSchedules,
|
|
initVolumeBackupsCronJobs,
|
|
sendDokployRestartNotifications,
|
|
setupDirectories,
|
|
} from "@dokploy/server";
|
|
import { toNodeHandler } from "better-auth/node";
|
|
import { config } from "dotenv";
|
|
import openApiHandler from "@/pages/api/[...trpc]";
|
|
import deployRefreshTokenHandler from "@/pages/api/deploy/[refreshToken]";
|
|
import deployComposeHandler from "@/pages/api/deploy/compose/[refreshToken]";
|
|
import deployGithubHandler from "@/pages/api/deploy/github";
|
|
import healthHandler from "@/pages/api/health";
|
|
import giteaAuthorizeHandler from "@/pages/api/providers/gitea/authorize";
|
|
import giteaCallbackHandler from "@/pages/api/providers/gitea/callback";
|
|
import githubSetupHandler from "@/pages/api/providers/github/setup";
|
|
import githubWebhookHandler from "@/pages/api/providers/github/webhook";
|
|
import gitlabCallbackHandler from "@/pages/api/providers/gitlab/callback";
|
|
import stripeWebhookHandler from "@/pages/api/stripe/webhook";
|
|
import trpcHandler from "@/pages/api/trpc/[trpc]";
|
|
import { setupDockerContainerLogsWebSocketServer } from "@/server/wss/docker-container-logs";
|
|
import { setupDockerContainerTerminalWebSocketServer } from "@/server/wss/docker-container-terminal";
|
|
import { setupDockerStatsMonitoringSocketServer } from "@/server/wss/docker-stats";
|
|
import { setupDrawerLogsWebSocketServer } from "@/server/wss/drawer-logs";
|
|
import { setupDeploymentLogsWebSocketServer } from "@/server/wss/listen-deployment";
|
|
import { setupTerminalWebSocketServer } from "@/server/wss/terminal";
|
|
import packageInfo from "../package.json";
|
|
import { runNextApiHandler } from "./next-compat";
|
|
import { createStartBridge } from "./start-bridge";
|
|
import { createStaticHandler } from "./static";
|
|
|
|
config({ path: ".env" });
|
|
config({ path: "../dokploy/.env" });
|
|
|
|
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
|
const HOST = process.env.HOST || "0.0.0.0";
|
|
const isProd = process.env.NODE_ENV === "production";
|
|
|
|
if (isProd && !IS_CLOUD) {
|
|
setupDirectories();
|
|
createDefaultTraefikConfig();
|
|
createDefaultServerTraefikConfig();
|
|
console.log("✅ initialization complete");
|
|
}
|
|
|
|
const authHandler = toNodeHandler(auth.handler);
|
|
|
|
const handleApi = async (
|
|
req: http.IncomingMessage,
|
|
res: http.ServerResponse,
|
|
pathname: string,
|
|
) => {
|
|
if (pathname === "/api/health") {
|
|
return runNextApiHandler(healthHandler, req, res);
|
|
}
|
|
if (pathname === "/api/branding") {
|
|
let config = null;
|
|
try {
|
|
config = await getPublicWhitelabelingConfig();
|
|
} catch {
|
|
config = null;
|
|
}
|
|
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
res.end(JSON.stringify(config));
|
|
return;
|
|
}
|
|
if (pathname.startsWith("/api/auth/")) {
|
|
return authHandler(req, res);
|
|
}
|
|
if (pathname.startsWith("/api/trpc/")) {
|
|
const trpc = decodeURIComponent(pathname.slice("/api/trpc/".length));
|
|
return runNextApiHandler(trpcHandler, req, res, {
|
|
params: { trpc },
|
|
parseBody: false,
|
|
});
|
|
}
|
|
if (pathname === "/api/deploy/github") {
|
|
return runNextApiHandler(deployGithubHandler, req, res);
|
|
}
|
|
if (pathname === "/api/stripe/webhook") {
|
|
return runNextApiHandler(stripeWebhookHandler, req, res, {
|
|
parseBody: false,
|
|
});
|
|
}
|
|
if (pathname === "/api/providers/github/setup") {
|
|
return runNextApiHandler(githubSetupHandler, req, res);
|
|
}
|
|
if (pathname === "/api/providers/github/webhook") {
|
|
return runNextApiHandler(githubWebhookHandler, req, res);
|
|
}
|
|
if (pathname === "/api/providers/gitlab/callback") {
|
|
return runNextApiHandler(gitlabCallbackHandler, req, res);
|
|
}
|
|
if (pathname === "/api/providers/gitea/authorize") {
|
|
return runNextApiHandler(giteaAuthorizeHandler, req, res);
|
|
}
|
|
if (pathname === "/api/providers/gitea/callback") {
|
|
return runNextApiHandler(giteaCallbackHandler, req, res);
|
|
}
|
|
const composeMatch = pathname.match(/^\/api\/deploy\/compose\/([^/]+)$/);
|
|
if (composeMatch?.[1]) {
|
|
return runNextApiHandler(deployComposeHandler, req, res, {
|
|
params: { refreshToken: composeMatch[1] },
|
|
});
|
|
}
|
|
const deployMatch = pathname.match(/^\/api\/deploy\/([^/]+)$/);
|
|
if (deployMatch?.[1]) {
|
|
return runNextApiHandler(deployRefreshTokenHandler, req, res, {
|
|
params: { refreshToken: deployMatch[1] },
|
|
});
|
|
}
|
|
const segments = pathname
|
|
.slice("/api/".length)
|
|
.split("/")
|
|
.filter(Boolean)
|
|
.map(decodeURIComponent);
|
|
return runNextApiHandler(openApiHandler, req, res, {
|
|
params: { trpc: segments },
|
|
});
|
|
};
|
|
|
|
const startServer = async () => {
|
|
console.log("Running DokployVersion: ", packageInfo.version);
|
|
|
|
let handleUi: (
|
|
req: http.IncomingMessage,
|
|
res: http.ServerResponse,
|
|
url: URL,
|
|
) => void | Promise<void> = (_req, res) => {
|
|
res.writeHead(503, { "content-type": "text/plain" });
|
|
res.end("UI not ready");
|
|
};
|
|
|
|
const server = http.createServer(async (req, res) => {
|
|
try {
|
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
if (url.pathname.startsWith("/api")) {
|
|
await handleApi(req, res, url.pathname);
|
|
return;
|
|
}
|
|
await handleUi(req, res, url);
|
|
} catch (error) {
|
|
console.error("Request error", error);
|
|
if (!res.headersSent) {
|
|
res.writeHead(500, { "content-type": "text/plain" });
|
|
}
|
|
res.end("Internal Server Error");
|
|
}
|
|
});
|
|
|
|
if (isProd) {
|
|
const clientDir = path.resolve(import.meta.dirname, "../dist/client");
|
|
const serverEntry = path.resolve(
|
|
import.meta.dirname,
|
|
"../dist/server/server.js",
|
|
);
|
|
const staticHandler = createStaticHandler(clientDir);
|
|
const entry = await import(pathToFileURL(serverEntry).href);
|
|
const bridge = createStartBridge(entry.default);
|
|
handleUi = async (req, res, url) => {
|
|
const pathname = decodeURIComponent(url.pathname);
|
|
const filePath = path.join(clientDir, pathname);
|
|
if (
|
|
pathname !== "/" &&
|
|
filePath.startsWith(clientDir) &&
|
|
fs.existsSync(filePath) &&
|
|
fs.statSync(filePath).isFile()
|
|
) {
|
|
staticHandler(req, res, url);
|
|
return;
|
|
}
|
|
await bridge(req, res);
|
|
};
|
|
console.log("TanStack Start SSR bridge enabled");
|
|
} else {
|
|
handleUi = (_req, res) => {
|
|
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
res.end(
|
|
"API-only mode: the UI dev server runs separately (pnpm --filter dokploy-vite dev → http://localhost:5173).",
|
|
);
|
|
};
|
|
}
|
|
|
|
setupDrawerLogsWebSocketServer(server);
|
|
setupDeploymentLogsWebSocketServer(server);
|
|
setupDockerContainerLogsWebSocketServer(server);
|
|
setupDockerContainerTerminalWebSocketServer(server);
|
|
setupTerminalWebSocketServer(server);
|
|
if (!IS_CLOUD) {
|
|
setupDockerStatsMonitoringSocketServer(server);
|
|
}
|
|
|
|
server.listen(PORT, HOST);
|
|
console.log(`Standalone Server Started on: http://${HOST}:${PORT}`);
|
|
|
|
if (isProd && !IS_CLOUD) {
|
|
createDefaultMiddlewares();
|
|
await initializeNetwork();
|
|
await initCronJobs();
|
|
await initSchedules();
|
|
await initCancelDeployments();
|
|
await initVolumeBackupsCronJobs();
|
|
await sendDokployRestartNotifications();
|
|
}
|
|
await initEnterpriseBackupCronJobs();
|
|
|
|
if (!IS_CLOUD) {
|
|
console.log("Starting Deployment Worker");
|
|
const { startDeploymentWorker } = await import(
|
|
"@/server/queues/queueSetup"
|
|
);
|
|
await startDeploymentWorker();
|
|
}
|
|
};
|
|
|
|
startServer().catch((error) => {
|
|
console.error("Main Server Error", error);
|
|
});
|