diff --git a/app/app/api/bgbye/process/route.ts b/app/app/api/bgbye/process/route.ts
index 7a91181..d41da54 100644
--- a/app/app/api/bgbye/process/route.ts
+++ b/app/app/api/bgbye/process/route.ts
@@ -1,7 +1,6 @@
export const runtime = "nodejs";
import { NextResponse } from "next/server";
-import { getUserBearerFromRequest } from "@/lib/directus";
import { rateLimit, requestIp } from "@/lib/rate-limit";
const MAX_BODY_BYTES = 30 * 1024 * 1024;
@@ -21,9 +20,6 @@ export async function POST(req: Request) {
);
}
- if (!getUserBearerFromRequest(req)) {
- return NextResponse.json({ error: "Not authenticated" }, { status: 401 });
- }
if (!rateLimit(`bgbye:${requestIp(req)}`, 10, 60_000)) {
return NextResponse.json({ error: "Too many processing requests" }, { status: 429 });
}
diff --git a/app/app/page.tsx b/app/app/page.tsx
index 739763a..cf20182 100644
--- a/app/app/page.tsx
+++ b/app/app/page.tsx
@@ -1,49 +1,15 @@
-// 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";
-import { isJwtValid } from "@/lib/jwt";
+import { Suspense } from "react";
+import TemporaryUtilityHub from "@/components/TemporaryUtilityHub";
-type SearchParams = { [key: string]: string | string[] | undefined };
-
-export default async function HomePage({
- searchParams,
-}: {
- searchParams?: SearchParams;
-}) {
- // If already signed in with a VALID token, go straight to the app
- const ck = await cookies();
- const at = ck.get("ma_at")?.value;
- if (isJwtValid(at)) redirect("/portal");
-
- const reauth = searchParams?.reauth === "1";
+export const metadata = {
+ title: "Laser Everything Community Utilities",
+ description: "Free laser calculators, public files, and image background removal tools.",
+};
+export default function HomePage() {
return (
-
- {reauth && (
-
- Your session expired. Please sign in again.
-
- )}
-
-
- MakeArmy
-
- Free to use. Manage laser rigs, settings, and projects—all in one
- place.
-
-
-
-
-
-
- This is the beta build v0.1.1 - this site is an active BETA. Some features may not be available or work as intended. If you're experiencing issues please report them here: https://forge.makearmy.io/makearmy/makearmy-app/issues
- PRIVACY: We only use cookies strictly necessary to operate the site (e.g., your sign-in session). We do not store user data or telemetry other than what you provide and never share or sell data to third parties. Ever.
-
-
+ Loading utilities…}>
+
+
);
}
diff --git a/app/components/TemporaryUtilityHub.tsx b/app/components/TemporaryUtilityHub.tsx
new file mode 100644
index 0000000..3f03332
--- /dev/null
+++ b/app/components/TemporaryUtilityHub.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import dynamic from "next/dynamic";
+import { useMemo } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
+import { cn } from "@/lib/utils";
+
+const LaserToolkit = dynamic(
+ () => import("@/components/portal/LaserToolkitSwitcher"),
+ { ssr: false }
+);
+const FileBrowser = dynamic(
+ () => import("@/components/utilities/files/FileBrowserPanel"),
+ { ssr: false }
+);
+const BackgroundRemover = dynamic(
+ () => import("@/components/utilities/BackgroundRemoverPanel"),
+ { ssr: false }
+);
+
+const TOOLS = [
+ {
+ key: "laser-toolkit",
+ label: "Laser Toolkit",
+ description: "Calculators for beam size, overlap, hatch spacing, job time, and more.",
+ icon: "/images/utils/toolkit.png",
+ component: LaserToolkit,
+ },
+ {
+ key: "files",
+ label: "File Server",
+ description: "Browse, preview, and download files from the public library.",
+ icon: "/images/utils/fs.png",
+ component: FileBrowser,
+ },
+ {
+ key: "background-remover",
+ label: "Background Remover",
+ description: "Remove image backgrounds using our self-hosted processing service.",
+ icon: "/images/utils/bgrm.png",
+ component: BackgroundRemover,
+ },
+] as const;
+
+export default function TemporaryUtilityHub() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const activeKey = searchParams.get("tool") || TOOLS[0].key;
+ const active = useMemo(
+ () => TOOLS.find((tool) => tool.key === activeKey) || TOOLS[0],
+ [activeKey]
+ );
+ const ActiveTool = active.component;
+
+ function selectTool(key: string) {
+ const query = new URLSearchParams(searchParams.toString());
+ query.set("tool", key);
+ if (key !== "laser-toolkit") query.delete("lt");
+ router.replace(`/?${query.toString()}`, { scroll: false });
+ }
+
+ return (
+
+
+
+
+ {TOOLS.map((tool) => {
+ const selected = tool.key === active.key;
+ return (
+ selectTool(tool.key)}
+ aria-pressed={selected}
+ className={cn(
+ "flex min-h-24 items-start gap-4 rounded-xl border p-4 text-left transition",
+ selected
+ ? "border-primary bg-primary text-primary-foreground shadow-sm"
+ : "bg-card hover:border-muted-foreground/50 hover:bg-muted/50"
+ )}
+ >
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ {tool.label}
+
+ {tool.description}
+
+
+
+ );
+ })}
+
+
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+
+
{active.description}
+
+
+
+
+
+
+ No account is required. Database-backed profiles, projects, settings, and submissions
+ are intentionally unavailable on this temporary site.
+
+
+ );
+}
diff --git a/app/components/portal/LaserToolkitSwitcher.tsx b/app/components/portal/LaserToolkitSwitcher.tsx
index 34e7673..69ddfda 100644
--- a/app/components/portal/LaserToolkitSwitcher.tsx
+++ b/app/components/portal/LaserToolkitSwitcher.tsx
@@ -6,7 +6,7 @@ import { useSearchParams, useRouter } from "next/navigation";
import { cn } from "@/lib/utils";
import { TOOLKIT_TABS } from "@/components/utilities/laser-toolkit/registry";
-export default function LaserToolkitSwitcher() {
+export default function LaserToolkitSwitcher({ basePath = "/portal/utilities" }: { basePath?: string }) {
const sp = useSearchParams();
const router = useRouter();
@@ -24,7 +24,7 @@ export default function LaserToolkitSwitcher() {
function setTab(k: string) {
const q = new URLSearchParams(sp.toString());
q.set("lt", k);
- router.replace(`/portal/utilities?${q.toString()}`, { scroll: false });
+ router.replace(`${basePath}?${q.toString()}`, { scroll: false });
}
if (!active) {
diff --git a/app/components/utilities/files/FileBrowserPanel.tsx b/app/components/utilities/files/FileBrowserPanel.tsx
index 28033d9..fae849a 100644
--- a/app/components/utilities/files/FileBrowserPanel.tsx
+++ b/app/components/utilities/files/FileBrowserPanel.tsx
@@ -208,8 +208,8 @@ export default function FileBrowserPanel() {
{path || "/"}
-
-
+
+
Name
Type
diff --git a/app/middleware.ts b/app/middleware.ts
index 2b5bf99..a178aa6 100644
--- a/app/middleware.ts
+++ b/app/middleware.ts
@@ -1,217 +1,62 @@
-// middleware.ts
-import { NextResponse, NextRequest } from "next/server";
+import { NextRequest, NextResponse } from "next/server";
+
+const PUBLIC_UTILITY_APIS = new Set([
+ "/api/bgbye/process",
+ "/api/files/list",
+ "/api/files/raw",
+ "/api/files/download",
+]);
+
+const LEGACY_TOOL_PATHS: Record
= {
+ "/laser-toolkit": "laser-toolkit",
+ "/files": "files",
+ "/background-remover": "background-remover",
+};
/**
- * Public pages that should remain reachable without being signed in.
- * Everything else is considered protected (including most /api/*).
+ * Temporary utility-only site.
+ *
+ * Directus-backed pages and APIs remain in source history but cannot be reached
+ * through this deployment. This middleware deliberately performs no session or
+ * Directus request.
*/
- const PUBLIC_PAGES = new Set([
- "/", // splash page is public
- "/auth/sign-in",
- "/auth/sign-up",
- ]);
+export function middleware(req: NextRequest) {
+ const { pathname } = req.nextUrl;
- /**
- * API paths that are explicitly allowed without auth.
- * Keep this list tiny; add broad /api/webhooks to allow ALL webhook endpoints.
- */
- const PUBLIC_API_PREFIXES: string[] = [
- "/api/auth/", // login/refresh/callback endpoints
- ];
+ if (pathname === "/") return NextResponse.next();
- /** 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 legacyTool = LEGACY_TOOL_PATHS[pathname.replace(/\/$/, "")];
+ if (legacyTool) {
+ const url = req.nextUrl.clone();
+ url.pathname = "/";
+ url.search = "";
+ url.searchParams.set("tool", legacyTool);
+ return NextResponse.redirect(url);
+ }
- type MapResult = { pathname: string; query?: Record };
+ if (pathname === "/portal/utilities") {
+ const url = req.nextUrl.clone();
+ const requested = url.searchParams.get("t");
+ url.pathname = "/";
+ url.search = "";
+ if (requested && ["laser-toolkit", "files", "background-remover"].includes(requested)) {
+ url.searchParams.set("tool", requested);
+ }
+ return NextResponse.redirect(url);
+ }
- /** Helper: does the path start with any prefix in a list? */
- function startsWithAny(pathname: string, prefixes: string[]) {
- return prefixes.some((p) => pathname.startsWith(p));
- }
+ if (PUBLIC_UTILITY_APIS.has(pathname)) return NextResponse.next();
- /** Helper: are we about to redirect to the same URL? */
- function isSameUrl(req: NextRequest, mapped: MapResult) {
- const dest = new URL(req.url);
- dest.pathname = mapped.pathname;
- if (mapped.query) {
- for (const [k, v] of Object.entries(mapped.query)) dest.searchParams.set(k, v);
- }
- return dest.href === req.url;
- }
+ if (pathname.startsWith("/api/")) {
+ return NextResponse.json({ error: "Not available on the temporary utility site" }, { status: 404 });
+ }
- /** Decode JWT exp (seconds since epoch). We don't verify signature here. */
- function jwtExp(token: string): number | null {
- try {
- const [, payload] = token.split(".");
- if (!payload) return null;
- const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
- return typeof json.exp === "number" ? json.exp : null;
- } catch {
- return null;
- }
- }
+ const home = req.nextUrl.clone();
+ home.pathname = "/";
+ home.search = "";
+ return NextResponse.redirect(home);
+}
- /**
- * Build redirect to /auth/sign-in?next=.
- * 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 = "";
- if (wantReauth) url.searchParams.set("reauth", "1");
- url.searchParams.set("next", next);
-
- const res = NextResponse.redirect(url);
-
- if (wantReauth) {
- res.cookies.set("ma_at", "", { maxAge: 0, path: "/" });
- res.cookies.set("ma_v", "", { maxAge: 0, path: "/" });
- }
-
- return res;
- }
-
- export async function middleware(req: NextRequest) {
- const url = req.nextUrl.clone();
- const { pathname } = url;
-
- // ── -1) Allow only the explicitly configured external webhook.
- if (pathname === "/api/webhooks/kofi") {
- return NextResponse.next();
- }
-
- // ── 0) Root must never redirect
- if (pathname === "/") return NextResponse.next();
-
- // ── 1) Legacy → Portal mapping (before auth gating)
- const mapped = legacyMap(pathname);
- if (mapped && !isSameUrl(req, mapped)) {
- url.pathname = mapped.pathname;
- if (mapped.query) {
- for (const [k, v] of Object.entries(mapped.query)) url.searchParams.set(k, v);
- }
- return NextResponse.redirect(url);
- }
-
- // ── 2) Auth gating
- const token = req.cookies.get("ma_at")?.value ?? "";
- const isAuthRoute = pathname.startsWith("/auth/");
- const isProtected = !isPublicPath(pathname);
-
- const forceAuth =
- isAuthRoute &&
- (url.searchParams.get("reauth") === "1" ||
- url.searchParams.get("force") === "1");
-
- if (!token && isProtected) {
- return kickToSignIn(req, { reauth: false });
- }
-
- if (token) {
- const exp = jwtExp(token);
- const expired = !exp || exp * 1000 <= Date.now();
-
- if (isAuthRoute && !expired && !forceAuth) {
- url.pathname = "/portal";
- url.search = "";
- return NextResponse.redirect(url);
- }
-
- if (isProtected) {
- if (expired) {
- return kickToSignIn(req, { reauth: true });
- }
-
- if (DIRECTUS) {
- const nowMinute = Math.floor(Date.now() / 60000).toString();
- const lastValidated = req.cookies.get("ma_v")?.value;
-
- if (lastValidated !== nowMinute) {
- try {
- const r = await fetch(`${DIRECTUS}/users/me?fields=id`, {
- headers: {
- Authorization: `Bearer ${token}`,
- Accept: "application/json",
- },
- cache: "no-store",
- });
-
- if (!r.ok) {
- return kickToSignIn(req, { reauth: true });
- }
-
- const res = NextResponse.next();
- res.cookies.set("ma_v", nowMinute, {
- path: "/",
- httpOnly: false,
- sameSite: "lax",
- maxAge: 90,
- });
- return res;
- } catch {
- return kickToSignIn(req, { reauth: true });
- }
- }
- }
- }
- }
-
- return NextResponse.next();
- }
-
- function legacyMap(pathname: string): MapResult | null {
- if (pathname === "/" || pathname.startsWith("/portal")) return null;
-
- const listRules: Array<[RegExp, MapResult]> = [
- [/^\/background-remover\/?$/i, { pathname: "/portal/utilities", query: { t: "background-remover" } }],
- [/^\/svgnest\/?$/i, { pathname: "/portal/utilities", query: { t: "svgnest" } }],
- [/^\/laser-toolkit\/?$/i, { pathname: "/portal/utilities", query: { t: "laser-toolkit" } }],
- [/^\/files\/?$/i, { pathname: "/portal/utilities", query: { t: "files" } }],
- [/^\/buying-guide\/?$/i, { pathname: "/portal/buying-guide" }],
- [/^\/lasers\/?$/i, { pathname: "/portal/laser-sources" }],
- [/^\/projects\/?$/i, { pathname: "/portal/projects" }],
- [/^\/my\/rigs\/?$/i, { pathname: "/portal/rigs", query: { t: "my" } }],
- ];
-
- for (const [re, dest] of listRules) {
- if (re.test(pathname)) return dest;
- }
-
- return null;
- }
-
- function isPublicPath(pathname: string): boolean {
- if (PUBLIC_PAGES.has(pathname)) return true;
-
- if (
- pathname.startsWith("/_next/") ||
- pathname.startsWith("/static/") ||
- pathname.startsWith("/images/") ||
- pathname === "/favicon.ico" ||
- pathname === "/robots.txt" ||
- pathname === "/sitemap.xml"
- ) {
- return true;
- }
-
- if (pathname.startsWith("/api/")) {
- return startsWithAny(pathname, PUBLIC_API_PREFIXES);
- }
-
- return false;
- }
-
- // Match all except the usual static assets
- export const config = {
- matcher: ["/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|images|static).*)"],
- };
+export const config = {
+ matcher: ["/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|images|static).*)"],
+};