Our Personal Data Server from scratch!
tranquil.farm
pds
rust
database
fun
oauth
atproto
1<script lang="ts">
2 import { getCurrentPath, navigate } from './lib/router.svelte'
3 import { initAuth, getAuthState } from './lib/auth.svelte'
4 import { initServerConfig } from './lib/serverConfig.svelte'
5 import { initI18n } from './lib/i18n'
6 import { isLoading as i18nLoading } from 'svelte-i18n'
7 import Toast from './components/Toast.svelte'
8 import Login from './routes/Login.svelte'
9 import RegisterSso from './routes/RegisterSso.svelte'
10 import Verify from './routes/Verify.svelte'
11 import ResetPassword from './routes/ResetPassword.svelte'
12 import RecoverPasskey from './routes/RecoverPasskey.svelte'
13 import RequestPasskeyRecovery from './routes/RequestPasskeyRecovery.svelte'
14 import Dashboard from './routes/Dashboard.svelte'
15 import OAuthConsent from './routes/OAuthConsent.svelte'
16 import OAuthLogin from './routes/OAuthLogin.svelte'
17 import OAuthAccounts from './routes/OAuthAccounts.svelte'
18 import OAuthVerifyCode from './routes/OAuthVerifyCode.svelte'
19 import OAuthPasskey from './routes/OAuthPasskey.svelte'
20 import OAuthDelegation from './routes/OAuthDelegation.svelte'
21 import OAuthError from './routes/OAuthError.svelte'
22 import SsoRegisterComplete from './routes/SsoRegisterComplete.svelte'
23 import Register from './routes/Register.svelte'
24
25 import ActAs from './routes/ActAs.svelte'
26 import Migration from './routes/Migration.svelte'
27 import { _ } from './lib/i18n'
28 initI18n()
29
30 const auth = $derived(getAuthState())
31
32 let oauthCallbackPending = $state(hasOAuthCallback())
33
34 function hasOAuthCallback(): boolean {
35 if (window.location.pathname === '/app/migrate') {
36 return false
37 }
38 const params = new URLSearchParams(window.location.search)
39 return !!(params.get('code') && params.get('state'))
40 }
41
42 $effect(() => {
43 initServerConfig()
44 initAuth().then(({ oauthLoginCompleted }) => {
45 if (oauthLoginCompleted) {
46 navigate('/dashboard', { replace: true })
47 }
48 oauthCallbackPending = false
49 })
50 })
51
52 const isLoading = $derived(
53 auth.kind === 'loading' || $i18nLoading || oauthCallbackPending
54 )
55
56 $effect(() => {
57 if (auth.kind === 'loading') return
58 const path = getCurrentPath()
59 if (path === '/') {
60 if (auth.kind === 'authenticated') {
61 navigate('/dashboard', { replace: true })
62 } else {
63 navigate('/login', { replace: true })
64 }
65 }
66 })
67
68 const dashboardRoutes = new Set([
69 '/dashboard',
70 '/settings',
71 '/security',
72 '/sessions',
73 '/app-passwords',
74 '/comms',
75 '/repo',
76 '/controllers',
77
78 '/invite-codes',
79 '/did-document',
80 '/admin',
81 ])
82
83 function getComponent(path: string) {
84 const pathWithoutQuery = path.split('?')[0]
85 if (dashboardRoutes.has(pathWithoutQuery)) {
86 return Dashboard
87 }
88 switch (pathWithoutQuery) {
89 case '/login':
90 return Login
91 case '/verify':
92 return Verify
93 case '/reset-password':
94 return ResetPassword
95 case '/recover-passkey':
96 return RecoverPasskey
97 case '/request-passkey-recovery':
98 return RequestPasskeyRecovery
99 case '/oauth/consent':
100 return OAuthConsent
101 case '/oauth/login':
102 return OAuthLogin
103 case '/oauth/accounts':
104 return OAuthAccounts
105 case '/oauth/2fa':
106 case '/oauth/totp':
107 return OAuthVerifyCode
108 case '/oauth/passkey':
109 return OAuthPasskey
110 case '/oauth/delegation':
111 return OAuthDelegation
112 case '/oauth/error':
113 return OAuthError
114 case '/oauth/sso-register':
115 return SsoRegisterComplete
116 case '/register':
117 case '/oauth/register':
118 case '/oauth/register-password':
119 return Register
120 case '/oauth/register-sso':
121 return RegisterSso
122 case '/act-as':
123 return ActAs
124 case '/migrate':
125 return Migration
126 default:
127 return Login
128 }
129 }
130
131 let currentPath = $derived(getCurrentPath())
132 let CurrentComponent = $derived(getComponent(currentPath))
133
134</script>
135
136<main>
137 {#if isLoading}
138 <div class="loading"></div>
139 {:else}
140 <CurrentComponent />
141 {/if}
142</main>
143<Toast />