this repo has no description
1<script lang="ts">
2 import { navigate } from '../lib/router.svelte'
3 import { api, ApiError } from '../lib/api'
4
5 let identifier = $state('')
6 let submitting = $state(false)
7 let error = $state<string | null>(null)
8 let success = $state(false)
9
10 async function handleSubmit(e: Event) {
11 e.preventDefault()
12 submitting = true
13 error = null
14
15 try {
16 await api.requestPasskeyRecovery(identifier)
17 success = true
18 } catch (err) {
19 if (err instanceof ApiError) {
20 error = err.message || 'Failed to send recovery link'
21 } else if (err instanceof Error) {
22 error = err.message || 'Failed to send recovery link'
23 } else {
24 error = 'Failed to send recovery link'
25 }
26 } finally {
27 submitting = false
28 }
29 }
30</script>
31
32<div class="recovery-container">
33 {#if success}
34 <div class="success-content">
35 <h1>Recovery Link Sent</h1>
36 <p class="subtitle">
37 If your account exists and is a passkey-only account, you'll receive a recovery link
38 at your preferred notification channel.
39 </p>
40 <p class="info">
41 The link will expire in 1 hour. Check your email, Discord, Telegram, or Signal
42 depending on your account settings.
43 </p>
44 <button onclick={() => navigate('/login')}>Back to Sign In</button>
45 </div>
46 {:else}
47 <h1>Recover Passkey Account</h1>
48 <p class="subtitle">
49 Lost access to your passkey? Enter your handle or email and we'll send you a recovery link.
50 </p>
51
52 {#if error}
53 <div class="error">{error}</div>
54 {/if}
55
56 <form onsubmit={handleSubmit}>
57 <div class="field">
58 <label for="identifier">Handle or Email</label>
59 <input
60 id="identifier"
61 type="text"
62 bind:value={identifier}
63 placeholder="handle or you@example.com"
64 disabled={submitting}
65 required
66 />
67 </div>
68
69 <div class="info-box">
70 <strong>How it works</strong>
71 <p>
72 We'll send a secure link to your registered notification channel.
73 Click the link to set a temporary password. Then you can sign in
74 and add a new passkey.
75 </p>
76 </div>
77
78 <button type="submit" disabled={submitting || !identifier.trim()}>
79 {submitting ? 'Sending...' : 'Send Recovery Link'}
80 </button>
81 </form>
82 {/if}
83
84 <p class="back-link">
85 <a href="#/login">Back to Sign In</a>
86 </p>
87</div>
88
89<style>
90 .recovery-container {
91 max-width: 400px;
92 margin: 4rem auto;
93 padding: 2rem;
94 }
95
96 h1 {
97 margin: 0 0 0.5rem 0;
98 }
99
100 .subtitle {
101 color: var(--text-secondary);
102 margin: 0 0 2rem 0;
103 }
104
105 form {
106 display: flex;
107 flex-direction: column;
108 gap: 1rem;
109 }
110
111 .field {
112 display: flex;
113 flex-direction: column;
114 gap: 0.25rem;
115 }
116
117 label {
118 font-size: 0.875rem;
119 font-weight: 500;
120 }
121
122 input {
123 padding: 0.75rem;
124 border: 1px solid var(--border-color-light);
125 border-radius: 4px;
126 font-size: 1rem;
127 background: var(--bg-input);
128 color: var(--text-primary);
129 }
130
131 input:focus {
132 outline: none;
133 border-color: var(--accent);
134 }
135
136 .info-box {
137 background: var(--bg-secondary);
138 border: 1px solid var(--border-color);
139 border-radius: 6px;
140 padding: 1rem;
141 font-size: 0.875rem;
142 }
143
144 .info-box strong {
145 display: block;
146 margin-bottom: 0.5rem;
147 }
148
149 .info-box p {
150 margin: 0;
151 color: var(--text-secondary);
152 }
153
154 button {
155 padding: 0.75rem;
156 background: var(--accent);
157 color: white;
158 border: none;
159 border-radius: 4px;
160 font-size: 1rem;
161 cursor: pointer;
162 }
163
164 button:hover:not(:disabled) {
165 background: var(--accent-hover);
166 }
167
168 button:disabled {
169 opacity: 0.6;
170 cursor: not-allowed;
171 }
172
173 .error {
174 padding: 0.75rem;
175 background: var(--error-bg);
176 border: 1px solid var(--error-border);
177 border-radius: 4px;
178 color: var(--error-text);
179 margin-bottom: 1rem;
180 }
181
182 .success-content {
183 text-align: center;
184 }
185
186 .info {
187 color: var(--text-secondary);
188 font-size: 0.875rem;
189 margin-bottom: 1.5rem;
190 }
191
192 .back-link {
193 text-align: center;
194 margin-top: 2rem;
195 }
196
197 .back-link a {
198 color: var(--accent);
199 text-decoration: none;
200 }
201
202 .back-link a:hover {
203 text-decoration: underline;
204 }
205</style>