mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
@@ -11,6 +11,7 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
cp apps/dokploy/.env.production.example .env.production
|
cp apps/dokploy/.env.production.example .env.production
|
||||||
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
|
cp apps/dokploy/.env.production.example apps/dokploy/.env.production
|
||||||
|
|
||||||
- run:
|
- run:
|
||||||
name: Build and push AMD64 image
|
name: Build and push AMD64 image
|
||||||
command: |
|
command: |
|
||||||
|
|||||||
71
.github/workflows/deploy.yml
vendored
71
.github/workflows/deploy.yml
vendored
@@ -48,3 +48,74 @@ jobs:
|
|||||||
push: true
|
push: true
|
||||||
tags: dokploy/website:latest
|
tags: dokploy/website:latest
|
||||||
platforms: linux/amd64
|
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
|
||||||
3
.github/workflows/pull-request.yml
vendored
3
.github/workflows/pull-request.yml
vendored
@@ -18,6 +18,7 @@ jobs:
|
|||||||
node-version: 18.18.0
|
node-version: 18.18.0
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm run server:build
|
||||||
- run: pnpm biome ci
|
- run: pnpm biome ci
|
||||||
- run: pnpm typecheck
|
- run: pnpm typecheck
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ jobs:
|
|||||||
node-version: 18.18.0
|
node-version: 18.18.0
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm run server:build
|
||||||
- run: pnpm build
|
- run: pnpm build
|
||||||
|
|
||||||
parallel-tests:
|
parallel-tests:
|
||||||
@@ -44,4 +46,5 @@ jobs:
|
|||||||
node-version: 18.18.0
|
node-version: 18.18.0
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
|
- run: pnpm run server:build
|
||||||
- run: pnpm test
|
- run: pnpm test
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
|
|||||||
# Deploy only the dokploy app
|
# Deploy only the dokploy app
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
RUN pnpm --filter=@dokploy/server build
|
||||||
RUN pnpm --filter=./apps/dokploy run build
|
RUN pnpm --filter=./apps/dokploy run build
|
||||||
|
|
||||||
RUN pnpm --filter=./apps/dokploy --prod deploy /prod/dokploy
|
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/.next /prod/dokploy/.next
|
||||||
|
|||||||
53
Dockerfile.cloud
Normal file
53
Dockerfile.cloud
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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 .env ./.env
|
||||||
|
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" ]
|
||||||
36
Dockerfile.schedule
Normal file
36
Dockerfile.schedule
Normal file
@@ -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
|
||||||
36
Dockerfile.server
Normal file
36
Dockerfile.server
Normal file
@@ -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
|
||||||
@@ -1,15 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "my-app",
|
"name": "@dokploy/api",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"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": {
|
"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/node-server": "^1.12.1",
|
||||||
"hono": "^4.5.8",
|
"hono": "^4.5.8",
|
||||||
"dotenv": "^16.3.1"
|
"dotenv": "^16.3.1",
|
||||||
|
"redis": "4.7.0",
|
||||||
|
"@nerimity/mimiqueue": "1.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"typescript": "^5.4.2",
|
||||||
|
"@types/react": "^18.2.37",
|
||||||
|
"@types/react-dom": "^18.2.15",
|
||||||
"@types/node": "^20.11.17",
|
"@types/node": "^20.11.17",
|
||||||
"tsx": "^4.7.1"
|
"tsx": "^4.7.1"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@9.5.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,61 @@
|
|||||||
import { serve } from "@hono/node-server";
|
import { serve } from "@hono/node-server";
|
||||||
import { config } from "dotenv";
|
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { cors } from "hono/cors";
|
import "dotenv/config";
|
||||||
import { validateLemonSqueezyLicense } from "./utils";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import { Queue } from "@nerimity/mimiqueue";
|
||||||
config();
|
import { createClient } from "redis";
|
||||||
|
import { logger } from "./logger";
|
||||||
|
import { type DeployJob, deployJobSchema } from "./schema";
|
||||||
|
import { deploy } from "./utils";
|
||||||
|
|
||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
|
const redisClient = createClient({
|
||||||
app.use(
|
url: process.env.REDIS_URL,
|
||||||
"/*",
|
|
||||||
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!");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/v1/validate-license", async (c) => {
|
app.use(async (c, next) => {
|
||||||
const { licenseKey } = await c.req.json();
|
if (c.req.path === "/health") {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
const authHeader = c.req.header("X-API-Key");
|
||||||
|
|
||||||
if (!licenseKey) {
|
if (process.env.API_KEY !== authHeader) {
|
||||||
return c.json({ error: "License key is required" }, 400);
|
return c.json({ message: "Invalid API Key" }, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return next();
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const port = 4000;
|
app.post("/deploy", zValidator("json", deployJobSchema), (c) => {
|
||||||
console.log(`Server is running on port ${port}`);
|
const data = c.req.valid("json");
|
||||||
|
const res = queue.add(data, { groupName: data.serverId });
|
||||||
serve({
|
return c.json(
|
||||||
fetch: app.fetch,
|
{
|
||||||
port,
|
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 });
|
||||||
|
|||||||
10
apps/api/src/logger.ts
Normal file
10
apps/api/src/logger.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import pino from "pino";
|
||||||
|
|
||||||
|
export const logger = pino({
|
||||||
|
transport: {
|
||||||
|
target: "pino-pretty",
|
||||||
|
options: {
|
||||||
|
colorize: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
24
apps/api/src/schema.ts
Normal file
24
apps/api/src/schema.ts
Normal file
@@ -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<typeof deployJobSchema>;
|
||||||
@@ -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";
|
import type { LemonSqueezyLicenseResponse } from "./types";
|
||||||
|
|
||||||
export const validateLemonSqueezyLicense = async (
|
// const LEMON_SQUEEZY_API_KEY = process.env.LEMON_SQUEEZY_API_KEY;
|
||||||
licenseKey: string,
|
// const LEMON_SQUEEZY_STORE_ID = process.env.LEMON_SQUEEZY_STORE_ID;
|
||||||
): Promise<LemonSqueezyLicenseResponse> => {
|
// export const validateLemonSqueezyLicense = async (
|
||||||
try {
|
// licenseKey: string,
|
||||||
const response = await fetch(
|
// ): Promise<LemonSqueezyLicenseResponse> => {
|
||||||
"https://api.lemonsqueezy.com/v1/licenses/validate",
|
// try {
|
||||||
{
|
// const response = await fetch(
|
||||||
method: "POST",
|
// "https://api.lemonsqueezy.com/v1/licenses/validate",
|
||||||
headers: {
|
// {
|
||||||
"Content-Type": "application/json",
|
// method: "POST",
|
||||||
"x-api-key": LEMON_SQUEEZY_API_KEY as string,
|
// headers: {
|
||||||
},
|
// "Content-Type": "application/json",
|
||||||
body: JSON.stringify({
|
// "x-api-key": LEMON_SQUEEZY_API_KEY as string,
|
||||||
license_key: licenseKey,
|
// },
|
||||||
store_id: LEMON_SQUEEZY_STORE_ID 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) {
|
} catch (error) {
|
||||||
console.error("Error validating license:", error);
|
console.log(error);
|
||||||
return { valid: false, error: "Error validating license" };
|
if (job.applicationType === "application") {
|
||||||
|
await updateApplicationStatus(job.applicationId, "error");
|
||||||
|
} else if (job.applicationType === "compose") {
|
||||||
|
await updateCompose(job.composeId, {
|
||||||
|
composeStatus: "error",
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Node",
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"types": ["node"],
|
"outDir": "dist",
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "hono/jsx"
|
"jsxImportSource": "hono/jsx"
|
||||||
}
|
},
|
||||||
|
"exclude": ["node_modules", "dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,15 +21,10 @@
|
|||||||
"react-ga4": "^2.1.0"
|
"react-ga4": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"tsx": "4.15.7",
|
|
||||||
"@biomejs/biome": "1.8.1",
|
|
||||||
"@types/mdx": "^2.0.13",
|
"@types/mdx": "^2.0.13",
|
||||||
"@types/node": "^20.14.2",
|
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
|
||||||
"autoprefixer": "^10.4.19",
|
|
||||||
"postcss": "^8.4.38",
|
"postcss": "^8.4.38",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5.4.5"
|
"typescript": "^5.4.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
DATABASE_URL="postgres://dokploy:amukds4wi9001583845717ad2@localhost:5432/dokploy"
|
DATABASE_URL="postgres://dokploy:amukds4wi9001583845717ad2@localhost:5432/dokploy"
|
||||||
PORT=3000
|
PORT=3000
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
IS_CLOUD="true"
|
||||||
|
SERVER_URL="http://localhost:4000"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { addSuffixToAllProperties } from "@/server/utils/docker/compose";
|
import { addSuffixToAllProperties } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToConfigsRoot } from "@/server/utils/docker/compose/configs";
|
import { addSuffixToConfigsRoot } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToConfigsInServices } from "@/server/utils/docker/compose/configs";
|
import { addSuffixToConfigsInServices } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import {
|
import { addSuffixToAllConfigs, addSuffixToConfigsRoot } from "@dokploy/server";
|
||||||
addSuffixToAllConfigs,
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
addSuffixToConfigsRoot,
|
|
||||||
} from "@/server/utils/docker/compose/configs";
|
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Domain } from "@/server/api/services/domain";
|
import type { Domain } from "@dokploy/server";
|
||||||
import { createDomainLabels } from "@/server/utils/docker/domain";
|
import { createDomainLabels } from "@dokploy/server";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("createDomainLabels", () => {
|
describe("createDomainLabels", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { addDokployNetworkToRoot } from "@/server/utils/docker/domain";
|
import { addDokployNetworkToRoot } from "@dokploy/server";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("addDokployNetworkToRoot", () => {
|
describe("addDokployNetworkToRoot", () => {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { addDokployNetworkToService } from "@/server/utils/docker/domain";
|
import { addDokployNetworkToService } from "@dokploy/server";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("addDokployNetworkToService", () => {
|
describe("addDokployNetworkToService", () => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network";
|
import { addSuffixToNetworksRoot } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNetworks } from "@/server/utils/docker/compose/network";
|
import { addSuffixToServiceNetworks } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import {
|
import {
|
||||||
addSuffixToAllNetworks,
|
addSuffixToAllNetworks,
|
||||||
addSuffixToServiceNetworks,
|
addSuffixToServiceNetworks,
|
||||||
} from "@/server/utils/docker/compose/network";
|
} from "@dokploy/server";
|
||||||
import { addSuffixToNetworksRoot } from "@/server/utils/docker/compose/network";
|
import { addSuffixToNetworksRoot } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToSecretsRoot } from "@/server/utils/docker/compose/secrets";
|
import { addSuffixToSecretsRoot } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { dump, load } from "js-yaml";
|
import { dump, load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToSecretsInServices } from "@/server/utils/docker/compose/secrets";
|
import { addSuffixToSecretsInServices } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { addSuffixToAllSecrets } from "@/server/utils/docker/compose/secrets";
|
import { addSuffixToAllSecrets } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
addSuffixToAllServiceNames,
|
addSuffixToAllServiceNames,
|
||||||
addSuffixToServiceNames,
|
addSuffixToServiceNames,
|
||||||
} from "@/server/utils/docker/compose/service";
|
} from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToServiceNames } from "@/server/utils/docker/compose/service";
|
import { addSuffixToServiceNames } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import {
|
import { addSuffixToAllVolumes, addSuffixToVolumesRoot } from "@dokploy/server";
|
||||||
addSuffixToAllVolumes,
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
addSuffixToVolumesRoot,
|
|
||||||
} from "@/server/utils/docker/compose/volume";
|
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToVolumesRoot } from "@/server/utils/docker/compose/volume";
|
import { addSuffixToVolumesRoot } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import { addSuffixToVolumesInServices } from "@/server/utils/docker/compose/volume";
|
import { addSuffixToVolumesInServices } from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { generateRandomHash } from "@/server/utils/docker/compose";
|
import { generateRandomHash } from "@dokploy/server";
|
||||||
import {
|
import {
|
||||||
addSuffixToAllVolumes,
|
addSuffixToAllVolumes,
|
||||||
addSuffixToVolumesInServices,
|
addSuffixToVolumesInServices,
|
||||||
} from "@/server/utils/docker/compose/volume";
|
} from "@dokploy/server";
|
||||||
import type { ComposeSpecification } from "@/server/utils/docker/types";
|
import type { ComposeSpecification } from "@dokploy/server";
|
||||||
import { load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { paths } from "@/server/constants";
|
import { paths } from "@dokploy/server/dist/constants";
|
||||||
const { APPLICATIONS_PATH } = paths();
|
const { APPLICATIONS_PATH } = paths();
|
||||||
import type { ApplicationNested } from "@/server/utils/builders";
|
import type { ApplicationNested } from "@dokploy/server";
|
||||||
import { unzipDrop } from "@/server/utils/builders/drop";
|
import { unzipDrop } from "@dokploy/server";
|
||||||
import AdmZip from "adm-zip";
|
import AdmZip from "adm-zip";
|
||||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
@@ -81,14 +81,17 @@ const baseApp: ApplicationNested = {
|
|||||||
username: null,
|
username: null,
|
||||||
dockerContextPath: 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", () => {
|
describe("unzipDrop using real zip files", () => {
|
||||||
// const { APPLICATIONS_PATH } = paths();
|
// const { APPLICATIONS_PATH } = paths();
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@@ -102,15 +105,19 @@ describe("unzipDrop using real zip files", () => {
|
|||||||
it("should correctly extract a zip with a single root folder", async () => {
|
it("should correctly extract a zip with a single root folder", async () => {
|
||||||
baseApp.appName = "single-file";
|
baseApp.appName = "single-file";
|
||||||
// const appName = "single-file";
|
// const appName = "single-file";
|
||||||
const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code");
|
try {
|
||||||
const zip = new AdmZip("./__test__/drop/zips/single-file.zip");
|
const outputPath = path.join(APPLICATIONS_PATH, baseApp.appName, "code");
|
||||||
|
const zip = new AdmZip("./__test__/drop/zips/single-file.zip");
|
||||||
const zipBuffer = zip.toBuffer();
|
console.log(`Output Path: ${outputPath}`);
|
||||||
const file = new File([zipBuffer], "single.zip");
|
const zipBuffer = zip.toBuffer();
|
||||||
await unzipDrop(file, baseApp);
|
const file = new File([zipBuffer], "single.zip");
|
||||||
|
await unzipDrop(file, baseApp);
|
||||||
const files = await fs.readdir(outputPath, { withFileTypes: true });
|
const files = await fs.readdir(outputPath, { withFileTypes: true });
|
||||||
expect(files.some((f) => f.name === "test.txt")).toBe(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 () => {
|
it("should correctly extract a zip with a single root folder and a subfolder", async () => {
|
||||||
|
|||||||
@@ -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";
|
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"}`;
|
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"}`;
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ vi.mock("node:fs", () => ({
|
|||||||
default: fs,
|
default: fs,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import type { Admin } from "@/server/api/services/admin";
|
import type { Admin, FileConfig } from "@dokploy/server";
|
||||||
import { createDefaultServerTraefikConfig } from "@/server/setup/traefik-setup";
|
import {
|
||||||
import { loadOrCreateConfig } from "@/server/utils/traefik/application";
|
createDefaultServerTraefikConfig,
|
||||||
import type { FileConfig } from "@/server/utils/traefik/file-types";
|
loadOrCreateConfig,
|
||||||
import { updateServerTraefik } from "@/server/utils/traefik/web-server";
|
updateServerTraefik,
|
||||||
|
} from "@dokploy/server";
|
||||||
import { beforeEach, expect, test, vi } from "vitest";
|
import { beforeEach, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
const baseAdmin: Admin = {
|
const baseAdmin: Admin = {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Domain } from "@/server/api/services/domain";
|
import type { Domain } from "@dokploy/server";
|
||||||
import type { Redirect } from "@/server/api/services/redirect";
|
import type { Redirect } from "@dokploy/server";
|
||||||
import type { ApplicationNested } from "@/server/utils/builders";
|
import type { ApplicationNested } from "@dokploy/server";
|
||||||
import { createRouterConfig } from "@/server/utils/traefik/domain";
|
import { createRouterConfig } from "@dokploy/server";
|
||||||
import { expect, test } from "vitest";
|
import { expect, test } from "vitest";
|
||||||
|
|
||||||
const baseApp: ApplicationNested = {
|
const baseApp: ApplicationNested = {
|
||||||
|
|||||||
@@ -13,4 +13,9 @@ export default defineConfig({
|
|||||||
exclude: ["**/node_modules/**", "**/dist/**", "**/.docker/**"],
|
exclude: ["**/node_modules/**", "**/dist/**", "**/.docker/**"],
|
||||||
pool: "forks",
|
pool: "forks",
|
||||||
},
|
},
|
||||||
|
define: {
|
||||||
|
"process.env": {
|
||||||
|
NODE: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,20 +16,37 @@ interface Props {
|
|||||||
export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
|
export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
|
||||||
const [data, setData] = useState("");
|
const [data, setData] = useState("");
|
||||||
const endOfLogsRef = useRef<HTMLDivElement>(null);
|
const endOfLogsRef = useRef<HTMLDivElement>(null);
|
||||||
|
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !logPath) return;
|
if (!open || !logPath) return;
|
||||||
|
|
||||||
|
setData("");
|
||||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
|
||||||
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`;
|
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}${serverId ? `&serverId=${serverId}` : ""}`;
|
||||||
const ws = new WebSocket(wsUrl);
|
const ws = new WebSocket(wsUrl);
|
||||||
|
wsRef.current = ws; // Store WebSocket instance in ref
|
||||||
|
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
setData((currentData) => currentData + e.data);
|
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]);
|
}, [logPath, open]);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
@@ -45,7 +62,15 @@ export const ShowDeployment = ({ logPath, open, onClose, serverId }: Props) => {
|
|||||||
open={open}
|
open={open}
|
||||||
onOpenChange={(e) => {
|
onOpenChange={(e) => {
|
||||||
onClose();
|
onClose();
|
||||||
if (!e) setData("");
|
if (!e) {
|
||||||
|
setData("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wsRef.current) {
|
||||||
|
if (wsRef.current.readyState === WebSocket.OPEN) {
|
||||||
|
wsRef.current.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
|
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
|
||||||
|
|||||||
@@ -21,20 +21,38 @@ export const ShowDeploymentCompose = ({
|
|||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [data, setData] = useState("");
|
const [data, setData] = useState("");
|
||||||
const endOfLogsRef = useRef<HTMLDivElement>(null);
|
const endOfLogsRef = useRef<HTMLDivElement>(null);
|
||||||
|
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !logPath) return;
|
if (!open || !logPath) return;
|
||||||
|
|
||||||
|
setData("");
|
||||||
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
|
||||||
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`;
|
const wsUrl = `${protocol}//${window.location.host}/listen-deployment?logPath=${logPath}&serverId=${serverId}`;
|
||||||
const ws = new WebSocket(wsUrl);
|
const ws = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
wsRef.current = ws; // Store WebSocket instance in ref
|
||||||
|
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
setData((currentData) => currentData + e.data);
|
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]);
|
}, [logPath, open]);
|
||||||
|
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
@@ -50,7 +68,15 @@ export const ShowDeploymentCompose = ({
|
|||||||
open={open}
|
open={open}
|
||||||
onOpenChange={(e) => {
|
onOpenChange={(e) => {
|
||||||
onClose();
|
onClose();
|
||||||
if (!e) setData("");
|
if (!e) {
|
||||||
|
setData("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wsRef.current) {
|
||||||
|
if (wsRef.current.readyState === WebSocket.OPEN) {
|
||||||
|
wsRef.current.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
|
<DialogContent className={"sm:max-w-5xl overflow-y-auto max-h-screen"}>
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ export const ComposeFileEditor = ({ composeId }: Props) => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e);
|
|
||||||
toast.error("Error to update the compose config");
|
toast.error("Error to update the compose config");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Terminal } from "@xterm/xterm";
|
import { Terminal } from "@xterm/xterm";
|
||||||
import React, { useEffect } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import { FitAddon } from "xterm-addon-fit";
|
import { FitAddon } from "xterm-addon-fit";
|
||||||
import "@xterm/xterm/css/xterm.css";
|
import "@xterm/xterm/css/xterm.css";
|
||||||
|
|
||||||
@@ -18,12 +18,24 @@ export const DockerLogsId: React.FC<Props> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [term, setTerm] = React.useState<Terminal>();
|
const [term, setTerm] = React.useState<Terminal>();
|
||||||
const [lines, setLines] = React.useState<number>(40);
|
const [lines, setLines] = React.useState<number>(40);
|
||||||
|
const wsRef = useRef<WebSocket | null>(null); // Ref to hold WebSocket instance
|
||||||
|
|
||||||
const createTerminal = (): Terminal => {
|
useEffect(() => {
|
||||||
|
// if (containerId === "select-a-container") {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
const container = document.getElementById(id);
|
const container = document.getElementById(id);
|
||||||
if (container) {
|
if (container) {
|
||||||
container.innerHTML = "";
|
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({
|
const termi = new Terminal({
|
||||||
cursorBlink: true,
|
cursorBlink: true,
|
||||||
cols: 80,
|
cols: 80,
|
||||||
@@ -45,7 +57,7 @@ export const DockerLogsId: React.FC<Props> = ({
|
|||||||
|
|
||||||
const wsUrl = `${protocol}//${window.location.host}/docker-container-logs?containerId=${containerId}&tail=${lines}${serverId ? `&serverId=${serverId}` : ""}`;
|
const wsUrl = `${protocol}//${window.location.host}/docker-container-logs?containerId=${containerId}&tail=${lines}${serverId ? `&serverId=${serverId}` : ""}`;
|
||||||
const ws = new WebSocket(wsUrl);
|
const ws = new WebSocket(wsUrl);
|
||||||
|
wsRef.current = ws;
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
termi.loadAddon(fitAddon);
|
termi.loadAddon(fitAddon);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -54,6 +66,10 @@ export const DockerLogsId: React.FC<Props> = ({
|
|||||||
termi.focus();
|
termi.focus();
|
||||||
setTerm(termi);
|
setTerm(termi);
|
||||||
|
|
||||||
|
ws.onerror = (error) => {
|
||||||
|
console.error("WebSocket error: ", error);
|
||||||
|
};
|
||||||
|
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
termi.write(e.data);
|
termi.write(e.data);
|
||||||
};
|
};
|
||||||
@@ -62,12 +78,14 @@ export const DockerLogsId: React.FC<Props> = ({
|
|||||||
console.log(e.reason);
|
console.log(e.reason);
|
||||||
|
|
||||||
termi.write(`Connection closed!\nReason: ${e.reason}\n`);
|
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]);
|
}, [lines, containerId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -11,13 +11,11 @@ import {
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { useUrl } from "@/utils/hooks/use-url";
|
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export const AddGithubProvider = () => {
|
export const AddGithubProvider = () => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const url = useUrl();
|
|
||||||
const { data } = api.auth.get.useQuery();
|
const { data } = api.auth.get.useQuery();
|
||||||
const [manifest, setManifest] = useState("");
|
const [manifest, setManifest] = useState("");
|
||||||
const [isOrganization, setIsOrganization] = useState(false);
|
const [isOrganization, setIsOrganization] = useState(false);
|
||||||
|
|||||||
@@ -145,7 +145,6 @@ export const ProfileForm = () => {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
onValueChange={(e) => {
|
onValueChange={(e) => {
|
||||||
console.log(e);
|
|
||||||
field.onChange(e);
|
field.onChange(e);
|
||||||
}}
|
}}
|
||||||
defaultValue={field.value}
|
defaultValue={field.value}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||||
import { Separator } from "@/components/ui/separator";
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import copy from "copy-to-clipboard";
|
import copy from "copy-to-clipboard";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AddProject } from "@/components/dashboard/projects/add";
|
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 { api } from "@/utils/api";
|
||||||
|
import type { Auth, IS_CLOUD, User } from "@dokploy/server";
|
||||||
|
import { is } from "drizzle-orm";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||||
@@ -11,6 +11,7 @@ interface TabInfo {
|
|||||||
tabLabel?: string;
|
tabLabel?: string;
|
||||||
description: string;
|
description: string;
|
||||||
index: string;
|
index: string;
|
||||||
|
type: TabState;
|
||||||
isShow?: ({ rol, user }: { rol?: Auth["rol"]; user?: User }) => boolean;
|
isShow?: ({ rol, user }: { rol?: Auth["rol"]; user?: User }) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,47 +23,65 @@ export type TabState =
|
|||||||
| "requests"
|
| "requests"
|
||||||
| "docker";
|
| "docker";
|
||||||
|
|
||||||
const tabMap: Record<TabState, TabInfo> = {
|
const getTabMaps = (isCloud: boolean) => {
|
||||||
projects: {
|
const elements: TabInfo[] = [
|
||||||
label: "Projects",
|
{
|
||||||
description: "Manage your projects",
|
label: "Projects",
|
||||||
index: "/dashboard/projects",
|
description: "Manage your projects",
|
||||||
},
|
index: "/dashboard/projects",
|
||||||
monitoring: {
|
type: "projects",
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
},
|
];
|
||||||
docker: {
|
|
||||||
label: "Docker",
|
if (!isCloud) {
|
||||||
description: "Manage your docker",
|
elements.push(
|
||||||
index: "/dashboard/docker",
|
{
|
||||||
isShow: ({ rol, user }) => {
|
label: "Monitoring",
|
||||||
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
description: "Monitor your projects",
|
||||||
},
|
index: "/dashboard/monitoring",
|
||||||
},
|
type: "monitoring",
|
||||||
requests: {
|
},
|
||||||
label: "Requests",
|
{
|
||||||
description: "Manage your requests",
|
label: "Traefik",
|
||||||
index: "/dashboard/requests",
|
tabLabel: "Traefik File System",
|
||||||
isShow: ({ rol, user }) => {
|
description: "Manage your traefik",
|
||||||
return Boolean(rol === "admin" || user?.canAccessToDocker);
|
index: "/dashboard/traefik",
|
||||||
},
|
isShow: ({ rol, user }) => {
|
||||||
},
|
return Boolean(rol === "admin" || user?.canAccessToTraefikFiles);
|
||||||
settings: {
|
},
|
||||||
|
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",
|
label: "Settings",
|
||||||
description: "Manage your settings",
|
description: "Manage your settings",
|
||||||
index: "/dashboard/settings/server",
|
type: "settings",
|
||||||
},
|
index: isCloud
|
||||||
|
? "/dashboard/settings/profile"
|
||||||
|
: "/dashboard/settings/server",
|
||||||
|
});
|
||||||
|
|
||||||
|
return elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -72,9 +91,10 @@ interface Props {
|
|||||||
|
|
||||||
export const NavigationTabs = ({ tab, children }: Props) => {
|
export const NavigationTabs = ({ tab, children }: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { data } = api.auth.get.useQuery();
|
const { data } = api.auth.get.useQuery();
|
||||||
const [activeTab, setActiveTab] = useState<TabState>(tab);
|
const [activeTab, setActiveTab] = useState<TabState>(tab);
|
||||||
|
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||||
|
const tabMap = useMemo(() => getTabMaps(isCloud ?? false), [isCloud]);
|
||||||
const { data: user } = api.user.byAuthId.useQuery(
|
const { data: user } = api.user.byAuthId.useQuery(
|
||||||
{
|
{
|
||||||
authId: data?.id || "",
|
authId: data?.id || "",
|
||||||
@@ -89,7 +109,7 @@ export const NavigationTabs = ({ tab, children }: Props) => {
|
|||||||
}, [tab]);
|
}, [tab]);
|
||||||
|
|
||||||
const activeTabInfo = useMemo(() => {
|
const activeTabInfo = useMemo(() => {
|
||||||
return tabMap[activeTab];
|
return tabMap.find((tab) => tab.type === activeTab);
|
||||||
}, [activeTab]);
|
}, [activeTab]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -97,10 +117,10 @@ export const NavigationTabs = ({ tab, children }: Props) => {
|
|||||||
<header className="mb-6 flex w-full items-center gap-2 justify-between flex-wrap">
|
<header className="mb-6 flex w-full items-center gap-2 justify-between flex-wrap">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h1 className="text-xl font-bold lg:text-3xl">
|
<h1 className="text-xl font-bold lg:text-3xl">
|
||||||
{activeTabInfo.label}
|
{activeTabInfo?.label}
|
||||||
</h1>
|
</h1>
|
||||||
<p className="lg:text-medium text-muted-foreground">
|
<p className="lg:text-medium text-muted-foreground">
|
||||||
{activeTabInfo.description}
|
{activeTabInfo?.description}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{tab === "projects" &&
|
{tab === "projects" &&
|
||||||
@@ -112,27 +132,26 @@ export const NavigationTabs = ({ tab, children }: Props) => {
|
|||||||
className="w-full"
|
className="w-full"
|
||||||
onValueChange={async (e) => {
|
onValueChange={async (e) => {
|
||||||
setActiveTab(e as TabState);
|
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" */}
|
|
||||||
<div className="flex flex-row items-center justify-between w-full gap-4 max-sm:overflow-x-auto border-b border-b-divider pb-1">
|
<div className="flex flex-row items-center justify-between w-full gap-4 max-sm:overflow-x-auto border-b border-b-divider pb-1">
|
||||||
<TabsList className="bg-transparent relative px-0">
|
<TabsList className="bg-transparent relative px-0">
|
||||||
{Object.keys(tabMap).map((key) => {
|
{tabMap.map((tab, index) => {
|
||||||
const tab = tabMap[key as TabState];
|
if (tab?.isShow && !tab?.isShow?.({ rol: data?.rol, user })) {
|
||||||
if (tab.isShow && !tab.isShow?.({ rol: data?.rol, user })) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
key={key}
|
key={tab.type}
|
||||||
value={key}
|
value={tab.type}
|
||||||
className="relative py-2.5 md:px-5 data-[state=active]:shadow-none data-[state=active]:bg-transparent rounded-md hover:bg-zinc-100 hover:dark:bg-zinc-800 data-[state=active]:hover:bg-zinc-100 data-[state=active]:hover:dark:bg-zinc-800"
|
className="relative py-2.5 md:px-5 data-[state=active]:shadow-none data-[state=active]:bg-transparent rounded-md hover:bg-zinc-100 hover:dark:bg-zinc-800 data-[state=active]:hover:bg-zinc-100 data-[state=active]:hover:dark:bg-zinc-800"
|
||||||
>
|
>
|
||||||
<span className="relative z-[1] w-full">
|
<span className="relative z-[1] w-full">
|
||||||
{tab.tabLabel || tab.label}
|
{tab?.tabLabel || tab?.label}
|
||||||
</span>
|
</span>
|
||||||
{key === activeTab && (
|
{tab.type === activeTab && (
|
||||||
<div className="absolute -bottom-[5.5px] w-full">
|
<div className="absolute -bottom-[5.5px] w-full">
|
||||||
<div className="h-0.5 bg-foreground rounded-t-md" />
|
<div className="h-0.5 bg-foreground rounded-t-md" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ interface Props {
|
|||||||
|
|
||||||
export const SettingsLayout = ({ children }: Props) => {
|
export const SettingsLayout = ({ children }: Props) => {
|
||||||
const { data } = api.auth.get.useQuery();
|
const { data } = api.auth.get.useQuery();
|
||||||
|
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||||
const { data: user } = api.user.byAuthId.useQuery(
|
const { data: user } = api.user.byAuthId.useQuery(
|
||||||
{
|
{
|
||||||
authId: data?.id || "",
|
authId: data?.id || "",
|
||||||
@@ -17,7 +18,7 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
<div className="md:max-w-[18rem] w-full">
|
<div className="md:max-w-[18rem] w-full">
|
||||||
<Nav
|
<Nav
|
||||||
links={[
|
links={[
|
||||||
...(data?.rol === "admin"
|
...(data?.rol === "admin" && !isCloud
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
title: "Server",
|
title: "Server",
|
||||||
@@ -47,12 +48,16 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
icon: Database,
|
icon: Database,
|
||||||
href: "/dashboard/settings/destinations",
|
href: "/dashboard/settings/destinations",
|
||||||
},
|
},
|
||||||
{
|
...(!isCloud
|
||||||
title: "Certificates",
|
? [
|
||||||
label: "",
|
{
|
||||||
icon: ShieldCheck,
|
title: "Certificates",
|
||||||
href: "/dashboard/settings/certificates",
|
label: "",
|
||||||
},
|
icon: ShieldCheck,
|
||||||
|
href: "/dashboard/settings/certificates",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
title: "SSH Keys",
|
title: "SSH Keys",
|
||||||
label: "",
|
label: "",
|
||||||
@@ -60,7 +65,7 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
href: "/dashboard/settings/ssh-keys",
|
href: "/dashboard/settings/ssh-keys",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Git ",
|
title: "Git",
|
||||||
label: "",
|
label: "",
|
||||||
icon: GitBranch,
|
icon: GitBranch,
|
||||||
href: "/dashboard/settings/git-providers",
|
href: "/dashboard/settings/git-providers",
|
||||||
@@ -71,12 +76,23 @@ export const SettingsLayout = ({ children }: Props) => {
|
|||||||
icon: Users,
|
icon: Users,
|
||||||
href: "/dashboard/settings/users",
|
href: "/dashboard/settings/users",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: "Cluster",
|
title: "Registry",
|
||||||
label: "",
|
label: "",
|
||||||
icon: BoxesIcon,
|
icon: ListMusic,
|
||||||
href: "/dashboard/settings/cluster",
|
href: "/dashboard/settings/registry",
|
||||||
},
|
},
|
||||||
|
...(!isCloud
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: "Cluster",
|
||||||
|
label: "",
|
||||||
|
icon: BoxesIcon,
|
||||||
|
href: "/dashboard/settings/cluster",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
title: "Notifications",
|
title: "Notifications",
|
||||||
label: "",
|
label: "",
|
||||||
@@ -128,6 +144,7 @@ import {
|
|||||||
GitBranch,
|
GitBranch,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
KeyRound,
|
KeyRound,
|
||||||
|
ListMusic,
|
||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
Route,
|
Route,
|
||||||
Server,
|
Server,
|
||||||
|
|||||||
25
apps/dokploy/drizzle/0039_many_tiger_shark.sql
Normal file
25
apps/dokploy/drizzle/0039_many_tiger_shark.sql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_authId_auth_id_fk";
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "notification" ADD COLUMN "adminId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "ssh-key" ADD COLUMN "privateKey" text DEFAULT '' NOT NULL;--> statement-breakpoint
|
||||||
|
ALTER TABLE "ssh-key" ADD COLUMN "adminId" text;--> statement-breakpoint
|
||||||
|
ALTER TABLE "git_provider" ADD COLUMN "adminId" text;--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "notification" ADD CONSTRAINT "notification_adminId_admin_adminId_fk" FOREIGN KEY ("adminId") REFERENCES "public"."admin"("adminId") ON DELETE cascade ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_adminId_admin_adminId_fk" FOREIGN KEY ("adminId") REFERENCES "public"."admin"("adminId") ON DELETE cascade ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_adminId_admin_adminId_fk" FOREIGN KEY ("adminId") REFERENCES "public"."admin"("adminId") ON DELETE cascade ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE "git_provider" DROP COLUMN IF EXISTS "authId";
|
||||||
3870
apps/dokploy/drizzle/meta/0039_snapshot.json
Normal file
3870
apps/dokploy/drizzle/meta/0039_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -274,6 +274,13 @@
|
|||||||
"when": 1727942090102,
|
"when": 1727942090102,
|
||||||
"tag": "0038_rapid_landau",
|
"tag": "0038_rapid_landau",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 39,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1728021127765,
|
||||||
|
"tag": "0039_many_tiger_shark",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,7 @@ const nextConfig = {
|
|||||||
typescript: {
|
typescript: {
|
||||||
ignoreBuildErrors: true,
|
ignoreBuildErrors: true,
|
||||||
},
|
},
|
||||||
|
transpilePackages: ["@dokploy/server"],
|
||||||
webpack: (config) => {
|
webpack: (config) => {
|
||||||
config.plugins.push(
|
config.plugins.push(
|
||||||
new CopyWebpackPlugin({
|
new CopyWebpackPlugin({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build-server && npm run build-next",
|
"build": "npm run build-server && npm run build-next",
|
||||||
"start": "node dist/server.mjs",
|
"start": "node -r dotenv/config dist/server.mjs",
|
||||||
"build-server": "tsx esbuild.config.ts",
|
"build-server": "tsx esbuild.config.ts",
|
||||||
"build-next": "next build",
|
"build-next": "next build",
|
||||||
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
|
"setup": "tsx -r dotenv/config setup.ts && sleep 5 && pnpm run migration:run",
|
||||||
@@ -34,17 +34,14 @@
|
|||||||
"test": "vitest --config __test__/vitest.config.ts"
|
"test": "vitest --config __test__/vitest.config.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rotating-file-stream": "3.2.3",
|
"@dokploy/server": "workspace:*",
|
||||||
"@codemirror/lang-json": "^6.0.1",
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
"@codemirror/lang-yaml": "^6.1.1",
|
"@codemirror/lang-yaml": "^6.1.1",
|
||||||
"@codemirror/language": "^6.10.1",
|
"@codemirror/language": "^6.10.1",
|
||||||
"@codemirror/legacy-modes": "6.4.0",
|
"@codemirror/legacy-modes": "6.4.0",
|
||||||
"@codemirror/view": "6.29.0",
|
"@codemirror/view": "6.29.0",
|
||||||
"@dokploy/trpc-openapi": "0.0.4",
|
"@dokploy/trpc-openapi": "0.0.4",
|
||||||
"@faker-js/faker": "^8.4.1",
|
|
||||||
"@hookform/resolvers": "^3.3.4",
|
"@hookform/resolvers": "^3.3.4",
|
||||||
"@lucia-auth/adapter-drizzle": "1.0.7",
|
|
||||||
"@octokit/auth-app": "^6.0.4",
|
|
||||||
"@octokit/webhooks": "^13.2.7",
|
"@octokit/webhooks": "^13.2.7",
|
||||||
"@radix-ui/react-accordion": "1.1.2",
|
"@radix-ui/react-accordion": "1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
@@ -64,7 +61,6 @@
|
|||||||
"@radix-ui/react-tabs": "^1.0.4",
|
"@radix-ui/react-tabs": "^1.0.4",
|
||||||
"@radix-ui/react-toggle": "^1.0.3",
|
"@radix-ui/react-toggle": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.0.7",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@react-email/components": "^0.0.21",
|
|
||||||
"@tanstack/react-query": "^4.36.1",
|
"@tanstack/react-query": "^4.36.1",
|
||||||
"@tanstack/react-table": "^8.16.0",
|
"@tanstack/react-table": "^8.16.0",
|
||||||
"@trpc/client": "^10.43.6",
|
"@trpc/client": "^10.43.6",
|
||||||
@@ -77,8 +73,6 @@
|
|||||||
"@xterm/xterm": "^5.4.0",
|
"@xterm/xterm": "^5.4.0",
|
||||||
"adm-zip": "^0.5.14",
|
"adm-zip": "^0.5.14",
|
||||||
"bcrypt": "5.1.1",
|
"bcrypt": "5.1.1",
|
||||||
"bl": "6.0.11",
|
|
||||||
"boxen": "^7.1.1",
|
|
||||||
"bullmq": "5.4.2",
|
"bullmq": "5.4.2",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
@@ -86,31 +80,22 @@
|
|||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
"date-fns": "3.6.0",
|
"date-fns": "3.6.0",
|
||||||
"dockerode": "4.0.2",
|
|
||||||
"dockerode-compose": "^1.4.0",
|
|
||||||
"dockerstats": "2.4.2",
|
|
||||||
"dotenv": "16.4.5",
|
"dotenv": "16.4.5",
|
||||||
"drizzle-orm": "^0.30.8",
|
"drizzle-orm": "^0.30.8",
|
||||||
"drizzle-zod": "0.5.1",
|
"drizzle-zod": "0.5.1",
|
||||||
"hi-base32": "^0.5.1",
|
|
||||||
"input-otp": "^1.2.4",
|
"input-otp": "^1.2.4",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"k6": "^0.0.0",
|
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"lucia": "^3.0.1",
|
"lucia": "^3.0.1",
|
||||||
"lucide-react": "^0.312.0",
|
"lucide-react": "^0.312.0",
|
||||||
"nanoid": "3",
|
"nanoid": "3",
|
||||||
"next": "^14.1.3",
|
"next": "^14.1.3",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"node-os-utils": "1.3.7",
|
|
||||||
"node-pty": "1.0.0",
|
"node-pty": "1.0.0",
|
||||||
"node-schedule": "2.1.1",
|
"node-schedule": "2.1.1",
|
||||||
"nodemailer": "6.9.14",
|
|
||||||
"octokit": "3.1.2",
|
"octokit": "3.1.2",
|
||||||
"otpauth": "^9.2.3",
|
|
||||||
"postgres": "3.4.4",
|
"postgres": "3.4.4",
|
||||||
"public-ip": "6.0.2",
|
"public-ip": "6.0.2",
|
||||||
"qrcode": "^1.5.3",
|
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.49.3",
|
"react-hook-form": "^7.49.3",
|
||||||
@@ -121,53 +106,35 @@
|
|||||||
"swagger-ui-react": "^5.17.14",
|
"swagger-ui-react": "^5.17.14",
|
||||||
"tailwind-merge": "^2.2.0",
|
"tailwind-merge": "^2.2.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"tar-fs": "3.0.5",
|
|
||||||
"undici": "^6.19.2",
|
"undici": "^6.19.2",
|
||||||
"use-resize-observer": "9.1.0",
|
"use-resize-observer": "9.1.0",
|
||||||
"ws": "8.16.0",
|
"ws": "8.16.0",
|
||||||
"xterm-addon-fit": "^0.8.0",
|
"xterm-addon-fit": "^0.8.0",
|
||||||
"zod": "^3.23.4",
|
"zod": "^3.23.4",
|
||||||
"zod-form-data": "^2.0.2",
|
"zod-form-data": "^2.0.2",
|
||||||
"@radix-ui/react-primitive": "2.0.0",
|
|
||||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
|
||||||
"ssh2": "1.15.0"
|
"ssh2": "1.15.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "1.8.3",
|
"autoprefixer": "10.4.12",
|
||||||
"@commitlint/cli": "^19.3.0",
|
|
||||||
"@commitlint/config-conventional": "^19.2.2",
|
|
||||||
"@types/adm-zip": "^0.5.5",
|
"@types/adm-zip": "^0.5.5",
|
||||||
"@types/bcrypt": "5.0.2",
|
"@types/bcrypt": "5.0.2",
|
||||||
"@types/dockerode": "3.3.23",
|
|
||||||
"@types/js-yaml": "4.0.9",
|
"@types/js-yaml": "4.0.9",
|
||||||
"@types/lodash": "4.17.4",
|
"@types/lodash": "4.17.4",
|
||||||
"@types/node": "^18.17.0",
|
"@types/node": "^18.17.0",
|
||||||
"@types/node-os-utils": "1.3.4",
|
|
||||||
"@types/node-schedule": "2.1.6",
|
"@types/node-schedule": "2.1.6",
|
||||||
"@types/nodemailer": "^6.4.15",
|
|
||||||
"@types/qrcode": "^1.5.5",
|
|
||||||
"@types/react": "^18.2.37",
|
"@types/react": "^18.2.37",
|
||||||
"@types/react-dom": "^18.2.15",
|
"@types/react-dom": "^18.2.15",
|
||||||
"@types/swagger-ui-react": "^4.18.3",
|
"@types/swagger-ui-react": "^4.18.3",
|
||||||
"@types/tar-fs": "2.0.4",
|
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"autoprefixer": "^10.4.14",
|
|
||||||
"drizzle-kit": "^0.21.1",
|
"drizzle-kit": "^0.21.1",
|
||||||
"esbuild": "0.20.2",
|
"esbuild": "0.20.2",
|
||||||
"husky": "^9.0.11",
|
|
||||||
"lint-staged": "^15.2.7",
|
"lint-staged": "^15.2.7",
|
||||||
"localtunnel": "2.0.2",
|
|
||||||
"memfs": "^4.11.0",
|
"memfs": "^4.11.0",
|
||||||
"postcss": "^8.4.31",
|
|
||||||
"prettier": "^3.2.4",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"tsconfig-paths": "4.2.0",
|
|
||||||
"tsx": "^4.7.0",
|
"tsx": "^4.7.0",
|
||||||
"typescript": "^5.4.2",
|
"typescript": "^5.4.2",
|
||||||
"vite-tsconfig-paths": "4.3.2",
|
"vite-tsconfig-paths": "4.3.2",
|
||||||
"vitest": "^1.6.0",
|
"vitest": "^1.6.0",
|
||||||
"xterm-readline": "1.1.1",
|
|
||||||
"@types/ssh2": "1.15.1"
|
"@types/ssh2": "1.15.1"
|
||||||
},
|
},
|
||||||
"ct3aMetadata": {
|
"ct3aMetadata": {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ interface Props {
|
|||||||
|
|
||||||
export default function Custom404({ statusCode, error }: Props) {
|
export default function Custom404({ statusCode, error }: Props) {
|
||||||
const displayStatusCode = statusCode || 400;
|
const displayStatusCode = statusCode || 400;
|
||||||
console.log(error, statusCode);
|
|
||||||
return (
|
return (
|
||||||
<div className="h-screen">
|
<div className="h-screen">
|
||||||
<div className="max-w-[50rem] flex flex-col mx-auto size-full">
|
<div className="max-w-[50rem] flex flex-col mx-auto size-full">
|
||||||
@@ -92,7 +91,6 @@ export default function Custom404({ statusCode, error }: Props) {
|
|||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
Error.getInitialProps = ({ res, err, ...rest }: NextPageContext) => {
|
Error.getInitialProps = ({ res, err, ...rest }: NextPageContext) => {
|
||||||
console.log(err, rest);
|
|
||||||
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
|
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
|
||||||
return { statusCode, error: err };
|
return { statusCode, error: err };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { createTRPCContext } from "@/server/api/trpc";
|
import { createTRPCContext } from "@/server/api/trpc";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateBearerToken, validateRequest } from "@dokploy/server";
|
||||||
import { validateBearerToken } from "@/server/auth/token";
|
|
||||||
import { createOpenApiNextHandler } from "@dokploy/trpc-openapi";
|
import { createOpenApiNextHandler } from "@dokploy/trpc-openapi";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
@@ -18,6 +17,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
res.status(401).json({ message: "Unauthorized" });
|
res.status(401).json({ message: "Unauthorized" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return createOpenApiNextHandler({
|
return createOpenApiNextHandler({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { db } from "@/server/db";
|
|||||||
import { applications } from "@/server/db/schema";
|
import { applications } from "@/server/db/schema";
|
||||||
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
||||||
import { myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
|
import { deploy } from "@/server/utils/deploy";
|
||||||
|
import { IS_CLOUD } from "@dokploy/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
@@ -89,6 +91,12 @@ export default async function handler(
|
|||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
server: !!application.serverId,
|
server: !!application.serverId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && application.serverId) {
|
||||||
|
jobData.serverId = application.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { db } from "@/server/db";
|
|||||||
import { compose } from "@/server/db/schema";
|
import { compose } from "@/server/db/schema";
|
||||||
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
||||||
import { myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
|
import { deploy } from "@/server/utils/deploy";
|
||||||
|
import { IS_CLOUD } from "@dokploy/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import {
|
import {
|
||||||
@@ -65,6 +67,12 @@ export default async function handler(
|
|||||||
descriptionLog: `Hash: ${deploymentHash}`,
|
descriptionLog: `Hash: ${deploymentHash}`,
|
||||||
server: !!composeResult.serverId,
|
server: !!composeResult.serverId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && composeResult.serverId) {
|
||||||
|
jobData.serverId = composeResult.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { findAdmin } from "@/server/api/services/admin";
|
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import { applications, compose, github } from "@/server/db/schema";
|
import { applications, compose, github } from "@/server/db/schema";
|
||||||
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
import type { DeploymentJob } from "@/server/queues/deployments-queue";
|
||||||
import { myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
|
import { deploy } from "@/server/utils/deploy";
|
||||||
|
import { IS_CLOUD } from "@dokploy/server";
|
||||||
import { Webhooks } from "@octokit/webhooks";
|
import { Webhooks } from "@octokit/webhooks";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
@@ -12,17 +13,10 @@ export default async function handler(
|
|||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) {
|
) {
|
||||||
const admin = await findAdmin();
|
|
||||||
|
|
||||||
if (!admin) {
|
|
||||||
res.status(200).json({ message: "Could not find admin" });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const signature = req.headers["x-hub-signature-256"];
|
const signature = req.headers["x-hub-signature-256"];
|
||||||
const githubBody = req.body;
|
const githubBody = req.body;
|
||||||
|
|
||||||
if (!githubBody?.installation.id) {
|
if (!githubBody?.installation?.id) {
|
||||||
res.status(400).json({ message: "Github Installation not found" });
|
res.status(400).json({ message: "Github Installation not found" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -88,6 +82,12 @@ export default async function handler(
|
|||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
server: !!app.serverId,
|
server: !!app.serverId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && app.serverId) {
|
||||||
|
jobData.serverId = app.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
@@ -116,6 +116,12 @@ export default async function handler(
|
|||||||
descriptionLog: `Hash: ${deploymentHash}`,
|
descriptionLog: `Hash: ${deploymentHash}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && composeApp.serverId) {
|
||||||
|
jobData.serverId = composeApp.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { createGithub } from "@/server/api/services/github";
|
|
||||||
import { db } from "@/server/db";
|
import { db } from "@/server/db";
|
||||||
import { github } from "@/server/db/schema";
|
import { github } from "@/server/db/schema";
|
||||||
|
import {
|
||||||
|
createGithub,
|
||||||
|
findAdminByAuthId,
|
||||||
|
findAuthById,
|
||||||
|
findUserByAuthId,
|
||||||
|
} from "@dokploy/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { Octokit } from "octokit";
|
import { Octokit } from "octokit";
|
||||||
@@ -34,16 +39,29 @@ export default async function handler(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await createGithub({
|
const auth = await findAuthById(value as string);
|
||||||
name: data.name,
|
|
||||||
githubAppName: data.html_url,
|
let adminId = "";
|
||||||
githubAppId: data.id,
|
if (auth.rol === "admin") {
|
||||||
githubClientId: data.client_id,
|
const admin = await findAdminByAuthId(auth.id);
|
||||||
githubClientSecret: data.client_secret,
|
adminId = admin.adminId;
|
||||||
githubWebhookSecret: data.webhook_secret,
|
} else {
|
||||||
githubPrivateKey: data.pem,
|
const user = await findUserByAuthId(auth.id);
|
||||||
authId: value as string,
|
adminId = user.adminId;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
await createGithub(
|
||||||
|
{
|
||||||
|
name: data.name,
|
||||||
|
githubAppName: data.html_url,
|
||||||
|
githubAppId: data.id,
|
||||||
|
githubClientId: data.client_id,
|
||||||
|
githubClientSecret: data.client_secret,
|
||||||
|
githubWebhookSecret: data.webhook_secret,
|
||||||
|
githubPrivateKey: data.pem,
|
||||||
|
},
|
||||||
|
adminId,
|
||||||
|
);
|
||||||
} else if (action === "gh_setup") {
|
} else if (action === "gh_setup") {
|
||||||
await db
|
await db
|
||||||
.update(github)
|
.update(github)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { findGitlabById, updateGitlab } from "@/server/api/services/gitlab";
|
import { findGitlabById, updateGitlab } from "@dokploy/server";
|
||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default async function handler(
|
export default async function handler(
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import type { NextRequest } from "next/server";
|
|
||||||
import { renderToString } from "react-dom/server";
|
|
||||||
import Page418 from "../hola"; // Importa la página 418
|
|
||||||
|
|
||||||
export const GET = async (req: NextRequest) => {
|
|
||||||
// Renderiza el componente de la página 418 como HTML
|
|
||||||
const htmlContent = renderToString(Page418());
|
|
||||||
|
|
||||||
// Devuelve la respuesta con el código de estado HTTP 418
|
|
||||||
return new Response(htmlContent, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "text/html",
|
|
||||||
},
|
|
||||||
status: 418,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GET;
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowContainers } from "@/components/dashboard/docker/show/show-containers";
|
import { ShowContainers } from "@/components/dashboard/docker/show/show-containers";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
@@ -19,6 +19,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
@@ -28,7 +36,7 @@ export async function getServerSideProps(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const { req, res, resolvedUrl } = ctx;
|
const { req, res } = ctx;
|
||||||
|
|
||||||
const helpers = createServerSideHelpers({
|
const helpers = createServerSideHelpers({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show";
|
import { ShowMonitoring } from "@/components/dashboard/monitoring/web-server/show";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
@@ -16,6 +16,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu";
|
} from "@/components/ui/dropdown-menu";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import type { findProjectById } from "@/server/api/services/project";
|
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import type { findProjectById } from "@dokploy/server";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import { CircuitBoard, FolderInput, GlobeIcon, PlusIcon } from "lucide-react";
|
import { CircuitBoard, FolderInput, GlobeIcon, PlusIcon } from "lucide-react";
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import { GlobeIcon } from "lucide-react";
|
import { GlobeIcon } from "lucide-react";
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import { CircuitBoard } from "lucide-react";
|
import { CircuitBoard } from "lucide-react";
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type {
|
import type {
|
||||||
GetServerSidePropsContext,
|
GetServerSidePropsContext,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ShowProjects } from "@/components/dashboard/projects/show";
|
import { ShowProjects } from "@/components/dashboard/projects/show";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ShowRequests } from "@/components/dashboard/requests/show-requests";
|
import { ShowRequests } from "@/components/dashboard/requests/show-requests";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import type { ReactElement } from "react";
|
import type { ReactElement } from "react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
@@ -14,6 +14,14 @@ Requests.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { AppearanceForm } from "@/components/dashboard/settings/appearance-form";
|
import { AppearanceForm } from "@/components/dashboard/settings/appearance-form";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowCertificates } from "@/components/dashboard/settings/certificates/show-certificates";
|
import { ShowCertificates } from "@/components/dashboard/settings/certificates/show-certificates";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-no
|
|||||||
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4 w-full">
|
<div className="flex flex-col gap-4 w-full">
|
||||||
<ShowRegistry />
|
|
||||||
<ShowNodes />
|
<ShowNodes />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -27,6 +26,14 @@ Page.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user || user.rol === "user") {
|
if (!user || user.rol === "user") {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowDestinations } from "@/components/dashboard/settings/destination/show-destinations";
|
import { ShowDestinations } from "@/components/dashboard/settings/destination/show-destinations";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ShowGitProviders } from "@/components/dashboard/settings/git/show-git-p
|
|||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/destination/sh
|
|||||||
import { ShowNotifications } from "@/components/dashboard/settings/notifications/show-notifications";
|
import { ShowNotifications } from "@/components/dashboard/settings/notifications/show-notifications";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { GenerateToken } from "@/components/dashboard/settings/profile/generate-
|
|||||||
import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form";
|
import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
41
apps/dokploy/pages/dashboard/settings/registry.tsx
Normal file
41
apps/dokploy/pages/dashboard/settings/registry.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/show-registry";
|
||||||
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4 w-full">
|
||||||
|
<ShowRegistry />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
||||||
|
Page.getLayout = (page: ReactElement) => {
|
||||||
|
return (
|
||||||
|
<DashboardLayout tab={"settings"}>
|
||||||
|
<SettingsLayout>{page}</SettingsLayout>
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export async function getServerSideProps(
|
||||||
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
|
) {
|
||||||
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
|
if (!user || user.rol === "user") {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { WebDomain } from "@/components/dashboard/settings/web-domain";
|
|||||||
import { WebServer } from "@/components/dashboard/settings/web-server";
|
import { WebServer } from "@/components/dashboard/settings/web-server";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
@@ -27,6 +27,14 @@ Page.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user } = await validateRequest(ctx.req, ctx.res);
|
const { user } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowServers } from "@/components/dashboard/settings/servers/show-servers";
|
import { ShowServers } from "@/components/dashboard/settings/servers/show-servers";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show-
|
|||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowUsers } from "@/components/dashboard/settings/users/show-users";
|
import { ShowUsers } from "@/components/dashboard/settings/users/show-users";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
import { SettingsLayout } from "@/components/layouts/settings-layout";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { validateRequest } from "@dokploy/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system";
|
import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system";
|
||||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
import { IS_CLOUD, validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import React, { type ReactElement } from "react";
|
import React, { type ReactElement } from "react";
|
||||||
@@ -19,6 +19,14 @@ Dashboard.getLayout = (page: ReactElement) => {
|
|||||||
export async function getServerSideProps(
|
export async function getServerSideProps(
|
||||||
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
ctx: GetServerSidePropsContext<{ serviceId: string }>,
|
||||||
) {
|
) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
const { user, session } = await validateRequest(ctx.req, ctx.res);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export default function hola() {
|
|
||||||
return <div>hola</div>;
|
|
||||||
}
|
|
||||||
@@ -17,9 +17,8 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { isAdminPresent } from "@/server/api/services/admin";
|
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -51,16 +50,12 @@ const loginSchema = z.object({
|
|||||||
|
|
||||||
type Login = z.infer<typeof loginSchema>;
|
type Login = z.infer<typeof loginSchema>;
|
||||||
|
|
||||||
interface Props {
|
|
||||||
hasAdmin: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthResponse = {
|
type AuthResponse = {
|
||||||
is2FAEnabled: boolean;
|
is2FAEnabled: boolean;
|
||||||
authId: string;
|
authId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Home({ hasAdmin }: Props) {
|
export default function Home() {
|
||||||
const [temp, setTemp] = useState<AuthResponse>({
|
const [temp, setTemp] = useState<AuthResponse>({
|
||||||
is2FAEnabled: false,
|
is2FAEnabled: false,
|
||||||
authId: "",
|
authId: "",
|
||||||
@@ -170,23 +165,13 @@ export default function Home({ hasAdmin }: Props) {
|
|||||||
<Login2FA authId={temp.authId} />
|
<Login2FA authId={temp.authId} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!hasAdmin && (
|
|
||||||
<div className="mt-4 text-center text-sm">
|
|
||||||
Dont have an account?
|
|
||||||
<Link className="underline" href="/register">
|
|
||||||
Sign up
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="flex flex-row justify-between flex-wrap">
|
<div className="flex flex-row justify-between flex-wrap">
|
||||||
<div className="mt-4 text-center text-sm flex flex-row justify-center gap-2">
|
<div className="mt-4 text-center text-sm flex flex-row justify-center gap-2">
|
||||||
Need help?
|
|
||||||
<Link
|
<Link
|
||||||
className="underline"
|
className="hover:underline text-muted-foreground"
|
||||||
href="https://dokploy.com"
|
href="/register"
|
||||||
target="_blank"
|
|
||||||
>
|
>
|
||||||
Contact us
|
Create an account
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -212,6 +197,24 @@ Home.getLayout = (page: ReactElement) => {
|
|||||||
return <OnboardingLayout>{page}</OnboardingLayout>;
|
return <OnboardingLayout>{page}</OnboardingLayout>;
|
||||||
};
|
};
|
||||||
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
try {
|
||||||
|
const { user } = await validateRequest(context.req, context.res);
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
const hasAdmin = await isAdminPresent();
|
const hasAdmin = await isAdminPresent();
|
||||||
|
|
||||||
if (!hasAdmin) {
|
if (!hasAdmin) {
|
||||||
@@ -233,6 +236,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
hasAdmin,
|
hasAdmin,
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { getUserByToken } from "@/server/api/services/admin";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { getUserByToken } from "@dokploy/server";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
import type { GetServerSidePropsContext } from "next";
|
import type { GetServerSidePropsContext } from "next";
|
||||||
|
|||||||
@@ -15,11 +15,11 @@ import {
|
|||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { isAdminPresent } from "@/server/api/services/admin";
|
|
||||||
// import { IS_CLOUD } from "@/server/constants";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { AlertTriangle } from "lucide-react";
|
import { AlertTriangle } from "lucide-react";
|
||||||
|
import type { GetServerSidePropsContext } from "next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
@@ -67,9 +67,10 @@ type Register = z.infer<typeof registerSchema>;
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
hasAdmin: boolean;
|
hasAdmin: boolean;
|
||||||
|
isCloud: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Register = ({ hasAdmin }: Props) => {
|
const Register = ({ isCloud }: Props) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync, error, isError } = api.auth.createAdmin.useMutation();
|
const { mutateAsync, error, isError } = api.auth.createAdmin.useMutation();
|
||||||
|
|
||||||
@@ -112,9 +113,12 @@ const Register = ({ hasAdmin }: Props) => {
|
|||||||
<span className="font-medium text-sm">Dokploy</span>
|
<span className="font-medium text-sm">Dokploy</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<CardTitle className="text-2xl font-bold">Setup the server</CardTitle>
|
<CardTitle className="text-2xl font-bold">
|
||||||
|
{isCloud ? "Create an account" : "Setup the server"}
|
||||||
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
Enter your email and password to setup the server
|
Enter your email and password to{" "}
|
||||||
|
{isCloud ? "create an account" : "setup the server"}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
<Card className="mx-auto w-full max-w-lg bg-transparent">
|
<Card className="mx-auto w-full max-w-lg bg-transparent">
|
||||||
<div className="p-3" />
|
<div className="p-3" />
|
||||||
@@ -192,24 +196,26 @@ const Register = ({ hasAdmin }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Form>
|
</Form>
|
||||||
{hasAdmin && (
|
<div className="flex flex-row justify-between flex-wrap">
|
||||||
<div className="mt-4 text-center text-sm">
|
{isCloud && (
|
||||||
Already have account?
|
<div className="mt-4 text-center text-sm flex gap-2">
|
||||||
<Link className="underline" href="/">
|
Already have account?
|
||||||
Sign in
|
<Link className="underline" href="/">
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="mt-4 text-center text-sm flex flex-row justify-center gap-2">
|
||||||
|
Need help?
|
||||||
|
<Link
|
||||||
|
className="underline"
|
||||||
|
href="https://dokploy.com"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Contact us
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-4 text-center text-sm flex flex-row justify-center gap-2">
|
|
||||||
Need help?
|
|
||||||
<Link
|
|
||||||
className="underline"
|
|
||||||
href="https://dokploy.com"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Contact us
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -220,12 +226,24 @@ const Register = ({ hasAdmin }: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default Register;
|
export default Register;
|
||||||
export async function getServerSideProps() {
|
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||||
// if (IS_CLOUD) {
|
if (IS_CLOUD) {
|
||||||
// return {
|
const { user } = await validateRequest(context.req, context.res);
|
||||||
// props: {},
|
|
||||||
// };
|
if (user) {
|
||||||
// }
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/dashboard/projects",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
isCloud: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
const hasAdmin = await isAdminPresent();
|
const hasAdmin = await isAdminPresent();
|
||||||
|
|
||||||
if (hasAdmin) {
|
if (hasAdmin) {
|
||||||
@@ -238,7 +256,7 @@ export async function getServerSideProps() {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
hasAdmin,
|
isCloud: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { appRouter } from "@/server/api/root";
|
import { appRouter } from "@/server/api/root";
|
||||||
import { validateRequest } from "@/server/auth/auth";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
import { validateRequest } from "@dokploy/server";
|
||||||
import { createServerSideHelpers } from "@trpc/react-query/server";
|
import { createServerSideHelpers } from "@trpc/react-query/server";
|
||||||
import type { GetServerSidePropsContext, NextPage } from "next";
|
import type { GetServerSidePropsContext, NextPage } from "next";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { findAdmin } from "./server/api/services/admin";
|
import { findAdmin } from "@dokploy/server";
|
||||||
import { updateAuthById } from "./server/api/services/auth";
|
import { updateAuthById } from "@dokploy/server";
|
||||||
import { generateRandomPassword } from "./server/auth/random-password";
|
import { generateRandomPassword } from "@dokploy/server";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -6,19 +6,22 @@ import {
|
|||||||
apiRemoveUser,
|
apiRemoveUser,
|
||||||
users,
|
users,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import {
|
import {
|
||||||
createInvitation,
|
createInvitation,
|
||||||
findAdmin,
|
findAdminById,
|
||||||
|
findUserByAuthId,
|
||||||
|
findUserById,
|
||||||
getUserByToken,
|
getUserByToken,
|
||||||
removeUserByAuthId,
|
removeUserByAuthId,
|
||||||
} from "../services/admin";
|
} from "@dokploy/server";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
import { adminProcedure, createTRPCRouter, publicProcedure } from "../trpc";
|
import { adminProcedure, createTRPCRouter, publicProcedure } from "../trpc";
|
||||||
|
|
||||||
export const adminRouter = createTRPCRouter({
|
export const adminRouter = createTRPCRouter({
|
||||||
one: adminProcedure.query(async ({ ctx }) => {
|
one: adminProcedure.query(async ({ ctx }) => {
|
||||||
const { sshPrivateKey, ...rest } = await findAdmin();
|
const { sshPrivateKey, ...rest } = await findAdminById(ctx.user.adminId);
|
||||||
return {
|
return {
|
||||||
haveSSH: !!sshPrivateKey,
|
haveSSH: !!sshPrivateKey,
|
||||||
...rest,
|
...rest,
|
||||||
@@ -26,9 +29,9 @@ export const adminRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
createUserInvitation: adminProcedure
|
createUserInvitation: adminProcedure
|
||||||
.input(apiCreateUserInvitation)
|
.input(apiCreateUserInvitation)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
await createInvitation(input);
|
await createInvitation(input, ctx.user.adminId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "BAD_REQUEST",
|
code: "BAD_REQUEST",
|
||||||
@@ -40,8 +43,16 @@ export const adminRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
removeUser: adminProcedure
|
removeUser: adminProcedure
|
||||||
.input(apiRemoveUser)
|
.input(apiRemoveUser)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
|
const user = await findUserByAuthId(input.authId);
|
||||||
|
|
||||||
|
if (user.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not allowed to delete this user",
|
||||||
|
});
|
||||||
|
}
|
||||||
return await removeUserByAuthId(input.authId);
|
return await removeUserByAuthId(input.authId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
@@ -58,8 +69,16 @@ export const adminRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
assignPermissions: adminProcedure
|
assignPermissions: adminProcedure
|
||||||
.input(apiAssignPermissions)
|
.input(apiAssignPermissions)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
try {
|
try {
|
||||||
|
const user = await findUserById(input.userId);
|
||||||
|
|
||||||
|
if (user.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not allowed to assign permissions",
|
||||||
|
});
|
||||||
|
}
|
||||||
await db
|
await db
|
||||||
.update(users)
|
.update(users)
|
||||||
.set({
|
.set({
|
||||||
@@ -67,10 +86,7 @@ export const adminRouter = createTRPCRouter({
|
|||||||
})
|
})
|
||||||
.where(eq(users.userId, input.userId));
|
.where(eq(users.userId, input.userId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new TRPCError({
|
throw error;
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error to assign permissions",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,46 +18,45 @@ import {
|
|||||||
apiSaveGitlabProvider,
|
apiSaveGitlabProvider,
|
||||||
apiUpdateApplication,
|
apiUpdateApplication,
|
||||||
applications,
|
applications,
|
||||||
} from "@/server/db/schema/application";
|
} from "@/server/db/schema";
|
||||||
import {
|
import {
|
||||||
type DeploymentJob,
|
type DeploymentJob,
|
||||||
cleanQueuesByApplication,
|
cleanQueuesByApplication,
|
||||||
} from "@/server/queues/deployments-queue";
|
} from "@/server/queues/deployments-queue";
|
||||||
import { myQueue } from "@/server/queues/queueSetup";
|
import { myQueue } from "@/server/queues/queueSetup";
|
||||||
import { unzipDrop } from "@/server/utils/builders/drop";
|
import { deploy } from "@/server/utils/deploy";
|
||||||
|
import { uploadFileSchema } from "@/utils/schema";
|
||||||
import {
|
import {
|
||||||
|
IS_CLOUD,
|
||||||
|
addNewService,
|
||||||
|
checkServiceAccess,
|
||||||
|
createApplication,
|
||||||
|
deleteAllMiddlewares,
|
||||||
|
findApplicationById,
|
||||||
|
findProjectById,
|
||||||
|
getApplicationStats,
|
||||||
|
readConfig,
|
||||||
|
readRemoteConfig,
|
||||||
|
removeDeployments,
|
||||||
|
removeDirectoryCode,
|
||||||
|
removeMonitoringDirectory,
|
||||||
removeService,
|
removeService,
|
||||||
|
removeTraefikConfig,
|
||||||
startService,
|
startService,
|
||||||
startServiceRemote,
|
startServiceRemote,
|
||||||
stopService,
|
stopService,
|
||||||
stopServiceRemote,
|
stopServiceRemote,
|
||||||
} from "@/server/utils/docker/utils";
|
unzipDrop,
|
||||||
import {
|
updateApplication,
|
||||||
removeDirectoryCode,
|
updateApplicationStatus,
|
||||||
removeMonitoringDirectory,
|
|
||||||
} from "@/server/utils/filesystem/directory";
|
|
||||||
import {
|
|
||||||
readConfig,
|
|
||||||
readRemoteConfig,
|
|
||||||
removeTraefikConfig,
|
|
||||||
writeConfig,
|
writeConfig,
|
||||||
writeConfigRemote,
|
writeConfigRemote,
|
||||||
} from "@/server/utils/traefik/application";
|
// uploadFileSchema
|
||||||
import { deleteAllMiddlewares } from "@/server/utils/traefik/middleware";
|
} from "@dokploy/server";
|
||||||
import { uploadFileSchema } from "@/utils/schema";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import {
|
|
||||||
createApplication,
|
|
||||||
findApplicationById,
|
|
||||||
getApplicationStats,
|
|
||||||
updateApplication,
|
|
||||||
updateApplicationStatus,
|
|
||||||
} from "../services/application";
|
|
||||||
import { removeDeployments } from "../services/deployment";
|
|
||||||
import { addNewService, checkServiceAccess } from "../services/user";
|
|
||||||
|
|
||||||
export const applicationRouter = createTRPCRouter({
|
export const applicationRouter = createTRPCRouter({
|
||||||
create: protectedProcedure
|
create: protectedProcedure
|
||||||
@@ -67,6 +66,21 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
if (ctx.user.rol === "user") {
|
if (ctx.user.rol === "user") {
|
||||||
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
|
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IS_CLOUD && !input.serverId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You need to use a server to create an application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const project = await findProjectById(input.projectId);
|
||||||
|
if (project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to access this project",
|
||||||
|
});
|
||||||
|
}
|
||||||
const newApplication = await createApplication(input);
|
const newApplication = await createApplication(input);
|
||||||
|
|
||||||
if (ctx.user.rol === "user") {
|
if (ctx.user.rol === "user") {
|
||||||
@@ -94,13 +108,26 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
"access",
|
"access",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to access this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return application;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
reload: protectedProcedure
|
reload: protectedProcedure
|
||||||
.input(apiReloadApplication)
|
.input(apiReloadApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to reload this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
await stopServiceRemote(application.serverId, input.appName);
|
await stopServiceRemote(application.serverId, input.appName);
|
||||||
} else {
|
} else {
|
||||||
@@ -129,6 +156,13 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to delete this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const result = await db
|
const result = await db
|
||||||
.delete(applications)
|
.delete(applications)
|
||||||
.where(eq(applications.applicationId, input.applicationId))
|
.where(eq(applications.applicationId, input.applicationId))
|
||||||
@@ -161,8 +195,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
|
|
||||||
stop: protectedProcedure
|
stop: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const service = await findApplicationById(input.applicationId);
|
const service = await findApplicationById(input.applicationId);
|
||||||
|
if (service.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to stop this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
if (service.serverId) {
|
if (service.serverId) {
|
||||||
await stopServiceRemote(service.serverId, service.appName);
|
await stopServiceRemote(service.serverId, service.appName);
|
||||||
} else {
|
} else {
|
||||||
@@ -175,8 +215,15 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
|
|
||||||
start: protectedProcedure
|
start: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const service = await findApplicationById(input.applicationId);
|
const service = await findApplicationById(input.applicationId);
|
||||||
|
if (service.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to start this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (service.serverId) {
|
if (service.serverId) {
|
||||||
await startServiceRemote(service.serverId, service.appName);
|
await startServiceRemote(service.serverId, service.appName);
|
||||||
} else {
|
} else {
|
||||||
@@ -189,8 +236,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
|
|
||||||
redeploy: protectedProcedure
|
redeploy: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to redeploy this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
const jobData: DeploymentJob = {
|
const jobData: DeploymentJob = {
|
||||||
applicationId: input.applicationId,
|
applicationId: input.applicationId,
|
||||||
titleLog: "Rebuild deployment",
|
titleLog: "Rebuild deployment",
|
||||||
@@ -199,6 +252,12 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
server: !!application.serverId,
|
server: !!application.serverId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (IS_CLOUD && application.serverId) {
|
||||||
|
jobData.serverId = application.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
@@ -210,7 +269,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveEnvironment: protectedProcedure
|
saveEnvironment: protectedProcedure
|
||||||
.input(apiSaveEnvironmentVariables)
|
.input(apiSaveEnvironmentVariables)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this environment",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
env: input.env,
|
env: input.env,
|
||||||
buildArgs: input.buildArgs,
|
buildArgs: input.buildArgs,
|
||||||
@@ -219,7 +285,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveBuildType: protectedProcedure
|
saveBuildType: protectedProcedure
|
||||||
.input(apiSaveBuildType)
|
.input(apiSaveBuildType)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this build type",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
buildType: input.buildType,
|
buildType: input.buildType,
|
||||||
dockerfile: input.dockerfile,
|
dockerfile: input.dockerfile,
|
||||||
@@ -232,7 +305,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveGithubProvider: protectedProcedure
|
saveGithubProvider: protectedProcedure
|
||||||
.input(apiSaveGithubProvider)
|
.input(apiSaveGithubProvider)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this github provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
repository: input.repository,
|
repository: input.repository,
|
||||||
branch: input.branch,
|
branch: input.branch,
|
||||||
@@ -247,7 +327,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveGitlabProvider: protectedProcedure
|
saveGitlabProvider: protectedProcedure
|
||||||
.input(apiSaveGitlabProvider)
|
.input(apiSaveGitlabProvider)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this gitlab provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
gitlabRepository: input.gitlabRepository,
|
gitlabRepository: input.gitlabRepository,
|
||||||
gitlabOwner: input.gitlabOwner,
|
gitlabOwner: input.gitlabOwner,
|
||||||
@@ -264,7 +351,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveBitbucketProvider: protectedProcedure
|
saveBitbucketProvider: protectedProcedure
|
||||||
.input(apiSaveBitbucketProvider)
|
.input(apiSaveBitbucketProvider)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this bitbucket provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
bitbucketRepository: input.bitbucketRepository,
|
bitbucketRepository: input.bitbucketRepository,
|
||||||
bitbucketOwner: input.bitbucketOwner,
|
bitbucketOwner: input.bitbucketOwner,
|
||||||
@@ -279,7 +373,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveDockerProvider: protectedProcedure
|
saveDockerProvider: protectedProcedure
|
||||||
.input(apiSaveDockerProvider)
|
.input(apiSaveDockerProvider)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this docker provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
dockerImage: input.dockerImage,
|
dockerImage: input.dockerImage,
|
||||||
username: input.username,
|
username: input.username,
|
||||||
@@ -292,7 +393,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
saveGitProdiver: protectedProcedure
|
saveGitProdiver: protectedProcedure
|
||||||
.input(apiSaveGitProvider)
|
.input(apiSaveGitProvider)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to save this git provider",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
customGitBranch: input.customGitBranch,
|
customGitBranch: input.customGitBranch,
|
||||||
customGitBuildPath: input.customGitBuildPath,
|
customGitBuildPath: input.customGitBuildPath,
|
||||||
@@ -306,18 +414,32 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
markRunning: protectedProcedure
|
markRunning: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to mark this application as running",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplicationStatus(input.applicationId, "running");
|
await updateApplicationStatus(input.applicationId, "running");
|
||||||
}),
|
}),
|
||||||
update: protectedProcedure
|
update: protectedProcedure
|
||||||
.input(apiUpdateApplication)
|
.input(apiUpdateApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to update this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
const { applicationId, ...rest } = input;
|
const { applicationId, ...rest } = input;
|
||||||
const application = await updateApplication(applicationId, {
|
const updateApp = await updateApplication(applicationId, {
|
||||||
...rest,
|
...rest,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!application) {
|
if (!updateApp) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "BAD_REQUEST",
|
code: "BAD_REQUEST",
|
||||||
message: "Update: Error to update application",
|
message: "Update: Error to update application",
|
||||||
@@ -328,7 +450,14 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
refreshToken: protectedProcedure
|
refreshToken: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to refresh this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
await updateApplication(input.applicationId, {
|
await updateApplication(input.applicationId, {
|
||||||
refreshToken: nanoid(),
|
refreshToken: nanoid(),
|
||||||
});
|
});
|
||||||
@@ -338,6 +467,12 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to deploy this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
const jobData: DeploymentJob = {
|
const jobData: DeploymentJob = {
|
||||||
applicationId: input.applicationId,
|
applicationId: input.applicationId,
|
||||||
titleLog: "Manual deployment",
|
titleLog: "Manual deployment",
|
||||||
@@ -346,6 +481,12 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
server: !!application.serverId,
|
server: !!application.serverId,
|
||||||
};
|
};
|
||||||
|
if (IS_CLOUD && application.serverId) {
|
||||||
|
jobData.serverId = application.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
@@ -358,14 +499,28 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
|
|
||||||
cleanQueues: protectedProcedure
|
cleanQueues: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to clean this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
await cleanQueuesByApplication(input.applicationId);
|
await cleanQueuesByApplication(input.applicationId);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
readTraefikConfig: protectedProcedure
|
readTraefikConfig: protectedProcedure
|
||||||
.input(apiFindOneApplication)
|
.input(apiFindOneApplication)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to read this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let traefikConfig = null;
|
let traefikConfig = null;
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
traefikConfig = await readRemoteConfig(
|
traefikConfig = await readRemoteConfig(
|
||||||
@@ -389,17 +544,24 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
})
|
})
|
||||||
.use(uploadProcedure)
|
.use(uploadProcedure)
|
||||||
.input(uploadFileSchema)
|
.input(uploadFileSchema)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const zipFile = input.zip;
|
const zipFile = input.zip;
|
||||||
|
|
||||||
|
const app = await findApplicationById(input.applicationId as string);
|
||||||
|
|
||||||
|
if (app.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to deploy this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
updateApplication(input.applicationId as string, {
|
updateApplication(input.applicationId as string, {
|
||||||
sourceType: "drop",
|
sourceType: "drop",
|
||||||
dropBuildPath: input.dropBuildPath,
|
dropBuildPath: input.dropBuildPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
const app = await findApplicationById(input.applicationId as string);
|
|
||||||
await unzipDrop(zipFile, app);
|
await unzipDrop(zipFile, app);
|
||||||
|
|
||||||
const jobData: DeploymentJob = {
|
const jobData: DeploymentJob = {
|
||||||
applicationId: app.applicationId,
|
applicationId: app.applicationId,
|
||||||
titleLog: "Manual deployment",
|
titleLog: "Manual deployment",
|
||||||
@@ -408,6 +570,12 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
applicationType: "application",
|
applicationType: "application",
|
||||||
server: !!app.serverId,
|
server: !!app.serverId,
|
||||||
};
|
};
|
||||||
|
if (IS_CLOUD && app.serverId) {
|
||||||
|
jobData.serverId = app.serverId;
|
||||||
|
await deploy(jobData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
await myQueue.add(
|
await myQueue.add(
|
||||||
"deployments",
|
"deployments",
|
||||||
{ ...jobData },
|
{ ...jobData },
|
||||||
@@ -420,9 +588,16 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
updateTraefikConfig: protectedProcedure
|
updateTraefikConfig: protectedProcedure
|
||||||
.input(z.object({ applicationId: z.string(), traefikConfig: z.string() }))
|
.input(z.object({ applicationId: z.string(), traefikConfig: z.string() }))
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
const application = await findApplicationById(input.applicationId);
|
const application = await findApplicationById(input.applicationId);
|
||||||
|
|
||||||
|
if (application.project.adminId !== ctx.user.adminId) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "You are not authorized to update this application",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
await writeConfigRemote(
|
await writeConfigRemote(
|
||||||
application.serverId,
|
application.serverId,
|
||||||
@@ -436,14 +611,15 @@ export const applicationRouter = createTRPCRouter({
|
|||||||
}),
|
}),
|
||||||
readAppMonitoring: protectedProcedure
|
readAppMonitoring: protectedProcedure
|
||||||
.input(apiFindMonitoringStats)
|
.input(apiFindMonitoringStats)
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
if (IS_CLOUD) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "UNAUTHORIZED",
|
||||||
|
message: "Functionality not available in cloud version",
|
||||||
|
});
|
||||||
|
}
|
||||||
const stats = await getApplicationStats(input.appName);
|
const stats = await getApplicationStats(input.appName);
|
||||||
|
|
||||||
return stats;
|
return stats;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Paketo Buildpacks: paketobuildpacks/builder-jammy-full Ubuntu 22.04 Jammy Jellyfish full image with buildpacks for Apache HTTPD, Go, Java, Java Native Image, .NET, NGINX, Node.js, PHP, Procfile, Python, and Ruby
|
|
||||||
// Heroku: heroku/builder:22 Heroku-22 (Ubuntu 22.04) base image with buildpacks for Go, Java, Node.js, PHP, Python, Ruby & Scala.
|
|
||||||
// pack build imageName --path ./ --builder paketobuildpacks/builder-jammy-full
|
|
||||||
// pack build prueba-pack --path ./ --builder heroku/builder:22
|
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import { lucia, validateRequest } from "@/server/auth/auth";
|
|
||||||
import { luciaToken } from "@/server/auth/token";
|
|
||||||
// import { IS_CLOUD } from "@/server/constants";
|
|
||||||
import {
|
import {
|
||||||
apiCreateAdmin,
|
apiCreateAdmin,
|
||||||
apiCreateUser,
|
apiCreateUser,
|
||||||
@@ -11,19 +8,23 @@ import {
|
|||||||
apiVerify2FA,
|
apiVerify2FA,
|
||||||
apiVerifyLogin2FA,
|
apiVerifyLogin2FA,
|
||||||
} from "@/server/db/schema";
|
} from "@/server/db/schema";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
import * as bcrypt from "bcrypt";
|
|
||||||
import { db } from "../../db";
|
|
||||||
import { getUserByToken } from "../services/admin";
|
|
||||||
import {
|
import {
|
||||||
|
IS_CLOUD,
|
||||||
createAdmin,
|
createAdmin,
|
||||||
createUser,
|
createUser,
|
||||||
findAuthByEmail,
|
findAuthByEmail,
|
||||||
findAuthById,
|
findAuthById,
|
||||||
generate2FASecret,
|
generate2FASecret,
|
||||||
|
getUserByToken,
|
||||||
|
lucia,
|
||||||
|
luciaToken,
|
||||||
updateAuthById,
|
updateAuthById,
|
||||||
|
validateRequest,
|
||||||
verify2FA,
|
verify2FA,
|
||||||
} from "../services/auth";
|
} from "@dokploy/server";
|
||||||
|
import { TRPCError } from "@trpc/server";
|
||||||
|
import * as bcrypt from "bcrypt";
|
||||||
|
import { db } from "../../db";
|
||||||
import {
|
import {
|
||||||
adminProcedure,
|
adminProcedure,
|
||||||
createTRPCRouter,
|
createTRPCRouter,
|
||||||
@@ -36,16 +37,15 @@ export const authRouter = createTRPCRouter({
|
|||||||
.input(apiCreateAdmin)
|
.input(apiCreateAdmin)
|
||||||
.mutation(async ({ ctx, input }) => {
|
.mutation(async ({ ctx, input }) => {
|
||||||
try {
|
try {
|
||||||
// if (!IS_CLOUD) {
|
if (!IS_CLOUD) {
|
||||||
const admin = await db.query.admins.findFirst({});
|
const admin = await db.query.admins.findFirst({});
|
||||||
if (admin) {
|
if (admin) {
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
code: "BAD_REQUEST",
|
code: "BAD_REQUEST",
|
||||||
message: "Admin already exists",
|
message: "Admin already exists",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
const newAdmin = await createAdmin(input);
|
const newAdmin = await createAdmin(input);
|
||||||
const session = await lucia.createSession(newAdmin.id || "", {});
|
const session = await lucia.createSession(newAdmin.id || "", {});
|
||||||
ctx.res.appendHeader(
|
ctx.res.appendHeader(
|
||||||
@@ -54,12 +54,7 @@ export const authRouter = createTRPCRouter({
|
|||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
throw error;
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error to create the main admin",
|
|
||||||
cause: error,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
createUser: publicProcedure
|
createUser: publicProcedure
|
||||||
@@ -225,7 +220,7 @@ export const authRouter = createTRPCRouter({
|
|||||||
lucia.createSessionCookie(session.id).serialize(),
|
lucia.createSessionCookie(session.id).serialize(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return auth;
|
return true;
|
||||||
}),
|
}),
|
||||||
disable2FA: protectedProcedure.mutation(async ({ ctx }) => {
|
disable2FA: protectedProcedure.mutation(async ({ ctx }) => {
|
||||||
const auth = await findAuthById(ctx.user.authId);
|
const auth = await findAuthById(ctx.user.authId);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user