feat(scaffold): Next 16 + Payload 3 scaffold with Kaniko CI and Swarm deploy

Initial app scaffold wired end-to-end: Payload 3.82 CMS integrated with Next
16.2 App Router (standalone output), PostgreSQL 17 adapter, Lexical rich text,
Tailwind 3 with Material 3 token palette ported from the stitch technical-
editorial design, self-hosted Space Grotesk + Inter via next/font, and
lucide-react icons. Admin lives at /admin, REST/GraphQL at /api/*, and
/api/health returns build SHA/REV for deploy verification.

Seven collections (Users, Media, Categories, Projects, Posts, Gear,
ContactSubmissions) and six globals (Home, About, Contact, Resume,
Navigation, SEO) model the content outlined in docs/PRD.md.

Multi-stage Dockerfile builds a non-root standalone runner; Woodpecker
pipeline lints, typechecks, builds, audits, builds with Kaniko to
git.mosaicstack.dev, scans with Trivy, and links the package. Swarm
compose mirrors the mosaic-stack-website Traefik entrypoints=web pattern
with www->apex redirect and immutable WEB_IMAGE_TAG.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 21:18:01 -05:00
parent c800bef739
commit 462d938297
51 changed files with 9353 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
export function SiteFooter() {
return (
<footer className="mt-24 border-t border-outline-variant/10 bg-surface-container-lowest">
<div className="mx-auto flex max-w-7xl flex-col gap-6 px-6 py-12 md:flex-row md:items-center md:justify-between">
<div className="font-headline text-sm uppercase tracking-[0.2em] text-on-surface-variant">
© {new Date().getFullYear()} Jason Woltje · All rights reserved
</div>
<div className="font-label text-[10px] uppercase tracking-[0.2em] text-tertiary/80">
LATENCY: 42ms · CORE STATUS: NOMINAL
</div>
</div>
</footer>
);
}

View File

@@ -0,0 +1,35 @@
import Link from "next/link";
const links = [
{ label: "Home", href: "/" },
{ label: "Projects", href: "/projects" },
{ label: "Writing", href: "/writing" },
{ label: "About", href: "/about" },
{ label: "Contact", href: "/contact" },
];
export function SiteHeader() {
return (
<header className="sticky top-0 z-50 w-full bg-background/80 backdrop-blur-xl">
<nav className="mx-auto flex max-w-7xl items-center justify-between px-6 py-4">
<Link
href="/"
className="font-headline text-xl font-bold uppercase tracking-tighter text-primary"
>
JASON WOLTJE
</Link>
<div className="hidden items-center gap-8 md:flex">
{links.map((link) => (
<Link
key={link.href}
href={link.href}
className="font-label text-[14px] uppercase tracking-tighter text-on-surface-variant transition-colors hover:text-primary"
>
{link.label}
</Link>
))}
</div>
</nav>
</header>
);
}

View File

@@ -0,0 +1,23 @@
const BUILD_SHA = process.env.NEXT_PUBLIC_BUILD_SHA ?? "dev";
const BUILD_REV = process.env.NEXT_PUBLIC_BUILD_REV ?? "local";
type Props = {
location?: string;
status?: string;
className?: string;
};
export function StatusTerminal({
location = "39.0997° N, 94.5786° W",
status = "ONLINE",
className = "",
}: Props) {
return (
<div className={`flex items-center gap-3 ${className}`}>
<span className="flex h-2 w-2 rounded-full bg-tertiary shadow-[0_0_8px_#8eff71]" />
<span className="font-label text-[10px] uppercase tracking-[0.2em] text-tertiary">
LOC: {location} · STATUS: {status} · REV: {BUILD_REV} · SHA: {BUILD_SHA}
</span>
</div>
);
}