This commit is contained in:
@@ -41,6 +41,21 @@ spring.output.ansi.enabled=ALWAYS
|
||||
|
||||
# Öffentliche Basis-URL, z. B. für künftig erzeugte absolute Links.
|
||||
mindflux.public-url=${MINDFLUX_PUBLIC_URL:http://localhost:8282}
|
||||
mindflux.mail.from=${MAIL_USERNAME:noreply@sysnaptik.de}
|
||||
|
||||
# =======================
|
||||
# E-Mail-Versand
|
||||
# =======================
|
||||
spring.mail.host=${MAIL_HOST:localhost}
|
||||
spring.mail.port=${MAIL_PORT:587}
|
||||
spring.mail.username=${MAIL_USERNAME:}
|
||||
spring.mail.password=${MAIL_PASSWORD:}
|
||||
spring.mail.properties.mail.smtp.auth=true
|
||||
spring.mail.properties.mail.smtp.starttls.enable=true
|
||||
spring.mail.properties.mail.smtp.starttls.required=true
|
||||
spring.mail.properties.mail.smtp.connectiontimeout=5000
|
||||
spring.mail.properties.mail.smtp.timeout=5000
|
||||
spring.mail.properties.mail.smtp.writetimeout=5000
|
||||
|
||||
# =======================
|
||||
# Datei-Uploads (Profilbilder)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
ALTER TABLE users
|
||||
ADD COLUMN email_verified BOOLEAN NOT NULL DEFAULT TRUE;
|
||||
|
||||
CREATE TABLE account_email_tokens (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
token_hash CHAR(64) NOT NULL UNIQUE,
|
||||
purpose VARCHAR(32) NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
consumed_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT account_email_tokens_purpose_check
|
||||
CHECK (purpose IN ('EMAIL_VERIFICATION', 'PASSWORD_RESET'))
|
||||
);
|
||||
|
||||
CREATE INDEX idx_account_email_tokens_lookup
|
||||
ON account_email_tokens (token_hash, purpose);
|
||||
@@ -28,6 +28,7 @@
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div th:if="${param.passwordPolicy}" class="mf-alert err">Das neue Passwort muss mindestens 8 Zeichen lang sein.</div>
|
||||
<div class="mf-panel">
|
||||
<table class="mf-table">
|
||||
<thead>
|
||||
@@ -48,8 +49,8 @@
|
||||
<form th:action="@{'/admin/users/' + ${user.id} + '/reset-password'}" method="post"
|
||||
style="display:flex; gap:.35rem; align-items:center;"
|
||||
onsubmit="return confirm('Passwort für diesen Nutzer wirklich neu setzen?');">
|
||||
<input class="mf-input" type="text" name="newPassword" placeholder="Neues Passwort"
|
||||
required minlength="4" style="width:150px; padding:.45rem .6rem;">
|
||||
<input class="mf-input" type="password" name="newPassword" placeholder="Neues Passwort"
|
||||
required minlength="8" style="width:150px; padding:.45rem .6rem;">
|
||||
<button type="submit" class="mf-btn">PW zurücksetzen</button>
|
||||
</form>
|
||||
<form th:action="@{'/admin/users/delete/' + ${user.id}}" method="post" style="display:inline;"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Passwort zurücksetzen · Mindflux</title>
|
||||
<link rel="stylesheet" href="/css/theme.css?v=3">
|
||||
<script src="/js/theme.js?v=4"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mf-theme-float"><button class="mf-theme"></button></div>
|
||||
<main class="mf-container" style="max-width:34rem; padding-top:5rem">
|
||||
<section class="mf-panel">
|
||||
<div class="mf-eyebrow">Account-Zugang</div>
|
||||
<h1>Passwort zurücksetzen</h1>
|
||||
<p>Gib deine E-Mail-Adresse ein. Falls ein bestätigter Account dazugehört, senden wir dir einen Link zum Setzen eines neuen Passworts.</p>
|
||||
<div th:if="${param.sent}" class="mf-alert ok">Falls ein passender Account existiert, wurde eine E-Mail versendet.</div>
|
||||
<form th:action="@{/forgot-password}" method="post">
|
||||
<div class="mf-field">
|
||||
<label class="mf-label" for="email">E-Mail-Adresse</label>
|
||||
<input id="email" class="mf-input" type="email" name="email" required autocomplete="email">
|
||||
</div>
|
||||
<button class="mf-btn" type="submit">Reset-Link senden</button>
|
||||
</form>
|
||||
<p style="margin-top:1rem"><a href="/">Zur Anmeldung</a></p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,7 +29,12 @@
|
||||
|
||||
<div th:if="${param.error}" class="mf-alert err">Benutzername oder Passwort sind falsch.</div>
|
||||
<div th:if="${param.logout}" class="mf-alert ok">Du hast dich erfolgreich abgemeldet.</div>
|
||||
<div th:if="${param.registerSuccess}" class="mf-alert ok">Registrierung erfolgreich! Du kannst dich jetzt einloggen.</div>
|
||||
<div th:if="${param.registerSuccess}" class="mf-alert ok">Fast geschafft: Wir haben dir einen Bestätigungslink per E-Mail gesendet.</div>
|
||||
<div th:if="${param.emailVerified}" class="mf-alert ok">Deine E-Mail-Adresse wurde bestätigt. Du kannst dich jetzt anmelden.</div>
|
||||
<div th:if="${param.passwordReset}" class="mf-alert ok">Dein Passwort wurde geändert. Du kannst dich jetzt anmelden.</div>
|
||||
<div th:if="${param.verificationSent}" class="mf-alert ok">Falls ein unbestätigter Account zu dieser Adresse existiert, wurde ein neuer Link versendet.</div>
|
||||
<div th:if="${param.verificationInvalid}" class="mf-alert err">Dieser Bestätigungslink ist ungültig oder abgelaufen.</div>
|
||||
<div th:if="${param.resetInvalid}" class="mf-alert err">Dieser Passwort-Link ist ungültig oder abgelaufen.</div>
|
||||
|
||||
<form th:action="@{/login}" method="post">
|
||||
<div class="mf-field">
|
||||
@@ -43,6 +48,8 @@
|
||||
<button class="neon-btn" type="submit">Login</button>
|
||||
</form>
|
||||
|
||||
<p class="mobile-switch"><a href="/forgot-password">Passwort vergessen?</a></p>
|
||||
|
||||
<p class="mobile-switch">Noch keinen Account? <a data-goto="signup">Jetzt registrieren</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -61,6 +68,8 @@
|
||||
<div th:if="${param.passwordMismatch}" class="mf-alert err">Die Passwörter stimmen nicht überein. Bitte erneut eingeben.</div>
|
||||
<div th:if="${param.passwordPolicy}" class="mf-alert err">Das Passwort muss mindestens 8 Zeichen lang sein.</div>
|
||||
<div th:if="${param.emailInvalid}" class="mf-alert err">Bitte gib eine gültige E-Mail-Adresse an.</div>
|
||||
<div th:if="${param.emailTaken}" class="mf-alert err">Diese E-Mail-Adresse wird bereits verwendet.</div>
|
||||
<div th:if="${param.registerMailError}" class="mf-alert err">Der Account wurde angelegt, aber die Bestätigungs-E-Mail konnte nicht gesendet werden. Fordere unten einen neuen Link an.</div>
|
||||
|
||||
<form th:action="@{/register}" method="post">
|
||||
<div class="mf-field">
|
||||
@@ -83,6 +92,14 @@
|
||||
<button class="neon-btn" type="submit">Account erstellen</button>
|
||||
</form>
|
||||
|
||||
<form th:action="@{/verification/resend}" method="post" style="margin-top:1rem">
|
||||
<label class="mf-label" for="resend-email">Bestätigungslink erneut senden</label>
|
||||
<div style="display:flex; gap:.5rem; flex-wrap:wrap; margin-top:.35rem">
|
||||
<input id="resend-email" class="mf-input" type="email" name="email" placeholder="E-Mail-Adresse" required>
|
||||
<button class="mf-btn outline" type="submit">Link senden</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p class="mobile-switch">Schon dabei? <a data-goto="login">Zum Login</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,7 +155,7 @@
|
||||
|
||||
// Bei Registrierungs-/Passwortfehler direkt das Signup-Panel öffnen
|
||||
var qp = new URLSearchParams(window.location.search);
|
||||
if (qp.has('registerError') || qp.has('passwordMismatch')) {
|
||||
if (qp.has('registerError') || qp.has('passwordMismatch') || qp.has('passwordPolicy') || qp.has('emailInvalid') || qp.has('emailTaken') || qp.has('registerMailError')) {
|
||||
c.classList.add('active');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Neues Passwort · Mindflux</title>
|
||||
<link rel="stylesheet" href="/css/theme.css?v=3">
|
||||
<script src="/js/theme.js?v=4"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="mf-theme-float"><button class="mf-theme"></button></div>
|
||||
<main class="mf-container" style="max-width:34rem; padding-top:5rem">
|
||||
<section class="mf-panel">
|
||||
<div class="mf-eyebrow">Account-Zugang</div>
|
||||
<h1>Neues Passwort vergeben</h1>
|
||||
<div th:if="${param.error == 'policy'}" class="mf-alert err">Das Passwort muss mindestens 8 Zeichen lang sein.</div>
|
||||
<div th:if="${param.error == 'mismatch'}" class="mf-alert err">Die Passwörter stimmen nicht überein.</div>
|
||||
<form th:action="@{/reset-password}" method="post">
|
||||
<input type="hidden" name="token" th:value="${token}">
|
||||
<div class="mf-field">
|
||||
<label class="mf-label" for="password">Neues Passwort</label>
|
||||
<input id="password" class="mf-input" type="password" name="password" required minlength="8" autocomplete="new-password">
|
||||
</div>
|
||||
<div class="mf-field">
|
||||
<label class="mf-label" for="confirm-password">Passwort wiederholen</label>
|
||||
<input id="confirm-password" class="mf-input" type="password" name="confirmPassword" required minlength="8" autocomplete="new-password">
|
||||
</div>
|
||||
<button class="mf-btn" type="submit">Passwort speichern</button>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user