chore: 工具链升级到最新版本(Vite 8 / React 19 / react-router 8 / TS 7 / Go 1.26 依赖更新)

- 前端:vite ^8.2.2、react/react-dom ^19.2.8、react-router ^8.3.1(移除 react-router-dom)、typescript ^7.0.2、zustand ^5.0.15;适配升级带来的类型变化(react-hook-form Resolver、ProseMirror node.parent、tiptap focus 参数、timer undefined 等)
- 后端:升级 gin/jwt/minio-go/gorm/modernc-sqlite/x-crypto 等直接依赖至最新;gowebp v0.1.1 已是最新版本
- tsconfig 移除 baseUrl,paths 改为相对路径以兼容 TS7
This commit is contained in:
2026-09-09 00:14:12 +08:00
parent 711cd8b759
commit 52d328ffc3
63 changed files with 2007 additions and 1563 deletions

View File

@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { useForm, type Resolver } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Plus, FolderKanban } from 'lucide-react';
@@ -52,8 +52,9 @@ export default function BoardsManagePage() {
const [submitting, setSubmitting] = useState(false);
const [reordering, setReordering] = useState(false);
// z.coerce 的 input 类型为 unknown,这里显式断言对齐 output 类型
const form = useForm<BoardFormValues>({
resolver: zodResolver(boardSchema),
resolver: zodResolver(boardSchema) as Resolver<BoardFormValues>,
defaultValues: { name: '', description: '', sort_order: 1, icon: '', color_index: -1 },
});

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useMemo, useRef } from 'react';
import { useNavigate, useSearchParams, useParams, useOutletContext } from 'react-router-dom';
import { useNavigate, useSearchParams, useParams, useOutletContext } from 'react-router';
import { Pencil } from 'lucide-react';
import { notify } from '@/lib/notify';
import { api } from '../api/client';

View File

@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { ArrowLeft, Star } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { useNavigate, Link } from 'react-router';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

View File

@@ -6,7 +6,7 @@ import {
useLocation,
useParams,
useNavigationType,
} from 'react-router-dom';
} from 'react-router';
import { notify } from '@/lib/notify';
import { api } from '../api/client';
import type { PostItem } from '../api/types';

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { Link, useNavigate, useSearchParams } from 'react-router';
import { ArrowLeft, Link2, Pencil, Plus, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useNavigate, Link, useSearchParams } from 'react-router-dom';
import { useNavigate, Link, useSearchParams } from 'react-router';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link, useNavigate, useSearchParams } from 'react-router-dom';
import { Link, useNavigate, useSearchParams } from 'react-router';
import {
ArrowLeft,
AtSign,

View File

@@ -1,4 +1,4 @@
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { FileQuestion, Home } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { usePageSEO } from '../hooks/usePageSEO';

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useLayoutEffect, useRef, useCallback, useMemo } from 'react';
import { useParams, useNavigate, useOutletContext, useLocation, useNavigationType } from 'react-router-dom';
import { useParams, useNavigate, useOutletContext, useLocation, useNavigationType } from 'react-router';
import { ArrowLeft, ThumbsUp, Star, Lock, MessageSquare, MessageSquareOff, Flag, MoreHorizontal } from 'lucide-react';
import FeaturedIcon from '@/components/FeaturedIcon';
import { Button } from '@/components/ui/button';
@@ -155,7 +155,7 @@ export default function PostDetailPage() {
const pageRef = useRef<HTMLDivElement>(null);
const commentSectionRef = useRef<HTMLDivElement>(null);
const commentBoxRef = useRef<HTMLDivElement>(null);
const highlightTimer = useRef<ReturnType<typeof setTimeout>>();
const highlightTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const postRef = useRef<PostItem | null>(null);
const scrollTopRef = useRef(
initialSnap && navType === 'POP' && !location.hash ? initialSnap.scrollTop : 0,

View File

@@ -1,5 +1,5 @@
import { useState, useRef, useEffect, useCallback } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
@@ -91,7 +91,7 @@ export default function ProfilePage() {
const [idCopied, setIdCopied] = useState(false);
const fileRef = useRef<HTMLInputElement>(null);
const dragCounter = useRef(0);
const copyTimer = useRef<ReturnType<typeof setTimeout>>();
const copyTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const sigSectionRef = useRef<HTMLDivElement>(null);
const pendingFocusSig = useRef(false);

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { ArrowLeft, FolderGit2, Search } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate, Link, useSearchParams } from 'react-router-dom';
import { useNavigate, Link, useSearchParams } from 'react-router';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { ArrowLeft, ExternalLink, Globe2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef } from 'react';
import { useOutletContext, useParams } from 'react-router-dom';
import { useOutletContext, useParams } from 'react-router';
import NotFoundPage from './NotFoundPage';
import { api } from '../api/client';
import type { SitePage } from '../api/types';

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useNavigate, useParams, useLocation } from 'react-router';
import {
ArrowLeft,
FileText,

View File

@@ -170,9 +170,9 @@ export default function AdminBadgesPage() {
setReordering(true);
try {
const merged = mergeReorderedSubset(before, subsetBefore, reorderedFiltered);
const after = await persistSortOrderChanges(before, merged, badge =>
api.adminUpsertBadge({ ...badge, sort_order: badge.sort_order }),
);
const after = await persistSortOrderChanges(before, merged, async badge => {
await api.adminUpsertBadge({ ...badge, sort_order: badge.sort_order });
});
setRows(after);
notify.success('徽章排序已更新');
} catch (e: unknown) {

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router';
import { Trash2, RotateCcw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@@ -53,7 +53,7 @@ export default function AdminCommentsPage() {
const [revComment, setRevComment] = useState<Comment | null>(null);
const [highlightId, setHighlightId] = useState<number | null>(focusId > 0 ? focusId : null);
const focusTriedRef = useRef(false);
const highlightTimer = useRef<ReturnType<typeof setTimeout>>();
const highlightTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const loadList = (p = page, st: Tab = tab) => {
setLoading(true);

View File

@@ -1,5 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import { Link } from 'react-router';
import { Globe2, ExternalLink, Star } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { Badge } from '@/components/ui/badge';

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { FileText, Flag, MessageSquare, Link2, Activity } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { Badge } from '@/components/ui/badge';

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Link, useNavigate } from 'react-router';
import {
Link2, Plus, Trash2, Check, X, ExternalLink, Eye, RotateCcw,
} from 'lucide-react';
@@ -173,24 +173,27 @@ export default function AdminLinksPage() {
aside_show_friend_links: s.limits?.aside_show_friend_links ?? true,
}),
);
const base: ForumLimits = {
open_posts_in_new_tab: true,
open_content_links_in_new_tab: true,
aside_show_tag_cloud: false,
aside_show_recent_comments: false,
aside_show_friend_links: true,
aside_show_showcase: false,
aside_widgets: loadedAsideWidgets,
nav_show_friend_links: true,
footer_show_friend_links: true,
nav_show_showcase: false,
footer_show_showcase: false,
feed_list_style: 'title',
feed_sort_tabs: DEFAULT_FEED_SORT_TABS,
permalink_enabled: false,
permalink_ext: 'html',
...s.limits,
};
// 默认值在前、server limits 覆盖在后(JSON 缺 key 不覆盖)
const base: ForumLimits = Object.assign(
{
open_posts_in_new_tab: true,
open_content_links_in_new_tab: true,
aside_show_tag_cloud: false,
aside_show_recent_comments: false,
aside_show_friend_links: true,
aside_show_showcase: false,
aside_widgets: loadedAsideWidgets,
nav_show_friend_links: true,
footer_show_friend_links: true,
nav_show_showcase: false,
footer_show_showcase: false,
feed_list_style: 'title',
feed_sort_tabs: DEFAULT_FEED_SORT_TABS,
permalink_enabled: false,
permalink_ext: 'html',
} as ForumLimits,
s.limits,
);
setForumLimits(mergeForumLimitsWithAsideWidgets(base, loadedAsideWidgets));
})
.catch(() => { /* 入口开关稍后仍可按默认展示 */ });

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useSearchParams } from 'react-router';
import { Activity, AlertTriangle } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { Button } from '@/components/ui/button';

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { FileText, Plus, Pencil, Trash2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
@@ -76,8 +76,8 @@ export default function AdminPagesPage() {
const before = [...rows];
setReordering(true);
try {
const after = await persistSortOrderChanges(before, reordered, page =>
api.adminUpdatePage(page.id, {
const after = await persistSortOrderChanges(before, reordered, async page => {
await api.adminUpdatePage(page.id, {
title: page.title,
slug: page.slug,
content: page.content,
@@ -85,8 +85,8 @@ export default function AdminPagesPage() {
sort_order: page.sort_order,
show_in_footer: page.show_in_footer,
show_in_nav: page.show_in_nav,
}),
);
});
});
setRows(after);
invalidateSitePagesCache();
notify.success('单页排序已更新');

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useNavigate, useSearchParams } from 'react-router';
import { Search, Lock, LockOpen, MessageSquareOff, Trash2, RotateCcw } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -46,7 +46,7 @@ export default function AdminPostsPage() {
const [search, setSearch] = useState('');
const [highlightId, setHighlightId] = useState<number | null>(focusId > 0 ? focusId : null);
const focusTriedRef = useRef(false);
const highlightTimer = useRef<ReturnType<typeof setTimeout>>();
const highlightTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const loadActive = (p = page, kw = search, status = tab === 'pending' ? 'pending' : 'all') => {
setLoading(true);

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Spinner } from '@/components/ui/spinner';

View File

@@ -294,24 +294,27 @@ export default function AdminSettingsPage() {
}),
);
setAsideWidgets(loadedAsideWidgets);
const baseLimits: ForumLimits = {
open_posts_in_new_tab: true,
open_content_links_in_new_tab: true,
aside_show_tag_cloud: false,
aside_show_recent_comments: false,
aside_show_friend_links: true,
aside_show_showcase: false,
aside_widgets: loadedAsideWidgets,
nav_show_friend_links: true,
footer_show_friend_links: true,
nav_show_showcase: false,
footer_show_showcase: false,
feed_list_style: 'title',
feed_sort_tabs: normalizeFeedSortTabs(s.limits?.feed_sort_tabs ?? DEFAULT_FEED_SORT_TABS),
permalink_enabled: false,
permalink_ext: 'html',
...s.limits,
};
// 默认值在前、server limits 覆盖在后(JSON 缺 key 不覆盖)
const baseLimits: ForumLimits = Object.assign(
{
open_posts_in_new_tab: true,
open_content_links_in_new_tab: true,
aside_show_tag_cloud: false,
aside_show_recent_comments: false,
aside_show_friend_links: true,
aside_show_showcase: false,
aside_widgets: loadedAsideWidgets,
nav_show_friend_links: true,
footer_show_friend_links: true,
nav_show_showcase: false,
footer_show_showcase: false,
feed_list_style: 'title',
feed_sort_tabs: normalizeFeedSortTabs(s.limits?.feed_sort_tabs ?? DEFAULT_FEED_SORT_TABS),
permalink_enabled: false,
permalink_ext: 'html',
} as ForumLimits,
s.limits,
);
setLimits(mergeForumLimitsWithAsideWidgets(baseLimits, loadedAsideWidgets));
setBranding({ ...DEFAULT_BRANDING, ...(s.branding ?? {}) });
setMail({ ...EMPTY_MAIL, ...s.mail, password: '' });

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router';
import { ArrowLeft, ExternalLink, Save } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

View File

@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router';
import {
Award, Ban, BadgeCheck, MoreHorizontal, Search, Shield, UserCog,
} from 'lucide-react';