diff --git a/package-lock.json b/package-lock.json index 67801ca..c79bf87 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@vue/tsconfig": "^0.9.1", "tailwindcss": "^4.3.3", "typescript": "~6.0.2", - "vite": "^8.0.12", + "vite": "^8.0.16", "vue-tsc": "^3.2.8" } }, @@ -214,9 +214,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.130.0", - "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.130.0.tgz", - "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -224,9 +224,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", - "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -241,9 +241,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -258,9 +258,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", - "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -275,9 +275,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", - "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -292,9 +292,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", - "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -309,9 +309,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", - "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], @@ -329,9 +329,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", - "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], @@ -349,9 +349,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", - "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], @@ -369,9 +369,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", - "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], @@ -389,9 +389,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", - "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -409,9 +409,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", - "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], @@ -429,9 +429,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", - "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -446,9 +446,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", - "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -465,9 +465,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -482,9 +482,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -1304,7 +1304,7 @@ }, "node_modules/fdir": { "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", @@ -1824,13 +1824,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.0.1.tgz", - "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.130.0", + "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -1840,21 +1840,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.1", - "@rolldown/binding-darwin-arm64": "1.0.1", - "@rolldown/binding-darwin-x64": "1.0.1", - "@rolldown/binding-freebsd-x64": "1.0.1", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", - "@rolldown/binding-linux-arm64-gnu": "1.0.1", - "@rolldown/binding-linux-arm64-musl": "1.0.1", - "@rolldown/binding-linux-ppc64-gnu": "1.0.1", - "@rolldown/binding-linux-s390x-gnu": "1.0.1", - "@rolldown/binding-linux-x64-gnu": "1.0.1", - "@rolldown/binding-linux-x64-musl": "1.0.1", - "@rolldown/binding-openharmony-arm64": "1.0.1", - "@rolldown/binding-wasm32-wasi": "1.0.1", - "@rolldown/binding-win32-arm64-msvc": "1.0.1", - "@rolldown/binding-win32-x64-msvc": "1.0.1" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/seemly": { @@ -1915,9 +1915,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -1979,17 +1979,17 @@ } }, "node_modules/vite": { - "version": "8.0.13", - "resolved": "https://registry.npmmirror.com/vite/-/vite-8.0.13.tgz", - "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.14", - "rolldown": "1.0.1", - "tinyglobby": "^0.2.16" + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index fd0a311..963028e 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@vue/tsconfig": "^0.9.1", "tailwindcss": "^4.3.3", "typescript": "~6.0.2", - "vite": "^8.0.12", + "vite": "^8.0.16", "vue-tsc": "^3.2.8" } } diff --git a/src/views/auth/AuthConfirmView.vue b/src/views/auth/AuthConfirmView.vue index ecf1899..7d4596a 100644 --- a/src/views/auth/AuthConfirmView.vue +++ b/src/views/auth/AuthConfirmView.vue @@ -10,6 +10,11 @@ const router = useRouter() const state = ref<'loading' | 'success' | 'failed'>('loading') const countdown = ref(5) let countdownTimer: ReturnType | null = null +const confirmationToken = typeof route.query.token === 'string' ? route.query.token : '' + +if (route.query.token !== undefined) { + window.history.replaceState(window.history.state, '', window.location.pathname) +} function startCountdown() { countdownTimer = setInterval(() => { @@ -23,8 +28,7 @@ function startCountdown() { onMounted(async () => { try { - const token = typeof route.query.token === 'string' ? route.query.token : '' - if (!token) { + if (!confirmationToken) { state.value = 'failed' return } @@ -32,9 +36,8 @@ onMounted(async () => { await apiRequest('/auth/confirm-email', { method: 'POST', auth: false, - body: { token }, + body: { token: confirmationToken }, }) - window.history.replaceState(null, '', window.location.pathname) state.value = 'success' startCountdown() } catch { diff --git a/src/views/auth/LoginView.vue b/src/views/auth/LoginView.vue index fe8b77d..e74f195 100644 --- a/src/views/auth/LoginView.vue +++ b/src/views/auth/LoginView.vue @@ -13,13 +13,25 @@ const password = ref('') const error = ref('') const loading = ref(false) +function getSafeRedirect(value: unknown) { + if (typeof value !== 'string' || !value.startsWith('/')) return '/' + + try { + const target = new URL(value, window.location.origin) + return target.origin === window.location.origin + ? `${target.pathname}${target.search}${target.hash}` + : '/' + } catch { + return '/' + } +} + async function handleLogin() { error.value = '' loading.value = true try { await authStore.login(email.value, password.value) - const redirect = (route.query.redirect as string) || '/' - router.push(redirect) + void router.push(getSafeRedirect(route.query.redirect)) } catch (e: unknown) { error.value = e instanceof Error ? e.message : '登录失败,请稍后重试' } finally { diff --git a/src/views/auth/ResetPasswordView.vue b/src/views/auth/ResetPasswordView.vue index d96f929..301f9ee 100644 --- a/src/views/auth/ResetPasswordView.vue +++ b/src/views/auth/ResetPasswordView.vue @@ -13,9 +13,14 @@ const error = ref('') const state = ref<'checking' | 'ready' | 'invalid' | 'success'>('checking') const loading = ref(false) const countdown = ref(5) -const resetToken = ref('') +const initialResetToken = typeof route.query.token === 'string' ? route.query.token : '' +const resetToken = ref(initialResetToken) let countdownTimer: ReturnType | null = null +if (route.query.token !== undefined) { + window.history.replaceState(window.history.state, '', window.location.pathname) +} + const canSubmit = computed(() => state.value === 'ready' && password.value.length >= 8 && password.value === confirmPassword.value, ) @@ -49,13 +54,10 @@ function startCountdown() { } async function initializeRecoverySession() { - const token = typeof route.query.token === 'string' ? route.query.token : '' - if (!token) { + if (!resetToken.value) { showInvalidRecoveryLink() return } - - resetToken.value = token state.value = 'ready' } @@ -78,7 +80,6 @@ async function handleResetPassword() { auth: false, body: { token: resetToken.value, password: password.value }, }) - window.history.replaceState(null, '', window.location.pathname) countdown.value = 5 state.value = 'success' startCountdown() diff --git a/src/views/map/MapView.vue b/src/views/map/MapView.vue index 0a1d0b5..53e52e9 100644 --- a/src/views/map/MapView.vue +++ b/src/views/map/MapView.vue @@ -165,38 +165,68 @@ function getMarkerOpacity(cloud: CloudMarkerData) { return 1 - progress * (1 - MIN_MARKER_OPACITY) } -function bubbleHtml(cloud: CloudMarkerData): string { +function bubbleContent(cloud: CloudMarkerData): HTMLElement { const border = rarityColors[cloud.rarity] || rarityColors.common const bubbleImage = cloud.thumbnailUrl || cloud.imageUrl const opacity = getMarkerOpacity(cloud) ?? MIN_MARKER_OPACITY - return `
-
- -
-
` + + const container = document.createElement('div') + container.style.cssText = `opacity:${opacity};width:46px;height:46px;cursor:pointer;line-height:0` + + const bubble = document.createElement('div') + bubble.style.cssText = `box-sizing:border-box;width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,#ffffff 0%,#e8ecf1 100%);border:3px solid ${border};box-shadow:0 4px 16px rgba(0,0,0,0.3),inset 0 2px 4px rgba(255,255,255,0.6),0 0 0 2px rgba(0,0,0,0.06);overflow:hidden` + + const image = document.createElement('img') + image.src = bubbleImage + image.alt = '' + image.style.cssText = 'display:block;width:100%;height:100%;object-fit:cover' + + bubble.append(image) + container.append(bubble) + return container } -function hoverCardHtml(cloud: CloudMarkerData): string { +function hoverCardContent(cloud: CloudMarkerData): HTMLElement { const border = rarityColors[cloud.rarity] || rarityColors.common const cardImage = cloud.thumbnailUrl || cloud.imageUrl const label = cloud.rarity === 'common' ? '常见' : cloud.rarity === 'uncommon' ? '少见' : '罕见' - return `
- -
-
- ${cloud.cloudTypeName} - ${label} -
-
📷 ${cloud.username} · ${timeAgo(cloud.capturedAt)}
-
-
` + + const card = document.createElement('div') + card.style.cssText = 'background:#fff;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.18);overflow:hidden;width:200px;font-family:system-ui,-apple-system,sans-serif' + + const image = document.createElement('img') + image.src = cardImage + image.alt = '' + image.style.cssText = 'display:block;width:100%;height:110px;object-fit:cover' + + const body = document.createElement('div') + body.style.cssText = 'padding:8px 10px' + const title = document.createElement('div') + title.style.cssText = 'display:flex;align-items:center;gap:6px' + + const name = document.createElement('span') + name.textContent = cloud.cloudTypeName + name.style.cssText = 'font-size:13px;font-weight:600;color:#1f2937' + + const rarity = document.createElement('span') + rarity.textContent = label + rarity.style.cssText = `font-size:10px;padding:1px 5px;border-radius:3px;color:${border};background:${border}22` + + const meta = document.createElement('div') + meta.textContent = `📷 ${cloud.username} · ${timeAgo(cloud.capturedAt)}` + meta.style.cssText = 'font-size:11px;color:#9ca3af;margin-top:3px' + + title.append(name, rarity) + body.append(title, meta) + card.append(image, body) + return card } function showHoverCard(cloud: CloudMarkerData, pos: [number, number]) { if (!AMapLib || !mapInst) return hoverIW?.close() const iw = new AMapLib.InfoWindow({ - content: hoverCardHtml(cloud), + content: hoverCardContent(cloud), offset: new AMapLib.Pixel(-100, -60), isCustom: true, } as AMap.InfoWindowOptions) @@ -287,7 +317,7 @@ function drawMarkers(clouds: CloudMarkerData[]) { for (const c of visibleClouds) { const m = new AMapLib.Marker({ position: [c.longitude, c.latitude], - content: bubbleHtml(c), + content: bubbleContent(c), offset: new AMapLib.Pixel(-23, -23), zIndex: 200, } as AMap.MarkerOptions) diff --git a/vercel.json b/vercel.json index 0706bd8..0e426d0 100644 --- a/vercel.json +++ b/vercel.json @@ -14,7 +14,11 @@ }, { "key": "Referrer-Policy", - "value": "strict-origin-when-cross-origin" + "value": "strict-origin" + }, + { + "key": "Strict-Transport-Security", + "value": "max-age=31536000" }, { "key": "Permissions-Policy",