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:
Mauricio Siu
2025-02-22 22:02:12 -06:00
parent b00c12965a
commit 0478419f7c
30 changed files with 394 additions and 615 deletions

View File

@@ -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", {

View File

@@ -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) {

View File

@@ -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>

View File

@@ -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>
);
};

View File

@@ -89,7 +89,7 @@ export const SetupMonitoring = ({ serverId }: Props) => {
enabled: !!serverId,
},
)
: api.user.get.useQuery();
: api.user.getServerMetrics.useQuery();
const url = useUrl();

View File

@@ -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;

View File

@@ -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" });

View File

@@ -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: {

View File

@@ -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();

View File

@@ -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 {

View File

@@ -53,7 +53,7 @@ export async function getServerSideProps(
},
transformer: superjson,
});
await helpers.auth.get.prefetch();
await helpers.user.get.prefetch();
return {
props: {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -209,7 +209,7 @@ export async function getServerSideProps(
},
transformer: superjson,
});
await helpers.auth.get.prefetch();
await helpers.user.get.prefetch();
return {
props: {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -110,7 +110,7 @@ export async function getServerSideProps(
},
transformer: superjson,
});
await helpers.auth.get.prefetch();
await helpers.user.get.prefetch();
return {
props: {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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,
},
};
}

View File

@@ -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 ? (

View File

@@ -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) => {

View File

@@ -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";
}),
});