added splash page
This commit is contained in:
parent
41614a96cd
commit
6abe450f1d
2 changed files with 66 additions and 141 deletions
154
app/page.tsx
154
app/page.tsx
|
|
@ -1,129 +1,41 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import Link from "next/link";
|
||||
// app/page.tsx
|
||||
import { cookies } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
import SignIn from "@/app/auth/sign-in/sign-in";
|
||||
import SignUp from "@/app/auth/sign-up/sign-up";
|
||||
|
||||
export default async function HomePage() {
|
||||
// If already signed in, go straight to the app
|
||||
const ck = await cookies();
|
||||
const at = ck.get("ma_at")?.value;
|
||||
if (at) redirect("/portal");
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="min-h-screen p-8 bg-background text-foreground">
|
||||
<h1 className="text-3xl font-bold mb-6">Laser Everything Community Database</h1>
|
||||
<main className="mx-auto max-w-5xl px-4 py-12">
|
||||
<section className="mb-10 text-center">
|
||||
<h1 className="text-3xl font-bold tracking-tight">MakeArmy</h1>
|
||||
<p className="mt-2 text-base text-muted-foreground">
|
||||
Free to use. Manage laser rigs, settings, and projects—all in one place.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Fiber Laser Settings</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Browse and submit settings for fiber laser engraving.
|
||||
</p>
|
||||
<Link href="/fiber-settings">
|
||||
<Button>View Settings</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<section className="grid gap-6 md:grid-cols-2">
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-3 text-lg font-semibold">Create an account</h2>
|
||||
{/* Uses your existing sign-up component */}
|
||||
<SignUp nextPath="/portal" />
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">CO2 Galvo Settings</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Settings for CO2 Galvo laser machines.
|
||||
</p>
|
||||
<Link href="/co2-galvo-settings">
|
||||
<Button>View Settings</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="rounded-lg border p-6">
|
||||
<h2 className="mb-3 text-lg font-semibold">Sign in</h2>
|
||||
{/* Uses your existing sign-in component */}
|
||||
<SignIn nextPath="/portal" reauth={false} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">CO2 Gantry Settings</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Settings for CO2 Gantry laser systems.
|
||||
</p>
|
||||
<Link href="/co2-gantry-settings">
|
||||
<Button>View Settings</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">UV Laser Settings</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Settings for UV laser engraving and marking.
|
||||
</p>
|
||||
<Link href="/uv-settings">
|
||||
<Button>View Settings</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Materials and Coatings</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Explore materials and surface coatings along with their laser safety classifications.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row gap-2">
|
||||
<Link href="/materials">
|
||||
<Button className="w-full sm:w-auto">View Materials</Button>
|
||||
</Link>
|
||||
<Link href="/materials-coatings">
|
||||
<Button className="w-full sm:w-auto">View Coatings</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Laser Source Database</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Technical specs and info on various laser sources.
|
||||
</p>
|
||||
<Link href="/lasers">
|
||||
<Button>View Sources</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Projects Database</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Community-submitted projects and guides.
|
||||
</p>
|
||||
<Link href="/projects">
|
||||
<Button>View Projects</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 🔽 NEW FILE DOWNLOAD SECTION 🔽 */}
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Downloadable Files</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Browse and download shared files from the server.
|
||||
</p>
|
||||
<Link href="/files">
|
||||
<Button>View Files</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 🔽 NEW BUYING GUIDE CARD 🔽 */}
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Buying Guide</h2>
|
||||
<p className="mb-4 text-sm text-muted-foreground">
|
||||
Reviews and recommendations for laser products and accessories.
|
||||
</p>
|
||||
<Link href="/buying-guide">
|
||||
<Button>View Guide</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<section className="mt-8 text-center text-xs text-muted-foreground">
|
||||
We only use cookies strictly necessary to operate the site (e.g., your sign-in session).
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
* Public pages that should remain reachable without being signed in.
|
||||
* Everything else is considered protected (including most /api/*).
|
||||
*/
|
||||
const PUBLIC_PAGES = new Set<string>(["/auth/sign-in", "/auth/sign-up"]);
|
||||
const PUBLIC_PAGES = new Set<string>([
|
||||
"/", // ← splash page is public
|
||||
"/auth/sign-in",
|
||||
"/auth/sign-up",
|
||||
]);
|
||||
|
||||
/**
|
||||
* API paths that are explicitly allowed without auth.
|
||||
|
|
@ -17,8 +21,7 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
];
|
||||
|
||||
/** Directus base (used to remotely validate the token after restarts). */
|
||||
const DIRECTUS =
|
||||
(process.env.NEXT_PUBLIC_API_BASE_URL || process.env.DIRECTUS_URL || "").replace(/\/$/, "");
|
||||
const DIRECTUS = (process.env.NEXT_PUBLIC_API_BASE_URL || process.env.DIRECTUS_URL || "").replace(/\/$/, "");
|
||||
|
||||
/** Helper: does the path start with any prefix in a list? */
|
||||
function startsWithAny(pathname: string, prefixes: string[]) {
|
||||
|
|
@ -47,22 +50,32 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
}
|
||||
}
|
||||
|
||||
/** Build redirect to /auth/sign-in?reauth=1&next=<original>, and clear auth markers. */
|
||||
function kickToSignIn(req: NextRequest) {
|
||||
/**
|
||||
* Build redirect to /auth/sign-in?next=<original>.
|
||||
* Only set reauth=1 (and clear cookies) when opts.reauth === true.
|
||||
*/
|
||||
function kickToSignIn(req: NextRequest, opts?: { reauth?: boolean }) {
|
||||
const wantReauth = !!opts?.reauth;
|
||||
|
||||
const orig = new URL(req.url);
|
||||
const next = orig.pathname + (orig.search || "");
|
||||
|
||||
const url = new URL(req.url);
|
||||
url.pathname = "/auth/sign-in";
|
||||
url.search = "";
|
||||
url.searchParams.set("reauth", "1");
|
||||
if (wantReauth) url.searchParams.set("reauth", "1");
|
||||
url.searchParams.set("next", next);
|
||||
|
||||
const res = NextResponse.redirect(url);
|
||||
// Clear tokens so the very next /auth/* request is truly unauthenticated
|
||||
res.cookies.set("ma_at", "", { maxAge: 0, path: "/" });
|
||||
res.cookies.set("ma_v", "", { maxAge: 0, path: "/" }); // throttle marker
|
||||
// If you also use a refresh token, clear it here too:
|
||||
// res.cookies.set("ma_rt", "", { maxAge: 0, path: "/" });
|
||||
|
||||
// Only clear auth markers in true re-auth scenarios
|
||||
if (wantReauth) {
|
||||
res.cookies.set("ma_at", "", { maxAge: 0, path: "/" });
|
||||
res.cookies.set("ma_v", "", { maxAge: 0, path: "/" }); // throttle marker
|
||||
// If you also use a refresh token, clear it here too:
|
||||
// res.cookies.set("ma_rt", "", { maxAge: 0, path: "/" });
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -90,9 +103,9 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
isAuthRoute &&
|
||||
(url.searchParams.get("reauth") === "1" || url.searchParams.get("force") === "1");
|
||||
|
||||
// If unauthenticated and the route is protected, send to sign-in (with next + reauth)
|
||||
// If unauthenticated and the route is protected, send to sign-in WITHOUT reauth
|
||||
if (!token && isProtected) {
|
||||
return kickToSignIn(req);
|
||||
return kickToSignIn(req, { reauth: false });
|
||||
}
|
||||
|
||||
// If we have a token, perform local expiry check.
|
||||
|
|
@ -100,8 +113,7 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
const exp = jwtExp(token);
|
||||
const expired = !exp || exp * 1000 <= Date.now();
|
||||
|
||||
// If it's an auth route and token looks valid, keep your existing UX:
|
||||
// bounce away from auth pages — unless this is a forced reauth.
|
||||
// If it's an auth route and token looks valid, bounce away from auth pages — unless this is a forced reauth.
|
||||
if (isAuthRoute && !expired && !forceAuth) {
|
||||
url.pathname = "/portal";
|
||||
url.search = "";
|
||||
|
|
@ -111,7 +123,8 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
// If protected route: enforce validity
|
||||
if (isProtected) {
|
||||
if (expired) {
|
||||
return kickToSignIn(req);
|
||||
// True reauth
|
||||
return kickToSignIn(req, { reauth: true });
|
||||
}
|
||||
|
||||
// ── Throttled remote validation (catches server restarts / revoked tokens)
|
||||
|
|
@ -131,8 +144,8 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
});
|
||||
|
||||
if (!r.ok) {
|
||||
// Token no longer valid on the server → force re-auth, carry next
|
||||
return kickToSignIn(req);
|
||||
// Token no longer valid on the server → true reauth, carry next
|
||||
return kickToSignIn(req, { reauth: true });
|
||||
}
|
||||
|
||||
// Cache the success for ~1 minute to avoid hammering Directus
|
||||
|
|
@ -146,7 +159,7 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
return res;
|
||||
} catch {
|
||||
// If Directus is unreachable, be conservative and require re-auth
|
||||
return kickToSignIn(req);
|
||||
return kickToSignIn(req, { reauth: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +226,7 @@ import { NextResponse, NextRequest } from "next/server";
|
|||
}
|
||||
|
||||
function isPublicPath(pathname: string): boolean {
|
||||
// 1) Public pages (auth screens)
|
||||
// 1) Public pages (root splash & auth screens)
|
||||
if (PUBLIC_PAGES.has(pathname)) return true;
|
||||
|
||||
// 2) Static assets / internals
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue