PageSourceSearch

https://www.vicoco.cn/assets/js/index-CaFYeZkC.js

js vicoco.cn collected 2026-10-06 14:15:01 UTC 14,798 bytes, 1 lines download raw bytes

1import{r as n,j as e}from"./vendor-CWbTxbDJ.js";import{u as ke}from"./useAuthCheck-L6805PrT.js";import{A as we}from"./index-CKOktoVJ.js";import{aj as ce,ak as le,al as $e,am as Re,an as Fe,ao as Ae,ap as Ce,aq as Te}from"./projectApi-2m0MUXrH.js";import{c as Le}from"./index-CE0gaqa4.js";import"./ui-vendor-BVdwggPR.js";const De="_pageWrapper_1twpy_1",Ie="_card_1twpy_12",Je="_sectionTitle_1twpy_19",Pe="_muted_1twpy_25",Ee="_btnRow_1twpy_29",He="_btnPrimary_1twpy_35",Me="_btnSecondary_1twpy_36",Oe="_btnDanger_1twpy_37",We="_input_1twpy_62",Ue="_grid2_1twpy_75",qe="_pre_1twpy_87",t={pageWrapper:De,card:Ie,sectionTitle:Je,muted:Pe,btnRow:Ee,btnPrimary:He,btnSecondary:Me,btnDanger:Oe,input:We,grid2:Ue,pre:qe},Ke=l=>{try{return JSON.stringify(l,null,2)}catch{return String(l)}},M=l=>{const c=Number(l);if(!Number.isFinite(c)||c<=0)return"-";const m=new Date(c*1e3);return Number.isNaN(m.getTime())?"-":m.toLocaleString()},O=l=>{const c=Number(l);return Number.isFinite(c)?c<60?`${c.toFixed(0)} 秒前`:c<3600?`${(c/60).toFixed(1)} 分钟前`:c<86400?`${(c/3600).toFixed(1)} 小时前`:`${(c/86400).toFixed(1)} 天前`:"-"},r=({label:l,value:c,tone:m="muted"})=>{const _=m==="ok"?"text-green-700":m==="warn"?"text-amber-700":m==="bad"?"text-red-700":"text-text-primary";return e.jsxs("div",{className:"flex items-start justify-between gap-3 py-1.5 border-b border-dashed border-neutral-200 last:border-0",children:[e.jsx("span",{className:"text-xs text-text-secondary shrink-0 pt-0.5",children:l}),e.jsx("span",{className:`text-sm text-right break-all ${_}`,children:c})]})},A=({value:l,title:c="原始数据(JSON)"})=>e.jsxs("details",{className:"mt-2",children:[e.jsx("summary",{className:"cursor-pointer text-xs text-text-secondary",children:c}),e.jsx("pre",{className:t.pre,children:Ke(l)})]}),W=({text:l})=>l?e.jsx("div",{className:"mt-2 rounded-lg bg-red-50 border border-red-200 text-red-700 text-xs p-2 break-all",children:l}):null,Ye=()=>{ke(!0,"/admin-dashboard",!0,"admin");const[l,c]=n.useState(!0),[m,_]=n.useState(null),[C,U]=n.useState(""),[g,q]=n.useState(null),[p,K]=n.useState(null),[S,ie]=n.useState(7),[T,L]=n.useState(null),[k,y]=n.useState({}),[de,z]=n.useState(!1),[B,oe]=n.useState(0),[G,ue]=n.useState(""),[Q,xe]=n.useState(!0),[D,V]=n.useState(!1),[X,me]=n.useState(3),[Y,he]=n.useState(!1),[I,Z]=n.useState(!1),[ee,ge]=n.useState(600),[J,se]=n.useState(!1),[b,w]=n.useState(""),[te,f]=n.useState(!1),[i,$]=n.useState(null),ae=n.useMemo(()=>String(C||"")==="slave",[C]),h=(s,a)=>{_({type:s,text:a})},j=s=>String(s?.message||s||"未知错误"),N=async()=>{z(!0);const s=$e().then(u=>{q(u),oe(Date.now()),y(x=>({...x,status:void 0}))}).catch(u=>{q(null),y(x=>({...x,status:j(u)}))}),a=le(S).then(u=>{L(u),y(x=>({...x,history:void 0}))}).catch(u=>{L(null),y(x=>({...x,history:j(u)}))}),H=Re(500).then(u=>{K(u),y(x=>({...x,pending:void 0}))}).catch(u=>{K(null),y(x=>({...x,pending:j(u)}))}).finally(()=>z(!1));await Promise.allSettled([s,a,H])};n.useEffect(()=>{let s=!1;return(async()=>{try{const a=await fetch(Le("/api/hybrid/status/"),{credentials:"include"}),H=a.ok?await a.json():null;s||U(String(H?.role||""))}catch{s||U("")}try{await N()}catch(a){s||h("error",a?.message||"加载失败")}finally{s||c(!1)}})(),()=>{s=!0}},[]),n.useEffect(()=>{let s=null;if(b)return s=setInterval(async()=>{try{f(!0);const a=await ce(b);$(a),a?.status&&["success","failed","canceled","error"].includes(String(a.status))&&(w(""),f(!1),await N())}catch{}finally{f(!1)}},2e3),()=>clearInterval(s)},[b]);const be=async()=>{if(!D){V(!0);try{const s=await Fe({reason:G,async:Q}),a=String(s?.job_id||s?.jobId||s?.id||"");a&&w(a),$(s),h("success","已触发同步任务"),await N()}catch(s){h("error",`触发同步失败:${j(s)}`)}finally{V(!1)}}},je=async()=>{if(b){f(!0);try{await Ae(b),h("success","已请求取消任务");const s=await ce(b);$(s),w(""),await N()}catch(s){h("error",`取消失败:${j(s)}`)}finally{f(!1)}}},pe=async()=>{if(!J){se(!0);try{const s=await Ce(ee),a=s?.result||{};h("success",a.cleared?`已清理陈旧锁(已存在 ${O(a.age_seconds)})`:s?.message||`未清理:${a.reason||"锁不存在或还未过期"}`),await N()}catch(s){h("error",`处理锁失败:${j(s)}`)}finally{se(!1)}}},ye=async()=>
1{if(!I){Z(!0);try{const s=await Te({days:X,checksum:Y}),a=String(s?.job_id||s?.jobId||s?.id||"");a&&w(a),$(s),h("success","已触发对账/补偿任务"),await N()}catch(s){h("error",`触发对账失败:${j(s)}`)}finally{Z(!1)}}},o=g?.state||null,P=g?.config||null,v=g?.background||null,d=o?.last_sync_record||null,R=g?.heartbeat_age_seconds,ne=o?.heartbeat_source||"",Ne=typeof R=="number"&&R<=180,ve=o?typeof R=="number"?`${O(R)}(来源:${ne||"未知"})`:`${ne||"未知"}(无心跳时间)`:"没有任何同步记录(自动同步服务可能从未运行)",fe=g?.lock?`被占用 ${O(g?.lock_age_seconds)}(pid=${g?.lock?.pid??"-"})`:"空闲",E=Array.isArray(o?.config_errors)?o.config_errors:[],_e=v?v.running?"正在同步(上传/部署触发的)":v.last_trigger_result?`上次 ${v.last_trigger_result.success?"成功":"失败"} · ${v.last_trigger_result.files_count??0} 个文件`:v.skipped_reason?`已跳过:${v.skipped_reason}`:"待命":"-",Se=o?.auto_trigger_last_skip?`${o.auto_trigger_last_skip.reason} · ${M(o.auto_trigger_last_skip.at)}`:"无",re=Array.isArray(T?.rows)?T.rows:[],F=Array.isArray(p?.files)?p.files:[];return e.jsx("div",{className:t.pageWrapper,children:e.jsxs("div",{className:"flex",children:[e.jsx(we,{}),e.jsx("main",{className:"admin-main-content min-h-screen flex-1 p-6",children:e.jsxs("div",{className:"max-w-6xl mx-auto space-y-4",children:[e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-text-primary",children:"文件同步救援"}),e.jsx("p",{className:"text-text-secondary mt-1",children:"主站触发 rsync 同步与查看同步状态(User_File → 从站)。自动同步脚本与后台页面共用同一份状态与文件锁。"})]}),e.jsxs("div",{className:t.btnRow,children:[B>0&&e.jsxs("span",{className:"text-xs text-text-secondary self-center",children:["上次刷新 ",new Date(B).toLocaleTimeString()]}),e.jsx("button",{className:t.btnSecondary,onClick:()=>N(),disabled:l,children:l?"加载中…":"刷新"})]})]}),m&&e.jsxs("div",{className:`rounded-xl p-3 border flex items-start justify-between gap-3 ${m.type==="success"?"bg-green-50 border-green-200 text-green-700":"bg-red-50 border-red-200 text-red-700"}`,children:[e.jsx("span",{className:"text-sm break-all",children:m.text}),e.jsx("button",{className:"text-xs underline shrink-0",onClick:()=>_(null),children:"关闭"})]}),ae&&e.jsxs("div",{className:t.card,children:[e.jsx("div",{className:t.sectionTitle,children:"当前为从站环境"}),e.jsx("p",{className:t.muted,children:"文件同步救援页仅主站可用,请到主站访问该页面。"})]}),!ae&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:t.grid2,children:[e.jsxs("div",{className:t.card,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:t.sectionTitle,children:"运行状态"}),e.jsxs("div",{className:t.muted,children:["role: ",C||"-"]})]}),e.jsxs("div",{className:"mt-2",children:[e.jsx(r,{label:"同步通道",value:P?`${P.mode} → ${P.dest}`:"未取到配置"}),e.jsx(r,{label:"心跳",value:ve,tone:Ne?"ok":o?"warn":"bad"}),e.jsx(r,{label:"上次同步",value:d?`${d.success?"成功":"失败"}(${d.runner||"未知来源"})`:"暂无记录",tone:d?d.success?"ok":"bad":"muted"}),e.jsx(r,{label:"上次时间",value:d?M(d.ended_at_ts):"-"}),e.jsx(r,{label:"上次原因",value:d?.reason||"-"}),e.jsx(r,{label:"上次文件数",value:d?`${d.files_count??0} 个,耗时 ${Number(d.duration_seconds||0).toFixed(1)}s`:"-"}),d?.tolerated_warning&&e.jsx(r,{label:"提示",value:String(d.tolerated_warning),tone:"warn"}),e.jsx(r,{label:"错误摘要",value:o?.last_sync_error||"无",tone:o?.last_sync_error?"bad":"muted"}),e.jsx(r,{label:"文件锁",value:fe,tone:g?.lock?"warn":"ok"}),e.jsx(r,{label:"配置问题",value:E.length?E.join(";"):"无",tone:E.length?"bad":"muted"}),e.jsx(r,{label:"上传触发的同步",value:_e}),e.jsx(r,{label:"上次被跳过",value:Se})]}),e.jsx(W,{text:k.status}),e.jsx(A,{value:g,title:"原始数据(状态接口 JSON)"})]}),e.jsxs("div",{className:t.card,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:t.
1sectionTitle,children:"待同步清单"}),e.jsx("div",{className:t.muted,children:"最近变更(最多 500)"})]}),k.pending?e.jsxs(e.Fragment,{children:[e.jsx(W,{text:k.pending}),e.jsx("p",{className:t.muted,children:"同步进行中时该清单会返回 409,属于正常现象,可稍后再看(不影响点「开始同步」)。"})]}):de&&!p?e.jsx("p",{className:t.muted,children:"正在预演(文件多时要等一会)……这一块慢不影响「运行状态」和「开始同步」。"}):p?.timed_out?e.jsx("div",{className:"mt-2 rounded-lg bg-amber-50 border border-amber-200 text-amber-800 text-xs p-2",children:p?.message||"预演超时:请直接点「开始同步」。"}):e.jsxs("div",{className:"mt-2",children:[e.jsx(r,{label:"待同步文件数",value:String(p?.pending_count??0),tone:Number(p?.pending_count??0)>0?"warn":"ok"}),F.length===0&&e.jsx("p",{className:t.muted,children:"从站已经和主站一致,没有待同步文件。"}),F.slice(0,20).map(s=>e.jsxs("div",{className:"text-xs text-text-secondary break-all py-0.5",children:["📄 ",s]},s)),F.length>20&&e.jsxs("p",{className:t.muted,children:["… 另有 ",F.length-20," 个文件(用下方“开始同步”即可全部推送)"]})]}),e.jsx(A,{value:p})]})]}),e.jsxs("div",{className:t.grid2,children:[e.jsxs("div",{className:t.card,children:[e.jsx("div",{className:t.sectionTitle,children:"触发同步"}),e.jsxs("div",{className:"mt-3 space-y-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:t.muted,children:"原因(可选)"}),e.jsx("input",{className:t.input,value:G,onChange:s=>ue(s.target.value),placeholder:"例如:发布后同步"})]}),e.jsxs("label",{className:"flex items-center gap-2 text-sm",children:[e.jsx("input",{type:"checkbox",checked:Q,onChange:s=>xe(s.target.checked)}),e.jsx("span",{children:"异步执行(推荐)"})]}),e.jsxs("div",{className:t.btnRow,children:[e.jsx("button",{className:t.btnPrimary,onClick:be,disabled:D,children:D?"处理中...":"开始同步"}),e.jsx("button",{className:t.btnDanger,onClick:je,disabled:!b||te,children:"取消当前任务"})]}),b&&e.jsxs("div",{className:t.muted,children:["当前任务:",b]})]})]}),e.jsxs("div",{className:t.card,children:[e.jsx("div",{className:t.sectionTitle,children:"锁/对账"}),e.jsxs("div",{className:"mt-3 space-y-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:t.muted,children:"清理陈旧锁(秒)"}),e.jsx("input",{className:t.input,value:String(ee),onChange:s=>ge(parseInt(s.target.value||"0",10)||0)})]}),e.jsx("div",{className:t.btnRow,children:e.jsx("button",{className:t.btnSecondary,onClick:pe,disabled:J,children:J?"处理中...":"清理锁"})}),e.jsx("hr",{}),e.jsx("div",{className:t.muted,children:"对账/补偿(可选:耗时较长)"}),e.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[e.jsxs("div",{children:[e.jsx("label",{className:t.muted,children:"回溯天数"}),e.jsx("input",{className:t.input,value:String(X),onChange:s=>me(parseInt(s.target.value||"0",10)||0)})]}),e.jsxs("label",{className:"flex items-center gap-2 text-sm mt-6",children:[e.jsx("input",{type:"checkbox",checked:Y,onChange:s=>he(s.target.checked)}),e.jsx("span",{children:"checksum(更严格)"})]})]}),e.jsx("div",{className:t.btnRow,children:e.jsx("button",{className:t.btnSecondary,onClick:ye,disabled:I,children:I?"处理中...":"执行对账/补偿"})})]})]})]}),e.jsxs("div",{className:t.grid2,children:[e.jsxs("div",{className:t.card,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:t.sectionTitle,children:"任务状态"}),e.jsx("div",{className:t.muted,children:te?"轮询中":"就绪"})]}),i?e.jsxs("div",{className:"mt-2",children:[e.jsx(r,{label:"状态",value:String(i?.status||"-"),tone:i?.status==="success"?"ok":i?.status==="error"?"bad":"muted"}),e.jsx(r,{label:"说明",value:String(i?.message||"-")}),i?.record&&e.jsxs(e.Fragment,{children:[e.jsx(r,{label:"结果",value:i.record.success?"成功":"失败",tone:i.record.success?"ok":"bad"}),e.jsx(r,{label:"文件数",value:String(i.record.files_count??0)}),e.jsx(r,{label:"耗时",value:`${Number(i.record.duration_seconds||0).toFixed(1)}s`}),!!i.record.stderr_tail&&e.jsx(r,{label:"错误",value:String(i.record.stderr_tail).slice(0,300),tone:"bad"})]}),!!i?.error&&e.jsx(r,{label:"异常",value:String(i.error),tone:"bad"})]}):e.jsx("p",{className:t.muted,children:"还没有触发过任务。"}),e.jsx(A,{value:i})]}),e.jsxs("div",{className:t.card,children:[e.jsxs("div",{className:"flex items-center justify-between gap-3",children:[e.jsx("div",{className:t.
1sectionTitle,children:"历史记录"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("input",{className:t.input,style:{width:120},value:String(S),onChange:s=>ie(parseInt(s.target.value||"0",10)||0)}),e.jsx("button",{className:t.btnSecondary,onClick:async()=>{try{L(await le(S)),y(s=>({...s,history:void 0}))}catch(s){h("error",`加载历史失败:${j(s)}`),y(a=>({...a,history:j(s)}))}},children:"查询"})]})]}),e.jsx(W,{text:k.history}),re.length===0?e.jsxs("p",{className:t.muted,children:["这台主站在 ",S," 天内没有任何同步记录(包含自动同步脚本的同步)。"]}):e.jsx("div",{className:"mt-2 max-h-72 overflow-auto",children:re.slice(0,30).map((s,a)=>e.jsxs("div",{className:"py-1.5 border-b border-dashed border-neutral-200 last:border-0",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("span",{className:`text-sm ${s.success?"text-green-700":"text-red-700"}`,children:[s.success?"成功":"失败"," · ",s.files_count??0," 个文件"]}),e.jsx("span",{className:"text-xs text-text-secondary",children:M(s.ended_at_ts)})]}),e.jsxs("div",{className:"text-xs text-text-secondary break-all",children:["原因:",s.reason||"-"," · 来源:",s.runner||"-"," · 耗时 ",Number(s.duration_seconds||0).toFixed(1),"s"]}),!!s.tolerated_warning&&e.jsxs("div",{className:"text-xs text-amber-700",children:["提示:",s.tolerated_warning]}),!s.success&&!!s.stderr_tail&&e.jsx("div",{className:"text-xs text-red-700 break-all",children:String(s.stderr_tail).slice(0,240)})]},`${s.ended_at_ts}-${a}`))}),e.jsx(A,{value:T})]})]})]})]})})]})})};export{Ye as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.