frontend: provide Clerk-safe wrappers for secretless CI prerender
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import {
|
import {
|
||||||
type ColumnDef,
|
type ColumnDef,
|
||||||
type SortingState,
|
type SortingState,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut } from "@/auth/clerk";
|
||||||
|
|
||||||
import { BoardApprovalsPanel } from "@/components/BoardApprovalsPanel";
|
import { BoardApprovalsPanel } from "@/components/BoardApprovalsPanel";
|
||||||
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
|
import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
import { useParams, useRouter, useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import {
|
import {
|
||||||
Activity,
|
Activity,
|
||||||
ArrowUpRight,
|
ArrowUpRight,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
import { useCreateBoardApiV1BoardsPost } from "@/api/generated/boards/boards";
|
import { useCreateBoardApiV1BoardsPost } from "@/api/generated/boards/boards";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import {
|
import {
|
||||||
type ColumnDef,
|
type ColumnDef,
|
||||||
flexRender,
|
flexRender,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import {
|
import {
|
||||||
Area,
|
Area,
|
||||||
AreaChart,
|
AreaChart,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
|
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useParams, useRouter } from "next/navigation";
|
import { useParams, useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
|
import { CheckCircle2, RefreshCcw, XCircle } from "lucide-react";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth } from "@/auth/clerk";
|
||||||
import {
|
import {
|
||||||
type ColumnDef,
|
type ColumnDef,
|
||||||
type SortingState,
|
type SortingState,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut, useAuth, useUser } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut, useAuth, useUser } from "@/auth/clerk";
|
||||||
import { Globe, Info, RotateCcw, Save, User } from "lucide-react";
|
import { Globe, Info, RotateCcw, Save, User } from "lucide-react";
|
||||||
|
|
||||||
import { ApiError } from "@/api/mutator";
|
import { ApiError } from "@/api/mutator";
|
||||||
|
|||||||
74
frontend/src/auth/clerk.tsx
Normal file
74
frontend/src/auth/clerk.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
// NOTE: We intentionally keep this file very small and dependency-free.
|
||||||
|
// It provides CI/secretless-build safe fallbacks for Clerk hooks/components.
|
||||||
|
|
||||||
|
import {
|
||||||
|
ClerkProvider,
|
||||||
|
SignedIn as ClerkSignedIn,
|
||||||
|
SignedOut as ClerkSignedOut,
|
||||||
|
SignInButton as ClerkSignInButton,
|
||||||
|
SignOutButton as ClerkSignOutButton,
|
||||||
|
useAuth as clerkUseAuth,
|
||||||
|
useUser as clerkUseUser,
|
||||||
|
} from "@clerk/nextjs";
|
||||||
|
|
||||||
|
export function isClerkEnabled(): boolean {
|
||||||
|
const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
|
||||||
|
if (!key) return false;
|
||||||
|
|
||||||
|
// Clerk validates publishable key contents at runtime; use a conservative heuristic.
|
||||||
|
const m = /^pk_(test|live)_([A-Za-z0-9]+)$/.exec(key);
|
||||||
|
if (!m) return false;
|
||||||
|
const body = m[2];
|
||||||
|
if (body.length < 16) return false;
|
||||||
|
if (/^0+$/.test(body)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SignedIn(props: { children: ReactNode }) {
|
||||||
|
if (!isClerkEnabled()) return null;
|
||||||
|
return <ClerkSignedIn>{props.children}</ClerkSignedIn>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SignedOut(props: { children: ReactNode }) {
|
||||||
|
if (!isClerkEnabled()) return <>{props.children}</>;
|
||||||
|
return <ClerkSignedOut>{props.children}</ClerkSignedOut>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept arbitrary Clerk component props so existing call sites don't need edits.
|
||||||
|
export function SignInButton(props: any) {
|
||||||
|
if (!isClerkEnabled()) return null;
|
||||||
|
return <ClerkSignInButton {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SignOutButton(props: any) {
|
||||||
|
if (!isClerkEnabled()) return null;
|
||||||
|
return <ClerkSignOutButton {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUser() {
|
||||||
|
if (!isClerkEnabled()) {
|
||||||
|
return { isLoaded: true, isSignedIn: false, user: null } as const;
|
||||||
|
}
|
||||||
|
return clerkUseUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAuth() {
|
||||||
|
if (!isClerkEnabled()) {
|
||||||
|
return {
|
||||||
|
isLoaded: true,
|
||||||
|
isSignedIn: false,
|
||||||
|
userId: null,
|
||||||
|
sessionId: null,
|
||||||
|
getToken: async () => null,
|
||||||
|
} as const;
|
||||||
|
}
|
||||||
|
return clerkUseAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export ClerkProvider for places that want to mount it, but strongly prefer
|
||||||
|
// gating via isClerkEnabled() at call sites.
|
||||||
|
export { ClerkProvider };
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useCallback, useMemo, useState } from "react";
|
import { useCallback, useMemo, useState } from "react";
|
||||||
|
|
||||||
import { useAuth } from "@clerk/nextjs";
|
import { useAuth } from "@/auth/clerk";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import { Clock } from "lucide-react";
|
import { Clock } from "lucide-react";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { SignInButton, SignedIn, SignedOut } from "@clerk/nextjs";
|
import { SignInButton, SignedIn, SignedOut } from "@/auth/clerk";
|
||||||
|
|
||||||
import { HeroCopy } from "@/components/molecules/HeroCopy";
|
import { HeroCopy } from "@/components/molecules/HeroCopy";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { SignOutButton, useUser } from "@clerk/nextjs";
|
import { SignOutButton, useUser } from "@/auth/clerk";
|
||||||
import { LogOut } from "lucide-react";
|
import { LogOut } from "lucide-react";
|
||||||
|
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { SignedIn, useUser } from "@clerk/nextjs";
|
import { SignedIn, useUser } from "@/auth/clerk";
|
||||||
|
|
||||||
import { BrandMark } from "@/components/atoms/BrandMark";
|
import { BrandMark } from "@/components/atoms/BrandMark";
|
||||||
import { UserMenu } from "@/components/organisms/UserMenu";
|
import { UserMenu } from "@/components/organisms/UserMenu";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { SignedIn } from "@clerk/nextjs";
|
import { SignedIn } from "@/auth/clerk";
|
||||||
|
|
||||||
import { BrandMark } from "@/components/atoms/BrandMark";
|
import { BrandMark } from "@/components/atoms/BrandMark";
|
||||||
import { UserMenu } from "@/components/organisms/UserMenu";
|
import { UserMenu } from "@/components/organisms/UserMenu";
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
import { clerkMiddleware } from "@clerk/nextjs/server";
|
import { clerkMiddleware } from "@clerk/nextjs/server";
|
||||||
|
|
||||||
export default clerkMiddleware();
|
const isClerkEnabled = () => {
|
||||||
|
const key = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
|
||||||
|
if (!key) return false;
|
||||||
|
const m = /^pk_(test|live)_([A-Za-z0-9]+)$/.exec(key);
|
||||||
|
if (!m) return false;
|
||||||
|
const body = m[2];
|
||||||
|
if (body.length < 16) return false;
|
||||||
|
if (/^0+$/.test(body)) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isClerkEnabled() ? clerkMiddleware() : () => NextResponse.next();
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
|
|||||||
Reference in New Issue
Block a user