adds password reset
This commit is contained in:
@@ -21,6 +21,8 @@
|
||||
"author": "Autor",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "Durchschn. Geschwindigkeit",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Basisinformation",
|
||||
"before": "Vor",
|
||||
"by": "von",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Eintrag",
|
||||
"error-creating-user": "Fehler beim Erstellen des Nutzers",
|
||||
"error-during-login": "Fehler beim Login",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "Fehler beim Exportieren des Trails",
|
||||
"error-printing-map": "Fehler beim Drucken der Karte",
|
||||
"error-reading-file": "Fehler beim Lesen der Datei",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "Kategorien filtern",
|
||||
"filter-difficulty": "Schwierigkeit filtern",
|
||||
"focus-map-on": "Karte fokussieren auf",
|
||||
"forgot-your-password": "",
|
||||
"french": "Französisch",
|
||||
"german": "Deutsch",
|
||||
"get-position-from-exif": "Koordinaten aus EXIF Daten",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Nahe",
|
||||
"new-list": "Neue Liste",
|
||||
"new-password": "Neues Passwort",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Neue Route",
|
||||
"no-account": "Du hast noch kein Konto?",
|
||||
"no-data": "Keine Daten",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "Ausrichtung",
|
||||
"paper-size": "Papierformat",
|
||||
"password": "Passwort",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "Passwort aktualisiert",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Fotos",
|
||||
"pick-a-trail": "Route auswählen",
|
||||
"polish": "Polnisch",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Registrieren",
|
||||
"removed-trail-from": "Route entfernt aus",
|
||||
"required": "Pflichtfeld",
|
||||
"reset-password": "",
|
||||
"save": "Speichern",
|
||||
"save-list": "Liste speichern",
|
||||
"save-trail": "Route speichern",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "Author",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "Avg. Speed",
|
||||
"back": "Back",
|
||||
"back-to-login": "Back to Login",
|
||||
"basic-info": "Basic Info",
|
||||
"before": "Before",
|
||||
"by": "by",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Entry",
|
||||
"error-creating-user": "Error creating user",
|
||||
"error-during-login": "Error during login",
|
||||
"error-during-password-reset": "Unable to send password reset email",
|
||||
"error-exporting-trail": "Error exporting trail",
|
||||
"error-printing-map": "Error printing map",
|
||||
"error-reading-file": "Error reading file",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "Filter categories",
|
||||
"filter-difficulty": "Filter difficulty",
|
||||
"focus-map-on": "Focus map on",
|
||||
"forgot-your-password": "Forgot your password?",
|
||||
"french": "French",
|
||||
"german": "German",
|
||||
"get-position-from-exif": "Get coordinates from EXIF data",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Near",
|
||||
"new-list": "New List",
|
||||
"new-password": "New password",
|
||||
"new-password-error": "Error setting new password",
|
||||
"new-password-success": "The new password has been set",
|
||||
"new-password-text": "Set a new password",
|
||||
"new-trail": "New Trail",
|
||||
"no-account": "Don't have an account?",
|
||||
"no-data": "No data",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "Orientation",
|
||||
"paper-size": "Paper size",
|
||||
"password": "Password",
|
||||
"password-confirm": "Confirm password",
|
||||
"password-reset-sent": "A password reset email has been sent",
|
||||
"password-reset-text": "We will send a reset link to your email address.",
|
||||
"password-updated": "Password updated",
|
||||
"passwords-must-match": "Passwords must match",
|
||||
"photos": "Photos",
|
||||
"pick-a-trail": "Pick a trail",
|
||||
"polish": "Polish",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Register",
|
||||
"removed-trail-from": "Removed trail from",
|
||||
"required": "Required",
|
||||
"reset-password": "Reset Password",
|
||||
"save": "Save",
|
||||
"save-list": "Save List",
|
||||
"save-trail": "Save Trail",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Informations de base",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Entrée",
|
||||
"error-creating-user": "Erreur durant la création de l'utilisateur",
|
||||
"error-during-login": "Erreur durant la connexion",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "Erreur d'impression de la carte",
|
||||
"error-reading-file": "Erreur de lecture du fichier",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Français",
|
||||
"german": "Allemand",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Proche",
|
||||
"new-list": "Nouvelle liste",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Nouvel itinéraire",
|
||||
"no-account": "Pas encore de compte ?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "Orientation",
|
||||
"paper-size": "Taille de papier",
|
||||
"password": "Mot de passe",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Photos",
|
||||
"pick-a-trail": "Choisir un itinéraire",
|
||||
"polish": "polonais",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "S'enregistrer",
|
||||
"removed-trail-from": "Enlever l'itinéraire de",
|
||||
"required": "Requis",
|
||||
"reset-password": "",
|
||||
"save": "Sauvegarder",
|
||||
"save-list": "",
|
||||
"save-trail": "Sauvegarder l'itinéraire",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Alap információk",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Bejegyzés",
|
||||
"error-creating-user": "Hiba felhasználó hozzáadása közben",
|
||||
"error-during-login": "Hiba bejelentkezés közben",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Hiba a fájl olvasása közben",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Francia",
|
||||
"german": "Német",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Közelben",
|
||||
"new-list": "Új lista",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Új útvonal",
|
||||
"no-account": "Nincs még fiókja?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Jelszó",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Képek",
|
||||
"pick-a-trail": "Válasszon útvonalat",
|
||||
"polish": "Lengyel",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Regisztráció",
|
||||
"removed-trail-from": "Eltávolított nyomvonal a",
|
||||
"required": "Kötelező",
|
||||
"reset-password": "",
|
||||
"save": "Mentés",
|
||||
"save-list": "",
|
||||
"save-trail": "Útvonal mentése",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Informazioni di base",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Voce",
|
||||
"error-creating-user": "Errore durante la creazione dell'utente",
|
||||
"error-during-login": "Errore durante il login",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "Errore durante l'esportazione del percorso",
|
||||
"error-printing-map": "Errore durante la stampa della mappa",
|
||||
"error-reading-file": "Errore durante la lettura del file",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Francese",
|
||||
"german": "Tedesco",
|
||||
"get-position-from-exif": "Ottieni posizione da dati EXIF",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Vicino",
|
||||
"new-list": "Nuova lista",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Nuovo percorso",
|
||||
"no-account": "Non hai ancora un account?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "Orientamento",
|
||||
"paper-size": "Formato carta",
|
||||
"password": "Password",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Foto",
|
||||
"pick-a-trail": "Scegli un percorso",
|
||||
"polish": "Polacco",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Registrati",
|
||||
"removed-trail-from": "Percorso rimosso da",
|
||||
"required": "Obbligatorio",
|
||||
"reset-password": "",
|
||||
"save": "Salva",
|
||||
"save-list": "",
|
||||
"save-trail": "Salva percorso",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Profielfoto",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Algemene informatie",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Item",
|
||||
"error-creating-user": "Het account kan niet worden aangemaakt",
|
||||
"error-during-login": "Het inloggen is mislukt",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "Fout bij afdrukken van kaart",
|
||||
"error-reading-file": "Het bestand kan niet worden ingelezen",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Frans",
|
||||
"german": "Duits",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Nabij",
|
||||
"new-list": "Nieuwe lijst",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Nieuwe wandelroute",
|
||||
"no-account": "Heb je nog geen account?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "Oriëntatie",
|
||||
"paper-size": "Papierformaat",
|
||||
"password": "Wachtwoord",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Foto's",
|
||||
"pick-a-trail": "Kies een wandelroute",
|
||||
"polish": "Pools",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Registreren",
|
||||
"removed-trail-from": "De wandelroute is verwijderd van",
|
||||
"required": "Verplicht",
|
||||
"reset-password": "",
|
||||
"save": "Bewaren",
|
||||
"save-list": "",
|
||||
"save-trail": "Wandelroute bewaren",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Awatar",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Podstawowe informacje",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Pozycja",
|
||||
"error-creating-user": "Błąd tworzenia użytkownika",
|
||||
"error-during-login": "Błąd podczas logowania",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Błąd wczytywania pliku",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Francuski",
|
||||
"german": "Niemiecki",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Blisko",
|
||||
"new-list": "Nowa Lista",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Nowa ścieżka",
|
||||
"no-account": "Nie masz konta?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Hasło",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Zdjęcia",
|
||||
"pick-a-trail": "Wybierz ścieżkę",
|
||||
"polish": "Polski",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Zarejestruj",
|
||||
"removed-trail-from": "Usunięto ścieżkę z",
|
||||
"required": "Wymagane",
|
||||
"reset-password": "",
|
||||
"save": "Zapisz",
|
||||
"save-list": "",
|
||||
"save-trail": "Zapisz ścieżkę",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "Avatar",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "Informações básicas",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "Entrada",
|
||||
"error-creating-user": "Erro ao criar usuário",
|
||||
"error-during-login": "Erro durante o login",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "Erro ao ler o arquivo",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "Francês",
|
||||
"german": "Alemão",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "Próximo",
|
||||
"new-list": "Nova lista",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "Nova trilha",
|
||||
"no-account": "Não tem uma conta?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "Senha",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "Fotos",
|
||||
"pick-a-trail": "Escolha uma trilha",
|
||||
"polish": "Polonês",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "Registo",
|
||||
"removed-trail-from": "Trilha removida de",
|
||||
"required": "Obrigatório",
|
||||
"reset-password": "",
|
||||
"save": "Guardar",
|
||||
"save-list": "",
|
||||
"save-trail": "Guardar trilho",
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
"author": "",
|
||||
"avatar": "头像",
|
||||
"average-speed": "",
|
||||
"back": "",
|
||||
"back-to-login": "",
|
||||
"basic-info": "基本信息",
|
||||
"before": "",
|
||||
"by": "",
|
||||
@@ -80,6 +82,7 @@
|
||||
"entry": "日程",
|
||||
"error-creating-user": "创建用户错误",
|
||||
"error-during-login": "登录错误",
|
||||
"error-during-password-reset": "",
|
||||
"error-exporting-trail": "",
|
||||
"error-printing-map": "",
|
||||
"error-reading-file": "读取文件错误",
|
||||
@@ -97,6 +100,7 @@
|
||||
"filter-categories": "",
|
||||
"filter-difficulty": "",
|
||||
"focus-map-on": "",
|
||||
"forgot-your-password": "",
|
||||
"french": "法语",
|
||||
"german": "德语",
|
||||
"get-position-from-exif": "",
|
||||
@@ -148,6 +152,9 @@
|
||||
"near": "附近",
|
||||
"new-list": "新列表",
|
||||
"new-password": "",
|
||||
"new-password-error": "",
|
||||
"new-password-success": "",
|
||||
"new-password-text": "",
|
||||
"new-trail": "创建新路线",
|
||||
"no-account": "还未注册?",
|
||||
"no-data": "",
|
||||
@@ -160,7 +167,11 @@
|
||||
"orientation": "",
|
||||
"paper-size": "",
|
||||
"password": "密码",
|
||||
"password-confirm": "",
|
||||
"password-reset-sent": "",
|
||||
"password-reset-text": "",
|
||||
"password-updated": "",
|
||||
"passwords-must-match": "",
|
||||
"photos": "图片",
|
||||
"pick-a-trail": "选择一个路线",
|
||||
"polish": "波兰语",
|
||||
@@ -173,6 +184,7 @@
|
||||
"register": "注册",
|
||||
"removed-trail-from": "路线已删除自",
|
||||
"required": "必填",
|
||||
"reset-password": "",
|
||||
"save": "保存",
|
||||
"save-list": "",
|
||||
"save-trail": "保存路线",
|
||||
|
||||
@@ -56,7 +56,7 @@ export async function trails_search_filter(filter: TrailFilter, page: number = 1
|
||||
|
||||
r = await f('/api/v1/trail?' + new URLSearchParams({
|
||||
expand: "category,waypoints,summit_logs,trail_share_via_trail",
|
||||
filter: trailIds.map((id: Record<string, any>) => `id="${id}"`).join('||'),
|
||||
filter: `'${trailIds.join(',')}'~id`,
|
||||
sort: `${filter.sortOrder}${filter.sort}`
|
||||
}), {
|
||||
method: 'GET',
|
||||
@@ -101,7 +101,7 @@ export async function trails_search_bounding_box(northEast: LatLng, southWest: L
|
||||
}
|
||||
|
||||
r = await fetch('/api/v1/trail?' + new URLSearchParams({
|
||||
filter: trailIds.map((id: Record<string, any>) => `id="${id}"`).join("||"),
|
||||
filter: `'${trailIds.join(',')}'~id`,
|
||||
expand: "category,waypoints,summit_logs",
|
||||
sort: `+name`,
|
||||
}), {
|
||||
|
||||
@@ -133,5 +133,30 @@ export async function users_delete(user: User) {
|
||||
if (!r.ok) {
|
||||
throw new ClientResponseError(await r.json())
|
||||
}
|
||||
}
|
||||
|
||||
export async function users_reset_password(reset: { email: string }) {
|
||||
const r = await fetch('/api/v1/auth/reset', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(reset),
|
||||
})
|
||||
|
||||
if (r.ok) {
|
||||
return await r.json();
|
||||
} else {
|
||||
throw new ClientResponseError(await r.json())
|
||||
}
|
||||
}
|
||||
|
||||
export async function users_confirm_reset(reset: { password: string, passwordConfirm: string, token: string }) {
|
||||
const r = await fetch('/api/v1/auth/confirm-reset', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(reset),
|
||||
})
|
||||
|
||||
if (r.ok) {
|
||||
return await r.json();
|
||||
} else {
|
||||
throw new ClientResponseError(await r.json())
|
||||
}
|
||||
}
|
||||
26
web/src/routes/+error.svelte
Normal file
26
web/src/routes/+error.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { page } from "$app/stores";
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="flex flex-col gap-y-4 items-center justify-center relative"
|
||||
style="height: calc(100dvh - 120px); font-size: 4rem"
|
||||
>
|
||||
<img
|
||||
class="absolute top-1/2 left-1/2 aspect-square rounded-xl object-cover"
|
||||
width="500"
|
||||
src="/imgs/error.webp"
|
||||
alt="Default error img"
|
||||
/>
|
||||
<div
|
||||
class="z-10 rounded-full p-8 bg-background flex flex-col gap-y-4 items-center"
|
||||
style="max-width: 400px"
|
||||
>
|
||||
<h1 class="font-bold" style="line-height: 4rem;">
|
||||
{$page.status}
|
||||
</h1>
|
||||
<p class="text-lg text-center">
|
||||
{$page.error?.message}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
20
web/src/routes/api/v1/auth/confirm-reset/+server.ts
Normal file
20
web/src/routes/api/v1/auth/confirm-reset/+server.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { error, json, type RequestEvent } from "@sveltejs/kit";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
export async function POST(event: RequestEvent) {
|
||||
try {
|
||||
let data;
|
||||
try {
|
||||
data = await event.request.json()
|
||||
} catch (e) {
|
||||
throw new ClientResponseError({ status: 400, response: { message: "Invalid json" } })
|
||||
}
|
||||
|
||||
const r = await pb.collection('users').confirmPasswordReset(data.token, data.password, data.passwordConfirm);
|
||||
return json(r);
|
||||
} catch (e: any) {
|
||||
throw error(e.status, e);
|
||||
}
|
||||
|
||||
}
|
||||
20
web/src/routes/api/v1/auth/reset/+server.ts
Normal file
20
web/src/routes/api/v1/auth/reset/+server.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { pb } from "$lib/pocketbase";
|
||||
import { error, json, type RequestEvent } from "@sveltejs/kit";
|
||||
import { ClientResponseError } from "pocketbase";
|
||||
|
||||
export async function POST(event: RequestEvent) {
|
||||
try {
|
||||
let data;
|
||||
try {
|
||||
data = await event.request.json()
|
||||
} catch (e) {
|
||||
throw new ClientResponseError({ status: 400, response: { message: "Invalid json" } })
|
||||
}
|
||||
|
||||
const r = await pb.collection('users').requestPasswordReset(data.email);
|
||||
return json(r);
|
||||
} catch (e: any) {
|
||||
throw error(e.status, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { error, type ServerLoad } from "@sveltejs/kit";
|
||||
|
||||
export const load: ServerLoad = async ({ params, locals, url, fetch }) => {
|
||||
if(!params.token) {
|
||||
error(400, "Invalid token")
|
||||
}
|
||||
};
|
||||
98
web/src/routes/auth/confirm-reset/[token]/+page.svelte
Normal file
98
web/src/routes/auth/confirm-reset/[token]/+page.svelte
Normal file
@@ -0,0 +1,98 @@
|
||||
<script lang="ts">
|
||||
import { goto } from "$app/navigation";
|
||||
import { page } from "$app/stores";
|
||||
import Button from "$lib/components/base/button.svelte";
|
||||
import TextField from "$lib/components/base/text_field.svelte";
|
||||
import LogoTextTwoLineDark from "$lib/components/logo/logo_text_two_line_dark.svelte";
|
||||
import LogoTextTwoLineLight from "$lib/components/logo/logo_text_two_line_light.svelte";
|
||||
import type { User } from "$lib/models/user";
|
||||
import { theme } from "$lib/stores/theme_store";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
import {
|
||||
users_confirm_reset,
|
||||
users_reset_password,
|
||||
} from "$lib/stores/user_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { object, ref, string } from "yup";
|
||||
|
||||
let loading: boolean = false;
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm({
|
||||
initialValues: {
|
||||
password: "",
|
||||
passwordConfirm: "",
|
||||
},
|
||||
validationSchema: object({
|
||||
password: string().required($_("required")),
|
||||
passwordConfirm: string()
|
||||
.required($_("required"))
|
||||
.oneOf([ref("password"), ""], $_('passwords-must-match')),
|
||||
}),
|
||||
onSubmit: async (form) => {
|
||||
loading = true;
|
||||
try {
|
||||
await users_confirm_reset({
|
||||
...form,
|
||||
token: $page.params.token,
|
||||
});
|
||||
goto("/login");
|
||||
show_toast({
|
||||
icon: "check",
|
||||
type: "success",
|
||||
text: $_("new-password-success"),
|
||||
});
|
||||
} catch (e) {
|
||||
show_toast({
|
||||
icon: "close",
|
||||
type: "error",
|
||||
text: $_("new-password-error"),
|
||||
});
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{$_("login")} | wanderer</title>
|
||||
</svelte:head>
|
||||
<main class="flex justify-center">
|
||||
<form
|
||||
class="login-panel max-w-md border border-input-border rounded-xl p-8 flex flex-col justify-center items-center gap-4 w-[28rem] mt-8"
|
||||
on:submit={handleSubmit}
|
||||
>
|
||||
{#if $theme == "light"}
|
||||
<LogoTextTwoLineDark></LogoTextTwoLineDark>
|
||||
{:else}
|
||||
<LogoTextTwoLineLight></LogoTextTwoLineLight>
|
||||
{/if}
|
||||
<h4 class="text-xl font-semibold">{$_("new-password-text")}</h4>
|
||||
<div class="space-y-6 w-80">
|
||||
<TextField
|
||||
name="password"
|
||||
label={$_("password")}
|
||||
type="password"
|
||||
bind:value={$form.password}
|
||||
on:change={handleChange}
|
||||
error={$errors.password}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="password"
|
||||
label={$_("password-confirm")}
|
||||
type="password"
|
||||
bind:value={$form.passwordConfirm}
|
||||
on:change={handleChange}
|
||||
error={$errors.passwordConfirm}
|
||||
></TextField>
|
||||
|
||||
<Button
|
||||
primary={true}
|
||||
extraClasses={"min-w-full"}
|
||||
type="submit"
|
||||
{loading}>{$_("reset-password")}</Button
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
83
web/src/routes/auth/reset/+page.svelte
Normal file
83
web/src/routes/auth/reset/+page.svelte
Normal file
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import { goto } from "$app/navigation";
|
||||
import Button from "$lib/components/base/button.svelte";
|
||||
import TextField from "$lib/components/base/text_field.svelte";
|
||||
import LogoTextTwoLineDark from "$lib/components/logo/logo_text_two_line_dark.svelte";
|
||||
import LogoTextTwoLineLight from "$lib/components/logo/logo_text_two_line_light.svelte";
|
||||
import type { User } from "$lib/models/user";
|
||||
import { theme } from "$lib/stores/theme_store";
|
||||
import { show_toast } from "$lib/stores/toast_store";
|
||||
import { users_reset_password } from "$lib/stores/user_store";
|
||||
import { createForm } from "$lib/vendor/svelte-form-lib";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { object, string } from "yup";
|
||||
|
||||
let loading: boolean = false;
|
||||
|
||||
const { form, errors, handleChange, handleSubmit } = createForm({
|
||||
initialValues: {
|
||||
email: "",
|
||||
},
|
||||
validationSchema: object<User>({
|
||||
email: string().required($_("required")),
|
||||
}),
|
||||
onSubmit: async (email) => {
|
||||
loading = true;
|
||||
try {
|
||||
await users_reset_password(email);
|
||||
goto("/");
|
||||
show_toast({
|
||||
icon: "check",
|
||||
type: "success",
|
||||
text: $_("password-reset-sent"),
|
||||
});
|
||||
} catch (e) {
|
||||
show_toast({
|
||||
icon: "close",
|
||||
type: "error",
|
||||
text: $_("error-during-password-reset"),
|
||||
});
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>{$_("login")} | wanderer</title>
|
||||
</svelte:head>
|
||||
<main class="flex justify-center">
|
||||
<form
|
||||
class="login-panel max-w-md border border-input-border rounded-xl p-8 flex flex-col justify-center items-center gap-4 w-[28rem] mt-8"
|
||||
on:submit={handleSubmit}
|
||||
>
|
||||
{#if $theme == "light"}
|
||||
<LogoTextTwoLineDark></LogoTextTwoLineDark>
|
||||
{:else}
|
||||
<LogoTextTwoLineLight></LogoTextTwoLineLight>
|
||||
{/if}
|
||||
<h4 class="text-xl font-semibold">{$_("forgot-your-password")}</h4>
|
||||
<p class="text-gray-500">{$_('password-reset-text')}</p>
|
||||
<div class="space-y-6 w-80">
|
||||
<TextField
|
||||
name="email"
|
||||
label={$_("email")}
|
||||
bind:value={$form.email}
|
||||
on:change={handleChange}
|
||||
error={$errors.email}
|
||||
></TextField>
|
||||
<div class="grid grid-cols-2 gap-x-4">
|
||||
<a class="btn-secondary text-center" href="/login"
|
||||
>{$_("back-to-login")}</a
|
||||
>
|
||||
<Button
|
||||
primary={true}
|
||||
extraClasses={"min-w-full"}
|
||||
type="submit"
|
||||
{loading}>{$_("reset-password")}</Button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
@@ -97,14 +97,20 @@
|
||||
on:change={handleChange}
|
||||
error={$errors.username}
|
||||
></TextField>
|
||||
<TextField
|
||||
name="password"
|
||||
label={$_("password")}
|
||||
type="password"
|
||||
bind:value={$form.password}
|
||||
on:change={handleChange}
|
||||
error={$errors.password}
|
||||
></TextField>
|
||||
<div class="flex flex-col">
|
||||
<TextField
|
||||
name="password"
|
||||
label={$_("password")}
|
||||
type="password"
|
||||
bind:value={$form.password}
|
||||
on:change={handleChange}
|
||||
error={$errors.password}
|
||||
></TextField>
|
||||
<a
|
||||
class="text-xs underline decoration-dashed float-end mt-1 self-end"
|
||||
href="/auth/reset">{$_("forgot-your-password")}</a
|
||||
>
|
||||
</div>
|
||||
<Button
|
||||
primary={true}
|
||||
extraClasses={"min-w-full"}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 176 KiB |
BIN
web/static/imgs/error.webp
Normal file
BIN
web/static/imgs/error.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Reference in New Issue
Block a user