Add in-memory caching for online count, enable compression, and add staleTimes for router cache
This commit is contained in:
1 parent
b058a3827b
commit
deac10e00a
5 files changed
+51
-10
No files matched your search
+24
-5
@@ -29,20 +29,39 @@ const securityHeaders = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
// This app lives inside the Laravel repo tree (which has its own lockfiles);
|
|
||||||
// pin the Turbopack root so Next doesn't infer a parent directory.
|
|
||||||
turbopack: { root: import.meta.dirname },
|
turbopack: { root: import.meta.dirname },
|
||||||
|
|
||||||
// Prisma + the MariaDB driver adapter are native/server-only — keep them out
|
|
||||||
// of the bundle (same approach as the habbo-next reference).
|
|
||||||
serverExternalPackages: ["@prisma/adapter-mariadb", "mariadb", "@prisma/client"],
|
serverExternalPackages: ["@prisma/adapter-mariadb", "mariadb", "@prisma/client"],
|
||||||
|
|
||||||
|
// Compress responses with gzip
|
||||||
|
compress: true,
|
||||||
|
|
||||||
|
// Cache pages longer in the router cache for faster back/forward navigation
|
||||||
|
experimental: {
|
||||||
|
staleTimes: {
|
||||||
|
dynamic: 30,
|
||||||
|
static: 180,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Add caching headers for static assets
|
||||||
async headers() {
|
async headers() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
source: "/(.*)",
|
source: "/(.*)",
|
||||||
headers: securityHeaders,
|
headers: securityHeaders,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: "/assets/(.*)",
|
||||||
|
headers: [
|
||||||
|
{ key: "Cache-Control", value: "public, max-age=31536000, immutable" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: "/images/(.*)",
|
||||||
|
headers: [
|
||||||
|
{ key: "Cache-Control", value: "public, max-age=86400" },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,16 +4,16 @@
|
|||||||
// { count: 0 } (never 500) on DB failure.
|
// { count: 0 } (never 500) on DB failure.
|
||||||
|
|
||||||
import { apiJson } from "@/lib/api";
|
import { apiJson } from "@/lib/api";
|
||||||
|
import { cached } from "@/lib/cache";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export async function GET(_req: Request) {
|
export async function GET(_req: Request) {
|
||||||
try {
|
try {
|
||||||
const count = await prisma.user.count({
|
const count = await cached("online_count", 10_000, () =>
|
||||||
where: { online: "1" },
|
prisma.user.count({ where: { online: "1" } }),
|
||||||
});
|
);
|
||||||
|
|
||||||
return apiJson({ count });
|
return apiJson({ count });
|
||||||
} catch {
|
} catch {
|
||||||
return apiJson({ count: 0 });
|
return apiJson({ count: 0 });
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ export default async function RootLayout({ children }: { children: ReactNode })
|
|||||||
<head>
|
<head>
|
||||||
<meta name="theme-default-dark" content={String(defaultDark)} />
|
<meta name="theme-default-dark" content={String(defaultDark)} />
|
||||||
<script src="/scripts/theme-init.js" />
|
<script src="/scripts/theme-init.js" />
|
||||||
|
<link rel="preconnect" href="https://www.habbo.com" />
|
||||||
|
<link rel="dns-prefetch" href="https://www.habbo.com" />
|
||||||
</head>
|
</head>
|
||||||
<body
|
<body
|
||||||
className="flex min-h-screen flex-col site-bg"
|
className="flex min-h-screen flex-col site-bg"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { getTranslations } from "next-intl/server";
|
import { getTranslations } from "next-intl/server";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
|
import { cached } from "@/lib/cache";
|
||||||
import { prisma } from "@/lib/prisma";
|
import { prisma } from "@/lib/prisma";
|
||||||
import { siteSettings } from "@/lib/services/site-settings";
|
import { siteSettings } from "@/lib/services/site-settings";
|
||||||
|
|
||||||
@@ -15,7 +16,9 @@ export async function SiteHeader() {
|
|||||||
|
|
||||||
let online = 0;
|
let online = 0;
|
||||||
try {
|
try {
|
||||||
online = await prisma.user.count({ where: { online: "1" } });
|
online = await cached("online_count", 10_000, () =>
|
||||||
|
prisma.user.count({ where: { online: "1" } }),
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
online = 0;
|
online = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
type CacheEntry<T> = { data: T; expiresAt: number };
|
||||||
|
const store = new Map<string, CacheEntry<unknown>>();
|
||||||
|
|
||||||
|
export function cached<T>(key: string, ttlMs: number, fn: () => Promise<T>): Promise<T> {
|
||||||
|
const existing = store.get(key);
|
||||||
|
if (existing && existing.expiresAt > Date.now()) {
|
||||||
|
return Promise.resolve(existing.data as T);
|
||||||
|
}
|
||||||
|
return fn().then((data) => {
|
||||||
|
store.set(key, { data, expiresAt: Date.now() + ttlMs });
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bustCache(key: string): void {
|
||||||
|
store.delete(key);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user