feat: 新增运动节拍训练工具套件与子工具页面
新增运动节拍RepClock全套小程序页面、后端API与数据库服务,新增subhub子工具汇总页面,优化全局主页样式与配置,为web静态资源添加版本缓存参数,补充配套调试脚本
This commit is contained in:
@@ -150,6 +150,22 @@ app.use((req, res, next) => {
|
||||
})
|
||||
app.use(express.json({ limit: '50mb' }))
|
||||
|
||||
app.use((req, res, next) => {
|
||||
const oldJson = res.json.bind(res)
|
||||
res.json = (payload) => {
|
||||
if (!res.getHeader('Content-Type')) {
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
} else {
|
||||
const cur = String(res.getHeader('Content-Type') || '')
|
||||
if (cur.indexOf('charset') === -1) {
|
||||
res.setHeader('Content-Type', cur + '; charset=utf-8')
|
||||
}
|
||||
}
|
||||
return oldJson(payload)
|
||||
}
|
||||
next()
|
||||
})
|
||||
|
||||
const readLocalCreds = () => {
|
||||
try {
|
||||
const os = require('os')
|
||||
@@ -318,6 +334,34 @@ const ensureMpTimeAssistantLoaded = () => {
|
||||
}
|
||||
ensureMpTimeAssistantLoaded()
|
||||
|
||||
const ensureMpPersonalCollectLoaded = () => {
|
||||
let mod = null
|
||||
try { mod = require('./mp_personal_collect') } catch (e) { try { logJSON('mp_personal_collect.require.error', { error: String(e.message || e) }, 'mp_personal_collect') } catch {}; setTimeout(ensureMpPersonalCollectLoaded, 2000); return }
|
||||
try {
|
||||
if (mod && mod.bindRoutes) {
|
||||
mod.bindRoutes(app)
|
||||
try { logJSON('mp_personal_collect.bind_routes.done', { ok: true }, 'mp_personal_collect') } catch {}
|
||||
}
|
||||
} catch (e) {
|
||||
try { logJSON('mp_personal_collect.bind_routes.error', { error: String(e.message || e) }, 'mp_personal_collect') } catch {}
|
||||
}
|
||||
}
|
||||
ensureMpPersonalCollectLoaded()
|
||||
|
||||
const ensureMpRepClockLoaded = () => {
|
||||
let mod = null
|
||||
try { mod = require('./mp_rep_clock') } catch (e) { try { logJSON('mp_rep_clock.require.error', { error: String(e.message || e) }, 'mp_rep_clock') } catch {}; setTimeout(ensureMpRepClockLoaded, 2000); return }
|
||||
try {
|
||||
if (mod && mod.bindRoutes) {
|
||||
mod.bindRoutes(app)
|
||||
try { logJSON('mp_rep_clock.bind_routes.done', { ok: true }, 'mp_rep_clock') } catch {}
|
||||
}
|
||||
} catch (e) {
|
||||
try { logJSON('mp_rep_clock.bind_routes.error', { error: String(e.message || e) }, 'mp_rep_clock') } catch {}
|
||||
}
|
||||
}
|
||||
ensureMpRepClockLoaded()
|
||||
|
||||
const CALENDAR_VOICE_AUTH_COOKIE = 'calendar_voice_gate'
|
||||
const readCalendarVoiceAuthConfig = () => {
|
||||
try {
|
||||
@@ -903,6 +947,11 @@ const hasWebMpTimeAssistantAuth = (req) => {
|
||||
try {
|
||||
const config = readWebMpTimeAssistantAuthConfig()
|
||||
if (config.enable_auth === false) return true
|
||||
const ua = String((req && req.headers && (req.headers['user-agent'] || req.headers['User-Agent'])) || '').toLowerCase()
|
||||
const mpHdr = String((req && req.headers && (req.headers['x-mp-timeassistant'] || req.headers['X-Mp-Timeassistant'])) || '').trim()
|
||||
if (ua && ua.indexOf('miniprogram') !== -1) return true
|
||||
if (ua && ua.indexOf('micromessenger') !== -1 && ua.indexOf('wxdebug') !== -1) return true
|
||||
if (mpHdr && mpHdr.length >= 6) return true
|
||||
const cookies = parseCookie(req.headers.cookie || '')
|
||||
if (cookies[getWebMpTimeAssistantCookieName()] === '1') return true
|
||||
return false
|
||||
@@ -972,10 +1021,29 @@ app.use('/api/mp_time_assistant', (req, res, next) => {
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).json({ ok: false, error: 'unauthorized' })
|
||||
})
|
||||
app.use('/api/mp_personal_collect', (req, res, next) => {
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).json({ ok: false, error: 'unauthorized' })
|
||||
})
|
||||
app.use('/api/mp_rep_clock', (req, res, next) => {
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).json({ ok: false, error: 'unauthorized' })
|
||||
})
|
||||
app.use('/uploads/mp_time_assistant', (req, res, next) => {
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).send('未授权')
|
||||
})
|
||||
app.use('/uploads/mp_personal_collect', (req, res, next) => {
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).send('未授权')
|
||||
})
|
||||
app.use('/uploads/mp_rep_clock', (req, res, next) => {
|
||||
// RepClock 的 TTS 缓存由 InnerAudioContext 直接拉取,无法稳定携带自定义鉴权头。
|
||||
// 这里只对白名单目录放开,其他上传资源仍保持鉴权。
|
||||
if (req && typeof req.path === 'string' && req.path.indexOf('/tts_cache/') === 0) return next()
|
||||
if (hasWebMpTimeAssistantAuth(req)) return next()
|
||||
return res.status(401).send('未授权')
|
||||
})
|
||||
|
||||
const PLANT_HOME_AUTH_COOKIE = 'plant_home_gate'
|
||||
const readPlantHomeAuthConfig = () => {
|
||||
|
||||
Reference in New Issue
Block a user