chore:lint

This commit is contained in:
Bereket Engida
2024-10-30 09:20:44 +03:00
parent 5d1a6772e8
commit 5e0edd35c2
21 changed files with 872 additions and 772 deletions

View File

@@ -267,32 +267,129 @@ export const Icons = {
height="1.2em"
viewBox="0 0 100 100"
>
<mask id="a" style={{maskType: "alpha"}} maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
<circle cx="50" cy="50" r="50" className="fill-foreground"/>
<mask
id="a"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<circle cx="50" cy="50" r="50" className="fill-foreground" />
</mask>
<g mask="url(#a)">
<circle cx="11" cy="119" r="52" className="fill-muted-foreground stroke-foreground" strokeWidth="4"/>
<circle cx="10" cy="125" r="52" className="fill-muted-foreground stroke-foreground" strokeWidth="4"/>
<circle cx="9" cy="131" r="52" className="fill-muted-foreground stroke-muted-foreground" strokeWidth="4"/>
<circle cx="88" cy="119" r="52" className="fill-muted-foreground stroke-foreground" strokeWidth="4"/>
<path className="fill-foreground" d="M89 35h2v5h-2zM83 34l2 1-1 4h-2zM77 31l2 1-3 4-2-1zM73 27l1 1-3 4-1-2zM70 23l1 1-4 3-1-2zM68 18v2l-4 1-1-2zM68 11l1 2-5 1-1-2zM69 6v2h-5V6z"/>
<circle cx="89" cy="125" r="52" className="fill-muted-foreground stroke-foreground" strokeWidth="4"/>
<circle cx="90" cy="131" r="52" className="fill-muted-foreground stroke-muted-foreground" strokeWidth="4"/>
<ellipse cx="49.5" cy="119" rx="41.5" ry="51" className="fill-muted-foreground"/>
<path d="M34 38v-9c1 1 2 4 5 6l7 30-8 2c-1-23-2-23-4-29Z" className="fill-foreground stroke-muted-foreground"/>
<path fillRule="evenodd" clipRule="evenodd" d="M95 123c0 31-20 57-45 57S5 154 5 123c0-27 14-50 33-56l12-2c25 0 45 26 45 58Zm-45 47c22 0 39-22 39-50S72 70 50 70s-39 22-39 50 17 50 39 50Z" className="fill-foreground"/>
<path d="M34 29c-4-8-11-5-14-4 2 3 5 4 9 4h5Z" className="fill-foreground stroke-muted-foreground"/>
<path d="M25 38c-1 6 0 14 2 18 5-7 7-13 7-18v-9c-5 1-7 5-9 9Z" className="fill-muted-foreground"/>
<path d="M34 29c-1 3-5 11-5 16m5-16c-5 1-7 5-9 9-1 6 0 14 2 18 5-7 7-13 7-18v-9Z" className="stroke-muted-foreground"/>
<path d="M44 18c-10 1-11 7-10 11l4-3c5-4 6-7 6-8Z" className="fill-foreground stroke-muted-foreground"/>
<path d="M34 29h7l18 4c-3-6-9-14-21-7l-4 3Z" className="fill-foreground"/>
<path d="M34 29c4-2 12-5 18-1m-18 1h7l18 4c-3-6-9-14-21-7l-4 3Z" className="stroke-muted-foreground"/>
<path d="M32 29a1189 1189 0 0 1-16 19c0-17 7-18 13-19h5a14 14 0 0 1-2 0Z" className="fill-foreground"/>
<path d="M34 29c-5 1-7 5-9 9l-9 10c0-17 7-18 13-19h5Zm0 0c-5 2-11 3-14 10" className="stroke-muted-foreground"/>
<path d="M41 29c9 2 13 10 15 14a25 25 0 0 1-22-14h7Z" className="fill-foreground"/>
<path d="M34 29c3 1 11 5 15 9m-15-9h7c9 2 13 10 15 14a25 25 0 0 1-22-14Z" className="stroke-muted-foreground"/>
<circle cx="91.5" cy="12.5" r="18.5" className="fill-foreground stroke-muted-foreground" strokeWidth="2"/>
</g>
<circle
cx="11"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="10"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="9"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<circle
cx="88"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<path
className="fill-foreground"
d="M89 35h2v5h-2zM83 34l2 1-1 4h-2zM77 31l2 1-3 4-2-1zM73 27l1 1-3 4-1-2zM70 23l1 1-4 3-1-2zM68 18v2l-4 1-1-2zM68 11l1 2-5 1-1-2zM69 6v2h-5V6z"
/>
<circle
cx="89"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="90"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<ellipse
cx="49.5"
cy="119"
rx="41.5"
ry="51"
className="fill-muted-foreground"
/>
<path
d="M34 38v-9c1 1 2 4 5 6l7 30-8 2c-1-23-2-23-4-29Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M95 123c0 31-20 57-45 57S5 154 5 123c0-27 14-50 33-56l12-2c25 0 45 26 45 58Zm-45 47c22 0 39-22 39-50S72 70 50 70s-39 22-39 50 17 50 39 50Z"
className="fill-foreground"
/>
<path
d="M34 29c-4-8-11-5-14-4 2 3 5 4 9 4h5Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M25 38c-1 6 0 14 2 18 5-7 7-13 7-18v-9c-5 1-7 5-9 9Z"
className="fill-muted-foreground"
/>
<path
d="M34 29c-1 3-5 11-5 16m5-16c-5 1-7 5-9 9-1 6 0 14 2 18 5-7 7-13 7-18v-9Z"
className="stroke-muted-foreground"
/>
<path
d="M44 18c-10 1-11 7-10 11l4-3c5-4 6-7 6-8Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M34 29h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="fill-foreground"
/>
<path
d="M34 29c4-2 12-5 18-1m-18 1h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="stroke-muted-foreground"
/>
<path
d="M32 29a1189 1189 0 0 1-16 19c0-17 7-18 13-19h5a14 14 0 0 1-2 0Z"
className="fill-foreground"
/>
<path
d="M34 29c-5 1-7 5-9 9l-9 10c0-17 7-18 13-19h5Zm0 0c-5 2-11 3-14 10"
className="stroke-muted-foreground"
/>
<path
d="M41 29c9 2 13 10 15 14a25 25 0 0 1-22-14h7Z"
className="fill-foreground"
/>
<path
d="M34 29c3 1 11 5 15 9m-15-9h7c9 2 13 10 15 14a25 25 0 0 1-22-14Z"
className="stroke-muted-foreground"
/>
<circle
cx="91.5"
cy="12.5"
r="18.5"
className="fill-foreground stroke-muted-foreground"
strokeWidth="2"
/>
</g>
</svg>
)
),
};

View File

@@ -37,5 +37,5 @@ export const techStackIcons: TechStackIconType = {
tanstack: {
name: "TanStack Start",
icon: <Icons.tanstack className="w-10 h-10" />,
}
},
};

View File

@@ -1,6 +1,6 @@
import {
createStartAPIHandler,
defaultAPIFileRouteHandler,
createStartAPIHandler,
defaultAPIFileRouteHandler,
} from "@tanstack/start/api";
export default createStartAPIHandler(defaultAPIFileRouteHandler);

View File

@@ -1,50 +1,50 @@
"use client"
"use client";
import * as React from "react"
import * as AvatarPrimitive from "@radix-ui/react-avatar"
import * as React from "react";
import * as AvatarPrimitive from "@radix-ui/react-avatar";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const Avatar = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
React.ElementRef<typeof AvatarPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className
)}
{...props}
/>
))
Avatar.displayName = AvatarPrimitive.Root.displayName
<AvatarPrimitive.Root
ref={ref}
className={cn(
"relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
className,
)}
{...props}
/>
));
Avatar.displayName = AvatarPrimitive.Root.displayName;
const AvatarImage = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
React.ElementRef<typeof AvatarPrimitive.Image>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
))
AvatarImage.displayName = AvatarPrimitive.Image.displayName
<AvatarPrimitive.Image
ref={ref}
className={cn("aspect-square h-full w-full", className)}
{...props}
/>
));
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
const AvatarFallback = React.forwardRef<
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
React.ElementRef<typeof AvatarPrimitive.Fallback>,
React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
>(({ className, ...props }, ref) => (
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className
)}
{...props}
/>
))
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
<AvatarPrimitive.Fallback
ref={ref}
className={cn(
"flex h-full w-full items-center justify-center rounded-full bg-muted",
className,
)}
{...props}
/>
));
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
export { Avatar, AvatarImage, AvatarFallback }
export { Avatar, AvatarImage, AvatarFallback };

View File

@@ -1,122 +1,122 @@
"use client"
"use client";
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { X } from "lucide-react";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const Dialog = DialogPrimitive.Root
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal
const DialogPortal = DialogPrimitive.Portal;
const DialogClose = DialogPrimitive.Close
const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
/>
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
...props
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className,
)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
...props
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className,
)}
{...props}
/>
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
<DialogPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className,
)}
{...props}
/>
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}
Dialog,
DialogPortal,
DialogOverlay,
DialogClose,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
};

View File

@@ -1,26 +1,26 @@
"use client"
"use client";
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import * as React from "react";
import * as LabelPrimitive from "@radix-ui/react-label";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "~/lib/utils"
import { cn } from "~/lib/utils";
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
);
const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
));
Label.displayName = LabelPrimitive.Root.displayName;
export { Label }
export { Label };

View File

@@ -1,9 +1,12 @@
import { twoFactorClient } from "better-auth/plugins";
import { createAuthClient } from "better-auth/react";
export const { useSession, signIn, signOut, signUp, twoFactor } = createAuthClient({
baseURL: "http://localhost:3000",
plugins: [twoFactorClient({
twoFactorPage: "/auth/two-factor",
})]
});
export const { useSession, signIn, signOut, signUp, twoFactor } =
createAuthClient({
baseURL: "http://localhost:3000",
plugins: [
twoFactorClient({
twoFactorPage: "/auth/two-factor",
}),
],
});

View File

@@ -1,4 +1,4 @@
import { twoFactor } from 'better-auth/plugins';
import { twoFactor } from "better-auth/plugins";
import { betterAuth } from "better-auth";
import Database from "better-sqlite3";
@@ -19,5 +19,5 @@ export const auth = betterAuth({
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
},
plugins: [twoFactor()]
plugins: [twoFactor()],
});

View File

@@ -1,4 +1,4 @@
import { X } from 'lucide-react-native';
import { iconWithClassName } from './iconWithClassName';
import { X } from "lucide-react-native";
import { iconWithClassName } from "./iconWithClassName";
iconWithClassName(X);
export { X };
export { X };

View File

@@ -1,14 +1,14 @@
import type { LucideIcon } from 'lucide-react-native';
import { cssInterop } from 'nativewind';
import type { LucideIcon } from "lucide-react-native";
import { cssInterop } from "nativewind";
export function iconWithClassName(icon: LucideIcon) {
cssInterop(icon, {
className: {
target: 'style',
nativeStyleToProp: {
color: true,
opacity: true,
},
},
});
}
cssInterop(icon, {
className: {
target: "style",
nativeStyleToProp: {
color: true,
opacity: true,
},
},
});
}

View File

@@ -2,5 +2,5 @@ import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
return twMerge(clsx(inputs));
}

View File

@@ -1,9 +1,9 @@
import { LoginForm } from "~/components/login-form"
import { LoginForm } from "~/components/login-form";
export default function Page() {
return (
<div className="flex h-screen w-full items-center justify-center px-4">
<LoginForm />
</div>
)
return (
<div className="flex h-screen w-full items-center justify-center px-4">
<LoginForm />
</div>
);
}

View File

@@ -10,123 +10,123 @@
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as IndexImport } from './routes/index'
import { Route as AuthTwoFactorImport } from './routes/auth/two-factor'
import { Route as AuthSignupImport } from './routes/auth/signup'
import { Route as AuthSigninImport } from './routes/auth/signin'
import { Route as rootRoute } from "./routes/__root";
import { Route as IndexImport } from "./routes/index";
import { Route as AuthTwoFactorImport } from "./routes/auth/two-factor";
import { Route as AuthSignupImport } from "./routes/auth/signup";
import { Route as AuthSigninImport } from "./routes/auth/signin";
// Create/Update Routes
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any)
id: "/",
path: "/",
getParentRoute: () => rootRoute,
} as any);
const AuthTwoFactorRoute = AuthTwoFactorImport.update({
id: '/auth/two-factor',
path: '/auth/two-factor',
getParentRoute: () => rootRoute,
} as any)
id: "/auth/two-factor",
path: "/auth/two-factor",
getParentRoute: () => rootRoute,
} as any);
const AuthSignupRoute = AuthSignupImport.update({
id: '/auth/signup',
path: '/auth/signup',
getParentRoute: () => rootRoute,
} as any)
id: "/auth/signup",
path: "/auth/signup",
getParentRoute: () => rootRoute,
} as any);
const AuthSigninRoute = AuthSigninImport.update({
id: '/auth/signin',
path: '/auth/signin',
getParentRoute: () => rootRoute,
} as any)
id: "/auth/signin",
path: "/auth/signin",
getParentRoute: () => rootRoute,
} as any);
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/auth/signin': {
id: '/auth/signin'
path: '/auth/signin'
fullPath: '/auth/signin'
preLoaderRoute: typeof AuthSigninImport
parentRoute: typeof rootRoute
}
'/auth/signup': {
id: '/auth/signup'
path: '/auth/signup'
fullPath: '/auth/signup'
preLoaderRoute: typeof AuthSignupImport
parentRoute: typeof rootRoute
}
'/auth/two-factor': {
id: '/auth/two-factor'
path: '/auth/two-factor'
fullPath: '/auth/two-factor'
preLoaderRoute: typeof AuthTwoFactorImport
parentRoute: typeof rootRoute
}
}
declare module "@tanstack/react-router" {
interface FileRoutesByPath {
"/": {
id: "/";
path: "/";
fullPath: "/";
preLoaderRoute: typeof IndexImport;
parentRoute: typeof rootRoute;
};
"/auth/signin": {
id: "/auth/signin";
path: "/auth/signin";
fullPath: "/auth/signin";
preLoaderRoute: typeof AuthSigninImport;
parentRoute: typeof rootRoute;
};
"/auth/signup": {
id: "/auth/signup";
path: "/auth/signup";
fullPath: "/auth/signup";
preLoaderRoute: typeof AuthSignupImport;
parentRoute: typeof rootRoute;
};
"/auth/two-factor": {
id: "/auth/two-factor";
path: "/auth/two-factor";
fullPath: "/auth/two-factor";
preLoaderRoute: typeof AuthTwoFactorImport;
parentRoute: typeof rootRoute;
};
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/auth/signin': typeof AuthSigninRoute
'/auth/signup': typeof AuthSignupRoute
'/auth/two-factor': typeof AuthTwoFactorRoute
"/": typeof IndexRoute;
"/auth/signin": typeof AuthSigninRoute;
"/auth/signup": typeof AuthSignupRoute;
"/auth/two-factor": typeof AuthTwoFactorRoute;
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/auth/signin': typeof AuthSigninRoute
'/auth/signup': typeof AuthSignupRoute
'/auth/two-factor': typeof AuthTwoFactorRoute
"/": typeof IndexRoute;
"/auth/signin": typeof AuthSigninRoute;
"/auth/signup": typeof AuthSignupRoute;
"/auth/two-factor": typeof AuthTwoFactorRoute;
}
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
'/auth/signin': typeof AuthSigninRoute
'/auth/signup': typeof AuthSignupRoute
'/auth/two-factor': typeof AuthTwoFactorRoute
__root__: typeof rootRoute;
"/": typeof IndexRoute;
"/auth/signin": typeof AuthSigninRoute;
"/auth/signup": typeof AuthSignupRoute;
"/auth/two-factor": typeof AuthTwoFactorRoute;
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/auth/signin' | '/auth/signup' | '/auth/two-factor'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/auth/signin' | '/auth/signup' | '/auth/two-factor'
id: '__root__' | '/' | '/auth/signin' | '/auth/signup' | '/auth/two-factor'
fileRoutesById: FileRoutesById
fileRoutesByFullPath: FileRoutesByFullPath;
fullPaths: "/" | "/auth/signin" | "/auth/signup" | "/auth/two-factor";
fileRoutesByTo: FileRoutesByTo;
to: "/" | "/auth/signin" | "/auth/signup" | "/auth/two-factor";
id: "__root__" | "/" | "/auth/signin" | "/auth/signup" | "/auth/two-factor";
fileRoutesById: FileRoutesById;
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AuthSigninRoute: typeof AuthSigninRoute
AuthSignupRoute: typeof AuthSignupRoute
AuthTwoFactorRoute: typeof AuthTwoFactorRoute
IndexRoute: typeof IndexRoute;
AuthSigninRoute: typeof AuthSigninRoute;
AuthSignupRoute: typeof AuthSignupRoute;
AuthTwoFactorRoute: typeof AuthTwoFactorRoute;
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AuthSigninRoute: AuthSigninRoute,
AuthSignupRoute: AuthSignupRoute,
AuthTwoFactorRoute: AuthTwoFactorRoute,
}
IndexRoute: IndexRoute,
AuthSigninRoute: AuthSigninRoute,
AuthSignupRoute: AuthSignupRoute,
AuthTwoFactorRoute: AuthTwoFactorRoute,
};
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>();
/* prettier-ignore-end */

View File

@@ -44,7 +44,7 @@ function RootComponent() {
const [theme, setTheme] = useState<"light" | "dark">("light");
const { data, isPending, error } = useSession();
const { navigate } = useRouter();
console.log()
console.log();
useEffect(() => {
if (!data?.user) {
@@ -61,7 +61,6 @@ function RootComponent() {
);
}, [data, navigate]);
useEffect(() => {
const root = window.document.documentElement;
@@ -73,228 +72,228 @@ function RootComponent() {
return (
<RootDocument>
<>
<nav className="grid grid-cols-3 items-center w-full p-4">
<div className="flex items-center justify-center gap-2">
<svg
width="60"
height="45"
viewBox="0 0 60 45"
fill="none"
className="w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
<nav className="grid grid-cols-3 items-center w-full p-4">
<div className="flex items-center justify-center gap-2">
<svg
width="60"
height="45"
viewBox="0 0 60 45"
fill="none"
className="w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
>
<title>Better Auth</title>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z"
className="fill-black dark:fill-white"
/>
</svg>
<p>BETTER-AUTH</p>
<p>x</p>
<svg
className="w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
width="45"
height="45"
viewBox="0 0 100 100"
fill="none"
>
<title>TanStack Start</title>
<mask
id="a"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<title>Better Auth</title>
<circle cx="50" cy="50" r="50" className="fill-foreground" />
</mask>
<g mask="url(#a)">
<circle
cx="11"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="10"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="9"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<circle
cx="88"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<path
className="fill-foreground"
d="M89 35h2v5h-2zM83 34l2 1-1 4h-2zM77 31l2 1-3 4-2-1zM73 27l1 1-3 4-1-2zM70 23l1 1-4 3-1-2zM68 18v2l-4 1-1-2zM68 11l1 2-5 1-1-2zM69 6v2h-5V6z"
/>
<circle
cx="89"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="90"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<ellipse
cx="49.5"
cy="119"
rx="41.5"
ry="51"
className="fill-muted-foreground"
/>
<path
d="M34 38v-9c1 1 2 4 5 6l7 30-8 2c-1-23-2-23-4-29Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M0 0H15V15H30V30H15V45H0V30V15V0ZM45 30V15H30V0H45H60V15V30V45H45H30V30H45Z"
className="fill-black dark:fill-white"
d="M95 123c0 31-20 57-45 57S5 154 5 123c0-27 14-50 33-56l12-2c25 0 45 26 45 58Zm-45 47c22 0 39-22 39-50S72 70 50 70s-39 22-39 50 17 50 39 50Z"
className="fill-foreground"
/>
</svg>
<p>BETTER-AUTH</p>
<p>x</p>
<svg
className="w-5 h-5"
xmlns="http://www.w3.org/2000/svg"
width="45"
height="45"
viewBox="0 0 100 100"
fill="none"
>
<title>TanStack Start</title>
<mask
id="a"
style={{ maskType: "alpha" }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="100"
height="100"
>
<circle cx="50" cy="50" r="50" className="fill-foreground" />
</mask>
<g mask="url(#a)">
<circle
cx="11"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="10"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="9"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<circle
cx="88"
cy="119"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<path
className="fill-foreground"
d="M89 35h2v5h-2zM83 34l2 1-1 4h-2zM77 31l2 1-3 4-2-1zM73 27l1 1-3 4-1-2zM70 23l1 1-4 3-1-2zM68 18v2l-4 1-1-2zM68 11l1 2-5 1-1-2zM69 6v2h-5V6z"
/>
<circle
cx="89"
cy="125"
r="52"
className="fill-muted-foreground stroke-foreground"
strokeWidth="4"
/>
<circle
cx="90"
cy="131"
r="52"
className="fill-muted-foreground stroke-muted-foreground"
strokeWidth="4"
/>
<ellipse
cx="49.5"
cy="119"
rx="41.5"
ry="51"
className="fill-muted-foreground"
/>
<path
d="M34 38v-9c1 1 2 4 5 6l7 30-8 2c-1-23-2-23-4-29Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M95 123c0 31-20 57-45 57S5 154 5 123c0-27 14-50 33-56l12-2c25 0 45 26 45 58Zm-45 47c22 0 39-22 39-50S72 70 50 70s-39 22-39 50 17 50 39 50Z"
className="fill-foreground"
/>
<path
d="M34 29c-4-8-11-5-14-4 2 3 5 4 9 4h5Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M25 38c-1 6 0 14 2 18 5-7 7-13 7-18v-9c-5 1-7 5-9 9Z"
className="fill-muted-foreground"
/>
<path
d="M34 29c-1 3-5 11-5 16m5-16c-5 1-7 5-9 9-1 6 0 14 2 18 5-7 7-13 7-18v-9Z"
className="stroke-muted-foreground"
/>
<path
d="M44 18c-10 1-11 7-10 11l4-3c5-4 6-7 6-8Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M34 29h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="fill-foreground"
/>
<path
d="M34 29c4-2 12-5 18-1m-18 1h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="stroke-muted-foreground"
/>
<path
d="M32 29a1189 1189 0 0 1-16 19c0-17 7-18 13-19h5a14 14 0 0 1-2 0Z"
className="fill-foreground"
/>
<path
d="M34 29c-5 1-7 5-9 9l-9 10c0-17 7-18 13-19h5Zm0 0c-5 2-11 3-14 10"
className="stroke-muted-foreground"
/>
<path
d="M41 29c9 2 13 10 15 14a25 25 0 0 1-22-14h7Z"
className="fill-foreground"
/>
<path
d="M34 29c3 1 11 5 15 9m-15-9h7c9 2 13 10 15 14a25 25 0 0 1-22-14Z"
className="stroke-muted-foreground"
/>
<circle
cx="91.5"
cy="12.5"
r="18.5"
className="fill-foreground stroke-muted-foreground"
strokeWidth="2"
/>
</g>
</svg>
<p>TANSTACK START.</p>
</div>
<div className="flex items-center justify-center gap-4">
{data?.user ? (
<p>Hello {data.user.name}</p>
) : (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<Link
to="/auth/signin"
className={navigationMenuTriggerStyle()}
activeProps={{ className: "bg-accent/50" }}
>
Sign In
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<Link
to="/auth/signup"
className={navigationMenuTriggerStyle()}
activeProps={{ className: "bg-accent/50" }}
>
Sign Up
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)}
</div>
<div className="flex items-center gap-4 justify-center">
{data?.user && (
<Button
onClick={() =>
signOut(
{},
{
onError: (error) => {
console.warn(error);
toast.error(error.error.message);
},
onSuccess: () => {
toast.success("You have been signed out!");
},
},
)
}
variant="destructive"
>
<DoorOpen className="w-5 h-5" />
</Button>
)}
<path
d="M34 29c-4-8-11-5-14-4 2 3 5 4 9 4h5Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M25 38c-1 6 0 14 2 18 5-7 7-13 7-18v-9c-5 1-7 5-9 9Z"
className="fill-muted-foreground"
/>
<path
d="M34 29c-1 3-5 11-5 16m5-16c-5 1-7 5-9 9-1 6 0 14 2 18 5-7 7-13 7-18v-9Z"
className="stroke-muted-foreground"
/>
<path
d="M44 18c-10 1-11 7-10 11l4-3c5-4 6-7 6-8Z"
className="fill-foreground stroke-muted-foreground"
/>
<path
d="M34 29h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="fill-foreground"
/>
<path
d="M34 29c4-2 12-5 18-1m-18 1h7l18 4c-3-6-9-14-21-7l-4 3Z"
className="stroke-muted-foreground"
/>
<path
d="M32 29a1189 1189 0 0 1-16 19c0-17 7-18 13-19h5a14 14 0 0 1-2 0Z"
className="fill-foreground"
/>
<path
d="M34 29c-5 1-7 5-9 9l-9 10c0-17 7-18 13-19h5Zm0 0c-5 2-11 3-14 10"
className="stroke-muted-foreground"
/>
<path
d="M41 29c9 2 13 10 15 14a25 25 0 0 1-22-14h7Z"
className="fill-foreground"
/>
<path
d="M34 29c3 1 11 5 15 9m-15-9h7c9 2 13 10 15 14a25 25 0 0 1-22-14Z"
className="stroke-muted-foreground"
/>
<circle
cx="91.5"
cy="12.5"
r="18.5"
className="fill-foreground stroke-muted-foreground"
strokeWidth="2"
/>
</g>
</svg>
<p>TANSTACK START.</p>
</div>
<div className="flex items-center justify-center gap-4">
{data?.user ? (
<p>Hello {data.user.name}</p>
) : (
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<Link
to="/auth/signin"
className={navigationMenuTriggerStyle()}
activeProps={{ className: "bg-accent/50" }}
>
Sign In
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<Link
to="/auth/signup"
className={navigationMenuTriggerStyle()}
activeProps={{ className: "bg-accent/50" }}
>
Sign Up
</Link>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
)}
</div>
<div className="flex items-center gap-4 justify-center">
{data?.user && (
<Button
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
onClick={() =>
signOut(
{},
{
onError: (error) => {
console.warn(error);
toast.error(error.error.message);
},
onSuccess: () => {
toast.success("You have been signed out!");
},
},
)
}
variant="destructive"
>
{theme === "light" ? (
<Moon onClick={() => setTheme("dark")} className="w-5 h-5" />
) : (
<Sun onClick={() => setTheme("light")} className="w-5 h-5" />
)}
<DoorOpen className="w-5 h-5" />
</Button>
</div>
</nav>
<Outlet />
</>
)}
<Button
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
{theme === "light" ? (
<Moon onClick={() => setTheme("dark")} className="w-5 h-5" />
) : (
<Sun onClick={() => setTheme("light")} className="w-5 h-5" />
)}
</Button>
</div>
</nav>
<Outlet />
</>
<Toaster richColors position="bottom-center" />
</RootDocument>
);

View File

@@ -2,10 +2,10 @@ import { createAPIFileRoute } from "@tanstack/start/api";
import { auth } from "~/lib/auth";
export const Route = createAPIFileRoute("/api/auth/$")({
GET: ({ request }) => {
return auth.handler(request);
},
POST: ({ request }) => {
return auth.handler(request);
},
GET: ({ request }) => {
return auth.handler(request);
},
POST: ({ request }) => {
return auth.handler(request);
},
});

View File

@@ -1,19 +1,25 @@
import { createFileRoute } from '@tanstack/react-router'
import { AlertCircle, CheckCircle2 } from 'lucide-react'
import { useState } from 'react'
import { Button } from '~/components/ui/button'
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '~/components/ui/card'
import { Input } from '~/components/ui/input'
import { Label } from '~/components/ui/label'
import { twoFactor } from '~/lib/auth-client'
import { createFileRoute } from "@tanstack/react-router";
import { AlertCircle, CheckCircle2 } from "lucide-react";
import { useState } from "react";
import { Button } from "~/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { twoFactor } from "~/lib/auth-client";
export const Route = createFileRoute('/auth/two-factor')({
component: TwoFactor
})
export const Route = createFileRoute("/auth/two-factor")({
component: TwoFactor,
});
function TwoFactor(){
const [totpCode, setTotpCode] = useState("");
function TwoFactor() {
const [totpCode, setTotpCode] = useState("");
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
@@ -36,8 +42,8 @@ function TwoFactor(){
}
});
};
return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
return (
<main className="flex flex-col items-center justify-center min-h-[calc(100vh-10rem)]">
<Card className="w-[350px]">
<CardHeader>
<CardTitle>TOTP Verification</CardTitle>
@@ -80,5 +86,5 @@ function TwoFactor(){
</CardContent>
</Card>
</main>
)
}
);
}

View File

@@ -1,215 +1,214 @@
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '~/components/ui/card'
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { twoFactor, useSession } from '~/lib/auth-client'
import { UAParser } from 'ua-parser-js'
import { Laptop, Loader2, Phone, ShieldCheck, ShieldOff } from 'lucide-react'
import { useState } from 'react'
import { twoFactor, useSession } from "~/lib/auth-client";
import { UAParser } from "ua-parser-js";
import { Laptop, Loader2, Phone, ShieldCheck, ShieldOff } from "lucide-react";
import { useState } from "react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '~/components/ui/dialog'
import { Button } from '~/components/ui/button'
import { Label } from '~/components/ui/label'
import { Input } from '~/components/ui/input'
import { toast } from 'sonner'
import QRCode from 'react-qr-code'
import { createFileRoute } from '@tanstack/react-router'
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "~/components/ui/dialog";
import { Button } from "~/components/ui/button";
import { Label } from "~/components/ui/label";
import { Input } from "~/components/ui/input";
import { toast } from "sonner";
import QRCode from "react-qr-code";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute('/')({
component: Home,
})
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
const { data } = useSession()
const [twoFactorDialog, setTwoFactorDialog] = useState(false)
const [twoFaPassword, setTwoFaPassword] = useState('')
const [isPendingTwoFa, setIsPendingTwoFa] = useState(false)
const [twoFactorVerifyURI, setTwoFactorVerifyURI] = useState<string>('')
return (
<div className="container flex justify-center items-center min-h-[80vh]">
<Card className="w-fit">
{data?.user && (
<>
<CardHeader>
<CardTitle>Welcome, {data.user.name}!</CardTitle>
<CardDescription>
You are signed in as {data.user.email}.
</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-2 justify-start">
<div className="flex flex-col">
<div className="flex items-center gap-2">
{new UAParser(data.session.userAgent).getDevice().type ===
'mobile' ? (
<Phone />
) : (
<Laptop size={16} />
)}
{new UAParser(data.session.userAgent).getOS().name},{' '}
{new UAParser(data.session.userAgent).getBrowser().name}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Dialog
open={twoFactorDialog}
onOpenChange={setTwoFactorDialog}
>
<DialogTrigger asChild>
<Button
variant={
data?.user.twoFactorEnabled
? 'destructive'
: 'outline'
}
className="gap-2"
>
{data?.user.twoFactorEnabled ? (
<ShieldOff size={16} />
) : (
<ShieldCheck size={16} />
)}
<span className="md:text-sm text-xs">
{data?.user.twoFactorEnabled
? 'Disable 2FA'
: 'Enable 2FA'}
</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] w-11/12">
<DialogHeader>
<DialogTitle>
{data?.user.twoFactorEnabled
? 'Disable 2FA'
: 'Enable 2FA'}
</DialogTitle>
<DialogDescription>
{data?.user.twoFactorEnabled
? 'Disable the second factor authentication from your account'
: 'Enable 2FA to secure your account'}
</DialogDescription>
</DialogHeader>
const { data } = useSession();
const [twoFactorDialog, setTwoFactorDialog] = useState(false);
const [twoFaPassword, setTwoFaPassword] = useState("");
const [isPendingTwoFa, setIsPendingTwoFa] = useState(false);
const [twoFactorVerifyURI, setTwoFactorVerifyURI] = useState<string>("");
return (
<div className="container flex justify-center items-center min-h-[80vh]">
<Card className="w-fit">
{data?.user && (
<>
<CardHeader>
<CardTitle>Welcome, {data.user.name}!</CardTitle>
<CardDescription>
You are signed in as {data.user.email}.
</CardDescription>
</CardHeader>
<CardContent className="flex flex-col gap-2 justify-start">
<div className="flex flex-col">
<div className="flex items-center gap-2">
{new UAParser(data.session.userAgent).getDevice().type ===
"mobile" ? (
<Phone />
) : (
<Laptop size={16} />
)}
{new UAParser(data.session.userAgent).getOS().name},{" "}
{new UAParser(data.session.userAgent).getBrowser().name}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Dialog
open={twoFactorDialog}
onOpenChange={setTwoFactorDialog}
>
<DialogTrigger asChild>
<Button
variant={
data?.user.twoFactorEnabled
? "destructive"
: "outline"
}
className="gap-2"
>
{data?.user.twoFactorEnabled ? (
<ShieldOff size={16} />
) : (
<ShieldCheck size={16} />
)}
<span className="md:text-sm text-xs">
{data?.user.twoFactorEnabled
? "Disable 2FA"
: "Enable 2FA"}
</span>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] w-11/12">
<DialogHeader>
<DialogTitle>
{data?.user.twoFactorEnabled
? "Disable 2FA"
: "Enable 2FA"}
</DialogTitle>
<DialogDescription>
{data?.user.twoFactorEnabled
? "Disable the second factor authentication from your account"
: "Enable 2FA to secure your account"}
</DialogDescription>
</DialogHeader>
{twoFactorVerifyURI ? (
<div className="flex flex-col gap-2">
<div className="flex items-center justify-center">
<QRCode value={twoFactorVerifyURI} />
</div>
<Label htmlFor="password">
Scan the QR code with your TOTP app
</Label>
<Input
value={twoFaPassword}
onChange={(e) => setTwoFaPassword(e.target.value)}
placeholder="Enter OTP"
/>
</div>
) : (
<div className="flex flex-col gap-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
placeholder="Password"
value={twoFaPassword}
onChange={(e) => setTwoFaPassword(e.target.value)}
/>
</div>
)}
<DialogFooter>
<Button
disabled={isPendingTwoFa}
onClick={async () => {
if (
twoFaPassword.length < 8 &&
!twoFactorVerifyURI
) {
toast.error(
'Password must be at least 8 characters',
)
return
}
setIsPendingTwoFa(true)
if (data?.user.twoFactorEnabled) {
const res = await twoFactor.disable({
//@ts-ignore
password: twoFaPassword,
fetchOptions: {
onError(context) {
toast.error(context.error.message)
},
onSuccess() {
toast('2FA disabled successfully')
setTwoFactorDialog(false)
},
},
})
} else {
if (twoFactorVerifyURI) {
await twoFactor.verifyTotp({
code: twoFaPassword,
fetchOptions: {
onError(context) {
setIsPendingTwoFa(false)
setTwoFaPassword('')
toast.error(context.error.message)
},
onSuccess() {
toast('2FA enabled successfully')
setTwoFactorVerifyURI('')
setIsPendingTwoFa(false)
setTwoFaPassword('')
setTwoFactorDialog(false)
},
},
})
return
}
await twoFactor.enable({
password: twoFaPassword,
fetchOptions: {
onError(context) {
toast.error(context.error.message)
},
onSuccess(ctx) {
setTwoFactorVerifyURI(ctx.data.totpURI)
},
},
})
}
setIsPendingTwoFa(false)
setTwoFaPassword('')
}}
>
{isPendingTwoFa ? (
<Loader2 size={15} className="animate-spin" />
) : data?.user.twoFactorEnabled ? (
'Disable 2FA'
) : (
'Enable 2FA'
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</CardContent>
</>
)}
</Card>
</div>
)
{twoFactorVerifyURI ? (
<div className="flex flex-col gap-2">
<div className="flex items-center justify-center">
<QRCode value={twoFactorVerifyURI} />
</div>
<Label htmlFor="password">
Scan the QR code with your TOTP app
</Label>
<Input
value={twoFaPassword}
onChange={(e) => setTwoFaPassword(e.target.value)}
placeholder="Enter OTP"
/>
</div>
) : (
<div className="flex flex-col gap-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
placeholder="Password"
value={twoFaPassword}
onChange={(e) => setTwoFaPassword(e.target.value)}
/>
</div>
)}
<DialogFooter>
<Button
disabled={isPendingTwoFa}
onClick={async () => {
if (
twoFaPassword.length < 8 &&
!twoFactorVerifyURI
) {
toast.error(
"Password must be at least 8 characters",
);
return;
}
setIsPendingTwoFa(true);
if (data?.user.twoFactorEnabled) {
const res = await twoFactor.disable({
//@ts-ignore
password: twoFaPassword,
fetchOptions: {
onError(context) {
toast.error(context.error.message);
},
onSuccess() {
toast("2FA disabled successfully");
setTwoFactorDialog(false);
},
},
});
} else {
if (twoFactorVerifyURI) {
await twoFactor.verifyTotp({
code: twoFaPassword,
fetchOptions: {
onError(context) {
setIsPendingTwoFa(false);
setTwoFaPassword("");
toast.error(context.error.message);
},
onSuccess() {
toast("2FA enabled successfully");
setTwoFactorVerifyURI("");
setIsPendingTwoFa(false);
setTwoFaPassword("");
setTwoFactorDialog(false);
},
},
});
return;
}
await twoFactor.enable({
password: twoFaPassword,
fetchOptions: {
onError(context) {
toast.error(context.error.message);
},
onSuccess(ctx) {
setTwoFactorVerifyURI(ctx.data.totpURI);
},
},
});
}
setIsPendingTwoFa(false);
setTwoFaPassword("");
}}
>
{isPendingTwoFa ? (
<Loader2 size={15} className="animate-spin" />
) : data?.user.twoFactorEnabled ? (
"Disable 2FA"
) : (
"Enable 2FA"
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</div>
</CardContent>
</>
)}
</Card>
</div>
);
}

View File

@@ -1,16 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/lib/style/global.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "~/components",
"utils": "~/lib/utils"
}
}
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/lib/style/global.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "~/components",
"utils": "~/lib/utils"
}
}

View File

@@ -1,6 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -11,4 +11,4 @@ export * from "../../plugins/admin/client";
export * from "../../plugins/generic-oauth/client";
export * from "../../plugins/jwt/client";
export * from "../../plugins/multi-session/client";
export * from "../../plugins/email-otp/client";
export * from "../../plugins/email-otp/client";

View File

@@ -137,12 +137,10 @@ export const jwt = (options?: JwtOptions) => {
publicKey: JSON.stringify(publicWebKey),
privateKey: privateKeyEncryptionEnabled
? JSON.stringify(
await symmetricEncrypt(
{
key: ctx.context.options.secret!,
data: stringifiedPrivateWebKey,
}
),
await symmetricEncrypt({
key: ctx.context.options.secret!,
data: stringifiedPrivateWebKey,
}),
)
: stringifiedPrivateWebKey,
createdAt: new Date(),
@@ -152,12 +150,10 @@ export const jwt = (options?: JwtOptions) => {
}
let privateWebKey = privateKeyEncryptionEnabled
? await symmetricDecrypt(
{
key: ctx.context.options.secret!,
data: JSON.parse(key.privateKey),
}
)
? await symmetricDecrypt({
key: ctx.context.options.secret!,
data: JSON.parse(key.privateKey),
})
: key.privateKey;
const privateKey = await importJWK(JSON.parse(privateWebKey));