refactor: update invitation

This commit is contained in:
Mauricio Siu
2025-02-16 02:57:49 -06:00
parent 4a1a14aeb4
commit e7db0ccb70
29 changed files with 270 additions and 330 deletions

View File

@@ -36,7 +36,7 @@ export const SearchCommand = () => {
const router = useRouter(); const router = useRouter();
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
const { data: session } = authClient.getSession(); const { data: session } = authClient.useSession();
const { data } = api.project.all.useQuery(undefined, { const { data } = api.project.all.useQuery(undefined, {
enabled: !!session, enabled: !!session,
}); });

View File

@@ -10,12 +10,14 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
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 { authClient } from "@/lib/auth";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
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 { data: activeOrganization } = authClient.useActiveOrganization();
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);
@@ -25,7 +27,7 @@ export const AddGithubProvider = () => {
const url = document.location.origin; const url = document.location.origin;
const manifest = JSON.stringify( const manifest = JSON.stringify(
{ {
redirect_url: `${origin}/api/providers/github/setup?authId=${data?.id}`, redirect_url: `${origin}/api/providers/github/setup?organizationId=${activeOrganization?.id}`,
name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`, name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`,
url: origin, url: origin,
hook_attributes: { hook_attributes: {
@@ -93,8 +95,8 @@ export const AddGithubProvider = () => {
<form <form
action={ action={
isOrganization isOrganization
? `https://github.com/organizations/${organizationName}/settings/apps/new?state=gh_init:${data?.id}` ? `https://github.com/organizations/${organizationName}/settings/apps/new?state=gh_init:${activeOrganization?.id}`
: `https://github.com/settings/apps/new?state=gh_init:${data?.id}` : `https://github.com/settings/apps/new?state=gh_init:${activeOrganization?.id}`
} }
method="post" method="post"
> >

View File

@@ -19,6 +19,7 @@ import {
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { PlusIcon } from "lucide-react"; import { PlusIcon } from "lucide-react";
@@ -40,6 +41,7 @@ export const AddUser = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const utils = api.useUtils(); const utils = api.useUtils();
const { data: activeOrganization } = authClient.useActiveOrganization();
const { mutateAsync, isError, error, isLoading } = const { mutateAsync, isError, error, isLoading } =
api.admin.createUserInvitation.useMutation(); api.admin.createUserInvitation.useMutation();
@@ -54,17 +56,23 @@ export const AddUser = () => {
}, [form, form.formState.isSubmitSuccessful, form.reset]); }, [form, form.formState.isSubmitSuccessful, form.reset]);
const onSubmit = async (data: AddUser) => { const onSubmit = async (data: AddUser) => {
await mutateAsync({ const result = await authClient.organization.inviteMember({
email: data.email.toLowerCase(), email: data.email.toLowerCase(),
}) role: "user",
.then(async () => { organizationId: activeOrganization?.id,
toast.success("Invitation created"); });
await utils.user.all.invalidate(); console.log(result);
setOpen(false); // await mutateAsync({
}) // email: data.email.toLowerCase(),
.catch(() => { // })
toast.error("Error creating the invitation"); // .then(async () => {
}); // toast.success("Invitation created");
// await utils.user.all.invalidate();
// setOpen(false);
// })
// .catch(() => {
// toast.error("Error creating the invitation");
// });
}; };
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>

View File

@@ -2,6 +2,6 @@ import { organizationClient } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react"; import { createAuthClient } from "better-auth/react";
export const authClient = createAuthClient({ export const authClient = createAuthClient({
baseURL: "http://localhost:3000", // the base url of your auth server // baseURL: "http://localhost:3000", // the base url of your auth server
plugins: [organizationClient()], plugins: [organizationClient()],
}); });

View File

@@ -0,0 +1,30 @@
import { Button } from "@/components/ui/button";
import { authClient } from "@/lib/auth";
import { useRouter } from "next/router";
export const AcceptInvitation = () => {
const { query } = useRouter();
const invitationId = query["accept-invitation"];
// const { data: organization } = api.organization.getById.useQuery({
// id: id as string
// })
return (
<div>
<Button
onClick={async () => {
const result = await authClient.organization.acceptInvitation({
invitationId: invitationId as string,
});
console.log(result);
}}
>
Accept Invitation
</Button>
</div>
);
};
export default AcceptInvitation;

View File

@@ -1,10 +1,12 @@
import { db } from "@/server/db"; import { db } from "@/server/db";
import { github } from "@/server/db/schema"; import { github } from "@/server/db/schema";
import { import {
auth,
createGithub, createGithub,
findAdminByAuthId, findAdminByAuthId,
findAuthById, findAuthById,
findUserByAuthId, findUserByAuthId,
findUserById,
} from "@dokploy/server"; } 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";
@@ -28,7 +30,7 @@ export default async function handler(
return res.status(400).json({ error: "Missing code parameter" }); return res.status(400).json({ error: "Missing code parameter" });
} }
const [action, value] = state?.split(":"); const [action, value] = state?.split(":");
// Value could be the authId or the githubProviderId // Value could be the organizationId or the githubProviderId
if (action === "gh_init") { if (action === "gh_init") {
const octokit = new Octokit({}); const octokit = new Octokit({});
@@ -39,17 +41,6 @@ export default async function handler(
}, },
); );
const auth = await findAuthById(value as string);
let adminId = "";
if (auth.role === "owner") {
const admin = await findAdminByAuthId(auth.id);
adminId = admin.adminId;
} else {
const user = await findUserByAuthId(auth.id);
adminId = user.adminId;
}
await createGithub( await createGithub(
{ {
name: data.name, name: data.name,
@@ -60,7 +51,7 @@ export default async function handler(
githubWebhookSecret: data.webhook_secret, githubWebhookSecret: data.webhook_secret,
githubPrivateKey: data.pem, githubPrivateKey: data.pem,
}, },
adminId, value as string,
); );
} else if (action === "gh_setup") { } else if (action === "gh_setup") {
await db await db

View File

@@ -8,7 +8,6 @@ import {
apiUpdateBitbucket, apiUpdateBitbucket,
} from "@/server/db/schema"; } from "@/server/db/schema";
import { import {
IS_CLOUD,
createBitbucket, createBitbucket,
findBitbucketById, findBitbucketById,
getBitbucketBranches, getBitbucketBranches,
@@ -37,11 +36,9 @@ export const bitbucketRouter = createTRPCRouter({
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId); const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if ( if (
IS_CLOUD &&
bitbucketProvider.gitProvider.organizationId !== bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider", message: "You are not allowed to access this bitbucket provider",
@@ -59,14 +56,11 @@ export const bitbucketRouter = createTRPCRouter({
}, },
}); });
if (IS_CLOUD) { result = result.filter(
// TODO: mAyBe a rEfaCtoR 🤫 (provider) =>
result = result.filter( provider.gitProvider.organizationId ===
(provider) => ctx.session.activeOrganizationId,
provider.gitProvider.organizationId === );
ctx.session.activeOrganizationId,
);
}
return result; return result;
}), }),
@@ -75,11 +69,9 @@ export const bitbucketRouter = createTRPCRouter({
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId); const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if ( if (
IS_CLOUD &&
bitbucketProvider.gitProvider.organizationId !== bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider", message: "You are not allowed to access this bitbucket provider",
@@ -94,11 +86,9 @@ export const bitbucketRouter = createTRPCRouter({
input.bitbucketId || "", input.bitbucketId || "",
); );
if ( if (
IS_CLOUD &&
bitbucketProvider.gitProvider.organizationId !== bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider", message: "You are not allowed to access this bitbucket provider",
@@ -112,11 +102,9 @@ export const bitbucketRouter = createTRPCRouter({
try { try {
const bitbucketProvider = await findBitbucketById(input.bitbucketId); const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if ( if (
IS_CLOUD &&
bitbucketProvider.gitProvider.organizationId !== bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider", message: "You are not allowed to access this bitbucket provider",
@@ -137,11 +125,9 @@ export const bitbucketRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId); const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if ( if (
IS_CLOUD &&
bitbucketProvider.gitProvider.organizationId !== bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider", message: "You are not allowed to access this bitbucket provider",
@@ -149,7 +135,7 @@ export const bitbucketRouter = createTRPCRouter({
} }
return await updateBitbucket(input.bitbucketId, { return await updateBitbucket(input.bitbucketId, {
...input, ...input,
userId: ctx.user.ownerId, organizationId: ctx.session.activeOrganizationId,
}); });
}), }),
}); });

View File

@@ -6,7 +6,6 @@ import {
apiUpdateGithub, apiUpdateGithub,
} from "@/server/db/schema"; } from "@/server/db/schema";
import { import {
IS_CLOUD,
findGithubById, findGithubById,
getGithubBranches, getGithubBranches,
getGithubRepositories, getGithubRepositories,
@@ -24,7 +23,6 @@ export const githubRouter = createTRPCRouter({
githubProvider.gitProvider.organizationId !== githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider", message: "You are not allowed to access this github provider",
@@ -40,7 +38,6 @@ export const githubRouter = createTRPCRouter({
githubProvider.gitProvider.organizationId !== githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider", message: "You are not allowed to access this github provider",
@@ -71,14 +68,11 @@ export const githubRouter = createTRPCRouter({
}, },
}); });
if (IS_CLOUD) { result = result.filter(
// TODO: mAyBe a rEfaCtoR 🤫 (provider) =>
result = result.filter( provider.gitProvider.organizationId ===
(provider) => ctx.session.activeOrganizationId,
provider.gitProvider.organizationId === );
ctx.session.activeOrganizationId,
);
}
const filtered = result const filtered = result
.filter((provider) => haveGithubRequirements(provider)) .filter((provider) => haveGithubRequirements(provider))
@@ -100,11 +94,9 @@ export const githubRouter = createTRPCRouter({
try { try {
const githubProvider = await findGithubById(input.githubId); const githubProvider = await findGithubById(input.githubId);
if ( if (
IS_CLOUD &&
githubProvider.gitProvider.organizationId !== githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider", message: "You are not allowed to access this github provider",
@@ -124,11 +116,9 @@ export const githubRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
const githubProvider = await findGithubById(input.githubId); const githubProvider = await findGithubById(input.githubId);
if ( if (
IS_CLOUD &&
githubProvider.gitProvider.organizationId !== githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider", message: "You are not allowed to access this github provider",

View File

@@ -9,7 +9,6 @@ import {
import { db } from "@/server/db"; import { db } from "@/server/db";
import { import {
IS_CLOUD,
createGitlab, createGitlab,
findGitlabById, findGitlabById,
getGitlabBranches, getGitlabBranches,
@@ -43,7 +42,6 @@ export const gitlabRouter = createTRPCRouter({
gitlabProvider.gitProvider.organizationId !== gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider", message: "You are not allowed to access this Gitlab provider",
@@ -58,14 +56,11 @@ export const gitlabRouter = createTRPCRouter({
}, },
}); });
if (IS_CLOUD) { result = result.filter(
// TODO: mAyBe a rEfaCtoR 🤫 (provider) =>
result = result.filter( provider.gitProvider.organizationId ===
(provider) => ctx.session.activeOrganizationId,
provider.gitProvider.organizationId === );
ctx.session.activeOrganizationId,
);
}
const filtered = result const filtered = result
.filter((provider) => haveGitlabRequirements(provider)) .filter((provider) => haveGitlabRequirements(provider))
.map((provider) => { .map((provider) => {
@@ -87,7 +82,6 @@ export const gitlabRouter = createTRPCRouter({
gitlabProvider.gitProvider.organizationId !== gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider", message: "You are not allowed to access this Gitlab provider",
@@ -104,7 +98,6 @@ export const gitlabRouter = createTRPCRouter({
gitlabProvider.gitProvider.organizationId !== gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider", message: "You are not allowed to access this Gitlab provider",
@@ -121,7 +114,6 @@ export const gitlabRouter = createTRPCRouter({
gitlabProvider.gitProvider.organizationId !== gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider", message: "You are not allowed to access this Gitlab provider",
@@ -145,7 +137,6 @@ export const gitlabRouter = createTRPCRouter({
gitlabProvider.gitProvider.organizationId !== gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId ctx.session.activeOrganizationId
) { ) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({ throw new TRPCError({
code: "UNAUTHORIZED", code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider", message: "You are not allowed to access this Gitlab provider",

View File

@@ -71,7 +71,7 @@ export const projectRouter = createTRPCRouter({
.input(apiFindOneProject) .input(apiFindOneProject)
.query(async ({ input, ctx }) => { .query(async ({ input, ctx }) => {
if (ctx.user.rol === "member") { if (ctx.user.rol === "member") {
const { accessedServices } = await findUserByAuthId(ctx.user.id); const { accessedServices } = await findUserById(ctx.user.id);
await checkProjectAccess(ctx.user.id, "access", input.projectId); await checkProjectAccess(ctx.user.id, "access", input.projectId);

View File

@@ -4,8 +4,8 @@ import { db } from "@dokploy/server/db";
import { member } from "@dokploy/server/db/schema"; import { member } from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
import { z } from "zod"; import { z } from "zod";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const userRouter = createTRPCRouter({ export const userRouter = createTRPCRouter({
all: adminProcedure.query(async ({ ctx }) => { all: adminProcedure.query(async ({ ctx }) => {
return await db.query.member.findMany({ return await db.query.member.findMany({

View File

@@ -34,14 +34,14 @@ void app.prepare().then(async () => {
}); });
// WEBSOCKET // WEBSOCKET
// setupDrawerLogsWebSocketServer(server); setupDrawerLogsWebSocketServer(server);
// setupDeploymentLogsWebSocketServer(server); setupDeploymentLogsWebSocketServer(server);
// setupDockerContainerLogsWebSocketServer(server); setupDockerContainerLogsWebSocketServer(server);
// setupDockerContainerTerminalWebSocketServer(server); setupDockerContainerTerminalWebSocketServer(server);
// setupTerminalWebSocketServer(server); setupTerminalWebSocketServer(server);
// if (!IS_CLOUD) { if (!IS_CLOUD) {
// setupDockerStatsMonitoringSocketServer(server); setupDockerStatsMonitoringSocketServer(server);
// } }
if (process.env.NODE_ENV === "production" && !IS_CLOUD) { if (process.env.NODE_ENV === "production" && !IS_CLOUD) {
setupDirectories(); setupDirectories();

View File

@@ -1,5 +1,5 @@
import type http from "node:http"; import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server"; import { findServerById, validateRequest } from "@dokploy/server";
import { spawn } from "node-pty"; import { spawn } from "node-pty";
import { Client } from "ssh2"; import { Client } from "ssh2";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
@@ -35,7 +35,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const since = url.searchParams.get("since"); const since = url.searchParams.get("since");
const serverId = url.searchParams.get("serverId"); const serverId = url.searchParams.get("serverId");
const runType = url.searchParams.get("runType"); const runType = url.searchParams.get("runType");
const { user, session } = await validateWebSocketRequest(req); const { user, session } = await validateRequest(req);
if (!containerId) { if (!containerId) {
ws.close(4000, "containerId no provided"); ws.close(4000, "containerId no provided");

View File

@@ -1,5 +1,5 @@
import type http from "node:http"; import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server"; import { findServerById, validateRequest } from "@dokploy/server";
import { spawn } from "node-pty"; import { spawn } from "node-pty";
import { Client } from "ssh2"; import { Client } from "ssh2";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
@@ -32,7 +32,7 @@ export const setupDockerContainerTerminalWebSocketServer = (
const containerId = url.searchParams.get("containerId"); const containerId = url.searchParams.get("containerId");
const activeWay = url.searchParams.get("activeWay"); const activeWay = url.searchParams.get("activeWay");
const serverId = url.searchParams.get("serverId"); const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req); const { user, session } = await validateRequest(req);
if (!containerId) { if (!containerId) {
ws.close(4000, "containerId no provided"); ws.close(4000, "containerId no provided");

View File

@@ -4,7 +4,7 @@ import {
execAsync, execAsync,
getLastAdvancedStatsFile, getLastAdvancedStatsFile,
recordAdvancedStats, recordAdvancedStats,
validateWebSocketRequest, validateRequest,
} from "@dokploy/server"; } from "@dokploy/server";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
@@ -36,7 +36,7 @@ export const setupDockerStatsMonitoringSocketServer = (
| "application" | "application"
| "stack" | "stack"
| "docker-compose"; | "docker-compose";
const { user, session } = await validateWebSocketRequest(req); const { user, session } = await validateRequest(req);
if (!appName) { if (!appName) {
ws.close(4000, "appName no provided"); ws.close(4000, "appName no provided");

View File

@@ -3,6 +3,7 @@ import { applyWSSHandler } from "@trpc/server/adapters/ws";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
import { appRouter } from "../api/root"; import { appRouter } from "../api/root";
import { createTRPCContext } from "../api/trpc"; import { createTRPCContext } from "../api/trpc";
import { validateRequest } from "@dokploy/server/index";
export const setupDrawerLogsWebSocketServer = ( export const setupDrawerLogsWebSocketServer = (
server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>,
@@ -32,5 +33,11 @@ export const setupDrawerLogsWebSocketServer = (
wssTerm.on("connection", async (ws, req) => { wssTerm.on("connection", async (ws, req) => {
const url = new URL(req.url || "", `http://${req.headers.host}`); const url = new URL(req.url || "", `http://${req.headers.host}`);
const { user, session } = await validateRequest(req);
if (!user || !session) {
ws.close();
return;
}
}); });
}; };

View File

@@ -1,6 +1,6 @@
import { spawn } from "node:child_process"; import { spawn } from "node:child_process";
import type http from "node:http"; import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server"; import { findServerById, validateRequest } from "@dokploy/server";
import { Client } from "ssh2"; import { Client } from "ssh2";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
@@ -29,7 +29,7 @@ export const setupDeploymentLogsWebSocketServer = (
const url = new URL(req.url || "", `http://${req.headers.host}`); const url = new URL(req.url || "", `http://${req.headers.host}`);
const logPath = url.searchParams.get("logPath"); const logPath = url.searchParams.get("logPath");
const serverId = url.searchParams.get("serverId"); const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req); const { user, session } = await validateRequest(req);
if (!logPath) { if (!logPath) {
console.log("logPath no provided"); console.log("logPath no provided");

View File

@@ -1,9 +1,5 @@
import type http from "node:http"; import type http from "node:http";
import { import { IS_CLOUD, findServerById, validateRequest } from "@dokploy/server";
IS_CLOUD,
findServerById,
validateWebSocketRequest,
} from "@dokploy/server";
import { publicIpv4, publicIpv6 } from "public-ip"; import { publicIpv4, publicIpv6 } from "public-ip";
import { Client, type ConnectConfig } from "ssh2"; import { Client, type ConnectConfig } from "ssh2";
import { WebSocketServer } from "ws"; import { WebSocketServer } from "ws";
@@ -71,7 +67,7 @@ export const setupTerminalWebSocketServer = (
wssTerm.on("connection", async (ws, req) => { wssTerm.on("connection", async (ws, req) => {
const url = new URL(req.url || "", `http://${req.headers.host}`); const url = new URL(req.url || "", `http://${req.headers.host}`);
const serverId = url.searchParams.get("serverId"); const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req); const { user, session } = await validateRequest(req);
if (!user || !session || !serverId) { if (!user || !session || !serverId) {
ws.close(); ws.close();
return; return;

View File

@@ -1,6 +1,3 @@
import type { IncomingMessage, ServerResponse } from "node:http";
import { findAdminByAuthId } from "@dokploy/server/services/admin";
import { findUserByAuthId } from "@dokploy/server/services/user";
import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle";
import { TimeSpan } from "lucia"; import { TimeSpan } from "lucia";
import { Lucia } from "lucia/dist/core.js"; import { Lucia } from "lucia/dist/core.js";
@@ -42,78 +39,3 @@ export type ReturnValidateToken = Promise<{
user: (User & { authId: string; adminId: string }) | null; user: (User & { authId: string; adminId: string }) | null;
session: Session | null; session: Session | null;
}>; }>;
export async function validateRequest(
req: IncomingMessage,
res: ServerResponse,
): ReturnValidateToken {
console.log(session);
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
if (!sessionId) {
return {
user: null,
session: null,
};
}
const result = await lucia.validateSession(sessionId);
if (result?.session?.fresh) {
res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(result.session.id).serialize(),
);
}
if (!result.session) {
res.appendHeader(
"Set-Cookie",
lucia.createBlankSessionCookie().serialize(),
);
}
if (result.user) {
try {
if (result.user?.rol === "owner") {
const admin = await findAdminByAuthId(result.user.id);
result.user.adminId = admin.adminId;
} else if (result.user?.rol === "member") {
const userResult = await findUserByAuthId(result.user.id);
result.user.adminId = userResult.adminId;
}
} catch (error) {
return {
user: null,
session: null,
};
}
}
return {
session: result.session,
...((result.user && {
user: {
authId: result.user.id,
email: result.user.email,
rol: result.user.rol,
id: result.user.id,
secret: result.user.secret,
adminId: result.user.adminId,
},
}) || {
user: null,
}),
};
}
export async function validateWebSocketRequest(
req: IncomingMessage,
): Promise<{ user: User; session: Session } | { user: null; session: null }> {
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
if (!sessionId) {
return {
user: null,
session: null,
};
}
const result = await lucia.validateSession(sessionId);
return result;
}

View File

@@ -21,79 +21,79 @@ export const luciaToken = new Lucia(adapter, {
}, },
}); });
export const validateBearerToken = async ( // export const validateBearerToken = async (
req: IncomingMessage, // req: IncomingMessage,
): ReturnValidateToken => { // ): ReturnValidateToken => {
const authorizationHeader = req.headers.authorization; // const authorizationHeader = req.headers.authorization;
const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); // const sessionId = luciaToken.readBearerToken(authorizationHeader ?? "");
if (!sessionId) { // if (!sessionId) {
return { // return {
user: null, // user: null,
session: null, // session: null,
}; // };
} // }
const result = await luciaToken.validateSession(sessionId); // const result = await luciaToken.validateSession(sessionId);
if (result.user) { // if (result.user) {
if (result.user?.rol === "owner") { // if (result.user?.rol === "owner") {
const admin = await findAdminByAuthId(result.user.id); // const admin = await findAdminByAuthId(result.user.id);
result.user.adminId = admin.adminId; // result.user.adminId = admin.adminId;
} else if (result.user?.rol === "member") { // } else if (result.user?.rol === "member") {
const userResult = await findUserByAuthId(result.user.id); // const userResult = await findUserByAuthId(result.user.id);
result.user.adminId = userResult.adminId; // result.user.adminId = userResult.adminId;
} // }
} // }
return { // return {
session: result.session, // session: result.session,
...((result.user && { // ...((result.user && {
user: { // user: {
adminId: result.user.adminId, // adminId: result.user.adminId,
authId: result.user.id, // authId: result.user.id,
email: result.user.email, // email: result.user.email,
rol: result.user.rol, // rol: result.user.rol,
id: result.user.id, // id: result.user.id,
secret: result.user.secret, // secret: result.user.secret,
}, // },
}) || { // }) || {
user: null, // user: null,
}), // }),
}; // };
}; // };
export const validateBearerTokenAPI = async ( // export const validateBearerTokenAPI = async (
authorizationHeader: string, // authorizationHeader: string,
): ReturnValidateToken => { // ): ReturnValidateToken => {
const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); // const sessionId = luciaToken.readBearerToken(authorizationHeader ?? "");
if (!sessionId) { // if (!sessionId) {
return { // return {
user: null, // user: null,
session: null, // session: null,
}; // };
} // }
const result = await luciaToken.validateSession(sessionId); // const result = await luciaToken.validateSession(sessionId);
if (result.user) { // if (result.user) {
if (result.user?.rol === "owner") { // if (result.user?.rol === "owner") {
const admin = await findAdminByAuthId(result.user.id); // const admin = await findAdminByAuthId(result.user.id);
result.user.adminId = admin.adminId; // result.user.adminId = admin.adminId;
} else if (result.user?.rol === "member") { // } else if (result.user?.rol === "member") {
const userResult = await findUserByAuthId(result.user.id); // const userResult = await findUserByAuthId(result.user.id);
result.user.adminId = userResult.adminId; // result.user.adminId = userResult.adminId;
} // }
} // }
return { // return {
session: result.session, // session: result.session,
...((result.user && { // ...((result.user && {
user: { // user: {
adminId: result.user.adminId, // adminId: result.user.adminId,
authId: result.user.id, // authId: result.user.id,
email: result.user.email, // email: result.user.email,
rol: result.user.rol, // rol: result.user.rol,
id: result.user.id, // id: result.user.id,
secret: result.user.secret, // secret: result.user.secret,
}, // },
}) || { // }) || {
user: null, // user: null,
}), // }),
}; // };
}; // };

View File

@@ -84,7 +84,22 @@ export const auth = betterAuth({
}, },
}, },
plugins: [organization()], plugins: [
organization({
async sendInvitationEmail(data, request) {
const inviteLink = `https://example.com/accept-invitation/${data.id}`;
// https://example.com/accept-invitation/8jlBi9Tb9isDb8mc8Sb85u1BaJYklKB2
// sendOrganizationInvitation({
// email: data.email,
// invitedByUsername: data.inviter.user.name,
// invitedByEmail: data.inviter.user.email,
// teamName: data.organization.name,
// inviteLink
// })
console.log("Invitation link", inviteLink);
},
}),
],
}); });
export const validateRequest = async (request: IncomingMessage) => { export const validateRequest = async (request: IncomingMessage) => {

View File

@@ -185,11 +185,11 @@ export const deployApplication = async ({
}); });
try { try {
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheApplications) { // if (admin.cleanupCacheApplications) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
if (application.sourceType === "github") { if (application.sourceType === "github") {
await cloneGithubRepository({ await cloneGithubRepository({
@@ -220,7 +220,7 @@ export const deployApplication = async ({
applicationName: application.name, applicationName: application.name,
applicationType: "application", applicationType: "application",
buildLink, buildLink,
userId: application.project.userId, organizationId: application.project.organizationId,
domains: application.domains, domains: application.domains,
}); });
} catch (error) { } catch (error) {
@@ -233,7 +233,7 @@ export const deployApplication = async ({
// @ts-ignore // @ts-ignore
errorMessage: error?.message || "Error building", errorMessage: error?.message || "Error building",
buildLink, buildLink,
userId: application.project.userId, organizationId: application.project.organizationId,
}); });
throw error; throw error;
@@ -260,11 +260,11 @@ export const rebuildApplication = async ({
}); });
try { try {
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheApplications) { // if (admin.cleanupCacheApplications) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
if (application.sourceType === "github") { if (application.sourceType === "github") {
await buildApplication(application, deployment.logPath); await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "gitlab") { } else if (application.sourceType === "gitlab") {
@@ -309,11 +309,11 @@ export const deployRemoteApplication = async ({
try { try {
if (application.serverId) { if (application.serverId) {
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheApplications) { // if (admin.cleanupCacheApplications) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
let command = "set -e;"; let command = "set -e;";
if (application.sourceType === "github") { if (application.sourceType === "github") {
command += await getGithubCloneCommand({ command += await getGithubCloneCommand({
@@ -352,7 +352,7 @@ export const deployRemoteApplication = async ({
applicationName: application.name, applicationName: application.name,
applicationType: "application", applicationType: "application",
buildLink, buildLink,
userId: application.project.userId, organizationId: application.project.organizationId,
domains: application.domains, domains: application.domains,
}); });
} catch (error) { } catch (error) {
@@ -376,7 +376,7 @@ export const deployRemoteApplication = async ({
// @ts-ignore // @ts-ignore
errorMessage: error?.message || "Error building", errorMessage: error?.message || "Error building",
buildLink, buildLink,
userId: application.project.userId, organizationId: application.project.organizationId,
}); });
throw error; throw error;
@@ -454,11 +454,11 @@ export const deployPreviewApplication = async ({
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`; application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
application.buildArgs = application.previewBuildArgs; application.buildArgs = application.previewBuildArgs;
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheOnPreviews) { // if (admin.cleanupCacheOnPreviews) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
if (application.sourceType === "github") { if (application.sourceType === "github") {
await cloneGithubRepository({ await cloneGithubRepository({
@@ -568,11 +568,11 @@ export const deployRemotePreviewApplication = async ({
application.buildArgs = application.previewBuildArgs; application.buildArgs = application.previewBuildArgs;
if (application.serverId) { if (application.serverId) {
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheOnPreviews) { // if (admin.cleanupCacheOnPreviews) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
let command = "set -e;"; let command = "set -e;";
if (application.sourceType === "github") { if (application.sourceType === "github") {
command += await getGithubCloneCommand({ command += await getGithubCloneCommand({
@@ -637,11 +637,11 @@ export const rebuildRemoteApplication = async ({
try { try {
if (application.serverId) { if (application.serverId) {
const admin = await findUserById(application.project.userId); // const admin = await findUserById(application.project.userId);
if (admin.cleanupCacheApplications) { // if (admin.cleanupCacheApplications) {
await cleanupFullDocker(application?.serverId); // await cleanupFullDocker(application?.serverId);
} // }
if (application.sourceType !== "docker") { if (application.sourceType !== "docker") {
let command = "set -e;"; let command = "set -e;";
command += getBuildCommand(application, deployment.logPath); command += getBuildCommand(application, deployment.logPath);

View File

@@ -217,10 +217,10 @@ export const deployCompose = async ({
}); });
try { try {
const admin = await findUserById(compose.project.userId); // const admin = await findUserById(compose.project.userId);
if (admin.cleanupCacheOnCompose) { // if (admin.cleanupCacheOnCompose) {
await cleanupFullDocker(compose?.serverId); // await cleanupFullDocker(compose?.serverId);
} // }
if (compose.sourceType === "github") { if (compose.sourceType === "github") {
await cloneGithubRepository({ await cloneGithubRepository({
...compose, ...compose,
@@ -247,7 +247,7 @@ export const deployCompose = async ({
applicationName: compose.name, applicationName: compose.name,
applicationType: "compose", applicationType: "compose",
buildLink, buildLink,
userId: compose.project.userId, organizationId: compose.project.organizationId,
domains: compose.domains, domains: compose.domains,
}); });
} catch (error) { } catch (error) {
@@ -262,7 +262,7 @@ export const deployCompose = async ({
// @ts-ignore // @ts-ignore
errorMessage: error?.message || "Error building", errorMessage: error?.message || "Error building",
buildLink, buildLink,
userId: compose.project.userId, organizationId: compose.project.organizationId,
}); });
throw error; throw error;
} }
@@ -286,10 +286,10 @@ export const rebuildCompose = async ({
}); });
try { try {
const admin = await findUserById(compose.project.userId); // const admin = await findUserById(compose.project.userId);
if (admin.cleanupCacheOnCompose) { // if (admin.cleanupCacheOnCompose) {
await cleanupFullDocker(compose?.serverId); // await cleanupFullDocker(compose?.serverId);
} // }
if (compose.serverId) { if (compose.serverId) {
await getBuildComposeCommand(compose, deployment.logPath); await getBuildComposeCommand(compose, deployment.logPath);
} else { } else {
@@ -332,10 +332,10 @@ export const deployRemoteCompose = async ({
}); });
try { try {
if (compose.serverId) { if (compose.serverId) {
const admin = await findUserById(compose.project.userId); // const admin = await findUserById(compose.project.userId);
if (admin.cleanupCacheOnCompose) { // if (admin.cleanupCacheOnCompose) {
await cleanupFullDocker(compose?.serverId); // await cleanupFullDocker(compose?.serverId);
} // }
let command = "set -e;"; let command = "set -e;";
if (compose.sourceType === "github") { if (compose.sourceType === "github") {
@@ -381,7 +381,7 @@ export const deployRemoteCompose = async ({
applicationName: compose.name, applicationName: compose.name,
applicationType: "compose", applicationType: "compose",
buildLink, buildLink,
userId: compose.project.userId, organizationId: compose.project.organizationId,
domains: compose.domains, domains: compose.domains,
}); });
} catch (error) { } catch (error) {
@@ -406,7 +406,7 @@ export const deployRemoteCompose = async ({
// @ts-ignore // @ts-ignore
errorMessage: error?.message || "Error building", errorMessage: error?.message || "Error building",
buildLink, buildLink,
userId: compose.project.userId, organizationId: compose.project.organizationId,
}); });
throw error; throw error;
} }
@@ -430,10 +430,10 @@ export const rebuildRemoteCompose = async ({
}); });
try { try {
const admin = await findUserById(compose.project.userId); // const admin = await findUserById(compose.project.userId);
if (admin.cleanupCacheOnCompose) { // if (admin.cleanupCacheOnCompose) {
await cleanupFullDocker(compose?.serverId); // await cleanupFullDocker(compose?.serverId);
} // }
if (compose.serverId) { if (compose.serverId) {
await getBuildComposeCommand(compose, deployment.logPath); await getBuildComposeCommand(compose, deployment.logPath);
} }

View File

@@ -2,6 +2,7 @@ import { db } from "@dokploy/server/db";
import { import {
type apiCreatePreviewDeployment, type apiCreatePreviewDeployment,
deployments, deployments,
organization,
previewDeployments, previewDeployments,
} from "@dokploy/server/db/schema"; } from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
@@ -154,11 +155,14 @@ export const createPreviewDeployment = async (
const application = await findApplicationById(schema.applicationId); const application = await findApplicationById(schema.applicationId);
const appName = `preview-${application.appName}-${generatePassword(6)}`; const appName = `preview-${application.appName}-${generatePassword(6)}`;
const org = await db.query.organization.findFirst({
where: eq(organization.id, application.project.organizationId),
});
const generateDomain = await generateWildcardDomain( const generateDomain = await generateWildcardDomain(
application.previewWildcard || "*.traefik.me", application.previewWildcard || "*.traefik.me",
appName, appName,
application.server?.ipAddress || "", application.server?.ipAddress || "",
application.project.userId, org?.ownerId || "",
); );
const octokit = authGithub(application?.github as Github); const octokit = authGithub(application?.github as Github);

View File

@@ -5,8 +5,6 @@ import {
execAsync, execAsync,
execAsyncRemote, execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync"; } from "@dokploy/server/utils/process/execAsync";
import { findAdminById } from "./admin";
// import packageInfo from "../../../package.json";
export interface IUpdateData { export interface IUpdateData {
latestVersion: string | null; latestVersion: string | null;

View File

@@ -20,17 +20,17 @@ export type User = typeof users_temp.$inferSelect;
// }; // };
export const findUserByAuthId = async (authId: string) => { export const findUserByAuthId = async (authId: string) => {
const userR = await db.query.user.findFirst({ // const userR = await db.query.user.findFirst({
where: eq(user.id, authId), // where: eq(user.id, authId),
with: {}, // with: {},
}); // });
if (!userR) { // if (!userR) {
throw new TRPCError({ // throw new TRPCError({
code: "NOT_FOUND", // code: "NOT_FOUND",
message: "User not found", // message: "User not found",
}); // });
} // }
return userR; // return userR;
}; };
export const findUsers = async (adminId: string) => { export const findUsers = async (adminId: string) => {

View File

@@ -18,7 +18,7 @@ interface Props {
applicationType: string; applicationType: string;
errorMessage: string; errorMessage: string;
buildLink: string; buildLink: string;
userId: string; organizationId: string;
} }
export const sendBuildErrorNotifications = async ({ export const sendBuildErrorNotifications = async ({
@@ -27,14 +27,14 @@ export const sendBuildErrorNotifications = async ({
applicationType, applicationType,
errorMessage, errorMessage,
buildLink, buildLink,
userId, organizationId,
}: Props) => { }: Props) => {
const date = new Date(); const date = new Date();
const unixDate = ~~(Number(date) / 1000); const unixDate = ~~(Number(date) / 1000);
const notificationList = await db.query.notifications.findMany({ const notificationList = await db.query.notifications.findMany({
where: and( where: and(
eq(notifications.appBuildError, true), eq(notifications.appBuildError, true),
eq(notifications.userId, userId), eq(notifications.organizationId, organizationId),
), ),
with: { with: {
email: true, email: true,

View File

@@ -18,7 +18,7 @@ interface Props {
applicationName: string; applicationName: string;
applicationType: string; applicationType: string;
buildLink: string; buildLink: string;
userId: string; organizationId: string;
domains: Domain[]; domains: Domain[];
} }
@@ -27,7 +27,7 @@ export const sendBuildSuccessNotifications = async ({
applicationName, applicationName,
applicationType, applicationType,
buildLink, buildLink,
userId, organizationId,
domains, domains,
}: Props) => { }: Props) => {
const date = new Date(); const date = new Date();
@@ -35,7 +35,7 @@ export const sendBuildSuccessNotifications = async ({
const notificationList = await db.query.notifications.findMany({ const notificationList = await db.query.notifications.findMany({
where: and( where: and(
eq(notifications.appDeploy, true), eq(notifications.appDeploy, true),
eq(notifications.userId, userId), eq(notifications.organizationId, organizationId),
), ),
with: { with: {
email: true, email: true,

View File

@@ -13,7 +13,7 @@ import {
} from "./utils"; } from "./utils";
export const sendDockerCleanupNotifications = async ( export const sendDockerCleanupNotifications = async (
userId: string, organizationId: string,
message = "Docker cleanup for dokploy", message = "Docker cleanup for dokploy",
) => { ) => {
const date = new Date(); const date = new Date();
@@ -21,7 +21,7 @@ export const sendDockerCleanupNotifications = async (
const notificationList = await db.query.notifications.findMany({ const notificationList = await db.query.notifications.findMany({
where: and( where: and(
eq(notifications.dockerCleanup, true), eq(notifications.dockerCleanup, true),
eq(notifications.userId, userId), eq(notifications.organizationId, organizationId),
), ),
with: { with: {
email: true, email: true,