This commit is contained in:
parent
961d9ff222
commit
492d511551
@ -12,6 +12,8 @@ import {Plus} from '@element-plus/icons-vue'
|
||||
|
||||
import {http} from "@/utils/axios";
|
||||
|
||||
import {buildFinalURL as buildSetupFinalURL, probeService} from './SetupNavigation.js'
|
||||
|
||||
const setupRequest = (payload) => http.post("/api/v1/setup", payload).then((response) => {
|
||||
if (!response || typeof response.errorNo !== "number") {
|
||||
throw new Error(lang.setup_failed)
|
||||
@ -374,48 +376,78 @@ export function usePage() {
|
||||
return null
|
||||
})
|
||||
}
|
||||
const buildFinalURL = (sslData) => {
|
||||
const scheme = "https:"
|
||||
const configuredDomain = String(domainSettings.web_domain || "").trim()
|
||||
if (!configuredDomain) {
|
||||
return window.location.href
|
||||
}
|
||||
const buildFinalURL = (sslData) => buildSetupFinalURL(sslData, domainSettings.web_domain)
|
||||
|
||||
let target
|
||||
try {
|
||||
target = new URL(`${scheme}//${configuredDomain}`)
|
||||
} catch (error) {
|
||||
return window.location.href
|
||||
}
|
||||
let statusTimer = null
|
||||
let statusCheckRunning = false
|
||||
let statusCheckStartedAt = 0
|
||||
const statusCheckTimeout = 10 * 60 * 1000
|
||||
|
||||
const currentHost = window.location.hostname.toLowerCase()
|
||||
const targetHost = target.hostname.toLowerCase()
|
||||
const currentPort = window.location.port
|
||||
const isSameHost = currentHost === targetHost
|
||||
const targetPort = Number(sslData?.httpsPort) || 443
|
||||
const proxyMode = sslData?.tlsMode === "proxy"
|
||||
if (isSameHost && currentPort && !proxyMode) {
|
||||
target.port = targetPort > 0 ? String(targetPort) : currentPort
|
||||
} else if (targetPort > 0 && ((scheme === "http:" && targetPort !== 80) || (scheme === "https:" && targetPort !== 443))) {
|
||||
target.port = String(targetPort)
|
||||
const stopStatusCheck = () => {
|
||||
if (statusTimer !== null) {
|
||||
window.clearTimeout(statusTimer)
|
||||
statusTimer = null
|
||||
}
|
||||
|
||||
target.pathname = window.location.pathname
|
||||
target.search = window.location.search
|
||||
target.hash = window.location.hash
|
||||
return target.toString()
|
||||
statusCheckRunning = false
|
||||
statusCheckStartedAt = 0
|
||||
}
|
||||
|
||||
const checkStatus = () => {
|
||||
setupRequest({"action": "get", "step": "ssl"}).then((res) => {
|
||||
if (res.errorNo === 0 && res.data.ready) {
|
||||
window.location.href = buildFinalURL(res.data)
|
||||
return
|
||||
const navigateToFormalService = (sslData) => {
|
||||
stopStatusCheck()
|
||||
fullscreenLoading.value = false
|
||||
const target = buildFinalURL(sslData)
|
||||
if (target && target !== window.location.href) {
|
||||
window.location.href = target
|
||||
}
|
||||
}
|
||||
|
||||
const scheduleStatusCheck = () => {
|
||||
if (statusTimer !== null) {
|
||||
window.clearTimeout(statusTimer)
|
||||
}
|
||||
statusTimer = window.setTimeout(() => {
|
||||
statusTimer = null
|
||||
checkStatus()
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const checkStatus = async () => {
|
||||
if (statusCheckRunning) {
|
||||
return
|
||||
}
|
||||
if (!statusCheckStartedAt) {
|
||||
statusCheckStartedAt = Date.now()
|
||||
}
|
||||
if (Date.now() - statusCheckStartedAt > statusCheckTimeout) {
|
||||
stopStatusCheck()
|
||||
fullscreenLoading.value = false
|
||||
ElMessage.error(lang.setup_failed)
|
||||
return
|
||||
}
|
||||
|
||||
statusCheckRunning = true
|
||||
let sslData = sslRuntime.value || {}
|
||||
try {
|
||||
const response = await setupRequest({"action": "get", "step": "ssl"})
|
||||
if (response.errorNo === 0) {
|
||||
sslData = response.data || sslData
|
||||
sslRuntime.value = sslData
|
||||
if (sslData.ready) {
|
||||
navigateToFormalService(sslData)
|
||||
return
|
||||
}
|
||||
}
|
||||
setTimeout(checkStatus, 1000)
|
||||
}).catch(() => {
|
||||
setTimeout(checkStatus, 1000)
|
||||
})
|
||||
} catch (error) {
|
||||
// 服务切换期间失败属于预期状态。
|
||||
} finally {
|
||||
statusCheckRunning = false
|
||||
}
|
||||
|
||||
if (await probeService(sslData, domainSettings.web_domain)) {
|
||||
navigateToFormalService(sslData)
|
||||
return
|
||||
}
|
||||
scheduleStatusCheck()
|
||||
}
|
||||
|
||||
const setDomainConfig = () => {
|
||||
|
||||
147
fe/src/views/Setup/SetupNavigation.js
Normal file
147
fe/src/views/Setup/SetupNavigation.js
Normal file
@ -0,0 +1,147 @@
|
||||
const DEFAULT_HTTPS_PORT = 443
|
||||
const REDIRECT_STATUS_MIN = 300
|
||||
const REDIRECT_STATUS_MAX = 399
|
||||
|
||||
const currentLocation = () => {
|
||||
if (typeof window === 'undefined' || !window.location) {
|
||||
return null
|
||||
}
|
||||
return window.location
|
||||
}
|
||||
|
||||
const normalizeDomain = (value) => {
|
||||
const domain = String(value || '').trim()
|
||||
if (!domain) {
|
||||
return ''
|
||||
}
|
||||
return domain.replace(/^[a-z][a-z\d+.-]*:\/\//i, '').replace(/\/.*$/, '')
|
||||
}
|
||||
|
||||
const isNonDefaultPort = (protocol, port) => {
|
||||
if (!port) {
|
||||
return false
|
||||
}
|
||||
return protocol === 'https:' ? port !== String(DEFAULT_HTTPS_PORT) : port !== '80'
|
||||
}
|
||||
|
||||
export const isProxyTLSMode = (sslData) => sslData?.tlsMode === 'proxy'
|
||||
|
||||
export const buildFinalURL = (sslData, webDomain, location = currentLocation()) => {
|
||||
if (!location) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const configuredDomain = normalizeDomain(webDomain)
|
||||
if (!configuredDomain) {
|
||||
return location.href
|
||||
}
|
||||
|
||||
let target
|
||||
try {
|
||||
target = new URL(`https://${configuredDomain}`)
|
||||
} catch (error) {
|
||||
return location.href
|
||||
}
|
||||
|
||||
const proxyMode = isProxyTLSMode(sslData)
|
||||
const configuredPort = target.port
|
||||
const targetPort = Number(sslData?.httpsPort)
|
||||
if (!proxyMode) {
|
||||
target.port = targetPort > 0 && targetPort !== DEFAULT_HTTPS_PORT ? String(targetPort) : ''
|
||||
} else if (!isNonDefaultPort(target.protocol, configuredPort)) {
|
||||
target.port = ''
|
||||
}
|
||||
|
||||
target.pathname = location.pathname
|
||||
target.search = location.search
|
||||
target.hash = location.hash
|
||||
return target.toString()
|
||||
}
|
||||
|
||||
const createURL = (value, path) => {
|
||||
try {
|
||||
const url = new URL(path, value)
|
||||
url.search = ''
|
||||
url.hash = ''
|
||||
return url.toString()
|
||||
} catch (error) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export const getServiceProbeURLs = (sslData, webDomain, location = currentLocation()) => {
|
||||
if (!location) {
|
||||
return []
|
||||
}
|
||||
|
||||
const urls = []
|
||||
const add = (value, path) => {
|
||||
const url = createURL(value, path)
|
||||
if (url && !urls.includes(url)) {
|
||||
urls.push(url)
|
||||
}
|
||||
}
|
||||
|
||||
add(location.href, '/api/ping')
|
||||
add(location.href, '/api/v1/ping')
|
||||
|
||||
const finalURL = buildFinalURL(sslData, webDomain, location)
|
||||
if (finalURL) {
|
||||
add(finalURL, '/api/ping')
|
||||
add(finalURL, '/api/v1/ping')
|
||||
}
|
||||
return urls
|
||||
}
|
||||
|
||||
export const isServiceProbeSuccess = (response) => {
|
||||
if (!response) {
|
||||
return false
|
||||
}
|
||||
return response.status >= 200 && response.status < 300
|
||||
}
|
||||
|
||||
export const isServiceRedirect = (response, location = currentLocation()) => {
|
||||
if (!response || !location) {
|
||||
return false
|
||||
}
|
||||
if (response.status < REDIRECT_STATUS_MIN || response.status > REDIRECT_STATUS_MAX) {
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(response.url).origin === location.origin
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const probeService = async (sslData, webDomain, location = currentLocation()) => {
|
||||
if (!location || typeof fetch !== 'function') {
|
||||
return false
|
||||
}
|
||||
|
||||
const proxyMode = isProxyTLSMode(sslData)
|
||||
const urls = getServiceProbeURLs(sslData, webDomain, location)
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
cache: 'no-store',
|
||||
redirect: 'manual',
|
||||
headers: {
|
||||
Accept: 'application/json'
|
||||
}
|
||||
})
|
||||
if (isServiceProbeSuccess(response)) {
|
||||
return true
|
||||
}
|
||||
if (!proxyMode && isServiceRedirect(response, location)) {
|
||||
return true
|
||||
}
|
||||
} catch (error) {
|
||||
// 服务切换期间失败属于预期状态。
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user