From baa01c38bd07573582d680b6ac9a9c3c0a96865d Mon Sep 17 00:00:00 2001 From: Deardo Satria Date: Tue, 3 Nov 2026 10:35:23 +0700 Subject: [PATCH] Memoize getCurrentUser per request and document cache rules getCurrentUser is wrapped in React cache so pages that call it from several components validate the session once per request. Cross-request caching ("use cache", unstable_cache, module-level storage) stays forbidden for cleo data and sessions: the PHP app writes to the same DB, and a cached session would keep a logged-out user signed in. CLAUDE.md and the cleo-code-review skill now state both rules. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/cleo-code-review/SKILL.md | 7 +++++++ CLAUDE.md | 5 +++++ data/auth.ts | 7 +++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.claude/skills/cleo-code-review/SKILL.md b/.claude/skills/cleo-code-review/SKILL.md index be5df7d..bbaa14d 100644 --- a/.claude/skills/cleo-code-review/SKILL.md +++ b/.claude/skills/cleo-code-review/SKILL.md @@ -174,6 +174,13 @@ sama supaya pembaca tahu ini bukan regresi. - Hapus baris yang masih direferensikan tabel lain harus ditolak dulu (lihat 3a). - Tanggal "hari ini" pakai zona `Asia/Jakarta`, bukan `toISOString()` (UTC). - Tanggal `0000-00-00` tetap string, tidak diubah ke `Date`. +- Fungsi baca yang dipanggil beberapa komponen dalam satu render (mis. opsi select yang dipakai + tombol tambah dan daftar) dibungkus `cache` dari `react`, bukan diambil dua kali. + `getCurrentUser()` harus tetap `cache` React. +- `"use cache"`, `unstable_cache`, atau penyimpanan lintas request (variabel modul, Map global) + untuk data `cleo` atau session adalah **Bug**: PHP lama ikut menulis ke DB sehingga data basi, + dan session yang tersimpan membuat user yang sudah logout tetap dianggap login. Cek: + `grep -rn '"use cache"\|unstable_cache' app data lib`. - Batas panjang zod sama dengan panjang kolom di `db/amigo/schema.ts`. - Aturan bisnis dicocokkan dengan kode lama di `old-source/` (mis. format kode pelanggan). diff --git a/CLAUDE.md b/CLAUDE.md index afe6807..7286040 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -128,6 +128,11 @@ proxy.ts pengganti middleware di Next 16; hanya cek rin - Session dicek lewat `getCurrentUser()` di `data/auth.ts` (redirect ke `/login` kalau tidak ada), dipanggil oleh page dan action yang butuh user. Layout tidak dipakai untuk cek session: layout tidak di-render ulang saat navigasi antar halaman di dalamnya. +- Fungsi baca di `data/` yang dipanggil beberapa komponen dalam satu render (mis. + `getCurrentUser`, `listGroupProductOptions`) dibungkus `cache` dari `react`: query jalan sekali + per request, request berikutnya mengambil ulang. Jangan pakai `"use cache"` (cache lintas + request) untuk data `cleo` atau session: PHP lama menulis ke DB yang sama sehingga data jadi + basi, dan session yang di-cache membuat user yang sudah logout tetap dianggap login. - `cacheComponents` aktif: komponen yang membaca session (`headers()`/`cookies()`) wajib berada di dalam ``, kalau tidak build gagal. Jangan `await` session di level atas layout. - `proxy.ts` hanya untuk redirect cepat berdasarkan ada/tidaknya cookie session, bukan pengganti diff --git a/data/auth.ts b/data/auth.ts index 8b231a8..df0d065 100644 --- a/data/auth.ts +++ b/data/auth.ts @@ -1,4 +1,5 @@ import "server-only"; +import { cache } from "react"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; import { auth } from "@/lib/auth"; @@ -9,7 +10,9 @@ export type CurrentUser = { username: string | null; }; -export async function getCurrentUser(): Promise { +// `cache` React: session divalidasi sekali per request walau dipanggil beberapa komponen. +// Jangan diganti `"use cache"`: cache lintas request membuat user logout tetap dianggap login. +export const getCurrentUser = cache(async (): Promise => { const session = await auth.api.getSession({ headers: await headers() }); if (!session) redirect("/login"); @@ -18,7 +21,7 @@ export async function getCurrentUser(): Promise { name: session.user.name, username: session.user.username ?? null, }; -} +}); // Dipakai halaman login: session divalidasi (bukan cuma cek cookie) supaya // cookie kedaluwarsa tidak memicu redirect bolak-balik dengan getCurrentUser().