mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat: add openapi and swagger support
This commit is contained in:
parent
f0eecf354b
commit
ad806437af
@ -98,6 +98,7 @@
|
|||||||
"node-pty": "1.0.0",
|
"node-pty": "1.0.0",
|
||||||
"node-schedule": "2.1.1",
|
"node-schedule": "2.1.1",
|
||||||
"octokit": "3.1.2",
|
"octokit": "3.1.2",
|
||||||
|
"openapi-trpc": "^0.2.0",
|
||||||
"otpauth": "^9.2.3",
|
"otpauth": "^9.2.3",
|
||||||
"postgres": "3.4.4",
|
"postgres": "3.4.4",
|
||||||
"public-ip": "6.0.2",
|
"public-ip": "6.0.2",
|
||||||
@ -109,9 +110,11 @@
|
|||||||
"slugify": "^1.6.6",
|
"slugify": "^1.6.6",
|
||||||
"sonner": "^1.4.0",
|
"sonner": "^1.4.0",
|
||||||
"superjson": "^2.2.1",
|
"superjson": "^2.2.1",
|
||||||
|
"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",
|
"tar-fs": "3.0.5",
|
||||||
|
"trpc-openapi": "^1.2.0",
|
||||||
"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",
|
||||||
@ -129,6 +132,7 @@
|
|||||||
"@types/qrcode": "^1.5.5",
|
"@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/tar-fs": "2.0.4",
|
"@types/tar-fs": "2.0.4",
|
||||||
"@types/ws": "8.5.10",
|
"@types/ws": "8.5.10",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
@ -5,14 +5,14 @@ import { createTRPCContext } from "@/server/api/trpc";
|
|||||||
|
|
||||||
// export API handler
|
// export API handler
|
||||||
export default createNextApiHandler({
|
export default createNextApiHandler({
|
||||||
router: appRouter,
|
router: appRouter,
|
||||||
createContext: createTRPCContext,
|
createContext: createTRPCContext,
|
||||||
onError:
|
onError:
|
||||||
process.env.NODE_ENV === "development"
|
process.env.NODE_ENV === "development"
|
||||||
? ({ path, error }) => {
|
? ({ path, error }) => {
|
||||||
console.error(
|
console.error(
|
||||||
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
|
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
});
|
});
|
||||||
|
34
pages/swagger.tsx
Normal file
34
pages/swagger.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { validateRequest } from "@/server/auth/auth";
|
||||||
|
import { api } from "@/utils/api";
|
||||||
|
import type { GetServerSidePropsContext, NextPage } from "next";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import "swagger-ui-react/swagger-ui.css";
|
||||||
|
|
||||||
|
const SwaggerUI = dynamic(() => import("swagger-ui-react"), { ssr: false });
|
||||||
|
|
||||||
|
const Home: NextPage = () => {
|
||||||
|
const { data } = api.settings.getOpenApiDocument.useQuery();
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return <div>Loading...</div>;
|
||||||
|
}
|
||||||
|
return <SwaggerUI spec={data} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
||||||
|
export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||||
|
const { user } = await validateRequest(context.req, context.res);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return {
|
||||||
|
redirect: {
|
||||||
|
permanent: true,
|
||||||
|
destination: "/",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
props: {},
|
||||||
|
};
|
||||||
|
}
|
1612
pnpm-lock.yaml
1612
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,8 @@ import { dockerRouter } from "./routers/docker";
|
|||||||
import { composeRouter } from "./routers/compose";
|
import { composeRouter } from "./routers/compose";
|
||||||
import { registryRouter } from "./routers/registry";
|
import { registryRouter } from "./routers/registry";
|
||||||
import { clusterRouter } from "./routers/cluster";
|
import { clusterRouter } from "./routers/cluster";
|
||||||
|
import { generateOpenAPIDocumentFromTRPCRouter } from "openapi-trpc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary router for your server.
|
* This is the primary router for your server.
|
||||||
*
|
*
|
||||||
@ -39,6 +41,7 @@ export const appRouter = createTRPCRouter({
|
|||||||
redis: redisRouter,
|
redis: redisRouter,
|
||||||
mongo: mongoRouter,
|
mongo: mongoRouter,
|
||||||
mariadb: mariadbRouter,
|
mariadb: mariadbRouter,
|
||||||
|
compose: composeRouter,
|
||||||
user: userRouter,
|
user: userRouter,
|
||||||
domain: domainRouter,
|
domain: domainRouter,
|
||||||
destination: destinationRouter,
|
destination: destinationRouter,
|
||||||
@ -50,10 +53,15 @@ export const appRouter = createTRPCRouter({
|
|||||||
security: securityRouter,
|
security: securityRouter,
|
||||||
redirects: redirectsRouter,
|
redirects: redirectsRouter,
|
||||||
port: portRouter,
|
port: portRouter,
|
||||||
compose: composeRouter,
|
|
||||||
registry: registryRouter,
|
registry: registryRouter,
|
||||||
cluster: clusterRouter,
|
cluster: clusterRouter,
|
||||||
});
|
});
|
||||||
|
|
||||||
// export type definition of API
|
// export type definition of API
|
||||||
export type AppRouter = typeof appRouter;
|
export type AppRouter = typeof appRouter;
|
||||||
|
export const doc = generateOpenAPIDocumentFromTRPCRouter(appRouter, {
|
||||||
|
pathPrefix: "/api/trpc",
|
||||||
|
processOperation(op) {
|
||||||
|
op.security = [{ bearerAuth: [] }];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -27,13 +27,15 @@ import { createAppAuth } from "@octokit/auth-app";
|
|||||||
import { haveGithubRequirements } from "@/server/utils/providers/github";
|
import { haveGithubRequirements } from "@/server/utils/providers/github";
|
||||||
|
|
||||||
export const adminRouter = createTRPCRouter({
|
export const adminRouter = createTRPCRouter({
|
||||||
one: adminProcedure.query(async () => {
|
one: adminProcedure
|
||||||
const { sshPrivateKey, ...rest } = await findAdmin();
|
.meta({ openapi: { method: "GET", path: "/say-hello" } })
|
||||||
return {
|
.query(async () => {
|
||||||
haveSSH: !!sshPrivateKey,
|
const { sshPrivateKey, ...rest } = await findAdmin();
|
||||||
...rest,
|
return {
|
||||||
};
|
haveSSH: !!sshPrivateKey,
|
||||||
}),
|
...rest,
|
||||||
|
};
|
||||||
|
}),
|
||||||
createUserInvitation: adminProcedure
|
createUserInvitation: adminProcedure
|
||||||
.input(apiCreateUserInvitation)
|
.input(apiCreateUserInvitation)
|
||||||
.mutation(async ({ input }) => {
|
.mutation(async ({ input }) => {
|
||||||
|
@ -59,29 +59,6 @@ export const projectRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
createCLI: protectedProcedure
|
|
||||||
.input(apiCreateProject)
|
|
||||||
.mutation(async ({ ctx, input }) => {
|
|
||||||
try {
|
|
||||||
console.log(ctx);
|
|
||||||
if (ctx.user.rol === "user") {
|
|
||||||
await checkProjectAccess(ctx.user.authId, "create");
|
|
||||||
}
|
|
||||||
const project = await createProject(input);
|
|
||||||
if (ctx.user.rol === "user") {
|
|
||||||
await addNewProject(ctx.user.authId, project.projectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return project;
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error to create the project",
|
|
||||||
cause: error,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
one: protectedProcedure
|
one: protectedProcedure
|
||||||
.input(apiFindOneProject)
|
.input(apiFindOneProject)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
|
@ -41,6 +41,7 @@ import {
|
|||||||
} from "../services/settings";
|
} from "../services/settings";
|
||||||
import { canAccessToTraefikFiles } from "../services/user";
|
import { canAccessToTraefikFiles } from "../services/user";
|
||||||
import { recreateDirectory } from "@/server/utils/filesystem/directory";
|
import { recreateDirectory } from "@/server/utils/filesystem/directory";
|
||||||
|
import { doc } from "../root";
|
||||||
|
|
||||||
export const settingsRouter = createTRPCRouter({
|
export const settingsRouter = createTRPCRouter({
|
||||||
reloadServer: adminProcedure.mutation(async () => {
|
reloadServer: adminProcedure.mutation(async () => {
|
||||||
@ -242,5 +243,22 @@ export const settingsRouter = createTRPCRouter({
|
|||||||
}
|
}
|
||||||
return readConfigInPath(input.path);
|
return readConfigInPath(input.path);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
getOpenApiDocument: protectedProcedure.query((): unknown => {
|
||||||
|
doc.components = {
|
||||||
|
securitySchemes: {
|
||||||
|
bearerAuth: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "bearer",
|
||||||
|
bearerFormat: "JWT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
doc.info = {
|
||||||
|
title: "Dokploy API",
|
||||||
|
description: "Endpoints for dokploy",
|
||||||
|
version: getDokployVersion(),
|
||||||
|
};
|
||||||
|
return doc;
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
// apt-get install apache2-utils
|
|
||||||
|
@ -16,11 +16,15 @@ import { createTraefikConfig } from "@/server/utils/traefik/application";
|
|||||||
import { docker } from "@/server/constants";
|
import { docker } from "@/server/constants";
|
||||||
import { getAdvancedStats } from "@/server/monitoring/utilts";
|
import { getAdvancedStats } from "@/server/monitoring/utilts";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
export type Application = typeof applications.$inferSelect;
|
export type Application = typeof applications.$inferSelect;
|
||||||
|
|
||||||
export const createApplication = async (
|
export const createApplication = async (
|
||||||
input: typeof apiCreateApplication._type,
|
input: typeof apiCreateApplication._type,
|
||||||
) => {
|
) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("app");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -14,10 +14,14 @@ import { COMPOSE_PATH } from "@/server/constants";
|
|||||||
import { cloneGithubRepository } from "@/server/utils/providers/github";
|
import { cloneGithubRepository } from "@/server/utils/providers/github";
|
||||||
import { cloneGitRepository } from "@/server/utils/providers/git";
|
import { cloneGitRepository } from "@/server/utils/providers/git";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
|
||||||
export type Compose = typeof compose.$inferSelect;
|
export type Compose = typeof compose.$inferSelect;
|
||||||
|
|
||||||
export const createCompose = async (input: typeof apiCreateCompose._type) => {
|
export const createCompose = async (input: typeof apiCreateCompose._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("compose");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -6,10 +6,14 @@ import { pullImage } from "@/server/utils/docker/utils";
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq, getTableColumns } from "drizzle-orm";
|
import { eq, getTableColumns } from "drizzle-orm";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
|
||||||
export type Mariadb = typeof mariadb.$inferSelect;
|
export type Mariadb = typeof mariadb.$inferSelect;
|
||||||
|
|
||||||
export const createMariadb = async (input: typeof apiCreateMariaDB._type) => {
|
export const createMariadb = async (input: typeof apiCreateMariaDB._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("mariadb");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -6,10 +6,14 @@ import { pullImage } from "@/server/utils/docker/utils";
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq, getTableColumns } from "drizzle-orm";
|
import { eq, getTableColumns } from "drizzle-orm";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
|
||||||
export type Mongo = typeof mongo.$inferSelect;
|
export type Mongo = typeof mongo.$inferSelect;
|
||||||
|
|
||||||
export const createMongo = async (input: typeof apiCreateMongo._type) => {
|
export const createMongo = async (input: typeof apiCreateMongo._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("postgres");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -6,10 +6,15 @@ import { pullImage } from "@/server/utils/docker/utils";
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq, getTableColumns } from "drizzle-orm";
|
import { eq, getTableColumns } from "drizzle-orm";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
|
||||||
export type MySql = typeof mysql.$inferSelect;
|
export type MySql = typeof mysql.$inferSelect;
|
||||||
|
|
||||||
export const createMysql = async (input: typeof apiCreateMySql._type) => {
|
export const createMysql = async (input: typeof apiCreateMySql._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("mysql");
|
||||||
|
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -6,10 +6,14 @@ import { pullImage } from "@/server/utils/docker/utils";
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq, getTableColumns } from "drizzle-orm";
|
import { eq, getTableColumns } from "drizzle-orm";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
|
||||||
export type Postgres = typeof postgres.$inferSelect;
|
export type Postgres = typeof postgres.$inferSelect;
|
||||||
|
|
||||||
export const createPostgres = async (input: typeof apiCreatePostgres._type) => {
|
export const createPostgres = async (input: typeof apiCreatePostgres._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("postgres");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -6,11 +6,15 @@ import { pullImage } from "@/server/utils/docker/utils";
|
|||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
|
import { generateAppName } from "@/server/db/schema/utils";
|
||||||
|
import { generatePassword } from "@/templates/utils";
|
||||||
|
|
||||||
export type Redis = typeof redis.$inferSelect;
|
export type Redis = typeof redis.$inferSelect;
|
||||||
|
|
||||||
// https://github.com/drizzle-team/drizzle-orm/discussions/1483#discussioncomment-7523881
|
// https://github.com/drizzle-team/drizzle-orm/discussions/1483#discussioncomment-7523881
|
||||||
export const createRedis = async (input: typeof apiCreateRedis._type) => {
|
export const createRedis = async (input: typeof apiCreateRedis._type) => {
|
||||||
|
input.appName =
|
||||||
|
`${input.appName}-${generatePassword(6)}` || generateAppName("redis");
|
||||||
if (input.appName) {
|
if (input.appName) {
|
||||||
const valid = await validUniqueServerAppName(input.appName);
|
const valid = await validUniqueServerAppName(input.appName);
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ import superjson from "superjson";
|
|||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
import { validateBearerToken, validateRequest } from "../auth/auth";
|
import { validateBearerToken, validateRequest } from "../auth/auth";
|
||||||
import type { Session, User } from "lucia";
|
import type { Session, User } from "lucia";
|
||||||
|
import type { OperationMeta } from "openapi-trpc";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 1. CONTEXT
|
* 1. CONTEXT
|
||||||
@ -94,19 +95,22 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => {
|
|||||||
* errors on the backend.
|
* errors on the backend.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
const t = initTRPC
|
||||||
transformer: superjson,
|
.meta<OperationMeta>()
|
||||||
errorFormatter({ shape, error }) {
|
.context<typeof createTRPCContext>()
|
||||||
return {
|
.create({
|
||||||
...shape,
|
transformer: superjson,
|
||||||
data: {
|
errorFormatter({ shape, error }) {
|
||||||
...shape.data,
|
return {
|
||||||
zodError:
|
...shape,
|
||||||
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
data: {
|
||||||
},
|
...shape.data,
|
||||||
};
|
zodError:
|
||||||
},
|
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||||
});
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
||||||
|
@ -308,17 +308,12 @@ const createSchema = createInsertSchema(applications, {
|
|||||||
networkSwarm: NetworkSwarmSchema.nullable(),
|
networkSwarm: NetworkSwarmSchema.nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiCreateApplication = createSchema
|
export const apiCreateApplication = createSchema.pick({
|
||||||
.pick({
|
name: true,
|
||||||
name: true,
|
appName: true,
|
||||||
appName: true,
|
description: true,
|
||||||
description: true,
|
projectId: true,
|
||||||
projectId: true,
|
});
|
||||||
})
|
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName: `${data.appName}-${generatePassword(6)}` || generateAppName("app"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOneApplication = createSchema
|
export const apiFindOneApplication = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -75,19 +75,13 @@ const createSchema = createInsertSchema(compose, {
|
|||||||
composeType: z.enum(["docker-compose", "stack"]).optional(),
|
composeType: z.enum(["docker-compose", "stack"]).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiCreateCompose = createSchema
|
export const apiCreateCompose = createSchema.pick({
|
||||||
.pick({
|
name: true,
|
||||||
name: true,
|
description: true,
|
||||||
description: true,
|
projectId: true,
|
||||||
projectId: true,
|
composeType: true,
|
||||||
composeType: true,
|
appName: true,
|
||||||
appName: true,
|
});
|
||||||
})
|
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("compose"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiCreateComposeByTemplate = createSchema
|
export const apiCreateComposeByTemplate = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -89,12 +89,7 @@ export const apiCreateMariaDB = createSchema
|
|||||||
databaseUser: true,
|
databaseUser: true,
|
||||||
databasePassword: true,
|
databasePassword: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required();
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("mariadb"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOneMariaDB = createSchema
|
export const apiFindOneMariaDB = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -81,12 +81,7 @@ export const apiCreateMongo = createSchema
|
|||||||
databaseUser: true,
|
databaseUser: true,
|
||||||
databasePassword: true,
|
databasePassword: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required();
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("postgres"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOneMongo = createSchema
|
export const apiFindOneMongo = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -87,12 +87,7 @@ export const apiCreateMySql = createSchema
|
|||||||
databasePassword: true,
|
databasePassword: true,
|
||||||
databaseRootPassword: true,
|
databaseRootPassword: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required();
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("mysql"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOneMySql = createSchema
|
export const apiFindOneMySql = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -83,12 +83,7 @@ export const apiCreatePostgres = createSchema
|
|||||||
projectId: true,
|
projectId: true,
|
||||||
description: true,
|
description: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required();
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("postgres"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOnePostgres = createSchema
|
export const apiFindOnePostgres = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -53,11 +53,6 @@ export const apiCreateProject = createSchema.pick({
|
|||||||
description: true,
|
description: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiCreateCLI = createSchema.pick({
|
|
||||||
name: true,
|
|
||||||
description: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const apiFindOneProject = createSchema
|
export const apiFindOneProject = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
projectId: true,
|
projectId: true,
|
||||||
|
@ -76,12 +76,7 @@ export const apiCreateRedis = createSchema
|
|||||||
projectId: true,
|
projectId: true,
|
||||||
description: true,
|
description: true,
|
||||||
})
|
})
|
||||||
.required()
|
.required();
|
||||||
.transform((data) => ({
|
|
||||||
...data,
|
|
||||||
appName:
|
|
||||||
`${data.appName}-${generatePassword(6)}` || generateAppName("redis"),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const apiFindOneRedis = createSchema
|
export const apiFindOneRedis = createSchema
|
||||||
.pick({
|
.pick({
|
||||||
|
@ -22,6 +22,30 @@ import { initializePostgres } from "./setup/postgres-setup";
|
|||||||
import { migration } from "@/server/db/migration";
|
import { migration } from "@/server/db/migration";
|
||||||
import { setupDockerContainerLogsWebSocketServer } from "./wss/docker-container-logs";
|
import { setupDockerContainerLogsWebSocketServer } from "./wss/docker-container-logs";
|
||||||
import { setupDockerContainerTerminalWebSocketServer } from "./wss/docker-container-terminal";
|
import { setupDockerContainerTerminalWebSocketServer } from "./wss/docker-container-terminal";
|
||||||
|
import { generateOpenAPIDocumentFromTRPCRouter } from "openapi-trpc";
|
||||||
|
import { appRouter } from "./api/root";
|
||||||
|
import { getDokployVersion } from "./api/services/settings";
|
||||||
|
|
||||||
|
export const doc = generateOpenAPIDocumentFromTRPCRouter(appRouter, {
|
||||||
|
pathPrefix: "/api/trpc",
|
||||||
|
processOperation(op) {
|
||||||
|
op.security = [{ bearerAuth: [] }];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
doc.components = {
|
||||||
|
securitySchemes: {
|
||||||
|
bearerAuth: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "bearer",
|
||||||
|
bearerFormat: "JWT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
doc.info = {
|
||||||
|
title: "Dokploy API",
|
||||||
|
description: "Endpoints for dokploy",
|
||||||
|
version: getDokployVersion(),
|
||||||
|
};
|
||||||
|
|
||||||
config({ path: ".env" });
|
config({ path: ".env" });
|
||||||
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
const PORT = Number.parseInt(process.env.PORT || "3000", 10);
|
||||||
@ -31,6 +55,81 @@ const handle = app.getRequestHandler();
|
|||||||
void app.prepare().then(async () => {
|
void app.prepare().then(async () => {
|
||||||
try {
|
try {
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
|
if (req.method === "GET" && req.url === "/trpc.json") {
|
||||||
|
res.setHeader("Content-Type", "application/json");
|
||||||
|
res.end(JSON.stringify(doc)); // Asegúrate de definir `doc`
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req.method === "GET" && req.url === "/trpc") {
|
||||||
|
res.setHeader("Content-Type", "text/html");
|
||||||
|
res.end(`<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="description" content="SwaggerUI" />
|
||||||
|
<title>SwaggerUI</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui.min.css" rel="stylesheet" />
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.17.0/favicon-32x32.png" rel="icon" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
|
||||||
|
<script>
|
||||||
|
window.onload = () => {
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: '/trpc.json',
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
layout: "BaseLayout",
|
||||||
|
requestInterceptor: (request) => {
|
||||||
|
const token = localStorage.getItem('bearerToken');
|
||||||
|
if (token) {
|
||||||
|
request.headers['Authorization'] = 'Bearer ' + token;
|
||||||
|
}
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.initOAuth({
|
||||||
|
persistAuthorization: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store the token in local storage when set in Swagger UI
|
||||||
|
ui.authActions.authorize({
|
||||||
|
bearerAuth: {
|
||||||
|
name: "bearerAuth",
|
||||||
|
value: localStorage.getItem('bearerToken') || '',
|
||||||
|
schema: {
|
||||||
|
type: "http",
|
||||||
|
scheme: "bearer",
|
||||||
|
bearerFormat: "JWT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.authSelectors.oauth().authorize({
|
||||||
|
bearerAuth: {
|
||||||
|
token: localStorage.getItem('bearerToken') || ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.ui = ui;
|
||||||
|
|
||||||
|
// Save token to localStorage
|
||||||
|
const tokenInput = document.querySelector('input[placeholder="Bearer token"]');
|
||||||
|
tokenInput.addEventListener('change', (event) => {
|
||||||
|
localStorage.setItem('bearerToken', event.target.value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
handle(req, res);
|
handle(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -158,3 +158,9 @@
|
|||||||
.animate-heartbeat {
|
.animate-heartbeat {
|
||||||
animation: heartbeat 2.5s infinite;
|
animation: heartbeat 2.5s infinite;
|
||||||
}
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.swagger-ui {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user