v2.19.0#273
Conversation
- refactor version fetching to use a dedicated API endpoint - add validation functions for ObjectId and custom IDs - improve URL sanitization to prevent path traversal - implement AbortController in data fetching hooks to prevent memory leaks - update component structure for better readability and maintainability
There was a problem hiding this comment.
Pull request overview
This PR bumps the frontend to v2.19.0 and focuses on security/stability improvements (moving GitHub token usage server-side, adding input validation/sanitization, and reducing client-side fetch memory leaks via AbortController), plus a new in-app changelog page.
Changes:
- Add validation utilities and apply URL param sanitization + stricter ID validation in routing.
- Introduce
/api/versionand refactor online-version fetching to avoid exposing GitHub tokens in client code. - Add a Changelog page (desktop/mobile) backed by a JSON changelog; add broader
AbortControllerusage across fetches.
Reviewed changes
Copilot reviewed 75 out of 75 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| utils/useUserAccount/index.ts | Import reordering. |
| utils/usePost/index.ts | Import reordering. |
| utils/useLocalStorage/index.tsx | Import reordering. |
| utils/useFetch/index.ts | Adds abort + mounted-guard behavior to useFetch. |
| package.json | Version bump to 2.19.0. |
| modules/validation/index.ts | New shared validators + URL param sanitization helpers. |
| modules/sigAPI/index.ts | Import reordering. |
| modules/sigAPI/function/getUserData.ts | Adds optional AbortSignal support. |
| modules/sigAPI/function/getSigData.ts | Adds optional AbortSignal support. |
| modules/sigAPI/function/_request.ts | Extends request option typing to allow signal. |
| modules/api/GetOnlineAppVersion/index.ts | Refactors to client-side fetch from /api/version. |
| components/Threads/mobile/ThreadsList.tsx | Import reordering. |
| components/Threads/desktop/ThreadsList.tsx | Import reordering. |
| components/PostEditor/mobile/PostEditor.tsx | Import reordering. |
| components/PostEditor/mobile/MetaDataForm.tsx | Import reordering. |
| components/PostEditor/mobile/Editor.tsx | Import reordering. |
| components/PostEditor/desktop/TitleSigForm.tsx | Import reordering / type-only import adjustments. |
| components/PostEditor/desktop/PostEditor.tsx | Import grouping changes. |
| components/PostEditor/desktop/MetaDataForm.tsx | Import reordering. |
| components/PostEditor/desktop/Editor.tsx | Import reordering. |
| components/NotFound/index.tsx | Import reordering. |
| app/providers.tsx | Import reordering. |
| app/post/[postID]/page.tsx | Adds param sanitization + ObjectId validation + abort on fetch. |
| app/post/[postID]/edit/page.tsx | Import reordering. |
| app/post/[postID]/(post)/mobile/Thread.tsx | Import reordering. |
| app/post/[postID]/(post)/mobile/Replies.tsx | Import reordering. |
| app/post/[postID]/(post)/desktop/ThreadInfo.tsx | Adds mounted-guard + abort controller for fetches. |
| app/post/[postID]/(post)/desktop/Thread.tsx | Import reordering. |
| app/post/[postID]/(post)/components/Reply.tsx | Hardens external-link confirm flow (protocol validation, HTML escaping, noopener). |
| app/ping/route.ts | Moves version fetching server-side; expands backend version shape; adds logging. |
| app/page.tsx | Import reordering. |
| app/new/page.tsx | Adds localStorage schema validation/sanitization for persisted draft data. |
| app/layout.tsx | Import reordering. |
| app/info/page.tsx | Import reordering. |
| app/info/(Info)/mobile/index.tsx | Formatting tweaks; uptime display refactor. |
| app/info/(Info)/desktop/index.tsx | Formatting tweaks; uptime display refactor. |
| app/info/(Info)/config/data.json | Renames a menu section and adds Changelog link. |
| app/device.tsx | Import reordering. |
| app/data/page.tsx | Import reordering. |
| app/data/(Data)/mobile/index.tsx | Import reordering. |
| app/data/(Data)/desktop/index.tsx | Import reordering. |
| app/dashboard/page.tsx | Import reordering. |
| app/dashboard/(Dashboard)/Mobile/index.tsx | Adds abort controllers + error logging to fetches. |
| app/dashboard/(Dashboard)/Desktop/index.tsx | Adds abort controllers + error logging to fetches. |
| app/confirm/page.tsx | Adds abort controller wiring to confirm request. |
| app/changelog/page.tsx | New changelog route with mobile/desktop split. |
| app/changelog/(Changelog)/mobile/index.tsx | New mobile changelog UI rendering JSON entries. |
| app/changelog/(Changelog)/mobile/index.module.scss | New styles for mobile changelog layout. |
| app/changelog/(Changelog)/desktop/index.tsx | New desktop changelog UI rendering JSON entries. |
| app/changelog/(Changelog)/config/changelog.json | New changelog content source. |
| app/api/version/route.ts | New server route to fetch main/development versions from GitHub. |
| app/api/auth/[...nextauth]/route.ts | Import cleanup/reordering (also removed a stray profile import). |
| app/admin/sig-members/page.tsx | Import reordering + formatting. |
| app/admin/sig-members/[sigID]/page.tsx | Formatting improvements. |
| app/admin/sig-leader/page.tsx | Import reordering + formatting. |
| app/admin/sig-leader/[sigID]/page.tsx | Formatting improvements. |
| app/admin/sig-advisor/page.tsx | Import reordering + formatting. |
| app/admin/sig-advisor/[sigID]/page.tsx | Formatting improvements. |
| app/admin/post/page.tsx | Import reordering + formatting. |
| app/admin/post-query/page.tsx | Import reordering + formatting. |
| app/admin/page.tsx | Import reordering + formatting. |
| app/[userID]/page.tsx | Adds userID validation/sanitization + abort support for account fetches. |
| app/[userID]/(User)/mobile/info/index.tsx | Hardens external-link confirm flow (protocol validation, escaping, noopener). |
| app/[userID]/(User)/mobile/index.tsx | Import reordering. |
| app/[userID]/(User)/desktop/Info/index.tsx | Hardens external-link confirm flow (protocol validation, escaping, noopener). |
| app/(Layout)/mobile/ToolBar/index.tsx | Import reordering. |
| app/(Layout)/mobile/HeaderBar/UserLogin/index.tsx | Import reordering. |
| app/(Layout)/mobile/HeaderBar/index.tsx | Import reordering. |
| app/(Layout)/desktop/ToolBar/index.tsx | Import reordering. |
| app/(Layout)/desktop/HeaderBar/index.tsx | Import reordering. |
| app/(home)/mobile/ThreadsList.tsx | Import reordering. |
| app/(home)/mobile/SigList.tsx | Import reordering. |
| app/(home)/desktop/Information.tsx | Import reordering. |
| AGENTS.md | Adds an AI agent guide for repo conventions and structure. |
| .env.local.example | Replaces NEXT_PUBLIC_GITHUB_TOKEN with server-only GITHUB_TOKEN. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
OnCloud125252
left a comment
There was a problem hiding this comment.
Code Review — v2.19.0
Overview
This is a well-targeted security release. The four main themes are:
- Security hardening — GitHub token exposure fix, XSS prevention, URL/input validation
- Memory leak prevention —
AbortController+isMountedpatterns across fetch calls - New feature — Changelog page (desktop + mobile) and
/api/versionroute - Code formatting — Biome formatter applied (import sorting, JSX line breaks)
Security Analysis ✅
The security work here is excellent:
NEXT_PUBLIC_GITHUB_TOKEN→GITHUB_TOKEN: Critical fix. The old token was exposed to every browser client via Next.js'sNEXT_PUBLIC_prefix. Moving it to a server-only env var with/api/versionas a proxy is the correct approach.- XSS prevention in
JumpOut: URL protocol allowlisting, HTML escaping, andnoopener,noreferrerare solid. - Validation module (
modules/validation/index.ts): Well-structured with ObjectId, custom ID, and path traversal sanitization. - localStorage validation: Good defensive practice to validate parsed JSON shape before trusting it.
Issues Found
| # | Severity | Issue |
|---|---|---|
| 1 | 🔴 Bug | useFetch dependency on options object will cause infinite re-render loops |
| 2 | 🟡 DRY | JumpOut function copy-pasted identically in 3 files |
| 3 | 🟡 DRY | getMainVersion() / getDevelopmentVersion() duplicated in 2 route files |
| 4 | 🟢 Minor | Unused useRef import in Dashboard components |
| 5 | 🟢 Minor | Unused urlObj variable assignments in JumpOut (linter may flag) |
See inline comments for details.
Recommendation
Please fix the useFetch infinite loop bug (#1) before merging — it will break any component using useFetch with options. The DRY issues (#2, #3) are strongly recommended but not blocking. The rest is solid work 👍
… leak Module-scope QueryClient was shared across all SSR requests, causing cache entries to accumulate in the Node.js process indefinitely. Moving creation into useState ensures each SSR request gets an isolated instance that is garbage-collected when the response completes.
config() from md-editor-rt was being called at module scope in layout.tsx, a Server Component, pulling a client-side library into the server bundle and letting its global state persist in the Node.js process. Extracted to a dedicated 'use client' module imported only by the components that actually render the editor.
sweetalert2 was imported but never used in the NextAuth route, adding a client-side DOM library to the server bundle unnecessarily. Also added .catch() to the fire-and-forget axios call in the webhook route to prevent unhandled promise rejections from accumulating under login traffic.
Condense AGENTS.md into a concise quick-reference, add CLAUDE.md and GEMINI.md as mirrors, and move detailed content into docs/ subdirectory.
Replace the brief technical description with a structured README including a banner, feature list, usage guide, FAQ, and tech stack summary in Traditional Chinese.
…pecifiers Update the biome script from `lint` to `check --fix --unsafe` for more comprehensive linting, and update AGENTS.md to reflect the new command. Switch to Node.js built-in module specifiers (`node:path`, `node:assert`).
Add missing dependencies to useEffect hooks across dashboard, info, post editor, and utility hooks to prevent stale closure bugs. Fix useLocalStorage to include key in the setValue callback dependency and drive the effect off setValue. Restore isLogin as a dep in useUserAccount despite the comment, and remove userData from edit page's redundant dep.
- Replace string concatenation with template literals in class names - Use optional chaining (?.) instead of short-circuit logical-and - Prefix unused variables/catch bindings with underscore (_error, _res, etc.) - Remove unused imports (React, Link, Fragment, useRef, unused hooks) - Add explicit radix 10 to parseInt() calls - Replace new Date().getTime() with Date.now() - Remove redundant Fragment wrapper elements - Remove !important from CSS editor wrapper rule
- Extract jumpOut URL validation utility from 4 duplicate implementations - Extract getBranchVersion API module from 2 duplicate implementations - Remove unused variables, imports, and dead functions - Stabilize React state and fix useEffect dependencies - Replace node:assert with standard error handling in client code - Parallelize sequential API calls in user page
Add comprehensive Biome config with sorted classes, noForEach, noConsole, noExplicitAny, block statements, and early return rules. Pin all dependency versions and add before-push script.
Document enforced and intentionally-off Biome rules to guide code generation and prevent lint violations.
Sort Tailwind classes, replace forEach with for-of, add block statements, remove else-after-return, replace any types with specific types, and remove bare console.log calls.
Use nullish coalescing for sig._id lookup and mark InfiniteData as optional to prevent undefined key access and prop type mismatches.
Adds a .githooks/pre-push script that runs install, format/lint, type-check, and build before every push. The prepare script wires it up automatically via git config core.hooksPath.
Pre-push now runs gitleaks, env-secret detection, console.log check, and bundle-size validation in addition to install/lint/type/build. Post-merge skips pnpm install when lockfile is unchanged, cleans .next cache on next.config changes, and warns on .env.local.example updates.
Introduces scripts/prepare.mjs which sets the hooksPath and auto-creates .env.local from the example on first install. Removes framer-motion, jwt-decode, and react-cookie (unused after dead-code cleanup). Drops stale turbopack resolveAlias. Updates AGENTS.md commands accordingly.
Next.js 15 passes route params as Promise<{…}>; using use(params)
at the top of the component unwraps it synchronously-safely and
removes the now-redundant inline destructures throughout the file.
Deletes thirteen orphaned files (APIs, utils, SCSS, modules) that are no longer imported anywhere. Removes CustomCode enum, isValidSigId, isValidUUID, and isMingdaoEmail which had no remaining callers. Unexports the Thread component since it is only used within its file.
Disables animations and transitions for users who have requested reduced motion, improving accessibility compliance.
OnCloud125252
left a comment
There was a problem hiding this comment.
PR Review: v2.19.0
Critical Issues
1. parseInt("0x34e718", 10) returns 0 — breaks Discord webhook color
The radix parameter 10 forces decimal parsing. Since "0x34e718" starts with "0x" (invalid in base-10), parseInt returns 0. The original code without radix correctly auto-detected the hex prefix.
Fix: Remove the radix, or use 0x34e718 directly as a number literal.
2. useUserAccount — isLogin re-added to dependency array (potential infinite loop)
The comment says "移除 isLogin 依賴避免循環" but the code adds isLogin back into the dependency array. This was previously identified as the cause of an infinite fetch loop. Adding it back risks re-introducing that bug.
3. Unsafe (error as Error).message without instanceof check
Two catch blocks cast error: unknown directly to Error without checking instanceof. If the thrown value is a string or other non-Error, this produces new Error(undefined). Use error instanceof Error ? error.message : String(error) like other files in this PR.
Positive Highlights
- Great security improvements (XSS, URL validation, token handling)
- Systematic
anyelimination across all admin pages <img>→<Image>migration with properunoptimizedflagstype="button"on all non-form buttons- AbortController for proper fetch cleanup
- Comprehensive git hooks and changelog system
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 119 out of 123 changed files in this pull request and generated 16 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (1)
app/post/[postID]/(post)/mobile/Replies.tsx:177
- The send button is
type="button"and has noonClick, so clicking it will not submit the form (only pressing Enter will). Change it totype="submit"or wire anonClickthat callshandleCommandSubmit/submits the form.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code fixes: - Fix parseInt radix bug in webhook (0x parsed as base-10 → hex literal) - Remove isLogin from useUserAccount deps to prevent infinite loop - Use instanceof check for error handling in platformLogin - Validate accessToken before passing to platformLogin - Fix cover validation in new post page (accept image IDs, not just URLs) - Use shared isValidObjectId instead of duplicated regex - Change send button to type="submit" in ThreadInfo - Fix broken Tailwind class overflow-y- → overflow-y-auto - Set notfound state on fetch error in post page - Use token prop in desktop Editor instead of redundant useUserAccount - Filter failed uploads in mobile Editor to prevent invalid URLs - Use [queryClient] instead of [queryClient.removeQueries] in usePost Doc fixes: - Update lint command from pnpm lint → pnpm check - Replace removed useFetch.ts example with useIsMobile.ts - Update features.md to reference TanStack Query hooks - Remove non-existent validation helpers from docs - Update Biome version to match package.json - Update stale module list in architecture Bump version from 2.18.8 to 2.19.0
Review Feedback AddressedAll 30 review threads have been resolved in commit
Build: passes | Biome check: passes | Type check: passes |
No description provided.