Files
hackerspace.sh/proxy.ts
T
Moheeb Zara 7e15dd1e63 feat: bake hackerspace-sh subsite into the management app
Ports the existing hackerspace.sh Vue site (sibling repo) into this
codebase as a route group `app/(resources)/` so a single domain serves
both the public research / games subsite and the authenticated
management app.

Routes added (all public, all prerendered):
  /resources         landing page (the original HomePage.vue)
  /zine              iframe to the printable hackerspace zine
  /governance        iframe to Governance in a Box
  /space-after-dark  iframe to the isometric exploration game
  /proposal-duel     iframe to the proposal-debate game
  /atlas             iframe to the bundled atlas.html (2.2 MB)

The individual resource paths match the originals exactly so existing
deep links and bookmarks survive the domain switch-over.

Ports six Vue components to React: Wordmark, SpaceIcon, TraceIcon,
CubeIcon, KeyIcon, GridIcon, PromptIcon, AtlasLogo, ProjectCard,
IframeWrapper. CSS scoped under `.resources-root` so the dark IBM Plex
Mono / Libre Baskerville theme cannot bleed into the management app's
shadcn design system. Fonts loaded via next/font/google. The 2.2 MB
atlas.html lives in /public.

proxy.ts adds the six routes plus /atlas.html to PUBLIC_ROUTES so
anonymous visitors can reach them without redirecting to /login.

The management landing page links to /resources twice: a "Resources"
nav link, and a callout under the CTA buttons ("Not running a space?
Read the hackerspace research and games").
2026-05-14 15:18:54 -07:00

74 lines
1.8 KiB
TypeScript

import { updateSession } from '@/lib/supabase/proxy'
import { createServerClient } from '@supabase/ssr'
import { NextResponse, type NextRequest } from 'next/server'
// Public routes are reachable without an authenticated session. The
// /resources subsite is the legacy hackerspace.sh content baked in; the
// five individual resource routes (/zine, /governance, /space-after-dark,
// /proposal-duel, /atlas) must keep these exact paths so existing deep
// links and bookmarks survive the domain switch-over.
const PUBLIC_ROUTES = [
'/',
'/login',
'/signup',
'/auth',
'/api/health',
'/resources',
'/zine',
'/governance',
'/space-after-dark',
'/proposal-duel',
'/atlas',
'/atlas.html',
]
export async function proxy(request: NextRequest) {
const { pathname } = request.nextUrl
// Always update session to refresh cookies
const response = await updateSession(request)
// Allow public routes through without auth check
const isPublic =
pathname === '/' ||
PUBLIC_ROUTES.some(r => pathname === r || pathname.startsWith(r + '/'))
if (isPublic) {
return response
}
// For protected routes, check auth
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
response.cookies.set(name, value, options)
)
},
},
}
)
const {
data: { user },
} = await supabase.auth.getUser()
if (!user) {
return NextResponse.redirect(new URL('/login', request.url))
}
return response
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
],
}