diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 00000000..2a95d8c7 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,33 @@ +FROM node:18-alpine AS base +ENV PNPM_HOME="/pnpm" +ENV PATH="$PNPM_HOME:$PATH" +RUN corepack enable + +FROM base AS build +COPY . /usr/src/app +WORKDIR /usr/src/app + + +# Install dependencies +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --filter=./apps/api --frozen-lockfile + +# Deploy only the dokploy app + +ENV NODE_ENV=production +RUN pnpm --filter=./apps/api run build +RUN pnpm --filter=./apps/api --prod deploy /prod/api + +RUN cp -R /usr/src/app/apps/api/.dist /prod/api/.dist + +FROM base AS dokploy +WORKDIR /app + +# Set production +ENV NODE_ENV=production + +# Copy only the necessary files +COPY --from=build /prod/api/.dist ./.dist +COPY --from=build /prod/api/package.json ./package.json +COPY --from=build /prod/api/node_modules ./node_modules + +CMD HOSTNAME=0.0.0.0 && pnpm start \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index 561cbdbf..072c652f 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -1,9 +1,11 @@ { - "name": "my-app", + "name": "@dokploy/api", + "version": "0.0.1", + "type": "module", "scripts": { "dev": "PORT=4000 tsx watch src/index.ts", - "dev2": "PORT=4001 tsx watch src/index.ts", - "tsc": "tsc --project tsconfig.json" + "build": "tsc --project tsconfig.json", + "start": "node dist/index.js" }, "dependencies": { "react": "18.2.0", @@ -13,13 +15,8 @@ "hono": "^4.5.8", "dotenv": "^16.3.1", "@upstash/qstash": "2.7.9", - "ioredis": "5.4.1", - "nats": "2.28.2", - "bullmq": "5.13.2", - "@nerimity/mimiqueue": "1.2.3", - "timers": "0.1.1", "redis": "4.7.0", - "date-fns": "4.1.0" + "@nerimity/mimiqueue": "1.2.3" }, "devDependencies": { "typescript": "^5.4.2", diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 631304a4..1e8b7072 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -4,7 +4,6 @@ import "dotenv/config"; import { createClient } from "redis"; import { Queue } from "@nerimity/mimiqueue"; import { deployApplication } from "@dokploy/builders"; -// import { setTimeout } from "timers/promises"; const app = new Hono(); const redisClient = createClient({ @@ -47,7 +46,7 @@ const queue = new Queue({ }, redisClient, }); -const port = process.env.PORT; +const port = Number.parseInt(process.env.PORT || "3000"); (async () => { await redisClient.connect(); await redisClient.flushAll(); diff --git a/apps/api/src/test.ts b/apps/api/src/test.ts index 9796eb60..635e0078 100644 --- a/apps/api/src/test.ts +++ b/apps/api/src/test.ts @@ -1,82 +1,82 @@ -import { Hono } from "hono"; -import { Client } from "@upstash/qstash"; -import { serve } from "@hono/node-server"; -import dotenv from "dotenv"; -import Redis from "ioredis"; +// import { Hono } from "hono"; +// import { Client } from "@upstash/qstash"; +// import { serve } from "@hono/node-server"; +// import dotenv from "dotenv"; +// import Redis from "ioredis"; -dotenv.config(); +// dotenv.config(); -const redis = new Redis({ - host: "localhost", - port: 7777, - password: "xlfvpQ0ma2BkkkPX", -}); +// const redis = new Redis({ +// host: "localhost", +// port: 7777, +// password: "xlfvpQ0ma2BkkkPX", +// }); -// redis.set("test", "test"); -// console.log(await redis.get("test")); +// // redis.set("test", "test"); +// // console.log(await redis.get("test")); -// console.log(await redis.get("user-1-processing")); -const app = new Hono(); -console.log("QStash Token:", process.env.PUBLIC_URL); +// // console.log(await redis.get("user-1-processing")); +// const app = new Hono(); +// console.log("QStash Token:", process.env.PUBLIC_URL); -const qstash = new Client({ - token: process.env.QSTASH_TOKEN as string, -}); +// const qstash = new Client({ +// token: process.env.QSTASH_TOKEN as string, +// }); -const queue = qstash.queue({ - queueName: "deployments", -}); +// const queue = qstash.queue({ +// queueName: "deployments", +// }); -// Endpoint que publica un mensaje en QStash -app.post("/enqueue", async (c) => { - const { userId, deploymentId } = await c.req.json(); - const response = await qstash.publishJSON({ - url: `${process.env.PUBLIC_URL}/process`, // Endpoint para procesar la tarea - body: { userId, deploymentId }, // Datos del despliegue - - }); +// // Endpoint que publica un mensaje en QStash +// app.post("/enqueue", async (c) => { +// const { userId, deploymentId } = await c.req.json(); +// const response = await qstash.publishJSON({ +// url: `${process.env.PUBLIC_URL}/process`, // Endpoint para procesar la tarea +// body: { userId, deploymentId }, // Datos del despliegue - return c.json({ message: "Task enqueued", id: response.messageId }); -}); +// }); -// Endpoint que recibe el mensaje procesado -app.post("/process", async (c) => { - const { userId, deploymentId } = await c.req.json(); +// return c.json({ message: "Task enqueued", id: response.messageId }); +// }); - const isProcessing = await redis.get(`user-${userId}-processing`); - console.log(`isProcessing for user ${userId}:`, isProcessing); +// // Endpoint que recibe el mensaje procesado +// app.post("/process", async (c) => { +// const { userId, deploymentId } = await c.req.json(); - if (isProcessing === "true") { - console.log( - `User ${userId} is already processing a deployment. Queuing the next one.`, - ); - return c.json( - { - status: "User is already processing a deployment, waiting...", - }, - { - status: 400, - }, - ); - } - redis.set(`user-${userId}-processing`, "true"); +// const isProcessing = await redis.get(`user-${userId}-processing`); +// console.log(`isProcessing for user ${userId}:`, isProcessing); - try { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } catch (error) { - } finally { - await redis.del(`user-${userId}-processing`); - } +// if (isProcessing === "true") { +// console.log( +// `User ${userId} is already processing a deployment. Queuing the next one.`, +// ); +// return c.json( +// { +// status: "User is already processing a deployment, waiting...", +// }, +// { +// status: 400, +// }, +// ); +// } +// redis.set(`user-${userId}-processing`, "true"); - return c.json({ status: "Processed", userId, deploymentId }); -}); +// try { +// await new Promise((resolve) => setTimeout(resolve, 5000)); +// } catch (error) { +// } finally { +// await redis.del(`user-${userId}-processing`); +// } -// Inicia el servidor en el puerto 3000 -const port = 3000; -console.log(`Server is running on port http://localhost:${port}`); +// return c.json({ status: "Processed", userId, deploymentId }); +// }); -serve({ - fetch: app.fetch, - port, -}); -// 18 +// // Inicia el servidor en el puerto 3000 +// const port = 3000; +// console.log(`Server is running on port http://localhost:${port}`); + +// serve({ +// fetch: app.fetch, +// port, +// }); +// // 18 diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index 79263955..2284aebc 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -1,6 +1,8 @@ -import { LEMON_SQUEEZY_API_KEY, LEMON_SQUEEZY_STORE_ID } from "."; +// import { LEMON_SQUEEZY_API_KEY, LEMON_SQUEEZY_STORE_ID } from "."; import type { LemonSqueezyLicenseResponse } from "./types"; +const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY; +const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID; export const validateLemonSqueezyLicense = async ( licenseKey: string, ): Promise => { diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 55e7a5e1..8d3088b4 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -5,10 +5,9 @@ "moduleResolution": "Node", "strict": true, "skipLibCheck": true, - "types": ["node"], + "outDir": "dist", "jsx": "react-jsx", - "jsxImportSource": "hono/jsx", - "traceResolution": true, - "diagnostics": true - } + "jsxImportSource": "hono/jsx" + }, + "exclude": ["node_modules", "dist"] } diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index 6eb4b350..92e07542 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -14,6 +14,7 @@ import { initializeTraefik, initCronJobs, sendDokployRestartNotifications, + IS_CLOUD, } from "@dokploy/builders"; import { setupDockerContainerLogsWebSocketServer } from "./wss/docker-container-logs"; import { setupDockerContainerTerminalWebSocketServer } from "./wss/docker-container-terminal"; @@ -42,9 +43,7 @@ void app.prepare().then(async () => { setupTerminalWebSocketServer(server); setupDockerStatsMonitoringSocketServer(server); - if (process.env.NODE_ENV === "production") { - setupDirectories(); - createDefaultMiddlewares(); + if (process.env.NODE_ENV === "production" && !IS_CLOUD) { setupDirectories(); createDefaultMiddlewares(); await initializeNetwork(); @@ -63,9 +62,16 @@ void app.prepare().then(async () => { await sendDokployRestartNotifications(); } + if (IS_CLOUD) { + await migration(); + } + server.listen(PORT); console.log("Server Started:", PORT); - deploymentWorker.run(); + + if (!IS_CLOUD) { + deploymentWorker.run(); + } } catch (e) { console.error("Main Server Error", e); } diff --git a/packages/builders/esbuild.config.ts b/packages/builders/esbuild.config.ts index 3f83abe9..04576d1d 100644 --- a/packages/builders/esbuild.config.ts +++ b/packages/builders/esbuild.config.ts @@ -1,5 +1,4 @@ import { build } from "esbuild"; -import TsconfigPathsPlugin from "@esbuild-plugins/tsconfig-paths"; import path from "node:path"; build({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60fa28df..b469136f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,24 +54,12 @@ importers: '@upstash/qstash': specifier: 2.7.9 version: 2.7.9 - bullmq: - specifier: 5.13.2 - version: 5.13.2 - date-fns: - specifier: 4.1.0 - version: 4.1.0 dotenv: specifier: ^16.3.1 version: 16.4.5 hono: specifier: ^4.5.8 version: 4.5.8 - ioredis: - specifier: 5.4.1 - version: 5.4.1 - nats: - specifier: 2.28.2 - version: 2.28.2 react: specifier: 18.2.0 version: 18.2.0 @@ -81,9 +69,6 @@ importers: redis: specifier: 4.7.0 version: 4.7.0 - timers: - specifier: 0.1.1 - version: 0.1.1 devDependencies: '@types/node': specifier: ^20.11.17 @@ -4023,9 +4008,6 @@ packages: resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==} engines: {node: '>=10.0.0'} - bullmq@5.13.2: - resolution: {integrity: sha512-McGE8k3mrCvdUHdU0sHkTKDS1xr4pff+hbEKBY51wk5S6Za0gkuejYA620VQTo3Zz37E/NVWMgumwiXPQ3yZcA==} - bullmq@5.4.2: resolution: {integrity: sha512-dkR/KGUw18miLe3QWtvSlmGvEe08aZF+w1jZyqEHMWFW3RP4162qp6OGud0/QCAOjusiRI8UOxUhbnortPY+rA==} @@ -4449,9 +4431,6 @@ packages: date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6502,10 +6481,6 @@ packages: napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} - nats@2.28.2: - resolution: {integrity: sha512-02cvR8EPach+0BfVaQjPgsbPFn6uMjEQAuvXS2ppg8jiWEm2KYdfmeFmtshiU9b2+kFh3LSEKMEaIfRgk3K8tw==} - engines: {node: '>= 14.0.0'} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6574,10 +6549,6 @@ packages: sass: optional: true - nkeys.js@1.1.0: - resolution: {integrity: sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==} - engines: {node: '>=10.0.0'} - node-abi@3.65.0: resolution: {integrity: sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==} engines: {node: '>=10'} @@ -7870,9 +7841,6 @@ packages: resolution: {integrity: sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==} engines: {node: '>=0.12'} - timers@0.1.1: - resolution: {integrity: sha512-pkJC8uIP/gxDHxNQUBUbjHyl6oZfT+ofn7tbaHW+CFIUjI+Q2MBbHcx1JSBQfhDaTcO9bNg328q0i7Vk5PismQ==} - tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -7995,9 +7963,6 @@ packages: tweetnacl@0.14.5: resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} - tweetnacl@1.0.3: - resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -12228,18 +12193,6 @@ snapshots: buildcheck@0.0.6: optional: true - bullmq@5.13.2: - dependencies: - cron-parser: 4.9.0 - ioredis: 5.4.1 - msgpackr: 1.11.0 - node-abort-controller: 3.1.1 - semver: 7.6.2 - tslib: 2.6.3 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - bullmq@5.4.2: dependencies: cron-parser: 4.9.0 @@ -12658,8 +12611,6 @@ snapshots: date-fns@3.6.0: {} - date-fns@4.1.0: {} - debug@3.2.7: dependencies: ms: 2.1.3 @@ -13252,7 +13203,7 @@ snapshots: eslint: 8.45.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.45.0) eslint-plugin-react: 7.35.0(eslint@8.45.0) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.45.0) @@ -13276,7 +13227,7 @@ snapshots: enhanced-resolve: 5.17.1 eslint: 8.45.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.15.0 @@ -13298,7 +13249,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1)(eslint@8.45.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.45.0)(typescript@5.1.6))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.45.0))(eslint@8.45.0))(eslint@8.45.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -15199,10 +15150,6 @@ snapshots: napi-build-utils@1.0.2: optional: true - nats@2.28.2: - dependencies: - nkeys.js: 1.1.0 - natural-compare@1.4.0: {} negotiator@0.6.3: {} @@ -15305,10 +15252,6 @@ snapshots: - '@babel/core' - babel-plugin-macros - nkeys.js@1.1.0: - dependencies: - tweetnacl: 1.0.3 - node-abi@3.65.0: dependencies: semver: 7.6.2 @@ -16750,8 +16693,6 @@ snapshots: es5-ext: 0.10.64 next-tick: 1.1.0 - timers@0.1.1: {} - tiny-invariant@1.3.3: {} tinybench@2.8.0: {} @@ -16871,8 +16812,6 @@ snapshots: tweetnacl@0.14.5: {} - tweetnacl@1.0.3: {} - type-check@0.4.0: dependencies: prelude-ls: 1.2.1