diff --git a/miniprogram/README.md b/miniprogram/README.md index 5e52cb4..3b7994c 100644 --- a/miniprogram/README.md +++ b/miniprogram/README.md @@ -21,9 +21,10 @@ miniprogram/ │ └── server.js # 远端地址/端口/API 路径统一配置 ├── utils/ │ ├── core/ # 纯算法模块 -│ └── mp/ # 小程序 API 适配层 +│ └── mp/ # 小程序 API 适配层(含 route-manager) ├── assets/fonts.json # 字体清单(由脚本生成) ├── assets/default.json # 首次加载默认配置(内容/颜色/字号/默认字体) +├── assets/route-config.json # 手动切换 A/B 服务器配置 ├── app.js / app.json / app.wxss └── project.config.json ``` @@ -47,9 +48,30 @@ miniprogram/ - `apiPrefix`: API 前缀(默认 `/api`) - `fontsManifestPath`: 字体清单路径(默认 `/miniprogram/assets/fonts.json`) - `defaultConfigPath`: 默认配置路径(默认 `/miniprogram/assets/default.json`) +- `routeConfigPath`: 路由配置路径(默认 `/miniprogram/assets/route-config.json`) `app.js` 和 API 调用会自动使用该配置生成完整 URL。 +## 手动切换 A/B 服务器(无需发版) + +远端 `route-config.json`(A、B 都部署)示例: + +```json +{ + "active": "A", + "cooldownMinutes": 10, + "servers": { + "A": { "baseUrl": "https://fonts.biboer.cn" }, + "B": { "baseUrl": "https://mac.biboer.cn" } + } +} +``` + +- 冷启动时先读取当前服务器的 `route-config.json`。 +- 若发现 `active` 指向另一台服务器,会读取目标服务器配置做“双确认”。 +- 仅当目标服务器也返回相同 `active`,并且满足 `cooldownMinutes`,才切换。 +- 回前台会按 60 秒节流检查一次;API/配置请求失败时会触发一次兜底检查。 + ## 导出说明 - `SVG`:受微信限制,`shareFileMessage` 需由单次点击直接触发,建议逐个字体导出。 diff --git a/miniprogram/app.js b/miniprogram/app.js index 993d999..ac69e58 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -1,4 +1,5 @@ const { buildRuntimeConfig } = require('./config/server') +const { bootstrapRoute, checkRouteOnShow } = require('./utils/mp/route-manager') const runtimeConfig = buildRuntimeConfig() @@ -8,5 +9,20 @@ App({ apiTimeoutMs: 30000, fonts: null, defaultConfig: null, + routeReadyPromise: null, + }, + + onLaunch() { + this.globalData.routeReadyPromise = bootstrapRoute(this) + .catch((error) => { + console.warn('[app] 路由初始化失败,使用当前配置继续运行:', error) + return null + }) + }, + + onShow() { + checkRouteOnShow(this).catch((error) => { + console.warn('[app] 回前台路由检查失败:', error) + }) }, }) diff --git a/miniprogram/assets/route-config.json b/miniprogram/assets/route-config.json new file mode 100644 index 0000000..8f35c0d --- /dev/null +++ b/miniprogram/assets/route-config.json @@ -0,0 +1,12 @@ +{ + "active": "A", + "cooldownMinutes": 10, + "servers": { + "A": { + "baseUrl": "https://fonts.biboer.cn" + }, + "B": { + "baseUrl": "https://mac.biboer.cn" + } + } +} diff --git a/miniprogram/config/server.js b/miniprogram/config/server.js index 9c09435..2b23d96 100644 --- a/miniprogram/config/server.js +++ b/miniprogram/config/server.js @@ -9,6 +9,7 @@ const SERVER_CONFIG = { apiPrefix: '/api', fontsManifestPath: '/miniprogram/assets/fonts.json', defaultConfigPath: '/miniprogram/assets/default.json', + routeConfigPath: '/miniprogram/assets/route-config.json', } function buildOrigin() { @@ -33,16 +34,29 @@ function normalizePath(path, fallback) { return value.startsWith('/') ? value : `/${value}` } -function buildRuntimeConfig() { - const origin = buildOrigin() +function normalizeBaseUrl(baseUrl) { + const value = String(baseUrl || '').trim() + if (!value) { + return buildOrigin() + } + + const withProtocol = /^https?:\/\//i.test(value) ? value : `https://${value}` + return withProtocol.replace(/\/+$/, '') +} + +function buildRuntimeConfig(options = {}) { + const origin = normalizeBaseUrl(options.baseUrl) const apiPrefix = normalizePath(SERVER_CONFIG.apiPrefix, '/api').replace(/\/$/, '') const fontsManifestPath = normalizePath(SERVER_CONFIG.fontsManifestPath, '/miniprogram/assets/fonts.json') const defaultConfigPath = normalizePath(SERVER_CONFIG.defaultConfigPath, '/miniprogram/assets/default.json') + const routeConfigPath = normalizePath(SERVER_CONFIG.routeConfigPath, '/miniprogram/assets/route-config.json') return { + activeServerKey: String(options.activeServerKey || '').trim(), fontsBaseUrl: origin, fontsManifestUrl: `${origin}${fontsManifestPath}`, defaultConfigUrl: `${origin}${defaultConfigPath}`, + routeConfigUrl: `${origin}${routeConfigPath}`, svgRenderApiUrl: `${origin}${apiPrefix}/render-svg`, pngRenderApiUrl: `${origin}${apiPrefix}/render-png`, } diff --git a/miniprogram/utils/mp/font-loader.js b/miniprogram/utils/mp/font-loader.js index 9233346..2b93100 100644 --- a/miniprogram/utils/mp/font-loader.js +++ b/miniprogram/utils/mp/font-loader.js @@ -1,4 +1,5 @@ const { request, downloadFile, readFile } = require('./wx-promisify') +const { ensureRouteReady, checkRouteOnFailure } = require('./route-manager') const localFonts = require('../../assets/fonts') const localDefaultConfig = require('../../assets/default') @@ -111,6 +112,7 @@ function buildDefaultConfigUrl(manifestUrl, baseUrl) { async function loadFontsManifest(options = {}) { const app = getApp() + await ensureRouteReady(app) const manifestUrl = options.manifestUrl || app.globalData.fontsManifestUrl const baseUrl = options.baseUrl || app.globalData.fontsBaseUrl @@ -138,7 +140,11 @@ async function loadFontsManifest(options = {}) { return fonts } catch (error) { console.warn('远程字体清单加载失败,回退到本地清单:', error) - const fallbackFonts = normalizeManifest(localFonts, baseUrl) + await checkRouteOnFailure(app).catch((routeError) => { + console.warn('字体清单失败后的路由检查失败:', routeError) + }) + const effectiveBaseUrl = app.globalData.fontsBaseUrl || baseUrl + const fallbackFonts = normalizeManifest(localFonts, effectiveBaseUrl) app.globalData.fonts = fallbackFonts return fallbackFonts } @@ -146,6 +152,7 @@ async function loadFontsManifest(options = {}) { async function loadDefaultConfig(options = {}) { const app = getApp() + await ensureRouteReady(app) const manifestUrl = options.manifestUrl || app.globalData.fontsManifestUrl const baseUrl = options.baseUrl || app.globalData.fontsBaseUrl const defaultConfigUrl = options.defaultConfigUrl || @@ -172,6 +179,9 @@ async function loadDefaultConfig(options = {}) { return remoteConfig } catch (error) { console.warn('远程 default.json 加载失败,回退到本地默认配置:', error) + await checkRouteOnFailure(app).catch((routeError) => { + console.warn('default 配置失败后的路由检查失败:', routeError) + }) const fallbackConfig = normalizeDefaultConfig(localDefaultConfig) app.globalData.defaultConfig = fallbackConfig return fallbackConfig diff --git a/miniprogram/utils/mp/render-api.js b/miniprogram/utils/mp/render-api.js index 6ee881f..2dcf814 100644 --- a/miniprogram/utils/mp/render-api.js +++ b/miniprogram/utils/mp/render-api.js @@ -1,4 +1,5 @@ const { request } = require('./wx-promisify') +const { ensureRouteReady, checkRouteOnFailure } = require('./route-manager') function buildApiUrl() { const app = getApp() @@ -47,41 +48,50 @@ function decodeArrayBuffer(buffer) { async function renderSvgByApi(payload) { const app = getApp() + await ensureRouteReady(app) const timeout = app && app.globalData && app.globalData.apiTimeoutMs ? Number(app.globalData.apiTimeoutMs) : 30000 - const response = await request({ - url: buildApiUrl(), - method: 'POST', - timeout, - header: { - 'content-type': 'application/json', - }, - data: { - fontId: payload.fontId, - text: payload.text, - fontSize: payload.fontSize, - fillColor: payload.fillColor, - letterSpacing: payload.letterSpacing, - maxCharsPerLine: payload.maxCharsPerLine, - }, - }) + try { + const response = await request({ + url: buildApiUrl(), + method: 'POST', + timeout, + header: { + 'content-type': 'application/json', + }, + data: { + fontId: payload.fontId, + text: payload.text, + fontSize: payload.fontSize, + fillColor: payload.fillColor, + letterSpacing: payload.letterSpacing, + maxCharsPerLine: payload.maxCharsPerLine, + }, + }) - if (!response || response.statusCode < 200 || response.statusCode >= 300) { - throw new Error(`渲染服务请求失败,状态码: ${response && response.statusCode}`) + if (!response || response.statusCode < 200 || response.statusCode >= 300) { + throw new Error(`渲染服务请求失败,状态码: ${response && response.statusCode}`) + } + + const body = response.data || {} + if (!body.ok) { + throw new Error(body.error || '渲染服务返回错误') + } + + return normalizeResult(body.data) + } catch (error) { + await checkRouteOnFailure(app).catch((routeError) => { + console.warn('SVG 渲染失败后的路由检查失败:', routeError) + }) + throw error } - - const body = response.data || {} - if (!body.ok) { - throw new Error(body.error || '渲染服务返回错误') - } - - return normalizeResult(body.data) } async function renderPngByApi(payload) { const app = getApp() + await ensureRouteReady(app) const timeout = app && app.globalData && app.globalData.apiTimeoutMs ? Number(app.globalData.apiTimeoutMs) : 30000 @@ -93,39 +103,46 @@ async function renderPngByApi(payload) { : `${baseApiUrl.replace(/\/$/, '')}/render-png` ) - const response = await request({ - url: apiUrl, - method: 'POST', - timeout, - responseType: 'arraybuffer', - header: { - 'content-type': 'application/json', - accept: 'image/png', - }, - data: { - fontId: payload.fontId, - text: payload.text, - fontSize: payload.fontSize, - fillColor: payload.fillColor, - letterSpacing: payload.letterSpacing, - maxCharsPerLine: payload.maxCharsPerLine, - }, - }) + try { + const response = await request({ + url: apiUrl, + method: 'POST', + timeout, + responseType: 'arraybuffer', + header: { + 'content-type': 'application/json', + accept: 'image/png', + }, + data: { + fontId: payload.fontId, + text: payload.text, + fontSize: payload.fontSize, + fillColor: payload.fillColor, + letterSpacing: payload.letterSpacing, + maxCharsPerLine: payload.maxCharsPerLine, + }, + }) - if (!response || response.statusCode !== 200) { - let message = `PNG 渲染服务请求失败,状态码: ${response && response.statusCode}` - const maybeText = decodeArrayBuffer(response && response.data) - if (maybeText && maybeText.includes('error')) { - message = maybeText + if (!response || response.statusCode !== 200) { + let message = `PNG 渲染服务请求失败,状态码: ${response && response.statusCode}` + const maybeText = decodeArrayBuffer(response && response.data) + if (maybeText && maybeText.includes('error')) { + message = maybeText + } + throw new Error(message) } - throw new Error(message) - } - if (!(response.data instanceof ArrayBuffer)) { - throw new Error('PNG 渲染服务返回格式无效') - } + if (!(response.data instanceof ArrayBuffer)) { + throw new Error('PNG 渲染服务返回格式无效') + } - return response.data + return response.data + } catch (error) { + await checkRouteOnFailure(app).catch((routeError) => { + console.warn('PNG 渲染失败后的路由检查失败:', routeError) + }) + throw error + } } module.exports = { diff --git a/miniprogram/utils/mp/route-manager.js b/miniprogram/utils/mp/route-manager.js new file mode 100644 index 0000000..f00e858 --- /dev/null +++ b/miniprogram/utils/mp/route-manager.js @@ -0,0 +1,399 @@ +const { buildRuntimeConfig } = require('../../config/server') +const { request } = require('./wx-promisify') +const { loadRouteState, saveRouteState } = require('./storage') + +const ROUTE_CHECK_INTERVAL_MS = 60 * 1000 +const ROUTE_REQUEST_TIMEOUT_MS = 8000 +const FAILURE_CHECK_INTERVAL_MS = 15 * 1000 + +let inFlightCheckPromise = null +let lastFailureRouteCheckAt = 0 + +function normalizeServerKey(value) { + const key = String(value || '').trim().toUpperCase() + if (!key) { + return '' + } + return key +} + +function normalizeBaseUrl(value) { + const text = String(value || '').trim() + if (!text) { + return '' + } + + const withProtocol = /^https?:\/\//i.test(text) ? text : `https://${text}` + return withProtocol.replace(/\/+$/, '') +} + +function findServerKeyByBaseUrl(servers, baseUrl) { + const target = normalizeBaseUrl(baseUrl) + if (!target || !servers || typeof servers !== 'object') { + return '' + } + + const keys = Object.keys(servers) + for (const key of keys) { + const item = servers[key] + const candidate = normalizeBaseUrl(item && item.baseUrl) + if (candidate && candidate === target) { + return key + } + } + + return '' +} + +function normalizeRouteConfig(payload) { + if (!payload || typeof payload !== 'object') { + throw new Error('route-config.json 格式错误') + } + + const rawServers = payload.servers && typeof payload.servers === 'object' ? payload.servers : {} + const servers = {} + + Object.keys(rawServers).forEach((rawKey) => { + const key = normalizeServerKey(rawKey) + const baseUrl = normalizeBaseUrl(rawServers[rawKey] && rawServers[rawKey].baseUrl) + if (!key || !baseUrl) { + return + } + servers[key] = { baseUrl } + }) + + const keys = Object.keys(servers) + if (!keys.length) { + throw new Error('route-config.json 缺少 servers.baseUrl') + } + + let active = normalizeServerKey(payload.active) + if (!active || !servers[active]) { + active = keys[0] + } + + const parsedCooldown = Number(payload.cooldownMinutes) + const cooldownMinutes = Number.isFinite(parsedCooldown) + ? Math.max(0, Math.round(parsedCooldown)) + : 10 + + return { + active, + cooldownMinutes, + servers, + } +} + +function canSwitchByCooldown(lastSwitchAt, cooldownMinutes, now) { + if (!Number.isFinite(cooldownMinutes) || cooldownMinutes <= 0) { + return true + } + const switchAt = Number(lastSwitchAt) || 0 + if (!switchAt) { + return true + } + return now - switchAt >= cooldownMinutes * 60 * 1000 +} + +function getAppInstance(explicitApp) { + if (explicitApp && explicitApp.globalData) { + return explicitApp + } + try { + return getApp() + } catch (error) { + return null + } +} + +function applyRuntimeConfig(app, baseUrl, activeServerKey, options = {}) { + if (!app || !app.globalData) { + return null + } + + const runtimeConfig = buildRuntimeConfig({ + baseUrl, + activeServerKey, + }) + + const previousBaseUrl = app.globalData.fontsBaseUrl || '' + const switched = previousBaseUrl && previousBaseUrl !== runtimeConfig.fontsBaseUrl + + Object.assign(app.globalData, runtimeConfig) + + if (switched && options.resetDataCache !== false) { + app.globalData.fonts = null + app.globalData.defaultConfig = null + } + + return runtimeConfig +} + +async function fetchRouteConfig(baseUrl) { + const runtimeConfig = buildRuntimeConfig({ baseUrl }) + const response = await request({ + url: runtimeConfig.routeConfigUrl, + method: 'GET', + timeout: ROUTE_REQUEST_TIMEOUT_MS, + }) + + if (!response || response.statusCode < 200 || response.statusCode >= 300) { + throw new Error(`读取 route-config 失败,状态码: ${response && response.statusCode}`) + } + + return normalizeRouteConfig(response.data) +} + +function resolveCurrentServerKey(routeConfig, state, currentBaseUrl) { + if (!routeConfig || !routeConfig.servers) { + return '' + } + + const stateKey = normalizeServerKey(state && state.activeServerKey) + if (stateKey && routeConfig.servers[stateKey]) { + return stateKey + } + + const fromBase = findServerKeyByBaseUrl(routeConfig.servers, currentBaseUrl) + if (fromBase) { + return fromBase + } + + return normalizeServerKey(routeConfig.active) +} + +async function runRouteCheck(explicitApp, options = {}) { + const app = getAppInstance(explicitApp) + if (!app || !app.globalData) { + return { + switched: false, + reason: 'app_not_ready', + } + } + + const now = Date.now() + const force = Boolean(options.force) + const state = loadRouteState() + const lastRouteCheckAt = Number(state.lastRouteCheckAt) || 0 + + if (!force && now - lastRouteCheckAt < ROUTE_CHECK_INTERVAL_MS) { + return { + switched: false, + reason: 'interval_skip', + } + } + + const currentBaseUrl = normalizeBaseUrl(app.globalData.fontsBaseUrl) + || buildRuntimeConfig().fontsBaseUrl + + let currentRouteConfig + try { + currentRouteConfig = await fetchRouteConfig(currentBaseUrl) + } catch (error) { + console.warn(`[route-manager] 读取当前路由配置失败(${options.reason || 'unknown'}):`, error) + saveRouteState({ + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'fetch_current_failed', + error, + } + } + + const currentServerKey = resolveCurrentServerKey(currentRouteConfig, state, currentBaseUrl) + const desiredServerKey = normalizeServerKey(currentRouteConfig.active) + const desiredServer = currentRouteConfig.servers[desiredServerKey] + + if (!currentServerKey || !desiredServerKey || !desiredServer) { + saveRouteState({ + routeConfigCache: currentRouteConfig, + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'invalid_server_key', + } + } + + if (desiredServerKey === currentServerKey) { + const stableBaseUrl = currentRouteConfig.servers[currentServerKey].baseUrl + applyRuntimeConfig(app, stableBaseUrl, currentServerKey, { resetDataCache: false }) + saveRouteState({ + activeServerKey: currentServerKey, + routeConfigCache: currentRouteConfig, + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'already_active', + activeServerKey: currentServerKey, + } + } + + let targetRouteConfig + try { + targetRouteConfig = await fetchRouteConfig(desiredServer.baseUrl) + } catch (error) { + console.warn(`[route-manager] 双确认读取目标配置失败(${currentServerKey}->${desiredServerKey}):`, error) + saveRouteState({ + activeServerKey: currentServerKey, + routeConfigCache: currentRouteConfig, + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'fetch_target_failed', + error, + } + } + + if (normalizeServerKey(targetRouteConfig.active) !== desiredServerKey) { + console.warn( + `[route-manager] 双确认未通过: 当前=${currentServerKey}, 期望=${desiredServerKey}, 目标返回=${targetRouteConfig.active}`, + ) + saveRouteState({ + activeServerKey: currentServerKey, + routeConfigCache: currentRouteConfig, + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'double_confirm_rejected', + } + } + + if (!canSwitchByCooldown(state.lastSwitchAt, currentRouteConfig.cooldownMinutes, now)) { + console.info( + `[route-manager] cooldown 未到,保留当前服务器 ${currentServerKey} (cooldownMinutes=${currentRouteConfig.cooldownMinutes})`, + ) + saveRouteState({ + activeServerKey: currentServerKey, + routeConfigCache: currentRouteConfig, + lastRouteCheckAt: now, + }) + return { + switched: false, + reason: 'cooldown_blocked', + } + } + + applyRuntimeConfig(app, desiredServer.baseUrl, desiredServerKey) + saveRouteState({ + activeServerKey: desiredServerKey, + routeConfigCache: targetRouteConfig, + lastSwitchAt: now, + lastRouteCheckAt: now, + }) + + console.info(`[route-manager] 已切换路由 ${currentServerKey} -> ${desiredServerKey}`) + + return { + switched: true, + reason: 'switched', + from: currentServerKey, + to: desiredServerKey, + } +} + +function checkRoute(explicitApp, options = {}) { + if (inFlightCheckPromise) { + return inFlightCheckPromise + } + + inFlightCheckPromise = runRouteCheck(explicitApp, options) + .catch((error) => { + console.warn('[route-manager] 路由检查异常:', error) + return { + switched: false, + reason: 'unexpected_error', + error, + } + }) + .finally(() => { + inFlightCheckPromise = null + }) + + return inFlightCheckPromise +} + +function hydrateRuntimeFromState(explicitApp) { + const app = getAppInstance(explicitApp) + if (!app || !app.globalData) { + return null + } + + const state = loadRouteState() + const fallbackRuntime = buildRuntimeConfig() + const cachedRouteConfig = state && state.routeConfigCache && typeof state.routeConfigCache === 'object' + ? state.routeConfigCache + : null + const activeServerKey = normalizeServerKey(state && state.activeServerKey) + + let baseUrl = fallbackRuntime.fontsBaseUrl + if (cachedRouteConfig && cachedRouteConfig.servers && activeServerKey && cachedRouteConfig.servers[activeServerKey]) { + const cachedBaseUrl = normalizeBaseUrl(cachedRouteConfig.servers[activeServerKey].baseUrl) + if (cachedBaseUrl) { + baseUrl = cachedBaseUrl + } + } + + return applyRuntimeConfig(app, baseUrl, activeServerKey, { resetDataCache: false }) +} + +function bootstrapRoute(explicitApp) { + const app = getAppInstance(explicitApp) + if (!app || !app.globalData) { + return Promise.resolve(null) + } + + hydrateRuntimeFromState(app) + return checkRoute(app, { + force: true, + reason: 'launch', + }) +} + +function ensureRouteReady(explicitApp) { + const app = getAppInstance(explicitApp) + if (!app || !app.globalData) { + return Promise.resolve() + } + + const promise = app.globalData.routeReadyPromise + if (promise && typeof promise.then === 'function') { + return promise.catch(() => null) + } + return Promise.resolve() +} + +function checkRouteOnShow(explicitApp) { + return checkRoute(explicitApp, { + force: false, + reason: 'foreground', + }) +} + +function checkRouteOnFailure(explicitApp) { + const now = Date.now() + if (now - lastFailureRouteCheckAt < FAILURE_CHECK_INTERVAL_MS) { + return Promise.resolve({ + switched: false, + reason: 'failure_interval_skip', + }) + } + + lastFailureRouteCheckAt = now + return checkRoute(explicitApp, { + force: true, + reason: 'failure', + }) +} + +module.exports = { + bootstrapRoute, + ensureRouteReady, + checkRouteOnShow, + checkRouteOnFailure, + checkRoute, +} diff --git a/miniprogram/utils/mp/storage.js b/miniprogram/utils/mp/storage.js index c330f29..9544219 100644 --- a/miniprogram/utils/mp/storage.js +++ b/miniprogram/utils/mp/storage.js @@ -1,6 +1,7 @@ const STORAGE_KEYS = { APP_STATE: 'font2svg:app-state', FAVORITES: 'font2svg:favorites', + ROUTE_STATE: 'font2svg:route-state', } function getStorage(key, fallbackValue) { @@ -49,6 +50,21 @@ function saveFavorites(favorites) { return unique } +function loadRouteState() { + return getStorage(STORAGE_KEYS.ROUTE_STATE, {}) +} + +function saveRouteState(partialState) { + const current = loadRouteState() + const next = { + ...current, + ...partialState, + updatedAt: Date.now(), + } + setStorage(STORAGE_KEYS.ROUTE_STATE, next) + return next +} + module.exports = { STORAGE_KEYS, getStorage, @@ -57,4 +73,6 @@ module.exports = { saveAppState, loadFavorites, saveFavorites, + loadRouteState, + saveRouteState, } diff --git a/scripts/deploy-fonts.sh b/scripts/deploy-fonts.sh index 098d090..8712795 100755 --- a/scripts/deploy-fonts.sh +++ b/scripts/deploy-fonts.sh @@ -14,6 +14,7 @@ LOCAL_WEB_FONTS_JSON="frontend/public/fonts.json" LOCAL_WEB_DEFAULT_JSON="frontend/public/default.json" LOCAL_MP_FONTS_JSON="miniprogram/assets/fonts.json" LOCAL_MP_DEFAULT_JSON="miniprogram/assets/default.json" +LOCAL_MP_ROUTE_CONFIG_JSON="miniprogram/assets/route-config.json" # 颜色输出 RED='\033[0;31m' @@ -91,6 +92,11 @@ upload_miniprogram_config() { else log_warn "未找到小程序 default.json,已跳过: $LOCAL_MP_DEFAULT_JSON" fi + if [ -f "$LOCAL_MP_ROUTE_CONFIG_JSON" ]; then + scp "$LOCAL_MP_ROUTE_CONFIG_JSON" "$SERVER:$REMOTE_MP_ASSETS_DIR/route-config.json" + else + log_warn "未找到小程序 route-config.json,已跳过: $LOCAL_MP_ROUTE_CONFIG_JSON" + fi log_info "小程序配置上传完成" } @@ -105,6 +111,7 @@ verify_deployment() { # 检查小程序 fonts.json MP_HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://fonts.biboer.cn/miniprogram/assets/fonts.json") + MP_ROUTE_HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://fonts.biboer.cn/miniprogram/assets/route-config.json") if [ "$MP_HTTP_CODE" = "200" ]; then log_info "小程序 fonts.json 可访问 ✓" @@ -114,6 +121,12 @@ verify_deployment() { exit 1 fi + if [ "$MP_ROUTE_HTTP_CODE" = "200" ]; then + log_info "小程序 route-config.json 可访问 ✓" + else + log_warn "小程序 route-config.json 不可访问 (HTTP $MP_ROUTE_HTTP_CODE)" + fi + WEB_HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://fonts.biboer.cn/fonts.json") if [ "$WEB_HTTP_CODE" = "200" ]; then log_info "Web fonts.json 可访问 ✓"