luxlabs 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/package.json +1 -1
  2. package/templates/interface-boilerplate/app/api/auth/[...all]/route.ts +52 -0
  3. package/templates/interface-boilerplate/app/auth/callback/page.tsx +22 -31
  4. package/templates/interface-boilerplate/app/dashboard/page.tsx +41 -214
  5. package/templates/interface-boilerplate/app/favicon.ico +0 -0
  6. package/templates/interface-boilerplate/app/globals.css +18 -113
  7. package/templates/interface-boilerplate/app/layout.tsx +21 -33
  8. package/templates/interface-boilerplate/app/page.tsx +37 -116
  9. package/templates/interface-boilerplate/app/settings/page.tsx +71 -0
  10. package/templates/interface-boilerplate/app/sign-in/page.tsx +19 -0
  11. package/templates/interface-boilerplate/app/sign-up/page.tsx +19 -0
  12. package/templates/interface-boilerplate/components/auth/sign-in-form.tsx +144 -0
  13. package/templates/interface-boilerplate/components/auth/sign-up-form.tsx +236 -0
  14. package/templates/interface-boilerplate/components/ui/badge.tsx +18 -14
  15. package/templates/interface-boilerplate/components/ui/button.tsx +29 -24
  16. package/templates/interface-boilerplate/components/ui/card.tsx +76 -57
  17. package/templates/interface-boilerplate/components/ui/input.tsx +8 -9
  18. package/templates/interface-boilerplate/eslint.config.mjs +18 -0
  19. package/templates/interface-boilerplate/lib/auth-client.ts +18 -0
  20. package/templates/interface-boilerplate/lib/auth.config.ts +111 -0
  21. package/templates/interface-boilerplate/lib/lux.ts +8 -0
  22. package/templates/interface-boilerplate/lib/utils.ts +3 -3
  23. package/templates/interface-boilerplate/middleware.ts +60 -0
  24. package/templates/interface-boilerplate/next.config.ts +7 -0
  25. package/templates/interface-boilerplate/package-lock.json +6855 -0
  26. package/templates/interface-boilerplate/package.json +18 -37
  27. package/templates/interface-boilerplate/postcss.config.mjs +7 -0
  28. package/templates/interface-boilerplate/tsconfig.json +18 -4
  29. package/templates/interface-boilerplate/.env.example +0 -2
  30. package/templates/interface-boilerplate/.eslintrc.json +0 -4
  31. package/templates/interface-boilerplate/app/login/page.tsx +0 -178
  32. package/templates/interface-boilerplate/app/signup/page.tsx +0 -199
  33. package/templates/interface-boilerplate/components/AnalyticsProvider.tsx +0 -142
  34. package/templates/interface-boilerplate/components/AuthGuard.tsx +0 -76
  35. package/templates/interface-boilerplate/components/ErrorBoundary.tsx +0 -106
  36. package/templates/interface-boilerplate/components/theme-provider.tsx +0 -9
  37. package/templates/interface-boilerplate/components/theme-toggle.tsx +0 -39
  38. package/templates/interface-boilerplate/components/ui/avatar.tsx +0 -46
  39. package/templates/interface-boilerplate/components/ui/checkbox.tsx +0 -27
  40. package/templates/interface-boilerplate/components/ui/dialog.tsx +0 -100
  41. package/templates/interface-boilerplate/components/ui/dropdown-menu.tsx +0 -173
  42. package/templates/interface-boilerplate/components/ui/index.ts +0 -53
  43. package/templates/interface-boilerplate/components/ui/label.tsx +0 -20
  44. package/templates/interface-boilerplate/components/ui/progress.tsx +0 -24
  45. package/templates/interface-boilerplate/components/ui/select.tsx +0 -149
  46. package/templates/interface-boilerplate/components/ui/separator.tsx +0 -25
  47. package/templates/interface-boilerplate/components/ui/skeleton.tsx +0 -12
  48. package/templates/interface-boilerplate/components/ui/switch.tsx +0 -28
  49. package/templates/interface-boilerplate/components/ui/tabs.tsx +0 -54
  50. package/templates/interface-boilerplate/components/ui/textarea.tsx +0 -22
  51. package/templates/interface-boilerplate/components/ui/tooltip.tsx +0 -29
  52. package/templates/interface-boilerplate/lib/analytics.ts +0 -182
  53. package/templates/interface-boilerplate/lib/auth-context.tsx +0 -83
  54. package/templates/interface-boilerplate/lib/auth.ts +0 -199
  55. package/templates/interface-boilerplate/lib/callFlow.ts +0 -234
  56. package/templates/interface-boilerplate/lib/flowTracer.ts +0 -195
  57. package/templates/interface-boilerplate/lib/hooks/.gitkeep +0 -0
  58. package/templates/interface-boilerplate/lib/stores/.gitkeep +0 -0
  59. package/templates/interface-boilerplate/next.config.js +0 -6
  60. package/templates/interface-boilerplate/postcss.config.js +0 -6
  61. package/templates/interface-boilerplate/tailwind.config.js +0 -103
@@ -1,32 +1,36 @@
1
- import * as React from 'react';
2
- import { cva, type VariantProps } from 'class-variance-authority';
3
- import { cn } from '@/lib/utils';
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
4
5
 
5
6
  const badgeVariants = cva(
6
- 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
7
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-gray-400 focus:ring-offset-2",
7
8
  {
8
9
  variants: {
9
10
  variant: {
10
- default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
11
- secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
12
- destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
13
- success: 'border-transparent bg-success text-success-foreground hover:bg-success/80',
14
- warning: 'border-transparent bg-warning text-warning-foreground hover:bg-warning/80',
15
- outline: 'text-foreground',
11
+ default:
12
+ "border-transparent bg-gray-900 text-white shadow hover:bg-gray-800",
13
+ secondary:
14
+ "border-transparent bg-gray-100 text-gray-900 hover:bg-gray-200",
15
+ destructive:
16
+ "border-transparent bg-red-600 text-white shadow hover:bg-red-700",
17
+ outline: "text-gray-900 border-gray-300",
16
18
  },
17
19
  },
18
20
  defaultVariants: {
19
- variant: 'default',
21
+ variant: "default",
20
22
  },
21
23
  }
22
- );
24
+ )
23
25
 
24
26
  export interface BadgeProps
25
27
  extends React.HTMLAttributes<HTMLDivElement>,
26
28
  VariantProps<typeof badgeVariants> {}
27
29
 
28
30
  function Badge({ className, variant, ...props }: BadgeProps) {
29
- return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
30
34
  }
31
35
 
32
- export { Badge, badgeVariants };
36
+ export { Badge, badgeVariants }
@@ -1,52 +1,57 @@
1
- import * as React from 'react';
2
- import { Slot } from '@radix-ui/react-slot';
3
- import { cva, type VariantProps } from 'class-variance-authority';
4
- import { cn } from '@/lib/utils';
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
5
6
 
6
7
  const buttonVariants = cva(
7
- 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
8
9
  {
9
10
  variants: {
10
11
  variant: {
11
- default: 'bg-primary text-primary-foreground hover:bg-primary/90',
12
- destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
13
- outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
14
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
15
- ghost: 'hover:bg-accent hover:text-accent-foreground',
16
- link: 'text-primary underline-offset-4 hover:underline',
12
+ default:
13
+ "bg-gray-900 text-white shadow hover:bg-gray-800",
14
+ destructive:
15
+ "bg-red-600 text-white shadow-sm hover:bg-red-700",
16
+ outline:
17
+ "border border-gray-300 bg-white shadow-sm hover:bg-gray-50",
18
+ secondary:
19
+ "bg-gray-100 text-gray-900 shadow-sm hover:bg-gray-200",
20
+ ghost: "hover:bg-gray-100",
21
+ link: "text-gray-900 underline-offset-4 hover:underline",
17
22
  },
18
23
  size: {
19
- default: 'h-10 px-4 py-2',
20
- sm: 'h-9 rounded-md px-3',
21
- lg: 'h-11 rounded-md px-8',
22
- icon: 'h-10 w-10',
24
+ default: "h-9 px-4 py-2",
25
+ sm: "h-8 rounded-md px-3 text-xs",
26
+ lg: "h-10 rounded-md px-8",
27
+ icon: "h-9 w-9",
23
28
  },
24
29
  },
25
30
  defaultVariants: {
26
- variant: 'default',
27
- size: 'default',
31
+ variant: "default",
32
+ size: "default",
28
33
  },
29
34
  }
30
- );
35
+ )
31
36
 
32
37
  export interface ButtonProps
33
38
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
34
39
  VariantProps<typeof buttonVariants> {
35
- asChild?: boolean;
40
+ asChild?: boolean
36
41
  }
37
42
 
38
43
  const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
39
44
  ({ className, variant, size, asChild = false, ...props }, ref) => {
40
- const Comp = asChild ? Slot : 'button';
45
+ const Comp = asChild ? Slot : "button"
41
46
  return (
42
47
  <Comp
43
48
  className={cn(buttonVariants({ variant, size, className }))}
44
49
  ref={ref}
45
50
  {...props}
46
51
  />
47
- );
52
+ )
48
53
  }
49
- );
50
- Button.displayName = 'Button';
54
+ )
55
+ Button.displayName = "Button"
51
56
 
52
- export { Button, buttonVariants };
57
+ export { Button, buttonVariants }
@@ -1,57 +1,76 @@
1
- import * as React from 'react';
2
- import { cn } from '@/lib/utils';
3
-
4
- const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
5
- ({ className, ...props }, ref) => (
6
- <div
7
- ref={ref}
8
- className={cn(
9
- 'rounded-lg border bg-card text-card-foreground shadow-sm',
10
- className
11
- )}
12
- {...props}
13
- />
14
- )
15
- );
16
- Card.displayName = 'Card';
17
-
18
- const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
19
- ({ className, ...props }, ref) => (
20
- <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
21
- )
22
- );
23
- CardHeader.displayName = 'CardHeader';
24
-
25
- const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
26
- ({ className, ...props }, ref) => (
27
- <h3
28
- ref={ref}
29
- className={cn('text-2xl font-semibold leading-none tracking-tight', className)}
30
- {...props}
31
- />
32
- )
33
- );
34
- CardTitle.displayName = 'CardTitle';
35
-
36
- const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
37
- ({ className, ...props }, ref) => (
38
- <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
39
- )
40
- );
41
- CardDescription.displayName = 'CardDescription';
42
-
43
- const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
44
- ({ className, ...props }, ref) => (
45
- <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
46
- )
47
- );
48
- CardContent.displayName = 'CardContent';
49
-
50
- const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
51
- ({ className, ...props }, ref) => (
52
- <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
53
- )
54
- );
55
- CardFooter.displayName = 'CardFooter';
56
-
57
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-xl border border-gray-200 bg-white shadow",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLDivElement,
34
+ React.HTMLAttributes<HTMLDivElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <div
37
+ ref={ref}
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ ))
42
+ CardTitle.displayName = "CardTitle"
43
+
44
+ const CardDescription = React.forwardRef<
45
+ HTMLDivElement,
46
+ React.HTMLAttributes<HTMLDivElement>
47
+ >(({ className, ...props }, ref) => (
48
+ <div
49
+ ref={ref}
50
+ className={cn("text-sm text-gray-500", className)}
51
+ {...props}
52
+ />
53
+ ))
54
+ CardDescription.displayName = "CardDescription"
55
+
56
+ const CardContent = React.forwardRef<
57
+ HTMLDivElement,
58
+ React.HTMLAttributes<HTMLDivElement>
59
+ >(({ className, ...props }, ref) => (
60
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61
+ ))
62
+ CardContent.displayName = "CardContent"
63
+
64
+ const CardFooter = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex items-center p-6 pt-0", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ CardFooter.displayName = "CardFooter"
75
+
76
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
@@ -1,23 +1,22 @@
1
- import * as React from 'react';
2
- import { cn } from '@/lib/utils';
1
+ import * as React from "react"
3
2
 
4
- export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
3
+ import { cn } from "@/lib/utils"
5
4
 
6
- const Input = React.forwardRef<HTMLInputElement, InputProps>(
5
+ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
7
6
  ({ className, type, ...props }, ref) => {
8
7
  return (
9
8
  <input
10
9
  type={type}
11
10
  className={cn(
12
- 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
11
+ "flex h-9 w-full rounded-md border border-gray-300 bg-white px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-gray-400 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-400 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
13
12
  className
14
13
  )}
15
14
  ref={ref}
16
15
  {...props}
17
16
  />
18
- );
17
+ )
19
18
  }
20
- );
21
- Input.displayName = 'Input';
19
+ )
20
+ Input.displayName = "Input"
22
21
 
23
- export { Input };
22
+ export { Input }
@@ -0,0 +1,18 @@
1
+ import { defineConfig, globalIgnores } from "eslint/config";
2
+ import nextVitals from "eslint-config-next/core-web-vitals";
3
+ import nextTs from "eslint-config-next/typescript";
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ ".next/**",
12
+ "out/**",
13
+ "build/**",
14
+ "next-env.d.ts",
15
+ ]),
16
+ ]);
17
+
18
+ export default eslintConfig;
@@ -0,0 +1,18 @@
1
+ import { createAuthClient } from "better-auth/react";
2
+
3
+ // Auth client configured to use the Lux Studio API
4
+ // The API URL and Org ID are injected at build/runtime
5
+ const baseURL = process.env.NEXT_PUBLIC_LUX_API_URL || "https://v2.uselux.ai";
6
+ const orgId = process.env.NEXT_PUBLIC_LUX_ORG_ID || "";
7
+
8
+ export const authClient = createAuthClient({
9
+ baseURL: `${baseURL}/api/auth`,
10
+ fetchOptions: {
11
+ headers: {
12
+ "X-Org-Id": orgId,
13
+ },
14
+ credentials: "include",
15
+ },
16
+ });
17
+
18
+ export const { signIn, signUp, signOut, useSession } = authClient;
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Authentication Configuration
3
+ *
4
+ * This file defines which routes require authentication and which are public.
5
+ * Update this file to control access to different parts of your application.
6
+ */
7
+
8
+ export const authConfig = {
9
+ /**
10
+ * Routes that require authentication
11
+ * Users will be redirected to sign-in if not authenticated
12
+ */
13
+ protectedRoutes: [
14
+ "/dashboard",
15
+ "/profile",
16
+ "/settings",
17
+ "/account",
18
+ "/auth/callback", // Organization setup after OAuth
19
+ // Add more protected routes here
20
+ ],
21
+
22
+ /**
23
+ * Auth pages (sign-in, sign-up, etc.)
24
+ * Authenticated users will be redirected away from these pages
25
+ */
26
+ authRoutes: [
27
+ "/sign-in",
28
+ "/sign-up",
29
+ "/verify-email",
30
+ ],
31
+
32
+ /**
33
+ * Public routes that anyone can access
34
+ * These routes are accessible whether authenticated or not
35
+ */
36
+ publicRoutes: [
37
+ "/",
38
+ "/about",
39
+ "/pricing",
40
+ "/contact",
41
+ "/blog",
42
+ "/docs",
43
+ "/accept-invite",
44
+ // Add more public routes here
45
+ ],
46
+
47
+ /**
48
+ * Default redirect destinations
49
+ */
50
+ redirects: {
51
+ // Where to redirect after successful sign-in
52
+ afterSignIn: "/dashboard",
53
+
54
+ // Where to redirect authenticated users who try to access auth pages
55
+ afterAuth: "/dashboard",
56
+
57
+ // Where to redirect unauthenticated users
58
+ toSignIn: "/sign-in",
59
+ },
60
+
61
+ /**
62
+ * Route matching options
63
+ */
64
+ options: {
65
+ // If true, "/dashboard" will also match "/dashboard/settings"
66
+ matchPrefixes: true,
67
+
68
+ // If true, "/" is treated as a public route even if not in publicRoutes
69
+ rootIsPublic: true,
70
+ },
71
+ } as const;
72
+
73
+ /**
74
+ * Helper function to check if a route matches a pattern
75
+ */
76
+ export function isRouteMatch(pathname: string, pattern: string, matchPrefix = true): boolean {
77
+ if (matchPrefix) {
78
+ return pathname === pattern || pathname.startsWith(pattern + "/");
79
+ }
80
+ return pathname === pattern;
81
+ }
82
+
83
+ /**
84
+ * Helper function to determine route type
85
+ */
86
+ export function getRouteType(pathname: string): "protected" | "auth" | "public" {
87
+ const { protectedRoutes, authRoutes, publicRoutes, options } = authConfig;
88
+
89
+ // Check if it's a protected route
90
+ if (protectedRoutes.some(route => isRouteMatch(pathname, route, options.matchPrefixes))) {
91
+ return "protected";
92
+ }
93
+
94
+ // Check if it's an auth route
95
+ if (authRoutes.some(route => isRouteMatch(pathname, route, options.matchPrefixes))) {
96
+ return "auth";
97
+ }
98
+
99
+ // Check if it's explicitly public
100
+ if (publicRoutes.some(route => isRouteMatch(pathname, route, options.matchPrefixes))) {
101
+ return "public";
102
+ }
103
+
104
+ // Special case for root
105
+ if (pathname === "/" && options.rootIsPublic) {
106
+ return "public";
107
+ }
108
+
109
+ // Default: treat as public (change to "protected" for whitelist approach)
110
+ return "public";
111
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Creates a lux helper for generating data-lux attribute values
3
+ * @param componentName - The name of the component
4
+ * @returns A function that generates prefixed element IDs
5
+ */
6
+ export function createLux(componentName: string) {
7
+ return (elementId: string) => `${componentName}__${elementId}`;
8
+ }
@@ -1,6 +1,6 @@
1
- import { type ClassValue, clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
1
+ import { type ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
3
 
4
4
  export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
5
+ return twMerge(clsx(inputs))
6
6
  }
@@ -0,0 +1,60 @@
1
+ import { NextResponse } from "next/server";
2
+ import type { NextRequest } from "next/server";
3
+ import { authConfig, getRouteType } from "@/lib/auth.config";
4
+
5
+ export async function middleware(request: NextRequest) {
6
+ const { pathname } = request.nextUrl;
7
+
8
+ // Check if user has a session cookie
9
+ const sessionToken = request.cookies.get("better-auth.session_token");
10
+ const isAuthenticated = !!sessionToken;
11
+
12
+ // Special handling for API routes
13
+ if (pathname.startsWith("/api/")) {
14
+ // API auth routes are always allowed
15
+ if (pathname.startsWith("/api/auth/")) {
16
+ return NextResponse.next();
17
+ }
18
+
19
+ // All other API routes require authentication
20
+ if (!isAuthenticated) {
21
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
22
+ }
23
+
24
+ return NextResponse.next();
25
+ }
26
+
27
+ // Page route handling (non-API)
28
+ // Determine route type from config
29
+ const routeType = getRouteType(pathname);
30
+
31
+ // Handle auth routes (signin, signup, etc.)
32
+ // Redirect authenticated users away from auth pages
33
+ if (routeType === "auth" && isAuthenticated) {
34
+ return NextResponse.redirect(new URL(authConfig.redirects.afterAuth, request.url));
35
+ }
36
+
37
+ // Handle protected routes
38
+ // Redirect unauthenticated users to signin
39
+ if (routeType === "protected" && !isAuthenticated) {
40
+ const signInUrl = new URL(authConfig.redirects.toSignIn, request.url);
41
+ // Preserve the original destination for redirect after sign-in
42
+ signInUrl.searchParams.set("callbackUrl", pathname);
43
+ return NextResponse.redirect(signInUrl);
44
+ }
45
+
46
+ // Allow access to public routes
47
+ return NextResponse.next();
48
+ }
49
+
50
+ export const config = {
51
+ matcher: [
52
+ /*
53
+ * Match all request paths except:
54
+ * - _next/static (static files)
55
+ * - _next/image (image optimization)
56
+ * - favicon.ico (favicon file)
57
+ */
58
+ "/((?!_next/static|_next/image|favicon.ico).*)",
59
+ ],
60
+ };
@@ -0,0 +1,7 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ /* config options here */
5
+ };
6
+
7
+ export default nextConfig;