Add SuperTokens authentication integration
- Install supertokens-auth-react, supertokens-node, supertokens-web-js - Create frontend and backend SuperTokens configuration - Add API route handler for auth endpoints - Add SuperTokensProvider wrapper in root layout - Create new auth component with SuperTokens UI - Configure Google and GitHub OAuth providers - Ready for SuperTokens core deployment Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
38
app/api/auth/[[...path]]/route.ts
Normal file
38
app/api/auth/[[...path]]/route.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import SuperTokens from "supertokens-node";
|
||||
import { backendConfig } from "@/lib/supertokens/backendConfig";
|
||||
import { getAppDirRequestHandler } from "supertokens-node/nextjs";
|
||||
|
||||
SuperTokens.init(backendConfig());
|
||||
|
||||
const handleRequest = getAppDirRequestHandler(NextResponse);
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function DELETE(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
export async function HEAD(request: NextRequest) {
|
||||
const response = await handleRequest(request);
|
||||
return response;
|
||||
}
|
||||
18
app/auth/supertokens-page.tsx
Normal file
18
app/auth/supertokens-page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
// Dynamically import to avoid SSR issues
|
||||
const SuperTokensComponentNoSSR = dynamic(
|
||||
() => import("@/app/components/SuperTokensAuthComponent"),
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export default function AuthPage() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background">
|
||||
<SuperTokensComponentNoSSR />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
20
app/components/SuperTokensAuthComponent.tsx
Normal file
20
app/components/SuperTokensAuthComponent.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { redirectToAuth } from "supertokens-auth-react";
|
||||
import { ThirdPartyEmailPasswordPreBuiltUI } from "supertokens-auth-react/recipe/thirdpartyemailpassword/prebuiltui";
|
||||
import { canHandleRoute, getRoutingComponent } from "supertokens-auth-react/ui";
|
||||
|
||||
export default function SuperTokensAuthComponent() {
|
||||
useEffect(() => {
|
||||
if (!canHandleRoute([ThirdPartyEmailPasswordPreBuiltUI])) {
|
||||
redirectToAuth();
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (canHandleRoute([ThirdPartyEmailPasswordPreBuiltUI])) {
|
||||
return getRoutingComponent([ThirdPartyEmailPasswordPreBuiltUI]);
|
||||
}
|
||||
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
18
app/components/SuperTokensProvider.tsx
Normal file
18
app/components/SuperTokensProvider.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { useEffect } from "react";
|
||||
import SuperTokensReact from "supertokens-auth-react";
|
||||
import { frontendConfig } from "@/lib/supertokens/frontendConfig";
|
||||
|
||||
export const SuperTokensProvider: React.FC<React.PropsWithChildren<{}>> = ({
|
||||
children,
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
SuperTokensReact.init(frontendConfig());
|
||||
}
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
@@ -2,6 +2,7 @@ import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { SuperTokensProvider } from "./components/SuperTokensProvider";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -28,8 +29,10 @@ export default function RootLayout({
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
<Toaster />
|
||||
<SuperTokensProvider>
|
||||
{children}
|
||||
<Toaster />
|
||||
</SuperTokensProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
94
app/page.tsx
Normal file
94
app/page.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||
<div className="w-full max-w-2xl space-y-6">
|
||||
{/* Logo */}
|
||||
<div className="flex justify-center">
|
||||
<img
|
||||
src="/vibn-black-circle-logo.png"
|
||||
alt="Vib'n"
|
||||
className="h-24 w-24"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Welcome Card */}
|
||||
<Card>
|
||||
<CardHeader className="space-y-1">
|
||||
<CardTitle className="text-3xl font-bold text-center">
|
||||
Welcome to Vib'n
|
||||
</CardTitle>
|
||||
<CardDescription className="text-center text-lg">
|
||||
Your AI-powered development platform
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-6">
|
||||
<div className="text-center space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
Vib'n is now running on Coolify! 🎉
|
||||
</p>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
<Card className="bg-muted/50">
|
||||
<CardContent className="pt-6">
|
||||
<h3 className="font-semibold mb-2">✅ API Status</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Connected and operational
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-muted/50">
|
||||
<CardContent className="pt-6">
|
||||
<h3 className="font-semibold mb-2">✅ Database</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
PostgreSQL running
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="pt-4">
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
<strong>Note:</strong> Authentication is being migrated from Firebase to PostgreSQL.
|
||||
<br />
|
||||
This page will be updated with login functionality soon.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4 justify-center">
|
||||
<Button asChild>
|
||||
<Link href="/marks-account/projects">
|
||||
View Projects (Demo)
|
||||
</Link>
|
||||
</Button>
|
||||
<Button variant="outline" asChild>
|
||||
<a href={process.env.NEXT_PUBLIC_PROXY_URL || "#"} target="_blank" rel="noopener noreferrer">
|
||||
API Docs
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* System Status */}
|
||||
<Card className="bg-muted/20">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-sm">System Information</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="text-xs space-y-1 font-mono text-muted-foreground">
|
||||
<div>Frontend: {process.env.NEXT_PUBLIC_APP_URL || 'localhost'}</div>
|
||||
<div>API: {process.env.NEXT_PUBLIC_PROXY_URL || 'Not configured'}</div>
|
||||
<div>Environment: {process.env.NODE_ENV}</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user