From 8547ffe6138498ffccd086ac098fbc33f8739314 Mon Sep 17 00:00:00 2001 From: Ryan Wang Date: Sat, 28 Sep 2024 17:38:32 +0800 Subject: [PATCH] Add frontend support for customizing login and logout pages Signed-off-by: JohnNiang --- .../src/main/resources/static/images/logo.png | Bin 0 -> 11144 bytes .../main/resources/static/images/wordmark.svg | 1 + .../src/main/resources/static/js/main.js | 154 +++++++ .../src/main/resources/static/styles/main.css | 410 ++++++++++++++++++ .../templates/challenges/two-factor/totp.html | 43 ++ .../challenges/two-factor/totp.properties | 4 + .../challenges/two-factor/totp_en.properties | 4 + .../gateway_modules/common_fragments.html | 110 +++++ .../common_fragments.properties | 1 + .../common_fragments_en.properties | 1 + .../gateway_modules/input_fragments.html | 31 ++ .../templates/gateway_modules/layout.html | 23 + .../gateway_modules/login_fragments.html | 95 ++++ .../login_fragments.properties | 13 + .../login_fragments_en.properties | 13 + .../src/main/resources/templates/login.html | 20 + .../main/resources/templates/login.properties | 1 + .../main/resources/templates/login_email.html | 37 ++ .../resources/templates/login_en.properties | 1 + .../main/resources/templates/login_local.html | 56 +++ .../templates/login_local.properties | 3 + .../templates/login_local_en.properties | 3 + .../src/main/resources/templates/logout.html | 18 + .../resources/templates/logout.properties | 3 + .../resources/templates/logout_en.properties | 3 + .../templates/password-reset-link.html | 39 ++ .../templates/password-reset-link.properties | 5 + .../password-reset-link_en.properties | 5 + .../resources/templates/password-reset.html | 36 ++ .../templates/password-reset.properties | 6 + .../templates/password-reset_en.properties | 6 + .../src/main/resources/templates/signup.html | 202 +++++++++ .../resources/templates/signup.properties | 13 + .../resources/templates/signup_en.properties | 9 + ui/console-src/layouts/BasicLayout.vue | 2 +- ui/console-src/main.ts | 4 +- ui/src/utils/cookie.ts | 4 + ui/uc-src/layouts/BasicLayout.vue | 2 +- ui/uc-src/main.ts | 4 +- ui/uc-src/router/guards/auth-check.ts | 2 +- 40 files changed, 1380 insertions(+), 7 deletions(-) create mode 100644 application/src/main/resources/static/images/logo.png create mode 100644 application/src/main/resources/static/images/wordmark.svg create mode 100644 application/src/main/resources/static/js/main.js create mode 100644 application/src/main/resources/static/styles/main.css create mode 100644 application/src/main/resources/templates/challenges/two-factor/totp.html create mode 100644 application/src/main/resources/templates/challenges/two-factor/totp.properties create mode 100644 application/src/main/resources/templates/challenges/two-factor/totp_en.properties create mode 100644 application/src/main/resources/templates/gateway_modules/common_fragments.html create mode 100644 application/src/main/resources/templates/gateway_modules/common_fragments.properties create mode 100644 application/src/main/resources/templates/gateway_modules/common_fragments_en.properties create mode 100644 application/src/main/resources/templates/gateway_modules/input_fragments.html create mode 100644 application/src/main/resources/templates/gateway_modules/layout.html create mode 100644 application/src/main/resources/templates/gateway_modules/login_fragments.html create mode 100644 application/src/main/resources/templates/gateway_modules/login_fragments.properties create mode 100644 application/src/main/resources/templates/gateway_modules/login_fragments_en.properties create mode 100644 application/src/main/resources/templates/login.html create mode 100644 application/src/main/resources/templates/login.properties create mode 100644 application/src/main/resources/templates/login_email.html create mode 100644 application/src/main/resources/templates/login_en.properties create mode 100644 application/src/main/resources/templates/login_local.html create mode 100644 application/src/main/resources/templates/login_local.properties create mode 100644 application/src/main/resources/templates/login_local_en.properties create mode 100644 application/src/main/resources/templates/logout.html create mode 100644 application/src/main/resources/templates/logout.properties create mode 100644 application/src/main/resources/templates/logout_en.properties create mode 100644 application/src/main/resources/templates/password-reset-link.html create mode 100644 application/src/main/resources/templates/password-reset-link.properties create mode 100644 application/src/main/resources/templates/password-reset-link_en.properties create mode 100644 application/src/main/resources/templates/password-reset.html create mode 100644 application/src/main/resources/templates/password-reset.properties create mode 100644 application/src/main/resources/templates/password-reset_en.properties create mode 100644 application/src/main/resources/templates/signup.html create mode 100644 application/src/main/resources/templates/signup.properties create mode 100644 application/src/main/resources/templates/signup_en.properties create mode 100644 ui/src/utils/cookie.ts diff --git a/application/src/main/resources/static/images/logo.png b/application/src/main/resources/static/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..135bb98e534133802f1dd50204dae618915673ef GIT binary patch literal 11144 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 DOTHoo literal 0 HcmV?d00001 diff --git a/application/src/main/resources/static/images/wordmark.svg b/application/src/main/resources/static/images/wordmark.svg new file mode 100644 index 000000000..be7572154 --- /dev/null +++ b/application/src/main/resources/static/images/wordmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/application/src/main/resources/static/js/main.js b/application/src/main/resources/static/js/main.js new file mode 100644 index 000000000..2db2e606b --- /dev/null +++ b/application/src/main/resources/static/js/main.js @@ -0,0 +1,154 @@ +const Toast = (function () { + let container; + + function getContainer() { + if (container) return container; + + container = document.createElement("div"); + container.style.cssText = ` + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + z-index: 9999; + `; + + if (document.body) { + document.body.appendChild(container); + } else { + document.addEventListener("DOMContentLoaded", () => { + document.body.appendChild(container); + }); + } + + return container; + } + + class ToastMessage { + constructor(message, type) { + this.message = message; + this.type = type; + this.element = null; + this.create(); + } + + create() { + this.element = document.createElement("div"); + this.element.textContent = this.message; + this.element.style.cssText = ` + background-color: ${this.type === "success" ? "#4CAF50" : "#F44336"}; + color: white; + padding: 12px 24px; + border-radius: 4px; + margin-bottom: 10px; + box-shadow: 0 2px 5px rgba(0,0,0,0.2); + opacity: 0; + transition: opacity 0.3s ease-in-out; + `; + getContainer().appendChild(this.element); + + setTimeout(() => { + this.element.style.opacity = "1"; + }, 10); + + setTimeout(() => { + this.remove(); + }, 3000); + } + + remove() { + this.element.style.opacity = "0"; + setTimeout(() => { + const parent = this.element.parentNode; + if (parent) { + parent.removeChild(this.element); + } + }, 300); + } + } + + function showToast(message, type) { + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", () => { + new ToastMessage(message, type); + }); + } else { + new ToastMessage(message, type); + } + } + + return { + success: function (message) { + showToast(message, "success"); + }, + error: function (message) { + showToast(message, "error"); + }, + }; +})(); + +function sendVerificationCode(button, sendRequest) { + let timer; + const countdown = 60; + + button.addEventListener("click", () => { + button.disabled = true; + sendRequest() + .then(() => { + startCountdown(); + Toast.success("发送成功"); + }) + .catch((e) => { + button.disabled = false; + if (e instanceof Error) { + Toast.error(e.message); + } else { + Toast.error("发送失败,请稍后再试"); + } + }); + }); + + function startCountdown() { + let remainingTime = countdown; + button.disabled = true; + button.classList.add("disabled"); + + timer = setInterval(() => { + if (remainingTime > 0) { + button.textContent = `${remainingTime}s`; + remainingTime--; + } else { + clearInterval(timer); + button.textContent = "Send"; + button.disabled = false; + button.classList.remove("disabled"); + } + }, 1000); + } +} + +document.addEventListener("DOMContentLoaded", () => { + const passwordContainers = document.querySelectorAll(".toggle-password-display-flag"); + + passwordContainers.forEach((container) => { + const passwordInput = container.querySelector('input[type="password"]'); + const toggleButton = container.querySelector(".toggle-password-button"); + const displayIcon = container.querySelector(".password-display-icon"); + const hiddenIcon = container.querySelector(".password-hidden-icon"); + + if (passwordInput && toggleButton && displayIcon && hiddenIcon) { + toggleButton.addEventListener("click", () => { + if (passwordInput.type === "password") { + passwordInput.type = "text"; + displayIcon.style.display = "none"; + hiddenIcon.style.display = "block"; + } else { + passwordInput.type = "password"; + displayIcon.style.display = "block"; + hiddenIcon.style.display = "none"; + } + }); + } + }); +}); + diff --git a/application/src/main/resources/static/styles/main.css b/application/src/main/resources/static/styles/main.css new file mode 100644 index 000000000..9d062b000 --- /dev/null +++ b/application/src/main/resources/static/styles/main.css @@ -0,0 +1,410 @@ +/* Base */ +.gateway-page { + width: 100vw; + height: 100vh; + background-color: #f5f5f5; + overflow: auto; +} + +.gateway-wrapper, +.gateway-wrapper:before, +.gateway-wrapper:after { + box-sizing: border-box; + border-width: 0; + border-style: solid; +} + +.gateway-wrapper *, +.gateway-wrapper *:before, +.gateway-wrapper *:after { + box-sizing: border-box; + border-width: 0; + border-style: solid; +} + +.gateway-wrapper { + --color-primary: #4ccba0; + --color-secondary: #0e1731; + --color-link: #1f75cb; + --color-text: #374151; + --color-border: #d1d5db; + --rounded-sm: 0.125em; + --rounded-base: 0.25em; + --rounded-lg: 0.5em; + --spacing-xl: 1.25em; + --spacing-lg: 1em; + --spacing-md: 0.875em; + --spacing-sm: 0.5em; + --text-md: 0.875em; +} + +.gateway-wrapper { + margin: 0 auto; + max-width: 28em; + padding: 5% 1em; + font-family: + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + Segoe UI, + Roboto, + Helvetica Neue, + Arial, + Noto Sans, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + Segoe UI Symbol, + "Noto Color Emoji"; +} + +/* Form */ +.halo-form-wrapper { + border-radius: var(--rounded-lg); + background: #fff; + padding: 1.5em; +} + +.form-title { + all: unset; + margin-bottom: 1em; + display: block; + font-weight: 500; + font-size: 1.75em; +} + +.halo-form .form-item { + display: flex; + flex-direction: column; + margin-bottom: 1.3em; + width: 100%; +} + +.halo-form .form-item:last-child { + margin-bottom: 0; +} + +.halo-form .form-item-group { + gap: var(--spacing-lg); + display: flex; + align-items: center; + margin-bottom: 1.3em; +} + +.halo-form .form-item-group .form-item { + margin-bottom: 0; +} + +.halo-form .form-input { + border-radius: var(--rounded-base); + border: 1px solid var(--color-border); + height: 2.5em; + background: #fff; + padding: 0 0.75rem; +} + +.halo-form .form-input:focus-within { + border-color: var(--color-primary); + outline: 2px solid transparent; + outline-offset: "2px"; +} + +.halo-form .form-item input { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + display: block; + font-size: 1em; + box-shadow: none; + width: 100%; + height: 100%; + background: transparent; +} + +.halo-form .form-item input:focus { + outline: none; +} + +.halo-form .form-input-stack { + display: flex; + align-items: center; + gap: 0.5em; +} + +.halo-form .form-input-stack-icon { + display: inline-flex; + align-items: center; + color: var(--color-text); + cursor: pointer; +} + +.halo-form .form-input-stack-select { + all: unset; + color: var(--color-text); + font-size: var(--text-md); + padding-right: 1.85em; + display: inline-flex; + align-items: center; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='1em' height='1em' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='m12 13.171l4.95-4.95l1.414 1.415L12 16L5.636 9.636L7.05 8.222z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 0.3em center; +} + +.halo-form .form-input-stack-text { + color: var(--color-text); + font-size: var(--text-md); +} + +.halo-form .form-item label { + color: var(--color-text); + margin-bottom: 0.5em; +} + +.halo-form .form-item .form-label-group { + margin-bottom: 0.5em; + display: flex; + justify-content: space-between; + align-items: center; +} + +.halo-form .form-item .form-label-group label { + margin-bottom: 0; +} + +.halo-form .form-item-extra-link { + color: var(--color-link); + font-size: var(--text-md); + text-decoration: none; +} + +.halo-form .form-item-compact { + gap: var(--spacing-sm); + margin-bottom: 1.5em; + display: flex; + align-items: center; +} + +.halo-form .form-item-compact label { + color: var(--color-text); + font-size: var(--text-md); +} + +.halo-form button[type="submit"] { + background: var(--color-secondary); + border-radius: var(--rounded-base); + height: 2.5em; + color: #fff; + border: none; + cursor: pointer; +} + +.halo-form button[type="submit"]:hover { + opacity: 0.8; +} + +.halo-form button[type="submit"]:active { + opacity: 0.9; +} + +.halo-form button[disabled] { + cursor: not-allowed !important; +} + +.halo-form input[type="checkbox"] { + border: 1px solid var(--color-border); + border-radius: var(--rounded-sm); + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding: 0; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + user-select: none; + flex-shrink: 0; + height: 1em; + width: 1em; + color: #2563eb; + background-color: #fff; +} + +.halo-form input[type="checkbox"]:focus { + outline: 2px solid transparent; + outline-offset: 2px; + box-shadow: + rgb(255, 255, 255) 0px 0px 0px 2px, + rgb(37, 99, 235) 0px 0px 0px 4px, + rgba(0, 0, 0, 0) 0px 0px 0px 0px; +} + +.halo-form input[type="checkbox"]:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +.halo-form .form-input-group { + gap: var(--spacing-sm); + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + align-items: center; +} + +.halo-form .form-input { + grid-column: span 2 / span 2; +} + +.halo-form .form-input-group button { + border-radius: var(--rounded-base); + border: 1px solid var(--color-border); + color: var(--color-text); + font-size: var(--text-md); + grid-column: span 1 / span 1; + height: 100%; + cursor: pointer; + background: #fff; +} + +.halo-form .form-input-group button:hover { + color: #333; + background: #f3f4f6; +} + +.halo-form .form-input-group button:active { + background: #f9fafb; +} + +.auth-provider-items { + all: unset; + gap: var(--spacing-md); + margin: 0; + display: flex; + flex-wrap: wrap; + justify-content: center; +} + +.auth-provider-items li { + all: unset; + border-radius: var(--rounded-lg); + overflow: hidden; + border: 1px solid #e5e7eb; + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 0.15s; +} + +.auth-provider-items li a { + gap: var(--spacing-sm); + padding: 0.7em 1em; + display: flex; + align-items: center; + color: #1f2937; + text-decoration: none; + font-size: 0.8em; +} + +.auth-provider-items li img { + width: 1.5em; + height: 1.5em; +} + +.auth-provider-items li:hover { + border-color: var(--color-primary); + background: #f3f4f6; +} + +.auth-provider-items li:hover a { + color: #111827; +} + +.auth-provider-items li:focus-within { + border-color: var(--color-primary); +} + +.divider-wrapper { + color: var(--color-text); + font-size: var(--text-md); + gap: var(--spacing-lg); + display: flex; + align-items: center; + margin: 1.5em 0; +} + +.divider-wrapper hr { + flex-grow: 1; + overflow: hidden; + border: 0; + border-top: 1px solid #f3f4f6; +} + +.alert { + border: 1px solid #e5e7eb; + border-radius: var(--rounded-base); + margin-bottom: var(--spacing-xl); + padding: var(--spacing-md) var(--spacing-xl); + font-size: var(--text-md); + overflow: hidden; + position: relative; + color: var(--color-text); +} + +.alert::before { + content: ""; + position: absolute; + height: 100%; + left: 0; + background: #d1d5db; + width: 0.25em; + top: 0; +} + +.alert-warning { + border-color: #fde047; +} + +.alert-warning::before { + background: #ea580c; +} + +.alert-error { + border-color: #fca5a5; +} + +.alert-error::before { + background: #dc2626; +} + +.alert-success { + border-color: #86efac; +} + +.alert-success::before { + background: #16a34a; +} + +.alert-info { + border-color: #7dd3fc; +} + +.alert-info::before { + background: #0284c7; +} + +@media (forced-colors: active) { + .halo-form input[type="checkbox"]:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +@media only screen and (max-width: 768px) { + .halo-form .form-item-group { + flex-direction: column; + } +} diff --git a/application/src/main/resources/templates/challenges/two-factor/totp.html b/application/src/main/resources/templates/challenges/two-factor/totp.html new file mode 100644 index 000000000..96baa8d8c --- /dev/null +++ b/application/src/main/resources/templates/challenges/two-factor/totp.html @@ -0,0 +1,43 @@ + + + +
+
+
+

+
+ +
+ +
+ +
+
+
+ +
+
+
+
+
+ diff --git a/application/src/main/resources/templates/challenges/two-factor/totp.properties b/application/src/main/resources/templates/challenges/two-factor/totp.properties new file mode 100644 index 000000000..46f96ed97 --- /dev/null +++ b/application/src/main/resources/templates/challenges/two-factor/totp.properties @@ -0,0 +1,4 @@ +title=两步验证 +messages.invalidError=错误的验证码 +form.code.label=验证码 +form.submit=验证 diff --git a/application/src/main/resources/templates/challenges/two-factor/totp_en.properties b/application/src/main/resources/templates/challenges/two-factor/totp_en.properties new file mode 100644 index 000000000..bffc96863 --- /dev/null +++ b/application/src/main/resources/templates/challenges/two-factor/totp_en.properties @@ -0,0 +1,4 @@ +title=Two-Factor Authentication +messages.invalidError=Invalid TOTP code +form.code.label=TOTP Code +form.submit=Verify diff --git a/application/src/main/resources/templates/gateway_modules/common_fragments.html b/application/src/main/resources/templates/gateway_modules/common_fragments.html new file mode 100644 index 000000000..75d718ca3 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/common_fragments.html @@ -0,0 +1,110 @@ + + + + + + + + + +
+ +
+ + + +
+
+ +
+ + +
+ +
+ +
+
+ +
+
+ +
+
diff --git a/application/src/main/resources/templates/gateway_modules/common_fragments.properties b/application/src/main/resources/templates/gateway_modules/common_fragments.properties new file mode 100644 index 000000000..6617dd0d7 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/common_fragments.properties @@ -0,0 +1 @@ +socialLogin.label=社交登录 \ No newline at end of file diff --git a/application/src/main/resources/templates/gateway_modules/common_fragments_en.properties b/application/src/main/resources/templates/gateway_modules/common_fragments_en.properties new file mode 100644 index 000000000..a10abbb87 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/common_fragments_en.properties @@ -0,0 +1 @@ +socialLogin.label=Social Login \ No newline at end of file diff --git a/application/src/main/resources/templates/gateway_modules/input_fragments.html b/application/src/main/resources/templates/gateway_modules/input_fragments.html new file mode 100644 index 000000000..ffffb5cd6 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/input_fragments.html @@ -0,0 +1,31 @@ +
+
+ + +
+ + + + + +
+
+
diff --git a/application/src/main/resources/templates/gateway_modules/layout.html b/application/src/main/resources/templates/gateway_modules/layout.html new file mode 100644 index 000000000..126838d12 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/layout.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/application/src/main/resources/templates/gateway_modules/login_fragments.html b/application/src/main/resources/templates/gateway_modules/login_fragments.html new file mode 100644 index 000000000..87e8fe64c --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/login_fragments.html @@ -0,0 +1,95 @@ + +
+ + + + +
+ +
+ + +
+ +
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + + +
diff --git a/application/src/main/resources/templates/gateway_modules/login_fragments.properties b/application/src/main/resources/templates/gateway_modules/login_fragments.properties new file mode 100644 index 000000000..5752dae82 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/login_fragments.properties @@ -0,0 +1,13 @@ +messages.loginError=无效的凭证。 +messages.logoutSuccess=登出成功。 +messages.signupSuccess=恭喜!注册成功,请立即登录。 + +error.invalid-credential=无效的凭证。 +error.rate-limit-exceeded=请求过于频繁,请稍后再试。 + +form.rememberMe.label=保持登录会话 +form.submit=登录 +otherLogin.label=其他登录方式 +signup.description=没有账号? +signup.link=立即注册 +returnToSite=返回网站 \ No newline at end of file diff --git a/application/src/main/resources/templates/gateway_modules/login_fragments_en.properties b/application/src/main/resources/templates/gateway_modules/login_fragments_en.properties new file mode 100644 index 000000000..d3b5b33f4 --- /dev/null +++ b/application/src/main/resources/templates/gateway_modules/login_fragments_en.properties @@ -0,0 +1,13 @@ +messages.loginError=Invalid credentials. +messages.logoutSuccess=Logout successfully. +messages.signupSuccess=Congratulations! Sign up successfully, please sign in now. + +error.invalid-credential=Invalid credentials. +error.rate-limit-exceeded=Too many requests, please try again later. + +form.rememberMe.label=Remember me +form.submit=Login +otherLogin.label=Other Login +signup.description=Don't have an account? +signup.link=Sign up +returnToSite=Return to site \ No newline at end of file diff --git a/application/src/main/resources/templates/login.html b/application/src/main/resources/templates/login.html new file mode 100644 index 000000000..47d808459 --- /dev/null +++ b/application/src/main/resources/templates/login.html @@ -0,0 +1,20 @@ + + + +
+
+ +
+
+
+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/application/src/main/resources/templates/login.properties b/application/src/main/resources/templates/login.properties new file mode 100644 index 000000000..26367c07c --- /dev/null +++ b/application/src/main/resources/templates/login.properties @@ -0,0 +1 @@ +title=登录 \ No newline at end of file diff --git a/application/src/main/resources/templates/login_email.html b/application/src/main/resources/templates/login_email.html new file mode 100644 index 000000000..cbdda6d64 --- /dev/null +++ b/application/src/main/resources/templates/login_email.html @@ -0,0 +1,37 @@ +
+
+ + +
+ +
+
+
+ +
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/application/src/main/resources/templates/login_en.properties b/application/src/main/resources/templates/login_en.properties new file mode 100644 index 000000000..eb0443eed --- /dev/null +++ b/application/src/main/resources/templates/login_en.properties @@ -0,0 +1 @@ +title=Login diff --git a/application/src/main/resources/templates/login_local.html b/application/src/main/resources/templates/login_local.html new file mode 100644 index 000000000..3ae24d400 --- /dev/null +++ b/application/src/main/resources/templates/login_local.html @@ -0,0 +1,56 @@ +
+ + + +
+ + +
+ +
+
+
+
+ + + +
+ + +
+
diff --git a/application/src/main/resources/templates/login_local.properties b/application/src/main/resources/templates/login_local.properties new file mode 100644 index 000000000..8163bfd74 --- /dev/null +++ b/application/src/main/resources/templates/login_local.properties @@ -0,0 +1,3 @@ +form.username.label=用户名 +form.password.label=密码 +form.password.forgot=忘记密码? diff --git a/application/src/main/resources/templates/login_local_en.properties b/application/src/main/resources/templates/login_local_en.properties new file mode 100644 index 000000000..d0fbb0901 --- /dev/null +++ b/application/src/main/resources/templates/login_local_en.properties @@ -0,0 +1,3 @@ +form.username.label=Username +form.password.label=Password +form.password.forgot=Forgot your password? diff --git a/application/src/main/resources/templates/logout.html b/application/src/main/resources/templates/logout.html new file mode 100644 index 000000000..cafcd1b7f --- /dev/null +++ b/application/src/main/resources/templates/logout.html @@ -0,0 +1,18 @@ + + + +
+
+

+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/application/src/main/resources/templates/logout.properties b/application/src/main/resources/templates/logout.properties new file mode 100644 index 000000000..4ff553242 --- /dev/null +++ b/application/src/main/resources/templates/logout.properties @@ -0,0 +1,3 @@ +title=退出登录 +form.title=确定要退出登录吗? +form.submit=退出登录 diff --git a/application/src/main/resources/templates/logout_en.properties b/application/src/main/resources/templates/logout_en.properties new file mode 100644 index 000000000..3ad1587f0 --- /dev/null +++ b/application/src/main/resources/templates/logout_en.properties @@ -0,0 +1,3 @@ +title=Logout +form.title=Are you sure want to log out? +form.submit=Logout \ No newline at end of file diff --git a/application/src/main/resources/templates/password-reset-link.html b/application/src/main/resources/templates/password-reset-link.html new file mode 100644 index 000000000..2a40d4d81 --- /dev/null +++ b/application/src/main/resources/templates/password-reset-link.html @@ -0,0 +1,39 @@ + + + +
+
+
+

+

+
+
+ + +
+
+ + +
+
+

+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/application/src/main/resources/templates/password-reset-link.properties b/application/src/main/resources/templates/password-reset-link.properties new file mode 100644 index 000000000..a401763b3 --- /dev/null +++ b/application/src/main/resources/templates/password-reset-link.properties @@ -0,0 +1,5 @@ +title=为 {0} 修改密码 +form.password.label=密码 +form.confirmPassword.label=确认密码 +form.password.tips=密码必须至少包含 8 个字符,并且至少包含一个大写字母、一个小写字母、一个数字和一个特殊字符。 +form.submit=修改密码 diff --git a/application/src/main/resources/templates/password-reset-link_en.properties b/application/src/main/resources/templates/password-reset-link_en.properties new file mode 100644 index 000000000..3075f3b83 --- /dev/null +++ b/application/src/main/resources/templates/password-reset-link_en.properties @@ -0,0 +1,5 @@ +title=Change password for @{0} +form.password.label=Password +form.confirmPassword.label=Confirm Password +form.password.tips=Password must be at least 8 characters long and contain at least one uppercase letter, one lowercase letter, one number, and one special character. +form.submit=Change password diff --git a/application/src/main/resources/templates/password-reset.html b/application/src/main/resources/templates/password-reset.html new file mode 100644 index 000000000..3e198e142 --- /dev/null +++ b/application/src/main/resources/templates/password-reset.html @@ -0,0 +1,36 @@ + + + +
+
+
+

+
+ +
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+
+
+ +
+
+
+
+
+ diff --git a/application/src/main/resources/templates/password-reset.properties b/application/src/main/resources/templates/password-reset.properties new file mode 100644 index 000000000..f008656fc --- /dev/null +++ b/application/src/main/resources/templates/password-reset.properties @@ -0,0 +1,6 @@ +title=重置密码 +form.email.label=电子邮箱 +form.submit=提交 +sent.form.submit=返回到登录页面 +sent.form.message=检查您的电子邮件中是否有重置密码的链接。如果几分钟内没有出现,请检查您的垃圾邮件文件夹。 +sent.title=已发送重置密码的邮件 \ No newline at end of file diff --git a/application/src/main/resources/templates/password-reset_en.properties b/application/src/main/resources/templates/password-reset_en.properties new file mode 100644 index 000000000..36555ac69 --- /dev/null +++ b/application/src/main/resources/templates/password-reset_en.properties @@ -0,0 +1,6 @@ +title=Reset password +form.email.label=Email +form.submit=Submit +sent.form.submit=Return to login +sent.form.message=Check your email for a link to reset your password. If it doesn’t appear within a few minutes, check your spam folder. +sent.title=Password reset email has been sent \ No newline at end of file diff --git a/application/src/main/resources/templates/signup.html b/application/src/main/resources/templates/signup.html new file mode 100644 index 000000000..01186b5ae --- /dev/null +++ b/application/src/main/resources/templates/signup.html @@ -0,0 +1,202 @@ + + + + + + + + + + \ No newline at end of file diff --git a/application/src/main/resources/templates/signup.properties b/application/src/main/resources/templates/signup.properties new file mode 100644 index 000000000..ce6a56c6d --- /dev/null +++ b/application/src/main/resources/templates/signup.properties @@ -0,0 +1,13 @@ +title=注册 +form.username.label=用户名 +form.displayName.label=名称 +form.email.label=电子邮箱 +form.emailCode.label=邮箱验证码 +form.emailCode.sendButton=发送 +form.password.label=密码 +form.confirmPassword.label=确认密码 +form.submit=注册 + +error.invalid-email-code=无效的邮箱验证码 +error.duplicate-name=用户名已经被注册 +error.rate-limit-exceeded=请求过于频繁,请稍后再试 diff --git a/application/src/main/resources/templates/signup_en.properties b/application/src/main/resources/templates/signup_en.properties new file mode 100644 index 000000000..bdbb22206 --- /dev/null +++ b/application/src/main/resources/templates/signup_en.properties @@ -0,0 +1,9 @@ +title=Sign up +form.username.label=Username +form.displayName.label=Display name +form.email.label=Email +form.emailCode.label=Email Code +form.emailCode.sendButton=Send +form.password.label=Password +form.confirmPassword.label=Confirm password +form.submit=Sign up \ No newline at end of file diff --git a/ui/console-src/layouts/BasicLayout.vue b/ui/console-src/layouts/BasicLayout.vue index b1fc71be7..1e6ffcbf6 100644 --- a/ui/console-src/layouts/BasicLayout.vue +++ b/ui/console-src/layouts/BasicLayout.vue @@ -57,7 +57,7 @@ const handleLogout = () => { document.cookie = "XSRF-TOKEN=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; - router.replace({ name: "Login" }); + window.location.href = "/login"; } catch (error) { console.error("Failed to logout", error); } diff --git a/ui/console-src/main.ts b/ui/console-src/main.ts index e05d17c4d..45ee18818 100644 --- a/ui/console-src/main.ts +++ b/ui/console-src/main.ts @@ -14,6 +14,7 @@ import { setupVueQuery } from "@/setup/setupVueQuery"; import { useGlobalInfoStore } from "@/stores/global-info"; import { useRoleStore } from "@/stores/role"; import { useUserStore } from "@/stores/user"; +import { getCookie } from "@/utils/cookie"; import { hasPermission } from "@/utils/permission"; import { setupCoreModules, @@ -78,8 +79,7 @@ async function initApp() { await userStore.fetchCurrentUser(); // set locale - i18n.global.locale.value = - localStorage.getItem("locale") || getBrowserLanguage(); + i18n.global.locale.value = getCookie("language") || getBrowserLanguage(); const globalInfoStore = useGlobalInfoStore(); await globalInfoStore.fetchGlobalInfo(); diff --git a/ui/src/utils/cookie.ts b/ui/src/utils/cookie.ts new file mode 100644 index 000000000..5b376aa44 --- /dev/null +++ b/ui/src/utils/cookie.ts @@ -0,0 +1,4 @@ +export function getCookie(name: string) { + const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)")); + return match ? match[2] : null; +} diff --git a/ui/uc-src/layouts/BasicLayout.vue b/ui/uc-src/layouts/BasicLayout.vue index 5db0136d3..be079413a 100644 --- a/ui/uc-src/layouts/BasicLayout.vue +++ b/ui/uc-src/layouts/BasicLayout.vue @@ -53,7 +53,7 @@ const handleLogout = () => { document.cookie = "XSRF-TOKEN=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"; - window.location.href = "/console/login"; + window.location.href = "/login"; } catch (error) { console.error("Failed to logout", error); } diff --git a/ui/uc-src/main.ts b/ui/uc-src/main.ts index 071543843..d79ac8536 100644 --- a/ui/uc-src/main.ts +++ b/ui/uc-src/main.ts @@ -6,6 +6,7 @@ import { setupVueQuery } from "@/setup/setupVueQuery"; import { useGlobalInfoStore } from "@/stores/global-info"; import { useRoleStore } from "@/stores/role"; import { useUserStore } from "@/stores/user"; +import { getCookie } from "@/utils/cookie"; import { hasPermission } from "@/utils/permission"; import { consoleApiClient } from "@halo-dev/api-client"; import router from "@uc/router"; @@ -66,8 +67,7 @@ async function initApp() { await userStore.fetchCurrentUser(); // set locale - i18n.global.locale.value = - localStorage.getItem("locale") || getBrowserLanguage(); + i18n.global.locale.value = getCookie("language") || getBrowserLanguage(); const globalInfoStore = useGlobalInfoStore(); await globalInfoStore.fetchGlobalInfo(); diff --git a/ui/uc-src/router/guards/auth-check.ts b/ui/uc-src/router/guards/auth-check.ts index 322a7be26..9ff1c02d5 100644 --- a/ui/uc-src/router/guards/auth-check.ts +++ b/ui/uc-src/router/guards/auth-check.ts @@ -13,7 +13,7 @@ export function setupAuthCheckGuard(router: Router) { const userStore = useUserStore(); if (userStore.isAnonymous) { - window.location.href = `/console/login?redirect_uri=${encodeURIComponent( + window.location.href = `/login?redirect_uri=${encodeURIComponent( window.location.href )}`; return;