This commit is contained in:
zuiho
2026-08-31 00:30:22 +08:00
committed by GitHub
24 changed files with 328 additions and 93 deletions
+7 -2
View File
@@ -1,15 +1,20 @@
package middleware
import (
"strings"
"github.com/gin-gonic/gin"
)
func Cache() func(c *gin.Context) {
return func(c *gin.Context) {
if c.Request.RequestURI == "/" {
path := c.Request.URL.Path
if strings.HasPrefix(path, "/static/") {
c.Header("Cache-Control", "public, max-age=31536000, immutable")
} else if path == "/" {
c.Header("Cache-Control", "no-cache")
} else {
c.Header("Cache-Control", "max-age=604800") // one week
c.Header("Cache-Control", "public, max-age=604800") // one week
}
c.Header("Cache-Version", "b688f2fb5be447c25e5aa3bd063087a83db32a288bf6a4f35f2d8db310e40b14")
c.Next()
+60
View File
@@ -0,0 +1,60 @@
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCacheHeaders(t *testing.T) {
gin.SetMode(gin.TestMode)
tests := []struct {
name string
path string
expected string
}{
{
name: "fingerprinted static assets are immutable",
path: "/static/js/index.0614d5c7d4.js",
expected: "public, max-age=31536000, immutable",
},
{
name: "the application shell is revalidated",
path: "/",
expected: "no-cache",
},
{
name: "application shell query parameters do not change caching",
path: "/?cache_bust=1",
expected: "no-cache",
},
{
name: "non-fingerprinted public assets keep the existing short cache",
path: "/favicon.ico",
expected: "public, max-age=604800",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router := gin.New()
router.Use(Cache())
router.GET("/*path", func(c *gin.Context) {
c.Status(http.StatusNoContent)
})
request := httptest.NewRequest(http.MethodGet, tt.path, nil)
response := httptest.NewRecorder()
router.ServeHTTP(response, request)
require.Equal(t, http.StatusNoContent, response.Code)
assert.Equal(t, tt.expected, response.Header().Get("Cache-Control"))
assert.NotEmpty(t, response.Header().Get("Cache-Version"))
})
}
}
+9 -2
View File
@@ -29,6 +29,13 @@ export default defineConfig(({ envMode }) => {
splitChunks: {
preset: 'default',
cacheGroups: {
'vendor-markdown': {
test: /node_modules[\\/](katex|marked)[\\/]/,
name: 'vendor-markdown',
chunks: 'async',
priority: 20,
enforce: true,
},
'vendor-react': {
test: /node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor-react',
@@ -39,14 +46,14 @@ export default defineConfig(({ envMode }) => {
'vendor-ui-primitives': {
test: /node_modules[\\/](@base-ui|@radix-ui)[\\/]/,
name: 'vendor-ui-primitives',
chunks: 'all',
chunks: 'initial',
priority: 0,
enforce: true,
},
'vendor-tanstack': {
test: /node_modules[\\/]@tanstack[\\/]/,
name: 'vendor-tanstack',
chunks: 'all',
chunks: 'initial',
priority: 0,
enforce: true,
},
@@ -0,0 +1,39 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { render, screen } from '@testing-library/react'
import { describe, expect, test } from 'vitest'
import { RichContent } from '../rich-content'
describe('RichContent', () => {
test('renders Markdown after its deferred renderer loads', async () => {
render(<RichContent content='**Deferred Markdown**' />)
expect(screen.getByRole('status')).toBeInTheDocument()
expect(screen.getByText('•••')).toHaveAttribute('aria-hidden', 'true')
expect(await screen.findByText('Deferred Markdown')).toBeInTheDocument()
expect(screen.getByText('Deferred Markdown').tagName).toBe('STRONG')
})
test('keeps HTML content on the immediate renderer path', () => {
render(<RichContent mode='html' content='<strong>Trusted HTML</strong>' />)
expect(screen.getByText('Trusted HTML').tagName).toBe('STRONG')
})
})
+30
View File
@@ -0,0 +1,30 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import type { ReactElement } from 'react'
export default function DevelopmentTools(): ReactElement {
return (
<>
<ReactQueryDevtools buttonPosition='bottom-left' />
<TanStackRouterDevtools position='bottom-right' />
</>
)
}
+29 -4
View File
@@ -16,8 +16,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { lazy, Suspense } from 'react'
import { useTranslation } from 'react-i18next'
import { HtmlContent, type HtmlContentVariant } from '@/components/html-content'
import { Markdown } from '@/components/ui/markdown'
const Markdown = lazy(() =>
import('@/components/ui/markdown').then((module) => ({
default: module.Markdown,
}))
)
type RichContentMode = 'markdown' | 'html'
@@ -30,6 +38,8 @@ interface RichContentProps {
}
export function RichContent(props: RichContentProps) {
const { t } = useTranslation()
if (props.mode === 'html') {
return (
<HtmlContent
@@ -41,8 +51,23 @@ export function RichContent(props: RichContentProps) {
}
return (
<Markdown breaks={props.breaks} className={props.className}>
{props.content}
</Markdown>
<Suspense
fallback={
<div
className={props.className}
data-testid='rich-content-loading'
role='status'
>
<span className='sr-only'>{t('Loading...')}</span>
<span aria-hidden='true' className='text-muted-foreground text-sm'>
</span>
</div>
}
>
<Markdown breaks={props.breaks} className={props.className}>
{props.content}
</Markdown>
</Suspense>
)
}
@@ -0,0 +1,41 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { describe, expect, test } from 'vitest'
import {
loadInterfaceLanguage,
supportedInterfaceLanguages,
} from '../locale-loader'
describe('lazy locale loading', () => {
test('loads only the requested interface language resource', async () => {
const resource = await loadInterfaceLanguage('zhCN')
const translations = resource.translation as Record<string, string>
expect(translations._copy).toBe('_复制')
expect(supportedInterfaceLanguages).toContain('zhCN')
})
test('falls back to English for an unknown locale', async () => {
const resource = await loadInterfaceLanguage('unknown')
const translations = resource.translation as Record<string, string>
expect(translations._copy).toBe('_copy')
})
})
+21 -20
View File
@@ -16,36 +16,37 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import i18n from 'i18next'
import i18n, { type BackendModule } from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next } from 'react-i18next'
import { convertDetectedLanguage } from './languages'
import en from './locales/en.json'
import fr from './locales/fr.json'
import ja from './locales/ja.json'
import ru from './locales/ru.json'
import vi from './locales/vi.json'
import zhTW from './locales/zh-TW.json'
import zhCN from './locales/zh.json'
import {
loadInterfaceLanguage,
supportedInterfaceLanguages,
} from './locale-loader'
export const resources = {
en,
zhCN,
fr,
ru,
ja,
vi,
zhTW,
} as const
const lazyLocaleBackend: BackendModule = {
type: 'backend',
init: () => undefined,
read: (language, namespace, callback) => {
const handleResource = (
resource: Awaited<ReturnType<typeof loadInterfaceLanguage>>
) => callback(null, resource[namespace])
const handleError = (error: unknown) =>
callback(error instanceof Error ? error : String(error), null)
i18n
void loadInterfaceLanguage(language).then(handleResource, handleError)
},
}
await i18n
.use(LanguageDetector)
.use(lazyLocaleBackend)
.use(initReactI18next)
.init({
resources,
fallbackLng: 'en',
supportedLngs: ['en', 'zhCN', 'fr', 'ru', 'ja', 'vi', 'zhTW'],
supportedLngs: supportedInterfaceLanguages,
load: 'currentOnly',
nsSeparator: false, // Allow literal colons in keys (e.g., URLs, labels)
debug: import.meta.env.DEV,
+40
View File
@@ -0,0 +1,40 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import type { Resource } from 'i18next'
const localeLoaders = {
en: () => import('./locales/en.json'),
zhCN: () => import('./locales/zh.json'),
fr: () => import('./locales/fr.json'),
ru: () => import('./locales/ru.json'),
ja: () => import('./locales/ja.json'),
vi: () => import('./locales/vi.json'),
zhTW: () => import('./locales/zh-TW.json'),
} as const
export const supportedInterfaceLanguages = Object.keys(localeLoaders)
export async function loadInterfaceLanguage(
language: string
): Promise<Resource[string]> {
const loader =
localeLoaders[language as keyof typeof localeLoaders] ?? localeLoaders.en
const locale = await loader()
return locale.default
}
+10 -9
View File
@@ -17,15 +17,13 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useQueryClient, type QueryClient } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import {
createRootRouteWithContext,
Outlet,
redirect,
useNavigate,
} from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
import { useEffect } from 'react'
import { lazy, Suspense, useEffect } from 'react'
import { NavigationProgress } from '@/components/navigation-progress'
import { Toaster } from '@/components/ui/sonner'
@@ -44,6 +42,10 @@ import { subscribeAuthSessionEvents } from '@/lib/auth-session-sync'
import { resolveLegacyRoute } from '@/lib/legacy-route'
import { useAuthStore } from '@/stores/auth-store'
const DevelopmentTools = import.meta.env.DEV
? lazy(() => import('@/components/development-tools'))
: null
function RootComponent() {
const navigate = useNavigate()
const queryClient = useQueryClient()
@@ -97,12 +99,11 @@ function RootComponent() {
<NavigationProgress />
<Outlet />
<Toaster closeButton duration={5000} position='top-center' richColors />
{import.meta.env.MODE === 'development' && (
<>
<ReactQueryDevtools buttonPosition='bottom-left' />
<TanStackRouterDevtools position='bottom-right' />
</>
)}
{DevelopmentTools ? (
<Suspense fallback={null}>
<DevelopmentTools />
</Suspense>
) : null}
</ThemeCustomizationProvider>
)
}
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { AuthSettings } from '@/features/system-settings/auth'
import {
AUTH_DEFAULT_SECTION,
AUTH_SECTION_IDS,
} from '@/features/system-settings/auth/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/auth/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { AUTH_DEFAULT_SECTION, AUTH_SECTION_IDS } =
await import('@/features/system-settings/auth/section-registry.tsx')
const validSections = AUTH_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,10 +18,10 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { AUTH_DEFAULT_SECTION } from '@/features/system-settings/auth/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/auth/')({
beforeLoad: () => {
beforeLoad: async () => {
const { AUTH_DEFAULT_SECTION } =
await import('@/features/system-settings/auth/section-registry.tsx')
throw redirect({
to: '/system-settings/auth/$section',
params: { section: AUTH_DEFAULT_SECTION },
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { BillingSettings } from '@/features/system-settings/billing'
import {
BILLING_DEFAULT_SECTION,
BILLING_SECTION_IDS,
} from '@/features/system-settings/billing/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/billing/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { BILLING_DEFAULT_SECTION, BILLING_SECTION_IDS } =
await import('@/features/system-settings/billing/section-registry.tsx')
const validSections = BILLING_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,12 +18,12 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { BILLING_DEFAULT_SECTION } from '@/features/system-settings/billing/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/billing/'
)({
beforeLoad: () => {
beforeLoad: async () => {
const { BILLING_DEFAULT_SECTION } =
await import('@/features/system-settings/billing/section-registry.tsx')
throw redirect({
to: '/system-settings/billing/$section',
params: { section: BILLING_DEFAULT_SECTION },
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { ContentSettings } from '@/features/system-settings/content'
import {
CONTENT_DEFAULT_SECTION,
CONTENT_SECTION_IDS,
} from '@/features/system-settings/content/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/content/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { CONTENT_DEFAULT_SECTION, CONTENT_SECTION_IDS } =
await import('@/features/system-settings/content/section-registry.tsx')
const validSections = CONTENT_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,12 +18,12 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { CONTENT_DEFAULT_SECTION } from '@/features/system-settings/content/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/content/'
)({
beforeLoad: () => {
beforeLoad: async () => {
const { CONTENT_DEFAULT_SECTION } =
await import('@/features/system-settings/content/section-registry.tsx')
throw redirect({
to: '/system-settings/content/$section',
params: { section: CONTENT_DEFAULT_SECTION },
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { ModelSettings } from '@/features/system-settings/models'
import {
MODELS_DEFAULT_SECTION,
MODELS_SECTION_IDS,
} from '@/features/system-settings/models/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/models/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { MODELS_DEFAULT_SECTION, MODELS_SECTION_IDS } =
await import('@/features/system-settings/models/section-registry.tsx')
const validSections = MODELS_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,11 +18,11 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { MODELS_DEFAULT_SECTION } from '@/features/system-settings/models/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/models/')(
{
beforeLoad: () => {
beforeLoad: async () => {
const { MODELS_DEFAULT_SECTION } =
await import('@/features/system-settings/models/section-registry.tsx')
throw redirect({
to: '/system-settings/models/$section',
params: { section: MODELS_DEFAULT_SECTION },
@@ -19,15 +19,11 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { OperationsSettings } from '@/features/system-settings/operations'
import {
OPERATIONS_DEFAULT_SECTION,
OPERATIONS_SECTION_IDS,
} from '@/features/system-settings/operations/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/operations/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
if (params.section === 'monitoring') {
throw redirect({
to: '/system-settings/models/$section',
@@ -35,6 +31,8 @@ export const Route = createFileRoute(
})
}
const { OPERATIONS_DEFAULT_SECTION, OPERATIONS_SECTION_IDS } =
await import('@/features/system-settings/operations/section-registry.tsx')
const validSections = OPERATIONS_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,12 +18,12 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { OPERATIONS_DEFAULT_SECTION } from '@/features/system-settings/operations/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/operations/'
)({
beforeLoad: () => {
beforeLoad: async () => {
const { OPERATIONS_DEFAULT_SECTION } =
await import('@/features/system-settings/operations/section-registry.tsx')
throw redirect({
to: '/system-settings/operations/$section',
params: { section: OPERATIONS_DEFAULT_SECTION },
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SecuritySettings } from '@/features/system-settings/security'
import {
SECURITY_DEFAULT_SECTION,
SECURITY_SECTION_IDS,
} from '@/features/system-settings/security/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/security/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { SECURITY_DEFAULT_SECTION, SECURITY_SECTION_IDS } =
await import('@/features/system-settings/security/section-registry.tsx')
const validSections = SECURITY_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,12 +18,12 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SECURITY_DEFAULT_SECTION } from '@/features/system-settings/security/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/security/'
)({
beforeLoad: () => {
beforeLoad: async () => {
const { SECURITY_DEFAULT_SECTION } =
await import('@/features/system-settings/security/section-registry.tsx')
throw redirect({
to: '/system-settings/security/$section',
params: { section: SECURITY_DEFAULT_SECTION },
@@ -19,15 +19,13 @@ For commercial licensing, please contact support@quantumnous.com
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SiteSettings } from '@/features/system-settings/site'
import {
SITE_DEFAULT_SECTION,
SITE_SECTION_IDS,
} from '@/features/system-settings/site/section-registry.tsx'
export const Route = createFileRoute(
'/_authenticated/system-settings/site/$section'
)({
beforeLoad: ({ params }) => {
beforeLoad: async ({ params }) => {
const { SITE_DEFAULT_SECTION, SITE_SECTION_IDS } =
await import('@/features/system-settings/site/section-registry.tsx')
const validSections = SITE_SECTION_IDS as unknown as string[]
if (!validSections.includes(params.section)) {
throw redirect({
@@ -18,10 +18,10 @@ For commercial licensing, please contact support@quantumnous.com
*/
import { createFileRoute, redirect } from '@tanstack/react-router'
import { SITE_DEFAULT_SECTION } from '@/features/system-settings/site/section-registry.tsx'
export const Route = createFileRoute('/_authenticated/system-settings/site/')({
beforeLoad: () => {
beforeLoad: async () => {
const { SITE_DEFAULT_SECTION } =
await import('@/features/system-settings/site/section-registry.tsx')
throw redirect({
to: '/system-settings/site/$section',
params: { section: SITE_DEFAULT_SECTION },