fix: address code review findings from 2026-08-17
- scope no-cache headers to non-static routes (restore immutable asset caching) - reset cached rejected MongoDB promise so retries can succeed - use last X-Forwarded-For entry in Content Editor rate limiter (anti-spoofing) - remove weak Mongo defaults from compose files (fail loudly on missing env) - move staging banner text to common.json content - read APP_PORT from env file in deploy.sh healthcheck - filter network noise from staging smoke console assertions Closes MITHOME-48, MITHOME-49, MITHOME-50, MITHOME-51, MITHOME-52, MITHOME-53, MITHOME-54
This commit is contained in:
@@ -10,7 +10,11 @@ test('SMOKE-01: health endpoint is available', async ({ request }) => {
|
||||
test('SMOKE-02: homepage renders its critical shell without console errors', async ({ page }) => {
|
||||
const consoleErrors: string[] = []
|
||||
page.on('console', message => {
|
||||
if (message.type() === 'error') consoleErrors.push(message.text())
|
||||
if (message.type() !== 'error') return
|
||||
// Chromium logs failed resource loads (e.g. favicon 404) as console errors.
|
||||
// Those are network noise here; real JS errors must still fail the test.
|
||||
if (message.text().startsWith('Failed to load resource')) return
|
||||
consoleErrors.push(message.text())
|
||||
})
|
||||
|
||||
await page.goto('/')
|
||||
|
||||
+11
-1
@@ -37,7 +37,8 @@ const nextConfig: NextConfig = {
|
||||
async headers() {
|
||||
return [
|
||||
{
|
||||
source: '/(.*)',
|
||||
// Security headers apply to every route, including static assets.
|
||||
source: '/:path*',
|
||||
headers: [
|
||||
{
|
||||
key: 'X-Frame-Options',
|
||||
@@ -51,6 +52,15 @@ const nextConfig: NextConfig = {
|
||||
key: 'Referrer-Policy',
|
||||
value: 'origin-when-cross-origin',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// WHY: no-cache must not hit hashed build assets (_next/static) or
|
||||
// optimized images (_next/image); they are content-addressed and rely on
|
||||
// long-lived caching. Overriding them would re-download the bundle on
|
||||
// every page load.
|
||||
source: '/((?!_next/static|_next/image).*)',
|
||||
headers: [
|
||||
{
|
||||
key: 'Cache-Control',
|
||||
value: 'private, no-cache, must-revalidate, max-age=0',
|
||||
|
||||
@@ -6,6 +6,7 @@ import Header from "../components/Header";
|
||||
import Footer from "../components/Footer";
|
||||
import { ThemeProvider } from "../components/ThemeProvider";
|
||||
import { siteConfig } from "../config/site";
|
||||
import { common } from "../content";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
@@ -93,7 +94,7 @@ export default function RootLayout({
|
||||
<ThemeProvider>
|
||||
{isStaging && (
|
||||
<div className="bg-amber-400 px-4 py-2 text-center text-xs font-extrabold tracking-[0.18em] text-amber-950 sm:text-sm">
|
||||
⚠ STAGING / TESZTKÖRNYEZET — A STAGING OLDALT LÁTOD
|
||||
{common.staging.banner}
|
||||
</div>
|
||||
)}
|
||||
<Header />
|
||||
|
||||
@@ -13,5 +13,8 @@
|
||||
"required": "Ez a mező kötelező",
|
||||
"invalidEmail": "Érvénytelen email cím formátum",
|
||||
"minLength": "Legalább {min} karakter szükséges"
|
||||
},
|
||||
"staging": {
|
||||
"banner": "⚠ STAGING / TESZTKÖRNYEZET — A STAGING OLDALT LÁTOD"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ const schemas = {
|
||||
buttons: object({ contact: string, learnMore: string, webmail: string, sendMessage: string }),
|
||||
labels: object({ required: string, features: string }),
|
||||
validation: object({ required: string, invalidEmail: string, minLength: string }),
|
||||
staging: object({ banner: string }),
|
||||
}),
|
||||
home: object({
|
||||
hero: object({ title: string, subtitle: string, description: string, trustBullets: array(string), cta: object({ primary: ctaLink, secondary: ctaLink }) }),
|
||||
|
||||
@@ -190,6 +190,9 @@ export interface CommonContent {
|
||||
invalidEmail: string
|
||||
minLength: string
|
||||
}
|
||||
staging: {
|
||||
banner: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface LegalPageContent {
|
||||
|
||||
@@ -139,5 +139,23 @@ describe('MongoDB Connection (Unit Tests)', () => {
|
||||
// Should be the same client instance
|
||||
expect(client1).toBe(client2)
|
||||
})
|
||||
|
||||
it('should not cache a rejected connection promise and retry on next call', async () => {
|
||||
// First attempt fails (e.g. transient DB outage at startup)
|
||||
const failingClient = {
|
||||
connect: jest.fn().mockRejectedValueOnce(new Error('transient failure')),
|
||||
db: jest.fn(),
|
||||
close: jest.fn()
|
||||
}
|
||||
MockedMongoClient.mockImplementation(() => failingClient)
|
||||
|
||||
const { getClientPromise } = await import('./mongodb')
|
||||
|
||||
await expect(getClientPromise()).rejects.toThrow('transient failure')
|
||||
|
||||
// Second call must retry instead of replaying the cached rejection
|
||||
failingClient.connect.mockResolvedValueOnce(failingClient as any)
|
||||
await expect(getClientPromise()).resolves.toBe(failingClient)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -24,13 +24,24 @@ export function getClientPromise(): Promise<MongoClient> {
|
||||
// @ts-expect-error - Global variable for development hot reload
|
||||
if (!global._mongoClientPromise) {
|
||||
// @ts-expect-error - Global variable for development hot reload
|
||||
global._mongoClientPromise = createClientPromise()
|
||||
global._mongoClientPromise = createClientPromise().catch((error: Error) => {
|
||||
// A rejected promise must not stay cached: a transient failure would
|
||||
// otherwise break every subsequent request in this process.
|
||||
// @ts-expect-error - Global variable for development hot reload
|
||||
global._mongoClientPromise = undefined
|
||||
throw error
|
||||
})
|
||||
}
|
||||
// @ts-expect-error - Global variable for development hot reload
|
||||
return global._mongoClientPromise
|
||||
}
|
||||
|
||||
clientPromise ??= createClientPromise()
|
||||
clientPromise ??= createClientPromise().catch((error: Error) => {
|
||||
// See the development branch above: drop the cached rejection so the next
|
||||
// call can retry the connection.
|
||||
clientPromise = undefined
|
||||
throw error
|
||||
})
|
||||
return clientPromise
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user