mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: migrate authentication routes to user router and update related components
This commit continues the refactoring of authentication-related code by: - Moving authentication routes from `auth.ts` to `user.ts` - Updating import paths and function calls across components - Removing commented-out authentication code - Simplifying user-related queries and mutations - Updating server-side authentication handling
This commit is contained in:
@@ -61,7 +61,7 @@ export const Disable2FA = () => {
|
||||
}
|
||||
|
||||
toast.success("2FA disabled successfully");
|
||||
utils.auth.get.invalidate();
|
||||
utils.user.get.invalidate();
|
||||
setIsOpen(false);
|
||||
} catch (_error) {
|
||||
form.setError("password", {
|
||||
|
||||
@@ -125,7 +125,7 @@ export const Enable2FA = () => {
|
||||
}
|
||||
|
||||
toast.success("2FA configured successfully");
|
||||
utils.auth.get.invalidate();
|
||||
utils.user.get.invalidate();
|
||||
setIsDialogOpen(false);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const GenerateToken = () => {
|
||||
const { data, refetch } = api.user.get.useQuery();
|
||||
|
||||
const { mutateAsync: generateToken, isLoading: isLoadingToken } =
|
||||
api.auth.generateToken.useMutation();
|
||||
api.user.generateToken.useMutation();
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
@@ -51,7 +51,7 @@ export const GenerateToken = () => {
|
||||
<Label>Token</Label>
|
||||
<ToggleVisibilityInput
|
||||
placeholder="Token"
|
||||
value={data?.user?.token || ""}
|
||||
value={data || ""}
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
import { AlertBlock } from "@/components/shared/alert-block";
|
||||
import { DialogAction } from "@/components/shared/dialog-action";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
|
||||
const profileSchema = z.object({
|
||||
password: z.string().min(1, {
|
||||
message: "Password is required",
|
||||
}),
|
||||
});
|
||||
|
||||
type Profile = z.infer<typeof profileSchema>;
|
||||
|
||||
export const RemoveSelfAccount = () => {
|
||||
const { data } = api.user.get.useQuery();
|
||||
const { mutateAsync, isLoading, error, isError } =
|
||||
api.auth.removeSelfAccount.useMutation();
|
||||
const { t } = useTranslation("settings");
|
||||
const router = useRouter();
|
||||
|
||||
const form = useForm<Profile>({
|
||||
defaultValues: {
|
||||
password: "",
|
||||
},
|
||||
resolver: zodResolver(profileSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
form.reset({
|
||||
password: "",
|
||||
});
|
||||
}
|
||||
form.reset();
|
||||
}, [form, form.reset, data]);
|
||||
|
||||
const onSubmit = async (values: Profile) => {
|
||||
await mutateAsync({
|
||||
password: values.password,
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Profile Deleted");
|
||||
router.push("/");
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Card className="h-full bg-sidebar p-2.5 rounded-xl max-w-5xl mx-auto">
|
||||
<div className="rounded-xl bg-background shadow-md ">
|
||||
<CardHeader className="flex flex-row gap-2 flex-wrap justify-between items-center">
|
||||
<div>
|
||||
<CardTitle className="text-xl">Remove Self Account</CardTitle>
|
||||
<CardDescription>
|
||||
If you want to remove your account, you can do it here
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
{isError && <AlertBlock type="error">{error?.message}</AlertBlock>}
|
||||
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={(e) => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
className="grid gap-4"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("settings.profile.password")}</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder={t("settings.profile.password")}
|
||||
{...field}
|
||||
value={field.value || ""}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<div className="flex justify-end pt-2">
|
||||
<DialogAction
|
||||
title="Are you sure you want to remove your account?"
|
||||
description="This action cannot be undone, all your projects/servers will be deleted."
|
||||
onClick={() => form.handleSubmit(onSubmit)()}
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
isLoading={isLoading}
|
||||
variant="destructive"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</DialogAction>
|
||||
</div>
|
||||
</CardContent>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -89,7 +89,7 @@ export const SetupMonitoring = ({ serverId }: Props) => {
|
||||
enabled: !!serverId,
|
||||
},
|
||||
)
|
||||
: api.user.get.useQuery();
|
||||
: api.user.getServerMetrics.useQuery();
|
||||
|
||||
const url = useUrl();
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ import { UpdateServerButton } from "./update-server";
|
||||
import { UserNav } from "./user-nav";
|
||||
|
||||
// The types of the queries we are going to use
|
||||
type AuthQueryOutput = inferRouterOutputs<AppRouter>["auth"]["get"];
|
||||
type AuthQueryOutput = inferRouterOutputs<AppRouter>["user"]["get"];
|
||||
|
||||
type SingleNavItem = {
|
||||
isSingle?: true;
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { appRouter } from "@/server/api/root";
|
||||
import { createTRPCContext } from "@/server/api/trpc";
|
||||
import { validateBearerToken, validateRequest } from "@dokploy/server";
|
||||
import { validateRequest } from "@dokploy/server";
|
||||
import { createOpenApiNextHandler } from "@dokploy/trpc-openapi";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
let { session, user } = await validateBearerToken(req);
|
||||
|
||||
if (!session) {
|
||||
const cookieResult = await validateRequest(req, res);
|
||||
session = cookieResult.session;
|
||||
user = cookieResult.user;
|
||||
}
|
||||
const { session, user } = await validateRequest(req);
|
||||
|
||||
if (!user || !session) {
|
||||
res.status(401).json({ message: "Unauthorized" });
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function getServerSideProps(
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
|
||||
@@ -52,7 +52,7 @@ export async function getServerSideProps(
|
||||
transformer: superjson,
|
||||
});
|
||||
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
try {
|
||||
await helpers.project.all.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
@@ -209,7 +209,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { GenerateToken } from "@/components/dashboard/settings/profile/generate-token";
|
||||
import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form";
|
||||
import { RemoveSelfAccount } from "@/components/dashboard/settings/profile/remove-self-account";
|
||||
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
|
||||
|
||||
import { appRouter } from "@/server/api/root";
|
||||
@@ -15,14 +14,14 @@ import superjson from "superjson";
|
||||
const Page = () => {
|
||||
const { data } = api.user.get.useQuery();
|
||||
|
||||
const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||
// const { data: isCloud } = api.settings.isCloud.useQuery();
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="h-full rounded-xl max-w-5xl mx-auto flex flex-col gap-4">
|
||||
<ProfileForm />
|
||||
{(data?.canAccessToAPI || data?.role === "owner") && <GenerateToken />}
|
||||
|
||||
{isCloud && <RemoveSelfAccount />}
|
||||
{/* {isCloud && <RemoveSelfAccount />} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -53,15 +52,7 @@ export async function getServerSideProps(
|
||||
});
|
||||
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
await helpers.auth.get.prefetch();
|
||||
if (user?.role === "member") {
|
||||
// const userR = await helpers.user.one.fetch({
|
||||
// userId: user.id,
|
||||
// });
|
||||
// await helpers.user.byAuthId.prefetch({
|
||||
// authId: user.authId,
|
||||
// });
|
||||
}
|
||||
await helpers.user.get.prefetch();
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -110,7 +110,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
|
||||
return {
|
||||
props: {
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -50,7 +50,7 @@ export async function getServerSideProps(
|
||||
},
|
||||
transformer: superjson,
|
||||
});
|
||||
await helpers.auth.get.prefetch();
|
||||
await helpers.user.get.prefetch();
|
||||
await helpers.settings.isCloud.prefetch();
|
||||
|
||||
return {
|
||||
|
||||
@@ -12,17 +12,13 @@ import {
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { db } from "@/server/db";
|
||||
import { auth } from "@/server/db/schema";
|
||||
import { api } from "@/utils/api";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { IS_CLOUD } from "@dokploy/server";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { isBefore } from "date-fns";
|
||||
import { eq } from "drizzle-orm";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { type ReactElement, useEffect } from "react";
|
||||
import { type ReactElement, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod";
|
||||
@@ -54,11 +50,12 @@ const loginSchema = z
|
||||
type Login = z.infer<typeof loginSchema>;
|
||||
|
||||
interface Props {
|
||||
token: string;
|
||||
tokenResetPassword: string;
|
||||
}
|
||||
export default function Home({ token }: Props) {
|
||||
const { mutateAsync, isLoading, isError, error } =
|
||||
api.auth.resetPassword.useMutation();
|
||||
export default function Home({ tokenResetPassword }: Props) {
|
||||
const [token, setToken] = useState<string | null>(tokenResetPassword);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
const form = useForm<Login>({
|
||||
defaultValues: {
|
||||
@@ -68,26 +65,32 @@ export default function Home({ token }: Props) {
|
||||
resolver: zodResolver(loginSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const token = new URLSearchParams(window.location.search).get("token");
|
||||
|
||||
if (token) {
|
||||
setToken(token);
|
||||
}
|
||||
}, [token]);
|
||||
|
||||
useEffect(() => {
|
||||
form.reset();
|
||||
}, [form, form.reset, form.formState.isSubmitSuccessful]);
|
||||
|
||||
const onSubmit = async (values: Login) => {
|
||||
await mutateAsync({
|
||||
resetPasswordToken: token,
|
||||
password: values.password,
|
||||
})
|
||||
.then((_data) => {
|
||||
toast.success("Password reset successfully", {
|
||||
duration: 2000,
|
||||
});
|
||||
router.push("/");
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error resetting password", {
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
setIsLoading(true);
|
||||
const { error } = await authClient.resetPassword({
|
||||
newPassword: values.password,
|
||||
token: token || "",
|
||||
});
|
||||
|
||||
if (error) {
|
||||
setError(error.message || "An error occurred");
|
||||
} else {
|
||||
toast.success("Password reset successfully");
|
||||
router.push("/");
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
return (
|
||||
<div className="flex h-screen w-full items-center justify-center ">
|
||||
@@ -104,9 +107,9 @@ export default function Home({ token }: Props) {
|
||||
|
||||
<div className="w-full">
|
||||
<CardContent className="p-0">
|
||||
{isError && (
|
||||
{error && (
|
||||
<AlertBlock type="error" className="my-2">
|
||||
{error?.message}
|
||||
{error}
|
||||
</AlertBlock>
|
||||
)}
|
||||
<Form {...form}>
|
||||
@@ -194,35 +197,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
};
|
||||
}
|
||||
|
||||
const authR = await db.query.auth.findFirst({
|
||||
where: eq(auth.resetPasswordToken, token),
|
||||
});
|
||||
|
||||
if (!authR || authR?.resetPasswordExpiresAt === null) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
const isExpired = isBefore(
|
||||
new Date(authR.resetPasswordExpiresAt),
|
||||
new Date(),
|
||||
);
|
||||
|
||||
if (isExpired) {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: true,
|
||||
destination: "/",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
token: authR.resetPasswordToken,
|
||||
tokenResetPassword: token,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { api } from "@/utils/api";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { IS_CLOUD } from "@dokploy/server";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
@@ -46,8 +46,9 @@ export default function Home() {
|
||||
is2FAEnabled: false,
|
||||
authId: "",
|
||||
});
|
||||
const { mutateAsync, isLoading, isError, error } =
|
||||
api.auth.sendResetPasswordEmail.useMutation();
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const _router = useRouter();
|
||||
const form = useForm<Login>({
|
||||
defaultValues: {
|
||||
@@ -61,19 +62,20 @@ export default function Home() {
|
||||
}, [form, form.reset, form.formState.isSubmitSuccessful]);
|
||||
|
||||
const onSubmit = async (values: Login) => {
|
||||
await mutateAsync({
|
||||
setIsLoading(true);
|
||||
const { error } = await authClient.forgetPassword({
|
||||
email: values.email,
|
||||
})
|
||||
.then((_data) => {
|
||||
toast.success("Email sent", {
|
||||
duration: 2000,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error sending email", {
|
||||
duration: 2000,
|
||||
});
|
||||
redirectTo: "/reset-password",
|
||||
});
|
||||
if (error) {
|
||||
setError(error.message || "An error occurred");
|
||||
setIsLoading(false);
|
||||
} else {
|
||||
toast.success("Email sent", {
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
return (
|
||||
<div className="flex w-full items-center justify-center ">
|
||||
@@ -89,9 +91,9 @@ export default function Home() {
|
||||
|
||||
<div className="mx-auto w-full max-w-lg bg-transparent ">
|
||||
<CardContent className="p-0">
|
||||
{isError && (
|
||||
{error && (
|
||||
<AlertBlock type="error" className="my-2">
|
||||
{error?.message}
|
||||
{error}
|
||||
</AlertBlock>
|
||||
)}
|
||||
{!temp.is2FAEnabled ? (
|
||||
|
||||
@@ -31,294 +31,268 @@ import {
|
||||
} from "../trpc";
|
||||
|
||||
export const authRouter = createTRPCRouter({
|
||||
createAdmin: publicProcedure.mutation(async ({ input }) => {
|
||||
try {
|
||||
if (!IS_CLOUD) {
|
||||
const admin = await db.query.admins.findFirst({});
|
||||
if (admin) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Admin already exists",
|
||||
});
|
||||
}
|
||||
}
|
||||
const newAdmin = await createAdmin(input);
|
||||
|
||||
if (IS_CLOUD) {
|
||||
await sendDiscordNotificationWelcome(newAdmin);
|
||||
await sendVerificationEmail(newAdmin.id);
|
||||
return {
|
||||
status: "success",
|
||||
type: "cloud",
|
||||
};
|
||||
}
|
||||
// const session = await lucia.createSession(newAdmin.id || "", {});
|
||||
// ctx.res.appendHeader(
|
||||
// "Set-Cookie",
|
||||
// lucia.createSessionCookie(session.id).serialize(),
|
||||
// );
|
||||
return {
|
||||
status: "success",
|
||||
type: "selfhosted",
|
||||
};
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
// @ts-ignore
|
||||
message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`,
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
createUser: publicProcedure.mutation(async ({ input }) => {
|
||||
try {
|
||||
const _token = await getUserByToken(input.token);
|
||||
// if (token.isExpired) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Invalid token",
|
||||
// });
|
||||
// }
|
||||
|
||||
// const newUser = await createUser(input);
|
||||
|
||||
// if (IS_CLOUD) {
|
||||
// await sendVerificationEmail(token.authId);
|
||||
// return true;
|
||||
// }
|
||||
// const session = await lucia.createSession(newUser?.authId || "", {});
|
||||
// ctx.res.appendHeader(
|
||||
// "Set-Cookie",
|
||||
// lucia.createSessionCookie(session.id).serialize(),
|
||||
// );
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error creating the user",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
login: publicProcedure.mutation(async ({ input }) => {
|
||||
try {
|
||||
const auth = await findAuthByEmail(input.email);
|
||||
|
||||
const correctPassword = bcrypt.compareSync(
|
||||
input.password,
|
||||
auth?.password || "",
|
||||
);
|
||||
|
||||
if (!correctPassword) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Credentials do not match",
|
||||
});
|
||||
}
|
||||
|
||||
if (auth?.confirmationToken && IS_CLOUD) {
|
||||
await sendVerificationEmail(auth.id);
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message:
|
||||
"Email not confirmed, we have sent you a confirmation email please check your inbox.",
|
||||
});
|
||||
}
|
||||
|
||||
if (auth?.is2FAEnabled) {
|
||||
return {
|
||||
is2FAEnabled: true,
|
||||
authId: auth.id,
|
||||
};
|
||||
}
|
||||
|
||||
// const session = await lucia.createSession(auth?.id || "", {});
|
||||
|
||||
// ctx.res.appendHeader(
|
||||
// "Set-Cookie",
|
||||
// lucia.createSessionCookie(session.id).serialize(),
|
||||
// );
|
||||
return {
|
||||
is2FAEnabled: false,
|
||||
authId: auth?.id,
|
||||
};
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Error: ${error instanceof Error ? error.message : "Login error"}`,
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
get: protectedProcedure.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(member.userId, ctx.user.id),
|
||||
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
|
||||
),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
return memberResult;
|
||||
}),
|
||||
|
||||
logout: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
const { req } = ctx;
|
||||
const { session } = await validateRequest(req);
|
||||
if (!session) return false;
|
||||
|
||||
// await lucia.invalidateSession(session.id);
|
||||
// res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
||||
return true;
|
||||
}),
|
||||
|
||||
update: protectedProcedure.mutation(async ({ ctx, input }) => {
|
||||
const currentAuth = await findAuthByEmail(ctx.user.email);
|
||||
|
||||
if (input.currentPassword || input.password) {
|
||||
const correctPassword = bcrypt.compareSync(
|
||||
input.currentPassword || "",
|
||||
currentAuth?.password || "",
|
||||
);
|
||||
if (!correctPassword) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Current password is incorrect",
|
||||
});
|
||||
}
|
||||
}
|
||||
// const auth = await updateAuthById(ctx.user.authId, {
|
||||
// ...(input.email && { email: input.email.toLowerCase() }),
|
||||
// ...(input.password && {
|
||||
// password: bcrypt.hashSync(input.password, 10),
|
||||
// }),
|
||||
// ...(input.image && { image: input.image }),
|
||||
// });
|
||||
|
||||
return auth;
|
||||
}),
|
||||
removeSelfAccount: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
password: z.string().min(1),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
if (!IS_CLOUD) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "This feature is only available in the cloud version",
|
||||
});
|
||||
}
|
||||
const currentAuth = await findAuthByEmail(ctx.user.email);
|
||||
|
||||
const correctPassword = bcrypt.compareSync(
|
||||
input.password,
|
||||
currentAuth?.password || "",
|
||||
);
|
||||
|
||||
if (!correctPassword) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Password is incorrect",
|
||||
});
|
||||
}
|
||||
const { req } = ctx;
|
||||
const { session } = await validateRequest(req);
|
||||
if (!session) return false;
|
||||
|
||||
// await lucia.invalidateSession(session.id);
|
||||
// res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
||||
|
||||
// if (ctx.user.rol === "owner") {
|
||||
// await removeAdminByAuthId(ctx.user.authId);
|
||||
// } else {
|
||||
// await removeUserByAuthId(ctx.user.authId);
|
||||
// }
|
||||
|
||||
return true;
|
||||
}),
|
||||
|
||||
generateToken: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
const auth = await findUserById(ctx.user.id);
|
||||
console.log(auth);
|
||||
|
||||
// if (auth.token) {
|
||||
// await luciaToken.invalidateSession(auth.token);
|
||||
// }
|
||||
// const session = await luciaToken.createSession(auth?.id || "", {
|
||||
// expiresIn: 60 * 60 * 24 * 30,
|
||||
// });
|
||||
// await updateUser(auth.id, {
|
||||
// token: session.id,
|
||||
// });
|
||||
return auth;
|
||||
}),
|
||||
verifyToken: protectedProcedure.mutation(async () => {
|
||||
return true;
|
||||
}),
|
||||
one: adminProcedure
|
||||
.input(z.object({ userId: z.string().min(1) }))
|
||||
.query(async ({ input }) => {
|
||||
// TODO: Check if the user is admin or member
|
||||
const user = await findUserById(input.userId);
|
||||
return user;
|
||||
}),
|
||||
sendResetPasswordEmail: publicProcedure
|
||||
.input(
|
||||
z.object({
|
||||
email: z.string().min(1).email(),
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
if (!IS_CLOUD) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "This feature is only available in the cloud version",
|
||||
});
|
||||
}
|
||||
const authR = await db.query.auth.findFirst({
|
||||
where: eq(auth.email, input.email),
|
||||
});
|
||||
if (!authR) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
const token = nanoid();
|
||||
await updateAuthById(authR.id, {
|
||||
resetPasswordToken: token,
|
||||
// Make resetPassword in 24 hours
|
||||
resetPasswordExpiresAt: new Date(
|
||||
new Date().getTime() + 24 * 60 * 60 * 1000,
|
||||
).toISOString(),
|
||||
});
|
||||
|
||||
await sendEmailNotification(
|
||||
{
|
||||
fromAddress: process.env.SMTP_FROM_ADDRESS!,
|
||||
toAddresses: [authR.email],
|
||||
smtpServer: process.env.SMTP_SERVER!,
|
||||
smtpPort: Number(process.env.SMTP_PORT),
|
||||
username: process.env.SMTP_USERNAME!,
|
||||
password: process.env.SMTP_PASSWORD!,
|
||||
},
|
||||
"Reset Password",
|
||||
`
|
||||
Reset your password by clicking the link below:
|
||||
The link will expire in 24 hours.
|
||||
<a href="${WEBSITE_URL}/reset-password?token=${token}">
|
||||
Reset Password
|
||||
</a>
|
||||
|
||||
`,
|
||||
);
|
||||
}),
|
||||
// createAdmin: publicProcedure.mutation(async ({ input }) => {
|
||||
// try {
|
||||
// if (!IS_CLOUD) {
|
||||
// const admin = await db.query.admins.findFirst({});
|
||||
// if (admin) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Admin already exists",
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// const newAdmin = await createAdmin(input);
|
||||
// if (IS_CLOUD) {
|
||||
// await sendDiscordNotificationWelcome(newAdmin);
|
||||
// await sendVerificationEmail(newAdmin.id);
|
||||
// return {
|
||||
// status: "success",
|
||||
// type: "cloud",
|
||||
// };
|
||||
// }
|
||||
// // const session = await lucia.createSession(newAdmin.id || "", {});
|
||||
// // ctx.res.appendHeader(
|
||||
// // "Set-Cookie",
|
||||
// // lucia.createSessionCookie(session.id).serialize(),
|
||||
// // );
|
||||
// return {
|
||||
// status: "success",
|
||||
// type: "selfhosted",
|
||||
// };
|
||||
// } catch (error) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// // @ts-ignore
|
||||
// message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`,
|
||||
// cause: error,
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// createUser: publicProcedure.mutation(async ({ input }) => {
|
||||
// try {
|
||||
// const _token = await getUserByToken(input.token);
|
||||
// // if (token.isExpired) {
|
||||
// // throw new TRPCError({
|
||||
// // code: "BAD_REQUEST",
|
||||
// // message: "Invalid token",
|
||||
// // });
|
||||
// // }
|
||||
// // const newUser = await createUser(input);
|
||||
// // if (IS_CLOUD) {
|
||||
// // await sendVerificationEmail(token.authId);
|
||||
// // return true;
|
||||
// // }
|
||||
// // const session = await lucia.createSession(newUser?.authId || "", {});
|
||||
// // ctx.res.appendHeader(
|
||||
// // "Set-Cookie",
|
||||
// // lucia.createSessionCookie(session.id).serialize(),
|
||||
// // );
|
||||
// return true;
|
||||
// } catch (error) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Error creating the user",
|
||||
// cause: error,
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// login: publicProcedure.mutation(async ({ input }) => {
|
||||
// try {
|
||||
// const auth = await findAuthByEmail(input.email);
|
||||
// const correctPassword = bcrypt.compareSync(
|
||||
// input.password,
|
||||
// auth?.password || "",
|
||||
// );
|
||||
// if (!correctPassword) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Credentials do not match",
|
||||
// });
|
||||
// }
|
||||
// if (auth?.confirmationToken && IS_CLOUD) {
|
||||
// await sendVerificationEmail(auth.id);
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message:
|
||||
// "Email not confirmed, we have sent you a confirmation email please check your inbox.",
|
||||
// });
|
||||
// }
|
||||
// if (auth?.is2FAEnabled) {
|
||||
// return {
|
||||
// is2FAEnabled: true,
|
||||
// authId: auth.id,
|
||||
// };
|
||||
// }
|
||||
// // const session = await lucia.createSession(auth?.id || "", {});
|
||||
// // ctx.res.appendHeader(
|
||||
// // "Set-Cookie",
|
||||
// // lucia.createSessionCookie(session.id).serialize(),
|
||||
// // );
|
||||
// return {
|
||||
// is2FAEnabled: false,
|
||||
// authId: auth?.id,
|
||||
// };
|
||||
// } catch (error) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: `Error: ${error instanceof Error ? error.message : "Login error"}`,
|
||||
// cause: error,
|
||||
// });
|
||||
// }
|
||||
// }),
|
||||
// get: protectedProcedure.query(async ({ ctx }) => {
|
||||
// const memberResult = await db.query.member.findFirst({
|
||||
// where: and(
|
||||
// eq(member.userId, ctx.user.id),
|
||||
// eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
|
||||
// ),
|
||||
// with: {
|
||||
// user: true,
|
||||
// },
|
||||
// });
|
||||
// return memberResult;
|
||||
// }),
|
||||
// logout: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
// const { req } = ctx;
|
||||
// const { session } = await validateRequest(req);
|
||||
// if (!session) return false;
|
||||
// // await lucia.invalidateSession(session.id);
|
||||
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
||||
// return true;
|
||||
// }),
|
||||
// update: protectedProcedure.mutation(async ({ ctx, input }) => {
|
||||
// const currentAuth = await findAuthByEmail(ctx.user.email);
|
||||
// if (input.currentPassword || input.password) {
|
||||
// const correctPassword = bcrypt.compareSync(
|
||||
// input.currentPassword || "",
|
||||
// currentAuth?.password || "",
|
||||
// );
|
||||
// if (!correctPassword) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Current password is incorrect",
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// // const auth = await updateAuthById(ctx.user.authId, {
|
||||
// // ...(input.email && { email: input.email.toLowerCase() }),
|
||||
// // ...(input.password && {
|
||||
// // password: bcrypt.hashSync(input.password, 10),
|
||||
// // }),
|
||||
// // ...(input.image && { image: input.image }),
|
||||
// // });
|
||||
// return auth;
|
||||
// }),
|
||||
// removeSelfAccount: protectedProcedure
|
||||
// .input(
|
||||
// z.object({
|
||||
// password: z.string().min(1),
|
||||
// }),
|
||||
// )
|
||||
// .mutation(async ({ ctx, input }) => {
|
||||
// if (!IS_CLOUD) {
|
||||
// throw new TRPCError({
|
||||
// code: "NOT_FOUND",
|
||||
// message: "This feature is only available in the cloud version",
|
||||
// });
|
||||
// }
|
||||
// const currentAuth = await findAuthByEmail(ctx.user.email);
|
||||
// const correctPassword = bcrypt.compareSync(
|
||||
// input.password,
|
||||
// currentAuth?.password || "",
|
||||
// );
|
||||
// if (!correctPassword) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Password is incorrect",
|
||||
// });
|
||||
// }
|
||||
// const { req } = ctx;
|
||||
// const { session } = await validateRequest(req);
|
||||
// if (!session) return false;
|
||||
// // await lucia.invalidateSession(session.id);
|
||||
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
||||
// // if (ctx.user.rol === "owner") {
|
||||
// // await removeAdminByAuthId(ctx.user.authId);
|
||||
// // } else {
|
||||
// // await removeUserByAuthId(ctx.user.authId);
|
||||
// // }
|
||||
// return true;
|
||||
// }),
|
||||
// generateToken: protectedProcedure.mutation(async ({ ctx }) => {
|
||||
// const auth = await findUserById(ctx.user.id);
|
||||
// console.log(auth);
|
||||
// // if (auth.token) {
|
||||
// // await luciaToken.invalidateSession(auth.token);
|
||||
// // }
|
||||
// // const session = await luciaToken.createSession(auth?.id || "", {
|
||||
// // expiresIn: 60 * 60 * 24 * 30,
|
||||
// // });
|
||||
// // await updateUser(auth.id, {
|
||||
// // token: session.id,
|
||||
// // });
|
||||
// return auth;
|
||||
// }),
|
||||
// verifyToken: protectedProcedure.mutation(async () => {
|
||||
// return true;
|
||||
// }),
|
||||
// one: adminProcedure
|
||||
// .input(z.object({ userId: z.string().min(1) }))
|
||||
// .query(async ({ input }) => {
|
||||
// // TODO: Check if the user is admin or member
|
||||
// const user = await findUserById(input.userId);
|
||||
// return user;
|
||||
// }),
|
||||
// sendResetPasswordEmail: publicProcedure
|
||||
// .input(
|
||||
// z.object({
|
||||
// email: z.string().min(1).email(),
|
||||
// }),
|
||||
// )
|
||||
// .mutation(async ({ input }) => {
|
||||
// if (!IS_CLOUD) {
|
||||
// throw new TRPCError({
|
||||
// code: "NOT_FOUND",
|
||||
// message: "This feature is only available in the cloud version",
|
||||
// });
|
||||
// }
|
||||
// const authR = await db.query.auth.findFirst({
|
||||
// where: eq(auth.email, input.email),
|
||||
// });
|
||||
// if (!authR) {
|
||||
// throw new TRPCError({
|
||||
// code: "NOT_FOUND",
|
||||
// message: "User not found",
|
||||
// });
|
||||
// }
|
||||
// const token = nanoid();
|
||||
// await updateAuthById(authR.id, {
|
||||
// resetPasswordToken: token,
|
||||
// // Make resetPassword in 24 hours
|
||||
// resetPasswordExpiresAt: new Date(
|
||||
// new Date().getTime() + 24 * 60 * 60 * 1000,
|
||||
// ).toISOString(),
|
||||
// });
|
||||
// await sendEmailNotification(
|
||||
// {
|
||||
// fromAddress: process.env.SMTP_FROM_ADDRESS!,
|
||||
// toAddresses: [authR.email],
|
||||
// smtpServer: process.env.SMTP_SERVER!,
|
||||
// smtpPort: Number(process.env.SMTP_PORT),
|
||||
// username: process.env.SMTP_USERNAME!,
|
||||
// password: process.env.SMTP_PASSWORD!,
|
||||
// },
|
||||
// "Reset Password",
|
||||
// `
|
||||
// Reset your password by clicking the link below:
|
||||
// The link will expire in 24 hours.
|
||||
// <a href="${WEBSITE_URL}/reset-password?token=${token}">
|
||||
// Reset Password
|
||||
// </a>
|
||||
// `,
|
||||
// );
|
||||
// }),
|
||||
});
|
||||
|
||||
// export const sendVerificationEmail = async (authId: string) => {
|
||||
|
||||
@@ -65,6 +65,19 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
return memberResult;
|
||||
}),
|
||||
getServerMetrics: protectedProcedure.query(async ({ ctx }) => {
|
||||
const memberResult = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(member.userId, ctx.user.id),
|
||||
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
|
||||
),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
return memberResult?.user;
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.input(apiUpdateUser)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
@@ -112,7 +125,6 @@ export const userRouter = createTRPCRouter({
|
||||
|
||||
const { id, ...rest } = input;
|
||||
|
||||
console.log(rest);
|
||||
await db
|
||||
.update(member)
|
||||
.set({
|
||||
@@ -202,4 +214,8 @@ export const userRouter = createTRPCRouter({
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
|
||||
generateToken: protectedProcedure.mutation(async () => {
|
||||
return "token";
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import { organization, twoFactor } from "better-auth/plugins";
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema";
|
||||
import { sendVerificationEmail } from "../verification/send-verification-email";
|
||||
import { sendEmail } from "../verification/send-verification-email";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
|
||||
export const auth = betterAuth({
|
||||
@@ -30,7 +30,11 @@ export const auth = betterAuth({
|
||||
autoSignInAfterVerification: true,
|
||||
sendVerificationEmail: async ({ user, url }) => {
|
||||
console.log("Sending verification email to", user.email);
|
||||
await sendVerificationEmail(user.email, url);
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: "Verify your email",
|
||||
text: `Click the link to verify your email: ${url}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
emailAndPassword: {
|
||||
@@ -45,6 +49,13 @@ export const auth = betterAuth({
|
||||
return bcrypt.compareSync(password, hash);
|
||||
},
|
||||
},
|
||||
sendResetPassword: async ({ user, url }) => {
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: "Reset your password",
|
||||
text: `Click the link to reset your password: ${url}`,
|
||||
});
|
||||
},
|
||||
},
|
||||
databaseHooks: {
|
||||
user: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
type apiCreateUserInvitation,
|
||||
invitation,
|
||||
member,
|
||||
organization,
|
||||
@@ -10,42 +9,6 @@ import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
|
||||
export type User = typeof users_temp.$inferSelect;
|
||||
export const createInvitation = async (
|
||||
_input: typeof apiCreateUserInvitation._type,
|
||||
_adminId: string,
|
||||
) => {
|
||||
// await db.transaction(async (tx) => {
|
||||
// const result = await tx
|
||||
// .insert(auth)
|
||||
// .values({
|
||||
// email: input.email.toLowerCase(),
|
||||
// rol: "user",
|
||||
// password: bcrypt.hashSync("01231203012312", 10),
|
||||
// })
|
||||
// .returning()
|
||||
// .then((res) => res[0]);
|
||||
// if (!result) {
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Error creating the user",
|
||||
// });
|
||||
// }
|
||||
// const expiresIn24Hours = new Date();
|
||||
// expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1);
|
||||
// const token = randomBytes(32).toString("hex");
|
||||
// await tx
|
||||
// .insert(users)
|
||||
// .values({
|
||||
// adminId: adminId,
|
||||
// authId: result.id,
|
||||
// token,
|
||||
// expirationDate: expiresIn24Hours.toISOString(),
|
||||
// })
|
||||
// .returning();
|
||||
// });
|
||||
};
|
||||
|
||||
export const findUserById = async (userId: string) => {
|
||||
const user = await db.query.users_temp.findFirst({
|
||||
where: eq(users_temp.id, userId),
|
||||
@@ -69,34 +32,6 @@ export const findOrganizationById = async (organizationId: string) => {
|
||||
return organizationResult;
|
||||
};
|
||||
|
||||
export const updateUser = async (userId: string, userData: Partial<User>) => {
|
||||
const user = await db
|
||||
.update(users_temp)
|
||||
.set({
|
||||
...userData,
|
||||
})
|
||||
.where(eq(users_temp.id, userId))
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export const updateAdminById = async (
|
||||
_adminId: string,
|
||||
_adminData: Partial<User>,
|
||||
) => {
|
||||
// const admin = await db
|
||||
// .update(admins)
|
||||
// .set({
|
||||
// ...adminData,
|
||||
// })
|
||||
// .where(eq(admins.adminId, adminId))
|
||||
// .returning()
|
||||
// .then((res) => res[0]);
|
||||
// return admin;
|
||||
};
|
||||
|
||||
export const isAdminPresent = async () => {
|
||||
const admin = await db.query.member.findFirst({
|
||||
where: eq(member.role, "owner"),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { member, type users_temp } from "@dokploy/server/db/schema";
|
||||
import { member, users_temp } from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
|
||||
@@ -235,3 +235,16 @@ export const findMemberById = async (
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const updateUser = async (userId: string, userData: Partial<User>) => {
|
||||
const user = await db
|
||||
.update(users_temp)
|
||||
.set({
|
||||
...userData,
|
||||
})
|
||||
.where(eq(users_temp.id, userId))
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ class LogRotationManager {
|
||||
private async getStateFromDB(): Promise<boolean> {
|
||||
// const setting = await db.query.admins.findFirst({});
|
||||
// return setting?.enableLogRotation ?? false;
|
||||
return false;
|
||||
}
|
||||
|
||||
private async setStateInDB(_active: boolean): Promise<void> {
|
||||
|
||||
@@ -11,13 +11,14 @@ import { runMariadbBackup } from "./mariadb";
|
||||
import { runMongoBackup } from "./mongo";
|
||||
import { runMySqlBackup } from "./mysql";
|
||||
import { runPostgresBackup } from "./postgres";
|
||||
import { findAdmin } from "../../services/admin";
|
||||
|
||||
export const initCronJobs = async () => {
|
||||
console.log("Setting up cron jobs....");
|
||||
|
||||
const admin = await findAdmin();
|
||||
|
||||
if (admin?.enableDockerCleanup) {
|
||||
if (admin?.user.enableDockerCleanup) {
|
||||
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
||||
console.log(
|
||||
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
|
||||
@@ -25,7 +26,7 @@ export const initCronJobs = async () => {
|
||||
await cleanUpUnusedImages();
|
||||
await cleanUpDockerBuilder();
|
||||
await cleanUpSystemPrune();
|
||||
await sendDockerCleanupNotifications(admin.adminId);
|
||||
await sendDockerCleanupNotifications(admin.user.id);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,7 +43,7 @@ export const initCronJobs = async () => {
|
||||
await cleanUpDockerBuilder(serverId);
|
||||
await cleanUpSystemPrune(serverId);
|
||||
await sendDockerCleanupNotifications(
|
||||
admin.adminId,
|
||||
admin.user.id,
|
||||
`Docker cleanup for Server ${name} (${serverId})`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,7 +2,15 @@ import {
|
||||
sendDiscordNotification,
|
||||
sendEmailNotification,
|
||||
} from "../utils/notifications/utils";
|
||||
export const sendVerificationEmail = async (email: string, url: string) => {
|
||||
export const sendEmail = async ({
|
||||
email,
|
||||
subject,
|
||||
text,
|
||||
}: {
|
||||
email: string;
|
||||
subject: string;
|
||||
text: string;
|
||||
}) => {
|
||||
await sendEmailNotification(
|
||||
{
|
||||
fromAddress: process.env.SMTP_FROM_ADDRESS || "",
|
||||
@@ -12,14 +20,8 @@ export const sendVerificationEmail = async (email: string, url: string) => {
|
||||
username: process.env.SMTP_USERNAME || "",
|
||||
password: process.env.SMTP_PASSWORD || "",
|
||||
},
|
||||
"Confirm your email | Dokploy",
|
||||
`
|
||||
Welcome to Dokploy!
|
||||
Please confirm your email by clicking the link below:
|
||||
<a href="${url}">
|
||||
Confirm Email
|
||||
</a>
|
||||
`,
|
||||
subject,
|
||||
text,
|
||||
);
|
||||
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user