From 453d04da978d3c76b1cddcf61db3061ffd8f5817 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 14:00:51 -0400 Subject: [PATCH 01/19] Add welcome page to display at / --- next.config.ts | 10 +--- public/index.html | 43 +++++++++++++++ src/app/page.tsx | 133 ---------------------------------------------- 3 files changed, 44 insertions(+), 142 deletions(-) create mode 100644 public/index.html delete mode 100644 src/app/page.tsx diff --git a/next.config.ts b/next.config.ts index a67efdf..575d24e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,15 +3,7 @@ import createMDX from '@next/mdx' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], output: 'standalone', - async redirects() { - return [ - { - source: '/', - destination: '/blog', - permanent: true, - }, - ] - }, + basePath: '/blog' } const withMDX = createMDX({ extension: /\.mdx?$/, diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..96db37f --- /dev/null +++ b/public/index.html @@ -0,0 +1,43 @@ + + + + + + Welcome to Nexlayer + + + + +
+

+ Welcome to Nexlayer! +

+

+ Your AI-native cloud deployment platform +

+ + + + + Read Our Blog + +
+ + diff --git a/src/app/page.tsx b/src/app/page.tsx deleted file mode 100644 index cfe2282..0000000 --- a/src/app/page.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import Image from "next/image"; -import fs from "fs"; -import path from "path"; -import matter from "gray-matter"; -// import LogoIcon from "../../../public/logo-icon.svg"; - -function getAllPosts() { - const contentDir = path.join(process.cwd(), "src", "content"); - const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); - return files.map((filename) => { - const filePath = path.join(contentDir, filename); - const source = fs.readFileSync(filePath, "utf-8"); - const { data } = matter(source); - - return { - title: data.title || filename.replace(/\.mdx$/, ""), - description: data.description || "", - author: data.author || "Unknown", - avatar: data.avatar || "/placeholder.svg", - readTime: data.readTime || "", - slug: filename.replace(/\.mdx$/, ""), - date: data.date || null, - }; - }); -} - -export default function BlogPage() { - const posts = getAllPosts(); - const featuredPosts = posts.slice(0, 4); - function parseCustomDate(dateStr: string | null): number { - if (!dateStr) return 0; - const months: { [key: string]: string } = { - JANUARY: "01", FEBRUARY: "02", MARCH: "03", APRIL: "04", MAY: "05", JUNE: "06", - JULY: "07", AUGUST: "08", SEPTEMBER: "09", OCTOBER: "10", NOVEMBER: "11", DECEMBER: "12" - }; - const parts: string[] = dateStr.toUpperCase().replace(/,/g, "").split(" "); - if (parts.length === 3 && months[parts[0] as keyof typeof months]) { - return new Date(`${parts[2]}-${months[parts[0] as keyof typeof months]}-${parts[1].padStart(2, "0")}`).getTime(); - } - return new Date(dateStr).getTime(); - } - const allPosts = [...posts].sort((a, b) => { - return parseCustomDate(b.date) - parseCustomDate(a.date); - }); - return ( -
-
-
-
-

Blog

-

- Compiled notes from the team -

-
- -
-

Featured

-
- {featuredPosts.map((post) => ( - -
-

- {post.title} -

-

- {post.description} -

- -
- {post.author} -
- - By {post.author} - - - {post.readTime} - -
-
-
- - ))} -
-
- -
-

All posts

-
- {allPosts.map((post) => ( - -
-

- {post.title} -

-

- {post.description} -

- -
- {post.author} -
- - By {post.author} - - - {post.readTime} - -
-
-
- - ))} -
-
-
-
-
- ); -} From 1391da6f41ee0a1911f52534ac9979b4cc4fdcaf Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 14:05:15 -0400 Subject: [PATCH 02/19] Try removing basePath = /blog --- next.config.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/next.config.ts b/next.config.ts index 575d24e..873b89e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,8 +2,7 @@ import { NextConfig } from 'next' import createMDX from '@next/mdx' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], - output: 'standalone', - basePath: '/blog' + output: 'standalone' } const withMDX = createMDX({ extension: /\.mdx?$/, From e611ab213b43925098a5bb24d606af4013a8ae04 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 14:30:39 -0400 Subject: [PATCH 03/19] Try something different --- public/index.html | 43 ------------------------------------------- src/app/page.tsx | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 43 deletions(-) delete mode 100644 public/index.html create mode 100644 src/app/page.tsx diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 96db37f..0000000 --- a/public/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - Welcome to Nexlayer - - - - -
-

- Welcome to Nexlayer! -

-

- Your AI-native cloud deployment platform -

- - - - - Read Our Blog - -
- - diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..2246a33 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import Link from "next/link"; + +export default function WelcomePage() { + return ( +
+
+

+ Welcome to Nexlayer! +

+

+ Your AI-native cloud deployment platform +

+ + + + + Read Our Blog + +
+
+ ); +} From d63e13ee842aa91a7d4a7737a95768d63b2e7b4b Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 14:35:42 -0400 Subject: [PATCH 04/19] Update nexlayer.yaml --- nexlayer.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nexlayer.yaml b/nexlayer.yaml index 5b32743..e9e426e 100644 --- a/nexlayer.yaml +++ b/nexlayer.yaml @@ -3,7 +3,7 @@ application: pods: - name: blog image: ghcr.io/nexlayer/nexlayer-blog:latest - path: "/blog" + path: "/" servicePorts: [3000] vars: NODE_ENV: "production" From 192a51198c8d28ebe827bdc25ed0658f381a07f8 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 14:49:24 -0400 Subject: [PATCH 05/19] Fix issue when clicking on blog post --- src/app/blog/[slug]/page.tsx | 49 +++++++++++++------ ...we're-building-the-agent-native-future.mdx | 2 +- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index a19614e..7e8c6bd 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -2,7 +2,6 @@ import React from "react"; import Image from "next/image"; import Link from "next/link"; import BlogMdxContent from "../../../components/BlogMdxContent"; -import BlogContentClient from "../../../components/BlogContentClient"; import fs from "fs"; import { ArrowLeft } from "lucide-react"; import path from "path"; @@ -21,17 +20,28 @@ async function getPostBySlug(slug: string) { if (!fs.existsSync(filePath)) return null; const source = fs.readFileSync(filePath, "utf-8"); const { data, content } = matter(source); - const mdxSource = await serialize(content, { scope: data }); - return { - title: data.title || slug, - description: data.description || "", - author: data.author || "Unknown", - avatar: data.avatar || "/placeholder.svg", - readTime: data.readTime || "", - date: data.date || "", - mdxSource, - content, - }; + + try { + const mdxSource = await serialize(content, { + scope: data, + mdxOptions: { + development: process.env.NODE_ENV === 'development' + } + }); + return { + title: data.title || slug, + description: data.description || "", + author: data.author || "Unknown", + avatar: data.avatar || "/placeholder.svg", + readTime: data.readTime || "", + date: data.date || "", + mdxSource, + content, + }; + } catch (error) { + console.error('Error serializing MDX:', error); + return null; + } } const BlogPost = async ({ params }: BlogPostProps) => { @@ -42,6 +52,13 @@ const BlogPost = async ({ params }: BlogPostProps) => { return ; } + // Debug logging + console.log('Post data:', { + title: post.title, + avatar: post.avatar, + author: post.author + }); + return (
@@ -62,7 +79,7 @@ const BlogPost = async ({ params }: BlogPostProps) => {
{post.author} {
- } /> + {post.mdxSource ? ( + + ) : ( +
Error loading content
+ )}
diff --git a/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx b/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx index ca63185..7978f4d 100644 --- a/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx +++ b/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx @@ -2,7 +2,7 @@ title: "The Cloud Agents Trust™ — Why We're Building the Agent-Native Future" description: "How Nexlayer became the infrastructure backbone for AI-generated applications worldwide." author: "Sarah Chen" -avatar: "/blog/developer-avatar.png" +avatar: "/developer-avatar.png" readTime: "3 minutes read" date: "AUGUST 5, 2025" --- From 45db6e0f2afc7bd0d7f97bf5519be50a1afcc25d Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:14:46 -0400 Subject: [PATCH 06/19] Try a different approach --- Dockerfile | 61 ++++++++---------------------------- default.conf | 44 ++++++++++++++++++++++++++ next.config.ts | 4 ++- src/app/blog/[slug]/page.tsx | 2 +- src/app/blog/page.tsx | 8 ++--- 5 files changed, 65 insertions(+), 54 deletions(-) create mode 100644 default.conf diff --git a/Dockerfile b/Dockerfile index 3a86f8a..51b753a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,55 +1,20 @@ -# Use Node.js 18 Alpine as base image -FROM node:18-alpine AS base - -# Install dependencies only when needed -FROM base AS deps -# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. -RUN apk add --no-cache libc6-compat -WORKDIR /app - -# Install dependencies (include devDependencies so build works) -COPY package.json package-lock.json* ./ -RUN npm ci - -# Rebuild the source code only when needed -FROM base AS builder +# Build stage +FROM node:20-alpine AS builder WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules COPY . . - -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -ENV NEXT_TELEMETRY_DISABLED 1 - +RUN npm install RUN npm run build -# Production image, copy all the files and run next -FROM base AS runner -WORKDIR /app - -ENV NODE_ENV production -ENV NEXT_TELEMETRY_DISABLED 1 - -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -COPY --from=builder /app/public ./public - -# Set the correct permission for prerender cache -RUN mkdir .next -RUN chown nextjs:nodejs .next - -# Automatically leverage output traces to reduce image size -# https://nextjs.org/docs/advanced-features/output-file-tracing -COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ -COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static - -USER nextjs +# Production stage +FROM nginx:alpine +WORKDIR /usr/share/nginx/html +RUN mkdir blog +COPY --from=builder /app/build ./blog -EXPOSE 3000 +# Replace default.conf with our own +RUN rm -rf /etc/nginx/conf.d/* +COPY default.conf /etc/nginx/conf.d/default.conf -ENV PORT 3000 -ENV HOSTNAME "0.0.0.0" +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] -# server.js is created by next build from the standalone output -CMD ["node", "server.js"] diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..c67dee5 --- /dev/null +++ b/default.conf @@ -0,0 +1,44 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index 873b89e..88a2579 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,7 +2,9 @@ import { NextConfig } from 'next' import createMDX from '@next/mdx' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], - output: 'standalone' + output: 'export', // Enable static export + distDir: 'build', // Output directory + basePath: '/blog' } const withMDX = createMDX({ extension: /\.mdx?$/, diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 7e8c6bd..a551ecb 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -64,7 +64,7 @@ const BlogPost = async ({ params }: BlogPostProps) => {
diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 78c6914..d8d4c57 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -59,7 +59,7 @@ export default function BlogPage() {

Featured

{featuredPosts.map((post) => ( - +

{post.title} @@ -70,7 +70,7 @@ export default function BlogPage() {
{post.author}All posts

{allPosts.map((post) => ( - +

{post.title} @@ -106,7 +106,7 @@ export default function BlogPage() {
{post.author} Date: Mon, 1 Sep 2025 15:19:13 -0400 Subject: [PATCH 07/19] Updates --- next.config.ts | 4 ++-- src/app/sitemap.xml/route.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/next.config.ts b/next.config.ts index 88a2579..d6ec00b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,8 +2,8 @@ import { NextConfig } from 'next' import createMDX from '@next/mdx' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], - output: 'export', // Enable static export - distDir: 'build', // Output directory + output: 'standalone', + distDir: 'build', basePath: '/blog' } const withMDX = createMDX({ diff --git a/src/app/sitemap.xml/route.ts b/src/app/sitemap.xml/route.ts index 07c0818..0f35939 100644 --- a/src/app/sitemap.xml/route.ts +++ b/src/app/sitemap.xml/route.ts @@ -14,7 +14,7 @@ export async function GET() { slugs = []; } - const urls = ["/", "/blog", ...slugs.map((slug) => `/blog/${slug}`)]; + const urls = ["/blog", ...slugs.map((slug) => `/blog/${slug}`)]; const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://nexlayer.com"; const sitemap = `\n\n${urls From 0cba6e4650d69383d7ab5ef1e9b836485f252c25 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:27:18 -0400 Subject: [PATCH 08/19] Updates --- next.config.ts | 4 ++-- src/app/robots.txt/route.ts | 12 ------------ src/app/sitemap.xml/route.ts | 32 -------------------------------- 3 files changed, 2 insertions(+), 46 deletions(-) delete mode 100644 src/app/robots.txt/route.ts delete mode 100644 src/app/sitemap.xml/route.ts diff --git a/next.config.ts b/next.config.ts index d6ec00b..88a2579 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,8 +2,8 @@ import { NextConfig } from 'next' import createMDX from '@next/mdx' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], - output: 'standalone', - distDir: 'build', + output: 'export', // Enable static export + distDir: 'build', // Output directory basePath: '/blog' } const withMDX = createMDX({ diff --git a/src/app/robots.txt/route.ts b/src/app/robots.txt/route.ts deleted file mode 100644 index ae87053..0000000 --- a/src/app/robots.txt/route.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NextResponse } from "next/server"; - -export async function GET() { - const robots = `User-agent: *\nAllow: /\nSitemap: ${ - process.env.NEXT_PUBLIC_SITE_URL || "https://nexlayer.com" - }/sitemap.xml`; - return new NextResponse(robots, { - headers: { - "Content-Type": "text/plain", - }, - }); -} diff --git a/src/app/sitemap.xml/route.ts b/src/app/sitemap.xml/route.ts deleted file mode 100644 index 0f35939..0000000 --- a/src/app/sitemap.xml/route.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NextResponse } from "next/server"; -import fs from "fs"; -import path from "path"; - -export async function GET() { - const contentDir = path.join(process.cwd(), "src", "content"); - let slugs: string[] = []; - try { - slugs = fs - .readdirSync(contentDir) - .filter((file) => file.endsWith(".mdx")) - .map((file) => file.replace(/\.mdx$/, "")); - } catch (err) { - slugs = []; - } - - const urls = ["/blog", ...slugs.map((slug) => `/blog/${slug}`)]; - - const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || "https://nexlayer.com"; - const sitemap = `\n\n${urls - .map( - (url) => - ` ${baseUrl}${url}monthly1.0` - ) - .join("\n")}\n`; - - return new NextResponse(sitemap, { - headers: { - "Content-Type": "application/xml", - }, - }); -} From 0dd0bc007286a875dfad817f9373d2b287ce393c Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:31:19 -0400 Subject: [PATCH 09/19] Export fix --- src/app/[slug]/page.tsx | 13 +++++++++++++ src/app/blog/[slug]/page.tsx | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx index b66b892..f90c60a 100644 --- a/src/app/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -10,6 +10,19 @@ import NotFound from "@/app/not-found"; import { serialize } from "next-mdx-remote/serialize"; +// Generate static params for all blog posts +export async function generateStaticParams() { + const contentDir = path.join(process.cwd(), "src", "content"); + try { + const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); + return files.map((filename) => ({ + slug: filename.replace(/\.mdx$/, ""), + })); + } catch (err) { + return []; + } +} + interface BlogPostProps { params: Promise<{ slug: string }>; } diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index a551ecb..12d87a5 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -10,6 +10,19 @@ import NotFound from "@/app/not-found"; import { serialize } from "next-mdx-remote/serialize"; +// Generate static params for all blog posts +export async function generateStaticParams() { + const contentDir = path.join(process.cwd(), "src", "content"); + try { + const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); + return files.map((filename) => ({ + slug: filename.replace(/\.mdx$/, ""), + })); + } catch (err) { + return []; + } +} + interface BlogPostProps { params: Promise<{ slug: string }>; } From 76e52def13e28281a4591158d147238ed4e12646 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:33:39 -0400 Subject: [PATCH 10/19] Update --- src/app/[slug]/page.tsx | 100 ---------------------------------------- 1 file changed, 100 deletions(-) delete mode 100644 src/app/[slug]/page.tsx diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx deleted file mode 100644 index f90c60a..0000000 --- a/src/app/[slug]/page.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import React from "react"; -import Image from "next/image"; -import Link from "next/link"; -import BlogMdxContent from "../../components/BlogMdxContent"; -import fs from "fs"; -import { ArrowLeft } from "lucide-react"; -import path from "path"; -import matter from "gray-matter"; -import NotFound from "@/app/not-found"; - -import { serialize } from "next-mdx-remote/serialize"; - -// Generate static params for all blog posts -export async function generateStaticParams() { - const contentDir = path.join(process.cwd(), "src", "content"); - try { - const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); - return files.map((filename) => ({ - slug: filename.replace(/\.mdx$/, ""), - })); - } catch (err) { - return []; - } -} - -interface BlogPostProps { - params: Promise<{ slug: string }>; -} - -async function getPostBySlug(slug: string) { - const contentDir = path.join(process.cwd(), "src", "content"); - const filePath = path.join(contentDir, `${slug}.mdx`); - if (!fs.existsSync(filePath)) return null; - const source = fs.readFileSync(filePath, "utf-8"); - const { data, content } = matter(source); - const mdxSource = await serialize(content, { scope: data }); - return { - title: data.title || slug, - description: data.description || "", - author: data.author || "Unknown", - avatar: data.avatar || "/placeholder.svg", - readTime: data.readTime || "", - date: data.date || "", - mdxSource, - content, - }; -} - -const BlogPost = async ({ params }: BlogPostProps) => { - const { slug } = await params; - const post = await getPostBySlug(slug); - - if (!post) { - return ; - } - - return ( -
-
-
- - - BACK TO THE MAIN BLOG - - -

{post.date}

- -

{post.title}

- -

{post.description}

- -
- {post.author} -
- Posted By {post.author} - {post.readTime} -
-
- -
- -
-
-
-
- ); -}; - -export default BlogPost; From 5e17e8d998488c243ce740e0401913af1894d0df Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:38:00 -0400 Subject: [PATCH 11/19] Fixes for static --- src/app/layout.tsx | 4 ++-- src/components/BlogCodeBlock.tsx | 1 - src/components/BlogMdxContent.tsx | 1 - src/components/MdxRenderer.tsx | 1 - src/components/StaticNavbar.tsx | 35 +++++++++++++++++++++++++++++++ 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/components/StaticNavbar.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ee269f2..18cf09b 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; -import Navbar from "../components/Navbar"; +import StaticNavigation from "../components/StaticNavbar"; import Footer from "../components/Footer"; import "./globals.css"; @@ -97,7 +97,7 @@ export default function RootLayout({ - +
{children}
diff --git a/src/components/BlogCodeBlock.tsx b/src/components/BlogCodeBlock.tsx index 53d3b84..e0c355b 100644 --- a/src/components/BlogCodeBlock.tsx +++ b/src/components/BlogCodeBlock.tsx @@ -1,4 +1,3 @@ -"use client"; import React from "react"; interface BlogCodeBlockProps { diff --git a/src/components/BlogMdxContent.tsx b/src/components/BlogMdxContent.tsx index cb3be79..e6373ed 100644 --- a/src/components/BlogMdxContent.tsx +++ b/src/components/BlogMdxContent.tsx @@ -1,4 +1,3 @@ -"use client"; import React from "react"; import MdxRenderer from "./MdxRenderer"; import type { MDXRemoteSerializeResult } from "next-mdx-remote"; diff --git a/src/components/MdxRenderer.tsx b/src/components/MdxRenderer.tsx index 34e906a..384ad1c 100644 --- a/src/components/MdxRenderer.tsx +++ b/src/components/MdxRenderer.tsx @@ -1,4 +1,3 @@ -"use client"; import React from "react"; import { MDXRemote } from "next-mdx-remote"; import type { MDXRemoteSerializeResult } from "next-mdx-remote"; diff --git a/src/components/StaticNavbar.tsx b/src/components/StaticNavbar.tsx new file mode 100644 index 0000000..d69642d --- /dev/null +++ b/src/components/StaticNavbar.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { NexlayerLogo } from "./NexlayerLogo"; + +export const StaticNavigation = () => { + return ( + + ); +}; + +export default StaticNavigation; From 1a000639b9f4e48a99355d2b569df30323ff37de Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:41:40 -0400 Subject: [PATCH 12/19] Updates --- src/app/blog/[slug]/page.tsx | 47 +++++++------------------- src/components/Navbar.tsx | 50 ---------------------------- src/components/SimpleMdxRenderer.tsx | 42 +++++++++++++++++++++++ 3 files changed, 54 insertions(+), 85 deletions(-) delete mode 100644 src/components/Navbar.tsx create mode 100644 src/components/SimpleMdxRenderer.tsx diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 12d87a5..0737189 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -1,15 +1,13 @@ import React from "react"; import Image from "next/image"; import Link from "next/link"; -import BlogMdxContent from "../../../components/BlogMdxContent"; +import SimpleMdxRenderer from "../../../components/SimpleMdxRenderer"; import fs from "fs"; import { ArrowLeft } from "lucide-react"; import path from "path"; import matter from "gray-matter"; import NotFound from "@/app/not-found"; -import { serialize } from "next-mdx-remote/serialize"; - // Generate static params for all blog posts export async function generateStaticParams() { const contentDir = path.join(process.cwd(), "src", "content"); @@ -34,27 +32,15 @@ async function getPostBySlug(slug: string) { const source = fs.readFileSync(filePath, "utf-8"); const { data, content } = matter(source); - try { - const mdxSource = await serialize(content, { - scope: data, - mdxOptions: { - development: process.env.NODE_ENV === 'development' - } - }); - return { - title: data.title || slug, - description: data.description || "", - author: data.author || "Unknown", - avatar: data.avatar || "/placeholder.svg", - readTime: data.readTime || "", - date: data.date || "", - mdxSource, - content, - }; - } catch (error) { - console.error('Error serializing MDX:', error); - return null; - } + return { + title: data.title || slug, + description: data.description || "", + author: data.author || "Unknown", + avatar: data.avatar || "/placeholder.svg", + readTime: data.readTime || "", + date: data.date || "", + content, + }; } const BlogPost = async ({ params }: BlogPostProps) => { @@ -65,12 +51,7 @@ const BlogPost = async ({ params }: BlogPostProps) => { return ; } - // Debug logging - console.log('Post data:', { - title: post.title, - avatar: post.avatar, - author: post.author - }); + return (
@@ -107,11 +88,7 @@ const BlogPost = async ({ params }: BlogPostProps) => {
- {post.mdxSource ? ( - - ) : ( -
Error loading content
- )} +

diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx deleted file mode 100644 index a10edbb..0000000 --- a/src/components/Navbar.tsx +++ /dev/null @@ -1,50 +0,0 @@ -"use client" - -import { useState, useEffect } from "react" -import { NexlayerLogo } from "../components/NexlayerLogo" - -export const Navigation = () => { - const [isScrolled, setIsScrolled] = useState(false) - - useEffect(() => { - const handleScroll = () => { - setIsScrolled(window.scrollY > 0) - } - - window.addEventListener("scroll", handleScroll) - return () => window.removeEventListener("scroll", handleScroll) - }, []) - - return ( - - ) -} - -export default Navigation diff --git a/src/components/SimpleMdxRenderer.tsx b/src/components/SimpleMdxRenderer.tsx new file mode 100644 index 0000000..a829e39 --- /dev/null +++ b/src/components/SimpleMdxRenderer.tsx @@ -0,0 +1,42 @@ +import React from "react"; +import BlogCodeBlock from "./BlogCodeBlock"; + +interface SimpleMdxRendererProps { + content: string; +} + +const SimpleMdxRenderer: React.FC = ({ content }) => { + // Simple MDX-like rendering for static export + // This is a basic implementation that handles common MDX elements + + const renderContent = (text: string) => { + // Split content into lines and process each one + const lines = text.split('\n'); + + return lines.map((line, index) => { + // Handle headers + if (line.startsWith('## ')) { + return

{line.substring(3)}

; + } + if (line.startsWith('### ')) { + return

{line.substring(4)}

; + } + + // Handle paragraphs + if (line.trim() && !line.startsWith('#')) { + return

{line}

; + } + + // Handle empty lines + return
; + }); + }; + + return ( +
+ {renderContent(content)} +
+ ); +}; + +export default SimpleMdxRenderer; From 65a9ddde7b214a9774fcf2a878354f9379c59f4a Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:48:02 -0400 Subject: [PATCH 13/19] Update nexlayer yamls --- nexlayer-update.yaml | 2 +- nexlayer.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nexlayer-update.yaml b/nexlayer-update.yaml index dc87d8e..f151a05 100644 --- a/nexlayer-update.yaml +++ b/nexlayer-update.yaml @@ -5,7 +5,7 @@ application: - name: blog image: ghcr.io/nexlayer/nexlayer-blog:latest path: "/" - servicePorts: [3000] + servicePorts: [80] vars: NODE_ENV: "production" NEXT_TELEMETRY_DISABLED: "1" diff --git a/nexlayer.yaml b/nexlayer.yaml index e9e426e..afb8f36 100644 --- a/nexlayer.yaml +++ b/nexlayer.yaml @@ -4,7 +4,7 @@ application: - name: blog image: ghcr.io/nexlayer/nexlayer-blog:latest path: "/" - servicePorts: [3000] + servicePorts: [80] vars: NODE_ENV: "production" NEXT_TELEMETRY_DISABLED: "1" From b0c51a63fc55b753f0ab5ee578351ad6cd48e6f0 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:52:44 -0400 Subject: [PATCH 14/19] Move around pages --- src/app/{blog => }/[slug]/page.tsx | 2 +- src/app/blog/page.tsx | 133 ------------------------ src/app/page.tsx | 156 +++++++++++++++++++++++------ 3 files changed, 127 insertions(+), 164 deletions(-) rename src/app/{blog => }/[slug]/page.tsx (97%) delete mode 100644 src/app/blog/page.tsx diff --git a/src/app/blog/[slug]/page.tsx b/src/app/[slug]/page.tsx similarity index 97% rename from src/app/blog/[slug]/page.tsx rename to src/app/[slug]/page.tsx index 0737189..5372409 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import Image from "next/image"; import Link from "next/link"; -import SimpleMdxRenderer from "../../../components/SimpleMdxRenderer"; +import SimpleMdxRenderer from "@/components/SimpleMdxRenderer"; import fs from "fs"; import { ArrowLeft } from "lucide-react"; import path from "path"; diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx deleted file mode 100644 index d8d4c57..0000000 --- a/src/app/blog/page.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import React from "react"; -import Link from "next/link"; -import Image from "next/image"; -import fs from "fs"; -import path from "path"; -import matter from "gray-matter"; -// import LogoIcon from "../../../public/logo-icon.svg"; - -function getAllPosts() { - const contentDir = path.join(process.cwd(), "src", "content"); - const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); - return files.map((filename) => { - const filePath = path.join(contentDir, filename); - const source = fs.readFileSync(filePath, "utf-8"); - const { data } = matter(source); - - return { - title: data.title || filename.replace(/\.mdx$/, ""), - description: data.description || "", - author: data.author || "Unknown", - avatar: data.avatar || "/placeholder.svg", - readTime: data.readTime || "", - slug: filename.replace(/\.mdx$/, ""), - date: data.date || null, - }; - }); -} - -export default function BlogPage() { - const posts = getAllPosts(); - const featuredPosts = posts.slice(0, 4); - function parseCustomDate(dateStr: string | null): number { - if (!dateStr) return 0; - const months: { [key: string]: string } = { - JANUARY: "01", FEBRUARY: "02", MARCH: "03", APRIL: "04", MAY: "05", JUNE: "06", - JULY: "07", AUGUST: "08", SEPTEMBER: "09", OCTOBER: "10", NOVEMBER: "11", DECEMBER: "12" - }; - const parts: string[] = dateStr.toUpperCase().replace(/,/g, "").split(" "); - if (parts.length === 3 && months[parts[0] as keyof typeof months]) { - return new Date(`${parts[2]}-${months[parts[0] as keyof typeof months]}-${parts[1].padStart(2, "0")}`).getTime(); - } - return new Date(dateStr).getTime(); - } - const allPosts = [...posts].sort((a, b) => { - return parseCustomDate(b.date) - parseCustomDate(a.date); - }); - return ( -
-
-
-
-

Blog

-

- Compiled notes from the team -

-
- -
-

Featured

-
- {featuredPosts.map((post) => ( - -
-

- {post.title} -

-

- {post.description} -

- -
- {post.author} -
- - By {post.author} - - - {post.readTime} - -
-
-
- - ))} -
-
- -
-

All posts

-
- {allPosts.map((post) => ( - -
-

- {post.title} -

-

- {post.description} -

- -
- {post.author} -
- - By {post.author} - - - {post.readTime} - -
-
-
- - ))} -
-
-
-
-
- ); -} diff --git a/src/app/page.tsx b/src/app/page.tsx index 2246a33..65bc1ef 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,36 +1,132 @@ import React from "react"; import Link from "next/link"; +import Image from "next/image"; +import fs from "fs"; +import path from "path"; +import matter from "gray-matter"; -export default function WelcomePage() { +function getAllPosts() { + const contentDir = path.join(process.cwd(), "src", "content"); + const files = fs.readdirSync(contentDir).filter((f) => f.endsWith(".mdx")); + return files.map((filename) => { + const filePath = path.join(contentDir, filename); + const source = fs.readFileSync(filePath, "utf-8"); + const { data } = matter(source); + + return { + title: data.title || filename.replace(/\.mdx$/, ""), + description: data.description || "", + author: data.author || "Unknown", + avatar: data.avatar || "/placeholder.svg", + readTime: data.readTime || "", + slug: filename.replace(/\.mdx$/, ""), + date: data.date || null, + }; + }); +} + +export default function BlogPage() { + const posts = getAllPosts(); + const featuredPosts = posts.slice(0, 4); + function parseCustomDate(dateStr: string | null): number { + if (!dateStr) return 0; + const months: { [key: string]: string } = { + JANUARY: "01", FEBRUARY: "02", MARCH: "03", APRIL: "04", MAY: "05", JUNE: "06", + JULY: "07", AUGUST: "08", SEPTEMBER: "09", OCTOBER: "10", NOVEMBER: "11", DECEMBER: "12" + }; + const parts: string[] = dateStr.toUpperCase().replace(/,/g, "").split(" "); + if (parts.length === 3 && months[parts[0] as keyof typeof months]) { + return new Date(`${parts[2]}-${months[parts[0] as keyof typeof months]}-${parts[1].padStart(2, "0")}`).getTime(); + } + return new Date(dateStr).getTime(); + } + const allPosts = [...posts].sort((a, b) => { + return parseCustomDate(b.date) - parseCustomDate(a.date); + }); return ( -
-
-

- Welcome to Nexlayer! -

-

- Your AI-native cloud deployment platform -

- - - - - Read Our Blog - -
+
+
+
+
+

Blog

+

+ Compiled notes from the team +

+
+ +
+

Featured

+
+ {featuredPosts.map((post) => ( + +
+

+ {post.title} +

+

+ {post.description} +

+ +
+ {post.author} +
+ + By {post.author} + + + {post.readTime} + +
+
+
+ + ))} +
+
+ +
+

All posts

+
+ {allPosts.map((post) => ( + +
+

+ {post.title} +

+

+ {post.description} +

+ +
+ {post.author} +
+ + By {post.author} + + + {post.readTime} + +
+
+
+ + ))} +
+
+
+
); -} +} \ No newline at end of file From 1ab183a695a5a19c6d5f051fd6cb4a32b37c5e55 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 15:57:21 -0400 Subject: [PATCH 15/19] Update mdx file --- ...-agents-trust-why-we're-building-the-agent-native-future.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx b/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx index 7978f4d..ca63185 100644 --- a/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx +++ b/src/content/the-cloud-agents-trust-why-we're-building-the-agent-native-future.mdx @@ -2,7 +2,7 @@ title: "The Cloud Agents Trust™ — Why We're Building the Agent-Native Future" description: "How Nexlayer became the infrastructure backbone for AI-generated applications worldwide." author: "Sarah Chen" -avatar: "/developer-avatar.png" +avatar: "/blog/developer-avatar.png" readTime: "3 minutes read" date: "AUGUST 5, 2025" --- From a1ee55a3a0e2309df1d9eecd61d6ad3a1a3e853a Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 16:06:10 -0400 Subject: [PATCH 16/19] Update default.conf --- default.conf | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/default.conf b/default.conf index c67dee5..5309d82 100644 --- a/default.conf +++ b/default.conf @@ -10,6 +10,17 @@ server { index index.html index.htm; } + location /blog { + alias /usr/share/nginx/html/blog; + try_files $uri $uri/ /blog/index.html; + + # Handle static assets (images, CSS, JS) + location ~* \.(png|jpg|jpeg|gif|svg|ico|css|js)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + } + #error_page 404 /404.html; # redirect server error pages to the static page /50x.html From b83aca601cf17435f5167f54d94abee6bf41b8b8 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 16:12:48 -0400 Subject: [PATCH 17/19] Updates --- .dockerignore | 1 + default.conf | 13 +++++++------ next.config.ts | 5 ++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.dockerignore b/.dockerignore index 0795297..8a5c499 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ Dockerfile +build/ .dockerignore node_modules npm-debug.log diff --git a/default.conf b/default.conf index 5309d82..84aabe9 100644 --- a/default.conf +++ b/default.conf @@ -13,12 +13,13 @@ server { location /blog { alias /usr/share/nginx/html/blog; try_files $uri $uri/ /blog/index.html; - - # Handle static assets (images, CSS, JS) - location ~* \.(png|jpg|jpeg|gif|svg|ico|css|js)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } + } + + # Handle static assets for blog + location ~* ^/blog/.*\.(png|jpg|jpeg|gif|svg|ico|css|js)$ { + alias /usr/share/nginx/html/blog; + expires 1y; + add_header Cache-Control "public, immutable"; } #error_page 404 /404.html; diff --git a/next.config.ts b/next.config.ts index 88a2579..16d0b27 100644 --- a/next.config.ts +++ b/next.config.ts @@ -4,7 +4,10 @@ const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'], output: 'export', // Enable static export distDir: 'build', // Output directory - basePath: '/blog' + basePath: '/blog', + images: { + unoptimized: true + } } const withMDX = createMDX({ extension: /\.mdx?$/, From 90706481624da1ea22228bd23c9d5d10695313d2 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 16:17:55 -0400 Subject: [PATCH 18/19] Updates --- default.conf | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/default.conf b/default.conf index 84aabe9..5309d82 100644 --- a/default.conf +++ b/default.conf @@ -13,13 +13,12 @@ server { location /blog { alias /usr/share/nginx/html/blog; try_files $uri $uri/ /blog/index.html; - } - - # Handle static assets for blog - location ~* ^/blog/.*\.(png|jpg|jpeg|gif|svg|ico|css|js)$ { - alias /usr/share/nginx/html/blog; - expires 1y; - add_header Cache-Control "public, immutable"; + + # Handle static assets (images, CSS, JS) + location ~* \.(png|jpg|jpeg|gif|svg|ico|css|js)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } } #error_page 404 /404.html; From 9fd7fdd79b8cd6171a8cccd566d4b3d148f41fa2 Mon Sep 17 00:00:00 2001 From: Katie Harris Date: Mon, 1 Sep 2025 16:26:37 -0400 Subject: [PATCH 19/19] Update MDX renderer --- src/components/SimpleMdxRenderer.tsx | 139 ++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 11 deletions(-) diff --git a/src/components/SimpleMdxRenderer.tsx b/src/components/SimpleMdxRenderer.tsx index a829e39..ed3f807 100644 --- a/src/components/SimpleMdxRenderer.tsx +++ b/src/components/SimpleMdxRenderer.tsx @@ -6,30 +6,147 @@ interface SimpleMdxRendererProps { } const SimpleMdxRenderer: React.FC = ({ content }) => { - // Simple MDX-like rendering for static export - // This is a basic implementation that handles common MDX elements + // Enhanced MDX-like rendering for static export + // This handles more MDX features while remaining static-compatible const renderContent = (text: string) => { // Split content into lines and process each one const lines = text.split('\n'); + const elements: React.ReactNode[] = []; + let currentList: React.ReactNode[] = []; + let inList = false; - return lines.map((line, index) => { + lines.forEach((line, index) => { + const trimmedLine = line.trim(); + // Handle headers - if (line.startsWith('## ')) { - return

{line.substring(3)}

; + if (trimmedLine.startsWith('## ')) { + if (inList && currentList.length > 0) { + elements.push(
    {currentList}
); + currentList = []; + inList = false; + } + elements.push(

{trimmedLine.substring(3)}

); + return; + } + + if (trimmedLine.startsWith('### ')) { + if (inList && currentList.length > 0) { + elements.push(
    {currentList}
); + currentList = []; + inList = false; + } + elements.push(

{trimmedLine.substring(4)}

); + return; } - if (line.startsWith('### ')) { - return

{line.substring(4)}

; + + // Handle lists + if (trimmedLine.startsWith('- **') || trimmedLine.startsWith('* **')) { + if (!inList) { + inList = true; + } + + // Extract bold text and description + const match = trimmedLine.match(/^[-*]\s\*\*(.*?)\*\*:\s*(.*)/); + if (match) { + const [, boldText, description] = match; + currentList.push( +
  • + {boldText}: {description} +
  • + ); + } else { + // Handle regular list items + const listText = trimmedLine.replace(/^[-*]\s/, ''); + currentList.push( +
  • {listText}
  • + ); + } + return; + } + + // Handle regular list items + if (trimmedLine.startsWith('- ') || trimmedLine.startsWith('* ')) { + if (!inList) { + inList = true; + } + const listText = trimmedLine.replace(/^[-*]\s/, ''); + currentList.push( +
  • {listText}
  • + ); + return; } - // Handle paragraphs - if (line.trim() && !line.startsWith('#')) { - return

    {line}

    ; + // Handle code blocks + if (trimmedLine.startsWith('```')) { + if (inList && currentList.length > 0) { + elements.push(
      {currentList}
    ); + currentList = []; + inList = false; + } + + // Start of code block + const language = trimmedLine.replace('```', '').trim(); + const codeLines: string[] = []; + let i = index + 1; + + // Collect code lines until we find the closing ``` + while (i < lines.length && !lines[i].trim().startsWith('```')) { + codeLines.push(lines[i]); + i++; + } + + if (i < lines.length) { + // Skip the closing ``` line in the main loop + index = i; + } + + const codeContent = codeLines.join('\n'); + elements.push( + + {codeContent} + + ); + return; + } + + // Handle paragraphs (non-empty lines that aren't headers, list items, or code blocks) + if (trimmedLine && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('-') && !trimmedLine.startsWith('*') && !trimmedLine.startsWith('```')) { + if (inList && currentList.length > 0) { + elements.push(
      {currentList}
    ); + currentList = []; + inList = false; + } + + // Process bold text and inline code within paragraphs + let processedText = trimmedLine + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/`([^`]+)`/g, '$1'); + + elements.push( +

    + ); + return; } // Handle empty lines - return
    ; + if (!trimmedLine) { + if (inList && currentList.length > 0) { + elements.push(

      {currentList}
    ); + currentList = []; + inList = false; + } + elements.push(
    ); + } }); + + // Handle any remaining list + if (inList && currentList.length > 0) { + elements.push(
      {currentList}
    ); + } + + return elements; }; return (