@vonall/auth 0.2.1 → 0.3.0

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +22 -0
  3. package/dist/components/ui/avatar/avatar-fallback.svelte +17 -0
  4. package/dist/components/ui/avatar/avatar-fallback.svelte.d.ts +4 -0
  5. package/dist/components/ui/avatar/avatar-image.svelte +17 -0
  6. package/dist/components/ui/avatar/avatar-image.svelte.d.ts +4 -0
  7. package/dist/components/ui/avatar/avatar.svelte +17 -0
  8. package/dist/components/ui/avatar/avatar.svelte.d.ts +4 -0
  9. package/dist/components/ui/avatar/index.d.ts +4 -0
  10. package/dist/components/ui/avatar/index.js +6 -0
  11. package/dist/components/ui/button/button.svelte +82 -0
  12. package/dist/components/ui/button/button.svelte.d.ts +70 -0
  13. package/dist/components/ui/button/index.d.ts +2 -0
  14. package/dist/components/ui/button/index.js +2 -0
  15. package/dist/components/ui/card/card-action.svelte +20 -0
  16. package/dist/components/ui/card/card-action.svelte.d.ts +5 -0
  17. package/dist/components/ui/card/card-content.svelte +15 -0
  18. package/dist/components/ui/card/card-content.svelte.d.ts +5 -0
  19. package/dist/components/ui/card/card-description.svelte +20 -0
  20. package/dist/components/ui/card/card-description.svelte.d.ts +5 -0
  21. package/dist/components/ui/card/card-footer.svelte +20 -0
  22. package/dist/components/ui/card/card-footer.svelte.d.ts +5 -0
  23. package/dist/components/ui/card/card-header.svelte +23 -0
  24. package/dist/components/ui/card/card-header.svelte.d.ts +5 -0
  25. package/dist/components/ui/card/card-title.svelte +20 -0
  26. package/dist/components/ui/card/card-title.svelte.d.ts +5 -0
  27. package/dist/components/ui/card/card.svelte +23 -0
  28. package/dist/components/ui/card/card.svelte.d.ts +5 -0
  29. package/dist/components/ui/card/index.d.ts +8 -0
  30. package/dist/components/ui/card/index.js +10 -0
  31. package/dist/components/ui/input/index.d.ts +2 -0
  32. package/dist/components/ui/input/index.js +4 -0
  33. package/dist/components/ui/input/input.svelte +48 -0
  34. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  35. package/dist/components/ui/label/index.d.ts +2 -0
  36. package/dist/components/ui/label/index.js +4 -0
  37. package/dist/components/ui/label/label.svelte +20 -0
  38. package/dist/components/ui/label/label.svelte.d.ts +4 -0
  39. package/dist/components/ui/tabs/index.d.ts +5 -0
  40. package/dist/components/ui/tabs/index.js +7 -0
  41. package/dist/components/ui/tabs/tabs-content.svelte +17 -0
  42. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  43. package/dist/components/ui/tabs/tabs-list.svelte +20 -0
  44. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  45. package/dist/components/ui/tabs/tabs-trigger.svelte +20 -0
  46. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  47. package/dist/components/ui/tabs/tabs.svelte +19 -0
  48. package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
  49. package/dist/messages.d.ts +196 -0
  50. package/dist/messages.js +404 -0
  51. package/dist/ui/AdminView.svelte +91 -0
  52. package/dist/ui/AdminView.svelte.d.ts +6 -0
  53. package/dist/ui/ChangePasswordCard.svelte +62 -0
  54. package/dist/ui/ChangePasswordCard.svelte.d.ts +3 -0
  55. package/dist/ui/DeletionTab.svelte +76 -0
  56. package/dist/ui/DeletionTab.svelte.d.ts +6 -0
  57. package/dist/ui/EinladungErstellen.svelte +74 -0
  58. package/dist/ui/EinladungErstellen.svelte.d.ts +3 -0
  59. package/dist/ui/EinladungPage.svelte +42 -0
  60. package/dist/ui/EinladungPage.svelte.d.ts +13 -0
  61. package/dist/ui/IconGithub.svelte +23 -0
  62. package/dist/ui/IconGithub.svelte.d.ts +4 -0
  63. package/dist/ui/IconGoogle.svelte +16 -0
  64. package/dist/ui/IconGoogle.svelte.d.ts +4 -0
  65. package/dist/ui/InvitationsTab.svelte +46 -0
  66. package/dist/ui/InvitationsTab.svelte.d.ts +3 -0
  67. package/dist/ui/LinksTab.svelte +91 -0
  68. package/dist/ui/LinksTab.svelte.d.ts +3 -0
  69. package/dist/ui/LoginForm.svelte +220 -0
  70. package/dist/ui/LoginForm.svelte.d.ts +8 -0
  71. package/dist/ui/MobileSettingsMenu.svelte +37 -0
  72. package/dist/ui/MobileSettingsMenu.svelte.d.ts +11 -0
  73. package/dist/ui/ProfileManagementTab.svelte +56 -0
  74. package/dist/ui/ProfileManagementTab.svelte.d.ts +7 -0
  75. package/dist/ui/RegisterForm.svelte +143 -0
  76. package/dist/ui/RegisterForm.svelte.d.ts +6 -0
  77. package/dist/ui/ResetPasswordForm.svelte +91 -0
  78. package/dist/ui/ResetPasswordForm.svelte.d.ts +3 -0
  79. package/dist/ui/SecurityTab.svelte +25 -0
  80. package/dist/ui/SecurityTab.svelte.d.ts +3 -0
  81. package/dist/ui/SessionsTab.svelte +98 -0
  82. package/dist/ui/SessionsTab.svelte.d.ts +3 -0
  83. package/dist/ui/SetPasswordCard.svelte +56 -0
  84. package/dist/ui/SetPasswordCard.svelte.d.ts +6 -0
  85. package/dist/ui/SettingsHeader.svelte +19 -0
  86. package/dist/ui/SettingsHeader.svelte.d.ts +7 -0
  87. package/dist/ui/SettingsPage.svelte +96 -0
  88. package/dist/ui/SettingsPage.svelte.d.ts +9 -0
  89. package/dist/ui/SettingsTabsList.svelte +17 -0
  90. package/dist/ui/SettingsTabsList.svelte.d.ts +7 -0
  91. package/dist/ui/SocialButton.svelte +21 -0
  92. package/dist/ui/SocialButton.svelte.d.ts +7 -0
  93. package/dist/ui/TwoFactorAuthenticationCard.svelte +216 -0
  94. package/dist/ui/TwoFactorAuthenticationCard.svelte.d.ts +3 -0
  95. package/dist/ui/Verify2FAForm.svelte +63 -0
  96. package/dist/ui/Verify2FAForm.svelte.d.ts +11 -0
  97. package/dist/ui/context.d.ts +93 -0
  98. package/dist/ui/context.js +17 -0
  99. package/dist/ui/href.d.ts +2 -0
  100. package/dist/ui/href.js +5 -0
  101. package/dist/ui/index.d.ts +10 -0
  102. package/dist/ui/index.js +11 -0
  103. package/dist/ui/tabs.d.ts +17 -0
  104. package/dist/ui/tabs.js +21 -0
  105. package/dist/utils.d.ts +12 -0
  106. package/dist/utils.js +6 -0
  107. package/package.json +17 -5
package/dist/messages.js CHANGED
@@ -43,6 +43,214 @@ export const en = {
43
43
  einladungHello: "Hello,",
44
44
  einladungBody: (inviter, app) => `${inviter || "Someone"} invites you to ${app}.`,
45
45
  einladungValid: (validUntil) => `The link works once and is valid until ${validUntil}.`,
46
+ // The login page
47
+ welcomeBack: "Welcome back",
48
+ logInTo: (app) => `Log in to ${app}`,
49
+ forgotPassword: "Forgot your password?",
50
+ forgotIntro: "Enter your email address and we'll send you a link to reset it.",
51
+ email: "Email",
52
+ emailPlaceholder: "you@example.org",
53
+ password: "Password",
54
+ sendTheLink: "Send the link",
55
+ backToLogin: "Back to login",
56
+ orWithEmail: "or with email",
57
+ logIn: "Log in",
58
+ noAccountYet: "No account yet?",
59
+ signUp: "Sign up",
60
+ continueWith: (provider) => `Continue with ${provider}`,
61
+ accountNotLinked: "This Google or GitHub account doesn't confirm your email address. Log in with email and password and link it in the settings.",
62
+ noNewAccountInvite: "New accounts need an invitation. Ask someone who already has an account for an invitation link.",
63
+ noNewAccountOpen: "Accounts can't be created at the moment. Please try again later.",
64
+ loginTooOld: "Your login is more than a day old. Please log in again for this action.",
65
+ confirmEmailFirst: "Confirm your email address first: we just sent you a new link.",
66
+ resetLinkNotSent: "The reset link could not be sent.",
67
+ resetLinkSent: "If an account with this email address exists, a link to reset the password has been sent.",
68
+ resetLinkNotRequested: "The reset link could not be requested.",
69
+ invalidEmailDot: "Please enter a valid email address.",
70
+ // Two-factor on login
71
+ twoFactorTitle: "Two-factor authentication",
72
+ twoFactorEnterCode: "Enter the 6-digit code from your authenticator app.",
73
+ confirm: "Confirm",
74
+ cancel: "Cancel",
75
+ invalidCode: "Please enter a valid 6-digit code.",
76
+ wrongCode: "Wrong or expired code.",
77
+ codeNotVerified: "The code could not be verified.",
78
+ // Signup
79
+ createAccount: "Create an account",
80
+ invitesYouTo: (inviter, app) => `${inviter} invites you to ${app}.`,
81
+ signUpWith: (providers) => `Sign up with ${providers} or with email and password`,
82
+ or: "or",
83
+ almostDone: (email, app) => `Almost done: if ${email} can be used for ${app}, a link to confirm it is on its way. Open it to start using ${app}.`,
84
+ accountReady: "Your account is ready.",
85
+ inviteOnly: (app) => `${app} is invite only. Ask someone who already has an account for an invitation link and open it on this device.`,
86
+ alreadyHaveAccount: "Already have an account?",
87
+ repeatPassword: "Repeat the password",
88
+ passwordsDontMatchDot: "The passwords don't match.",
89
+ needsValidEinladung: "Signing up needs a valid invitation.",
90
+ emailInUse: "This email address is already in use.",
91
+ signupFailed: "Signing up failed.",
92
+ // Reset
93
+ resetTitle: "Reset your password",
94
+ resetIntro: "Enter your new password",
95
+ resetNoToken: "No valid token found. Please request a new link to reset your password.",
96
+ resetLinkInvalid: "The reset link is invalid or incomplete.",
97
+ passwordTooShortDot: (min) => `The password needs at least ${min} characters.`,
98
+ newPassword: "New password",
99
+ confirmNewPassword: "Confirm the new password",
100
+ savePassword: "Save the password",
101
+ passwordNotReset: "The password could not be reset.",
102
+ passwordWasReset: "Password reset. You can log in now.",
103
+ somethingWentWrong: "Something went wrong.",
104
+ // The Einladung page
105
+ invitationTitle: "Invitation",
106
+ invitationTo: (app) => `Invitation to ${app}`,
107
+ invitesYou: (inviter) => `${inviter} invites you.`,
108
+ askForNewLink: "Please ask for a new link.",
109
+ toStartPage: "To the start page",
110
+ alreadyHaveAccountNoInvite: "You already have an account; you don't need this invitation.",
111
+ openApp: (app) => `Open ${app}`,
112
+ iHaveAnAccount: "I already have an account",
113
+ // Settings
114
+ settingsTitle: "Settings",
115
+ backToOverview: "Back to the overview",
116
+ user: "User",
117
+ profile: "Profile",
118
+ memberSince: (since) => `Member since ${since}`,
119
+ tabProfile: "Profile",
120
+ tabProfileHint: "Name and email",
121
+ tabSecurity: "Security",
122
+ tabSecurityHint: "Password and two-factor authentication",
123
+ tabSessions: "Sessions",
124
+ tabSessionsHint: "Devices you are logged in on",
125
+ tabLinks: "Linked accounts",
126
+ tabLinksHint: "GitHub and Google login",
127
+ tabInvitations: "Invitations",
128
+ tabInvitationsHint: "Invite someone",
129
+ tabDeletion: "Delete account",
130
+ tabDeletionHint: "Delete your account for good",
131
+ name: "Name",
132
+ yourName: "Your name",
133
+ save: "Save",
134
+ saving: "Saving…",
135
+ profileUpdated: "Profile updated",
136
+ profileNotUpdated: "The profile could not be updated",
137
+ loadingSecurity: "Loading security settings…",
138
+ changePassword: "Change password",
139
+ changing: "Changing…",
140
+ passwordChanged: "Password changed",
141
+ passwordNotChanged: "The password could not be changed",
142
+ currentPassword: "Current password",
143
+ confirmNewPasswordShort: "Confirm new password",
144
+ setPassword: "Set a password",
145
+ setPasswordIntro: "Your account has no password yet. Set one to log in with your email address and password as well, and to use two-factor authentication.",
146
+ setPasswordButton: "Set password",
147
+ passwordSet: "Password set",
148
+ passwordNotSet: "The password could not be set",
149
+ // Two-factor settings
150
+ twoFactorHeading: "Two-factor authentication (2FA)",
151
+ twoFactorIntro: "Protect your account with a one-time code from an authenticator app.",
152
+ on: "On",
153
+ off: "Off",
154
+ twoFactorProtects: "Two-factor authentication protects your account. To turn it off, enter your password.",
155
+ yourPassword: "Your password",
156
+ turningOff: "Turning off…",
157
+ turnOff: "Turn off",
158
+ turnOff2fa: "Turn off 2FA",
159
+ scanQr: "Scan the QR code",
160
+ qrAlt: "TOTP QR Code",
161
+ scanWithApp: "Scan this code with your authenticator app (e.g. Google Authenticator, Authy, Bitwarden).",
162
+ saveBackupCodes: "1. Save the backup codes",
163
+ backupCodesIntro: "Keep these one-time codes somewhere safe. Each lets you log in once if you lose access to your phone.",
164
+ copyBackupCodes: "Copy the backup codes",
165
+ backupCodesCopied: "Backup codes copied",
166
+ confirmSetup: "2. Confirm the setup",
167
+ enterCodeFromApp: "Enter the 6-digit code from the app",
168
+ checking: "Checking…",
169
+ turnOn2fa: "Turn on 2FA",
170
+ setup2faIntro: "To set up 2FA, confirm it is you by entering your password.",
171
+ starting: "Starting…",
172
+ start2faSetup: "Start 2FA setup",
173
+ enterPasswordOn: "Please enter your password to turn on 2FA.",
174
+ enterPasswordOff: "Please enter your password to turn off 2FA.",
175
+ twoFactorNotOn: "2FA could not be turned on",
176
+ twoFactorNotOff: "2FA could not be turned off",
177
+ twoFactorIsOn: "Two-factor authentication is on.",
178
+ twoFactorIsOff: "2FA is off.",
179
+ setupNotStarted: "The 2FA setup could not be started",
180
+ // Sessions
181
+ activeSessions: "Active sessions",
182
+ devicesLoggedIn: "The devices you are logged in on.",
183
+ endAllOthers: "End all others",
184
+ noActiveSessions: "No active sessions.",
185
+ unknownBrowser: "Unknown browser",
186
+ thisSession: "This session",
187
+ noIpAddress: "No IP address",
188
+ lastActive: (when) => `Last active: ${when}`,
189
+ unknown: "Unknown",
190
+ sessionEnded: "Session ended",
191
+ sessionNotEnded: "The session could not be ended",
192
+ otherSessionsEnded: "Other sessions ended",
193
+ otherSessionsNotEnded: "The other sessions could not be ended",
194
+ // Linked accounts
195
+ linkedAccounts: "Linked accounts",
196
+ linkedIntro: "Log in to your account with GitHub or Google as well.",
197
+ linked: "Linked",
198
+ notLinked: "Not linked",
199
+ unlink: "Unlink",
200
+ link: "Link",
201
+ noSocialLogin: "No GitHub or Google login is set up on this server.",
202
+ autoLinkNote: "If you log in with GitHub or Google and the provider confirms the same email as your account, the two are linked automatically.",
203
+ providerLinked: (provider) => `${provider} linked`,
204
+ providerUnlinked: (provider) => `${provider} unlinked`,
205
+ providerNotLinked: (provider) => `${provider} could not be linked`,
206
+ providerNotUnlinked: (provider) => `${provider} could not be unlinked`,
207
+ // Invitations
208
+ inviteSomeone: "Invite someone",
209
+ inviteIntro: "An invitation link works once and for 14 days. Whoever opens it can create an account.",
210
+ linkWord: "Link",
211
+ validUntil: (date) => `valid until ${date}`,
212
+ withdrawInvitation: "Withdraw invitation",
213
+ invitationWithdrawn: "Invitation withdrawn",
214
+ invitationNotWithdrawn: "The invitation could not be withdrawn.",
215
+ emailOptional: "Email (optional, otherwise just a link)",
216
+ creating: "Creating…",
217
+ createInvitation: "Create invitation",
218
+ copy: "Copy",
219
+ share: "Share",
220
+ linkShownOnce: "The link is shown only now. Send it by message or email.",
221
+ invitationText: (app, link) => `I'm inviting you to ${app}: ${link}`,
222
+ shareYourself: (error) => `${error}. Share the link yourself instead.`,
223
+ invitationSentTo: (email) => `Invitation sent to ${email}`,
224
+ invitationNotCreated: "The invitation could not be created.",
225
+ linkCopied: "Link copied",
226
+ copyLinkFailed: "Copying failed; select the link and copy it by hand.",
227
+ // Deletion
228
+ deleteForGood: "Delete your account for good",
229
+ cannotBeUndone: "This cannot be undone",
230
+ deletionText: "Your account is deleted with everything that belongs to it, your invitations and sessions included.",
231
+ deletionAgents: "Connected AI assistants lose their access.",
232
+ deletionNoLogin: "You won't be able to log in afterwards.",
233
+ typeToContinue: "to continue:",
234
+ type: "Type",
235
+ deleteWord: "DELETE",
236
+ deleting: "Deleting…",
237
+ deletePermanently: "Delete account permanently",
238
+ accountDeleted: "Your account has been deleted.",
239
+ accountNotDeleted: "The account could not be deleted.",
240
+ // Admin
241
+ administration: "Administration",
242
+ accounts: "Accounts",
243
+ since: (date) => `since ${date}`,
244
+ you: "(you)",
245
+ roleOf: (name) => `Role of ${name}`,
246
+ roleChanged: "Role changed",
247
+ deleteAccountOf: (name) => `Delete ${name}'s account`,
248
+ confirmDeleteAccount: (name) => `Delete ${name}'s account with all its data? This cannot be undone.`,
249
+ accountDeletedAdmin: "Account deleted",
250
+ invitations: "Invitations",
251
+ noOpenInvitations: "No open invitations.",
252
+ by: (inviter) => `by ${inviter}`,
253
+ didntWork: "That didn't work.",
46
254
  };
47
255
  export const de = {
48
256
  needsEinladung: "Zum Registrieren braucht es eine Einladung",
@@ -79,6 +287,202 @@ export const de = {
79
287
  einladungHello: "Hallo",
80
288
  einladungBody: (inviter, app) => `${inviter || "Jemand"} lädt dich zu ${app} ein.`,
81
289
  einladungValid: (validUntil) => `Der Link funktioniert einmal und ist bis ${validUntil} gültig.`,
290
+ welcomeBack: "Willkommen zurück",
291
+ logInTo: (app) => `Bei ${app} anmelden`,
292
+ forgotPassword: "Passwort vergessen?",
293
+ forgotIntro: "Gib deine E-Mail-Adresse ein, dann schicken wir dir einen Link zum Zurücksetzen.",
294
+ email: "E-Mail",
295
+ emailPlaceholder: "du@beispiel.ch",
296
+ password: "Passwort",
297
+ sendTheLink: "Link schicken",
298
+ backToLogin: "Zurück zur Anmeldung",
299
+ orWithEmail: "oder mit E-Mail",
300
+ logIn: "Anmelden",
301
+ noAccountYet: "Noch kein Konto?",
302
+ signUp: "Registrieren",
303
+ continueWith: (provider) => `Weiter mit ${provider}`,
304
+ accountNotLinked: "Dieses Google- oder GitHub-Konto bestätigt deine E-Mail-Adresse nicht. Melde dich mit E-Mail und Passwort an und verknüpfe es in den Einstellungen.",
305
+ noNewAccountInvite: "Neue Konten brauchen eine Einladung. Frag jemanden, der schon ein Konto hat, nach einem Einladungslink.",
306
+ noNewAccountOpen: "Im Moment können keine Konten erstellt werden. Bitte versuch es später wieder.",
307
+ loginTooOld: "Deine Anmeldung ist älter als einen Tag. Melde dich für diese Aktion bitte neu an.",
308
+ confirmEmailFirst: "Bestätige zuerst deine E-Mail-Adresse: Wir haben dir gerade einen neuen Link geschickt.",
309
+ resetLinkNotSent: "Der Link zum Zurücksetzen konnte nicht verschickt werden.",
310
+ resetLinkSent: "Wenn es ein Konto mit dieser E-Mail-Adresse gibt, ist ein Link zum Zurücksetzen des Passworts unterwegs.",
311
+ resetLinkNotRequested: "Der Link zum Zurücksetzen konnte nicht angefordert werden.",
312
+ invalidEmailDot: "Bitte gib eine gültige E-Mail-Adresse ein.",
313
+ twoFactorTitle: "Zwei-Faktor-Authentisierung",
314
+ twoFactorEnterCode: "Gib den 6-stelligen Code aus deiner Authenticator-App ein.",
315
+ confirm: "Bestätigen",
316
+ cancel: "Abbrechen",
317
+ invalidCode: "Bitte gib einen gültigen 6-stelligen Code ein.",
318
+ wrongCode: "Falscher oder abgelaufener Code.",
319
+ codeNotVerified: "Der Code konnte nicht geprüft werden.",
320
+ createAccount: "Konto erstellen",
321
+ invitesYouTo: (inviter, app) => `${inviter} lädt dich zu ${app} ein.`,
322
+ signUpWith: (providers) => `Registrieren mit ${providers} oder mit E-Mail und Passwort`,
323
+ or: "oder",
324
+ almostDone: (email, app) => `Fast geschafft: Wenn ${email} für ${app} verwendet werden kann, ist ein Link zur Bestätigung unterwegs. Öffne ihn, um ${app} zu benutzen.`,
325
+ accountReady: "Dein Konto ist bereit.",
326
+ inviteOnly: (app) => `${app} gibt es nur auf Einladung. Frag jemanden, der schon ein Konto hat, nach einem Einladungslink und öffne ihn auf diesem Gerät.`,
327
+ alreadyHaveAccount: "Schon ein Konto?",
328
+ repeatPassword: "Passwort wiederholen",
329
+ passwordsDontMatchDot: "Die Passwörter stimmen nicht überein.",
330
+ needsValidEinladung: "Zum Registrieren braucht es eine gültige Einladung.",
331
+ emailInUse: "Diese E-Mail-Adresse wird schon verwendet.",
332
+ signupFailed: "Die Registrierung hat nicht geklappt.",
333
+ resetTitle: "Passwort zurücksetzen",
334
+ resetIntro: "Gib dein neues Passwort ein",
335
+ resetNoToken: "Kein gültiger Link. Bitte fordere einen neuen Link zum Zurücksetzen an.",
336
+ resetLinkInvalid: "Der Link zum Zurücksetzen ist ungültig oder unvollständig.",
337
+ passwordTooShortDot: (min) => `Das Passwort braucht mindestens ${min} Zeichen.`,
338
+ newPassword: "Neues Passwort",
339
+ confirmNewPassword: "Neues Passwort bestätigen",
340
+ savePassword: "Passwort speichern",
341
+ passwordNotReset: "Das Passwort konnte nicht zurückgesetzt werden.",
342
+ passwordWasReset: "Passwort zurückgesetzt. Du kannst dich jetzt anmelden.",
343
+ somethingWentWrong: "Etwas ist schiefgelaufen.",
344
+ invitationTitle: "Einladung",
345
+ invitationTo: (app) => `Einladung zu ${app}`,
346
+ invitesYou: (inviter) => `${inviter} lädt dich ein.`,
347
+ askForNewLink: "Bitte frag nach einem neuen Link.",
348
+ toStartPage: "Zur Startseite",
349
+ alreadyHaveAccountNoInvite: "Du hast schon ein Konto; diese Einladung brauchst du nicht.",
350
+ openApp: (app) => `${app} öffnen`,
351
+ iHaveAnAccount: "Ich habe schon ein Konto",
352
+ settingsTitle: "Einstellungen",
353
+ backToOverview: "Zurück zur Übersicht",
354
+ user: "Konto",
355
+ profile: "Profil",
356
+ memberSince: (since) => `Dabei seit ${since}`,
357
+ tabProfile: "Profil",
358
+ tabProfileHint: "Name und E-Mail",
359
+ tabSecurity: "Sicherheit",
360
+ tabSecurityHint: "Passwort und Zwei-Faktor-Authentisierung",
361
+ tabSessions: "Sitzungen",
362
+ tabSessionsHint: "Geräte, auf denen du angemeldet bist",
363
+ tabLinks: "Verknüpfte Konten",
364
+ tabLinksHint: "Anmeldung mit GitHub und Google",
365
+ tabInvitations: "Einladungen",
366
+ tabInvitationsHint: "Jemanden einladen",
367
+ tabDeletion: "Konto löschen",
368
+ tabDeletionHint: "Dein Konto endgültig löschen",
369
+ name: "Name",
370
+ yourName: "Dein Name",
371
+ save: "Speichern",
372
+ saving: "Wird gespeichert…",
373
+ profileUpdated: "Profil gespeichert",
374
+ profileNotUpdated: "Das Profil konnte nicht gespeichert werden",
375
+ loadingSecurity: "Sicherheitseinstellungen werden geladen…",
376
+ changePassword: "Passwort ändern",
377
+ changing: "Wird geändert…",
378
+ passwordChanged: "Passwort geändert",
379
+ passwordNotChanged: "Das Passwort konnte nicht geändert werden",
380
+ currentPassword: "Aktuelles Passwort",
381
+ confirmNewPasswordShort: "Neues Passwort bestätigen",
382
+ setPassword: "Passwort festlegen",
383
+ setPasswordIntro: "Dein Konto hat noch kein Passwort. Lege eines fest, um dich auch mit E-Mail und Passwort anzumelden und die Zwei-Faktor-Authentisierung zu nutzen.",
384
+ setPasswordButton: "Passwort festlegen",
385
+ passwordSet: "Passwort festgelegt",
386
+ passwordNotSet: "Das Passwort konnte nicht festgelegt werden",
387
+ twoFactorHeading: "Zwei-Faktor-Authentisierung (2FA)",
388
+ twoFactorIntro: "Schütze dein Konto mit einem Einmalcode aus einer Authenticator-App.",
389
+ on: "Ein",
390
+ off: "Aus",
391
+ twoFactorProtects: "Die Zwei-Faktor-Authentisierung schützt dein Konto. Gib dein Passwort ein, um sie auszuschalten.",
392
+ yourPassword: "Dein Passwort",
393
+ turningOff: "Wird ausgeschaltet…",
394
+ turnOff: "Ausschalten",
395
+ turnOff2fa: "2FA ausschalten",
396
+ scanQr: "QR-Code scannen",
397
+ qrAlt: "QR-Code für die Authenticator-App",
398
+ scanWithApp: "Scanne diesen Code mit deiner Authenticator-App (z. B. Google Authenticator, Authy, Bitwarden).",
399
+ saveBackupCodes: "1. Ersatzcodes sichern",
400
+ backupCodesIntro: "Bewahre diese Einmalcodes sicher auf. Mit jedem kannst du dich einmal anmelden, wenn du keinen Zugriff auf dein Telefon hast.",
401
+ copyBackupCodes: "Ersatzcodes kopieren",
402
+ backupCodesCopied: "Ersatzcodes kopiert",
403
+ confirmSetup: "2. Einrichtung bestätigen",
404
+ enterCodeFromApp: "Gib den 6-stelligen Code aus der App ein",
405
+ checking: "Wird geprüft…",
406
+ turnOn2fa: "2FA einschalten",
407
+ setup2faIntro: "Gib zum Einrichten von 2FA dein Passwort ein, damit wir wissen, dass du es bist.",
408
+ starting: "Wird gestartet…",
409
+ start2faSetup: "2FA einrichten",
410
+ enterPasswordOn: "Bitte gib dein Passwort ein, um 2FA einzuschalten.",
411
+ enterPasswordOff: "Bitte gib dein Passwort ein, um 2FA auszuschalten.",
412
+ twoFactorNotOn: "2FA konnte nicht eingeschaltet werden",
413
+ twoFactorNotOff: "2FA konnte nicht ausgeschaltet werden",
414
+ twoFactorIsOn: "Die Zwei-Faktor-Authentisierung ist eingeschaltet.",
415
+ twoFactorIsOff: "2FA ist ausgeschaltet.",
416
+ setupNotStarted: "Die Einrichtung von 2FA konnte nicht gestartet werden",
417
+ activeSessions: "Aktive Sitzungen",
418
+ devicesLoggedIn: "Die Geräte, auf denen du angemeldet bist.",
419
+ endAllOthers: "Alle anderen beenden",
420
+ noActiveSessions: "Keine aktiven Sitzungen.",
421
+ unknownBrowser: "Unbekannter Browser",
422
+ thisSession: "Diese Sitzung",
423
+ noIpAddress: "Keine IP-Adresse",
424
+ lastActive: (when) => `Zuletzt aktiv: ${when}`,
425
+ unknown: "Unbekannt",
426
+ sessionEnded: "Sitzung beendet",
427
+ sessionNotEnded: "Die Sitzung konnte nicht beendet werden",
428
+ otherSessionsEnded: "Andere Sitzungen beendet",
429
+ otherSessionsNotEnded: "Die anderen Sitzungen konnten nicht beendet werden",
430
+ linkedAccounts: "Verknüpfte Konten",
431
+ linkedIntro: "Melde dich auch mit GitHub oder Google bei deinem Konto an.",
432
+ linked: "Verknüpft",
433
+ notLinked: "Nicht verknüpft",
434
+ unlink: "Trennen",
435
+ link: "Verknüpfen",
436
+ noSocialLogin: "Auf diesem Server ist keine Anmeldung mit GitHub oder Google eingerichtet.",
437
+ autoLinkNote: "Meldest du dich mit GitHub oder Google an und bestätigt der Anbieter dieselbe E-Mail-Adresse wie bei deinem Konto, werden die beiden automatisch verknüpft.",
438
+ providerLinked: (provider) => `${provider} verknüpft`,
439
+ providerUnlinked: (provider) => `${provider} getrennt`,
440
+ providerNotLinked: (provider) => `${provider} konnte nicht verknüpft werden`,
441
+ providerNotUnlinked: (provider) => `${provider} konnte nicht getrennt werden`,
442
+ inviteSomeone: "Jemanden einladen",
443
+ inviteIntro: "Ein Einladungslink funktioniert einmal und 14 Tage lang. Wer ihn öffnet, kann ein Konto erstellen.",
444
+ linkWord: "Link",
445
+ validUntil: (date) => `gültig bis ${date}`,
446
+ withdrawInvitation: "Einladung zurückziehen",
447
+ invitationWithdrawn: "Einladung zurückgezogen",
448
+ invitationNotWithdrawn: "Die Einladung konnte nicht zurückgezogen werden.",
449
+ emailOptional: "E-Mail (freiwillig, sonst nur ein Link)",
450
+ creating: "Wird erstellt…",
451
+ createInvitation: "Einladung erstellen",
452
+ copy: "Kopieren",
453
+ share: "Teilen",
454
+ linkShownOnce: "Der Link wird nur jetzt angezeigt. Schick ihn per Nachricht oder E-Mail.",
455
+ invitationText: (app, link) => `Ich lade dich zu ${app} ein: ${link}`,
456
+ shareYourself: (error) => `${error}. Teile den Link stattdessen selbst.`,
457
+ invitationSentTo: (email) => `Einladung an ${email} geschickt`,
458
+ invitationNotCreated: "Die Einladung konnte nicht erstellt werden.",
459
+ linkCopied: "Link kopiert",
460
+ copyLinkFailed: "Kopieren hat nicht geklappt; markiere den Link und kopiere ihn von Hand.",
461
+ deleteForGood: "Dein Konto endgültig löschen",
462
+ cannotBeUndone: "Das lässt sich nicht rückgängig machen",
463
+ deletionText: "Dein Konto wird mit allem gelöscht, was dazugehört, auch deinen Einladungen und Sitzungen.",
464
+ deletionAgents: "Verbundene KI-Assistenten verlieren ihren Zugriff.",
465
+ deletionNoLogin: "Danach kannst du dich nicht mehr anmelden.",
466
+ typeToContinue: "ein, um fortzufahren:",
467
+ type: "Gib",
468
+ deleteWord: "LÖSCHEN",
469
+ deleting: "Wird gelöscht…",
470
+ deletePermanently: "Konto endgültig löschen",
471
+ accountDeleted: "Dein Konto ist gelöscht.",
472
+ accountNotDeleted: "Das Konto konnte nicht gelöscht werden.",
473
+ administration: "Verwaltung",
474
+ accounts: "Konten",
475
+ since: (date) => `seit ${date}`,
476
+ you: "(du)",
477
+ roleOf: (name) => `Rolle von ${name}`,
478
+ roleChanged: "Rolle geändert",
479
+ deleteAccountOf: (name) => `Konto von ${name} löschen`,
480
+ confirmDeleteAccount: (name) => `Das Konto von ${name} mit allen Daten löschen? Das lässt sich nicht rückgängig machen.`,
481
+ accountDeletedAdmin: "Konto gelöscht",
482
+ invitations: "Einladungen",
483
+ noOpenInvitations: "Keine offenen Einladungen.",
484
+ by: (inviter) => `von ${inviter}`,
485
+ didntWork: "Das hat nicht geklappt.",
82
486
  };
83
487
  let current = en;
84
488
  export function configureAuthMessages({ locale, overrides }) {
@@ -0,0 +1,91 @@
1
+ <script lang="ts">
2
+ import { Trash2, X } from "@lucide/svelte";
3
+ import { toast } from "svelte-sonner";
4
+ import { m } from "../messages.js";
5
+ import { deleteUser, getAllUsers, setUserRole } from "../remote/app_user.remote.js";
6
+ import { adminWithdrawEinladung, getAlleEinladungen } from "../remote/einladung.remote.js";
7
+ import { authUi } from "./context.js";
8
+ import EinladungErstellen from "./EinladungErstellen.svelte";
9
+ import { messageOf } from "../util/messageOf.js";
10
+
11
+ /** The app admin: accounts (role, delete), new Einladungen and every open one. No impersonation, no access to anyone's data. */
12
+ let { currentUserId }: { currentUserId: string } = $props();
13
+ const { locale } = authUi();
14
+
15
+ const users = $derived(await getAllUsers());
16
+ const einladungen = $derived(await getAlleEinladungen());
17
+ const datum = new Intl.DateTimeFormat(locale, { dateStyle: "medium" });
18
+
19
+ async function run(action: () => Promise<unknown>, success: string) {
20
+ try {
21
+ await action();
22
+ toast.success(success);
23
+ } catch (e) {
24
+ toast.error(messageOf(e, m().didntWork));
25
+ }
26
+ }
27
+
28
+ function remove(id: string, name: string) {
29
+ if (!confirm(m().confirmDeleteAccount(name))) return;
30
+ run(() => deleteUser({ userId: id }), m().accountDeletedAdmin);
31
+ }
32
+ </script>
33
+
34
+ <div class="mx-auto grid max-w-3xl gap-6">
35
+ <header>
36
+ <span class="section-kicker">{m().administration}</span>
37
+ <h1 class="text-2xl font-semibold tracking-tight">{m().accounts}</h1>
38
+ </header>
39
+
40
+ <section class="panel p-5">
41
+ <ul class="grid gap-3">
42
+ {#each users as u (u.id)}
43
+ <li class="flex items-center gap-3">
44
+ <div class="grid min-w-0 flex-1">
45
+ <strong class="truncate text-sm font-semibold">{u.name}</strong>
46
+ <small class="text-xs text-muted-foreground">{u.email} · {m().since(datum.format(new Date(u.createdAt)))}{u.twoFactorEnabled ? " · 2FA" : ""}</small>
47
+ </div>
48
+ {#if u.id === currentUserId}
49
+ <span class="text-xs text-muted-foreground">{u.role} {m().you}</span>
50
+ {:else}
51
+ <select
52
+ value={u.role}
53
+ aria-label={m().roleOf(u.name)}
54
+ class="rounded-md border bg-background px-2 py-1.5 text-xs"
55
+ onchange={(e) => run(() => setUserRole({ userId: u.id, role: e.currentTarget.value as "admin" | "user" }), m().roleChanged)}
56
+ >
57
+ <option value="user">user</option>
58
+ <option value="admin">admin</option>
59
+ </select>
60
+ <button type="button" class="text-muted-foreground hover:text-destructive" aria-label={m().deleteAccountOf(u.name)} onclick={() => remove(u.id, u.name)}>
61
+ <Trash2 size={15} />
62
+ </button>
63
+ {/if}
64
+ </li>
65
+ {/each}
66
+ </ul>
67
+ </section>
68
+
69
+ <section class="panel grid gap-3 p-5">
70
+ <span class="section-kicker">{m().invitations}</span>
71
+ <p class="text-sm text-muted-foreground">{m().inviteIntro}</p>
72
+ <EinladungErstellen />
73
+ {#if einladungen.length === 0}
74
+ <p class="text-sm text-muted-foreground">{m().noOpenInvitations}</p>
75
+ {:else}
76
+ <ul class="grid gap-3">
77
+ {#each einladungen as e (e.id)}
78
+ <li class="flex items-center gap-3">
79
+ <div class="grid min-w-0 flex-1">
80
+ <strong class="truncate text-sm font-semibold">{e.email ?? m().linkWord}</strong>
81
+ <small class="text-xs text-muted-foreground">{m().by(e.inviterName)} · {m().validUntil(datum.format(new Date(e.expiresAt)))}</small>
82
+ </div>
83
+ <button type="button" class="text-muted-foreground hover:text-destructive" aria-label={m().withdrawInvitation} onclick={() => run(() => adminWithdrawEinladung({ einladungId: e.id }), m().invitationWithdrawn)}>
84
+ <X size={15} />
85
+ </button>
86
+ </li>
87
+ {/each}
88
+ </ul>
89
+ {/if}
90
+ </section>
91
+ </div>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ currentUserId: string;
3
+ };
4
+ declare const AdminView: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type AdminView = ReturnType<typeof AdminView>;
6
+ export default AdminView;
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ import { KeyRound } from "@lucide/svelte";
3
+ import { toast } from "svelte-sonner";
4
+ import { Button } from "../components/ui/button/index.js";
5
+ import { Input } from "../components/ui/input/index.js";
6
+ import { Label } from "../components/ui/label/index.js";
7
+ import { m } from "../messages.js";
8
+ import { changePassword } from "../remote/app_user.remote.js";
9
+ import { messageOf } from "../util/messageOf.js";
10
+
11
+ let isSubmitting = $state(false);
12
+ </script>
13
+
14
+ <div class="panel p-6 md:p-8">
15
+ <h3 class="mb-6 flex items-center gap-2 text-lg font-semibold">
16
+ <KeyRound class="size-5 text-muted-foreground" />
17
+ {m().changePassword}
18
+ </h3>
19
+ <form
20
+ class="space-y-6"
21
+ {...changePassword.enhance(async ({ submit }) => {
22
+ isSubmitting = true;
23
+ try {
24
+ await submit();
25
+ toast.success(m().passwordChanged);
26
+ changePassword.fields.currentPassword.set("");
27
+ changePassword.fields.newPassword.set("");
28
+ changePassword.fields.confirmPassword.set("");
29
+ } catch (e) {
30
+ toast.error(messageOf(e, m().passwordNotChanged));
31
+ } finally {
32
+ isSubmitting = false;
33
+ }
34
+ })}
35
+ >
36
+ <div class="space-y-2">
37
+ <Label for="currentPassword" class="text-sm font-medium">{m().currentPassword}</Label>
38
+ <Input id="currentPassword" type="password" {...changePassword.fields.currentPassword.as("text")} placeholder="••••••••" class="bg-muted/30" />
39
+ {#each changePassword.fields.currentPassword.issues() as issue (issue.message)}
40
+ <p class="text-sm text-destructive">{issue.message}</p>
41
+ {/each}
42
+ </div>
43
+
44
+ <div class="space-y-2">
45
+ <Label for="newPassword" class="text-sm font-medium">{m().newPassword}</Label>
46
+ <Input id="newPassword" type="password" {...changePassword.fields.newPassword.as("text")} placeholder="••••••••" class="bg-muted/30" />
47
+ {#each changePassword.fields.newPassword.issues() as issue (issue.message)}
48
+ <p class="text-sm text-destructive">{issue.message}</p>
49
+ {/each}
50
+ </div>
51
+
52
+ <div class="space-y-2">
53
+ <Label for="confirmPassword" class="text-sm font-medium">{m().confirmNewPasswordShort}</Label>
54
+ <Input id="confirmPassword" type="password" {...changePassword.fields.confirmPassword.as("text")} placeholder="••••••••" class="bg-muted/30" />
55
+ {#each changePassword.fields.confirmPassword.issues() as issue (issue.message)}
56
+ <p class="text-sm text-destructive">{issue.message}</p>
57
+ {/each}
58
+ </div>
59
+
60
+ <Button type="submit" class="cursor-pointer" disabled={isSubmitting}>{isSubmitting ? m().changing : m().changePassword}</Button>
61
+ </form>
62
+ </div>
@@ -0,0 +1,3 @@
1
+ declare const ChangePasswordCard: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type ChangePasswordCard = ReturnType<typeof ChangePasswordCard>;
3
+ export default ChangePasswordCard;
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ import { AlertTriangle, Trash2 } from "@lucide/svelte";
3
+ import { toast } from "svelte-sonner";
4
+ import { Button } from "../components/ui/button/index.js";
5
+ import { Input } from "../components/ui/input/index.js";
6
+ import { Label } from "../components/ui/label/index.js";
7
+ import * as Tabs from "../components/ui/tabs/index.js";
8
+ import { m } from "../messages.js";
9
+ import { deleteOwnAccount } from "../remote/app_user.remote.js";
10
+ import { authUi } from "./context.js";
11
+ import { messageOf } from "../util/messageOf.js";
12
+
13
+ /** Deleting the own account; `agents` adds the sentence about connected AI assistants (the MCP endpoint). */
14
+ let { agents = false }: { agents?: boolean } = $props();
15
+ const { authClient } = authUi();
16
+
17
+ let confirmationText = $state("");
18
+ let isSubmitting = $state(false);
19
+
20
+ const isConfirmed = $derived(confirmationText.toLowerCase() === m().deleteWord.toLowerCase());
21
+
22
+ async function handleDeleteAccount() {
23
+ if (!isConfirmed) return;
24
+ isSubmitting = true;
25
+ try {
26
+ const res = await deleteOwnAccount();
27
+ if (res.success) {
28
+ toast.success(m().accountDeleted);
29
+ await authClient.signOut().catch(() => undefined);
30
+ window.location.href = "/";
31
+ } else {
32
+ toast.error(m().accountNotDeleted);
33
+ }
34
+ } catch (error) {
35
+ toast.error(messageOf(error, m().accountNotDeleted));
36
+ } finally {
37
+ isSubmitting = false;
38
+ }
39
+ }
40
+ </script>
41
+
42
+ <Tabs.Content value="deletion" class="mt-6 space-y-6">
43
+ <div class="rounded-xl border border-destructive/20 bg-destructive/5 p-8 shadow-sm dark:bg-destructive/10">
44
+ <h3 class="mb-4 flex items-center gap-2 text-lg font-semibold text-destructive">
45
+ <Trash2 class="size-5" />
46
+ {m().deleteForGood}
47
+ </h3>
48
+
49
+ <div class="max-w-2xl space-y-4 text-sm leading-relaxed text-muted-foreground">
50
+ <div class="flex gap-3 rounded-lg border border-destructive/25 bg-destructive/10 p-4 text-destructive dark:bg-destructive/20 dark:text-destructive">
51
+ <AlertTriangle class="mt-0.5 size-5 shrink-0" />
52
+ <div>
53
+ <h4 class="mb-1 font-semibold">{m().cannotBeUndone}</h4>
54
+ <p>
55
+ {m().deletionText}
56
+ {#if agents}{m().deletionAgents}{/if}
57
+ {m().deletionNoLogin}
58
+ </p>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="space-y-2 pt-2">
63
+ <Label for="confirm-text" class="font-medium text-foreground">
64
+ {m().type} <span class="font-bold text-destructive">{m().deleteWord}</span> {m().typeToContinue}
65
+ </Label>
66
+ <Input id="confirm-text" type="text" bind:value={confirmationText} placeholder={m().deleteWord} class="max-w-md border-destructive/30 bg-background focus-visible:ring-destructive" disabled={isSubmitting} />
67
+ </div>
68
+
69
+ <div class="pt-4">
70
+ <Button variant="destructive" disabled={!isConfirmed || isSubmitting} onclick={handleDeleteAccount} class="cursor-pointer">
71
+ {isSubmitting ? m().deleting : m().deletePermanently}
72
+ </Button>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ </Tabs.Content>