Applied 30 Aug 2026. Targets Next.js 16. Covers the five critical items (C1–C7) from DEEP_CHECK_REPORT.md.
- Create
.env.localyourself. Remote tooling is blocked from writing.env.local, so it's the one file not applied automatically. Create it in the project root with:(Dev-only secret — use a different one in production.DB_URI=mongodb://127.0.0.1:27017/inventory-nextjs-antd JWT_SECRET=Zun5wZ3PNZDHtJWpvxQy0eU0dVgy3Uz3YDBjFUs5DalSSmlNsOU0E463kj9pl0Do NEXT_PUBLIC_IMAGES_PATH=/productImage.env.localis git-ignored.) - Rotate every exposed credential now. These were committed in plaintext and must be treated as compromised:
- MongoDB Atlas users
sathya(clusterwrqpt) andsuperstar9772(clusterysxjxvp) — change their passwords in Atlas (or delete the users). - The JWT secret (
blackghost) — a new dev secret is already in.env.local; set a different strong one in your production host's environment (openssl rand -base64 48).
- MongoDB Atlas users
- Scrub git history. Neutralizing the files (done) doesn't remove the secrets from past commits. Run BFG or
git filter-repoto purge.local.envand the oldconfig.ts, then force-push and re-rotate:git rm --cached .local.env config.ts # stop tracking (files are now emptied) git filter-repo --path .local.env --path config.ts --invert-paths # purge from history
- Reinstall dependencies.
package.jsonchanged (addedjose,tsx; removedexpress,jsonwebtoken,next-cookie,next-cookies,nookies). Recommended:rm -rf node_modules package-lock.json && npm install - Create the admin account (the login backdoor was removed):
Existing users are unaffected. If you previously logged in through the old
ADMIN_NAME=admin ADMIN_PASSWORD='a-strong-password' \ DB_URI='mongodb://127.0.0.1:27017/inventory-nextjs-antd' \ npm run seed:admin
adminauto-create, reset that account's password. - Delete the dead files once nothing imports them:
git rm config.ts .local.env(both are emptied of secrets and no longer read). - Serve over HTTPS in production. Auth cookies are now
securein production, so they won't be sent over plain HTTP.
config.tsemptied (no more hardcodedJWT_SECRET/DB_URI)..local.envemptied (it held live Atlas credentials; note it was never actually loaded by Next — a misnamed file).- New
.env.example(committed, no secrets) and.env.local(git-ignored, real local values). .gitignorenow ignores.env,.env.*, and.local.env(keeps.env.example), and the uploadedpublic/productImagefolder.
next.config.jsno longer putsDB_URI/JWT_SECRETin theenvblock (that inlined them into client JS). Server code readsprocess.env.*directly; onlyNEXT_PUBLIC_*reaches the browser. Also replaced the deprecatedimages.domainswithremotePatterns.
- New
lib/jwt.ts— sign/verify withjose(Edge-compatible, works in middleware), anAuthErrortype, and correct cookie options (httpOnly,sameSite,securein prod,path, andmaxAgein seconds, fixing the ms/seconds bug). helpers/validateJWT.tsnow throws on a missing/invalid token instead of swallowing the error and returningundefined(which had made every "protected" route effectively public). It no longer mutates cookies as a side effect.- New
helpers/auth.ts—requireUser,requirePermission(key), anderrorResponse(maps auth failures to 401, everything else to a generic 500 without leaking internals).
middleware.tsnow verifies the JWT signature and runs on all routes including/api/*(matcher widened), returning 401 for API and redirecting pages to/login. Public allow-list:/login,/api/users/login,/api/users/logout.- Per-route authorization added where it was missing: user create/update require
addUser; product create / stock / image upload requirenewProduct; recording, check, locations, queries require a valid session.PUT /api/usersno longer blindly spreads the request body (it whitelists fields and hashes any new password).
app/api/image/route.tsrejects keys containing/,\, or.., resolves the path and asserts it stays inside the images directory, restricts to known image extensions, requires auth, and uses asyncfs.
app/api/products/saveImage/route.tsnow requires thenewProductpermission, whitelists extension and MIME type, and caps size at 5 MB.
app/api/users/login/route.tsno longer auto-creates anadminwith a hardcoded hash. Login now returns a generic 401 on bad credentials (no user-enumeration). Usescripts/seedAdmin.ts(npm run seed:admin) to create the first admin.
config/dbConfig.tsis now a cached async connection singleton, soawait connectDB()is meaningful and imports don't open new connections.- Removed no-op
.populate()calls on plain-string fields (queryAll,queryOne) — those throw in newer Mongoose.
- All 29 changed/new TypeScript files pass an esbuild syntax check.
- No remaining imports of
jsonwebtokenorrequire("./config.ts"); no stray top-levelconnectDB()calls.
Before deploying, run npm install then npm run build locally and click through login → each page once. The build is the real integration test for the Next 16 async-API changes.
These are the High/Medium items beyond the five criticals — worth a follow-up pass: floating-point money math (use integers/Decimal128), inconsistent date handling (toLocaleString strings vs createdAt), the client-only menu authorization + direct state mutation in AntdProvider, a validation library (e.g. zod) at route boundaries, and tests/CI. The remaining Next 16 codemod items can be swept with npx @next/codemod@canary upgrade latest.