diff --git a/.circleci/config.yml b/.circleci/config.yml index 9eca0514..07991599 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,6 +11,7 @@ jobs: command: | cp apps/dokploy/.env.production.example .env.production cp apps/dokploy/.env.production.example apps/dokploy/.env.production + - run: name: Build and push AMD64 image command: | @@ -61,7 +62,7 @@ jobs: VERSION=$(node -p "require('./apps/dokploy/package.json').version") echo $VERSION TAG="latest" - + docker manifest create dokploy/dokploy:${TAG} \ dokploy/dokploy:${TAG}-amd64 \ dokploy/dokploy:${TAG}-arm64 diff --git a/.github/sponsors/lxaer.png b/.github/sponsors/lxaer.png index 167c0653..722f8c34 100644 Binary files a/.github/sponsors/lxaer.png and b/.github/sponsors/lxaer.png differ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2051680b..e50c7207 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Build Docs & Website Docker images +name: Build Docker images on: push: @@ -48,3 +48,74 @@ jobs: push: true tags: dokploy/website:latest platforms: linux/amd64 + + + build-and-push-cloud-image: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile.cloud + push: true + tags: | + siumauricio/cloud:${{ github.ref_name == 'main' && 'main' || 'canary' }} + platforms: linux/amd64 + + build-and-push-schedule-image: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile.schedule + push: true + tags: | + siumauricio/schedule:${{ github.ref_name == 'main' && 'main' || 'canary' }} + platforms: linux/amd64 + + + build-and-push-server-image: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Log in to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + file: ./Dockerfile.server + push: true + tags: | + siumauricio/server:${{ github.ref_name == 'main' && 'main' || 'canary' }} + platforms: linux/amd64 \ No newline at end of file diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index b98dae66..bbec27a5 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -18,6 +18,7 @@ jobs: node-version: 18.18.0 cache: "pnpm" - run: pnpm install --frozen-lockfile + - run: pnpm run server:build - run: pnpm biome ci - run: pnpm typecheck @@ -32,6 +33,7 @@ jobs: node-version: 18.18.0 cache: "pnpm" - run: pnpm install --frozen-lockfile + - run: pnpm run server:build - run: pnpm build parallel-tests: @@ -44,4 +46,5 @@ jobs: node-version: 18.18.0 cache: "pnpm" - run: pnpm install --frozen-lockfile + - run: pnpm run server:build - run: pnpm test diff --git a/Dockerfile b/Dockerfile index 8b9d215c..74b70db0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,9 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile # Deploy only the dokploy app ENV NODE_ENV=production +RUN pnpm --filter=@dokploy/server build RUN pnpm --filter=./apps/dokploy run build + RUN pnpm --filter=./apps/dokploy --prod deploy /prod/dokploy RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next diff --git a/Dockerfile.cloud b/Dockerfile.cloud new file mode 100644 index 00000000..ee9d0ef9 --- /dev/null +++ b/Dockerfile.cloud @@ -0,0 +1,52 @@ +FROM node:18-slim 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 + +RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/* + +# Install dependencies +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/dokploy install --frozen-lockfile + +# Deploy only the dokploy app + +ENV NODE_ENV=production +RUN pnpm --filter=@dokploy/server build +RUN pnpm --filter=./apps/dokploy run build + +RUN pnpm --filter=./apps/dokploy --prod deploy /prod/dokploy + +RUN cp -R /usr/src/app/apps/dokploy/.next /prod/dokploy/.next +RUN cp -R /usr/src/app/apps/dokploy/dist /prod/dokploy/dist + +FROM base AS dokploy +WORKDIR /app + +# Set production +ENV NODE_ENV=production + +RUN apt-get update && apt-get install -y curl unzip apache2-utils && rm -rf /var/lib/apt/lists/* + +# Copy only the necessary files +COPY --from=build /prod/dokploy/.next ./.next +COPY --from=build /prod/dokploy/dist ./dist +COPY --from=build /prod/dokploy/next.config.mjs ./next.config.mjs +COPY --from=build /prod/dokploy/public ./public +COPY --from=build /prod/dokploy/package.json ./package.json +COPY --from=build /prod/dokploy/drizzle ./drizzle +COPY --from=build /prod/dokploy/components.json ./components.json +COPY --from=build /prod/dokploy/node_modules ./node_modules + + +# Install RCLONE +RUN curl https://rclone.org/install.sh | bash + +# tsx +RUN pnpm install -g tsx + +EXPOSE 3000 +CMD [ "pnpm", "start" ] \ No newline at end of file diff --git a/Dockerfile.schedule b/Dockerfile.schedule new file mode 100644 index 00000000..5eca3420 --- /dev/null +++ b/Dockerfile.schedule @@ -0,0 +1,36 @@ +FROM node:18-slim 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 + +RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/* + +# Install dependencies +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/schedules install --frozen-lockfile + +# Deploy only the dokploy app + +ENV NODE_ENV=production +RUN pnpm --filter=@dokploy/server build +RUN pnpm --filter=./apps/schedules run build + +RUN pnpm --filter=./apps/schedules --prod deploy /prod/schedules + +RUN cp -R /usr/src/app/apps/schedules/dist /prod/schedules/dist + +FROM base AS dokploy +WORKDIR /app + +# Set production +ENV NODE_ENV=production + +# Copy only the necessary files +COPY --from=build /prod/schedules/dist ./dist +COPY --from=build /prod/schedules/package.json ./package.json +COPY --from=build /prod/schedules/node_modules ./node_modules + +CMD HOSTNAME=0.0.0.0 && pnpm start \ No newline at end of file diff --git a/Dockerfile.server b/Dockerfile.server new file mode 100644 index 00000000..a25b22e5 --- /dev/null +++ b/Dockerfile.server @@ -0,0 +1,36 @@ +FROM node:18-slim 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 + +RUN apt-get update && apt-get install -y python3 make g++ git && rm -rf /var/lib/apt/lists/* + +# Install dependencies +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm --filter=@dokploy/server --filter=./apps/api install --frozen-lockfile + +# Deploy only the dokploy app + +ENV NODE_ENV=production +RUN pnpm --filter=@dokploy/server build +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/README.md b/README.md index 33891a3c..b0d05bb6 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Dokploy include multiples features to make your life easier. - **Docker Management**: Easily deploy and manage Docker containers. - **CLI/API**: Manage your applications and databases using the command line or trought the API. - **Notifications**: Get notified when your deployments are successful or failed (Slack, Discord, Telegram, Email, etc.) +- **Multi Server**: Deploy and manager your applications remotely to external servers. - **Self-Hosted**: Self-host Dokploy on your VPS. ## 🚀 Getting Started @@ -58,7 +59,14 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). ### Hero Sponsors 🎖 -Hostinger +
+ + Hostinger + + + LX Aer + +
### Premium Supporters 🥇 @@ -81,6 +89,7 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
Steamsets.com +Rivo.gg
#### Organizations: diff --git a/apps/api/package.json b/apps/api/package.json index 5450ceab..0da226af 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -1,15 +1,33 @@ { - "name": "my-app", + "name": "@dokploy/api", + "version": "0.0.1", + "type": "module", "scripts": { - "dev": "tsx watch src/index.ts" + "dev": "PORT=4000 tsx watch src/index.ts", + "build": "tsc --project tsconfig.json", + "start": "node --experimental-specifier-resolution=node dist/index.js", + "typecheck": "tsc --noEmit" }, "dependencies": { + "pino": "9.4.0", + "pino-pretty": "11.2.2", + "@hono/zod-validator": "0.3.0", + "zod": "^3.23.4", + "react": "18.2.0", + "react-dom": "18.2.0", + "@dokploy/server": "workspace:*", "@hono/node-server": "^1.12.1", "hono": "^4.5.8", - "dotenv": "^16.3.1" + "dotenv": "^16.3.1", + "redis": "4.7.0", + "@nerimity/mimiqueue": "1.2.3" }, "devDependencies": { + "typescript": "^5.4.2", + "@types/react": "^18.2.37", + "@types/react-dom": "^18.2.15", "@types/node": "^20.11.17", "tsx": "^4.7.1" - } + }, + "packageManager": "pnpm@9.5.0" } diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 866c1f3f..bf91b040 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,66 +1,61 @@ import { serve } from "@hono/node-server"; -import { config } from "dotenv"; import { Hono } from "hono"; -import { cors } from "hono/cors"; -import { validateLemonSqueezyLicense } from "./utils"; - -config(); +import "dotenv/config"; +import { zValidator } from "@hono/zod-validator"; +import { Queue } from "@nerimity/mimiqueue"; +import { createClient } from "redis"; +import { logger } from "./logger"; +import { type DeployJob, deployJobSchema } from "./schema"; +import { deploy } from "./utils"; const app = new Hono(); - -app.use( - "/*", - cors({ - origin: ["http://localhost:3000", "http://localhost:3001"], // Ajusta esto a los orígenes de tu aplicación Next.js - allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], - allowHeaders: ["Content-Type", "Authorization"], - exposeHeaders: ["Content-Length", "X-Kuma-Revision"], - maxAge: 600, - credentials: true, - }), -); - -export const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY; -export const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID; - -app.get("/v1/health", (c) => { - return c.text("Hello Hono!"); +const redisClient = createClient({ + url: process.env.REDIS_URL, }); -app.post("/v1/validate-license", async (c) => { - const { licenseKey } = await c.req.json(); +app.use(async (c, next) => { + if (c.req.path === "/health") { + return next(); + } + const authHeader = c.req.header("X-API-Key"); - if (!licenseKey) { - return c.json({ error: "License key is required" }, 400); + if (process.env.API_KEY !== authHeader) { + return c.json({ message: "Invalid API Key" }, 403); } - try { - const licenseValidation = await validateLemonSqueezyLicense(licenseKey); - - if (licenseValidation.valid) { - return c.json({ - valid: true, - message: "License is valid", - metadata: licenseValidation.meta, - }); - } - return c.json( - { - valid: false, - message: licenseValidation.error || "Invalid license", - }, - 400, - ); - } catch (error) { - console.error("Error during license validation:", error); - return c.json({ error: "Internal server error" }, 500); - } + return next(); }); -const port = 4000; -console.log(`Server is running on port ${port}`); - -serve({ - fetch: app.fetch, - port, +app.post("/deploy", zValidator("json", deployJobSchema), (c) => { + const data = c.req.valid("json"); + const res = queue.add(data, { groupName: data.serverId }); + return c.json( + { + message: "Deployment Added", + }, + 200, + ); }); + +app.get("/health", async (c) => { + return c.json({ status: "ok" }); +}); + +const queue = new Queue({ + name: "deployments", + process: async (job: DeployJob) => { + logger.info("Deploying job", job); + return await deploy(job); + }, + redisClient, +}); + +(async () => { + await redisClient.connect(); + await redisClient.flushAll(); + logger.info("Redis Cleaned"); +})(); + +const port = Number.parseInt(process.env.PORT || "3000"); +logger.info("Starting Deployments Server ✅", port); +serve({ fetch: app.fetch, port }); diff --git a/apps/api/src/logger.ts b/apps/api/src/logger.ts new file mode 100644 index 00000000..3d8b5840 --- /dev/null +++ b/apps/api/src/logger.ts @@ -0,0 +1,10 @@ +import pino from "pino"; + +export const logger = pino({ + transport: { + target: "pino-pretty", + options: { + colorize: true, + }, + }, +}); diff --git a/apps/api/src/schema.ts b/apps/api/src/schema.ts new file mode 100644 index 00000000..4655f006 --- /dev/null +++ b/apps/api/src/schema.ts @@ -0,0 +1,24 @@ +import { z } from "zod"; + +export const deployJobSchema = z.discriminatedUnion("applicationType", [ + z.object({ + applicationId: z.string(), + titleLog: z.string(), + descriptionLog: z.string(), + server: z.boolean().optional(), + type: z.enum(["deploy", "redeploy"]), + applicationType: z.literal("application"), + serverId: z.string(), + }), + z.object({ + composeId: z.string(), + titleLog: z.string(), + descriptionLog: z.string(), + server: z.boolean().optional(), + type: z.enum(["deploy", "redeploy"]), + applicationType: z.literal("compose"), + serverId: z.string(), + }), +]); + +export type DeployJob = z.infer; diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index 79263955..01b83a77 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -1,28 +1,96 @@ -import { LEMON_SQUEEZY_API_KEY, LEMON_SQUEEZY_STORE_ID } from "."; +import { + deployApplication, + deployCompose, + deployRemoteApplication, + deployRemoteCompose, + rebuildApplication, + rebuildCompose, + rebuildRemoteApplication, + rebuildRemoteCompose, + updateApplicationStatus, + updateCompose, +} from "@dokploy/server"; +import type { DeployJob } from "./schema"; import type { LemonSqueezyLicenseResponse } from "./types"; -export const validateLemonSqueezyLicense = async ( - licenseKey: string, -): Promise => { - try { - const response = await fetch( - "https://api.lemonsqueezy.com/v1/licenses/validate", - { - method: "POST", - headers: { - "Content-Type": "application/json", - "x-api-key": LEMON_SQUEEZY_API_KEY as string, - }, - body: JSON.stringify({ - license_key: licenseKey, - store_id: LEMON_SQUEEZY_STORE_ID as string, - }), - }, - ); +// 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 => { +// try { +// const response = await fetch( +// "https://api.lemonsqueezy.com/v1/licenses/validate", +// { +// method: "POST", +// headers: { +// "Content-Type": "application/json", +// "x-api-key": LEMON_SQUEEZY_API_KEY as string, +// }, +// body: JSON.stringify({ +// license_key: licenseKey, +// store_id: LEMON_SQUEEZY_STORE_ID as string, +// }), +// }, +// ); - return response.json(); +// return response.json(); +// } catch (error) { +// console.error("Error validating license:", error); +// return { valid: false, error: "Error validating license" }; +// } +// }; + +export const deploy = async (job: DeployJob) => { + try { + if (job.applicationType === "application") { + await updateApplicationStatus(job.applicationId, "running"); + if (job.server) { + if (job.type === "redeploy") { + await rebuildRemoteApplication({ + applicationId: job.applicationId, + titleLog: job.titleLog, + descriptionLog: job.descriptionLog, + }); + } else if (job.type === "deploy") { + await deployRemoteApplication({ + applicationId: job.applicationId, + titleLog: job.titleLog, + descriptionLog: job.descriptionLog, + }); + } + } + } else if (job.applicationType === "compose") { + await updateCompose(job.composeId, { + composeStatus: "running", + }); + + if (job.server) { + if (job.type === "redeploy") { + await rebuildRemoteCompose({ + composeId: job.composeId, + titleLog: job.titleLog, + descriptionLog: job.descriptionLog, + }); + } else if (job.type === "deploy") { + await deployRemoteCompose({ + composeId: job.composeId, + titleLog: job.titleLog, + descriptionLog: job.descriptionLog, + }); + } + } + } } catch (error) { - console.error("Error validating license:", error); - return { valid: false, error: "Error validating license" }; + console.log(error); + if (job.applicationType === "application") { + await updateApplicationStatus(job.applicationId, "error"); + } else if (job.applicationType === "compose") { + await updateCompose(job.composeId, { + composeStatus: "error", + }); + } } + + return true; }; diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 68a9e8f0..3c0b02bc 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -2,11 +2,12 @@ "compilerOptions": { "target": "ESNext", "module": "ESNext", - "moduleResolution": "Bundler", + "moduleResolution": "Node", "strict": true, "skipLibCheck": true, - "types": ["node"], + "outDir": "dist", "jsx": "react-jsx", "jsxImportSource": "hono/jsx" - } + }, + "exclude": ["node_modules", "dist"] } diff --git a/apps/docs/content/docs/core/templates/overview.mdx b/apps/docs/content/docs/core/templates/overview.mdx index 363650f6..6c8a7e8c 100644 --- a/apps/docs/content/docs/core/templates/overview.mdx +++ b/apps/docs/content/docs/core/templates/overview.mdx @@ -31,8 +31,7 @@ The following templates are available: - **Wordpress**: Open Source Content Management System - **Open WebUI**: Free and Open Source ChatGPT Alternative - **Teable**: Open Source Airtable Alternative, Developer Friendly, No-code Database Built on Postgres - - +- **Roundcube**: Free and open source webmail software for the masses, written in PHP, uses SMTP[^1]. ## Create your own template @@ -41,3 +40,5 @@ We accept contributions to upload new templates to the dokploy repository. Make sure to follow the guidelines for creating a template: [Steps to create your own template](https://github.com/Dokploy/dokploy/blob/canary/CONTRIBUTING.md#templates) + +[^1]: Please note that if you're self-hosting a mail server you need port 25 to be open for SMTP (Mail Transmission Protocol that allows you to send and receive) to work properly. Some VPS providers like [Hetzner](https://docs.hetzner.com/cloud/servers/faq/#why-can-i-not-send-any-mails-from-my-server) block this port by default for new clients. diff --git a/apps/docs/mdx-components.tsx b/apps/docs/mdx-components.tsx index 96c5b74b..10488dca 100644 --- a/apps/docs/mdx-components.tsx +++ b/apps/docs/mdx-components.tsx @@ -10,8 +10,10 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { p: ({ children }) => (

{children}

), - li: ({ children }) => ( -
  • {children}
  • + li: ({ children, id }) => ( +
  • + {children} +
  • ), }; } diff --git a/apps/docs/package.json b/apps/docs/package.json index a739744e..211d8449 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -21,15 +21,11 @@ "react-ga4": "^2.1.0" }, "devDependencies": { - "tsx": "4.15.7", - "@biomejs/biome": "1.8.1", + "autoprefixer": "10.4.12", "@types/mdx": "^2.0.13", - "@types/node": "^20.14.2", "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "autoprefixer": "^10.4.19", "postcss": "^8.4.38", - "tailwindcss": "^3.4.4", + "tailwindcss": "^3.4.1", "typescript": "^5.4.5" } } diff --git a/apps/dokploy/__test__/compose/compose.test.ts b/apps/dokploy/__test__/compose/compose.test.ts index 4c47b111..9d4ba20f 100644 --- a/apps/dokploy/__test__/compose/compose.test.ts +++ b/apps/dokploy/__test__/compose/compose.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllProperties } from "@/server/utils/docker/compose"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { addSuffixToAllProperties } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-root.test.ts b/apps/dokploy/__test__/compose/config/config-root.test.ts index 7f4311f6..4b40c073 100644 --- a/apps/dokploy/__test__/compose/config/config-root.test.ts +++ b/apps/dokploy/__test__/compose/config/config-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToConfigsRoot } from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToConfigsRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config-service.test.ts b/apps/dokploy/__test__/compose/config/config-service.test.ts index 4d3051a5..de014eb5 100644 --- a/apps/dokploy/__test__/compose/config/config-service.test.ts +++ b/apps/dokploy/__test__/compose/config/config-service.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToConfigsInServices } from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToConfigsInServices } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/config/config.test.ts b/apps/dokploy/__test__/compose/config/config.test.ts index 4677fbe9..3f98525a 100644 --- a/apps/dokploy/__test__/compose/config/config.test.ts +++ b/apps/dokploy/__test__/compose/config/config.test.ts @@ -1,9 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { - addSuffixToAllConfigs, - addSuffixToConfigsRoot, -} from "@/server/utils/docker/compose/configs"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToAllConfigs, addSuffixToConfigsRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/domain/labels.test.ts b/apps/dokploy/__test__/compose/domain/labels.test.ts index 9d5c0b52..5076a204 100644 --- a/apps/dokploy/__test__/compose/domain/labels.test.ts +++ b/apps/dokploy/__test__/compose/domain/labels.test.ts @@ -1,5 +1,5 @@ -import type { Domain } from "@/server/api/services/domain"; -import { createDomainLabels } from "@/server/utils/docker/domain"; +import type { Domain } from "@dokploy/server"; +import { createDomainLabels } from "@dokploy/server"; import { describe, expect, it } from "vitest"; describe("createDomainLabels", () => { diff --git a/apps/dokploy/__test__/compose/domain/network-root.test.ts b/apps/dokploy/__test__/compose/domain/network-root.test.ts index ca8b1655..73b850a4 100644 --- a/apps/dokploy/__test__/compose/domain/network-root.test.ts +++ b/apps/dokploy/__test__/compose/domain/network-root.test.ts @@ -1,4 +1,4 @@ -import { addDokployNetworkToRoot } from "@/server/utils/docker/domain"; +import { addDokployNetworkToRoot } from "@dokploy/server"; import { describe, expect, it } from "vitest"; describe("addDokployNetworkToRoot", () => { diff --git a/apps/dokploy/__test__/compose/domain/network-service.test.ts b/apps/dokploy/__test__/compose/domain/network-service.test.ts index 18faf564..b8d03c75 100644 --- a/apps/dokploy/__test__/compose/domain/network-service.test.ts +++ b/apps/dokploy/__test__/compose/domain/network-service.test.ts @@ -1,4 +1,4 @@ -import { addDokployNetworkToService } from "@/server/utils/docker/domain"; +import { addDokployNetworkToService } from "@dokploy/server"; import { describe, expect, it } from "vitest"; describe("addDokployNetworkToService", () => { diff --git a/apps/dokploy/__test__/compose/network/network-root.test.ts b/apps/dokploy/__test__/compose/network/network-root.test.ts index b5156b17..7e06a9f0 100644 --- a/apps/dokploy/__test__/compose/network/network-root.test.ts +++ b/apps/dokploy/__test__/compose/network/network-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToNetworksRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network-service.test.ts b/apps/dokploy/__test__/compose/network/network-service.test.ts index 92890e17..ee07d9de 100644 --- a/apps/dokploy/__test__/compose/network/network-service.test.ts +++ b/apps/dokploy/__test__/compose/network/network-service.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNetworks } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNetworks } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network.test.ts b/apps/dokploy/__test__/compose/network/network.test.ts index ba1b1395..39cf0395 100644 --- a/apps/dokploy/__test__/compose/network/network.test.ts +++ b/apps/dokploy/__test__/compose/network/network.test.ts @@ -1,10 +1,10 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/server"; import { addSuffixToAllNetworks, addSuffixToServiceNetworks, -} from "@/server/utils/docker/compose/network"; -import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/server"; +import { addSuffixToNetworksRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts index 3185b966..2bd91b58 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToSecretsRoot } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToSecretsRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { dump, load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts index 4a3df137..5206bbba 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-services.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-services.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToSecretsInServices } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToSecretsInServices } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/secrets/secret.test.ts b/apps/dokploy/__test__/compose/secrets/secret.test.ts index ea3552f3..d874dc5e 100644 --- a/apps/dokploy/__test__/compose/secrets/secret.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret.test.ts @@ -1,5 +1,5 @@ -import { addSuffixToAllSecrets } from "@/server/utils/docker/compose/secrets"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { addSuffixToAllSecrets } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-container-name.test.ts b/apps/dokploy/__test__/compose/service/service-container-name.test.ts index 623b5381..bcb51fd0 100644 --- a/apps/dokploy/__test__/compose/service/service-container-name.test.ts +++ b/apps/dokploy/__test__/compose/service/service-container-name.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts index 02d88591..b27414be 100644 --- a/apps/dokploy/__test__/compose/service/service-depends-on.test.ts +++ b/apps/dokploy/__test__/compose/service/service-depends-on.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-extends.test.ts b/apps/dokploy/__test__/compose/service/service-extends.test.ts index 5590005b..8309a32f 100644 --- a/apps/dokploy/__test__/compose/service/service-extends.test.ts +++ b/apps/dokploy/__test__/compose/service/service-extends.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-links.test.ts b/apps/dokploy/__test__/compose/service/service-links.test.ts index 70697e29..5f9b01ab 100644 --- a/apps/dokploy/__test__/compose/service/service-links.test.ts +++ b/apps/dokploy/__test__/compose/service/service-links.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service-names.test.ts b/apps/dokploy/__test__/compose/service/service-names.test.ts index 95910ef0..936a32ec 100644 --- a/apps/dokploy/__test__/compose/service/service-names.test.ts +++ b/apps/dokploy/__test__/compose/service/service-names.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/service.test.ts b/apps/dokploy/__test__/compose/service/service.test.ts index 02ccaea2..c6050f75 100644 --- a/apps/dokploy/__test__/compose/service/service.test.ts +++ b/apps/dokploy/__test__/compose/service/service.test.ts @@ -1,8 +1,8 @@ import { addSuffixToAllServiceNames, addSuffixToServiceNames, -} from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts index 49c2b55e..8066a6dd 100644 --- a/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts +++ b/apps/dokploy/__test__/compose/service/sevice-volumes-from.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToServiceNames } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-2.test.ts b/apps/dokploy/__test__/compose/volume/volume-2.test.ts index 9424bc0e..bf34ed49 100644 --- a/apps/dokploy/__test__/compose/volume/volume-2.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-2.test.ts @@ -1,9 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { - addSuffixToAllVolumes, - addSuffixToVolumesRoot, -} from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToAllVolumes, addSuffixToVolumesRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-root.test.ts b/apps/dokploy/__test__/compose/volume/volume-root.test.ts index 6c1f5d10..d91cb64d 100644 --- a/apps/dokploy/__test__/compose/volume/volume-root.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-root.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToVolumesRoot } from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToVolumesRoot } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume-services.test.ts b/apps/dokploy/__test__/compose/volume/volume-services.test.ts index 1940a4f2..04a1a45a 100644 --- a/apps/dokploy/__test__/compose/volume/volume-services.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume-services.test.ts @@ -1,6 +1,6 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; -import { addSuffixToVolumesInServices } from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +import { generateRandomHash } from "@dokploy/server"; +import { addSuffixToVolumesInServices } from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/volume/volume.test.ts b/apps/dokploy/__test__/compose/volume/volume.test.ts index 8d799bcd..d4623aeb 100644 --- a/apps/dokploy/__test__/compose/volume/volume.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume.test.ts @@ -1,9 +1,9 @@ -import { generateRandomHash } from "@/server/utils/docker/compose"; +import { generateRandomHash } from "@dokploy/server"; import { addSuffixToAllVolumes, addSuffixToVolumesInServices, -} from "@/server/utils/docker/compose/volume"; -import type { ComposeSpecification } from "@/server/utils/docker/types"; +} from "@dokploy/server"; +import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index c411566a..92821fb5 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -1,9 +1,9 @@ import fs from "node:fs/promises"; import path from "node:path"; -import { paths } from "@/server/constants"; +import { paths } from "@dokploy/server/dist/constants"; const { APPLICATIONS_PATH } = paths(); -import type { ApplicationNested } from "@/server/utils/builders"; -import { unzipDrop } from "@/server/utils/builders/drop"; +import type { ApplicationNested } from "@dokploy/server"; +import { unzipDrop } from "@dokploy/server"; import AdmZip from "adm-zip"; import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; @@ -81,14 +81,17 @@ const baseApp: ApplicationNested = { username: null, dockerContextPath: null, }; -// -vi.mock("@/server/constants", () => ({ - paths: () => ({ - APPLICATIONS_PATH: "./__test__/drop/zips/output", - }), - // APPLICATIONS_PATH: "./__test__/drop/zips/output", -})); +vi.mock("@dokploy/server/dist/constants", async (importOriginal) => { + const actual = await importOriginal(); + return { + // @ts-ignore + ...actual, + paths: () => ({ + APPLICATIONS_PATH: "./__test__/drop/zips/output", + }), + }; +}); describe("unzipDrop using real zip files", () => { // const { APPLICATIONS_PATH } = paths(); beforeAll(async () => { @@ -102,15 +105,19 @@ describe("unzipDrop using real zip files", () => { it("should correctly extract a zip with a single root folder", async () => { baseApp.appName = "single-file"; // const appName = "single-file"; - const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code"); - const zip = new AdmZip("./__test__/drop/zips/single-file.zip"); - - const zipBuffer = zip.toBuffer(); - const file = new File([zipBuffer], "single.zip"); - await unzipDrop(file, baseApp); - - const files = await fs.readdir(outputPath, { withFileTypes: true }); - expect(files.some((f) => f.name === "test.txt")).toBe(true); + try { + const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code"); + const zip = new AdmZip("./__test__/drop/zips/single-file.zip"); + console.log(`Output Path: ${outputPath}`); + const zipBuffer = zip.toBuffer(); + const file = new File([zipBuffer], "single.zip"); + await unzipDrop(file, baseApp); + const files = await fs.readdir(outputPath, { withFileTypes: true }); + expect(files.some((f) => f.name === "test.txt")).toBe(true); + } catch (err) { + console.log(err); + } finally { + } }); it("should correctly extract a zip with a single root folder and a subfolder", async () => { diff --git a/apps/dokploy/__test__/requests/request.test.ts b/apps/dokploy/__test__/requests/request.test.ts index 0c2e5f67..997bd9ec 100644 --- a/apps/dokploy/__test__/requests/request.test.ts +++ b/apps/dokploy/__test__/requests/request.test.ts @@ -1,4 +1,4 @@ -import { parseRawConfig, processLogs } from "@/server/utils/access-log/utils"; +import { parseRawConfig, processLogs } from "@dokploy/server"; import { describe, expect, it } from "vitest"; const sampleLogEntry = `{"ClientAddr":"172.19.0.1:56732","ClientHost":"172.19.0.1","ClientPort":"56732","ClientUsername":"-","DownstreamContentSize":0,"DownstreamStatus":304,"Duration":14729375,"OriginContentSize":0,"OriginDuration":14051833,"OriginStatus":304,"Overhead":677542,"RequestAddr":"s222-umami-c381af.traefik.me","RequestContentSize":0,"RequestCount":122,"RequestHost":"s222-umami-c381af.traefik.me","RequestMethod":"GET","RequestPath":"/dashboard?_rsc=1rugv","RequestPort":"-","RequestProtocol":"HTTP/1.1","RequestScheme":"http","RetryAttempts":0,"RouterName":"s222-umami-60e104-47-web@docker","ServiceAddr":"10.0.1.15:3000","ServiceName":"s222-umami-60e104-47-web@docker","ServiceURL":{"Scheme":"http","Opaque":"","User":null,"Host":"10.0.1.15:3000","Path":"","RawPath":"","ForceQuery":false,"RawQuery":"","Fragment":"","RawFragment":""},"StartLocal":"2024-08-25T04:34:37.306691884Z","StartUTC":"2024-08-25T04:34:37.306691884Z","entryPointName":"web","level":"info","msg":"","time":"2024-08-25T04:34:37Z"}`; diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 8dd5dbc0..8eb387cc 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -5,11 +5,12 @@ vi.mock("node:fs", () => ({ default: fs, })); -import type { Admin } from "@/server/api/services/admin"; -import { createDefaultServerTraefikConfig } from "@/server/setup/traefik-setup"; -import { loadOrCreateConfig } from "@/server/utils/traefik/application"; -import type { FileConfig } from "@/server/utils/traefik/file-types"; -import { updateServerTraefik } from "@/server/utils/traefik/web-server"; +import type { Admin, FileConfig } from "@dokploy/server"; +import { + createDefaultServerTraefikConfig, + loadOrCreateConfig, + updateServerTraefik, +} from "@dokploy/server"; import { beforeEach, expect, test, vi } from "vitest"; const baseAdmin: Admin = { diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index 222f8fd7..637d12ff 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -1,7 +1,7 @@ -import type { Domain } from "@/server/api/services/domain"; -import type { Redirect } from "@/server/api/services/redirect"; -import type { ApplicationNested } from "@/server/utils/builders"; -import { createRouterConfig } from "@/server/utils/traefik/domain"; +import type { Domain } from "@dokploy/server"; +import type { Redirect } from "@dokploy/server"; +import type { ApplicationNested } from "@dokploy/server"; +import { createRouterConfig } from "@dokploy/server"; import { expect, test } from "vitest"; const baseApp: ApplicationNested = { diff --git a/apps/dokploy/__test__/vitest.config.ts b/apps/dokploy/__test__/vitest.config.ts index 71749b6c..c11f6cef 100644 --- a/apps/dokploy/__test__/vitest.config.ts +++ b/apps/dokploy/__test__/vitest.config.ts @@ -13,4 +13,9 @@ export default defineConfig({ exclude: ["**/node_modules/**", "**/dist/**", "**/.docker/**"], pool: "forks", }, + define: { + "process.env": { + NODE: "test", + }, + }, }); diff --git a/apps/dokploy/components/dashboard/application/advanced/show-application-advanced-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/show-application-advanced-settings.tsx index 56513465..e77be2d1 100644 --- a/apps/dokploy/components/dashboard/application/advanced/show-application-advanced-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/show-application-advanced-settings.tsx @@ -80,7 +80,7 @@ export const ShowApplicationResources = ({ applicationId }: Props) => { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx index 2e892523..6913aa24 100644 --- a/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/show-deployment.tsx @@ -16,20 +16,37 @@ interface Props { export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { const [data, setData] = useState(""); const endOfLogsRef = useRef(null); + const wsRef = useRef(null); // Ref to hold WebSocket instance useEffect(() => { if (!open || !logPath) return; + setData(""); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`; const ws = new WebSocket(wsUrl); + wsRef.current = ws; // Store WebSocket instance in ref ws.onmessage = (e) => { setData((currentData) => currentData + e.data); }; - return () => ws.close(); + ws.onerror = (error) => { + console.error("WebSocket error: ", error); + }; + + ws.onclose = () => { + console.log("WebSocket connection closed"); + wsRef.current = null; // Clear reference on close + }; + + return () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + ws.close(); + wsRef.current = null; + } + }; }, [logPath, open]); const scrollToBottom = () => { @@ -45,7 +62,15 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => { open={open} onOpenChange={(e) => { onClose(); - if (!e) setData(""); + if (!e) { + setData(""); + } + + if (wsRef.current) { + if (wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.close(); + } + } }} > diff --git a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx index 90112e3c..8ca59061 100644 --- a/apps/dokploy/components/dashboard/application/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/application/domains/show-domains.tsx @@ -52,7 +52,7 @@ export const ShowDomains = ({ applicationId }: Props) => {
    - To access to the application is required to set at least 1 + To access the application it is required to set at least 1 domain
    diff --git a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx index e7910469..8413b14b 100644 --- a/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/deployments/show-deployment-compose.tsx @@ -21,20 +21,38 @@ export const ShowDeploymentCompose = ({ }: Props) => { const [data, setData] = useState(""); const endOfLogsRef = useRef(null); + const wsRef = useRef(null); // Ref to hold WebSocket instance useEffect(() => { if (!open || !logPath) return; + setData(""); const protocol = window.location.protocol === "https:" ? "wss:" : "ws:"; const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`; const ws = new WebSocket(wsUrl); + wsRef.current = ws; // Store WebSocket instance in ref + ws.onmessage = (e) => { setData((currentData) => currentData + e.data); }; - return () => ws.close(); + ws.onerror = (error) => { + console.error("WebSocket error: ", error); + }; + + ws.onclose = () => { + console.log("WebSocket connection closed"); + wsRef.current = null; + }; + + return () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + ws.close(); + wsRef.current = null; + } + }; }, [logPath, open]); const scrollToBottom = () => { @@ -50,7 +68,15 @@ export const ShowDeploymentCompose = ({ open={open} onOpenChange={(e) => { onClose(); - if (!e) setData(""); + if (!e) { + setData(""); + } + + if (wsRef.current) { + if (wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.close(); + } + } }} > diff --git a/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx index 01e6f1ab..dc798f80 100644 --- a/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx @@ -53,7 +53,7 @@ export const ShowDomainsCompose = ({ composeId }: Props) => {
    - To access to the application is required to set at least 1 + To access to the application it is required to set at least 1 domain
    diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index 292ff4ca..90bf6e30 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -77,7 +77,6 @@ export const ComposeFileEditor = ({ composeId }: Props) => { }); }) .catch((e) => { - console.log(e); toast.error("Error to update the compose config"); }); }; diff --git a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx index bb01badc..ec441e02 100644 --- a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx @@ -11,7 +11,7 @@ import { } from "@/components/ui/dialog"; import { api } from "@/utils/api"; import { Puzzle, RefreshCw } from "lucide-react"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { toast } from "sonner"; interface Props { @@ -34,6 +34,16 @@ export const ShowConvertedCompose = ({ composeId }: Props) => { const { mutateAsync, isLoading } = api.compose.fetchSourceType.useMutation(); + useEffect(() => { + if (isOpen) { + mutateAsync({ composeId }) + .then(() => { + refetch(); + }) + .catch((err) => {}); + } + }, [isOpen]); + return ( diff --git a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx index d9bce727..ca9dbbb6 100644 --- a/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/docker-logs-id.tsx @@ -1,7 +1,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Terminal } from "@xterm/xterm"; -import React, { useEffect } from "react"; +import React, { useEffect, useRef } from "react"; import { FitAddon } from "xterm-addon-fit"; import "@xterm/xterm/css/xterm.css"; @@ -18,12 +18,24 @@ export const DockerLogsId: React.FC = ({ }) => { const [term, setTerm] = React.useState(); const [lines, setLines] = React.useState(40); + const wsRef = useRef(null); // Ref to hold WebSocket instance - const createTerminal = (): Terminal => { + useEffect(() => { + // if (containerId === "select-a-container") { + // return; + // } const container = document.getElementById(id); if (container) { container.innerHTML = ""; } + + if (wsRef.current) { + console.log(wsRef.current); + if (wsRef.current.readyState === WebSocket.OPEN) { + wsRef.current.close(); + } + wsRef.current = null; + } const termi = new Terminal({ cursorBlink: true, cols: 80, @@ -45,7 +57,7 @@ export const DockerLogsId: React.FC = ({ const wsUrl = `${protocol}//${window.location.host}/docker-container-logs?containerId=${containerId}&tail=${lines}${serverId ? `&serverId=${serverId}` : ""}`; const ws = new WebSocket(wsUrl); - + wsRef.current = ws; const fitAddon = new FitAddon(); termi.loadAddon(fitAddon); // @ts-ignore @@ -54,6 +66,10 @@ export const DockerLogsId: React.FC = ({ termi.focus(); setTerm(termi); + ws.onerror = (error) => { + console.error("WebSocket error: ", error); + }; + ws.onmessage = (e) => { termi.write(e.data); }; @@ -62,12 +78,14 @@ export const DockerLogsId: React.FC = ({ console.log(e.reason); termi.write(`Connection closed!\nReason: ${e.reason}\n`); + wsRef.current = null; + }; + return () => { + if (wsRef.current?.readyState === WebSocket.OPEN) { + ws.close(); + wsRef.current = null; + } }; - return termi; - }; - - useEffect(() => { - createTerminal(); }, [lines, containerId]); useEffect(() => { diff --git a/apps/dokploy/components/dashboard/mariadb/advanced/show-mariadb-resources.tsx b/apps/dokploy/components/dashboard/mariadb/advanced/show-mariadb-resources.tsx index bbc5d2f5..60cdbda0 100644 --- a/apps/dokploy/components/dashboard/mariadb/advanced/show-mariadb-resources.tsx +++ b/apps/dokploy/components/dashboard/mariadb/advanced/show-mariadb-resources.tsx @@ -79,7 +79,7 @@ export const ShowMariadbResources = ({ mariadbId }: Props) => { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/mariadb/backups/show-backup-mariadb.tsx b/apps/dokploy/components/dashboard/mariadb/backups/show-backup-mariadb.tsx index 85353e8b..f3d214e3 100644 --- a/apps/dokploy/components/dashboard/mariadb/backups/show-backup-mariadb.tsx +++ b/apps/dokploy/components/dashboard/mariadb/backups/show-backup-mariadb.tsx @@ -44,7 +44,7 @@ export const ShowBackupMariadb = ({ mariadbId }: Props) => {
    Backups - Add backup to your database to save the data to a different + Add backups to your database to save the data to a different providers.
    @@ -62,8 +62,8 @@ export const ShowBackupMariadb = ({ mariadbId }: Props) => {
    - To create a backup is required to set at least 1 provider. Please, - go to{" "} + To create a backup it is required to set at least 1 provider. + Please, go to{" "} { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/mongo/backups/show-backup-mongo.tsx b/apps/dokploy/components/dashboard/mongo/backups/show-backup-mongo.tsx index e54fe70d..0f0478c5 100644 --- a/apps/dokploy/components/dashboard/mongo/backups/show-backup-mongo.tsx +++ b/apps/dokploy/components/dashboard/mongo/backups/show-backup-mongo.tsx @@ -44,8 +44,8 @@ export const ShowBackupMongo = ({ mongoId }: Props) => {
    Backups - Add backup to your database to save the data to a different - providers. + Add backups to your database to save the data to a different + provider.
    @@ -62,8 +62,8 @@ export const ShowBackupMongo = ({ mongoId }: Props) => {
    - To create a backup is required to set at least 1 provider. Please, - go to{" "} + To create a backup it is required to set at least 1 provider. + Please, go to{" "} {
    Volumes - If you want to persist data in this mongo use the following config + If you want to persist data in this mongo use the following config. to setup the volumes
    diff --git a/apps/dokploy/components/dashboard/monitoring/docker/show.tsx b/apps/dokploy/components/dashboard/monitoring/docker/show.tsx index 365615ce..d9deaa35 100644 --- a/apps/dokploy/components/dashboard/monitoring/docker/show.tsx +++ b/apps/dokploy/components/dashboard/monitoring/docker/show.tsx @@ -191,7 +191,7 @@ export const DockerMonitoring = ({ Monitoring - Watch the usage of your server in the current app + Watch the usage of your server in the current app. diff --git a/apps/dokploy/components/dashboard/mysql/advanced/show-mysql-resources.tsx b/apps/dokploy/components/dashboard/mysql/advanced/show-mysql-resources.tsx index 7feeb031..07ceeae9 100644 --- a/apps/dokploy/components/dashboard/mysql/advanced/show-mysql-resources.tsx +++ b/apps/dokploy/components/dashboard/mysql/advanced/show-mysql-resources.tsx @@ -79,7 +79,7 @@ export const ShowMysqlResources = ({ mysqlId }: Props) => { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/mysql/backups/show-backup-mysql.tsx b/apps/dokploy/components/dashboard/mysql/backups/show-backup-mysql.tsx index b44d2055..a760fd92 100644 --- a/apps/dokploy/components/dashboard/mysql/backups/show-backup-mysql.tsx +++ b/apps/dokploy/components/dashboard/mysql/backups/show-backup-mysql.tsx @@ -44,8 +44,8 @@ export const ShowBackupMySql = ({ mysqlId }: Props) => {
    Backups - Add backup to your database to save the data to a different - providers. + Add backups to your database to save the data to a different + provider.
    @@ -62,8 +62,8 @@ export const ShowBackupMySql = ({ mysqlId }: Props) => {
    - To create a backup is required to set at least 1 provider. Please, - go to{" "} + To create a backup it is required to set at least 1 provider. + Please, go to{" "} { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/postgres/backups/show-backup-postgres.tsx b/apps/dokploy/components/dashboard/postgres/backups/show-backup-postgres.tsx index 877769b7..c5a36a6a 100644 --- a/apps/dokploy/components/dashboard/postgres/backups/show-backup-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/backups/show-backup-postgres.tsx @@ -45,8 +45,8 @@ export const ShowBackupPostgres = ({ postgresId }: Props) => {
    Backups - Add backup to your database to save the data to a different - providers. + Add backups to your database to save the data to a different + provider.
    @@ -63,8 +63,8 @@ export const ShowBackupPostgres = ({ postgresId }: Props) => {
    - To create a backup is required to set at least 1 provider. Please, - go to{" "} + To create a backup it is required to set at least 1 provider. + Please, go to{" "} { }, ); const { mutateAsync } = api.project.remove.useMutation(); + return ( <> {data?.length === 0 && ( @@ -74,17 +80,87 @@ export const ShowProjects = () => { project?.redis.length + project?.applications.length + project?.compose.length; + + const flattedDomains = [ + ...project.applications.flatMap((a) => a.domains), + ...project.compose.flatMap((a) => a.domains), + ]; + + const renderDomainsDropdown = ( + item: typeof project.compose | typeof project.applications, + ) => + item[0] ? ( + + + {"applicationId" in item[0] ? "Applications" : "Compose"} + + {item.map((a) => ( + + + + + {a.name} + + + {a.domains.map((domain) => ( + + + {domain.host} + + + + ))} + + + ))} + + ) : null; + return (
    - + {flattedDomains.length > 1 ? ( + + + + + e.stopPropagation()} + > + {renderDomainsDropdown(project.applications)} + {renderDomainsDropdown(project.compose)} + + + ) : flattedDomains[0] ? ( + + ) : null} + diff --git a/apps/dokploy/components/dashboard/redis/advanced/show-redis-resources.tsx b/apps/dokploy/components/dashboard/redis/advanced/show-redis-resources.tsx index cac15e0b..1e719399 100644 --- a/apps/dokploy/components/dashboard/redis/advanced/show-redis-resources.tsx +++ b/apps/dokploy/components/dashboard/redis/advanced/show-redis-resources.tsx @@ -79,7 +79,7 @@ export const ShowRedisResources = ({ redisId }: Props) => { Resources - If you want to decrease or increase the resources to a specific + If you want to decrease or increase the resources to a specific. application or database diff --git a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx index 6572e3ba..50b2dd90 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx @@ -18,10 +18,25 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle } from "lucide-react"; +import { AlertTriangle, HelpCircle } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; @@ -35,6 +50,7 @@ const addCertificate = z.object({ certificateData: z.string().min(1, "Certificate data is required"), privateKey: z.string().min(1, "Private key is required"), autoRenew: z.boolean().optional(), + serverId: z.string().optional(), }); type AddCertificate = z.infer; @@ -44,6 +60,7 @@ export const AddCertificate = () => { const { mutateAsync, isError, error, isLoading } = api.certificates.create.useMutation(); + const { data: servers } = api.server.withSSHKey.useQuery(); const form = useForm({ defaultValues: { @@ -64,6 +81,7 @@ export const AddCertificate = () => { certificateData: data.certificateData, privateKey: data.privateKey, autoRenew: data.autoRenew, + serverId: data.serverId, }) .then(async () => { toast.success("Certificate Created"); @@ -144,6 +162,47 @@ export const AddCertificate = () => { )} /> + ( + + + + + + Select a Server (Optional) + + + + + + + + + + )} + /> diff --git a/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx b/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx index 963bc571..69b1a332 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/show-certificates.tsx @@ -27,7 +27,8 @@ export const ShowCertificates = () => {
    - To create a certificate is required to upload your certificate + To create a certificate it is required to upload an existing + certificate
    diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/show-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/show-registry.tsx index a754737d..94c82c48 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/show-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/show-registry.tsx @@ -43,7 +43,7 @@ export const ShowRegistry = () => {
    - To create a cluster is required to set a registry. + To create a cluster it is required to set a registry.
    diff --git a/apps/dokploy/components/dashboard/settings/destination/show-destinations.tsx b/apps/dokploy/components/dashboard/settings/destination/show-destinations.tsx index d05d824b..d1dabf42 100644 --- a/apps/dokploy/components/dashboard/settings/destination/show-destinations.tsx +++ b/apps/dokploy/components/dashboard/settings/destination/show-destinations.tsx @@ -29,7 +29,7 @@ export const ShowDestinations = () => {
    - To create a backup is required to set at least 1 provider. + To create a backup it is required to set at least 1 provider.
    diff --git a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx index 914d0338..58cc8723 100644 --- a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx @@ -11,13 +11,11 @@ import { import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; -import { useUrl } from "@/utils/hooks/use-url"; import { format } from "date-fns"; import { useEffect, useState } from "react"; export const AddGithubProvider = () => { const [isOpen, setIsOpen] = useState(false); - const url = useUrl(); const { data } = api.auth.get.useQuery(); const [manifest, setManifest] = useState(""); const [isOrganization, setIsOrganization] = useState(false); diff --git a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx index 46fa5e8b..c22f7b72 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx @@ -34,7 +34,7 @@ export const ShowNotifications = () => {
    - To send notifications is required to set at least 1 provider. + To send notifications it is required to set at least 1 provider.
    diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 1aa25903..43cd2f0e 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -95,7 +95,7 @@ export const ProfileForm = () => {
    Account - Change your details of your profile here. + Change the details of your profile here.
    {!data?.is2FAEnabled ? : } @@ -145,7 +145,6 @@ export const ProfileForm = () => { { - console.log(e); field.onChange(e); }} defaultValue={field.value} diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx index bf1a298b..8bfcf4da 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-server.tsx @@ -19,7 +19,6 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { Separator } from "@/components/ui/separator"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { api } from "@/utils/api"; import copy from "copy-to-clipboard"; diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 5157239e..354f1158 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -29,11 +29,22 @@ import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -const addServerDomain = z.object({ - domain: z.string().min(1, { message: "URL is required" }), - letsEncryptEmail: z.string().min(1, "Email is required").email(), - certificateType: z.enum(["letsencrypt", "none"]), -}); +const addServerDomain = z + .object({ + domain: z.string().min(1, { message: "URL is required" }), + letsEncryptEmail: z.string(), + certificateType: z.enum(["letsencrypt", "none"]), + }) + .superRefine((data, ctx) => { + if (data.certificateType === "letsencrypt" && !data.letsEncryptEmail) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: + "LetsEncrypt email is required when certificate type is letsencrypt", + path: ["letsEncryptEmail"], + }); + } + }); type AddServerDomain = z.infer; @@ -80,7 +91,7 @@ export const WebDomain = () => { Server Domain - Add your server domain to your application + Add a domain to your server application. diff --git a/apps/dokploy/components/layouts/navigation-tabs.tsx b/apps/dokploy/components/layouts/navigation-tabs.tsx index decab28a..9110ba50 100644 --- a/apps/dokploy/components/layouts/navigation-tabs.tsx +++ b/apps/dokploy/components/layouts/navigation-tabs.tsx @@ -1,7 +1,7 @@ import { AddProject } from "@/components/dashboard/projects/add"; -import type { Auth } from "@/server/api/services/auth"; -import type { User } from "@/server/api/services/user"; import { api } from "@/utils/api"; +import type { Auth, IS_CLOUD, User } from "@dokploy/server"; +import { is } from "drizzle-orm"; import { useRouter } from "next/router"; import { useEffect, useMemo, useState } from "react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs"; @@ -11,6 +11,7 @@ interface TabInfo { tabLabel?: string; description: string; index: string; + type: TabState; isShow?: ({ rol, user }: { rol?: Auth["rol"]; user?: User }) => boolean; } @@ -22,47 +23,65 @@ export type TabState = | "requests" | "docker"; -const tabMap: Record = { - projects: { - label: "Projects", - description: "Manage your projects", - index: "/dashboard/projects", - }, - monitoring: { - label: "Monitoring", - description: "Monitor your projects", - index: "/dashboard/monitoring", - }, - traefik: { - label: "Traefik", - tabLabel: "Traefik File System", - description: "Manage your traefik", - index: "/dashboard/traefik", - isShow: ({ rol, user }) => { - return Boolean(rol === "admin" || user?.canAccessToTraefikFiles); +const getTabMaps = (isCloud: boolean) => { + const elements: TabInfo[] = [ + { + label: "Projects", + description: "Manage your projects", + index: "/dashboard/projects", + type: "projects", }, - }, - docker: { - label: "Docker", - description: "Manage your docker", - index: "/dashboard/docker", - isShow: ({ rol, user }) => { - return Boolean(rol === "admin" || user?.canAccessToDocker); - }, - }, - requests: { - label: "Requests", - description: "Manage your requests", - index: "/dashboard/requests", - isShow: ({ rol, user }) => { - return Boolean(rol === "admin" || user?.canAccessToDocker); - }, - }, - settings: { + ]; + + if (!isCloud) { + elements.push( + { + label: "Monitoring", + description: "Monitor your projects", + index: "/dashboard/monitoring", + type: "monitoring", + }, + { + label: "Traefik", + tabLabel: "Traefik File System", + description: "Manage your traefik", + index: "/dashboard/traefik", + isShow: ({ rol, user }) => { + return Boolean(rol === "admin" || user?.canAccessToTraefikFiles); + }, + type: "traefik", + }, + { + label: "Docker", + description: "Manage your docker", + index: "/dashboard/docker", + isShow: ({ rol, user }) => { + return Boolean(rol === "admin" || user?.canAccessToDocker); + }, + type: "docker", + }, + { + label: "Requests", + description: "Manage your requests", + index: "/dashboard/requests", + isShow: ({ rol, user }) => { + return Boolean(rol === "admin" || user?.canAccessToDocker); + }, + type: "requests", + }, + ); + } + + elements.push({ label: "Settings", description: "Manage your settings", - index: "/dashboard/settings/server", - }, + type: "settings", + index: isCloud + ? "/dashboard/settings/profile" + : "/dashboard/settings/server", + }); + + return elements; }; interface Props { @@ -72,9 +91,10 @@ interface Props { export const NavigationTabs = ({ tab, children }: Props) => { const router = useRouter(); - const { data } = api.auth.get.useQuery(); const [activeTab, setActiveTab] = useState(tab); + const { data: isCloud } = api.settings.isCloud.useQuery(); + const tabMap = useMemo(() => getTabMaps(isCloud ?? false), [isCloud]); const { data: user } = api.user.byAuthId.useQuery( { authId: data?.id || "", @@ -89,7 +109,7 @@ export const NavigationTabs = ({ tab, children }: Props) => { }, [tab]); const activeTabInfo = useMemo(() => { - return tabMap[activeTab]; + return tabMap.find((tab) => tab.type === activeTab); }, [activeTab]); return ( @@ -97,10 +117,10 @@ export const NavigationTabs = ({ tab, children }: Props) => {

    - {activeTabInfo.label} + {activeTabInfo?.label}

    - {activeTabInfo.description} + {activeTabInfo?.description}

    {tab === "projects" && @@ -112,27 +132,26 @@ export const NavigationTabs = ({ tab, children }: Props) => { className="w-full" onValueChange={async (e) => { setActiveTab(e as TabState); - router.push(tabMap[e as TabState].index); + const tab = tabMap.find((tab) => tab.type === e); + router.push(tab?.index || ""); }} > - {/* className="grid w-fit grid-cols-4 bg-transparent" */}
    - {Object.keys(tabMap).map((key) => { - const tab = tabMap[key as TabState]; - if (tab.isShow && !tab.isShow?.({ rol: data?.rol, user })) { + {tabMap.map((tab, index) => { + if (tab?.isShow && !tab?.isShow?.({ rol: data?.rol, user })) { return null; } return ( - {tab.tabLabel || tab.label} + {tab?.tabLabel || tab?.label} - {key === activeTab && ( + {tab.type === activeTab && (
    diff --git a/apps/dokploy/components/layouts/settings-layout.tsx b/apps/dokploy/components/layouts/settings-layout.tsx index 3dadbe97..a44278d1 100644 --- a/apps/dokploy/components/layouts/settings-layout.tsx +++ b/apps/dokploy/components/layouts/settings-layout.tsx @@ -4,6 +4,7 @@ interface Props { export const SettingsLayout = ({ children }: Props) => { const { data } = api.auth.get.useQuery(); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: user } = api.user.byAuthId.useQuery( { authId: data?.id || "", @@ -17,7 +18,7 @@ export const SettingsLayout = ({ children }: Props) => {