const API_BASE = window.API_BASE || 'https://sms-api.ouai.nyc.mn' const app = document.getElementById('app') const state = { view: 'login', messages: [], logs: [], page: 1, total: 0, logsPage: 1, logsTotal: 0, currentMessage: null, fromNumbers: [], selectedFrom: '', search: '', stats: { total: 0, today: 0, failed: 0 }, startTs: 0, endTs: 0, } function monthRange(offset = 0) { const now = new Date() const y = now.getFullYear() const m = now.getMonth() + offset const start = new Date(y, m, 1, 0, 0, 0) const end = new Date(y, m + 1, 0, 23, 59, 59) return [start.getTime(), end.getTime()] } function initDefaultRange() { const [s, e] = monthRange(0) state.startTs = s state.endTs = e } async function api(path, options = {}) { const res = await fetch(`${API_BASE}${path}`, { credentials: 'include', headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }, ...options, }) return res.json() } function setView(view) { state.view = view render() } function nav(active) { return `
📱 短信转发接收端
` } function loginView() { return `

📱 短信转发接收端

` } function statsCards() { const s = state.stats return `
总短信
${s.total}
今日
${s.today}
异常
${s.failed}
` } function fromFilter() { const tags = state.fromNumbers.map(n => ` ${n} `).join('') return `
发送方筛选
全部 ${tags}
` } function timeFilter() { return `
时间筛选
` } function toolbar() { return `
` } function listView() { const rows = state.messages.map(m => ` ${m.id} ${m.from_number} ${m.content} ${new Date(m.timestamp).toLocaleString()} 详情 `).join('') return ` ${nav('list')} ${statsCards()} ${fromFilter()} ${timeFilter()} ${toolbar()}
${rows || ''}
ID号码内容时间
第 ${state.page} 页
` } function detailView() { const m = state.currentMessage if (!m) return '' return ` ${nav('list')}

📱 短信详情

ID
${m.id}
发送方号码
${m.from_number}
短信内容
${m.content}
原始时间戳
${m.timestamp}
入库时间
${new Date(m.created_at || m.timestamp).toLocaleString()}
签名验证
${m.sign_verified ? '已验证' : '未验证'}
设备信息
${m.device_info || '-'}
SIM 卡信息
${m.sim_info || '-'}
IP 地址
${m.ip_address || '-'}
` } function logsView() { const rows = state.logs.map(l => ` ${l.id} ${l.from_number} ${l.status} ${l.error_message || ''} ${new Date(l.timestamp).toLocaleString()} `).join('') return ` ${nav('logs')}
${rows || ''}
ID号码状态错误时间
第 ${state.logsPage} 页
` } function statsView() { return ` ${nav('stats')} ${statsCards()}

统计概览

总短信
${state.stats.total}
今日
${state.stats.today}
异常
${state.stats.failed}
` } async function loadStats() { const data = await api('/api/stats') if (data.success) state.stats = data.data } async function loadMessages() { const data = await api(`/api/messages?page=${state.page}&limit=20&from=${encodeURIComponent(state.selectedFrom)}&q=${encodeURIComponent(state.search)}&start_ts=${state.startTs}&end_ts=${state.endTs}`) if (!data.success) return state.messages = data.data state.total = data.total state.fromNumbers = data.from_numbers || [] } async function loadLogs() { const data = await api(`/api/logs?page=${state.logsPage}&limit=20`) if (!data.success) return state.logs = data.data state.logsTotal = data.total } async function render() { if (state.view === 'login') { app.innerHTML = loginView() document.getElementById('loginBtn').onclick = async () => { const username = document.getElementById('username').value const password = document.getElementById('password').value const res = await api('/api/auth/login', { method: 'POST', body: JSON.stringify({ username, password }) }) if (res.success) { initDefaultRange() state.view = 'list' await loadStats() await loadMessages() render() } else alert(res.error || '登录失败') } return } if (state.view === 'list') { await loadStats() await loadMessages() app.innerHTML = listView() bindCommon() document.querySelectorAll('.tag').forEach(tag => { tag.onclick = () => { state.selectedFrom = tag.dataset.from || ''; state.page = 1; render(); } }) document.getElementById('searchBtn').onclick = () => { state.search = document.getElementById('search').value; state.page = 1; render(); } document.getElementById('refreshBtn').onclick = () => render() document.getElementById('prevPage').onclick = () => { if (state.page>1) { state.page--; render(); } } document.getElementById('nextPage').onclick = () => { state.page++; render(); } document.getElementById('thisMonthBtn').onclick = () => { const [s,e] = monthRange(0); state.startTs=s; state.endTs=e; state.page=1; render(); } document.getElementById('lastMonthBtn').onclick = () => { const [s,e] = monthRange(-1); state.startTs=s; state.endTs=e; state.page=1; render(); } document.getElementById('allBtn').onclick = () => { state.startTs=0; state.endTs=0; state.page=1; render(); } document.querySelectorAll('[data-action="detail"]').forEach(btn => { btn.onclick = async () => { const id = btn.dataset.id const data = await api(`/api/messages/${id}`) if (data.success) { state.currentMessage = data.data; state.view = 'detail'; render(); } } }) return } if (state.view === 'detail') { app.innerHTML = detailView() bindCommon() document.getElementById('backBtn').onclick = () => { state.view = 'list'; render(); } return } if (state.view === 'logs') { await loadLogs() app.innerHTML = logsView() bindCommon() document.getElementById('prevLogs').onclick = () => { if (state.logsPage>1) { state.logsPage--; render(); } } document.getElementById('nextLogs').onclick = () => { state.logsPage++; render(); } return } if (state.view === 'stats') { await loadStats() app.innerHTML = statsView() bindCommon() return } } function bindCommon() { document.querySelectorAll('[data-view]').forEach(a => { a.onclick = () => setView(a.dataset.view) }) const logout = document.getElementById('logoutBtn') if (logout) logout.onclick = async () => { await api('/api/auth/logout', { method: 'POST' }); state.view='login'; render(); } } initDefaultRange() render()