Skip to content

Commit b02a2cc

Browse files
committed
fix(web): redirect unauthenticated users back after login
1 parent c794e08 commit b02a2cc

File tree

5 files changed

+52
-16
lines changed

5 files changed

+52
-16
lines changed

web/src/app/layout.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export function Layout() {
7171
) : (
7272
<Link
7373
to="/login"
74+
search={{ returnTo: '' }}
7475
className="text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
7576
activeProps={{ className: 'text-primary' }}
7677
>

web/src/app/router.tsx

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,17 @@ const rootRoute = createRootRoute({
4242
component: Layout,
4343
})
4444

45-
async function requireAuth() {
45+
function buildReturnTo(location: { pathname: string; searchStr?: string; hash?: string }) {
46+
return `${location.pathname}${location.searchStr ?? ''}${location.hash ?? ''}`
47+
}
48+
49+
async function requireAuth({ location }: { location: { pathname: string; searchStr?: string; hash?: string } }) {
4650
const user = await getCurrentUser()
4751
if (!user) {
48-
throw redirect({ to: '/login' })
52+
throw redirect({
53+
to: '/login',
54+
search: { returnTo: buildReturnTo(location) },
55+
})
4956
}
5057
return { user }
5158
}
@@ -59,12 +66,18 @@ const skillsRoute = createRoute({
5966
const loginRoute = createRoute({
6067
getParentRoute: () => rootRoute,
6168
path: 'login',
69+
validateSearch: (search: Record<string, unknown>) => ({
70+
returnTo: typeof search.returnTo === 'string' ? search.returnTo : '',
71+
}),
6272
component: LoginPage,
6373
})
6474

6575
const registerRoute = createRoute({
6676
getParentRoute: () => rootRoute,
6777
path: 'register',
78+
validateSearch: (search: Record<string, unknown>) => ({
79+
returnTo: typeof search.returnTo === 'string' ? search.returnTo : '',
80+
}),
6881
component: RegisterPage,
6982
})
7083

@@ -152,8 +165,8 @@ const dashboardReviewDetailRoute = createRoute({
152165
const dashboardPromotionsRoute = createRoute({
153166
getParentRoute: () => rootRoute,
154167
path: 'dashboard/promotions',
155-
beforeLoad: async () => {
156-
const { user } = await requireAuth()
168+
beforeLoad: async (ctx) => {
169+
const { user } = await requireAuth(ctx)
157170
if (!user.platformRoles?.includes('SKILL_ADMIN') && !user.platformRoles?.includes('SUPER_ADMIN')) {
158171
throw redirect({ to: '/dashboard' })
159172
}
@@ -199,8 +212,8 @@ const settingsAccountsRoute = createRoute({
199212
const adminUsersRoute = createRoute({
200213
getParentRoute: () => rootRoute,
201214
path: 'admin/users',
202-
beforeLoad: async () => {
203-
const { user } = await requireAuth()
215+
beforeLoad: async (ctx) => {
216+
const { user } = await requireAuth(ctx)
204217
if (!user.platformRoles?.includes('USER_ADMIN') && !user.platformRoles?.includes('SUPER_ADMIN')) {
205218
throw redirect({ to: '/dashboard' })
206219
}
@@ -212,8 +225,8 @@ const adminUsersRoute = createRoute({
212225
const adminAuditLogRoute = createRoute({
213226
getParentRoute: () => rootRoute,
214227
path: 'admin/audit-log',
215-
beforeLoad: async () => {
216-
const { user } = await requireAuth()
228+
beforeLoad: async (ctx) => {
229+
const { user } = await requireAuth(ctx)
217230
if (!user.platformRoles?.includes('AUDITOR') && !user.platformRoles?.includes('SUPER_ADMIN')) {
218231
throw redirect({ to: '/dashboard' })
219232
}

web/src/pages/login.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Link } from '@tanstack/react-router'
1+
import { Link, useNavigate, useSearch } from '@tanstack/react-router'
22
import { useState } from 'react'
33
import { LoginButton } from '@/features/auth/login-button'
44
import { useLocalLogin } from '@/features/auth/use-local-auth'
@@ -7,15 +7,19 @@ import { Input } from '@/shared/ui/input'
77
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/shared/ui/tabs'
88

99
export function LoginPage() {
10+
const navigate = useNavigate()
11+
const search = useSearch({ from: '/login' })
1012
const loginMutation = useLocalLogin()
1113
const [username, setUsername] = useState('')
1214
const [password, setPassword] = useState('')
1315

16+
const returnTo = search.returnTo && search.returnTo.startsWith('/') ? search.returnTo : '/dashboard'
17+
1418
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
1519
event.preventDefault()
1620
try {
1721
await loginMutation.mutateAsync({ username, password })
18-
window.location.href = '/dashboard'
22+
await navigate({ to: returnTo })
1923
} catch {
2024
// mutation state drives the error UI
2125
}
@@ -73,7 +77,11 @@ export function LoginPage() {
7377
<p className="text-center text-sm text-muted-foreground">
7478
还没有账号?
7579
{' '}
76-
<Link to="/register" className="font-medium text-primary hover:underline">
80+
<Link
81+
to="/register"
82+
search={{ returnTo }}
83+
className="font-medium text-primary hover:underline"
84+
>
7785
立即注册
7886
</Link>
7987
</p>

web/src/pages/register.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Link } from '@tanstack/react-router'
1+
import { Link, useNavigate, useSearch } from '@tanstack/react-router'
22
import { useState } from 'react'
33
import { LoginButton } from '@/features/auth/login-button'
44
import { useLocalRegister } from '@/features/auth/use-local-auth'
@@ -8,16 +8,20 @@ import { Input } from '@/shared/ui/input'
88
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/shared/ui/tabs'
99

1010
export function RegisterPage() {
11+
const navigate = useNavigate()
12+
const search = useSearch({ from: '/register' })
1113
const registerMutation = useLocalRegister()
1214
const [username, setUsername] = useState('')
1315
const [email, setEmail] = useState('')
1416
const [password, setPassword] = useState('')
1517

18+
const returnTo = search.returnTo && search.returnTo.startsWith('/') ? search.returnTo : '/dashboard'
19+
1620
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
1721
event.preventDefault()
1822
try {
1923
await registerMutation.mutateAsync({ username, email, password })
20-
window.location.href = '/dashboard'
24+
await navigate({ to: returnTo })
2125
} catch {
2226
// mutation state drives the error UI
2327
}
@@ -80,7 +84,11 @@ export function RegisterPage() {
8084
<p className="text-center text-sm text-muted-foreground">
8185
已有账号?
8286
{' '}
83-
<Link to="/login" className="font-medium text-primary hover:underline">
87+
<Link
88+
to="/login"
89+
search={{ returnTo }}
90+
className="font-medium text-primary hover:underline"
91+
>
8492
返回登录
8593
</Link>
8694
</p>

web/src/pages/skill-detail.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useParams, useNavigate } from '@tanstack/react-router'
1+
import { useParams, useNavigate, useRouterState } from '@tanstack/react-router'
22
import { useMutation, useQueryClient } from '@tanstack/react-query'
33
import { MarkdownRenderer } from '@/features/skill/markdown-renderer'
44
import { FileTree } from '@/features/skill/file-tree'
@@ -20,6 +20,7 @@ import {
2020

2121
export function SkillDetailPage() {
2222
const navigate = useNavigate()
23+
const location = useRouterState({ select: (s) => s.location })
2324
const queryClient = useQueryClient()
2425
const { namespace, slug } = useParams({ from: '/@$namespace/$slug' })
2526
const { user, hasRole } = useAuth()
@@ -53,7 +54,12 @@ export function SkillDetailPage() {
5354
})
5455

5556
const requireLogin = () => {
56-
navigate({ to: '/login' })
57+
navigate({
58+
to: '/login',
59+
search: {
60+
returnTo: `${location.pathname}${location.searchStr}${location.hash}`,
61+
},
62+
})
5763
}
5864

5965
if (isLoadingSkill) {

0 commit comments

Comments
 (0)