@@ -824,15 +824,15 @@ <h3 class="text-lg font-semibold">导入 Token</h3>
824824 generateRandomToken = ( ) => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' ; let token = '' ; for ( let i = 0 ; i < 32 ; i ++ ) { token += chars . charAt ( Math . floor ( Math . random ( ) * chars . length ) ) } $ ( 'cfgPluginConnectionToken' ) . value = token ; showToast ( '随机Token已生成' , 'success' ) } ,
825825 toggleATAutoRefresh = async ( ) => { try { const enabled = $ ( 'atAutoRefreshToggle' ) . checked ; const r = await apiRequest ( '/api/token-refresh/enabled' , { method :'POST' , body :JSON . stringify ( { enabled :enabled } ) } ) ; if ( ! r ) { $ ( 'atAutoRefreshToggle' ) . checked = ! enabled ; return } const d = await r . json ( ) ; if ( d . success ) { showToast ( enabled ?'AT自动刷新已启用' :'AT自动刷新已禁用' , 'success' ) } else { showToast ( '操作失败: ' + ( d . detail || '未知错误' ) , 'error' ) ; $ ( 'atAutoRefreshToggle' ) . checked = ! enabled } } catch ( e ) { showToast ( '操作失败: ' + e . message , 'error' ) ; $ ( 'atAutoRefreshToggle' ) . checked = ! enabled } } ,
826826 loadATAutoRefreshConfig = async ( ) => { try { const r = await apiRequest ( '/api/token-refresh/config' ) ; if ( ! r ) return ; const d = await r . json ( ) ; if ( d . success && d . config ) { $ ( 'atAutoRefreshToggle' ) . checked = d . config . at_auto_refresh_enabled || false } else { console . error ( 'AT自动刷新配置数据格式错误:' , d ) } } catch ( e ) { console . error ( '加载AT自动刷新配置失败:' , e ) } } ,
827- formatLogStatus = l => { const statusText = ( l . status_text || '' ) . trim ( ) ; if ( statusText ) { const map = { started :'???' , token_selected :'?????' , token_ready :'??????' , project_ready :'?????' , uploading_images :'??????' , submitting_image :'???????????' , image_generated :'??????' , preparing_video :'??????' , submitting_video :'???????????' , video_submitted :'???????' , video_polling :'?????' , caching_image :'?????' , caching_video :'?????' , completed :'???' , failed :'??' , processing :'???' , upsampling_2k :'?????2K' , upsampling_4k :'?????4K' , upsampling_1080p :'?????1080P' } ; return map [ statusText ] || statusText } if ( l . status_code === 102 ) return '???' ; if ( l . status_code === 200 ) return '???' ; if ( l . status_code && l . status_code >= 400 ) return '??' ; return '-' } ,
827+ formatLogStatus = l => { const statusText = ( l . status_text || '' ) . trim ( ) ; if ( statusText ) { const map = { started :'\u5df2\u542f\u52a8' , token_selected :'\u5df2\u9009\u4e2d\u8d26\u53f7' , token_ready :'\u51c6\u5907\u751f\u6210\u73af\u5883' , project_ready :'\u9879\u76ee\u5df2\u5c31\u7eea' , uploading_images :'\u4e0a\u4f20\u53c2\u8003\u56fe\u4e2d' , submitting_image :'\u83b7\u53d6\u6253\u7801\u5e76\u63d0\u4ea4\u56fe\u7247\u4efb\u52a1' , image_generated :'\u56fe\u7247\u751f\u6210\u5b8c\u6210' , preparing_video :'\u51c6\u5907\u89c6\u9891\u4efb\u52a1' , submitting_video :'\u83b7\u53d6\u6253\u7801\u5e76\u63d0\u4ea4\u89c6\u9891\u4efb\u52a1' , video_submitted :'\u89c6\u9891\u4efb\u52a1\u5df2\u63d0\u4ea4' , video_polling :'\u89c6\u9891\u751f\u6210\u4e2d' , caching_image :'\u7f13\u5b58\u56fe\u7247\u4e2d' , caching_video :'\u7f13\u5b58\u89c6\u9891\u4e2d' , completed :'\u5df2\u5b8c\u6210' , failed :'\u5931\u8d25' , processing :'\u5904\u7406\u4e2d' , upsampling_2k :'\u6b63\u5728\u653e\u5927\u52302K' , upsampling_4k :'\u6b63\u5728\u653e\u5927\u52304K' , upsampling_1080p :'\u6b63\u5728\u653e\u5927\u52301080P' } ; return map [ statusText ] || statusText } if ( l . status_code === 102 ) return '\u5904\u7406\u4e2d' ; if ( l . status_code === 200 ) return '\u5df2\u5b8c\u6210' ; if ( l . status_code && l . status_code >= 400 ) return '\u5931\u8d25' ; return '-' } ,
828828 formatLogStatusClass = l => { const statusText = formatLogStatus ( l ) ; if ( statusText === '\u5904\u7406\u4e2d' ) return 'bg-amber-50 text-amber-700' ; if ( statusText === '\u5df2\u5b8c\u6210' ) return 'bg-green-50 text-green-700' ; if ( statusText === '\u5931\u8d25' ) return 'bg-red-50 text-red-700' ; return 'bg-gray-100 text-gray-700' } ,
829829 formatLogProgress = l => { if ( l . progress === null || l . progress === undefined || l . progress === '' ) return '-' ; const progress = Number ( l . progress ) ; return Number . isFinite ( progress ) ?`${ Math . max ( 0 , Math . min ( 100 , progress ) ) } %` :'-' } ,
830830 startLogsAutoRefresh = ( ) => { if ( window . logsAutoRefreshTimer ) return ; window . logsAutoRefreshTimer = setInterval ( ( ) => { const panel = $ ( 'panelLogs' ) ; if ( panel && ! panel . classList . contains ( 'hidden' ) ) loadLogs ( ) } , 3000 ) } ,
831831 stopLogsAutoRefresh = ( ) => { if ( window . logsAutoRefreshTimer ) { clearInterval ( window . logsAutoRefreshTimer ) ; window . logsAutoRefreshTimer = null } } ,
832- loadLogs = async ( ) => { try { const r = await apiRequest ( '/api/logs?limit=100' ) ; if ( ! r ) return ; const logs = await r . json ( ) ; window . allLogs = logs ; window . logDetailCache = window . logDetailCache || Object . create ( null ) ; const tb = $ ( 'logsTableBody' ) ; if ( ! logs . length ) { tb . innerHTML = '<tr><td colspan="8" class="py-8 px-3 text-center text-sm text-muted-foreground">\u6682\u65e0\u65e5\u5fd7</td></tr>' ; return } tb . innerHTML = logs . map ( l => { const statusText = formatLogStatus ( l ) , progressText = formatLogProgress ( l ) , statusCodeClass = l . status_code === 102 ?'bg-amber-50 text-amber-700' :( l . status_code === 200 ?'bg-green-50 text-green-700' :'bg-red-50 text-red-700' ) ; return `<tr><td class="py-2.5 px-3">${ escapeLogHtml ( l . operation || '-' ) } </td><td class="py-2.5 px-3"><span class="text-xs ${ l . token_email ?'text-blue-600' :'text-muted-foreground' } ">${ escapeLogHtml ( l . token_email || '\u672a\u77e5' ) } </span></td><td class="py-2.5 px-3"><span class="inline-flex items-center rounded px-2 py-0.5 text-xs ${ formatLogStatusClass ( l ) } ">${ escapeLogHtml ( statusText ) } </span></td><td class="py-2.5 px-3 text-xs">${ escapeLogHtml ( progressText ) } </td><td class="py-2.5 px-3"><span class="inline-flex items-center rounded px-2 py-0.5 text-xs ${ statusCodeClass } ">${ escapeLogHtml ( l . status_code ?? '-' ) } </span></td><td class="py-2.5 px-3">${ Number ( l . duration || 0 ) . toFixed ( 2 ) } </td><td class="py-2.5 px-3 text-xs text-muted-foreground">${ l . created_at ?new Date ( l . created_at ) . toLocaleString ( 'zh-CN' ) :'-' } </td><td class="py-2.5 px-3"><button onclick="showLogDetail(${ l . id } )" class="inline-flex items-center justify-center rounded-md hover:bg-blue-50 hover:text-blue-700 h-7 px-2 text-xs">详情</button></td></tr>` } ) . join ( '' ) } catch ( e ) { console . error ( 'load logs failed:' , e ) ; showToast ( '\u52a0\u8f7d\u65e5\u5fd7\u5931\u8d25: ' + e . message , 'error' ) } } ,
832+ loadLogs = async ( ) => { try { const r = await apiRequest ( '/api/logs?limit=100' ) ; if ( ! r ) return ; const logs = await r . json ( ) ; window . allLogs = logs ; window . logDetailCache = window . logDetailCache || Object . create ( null ) ; window . logListMeta = Object . create ( null ) ; logs . forEach ( l => window . logListMeta [ l . id ] = { updated_at :l . updated_at , created_at :l . created_at , status_code :l . status_code , progress :l . progress , status_text :l . status_text } ) ; Object . keys ( window . logDetailCache ) . forEach ( key => { const meta = window . logListMeta [ key ] ; const cached = window . logDetailCache [ key ] ; if ( ! meta || ! cached || cached . updated_at !== meta . updated_at ) delete window . logDetailCache [ key ] } ) ; const tb = $ ( 'logsTableBody' ) ; if ( ! logs . length ) { tb . innerHTML = '<tr><td colspan="8" class="py-8 px-3 text-center text-sm text-muted-foreground">\u6682\u65e0\u65e5\u5fd7</td></tr>' ; return } tb . innerHTML = logs . map ( l => { const statusText = formatLogStatus ( l ) , progressText = formatLogProgress ( l ) , statusCodeClass = l . status_code === 102 ?'bg-amber-50 text-amber-700' :( l . status_code === 200 ?'bg-green-50 text-green-700' :'bg-red-50 text-red-700' ) ; return `<tr><td class="py-2.5 px-3">${ escapeLogHtml ( l . operation || '-' ) } </td><td class="py-2.5 px-3"><span class="text-xs ${ l . token_email ?'text-blue-600' :'text-muted-foreground' } ">${ escapeLogHtml ( l . token_email || '\u672a\u77e5' ) } </span></td><td class="py-2.5 px-3"><span class="inline-flex items-center rounded px-2 py-0.5 text-xs ${ formatLogStatusClass ( l ) } ">${ escapeLogHtml ( statusText ) } </span></td><td class="py-2.5 px-3 text-xs">${ escapeLogHtml ( progressText ) } </td><td class="py-2.5 px-3"><span class="inline-flex items-center rounded px-2 py-0.5 text-xs ${ statusCodeClass } ">${ escapeLogHtml ( l . status_code ?? '-' ) } </span></td><td class="py-2.5 px-3">${ Number ( l . duration || 0 ) . toFixed ( 2 ) } </td><td class="py-2.5 px-3 text-xs text-muted-foreground">${ l . created_at ?new Date ( l . created_at ) . toLocaleString ( 'zh-CN' ) :'-' } </td><td class="py-2.5 px-3"><button onclick="showLogDetail(${ l . id } )" class="inline-flex items-center justify-center rounded-md hover:bg-blue-50 hover:text-blue-700 h-7 px-2 text-xs">详情</button></td></tr>` } ) . join ( '' ) ; if ( window . activeLogDetailId && window . logListMeta [ window . activeLogDetailId ] ) await showLogDetail ( window . activeLogDetailId ) } catch ( e ) { console . error ( 'load logs failed:' , e ) ; showToast ( '\u52a0\u8f7d\u65e5\u5fd7\u5931\u8d25: ' + e . message , 'error' ) } } ,
833833 refreshLogs = async ( ) => { await loadLogs ( ) } ,
834834 clearAllLogs = async ( ) => { if ( ! confirm ( '确定要清空所有日志吗?此操作不可恢复!' ) ) return ; try { const r = await apiRequest ( '/api/logs' , { method :'DELETE' } ) ; if ( ! r ) return ; const d = await r . json ( ) ; if ( d . success ) { window . logDetailCache = Object . create ( null ) ; window . activeLogDetailId = null ; resetLogMediaCache ( ) ; showToast ( '日志已清空' , 'success' ) ; await loadLogs ( ) } else { showToast ( '清空失败: ' + ( d . message || '未知错误' ) , 'error' ) } } catch ( e ) { showToast ( '清空失败: ' + e . message , 'error' ) } } ,
835- showLogDetail = async ( logId ) => { const modal = $ ( 'logDetailModal' ) ; const content = $ ( 'logDetailContent' ) ; window . logDetailCache = window . logDetailCache || Object . create ( null ) ; window . logDetailRequestSeq = ( window . logDetailRequestSeq || 0 ) + 1 ; const requestSeq = window . logDetailRequestSeq ; window . activeLogDetailId = logId ; modal . classList . remove ( 'hidden' ) ; content . innerHTML = '<div class="rounded-md border border-border p-4 bg-muted/30 text-sm text-muted-foreground">日志详情加载中...</div>' ; try { let log = window . logDetailCache [ logId ] ; if ( ! log ) { const r = await apiRequest ( `/api/logs/${ logId } ` ) ; if ( ! r ) return ; if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; if ( r . status === 404 ) { content . innerHTML = '<div class="rounded-md border border-red-200 p-4 bg-red-50 text-sm text-red-700">日志不存在或已被删除</div>' ; return } log = await r . json ( ) ; window . logDetailCache [ logId ] = log } if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; renderLogDetail ( log ) } catch ( e ) { if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; console . error ( '加载日志详情失败:' , e ) ; content . innerHTML = `<div class="rounded-md border border-red-200 p-4 bg-red-50 text-sm text-red-700">加载日志详情失败: ${ escapeLogHtml ( e . message || '未知错误' ) } </div>` ; showToast ( '加载日志详情失败: ' + e . message , 'error' ) } } ,
835+ showLogDetail = async ( logId ) => { const modal = $ ( 'logDetailModal' ) ; const content = $ ( 'logDetailContent' ) ; window . logDetailCache = window . logDetailCache || Object . create ( null ) ; window . logListMeta = window . logListMeta || Object . create ( null ) ; window . logDetailRequestSeq = ( window . logDetailRequestSeq || 0 ) + 1 ; const requestSeq = window . logDetailRequestSeq ; window . activeLogDetailId = logId ; modal . classList . remove ( 'hidden' ) ; content . innerHTML = '<div class="rounded-md border border-border p-4 bg-muted/30 text-sm text-muted-foreground">\u65e5\u5fd7\u8be6\u60c5\u52a0\u8f7d\u4e2d...</div>' ; try { const meta = window . logListMeta [ logId ] ; let log = window . logDetailCache [ logId ] ; if ( ! log || ! meta || log . updated_at !== meta . updated_at ) { const r = await apiRequest ( `/api/logs/${ logId } ` ) ; if ( ! r ) return ; if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; if ( r . status === 404 ) { content . innerHTML = '<div class="rounded-md border border-red-200 p-4 bg-red-50 text-sm text-red-700">\u65e5\u5fd7\u4e0d\u5b58\u5728\u6216\u5df2\u88ab\u5220\u9664</div>' ; return } log = await r . json ( ) ; window . logDetailCache [ logId ] = log } if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; renderLogDetail ( log ) } catch ( e ) { if ( window . logDetailRequestSeq !== requestSeq || window . activeLogDetailId !== logId || modal . classList . contains ( 'hidden' ) ) return ; console . error ( '\u52a0\u8f7d\u65e5\u5fd7\u8be6\u60c5\u5931\u8d25:' , e ) ; content . innerHTML = `<div class="rounded-md border border-red-200 p-4 bg-red-50 text-sm text-red-700">\u52a0\u8f7d\u65e5\u5fd7\u8be6\u60c5\u5931\u8d25: ${ escapeLogHtml ( e . message || '\u672a\u77e5\u9519\u8bef' ) } </div>` ; showToast ( '\u52a0\u8f7d\u65e5\u5fd7\u8be6\u60c5\u5931\u8d25: ' + e . message , 'error' ) } } ,
836836 closeLogDetailModal = ( ) => { window . activeLogDetailId = null ; resetLogMediaCache ( ) ; $ ( 'logDetailModal' ) . classList . add ( 'hidden' ) } ,
837837 showToast = ( m , t = 'info' ) => { const d = document . createElement ( 'div' ) , bc = { success :'bg-green-600' , error :'bg-destructive' , info :'bg-primary' } ; d . className = `fixed bottom-4 right-4 ${ bc [ t ] || bc . info } text-white px-4 py-2.5 rounded-lg shadow-lg text-sm font-medium z-50 animate-slide-up` ; d . textContent = m ; document . body . appendChild ( d ) ; setTimeout ( ( ) => { d . style . opacity = '0' ; d . style . transition = 'opacity .3s' ; setTimeout ( ( ) => d . parentNode && document . body . removeChild ( d ) , 300 ) } , 2000 ) } ,
838838 logout = ( ) => { if ( ! confirm ( '确定要退出登录吗?' ) ) return ; localStorage . removeItem ( 'adminToken' ) ; location . href = '/login' } ,
0 commit comments