Compare commits
7 Commits
v2.3.22-rc
...
85797ccf73
| Author | SHA1 | Date | |
|---|---|---|---|
| 85797ccf73 | |||
| ac437e19f3 | |||
| 5a61ebf269 | |||
| 6b8ae11e45 | |||
| 071fba540d | |||
| 04fb328850 | |||
| 84da0e57e5 |
17
Dockerfile
17
Dockerfile
@@ -56,16 +56,15 @@ ENV UV_THREADPOOL_SIZE=3
|
|||||||
RUN pnpm build
|
RUN pnpm build
|
||||||
|
|
||||||
# Stage 2: Runner
|
# Stage 2: Runner
|
||||||
FROM node:20-slim AS runner
|
FROM node:20-alpine AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install curl for health checks and procps
|
# Install dependencies for health checks and system compatibility
|
||||||
RUN apt-get update && apt-get install -y curl procps && rm -rf /var/lib/apt/lists/*
|
RUN apk add --no-cache libc6-compat curl
|
||||||
|
|
||||||
# Create nextjs user and group
|
# Create nextjs user and group
|
||||||
RUN groupadd --system --gid 1001 nodejs && \
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
useradd --system --uid 1001 nextjs && \
|
adduser --system --uid 1001 nextjs && \
|
||||||
mkdir -p .next/cache/images && \
|
|
||||||
chown -R nextjs:nodejs /app
|
chown -R nextjs:nodejs /app
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
@@ -73,15 +72,11 @@ USER nextjs
|
|||||||
ENV HOSTNAME="0.0.0.0"
|
ENV HOSTNAME="0.0.0.0"
|
||||||
ENV PORT=3000
|
ENV PORT=3000
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV NEXT_SHARP_PATH=/app/node_modules/sharp
|
|
||||||
|
|
||||||
# Copy standalone output
|
# Copy standalone output
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
# Copy public and static files - standalone expects them in specific locations
|
# Copy public and static files
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
# Ensure cache directory is writable for the nextjs user
|
|
||||||
# (Already handled by chown -R above, but explicit is better)
|
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||||
|
import { getLocale } from 'next-intl/server';
|
||||||
|
|
||||||
|
// Blog components
|
||||||
|
import TechnicalGrid from './blog/TechnicalGrid';
|
||||||
|
import Stats from './blog/Stats';
|
||||||
|
import VisualLinkPreview from './blog/VisualLinkPreview';
|
||||||
|
import StickyNarrative from './blog/StickyNarrative';
|
||||||
|
import ComparisonGrid from './blog/ComparisonGrid';
|
||||||
|
import HighlightBox from './blog/HighlightBox';
|
||||||
|
import ChatBubble from './blog/ChatBubble';
|
||||||
|
|
||||||
|
// Home components
|
||||||
|
import Hero from './home/Hero';
|
||||||
|
import ProductCategories from './home/ProductCategories';
|
||||||
|
import WhatWeDo from './home/WhatWeDo';
|
||||||
|
import RecentPosts from './home/RecentPosts';
|
||||||
|
import Experience from './home/Experience';
|
||||||
|
import WhyChooseUs from './home/WhyChooseUs';
|
||||||
|
import MeetTheTeam from './home/MeetTheTeam';
|
||||||
|
import GallerySection from './home/GallerySection';
|
||||||
|
import VideoSection from './home/VideoSection';
|
||||||
|
import CTA from './home/CTA';
|
||||||
function ContactSection(props: any) {
|
function ContactSection(props: any) {
|
||||||
return (
|
return (
|
||||||
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
|
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
|
||||||
@@ -17,10 +39,72 @@ function HeroSection(props: any) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Block(props: any) {
|
async function Block(props: any) {
|
||||||
return (
|
const { type, children } = props;
|
||||||
<div className="p-4 border-2 border-dashed border-gray-300">Unknown Block: {props.type}</div>
|
let { data } = props;
|
||||||
);
|
const locale = await getLocale();
|
||||||
|
|
||||||
|
// If data was passed as a JSON string (via our lib/blog.ts fix), parse it back
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse MDX block data:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize type (Payload sometimes uses blockType in data)
|
||||||
|
const blockType = type || data?.blockType;
|
||||||
|
|
||||||
|
switch (blockType) {
|
||||||
|
// Blog Components
|
||||||
|
case 'technicalGrid':
|
||||||
|
return <TechnicalGrid {...data} />;
|
||||||
|
case 'stats':
|
||||||
|
return <Stats {...data} />;
|
||||||
|
case 'visualLinkPreview':
|
||||||
|
return <VisualLinkPreview {...data} />;
|
||||||
|
case 'stickyNarrative':
|
||||||
|
return <StickyNarrative {...data} />;
|
||||||
|
case 'comparisonGrid':
|
||||||
|
return <ComparisonGrid {...data} />;
|
||||||
|
case 'highlightBox':
|
||||||
|
return <HighlightBox {...data}>{children}</HighlightBox>;
|
||||||
|
case 'chatBubble':
|
||||||
|
return <ChatBubble {...data}>{children}</ChatBubble>;
|
||||||
|
|
||||||
|
// Home Components
|
||||||
|
case 'homeHero':
|
||||||
|
return <Hero data={data} />;
|
||||||
|
case 'homeProductCategories':
|
||||||
|
return <ProductCategories data={data} />;
|
||||||
|
case 'homeWhatWeDo':
|
||||||
|
return <WhatWeDo data={data} />;
|
||||||
|
case 'homeRecentPosts':
|
||||||
|
return <RecentPosts data={data} locale={locale} />;
|
||||||
|
case 'homeExperience':
|
||||||
|
return <Experience data={data} />;
|
||||||
|
case 'homeWhyChooseUs':
|
||||||
|
return <WhyChooseUs data={data} />;
|
||||||
|
case 'homeMeetTheTeam':
|
||||||
|
return <MeetTheTeam data={data} />;
|
||||||
|
case 'homeGallery':
|
||||||
|
return <GallerySection data={data} />;
|
||||||
|
case 'homeVideo':
|
||||||
|
return <VideoSection data={data} />;
|
||||||
|
case 'homeCTA':
|
||||||
|
return <CTA data={data} />;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="p-8 border-2 border-dashed border-red-200 rounded-2xl my-8 bg-red-50 text-red-600">
|
||||||
|
<p className="font-bold mb-2">Unknown Block Type: {blockType}</p>
|
||||||
|
<pre className="text-xs overflow-auto p-4 bg-white/50 rounded">
|
||||||
|
{JSON.stringify({ type, data: !!data, hasChildren: !!children }, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
|
|||||||
@@ -62,7 +62,13 @@ export async function getPostBySlug(slug: string, locale: string): Promise<PostD
|
|||||||
const fileContent = await fs.readFile(filePath, 'utf-8');
|
const fileContent = await fs.readFile(filePath, 'utf-8');
|
||||||
const { data, content } = matter(fileContent);
|
const { data, content } = matter(fileContent);
|
||||||
|
|
||||||
let parsedContent = content;
|
// Fix MDX data props dropped by next-mdx-remote
|
||||||
|
// Payload serializes as data={{"items":...}} which Acorn treats as a Block statement.
|
||||||
|
const fixedContent = content.replace(/data=\{\{([\s\S]*?)\}\}/g, (match, p1) => {
|
||||||
|
return 'data="{' + p1.replace(/"/g, '"') + '}"';
|
||||||
|
});
|
||||||
|
|
||||||
|
let parsedContent = fixedContent;
|
||||||
try {
|
try {
|
||||||
if (content.trim().startsWith('{')) {
|
if (content.trim().startsWith('{')) {
|
||||||
parsedContent = JSON.parse(content);
|
parsedContent = JSON.parse(content);
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export default async function middleware(request: NextRequest) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
isServerAction ||
|
isServerAction ||
|
||||||
pathname.startsWith('/admin') ||
|
|
||||||
pathname.startsWith('/api') ||
|
pathname.startsWith('/api') ||
|
||||||
pathname.startsWith('/stats') ||
|
pathname.startsWith('/stats') ||
|
||||||
pathname.startsWith('/errors') ||
|
pathname.startsWith('/errors') ||
|
||||||
@@ -114,7 +113,7 @@ export default async function middleware(request: NextRequest) {
|
|||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
'/((?!api|_next/static|_next/image|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
|
'/((?!api|_next/static|_next/image|favicon.ico|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
|
||||||
'/(de|en)/:path*',
|
'/(de|en)/:path*',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ const nextConfig = {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
|
unoptimized: true,
|
||||||
formats: ['image/webp'],
|
formats: ['image/webp'],
|
||||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.3.22-rc.16",
|
"version": "2.3.22-rc.22",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
8
push_error.log
Normal file
8
push_error.log
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
remote:
|
||||||
|
remote: Create a new pull request for 'feature/mdx-migration':
|
||||||
|
remote: https://git.infra.mintel.me/mmintel/klz-cables.com/pulls/new/feature/mdx-migration
|
||||||
|
remote:
|
||||||
|
remote: . Processing 1 references
|
||||||
|
remote: Processed 1 references in total
|
||||||
|
To https://git.infra.mintel.me/mmintel/klz-cables.com.git
|
||||||
|
58092917..f17a8c8c feature/mdx-migration -> feature/mdx-migration
|
||||||
@@ -42,6 +42,7 @@ async function run() {
|
|||||||
const broken: string[] = [];
|
const broken: string[] = [];
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
urls.map(async (url) => {
|
urls.map(async (url) => {
|
||||||
|
if (url.includes('openstreetmap.org') || url.includes('unpkg.com')) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(url, { method: 'HEAD' });
|
const res = await fetch(url, { method: 'HEAD' });
|
||||||
if (res.status >= 400) {
|
if (res.status >= 400) {
|
||||||
|
|||||||
Reference in New Issue
Block a user