Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 36 additions & 76 deletions src/app/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,102 +1,62 @@
// @TheTechMargin 2026
"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";

export default function LoginPage() {
const router = useRouter();
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setLoading(true);

try {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ password }),
});

const data = await res.json();

if (!res.ok) {
setError(data.error || "Authentication failed");
setLoading(false);
return;
}

router.push("/");
} catch {
setError("Network error — please try again");
setLoading(false);
}
};
import Link from "next/link";

export default function RetiredPage() {
return (
<div
className="w-full min-h-screen bg-[var(--el-bg)] text-zinc-100 font-mono flex items-center justify-center p-4 scan-line-bg"
>
<div className="w-full max-w-sm">
<div className="w-full min-h-screen bg-[var(--el-bg)] text-zinc-100 font-mono flex items-center justify-center p-4 scan-line-bg">
<div className="w-full max-w-md">
<div className="border border-[var(--el-primary-d9)] bg-[rgba(26,26,26,0.8)] p-6 backdrop-blur">
<div className="flex items-center gap-2 border-b border-[var(--el-primary-99)] pb-3 mb-6">
<div className="h-2 w-2 rounded-full bg-[var(--el-primary)]" />
<div className="h-2 w-2 rounded-full bg-[var(--el-primary-d9)]" />
<div className="h-2 w-2 rounded-full bg-[var(--el-primary-d9)]" />
<span className="ml-2 text-[10px] uppercase tracking-widest text-[var(--el-primary-d9)]">
eventlens://auth
eventlens://notice
</span>
</div>

<div className="space-y-6">
<div>
<h1 className="text-lg font-bold text-[var(--el-primary)] uppercase tracking-wider mb-2">
ACCESS REQUIRED
EVENT CONCLUDED
</h1>
<p className="text-xs text-[var(--el-primary-d9)] uppercase tracking-wider">
Enter your access credential to continue
<p className="text-sm text-zinc-300 leading-relaxed">
The HardMode EventLens has concluded. Reach out to me for access to photos or with questions about the app.
</p>
</div>

<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-3 border-t border-[var(--el-primary-d9)] pt-4">
<div>
<label
htmlFor="password"
className="block text-[10px] uppercase tracking-wider text-[var(--el-primary-d9)] mb-2"
<span className="block text-[10px] uppercase tracking-wider text-[var(--el-primary-d9)] mb-1">
Website
</span>
<Link
href="https://www.thetechmargin.com"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-[var(--el-primary)] hover:text-[var(--el-accent)] transition-colors underline underline-offset-2"
>
PASSWORD
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="••••••••"
className="w-full bg-[var(--el-bg)] border border-[var(--el-primary-d9)] px-3 py-2 text-xs text-[var(--el-primary)] placeholder-[var(--el-accent)] focus:border-[var(--el-primary)] focus:outline-none transition-colors"
disabled={loading}
autoFocus
/>
www.thetechmargin.com
</Link>
</div>
<div>
<span className="block text-[10px] uppercase tracking-wider text-[var(--el-primary-d9)] mb-1">
Email
</span>
<Link
href="mailto:sonia@thetechmargin.com"
className="text-sm text-[var(--el-primary)] hover:text-[var(--el-accent)] transition-colors underline underline-offset-2"
>
sonia@thetechmargin.com
</Link>
</div>
</div>

{error && (
<div className="text-xs text-red-500 uppercase tracking-wider border border-red-500/30 bg-red-500/5 px-3 py-2">
✗ {error}
</div>
)}

<button
type="submit"
disabled={loading}
className="w-full border border-[var(--el-primary-99)] bg-[var(--el-primary-11)] px-4 py-2 text-xs font-bold uppercase tracking-wider text-[var(--el-primary-99)] hover:bg-[var(--el-accent-28)] hover:border-[var(--el-accent)] hover:text-[var(--el-accent)] focus:outline-none focus:ring-1 focus:ring-[var(--el-primary)] disabled:border-[var(--el-amber)]/20 disabled:text-[var(--el-amber)]/40 disabled:bg-transparent disabled:cursor-not-allowed transition-all"
>
{loading ? "VERIFYING..." : "AUTHENTICATE"}
</button>
</form>

<div className="text-center pt-2">
<span className="text-[10px] uppercase tracking-widest text-[var(--el-primary-d9)]">
Thank you for participating
</span>
</div>
</div>
</div>
</div>
Expand Down
15 changes: 2 additions & 13 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,6 @@
// @TheTechMargin 2026
"use client";

import { Suspense } from "react";
import ErrorBoundary from "@/components/ErrorBoundary";
import TerminalLoader from "@/components/gallery/TerminalLoader";
import PhotoGallery from "@/components/gallery/PhotoGallery";
import { redirect } from "next/navigation";

export default function Home() {
return (
<ErrorBoundary>
<Suspense fallback={<TerminalLoader />}>
<PhotoGallery />
</Suspense>
</ErrorBoundary>
);
redirect("/login");
}
28 changes: 8 additions & 20 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,19 @@
// @TheTechMargin 2026
// App retired - redirect all traffic to the retired notice page
import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;

const publicPaths = ["/login", "/api/auth/login"];
const isPublicPath = publicPaths.some((path) => pathname.startsWith(path));

const authCookie = request.cookies.get("auth");
const isAuthenticated = authCookie?.value === "true";

if (!isAuthenticated && !isPublicPath) {
return NextResponse.redirect(new URL("/login", request.url));
}

if (isAuthenticated && pathname === "/login") {
return NextResponse.redirect(new URL("/", request.url));
// Allow access to the login page (now retired notice) and static assets
if (pathname === "/login" || pathname.startsWith("/_next") || pathname.startsWith("/favicon")) {
const response = NextResponse.next();
response.headers.set("Content-Security-Policy", "frame-ancestors 'self'");
return response;
}

const response = NextResponse.next();

response.headers.set(
"Content-Security-Policy",
"frame-ancestors 'self'"
);

return response;
// Redirect all other routes to the retired notice
return NextResponse.redirect(new URL("/login", request.url));
}

export const config = {
Expand Down
Loading