@allbluecn/admin-app 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/src/components/invitations/invitations-page.tsx +527 -0
- package/src/components/layout/sidebar-02/app-sidebar.tsx +4 -0
- package/src/components/oauth-providers/provider-page.tsx +324 -0
- package/src/routeTree.gen.ts +46 -0
- package/src/routes/_nav/invitations/route.lazy.tsx +28 -0
- package/src/routes/_nav/invitations/route.tsx +20 -0
- package/src/routes/_nav/login-providers/route.lazy.tsx +28 -0
- package/src/routes/_nav/login-providers/route.tsx +20 -0
- package/src/server/__tests__/invitations.test.ts +311 -0
- package/src/server/serverFns/invitations.ts +165 -0
- package/src/server/serverFns/oauth-providers.ts +161 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { useEffect, useState } from 'react'
|
|
19
|
+
import type React from 'react'
|
|
20
|
+
import { m } from '@/paraglide/messages.js'
|
|
21
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
|
22
|
+
import { toast } from 'sonner'
|
|
23
|
+
import { Eye, EyeOff, KeyRound, PlugZap, Save, Trash2 } from 'lucide-react'
|
|
24
|
+
import { PageHeader } from '@/components/common/page-header'
|
|
25
|
+
import { Button } from '@/components/ui/button'
|
|
26
|
+
import { Badge } from '@/components/ui/badge'
|
|
27
|
+
import { Card, CardContent } from '@/components/ui/card'
|
|
28
|
+
import { Input } from '@/components/ui/input'
|
|
29
|
+
import { Label } from '@/components/ui/label'
|
|
30
|
+
import { Switch } from '@/components/ui/switch'
|
|
31
|
+
import { Skeleton } from '@/components/ui/skeleton'
|
|
32
|
+
import {
|
|
33
|
+
AlertDialog,
|
|
34
|
+
AlertDialogAction,
|
|
35
|
+
AlertDialogCancel,
|
|
36
|
+
AlertDialogContent,
|
|
37
|
+
AlertDialogDescription,
|
|
38
|
+
AlertDialogFooter,
|
|
39
|
+
AlertDialogHeader,
|
|
40
|
+
AlertDialogTitle,
|
|
41
|
+
} from '@/components/ui/alert-dialog'
|
|
42
|
+
import {
|
|
43
|
+
deleteOAuthProviderFn,
|
|
44
|
+
listOAuthProvidersFn,
|
|
45
|
+
saveOAuthProviderFn,
|
|
46
|
+
testOAuthProviderFn,
|
|
47
|
+
} from '@/server/serverFns/oauth-providers'
|
|
48
|
+
|
|
49
|
+
function GoogleIcon({ className }: { className?: string }) {
|
|
50
|
+
return (
|
|
51
|
+
<svg viewBox="0 0 24 24" className={className} aria-hidden="true">
|
|
52
|
+
<path
|
|
53
|
+
fill="#EA4335"
|
|
54
|
+
d="M12 10.2v3.6h5.1c-.3 2-2 3.4-5.1 3.4-3.1 0-5.6-2.4-5.6-5.6s2.5-5.6 5.6-5.6c1.6 0 2.9.5 4 1.5l2.4-2.4C16.7 3.2 14.5 2.3 12 2.3 6.8 2.3 2.6 6.5 2.6 11.6S6.8 21 12 21c5.8 0 9.6-4 9.6-9.7 0-.6 0-1.1-.1-1.6H12z"
|
|
55
|
+
/>
|
|
56
|
+
</svg>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function OAuthProviderPage(): React.ReactElement {
|
|
61
|
+
const queryClient = useQueryClient()
|
|
62
|
+
const [clientId, setClientId] = useState('')
|
|
63
|
+
const [clientSecret, setClientSecret] = useState('')
|
|
64
|
+
const [enabled, setEnabled] = useState(false)
|
|
65
|
+
const [showSecret, setShowSecret] = useState(false)
|
|
66
|
+
const [pendingDelete, setPendingDelete] = useState(false)
|
|
67
|
+
|
|
68
|
+
const providersQuery = useQuery({
|
|
69
|
+
queryKey: ['oauth-providers', 'list'],
|
|
70
|
+
queryFn: () => listOAuthProvidersFn({ data: {} }),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const googleRow = providersQuery.data?.providers?.find((p) => p.provider === 'google')
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!googleRow) return
|
|
77
|
+
if (googleRow.fromDb) {
|
|
78
|
+
setClientId(googleRow.clientId)
|
|
79
|
+
setEnabled(googleRow.enabled)
|
|
80
|
+
setClientSecret('')
|
|
81
|
+
} else if (clientId === '') {
|
|
82
|
+
setEnabled(false)
|
|
83
|
+
}
|
|
84
|
+
}, [googleRow])
|
|
85
|
+
|
|
86
|
+
const invalidateList = () =>
|
|
87
|
+
queryClient.invalidateQueries({ queryKey: ['oauth-providers', 'list'] })
|
|
88
|
+
|
|
89
|
+
const saveMutation = useMutation({
|
|
90
|
+
mutationFn: () =>
|
|
91
|
+
saveOAuthProviderFn({
|
|
92
|
+
data: {
|
|
93
|
+
provider: 'google',
|
|
94
|
+
clientId: clientId.trim(),
|
|
95
|
+
clientSecret: clientSecret.trim() || undefined,
|
|
96
|
+
enabled,
|
|
97
|
+
},
|
|
98
|
+
}),
|
|
99
|
+
onSuccess: () => {
|
|
100
|
+
toast.success(m["admin-loginProviders_toast_saved"]())
|
|
101
|
+
setClientSecret('')
|
|
102
|
+
invalidateList()
|
|
103
|
+
},
|
|
104
|
+
onError: (err) =>
|
|
105
|
+
toast.error(
|
|
106
|
+
err instanceof Error ? err.message : m["admin-loginProviders_toast_saveFailed"](),
|
|
107
|
+
),
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const testMutation = useMutation({
|
|
111
|
+
mutationFn: () =>
|
|
112
|
+
testOAuthProviderFn({
|
|
113
|
+
data: {
|
|
114
|
+
provider: 'google',
|
|
115
|
+
clientId: clientId.trim(),
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
onSuccess: (res) => {
|
|
119
|
+
if (res.ok) {
|
|
120
|
+
toast.success(res.detail || m["admin-loginProviders_toast_testOk"]())
|
|
121
|
+
} else {
|
|
122
|
+
toast.error(res.detail || m["admin-loginProviders_toast_testFailed"]())
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
onError: (err) =>
|
|
126
|
+
toast.error(
|
|
127
|
+
err instanceof Error ? err.message : m["admin-loginProviders_toast_testFailed"](),
|
|
128
|
+
),
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const deleteMutation = useMutation({
|
|
132
|
+
mutationFn: () => deleteOAuthProviderFn({ data: { provider: 'google' } }),
|
|
133
|
+
onSuccess: () => {
|
|
134
|
+
toast.success(m["admin-loginProviders_toast_deleted"]())
|
|
135
|
+
setClientId('')
|
|
136
|
+
setClientSecret('')
|
|
137
|
+
setEnabled(false)
|
|
138
|
+
setPendingDelete(false)
|
|
139
|
+
invalidateList()
|
|
140
|
+
},
|
|
141
|
+
onError: (err) => {
|
|
142
|
+
toast.error(
|
|
143
|
+
err instanceof Error ? err.message : m["admin-loginProviders_toast_deleteFailed"](),
|
|
144
|
+
)
|
|
145
|
+
setPendingDelete(false)
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const loading = providersQuery.isLoading
|
|
150
|
+
const fromDb = googleRow?.fromDb ?? false
|
|
151
|
+
const hasEnvFallback = googleRow?.hasEnvFallback ?? false
|
|
152
|
+
const canTest = clientId.trim().length > 0
|
|
153
|
+
const canSave = clientId.trim().length > 0
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<div className="space-y-6 p-6">
|
|
157
|
+
<PageHeader
|
|
158
|
+
title={m["admin-loginProviders_page_title"]()}
|
|
159
|
+
description={m["admin-loginProviders_page_subtitle"]()}
|
|
160
|
+
icon={KeyRound}
|
|
161
|
+
/>
|
|
162
|
+
|
|
163
|
+
<div className="grid gap-6 lg:grid-cols-2">
|
|
164
|
+
<Card>
|
|
165
|
+
<CardContent className="pt-6">
|
|
166
|
+
{loading ? (
|
|
167
|
+
<div className="space-y-3">
|
|
168
|
+
<Skeleton className="h-8 w-full" />
|
|
169
|
+
<Skeleton className="h-8 w-full" />
|
|
170
|
+
<Skeleton className="h-8 w-full" />
|
|
171
|
+
</div>
|
|
172
|
+
) : (
|
|
173
|
+
<div className="space-y-5">
|
|
174
|
+
<div className="flex items-center gap-3">
|
|
175
|
+
<div className="flex size-10 shrink-0 items-center justify-center rounded-lg border bg-background">
|
|
176
|
+
<GoogleIcon className="size-5" />
|
|
177
|
+
</div>
|
|
178
|
+
<div className="flex-1">
|
|
179
|
+
<div className="text-sm font-medium">Google</div>
|
|
180
|
+
<div className="text-xs text-muted-foreground">
|
|
181
|
+
{fromDb
|
|
182
|
+
? m["admin-loginProviders_source_db"]()
|
|
183
|
+
: hasEnvFallback
|
|
184
|
+
? m["admin-loginProviders_source_env"]()
|
|
185
|
+
: m["admin-loginProviders_source_none"]()}
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
<Badge variant={fromDb ? 'default' : 'secondary'}>
|
|
189
|
+
{fromDb
|
|
190
|
+
? m["admin-loginProviders_source_db"]()
|
|
191
|
+
: hasEnvFallback
|
|
192
|
+
? m["admin-loginProviders_source_env"]()
|
|
193
|
+
: m["admin-loginProviders_source_none"]()}
|
|
194
|
+
</Badge>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
<div className="space-y-2">
|
|
198
|
+
<Label htmlFor="oauth-client-id">
|
|
199
|
+
{m["admin-loginProviders_field_clientId"]()}
|
|
200
|
+
</Label>
|
|
201
|
+
<Input
|
|
202
|
+
id="oauth-client-id"
|
|
203
|
+
value={clientId}
|
|
204
|
+
onChange={(e) => setClientId(e.target.value)}
|
|
205
|
+
placeholder={m["admin-loginProviders_field_clientId_placeholder"]()}
|
|
206
|
+
autoComplete="off"
|
|
207
|
+
spellCheck={false}
|
|
208
|
+
/>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div className="space-y-2">
|
|
212
|
+
<Label htmlFor="oauth-client-secret">
|
|
213
|
+
{m["admin-loginProviders_field_clientSecret"]()}
|
|
214
|
+
</Label>
|
|
215
|
+
<div className="relative">
|
|
216
|
+
<Input
|
|
217
|
+
id="oauth-client-secret"
|
|
218
|
+
type={showSecret ? 'text' : 'password'}
|
|
219
|
+
value={clientSecret}
|
|
220
|
+
onChange={(e) => setClientSecret(e.target.value)}
|
|
221
|
+
placeholder={
|
|
222
|
+
googleRow?.hasSecret
|
|
223
|
+
? m["admin-loginProviders_field_clientSecret_hint"]()
|
|
224
|
+
: m["admin-loginProviders_field_clientSecret_placeholder"]()
|
|
225
|
+
}
|
|
226
|
+
autoComplete="new-password"
|
|
227
|
+
spellCheck={false}
|
|
228
|
+
className="pr-10"
|
|
229
|
+
/>
|
|
230
|
+
<Button
|
|
231
|
+
type="button"
|
|
232
|
+
variant="ghost"
|
|
233
|
+
size="icon"
|
|
234
|
+
className="absolute right-0 top-0 h-full w-10 px-0"
|
|
235
|
+
onClick={() => setShowSecret((v) => !v)}
|
|
236
|
+
aria-label={
|
|
237
|
+
showSecret
|
|
238
|
+
? m["admin-loginProviders_action_hideSecret"]()
|
|
239
|
+
: m["admin-loginProviders_action_showSecret"]()
|
|
240
|
+
}
|
|
241
|
+
>
|
|
242
|
+
{showSecret ? (
|
|
243
|
+
<EyeOff className="size-4" />
|
|
244
|
+
) : (
|
|
245
|
+
<Eye className="size-4" />
|
|
246
|
+
)}
|
|
247
|
+
</Button>
|
|
248
|
+
</div>
|
|
249
|
+
<p className="text-xs text-muted-foreground">
|
|
250
|
+
{m["admin-loginProviders_field_clientSecret_desc"]()}
|
|
251
|
+
</p>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<div className="flex items-center justify-between rounded-md border px-3 py-2">
|
|
255
|
+
<div className="text-sm font-medium">
|
|
256
|
+
{m["admin-loginProviders_field_enabled"]()}
|
|
257
|
+
</div>
|
|
258
|
+
<Switch
|
|
259
|
+
checked={enabled}
|
|
260
|
+
onCheckedChange={setEnabled}
|
|
261
|
+
aria-label={m["admin-loginProviders_field_enabled"]()}
|
|
262
|
+
/>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
266
|
+
<Button
|
|
267
|
+
onClick={() => saveMutation.mutate()}
|
|
268
|
+
disabled={saveMutation.isPending || !canSave}
|
|
269
|
+
>
|
|
270
|
+
<Save className="size-4" />
|
|
271
|
+
{m["admin-loginProviders_action_save"]()}
|
|
272
|
+
</Button>
|
|
273
|
+
<Button
|
|
274
|
+
variant="outline"
|
|
275
|
+
onClick={() => testMutation.mutate()}
|
|
276
|
+
disabled={testMutation.isPending || !canTest}
|
|
277
|
+
>
|
|
278
|
+
<PlugZap className="size-4" />
|
|
279
|
+
{m["admin-loginProviders_action_test"]()}
|
|
280
|
+
</Button>
|
|
281
|
+
{fromDb && (
|
|
282
|
+
<Button
|
|
283
|
+
variant="outline"
|
|
284
|
+
className="text-destructive hover:bg-destructive/10 hover:text-destructive"
|
|
285
|
+
onClick={() => setPendingDelete(true)}
|
|
286
|
+
>
|
|
287
|
+
<Trash2 className="size-4" />
|
|
288
|
+
{m["admin-loginProviders_action_delete"]()}
|
|
289
|
+
</Button>
|
|
290
|
+
)}
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<p className="text-xs text-muted-foreground">
|
|
294
|
+
{m["admin-loginProviders_cache_hint"]()}
|
|
295
|
+
</p>
|
|
296
|
+
</div>
|
|
297
|
+
)}
|
|
298
|
+
</CardContent>
|
|
299
|
+
</Card>
|
|
300
|
+
</div>
|
|
301
|
+
|
|
302
|
+
<AlertDialog open={pendingDelete} onOpenChange={(o: boolean) => !o && setPendingDelete(false)}>
|
|
303
|
+
<AlertDialogContent>
|
|
304
|
+
<AlertDialogHeader>
|
|
305
|
+
<AlertDialogTitle>{m["admin-loginProviders_delete_confirm_title"]()}</AlertDialogTitle>
|
|
306
|
+
<AlertDialogDescription>
|
|
307
|
+
{m["admin-loginProviders_delete_confirm_desc"]()}
|
|
308
|
+
</AlertDialogDescription>
|
|
309
|
+
</AlertDialogHeader>
|
|
310
|
+
<AlertDialogFooter>
|
|
311
|
+
<AlertDialogCancel>{m.common_common_cancel()}</AlertDialogCancel>
|
|
312
|
+
<AlertDialogAction
|
|
313
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
314
|
+
disabled={deleteMutation.isPending}
|
|
315
|
+
onClick={() => deleteMutation.mutate()}
|
|
316
|
+
>
|
|
317
|
+
{m["admin-loginProviders_action_delete"]()}
|
|
318
|
+
</AlertDialogAction>
|
|
319
|
+
</AlertDialogFooter>
|
|
320
|
+
</AlertDialogContent>
|
|
321
|
+
</AlertDialog>
|
|
322
|
+
</div>
|
|
323
|
+
)
|
|
324
|
+
}
|
package/src/routeTree.gen.ts
CHANGED
|
@@ -26,7 +26,9 @@ import { Route as NavDashboardRouteRouteImport } from './routes/_nav/dashboard/r
|
|
|
26
26
|
import { Route as NavDependenciesRouteRouteImport } from './routes/_nav/dependencies/route'
|
|
27
27
|
import { Route as NavDeveloperLogsRouteRouteImport } from './routes/_nav/developer-logs/route'
|
|
28
28
|
import { Route as NavDocsRouteRouteImport } from './routes/_nav/docs/route'
|
|
29
|
+
import { Route as NavInvitationsRouteRouteImport } from './routes/_nav/invitations/route'
|
|
29
30
|
import { Route as NavLicensesRouteRouteImport } from './routes/_nav/licenses/route'
|
|
31
|
+
import { Route as NavLoginProvidersRouteRouteImport } from './routes/_nav/login-providers/route'
|
|
30
32
|
import { Route as NavMarketRouteRouteImport } from './routes/_nav/market/route'
|
|
31
33
|
import { Route as NavMonitoringRouteRouteImport } from './routes/_nav/monitoring/route'
|
|
32
34
|
import { Route as NavPermissionsRouteRouteImport } from './routes/_nav/permissions/route'
|
|
@@ -166,6 +168,13 @@ const NavDocsRouteRoute = NavDocsRouteRouteImport.update({
|
|
|
166
168
|
} as any).lazy(() =>
|
|
167
169
|
import('./routes/_nav/docs/route.lazy').then((d) => d.Route),
|
|
168
170
|
)
|
|
171
|
+
const NavInvitationsRouteRoute = NavInvitationsRouteRouteImport.update({
|
|
172
|
+
id: '/invitations',
|
|
173
|
+
path: '/invitations',
|
|
174
|
+
getParentRoute: () => NavRouteRoute,
|
|
175
|
+
} as any).lazy(() =>
|
|
176
|
+
import('./routes/_nav/invitations/route.lazy').then((d) => d.Route),
|
|
177
|
+
)
|
|
169
178
|
const NavLicensesRouteRoute = NavLicensesRouteRouteImport.update({
|
|
170
179
|
id: '/licenses',
|
|
171
180
|
path: '/licenses',
|
|
@@ -173,6 +182,13 @@ const NavLicensesRouteRoute = NavLicensesRouteRouteImport.update({
|
|
|
173
182
|
} as any).lazy(() =>
|
|
174
183
|
import('./routes/_nav/licenses/route.lazy').then((d) => d.Route),
|
|
175
184
|
)
|
|
185
|
+
const NavLoginProvidersRouteRoute = NavLoginProvidersRouteRouteImport.update({
|
|
186
|
+
id: '/login-providers',
|
|
187
|
+
path: '/login-providers',
|
|
188
|
+
getParentRoute: () => NavRouteRoute,
|
|
189
|
+
} as any).lazy(() =>
|
|
190
|
+
import('./routes/_nav/login-providers/route.lazy').then((d) => d.Route),
|
|
191
|
+
)
|
|
176
192
|
const NavMarketRouteRoute = NavMarketRouteRouteImport.update({
|
|
177
193
|
id: '/market',
|
|
178
194
|
path: '/market',
|
|
@@ -417,7 +433,9 @@ export interface FileRoutesByFullPath {
|
|
|
417
433
|
'/dependencies': typeof NavDependenciesRouteRoute
|
|
418
434
|
'/developer-logs': typeof NavDeveloperLogsRouteRoute
|
|
419
435
|
'/docs': typeof NavDocsRouteRoute
|
|
436
|
+
'/invitations': typeof NavInvitationsRouteRoute
|
|
420
437
|
'/licenses': typeof NavLicensesRouteRouteWithChildren
|
|
438
|
+
'/login-providers': typeof NavLoginProvidersRouteRoute
|
|
421
439
|
'/market': typeof NavMarketRouteRoute
|
|
422
440
|
'/monitoring': typeof NavMonitoringRouteRouteWithChildren
|
|
423
441
|
'/permissions': typeof NavPermissionsRouteRoute
|
|
@@ -463,7 +481,9 @@ export interface FileRoutesByTo {
|
|
|
463
481
|
'/dependencies': typeof NavDependenciesRouteRoute
|
|
464
482
|
'/developer-logs': typeof NavDeveloperLogsRouteRoute
|
|
465
483
|
'/docs': typeof NavDocsRouteRoute
|
|
484
|
+
'/invitations': typeof NavInvitationsRouteRoute
|
|
466
485
|
'/licenses': typeof NavLicensesRouteRouteWithChildren
|
|
486
|
+
'/login-providers': typeof NavLoginProvidersRouteRoute
|
|
467
487
|
'/market': typeof NavMarketRouteRoute
|
|
468
488
|
'/monitoring': typeof NavMonitoringRouteRouteWithChildren
|
|
469
489
|
'/permissions': typeof NavPermissionsRouteRoute
|
|
@@ -512,7 +532,9 @@ export interface FileRoutesById {
|
|
|
512
532
|
'/_nav/dependencies': typeof NavDependenciesRouteRoute
|
|
513
533
|
'/_nav/developer-logs': typeof NavDeveloperLogsRouteRoute
|
|
514
534
|
'/_nav/docs': typeof NavDocsRouteRoute
|
|
535
|
+
'/_nav/invitations': typeof NavInvitationsRouteRoute
|
|
515
536
|
'/_nav/licenses': typeof NavLicensesRouteRouteWithChildren
|
|
537
|
+
'/_nav/login-providers': typeof NavLoginProvidersRouteRoute
|
|
516
538
|
'/_nav/market': typeof NavMarketRouteRoute
|
|
517
539
|
'/_nav/monitoring': typeof NavMonitoringRouteRouteWithChildren
|
|
518
540
|
'/_nav/permissions': typeof NavPermissionsRouteRoute
|
|
@@ -561,7 +583,9 @@ export interface FileRouteTypes {
|
|
|
561
583
|
| '/dependencies'
|
|
562
584
|
| '/developer-logs'
|
|
563
585
|
| '/docs'
|
|
586
|
+
| '/invitations'
|
|
564
587
|
| '/licenses'
|
|
588
|
+
| '/login-providers'
|
|
565
589
|
| '/market'
|
|
566
590
|
| '/monitoring'
|
|
567
591
|
| '/permissions'
|
|
@@ -607,7 +631,9 @@ export interface FileRouteTypes {
|
|
|
607
631
|
| '/dependencies'
|
|
608
632
|
| '/developer-logs'
|
|
609
633
|
| '/docs'
|
|
634
|
+
| '/invitations'
|
|
610
635
|
| '/licenses'
|
|
636
|
+
| '/login-providers'
|
|
611
637
|
| '/market'
|
|
612
638
|
| '/monitoring'
|
|
613
639
|
| '/permissions'
|
|
@@ -655,7 +681,9 @@ export interface FileRouteTypes {
|
|
|
655
681
|
| '/_nav/dependencies'
|
|
656
682
|
| '/_nav/developer-logs'
|
|
657
683
|
| '/_nav/docs'
|
|
684
|
+
| '/_nav/invitations'
|
|
658
685
|
| '/_nav/licenses'
|
|
686
|
+
| '/_nav/login-providers'
|
|
659
687
|
| '/_nav/market'
|
|
660
688
|
| '/_nav/monitoring'
|
|
661
689
|
| '/_nav/permissions'
|
|
@@ -808,6 +836,13 @@ declare module '@tanstack/react-router' {
|
|
|
808
836
|
preLoaderRoute: typeof NavDocsRouteRouteImport
|
|
809
837
|
parentRoute: typeof NavRouteRoute
|
|
810
838
|
}
|
|
839
|
+
'/_nav/invitations': {
|
|
840
|
+
id: '/_nav/invitations'
|
|
841
|
+
path: '/invitations'
|
|
842
|
+
fullPath: '/invitations'
|
|
843
|
+
preLoaderRoute: typeof NavInvitationsRouteRouteImport
|
|
844
|
+
parentRoute: typeof NavRouteRoute
|
|
845
|
+
}
|
|
811
846
|
'/_nav/licenses': {
|
|
812
847
|
id: '/_nav/licenses'
|
|
813
848
|
path: '/licenses'
|
|
@@ -815,6 +850,13 @@ declare module '@tanstack/react-router' {
|
|
|
815
850
|
preLoaderRoute: typeof NavLicensesRouteRouteImport
|
|
816
851
|
parentRoute: typeof NavRouteRoute
|
|
817
852
|
}
|
|
853
|
+
'/_nav/login-providers': {
|
|
854
|
+
id: '/_nav/login-providers'
|
|
855
|
+
path: '/login-providers'
|
|
856
|
+
fullPath: '/login-providers'
|
|
857
|
+
preLoaderRoute: typeof NavLoginProvidersRouteRouteImport
|
|
858
|
+
parentRoute: typeof NavRouteRoute
|
|
859
|
+
}
|
|
818
860
|
'/_nav/market': {
|
|
819
861
|
id: '/_nav/market'
|
|
820
862
|
path: '/market'
|
|
@@ -1132,7 +1174,9 @@ interface NavRouteRouteChildren {
|
|
|
1132
1174
|
NavDependenciesRouteRoute: typeof NavDependenciesRouteRoute
|
|
1133
1175
|
NavDeveloperLogsRouteRoute: typeof NavDeveloperLogsRouteRoute
|
|
1134
1176
|
NavDocsRouteRoute: typeof NavDocsRouteRoute
|
|
1177
|
+
NavInvitationsRouteRoute: typeof NavInvitationsRouteRoute
|
|
1135
1178
|
NavLicensesRouteRoute: typeof NavLicensesRouteRouteWithChildren
|
|
1179
|
+
NavLoginProvidersRouteRoute: typeof NavLoginProvidersRouteRoute
|
|
1136
1180
|
NavMarketRouteRoute: typeof NavMarketRouteRoute
|
|
1137
1181
|
NavMonitoringRouteRoute: typeof NavMonitoringRouteRouteWithChildren
|
|
1138
1182
|
NavPermissionsRouteRoute: typeof NavPermissionsRouteRoute
|
|
@@ -1155,7 +1199,9 @@ const NavRouteRouteChildren: NavRouteRouteChildren = {
|
|
|
1155
1199
|
NavDependenciesRouteRoute: NavDependenciesRouteRoute,
|
|
1156
1200
|
NavDeveloperLogsRouteRoute: NavDeveloperLogsRouteRoute,
|
|
1157
1201
|
NavDocsRouteRoute: NavDocsRouteRoute,
|
|
1202
|
+
NavInvitationsRouteRoute: NavInvitationsRouteRoute,
|
|
1158
1203
|
NavLicensesRouteRoute: NavLicensesRouteRouteWithChildren,
|
|
1204
|
+
NavLoginProvidersRouteRoute: NavLoginProvidersRouteRoute,
|
|
1159
1205
|
NavMarketRouteRoute: NavMarketRouteRoute,
|
|
1160
1206
|
NavMonitoringRouteRoute: NavMonitoringRouteRouteWithChildren,
|
|
1161
1207
|
NavPermissionsRouteRoute: NavPermissionsRouteRoute,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { createLazyFileRoute } from "@tanstack/react-router"
|
|
19
|
+
import { InvitationsPage } from "@/components/invitations/invitations-page"
|
|
20
|
+
|
|
21
|
+
export const Route = createLazyFileRoute("/_nav/invitations")({
|
|
22
|
+
pendingComponent: () => (
|
|
23
|
+
<div className="flex h-64 items-center justify-center">
|
|
24
|
+
<div className="size-8 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
25
|
+
</div>
|
|
26
|
+
),
|
|
27
|
+
component: InvitationsPage,
|
|
28
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { createFileRoute } from "@tanstack/react-router"
|
|
19
|
+
|
|
20
|
+
export const Route = createFileRoute("/_nav/invitations")({})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { createLazyFileRoute } from '@tanstack/react-router'
|
|
19
|
+
import { OAuthProviderPage } from '@/components/oauth-providers/provider-page'
|
|
20
|
+
|
|
21
|
+
export const Route = createLazyFileRoute('/_nav/login-providers')({
|
|
22
|
+
pendingComponent: () => (
|
|
23
|
+
<div className="flex h-64 items-center justify-center">
|
|
24
|
+
<div className="size-8 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
|
25
|
+
</div>
|
|
26
|
+
),
|
|
27
|
+
component: OAuthProviderPage,
|
|
28
|
+
})
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
//
|
|
5
|
+
// This program is free software: you can redistribute it and/or modify
|
|
6
|
+
// it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
// the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
// (at your option) any later version.
|
|
9
|
+
//
|
|
10
|
+
// This program is distributed in the hope that it will be useful,
|
|
11
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
// GNU Affero General Public License for more details.
|
|
14
|
+
//
|
|
15
|
+
// You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
19
|
+
|
|
20
|
+
export const Route = createFileRoute('/_nav/login-providers')({})
|