mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-07 20:37:44 +00:00
chore:lint
This commit is contained in:
@@ -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>
|
||||
)
|
||||
),
|
||||
};
|
||||
|
||||
@@ -37,5 +37,5 @@ export const techStackIcons: TechStackIconType = {
|
||||
tanstack: {
|
||||
name: "TanStack Start",
|
||||
icon: <Icons.tanstack className="w-10 h-10" />,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {
|
||||
createStartAPIHandler,
|
||||
defaultAPIFileRouteHandler,
|
||||
createStartAPIHandler,
|
||||
defaultAPIFileRouteHandler,
|
||||
} from "@tanstack/start/api";
|
||||
|
||||
export default createStartAPIHandler(defaultAPIFileRouteHandler);
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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",
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -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()],
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user