refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)

* refactor(auth): replace dashboard sessions with stateless tokens

* feat(auth): harden session issuance and distributed enforcement

* fix(proxy): preserve trusted proxy compatibility defaults

* refactor: address dashboard auth review feedback

* refactor: remove classic frontend and flatten web app
This commit is contained in:
Calcium-Ion
2026-07-20 16:48:43 +08:00
committed by GitHub
parent 5a6c53d496
commit 31d70fca39
1605 changed files with 17511 additions and 147913 deletions
+31
View File
@@ -0,0 +1,31 @@
/*
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 { api } from '@/lib/api'
import type { LegalDocumentResponse } from './types'
export async function getUserAgreement() {
const res = await api.get<LegalDocumentResponse>('/api/user-agreement')
return res.data
}
export async function getPrivacyPolicy() {
const res = await api.get<LegalDocumentResponse>('/api/privacy-policy')
return res.data
}
+20
View File
@@ -0,0 +1,20 @@
/*
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
*/
export { UserAgreement } from './user-agreement'
export { PrivacyPolicy } from './privacy-policy'
+144
View File
@@ -0,0 +1,144 @@
/*
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 { useQuery } from '@tanstack/react-query'
import { FileWarning } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { PublicLayout } from '@/components/layout'
import { RichContent } from '@/components/rich-content'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Skeleton } from '@/components/ui/skeleton'
import { isHttpUrl, isLikelyHtml } from '@/lib/content-format'
import type { LegalDocumentResponse } from './types'
type LegalDocumentProps = {
title: string
queryKey: string
fetchDocument: () => Promise<LegalDocumentResponse>
emptyMessage: string
}
export function LegalDocument({
title,
queryKey,
fetchDocument,
emptyMessage,
}: LegalDocumentProps) {
const { t } = useTranslation()
const { data, isLoading } = useQuery({
queryKey: [queryKey],
queryFn: fetchDocument,
staleTime: 10 * 60 * 1000,
})
const rawContent = data?.data?.trim() ?? ''
const hasContent = rawContent.length > 0
const isUrl = hasContent && isHttpUrl(rawContent)
const contentIsHtml = hasContent && isLikelyHtml(rawContent)
const success = data?.success ?? false
if (isLoading) {
return (
<PublicLayout>
<div className='mx-auto flex max-w-4xl flex-col gap-4 py-12'>
<Skeleton className='h-8 w-[45%]' />
<Skeleton className='h-4 w-full' />
<Skeleton className='h-4 w-[90%]' />
<Skeleton className='h-4 w-[80%]' />
</div>
</PublicLayout>
)
}
if (!success || !hasContent) {
return (
<PublicLayout>
<div className='mx-auto max-w-2xl py-12'>
<Card className='border-dashed'>
<CardHeader className='flex flex-row items-center gap-4'>
<div className='bg-muted rounded-lg p-2'>
<FileWarning className='text-muted-foreground h-5 w-5' />
</div>
<div className='space-y-1'>
<CardTitle className='text-lg font-semibold'>{title}</CardTitle>
<p className='text-muted-foreground text-sm'>
{data?.message || emptyMessage}
</p>
</div>
</CardHeader>
</Card>
</div>
</PublicLayout>
)
}
if (isUrl) {
return (
<PublicLayout>
<div className='mx-auto max-w-2xl py-12'>
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent className='space-y-4'>
<p className='text-muted-foreground text-sm'>
{t(
'The administrator configured an external link for this document.'
)}
</p>
<Button
render={
<a
href={rawContent}
target='_blank'
rel='noopener noreferrer'
/>
}
>
{t('View document')}
</Button>
</CardContent>
</Card>
</div>
</PublicLayout>
)
}
return (
<PublicLayout showMainContainer={!contentIsHtml}>
{contentIsHtml ? (
<RichContent mode='html' htmlVariant='isolated' content={rawContent} />
) : (
<div className='mx-auto max-w-4xl space-y-6 py-12'>
<div className='space-y-2'>
<h1 className='text-3xl font-semibold tracking-tight'>{title}</h1>
</div>
<RichContent
mode='markdown'
content={rawContent}
className='prose-neutral dark:prose-invert max-w-none'
/>
</div>
)}
</PublicLayout>
)
}
+36
View File
@@ -0,0 +1,36 @@
/*
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 { useTranslation } from 'react-i18next'
import { getPrivacyPolicy } from './api'
import { LegalDocument } from './legal-document'
export function PrivacyPolicy() {
const { t } = useTranslation()
return (
<LegalDocument
title={t('Privacy Policy')}
queryKey='privacy-policy'
fetchDocument={getPrivacyPolicy}
emptyMessage={t(
'The administrator has not configured a privacy policy yet.'
)}
/>
)
}
+23
View File
@@ -0,0 +1,23 @@
/*
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
*/
export type LegalDocumentResponse = {
success: boolean
message?: string
data?: string
}
+36
View File
@@ -0,0 +1,36 @@
/*
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 { useTranslation } from 'react-i18next'
import { getUserAgreement } from './api'
import { LegalDocument } from './legal-document'
export function UserAgreement() {
const { t } = useTranslation()
return (
<LegalDocument
title={t('User Agreement')}
queryKey='user-agreement'
fetchDocument={getUserAgreement}
emptyMessage={t(
'The administrator has not configured a user agreement yet.'
)}
/>
)
}