import {chromium} from 'playwright'; import {readFileSync,writeFileSync,mkdirSync} from 'node:fs'; const env={};for(const line of readFileSync('/Users/mac/.config/xiaoshu/legacy-sync-nightly.env','utf8').split('\n')){const m=line.match(/^(?:export\s+)?(XIAOSHU_[A-Z0-9_]+)=(.*)$/);if(m)env[m[1]]=m[2].replace(/^['"]|['"]$/g,'');} const token=env.XIAOSHU_SYNC_OPERATOR_TOKEN;if(!token)throw Error('Missing operator'); const origin=process.env.ADMIN_ORIGIN||'http://127.0.0.1:4300',out='tmp/admin-data-2026-10-15';mkdirSync(out,{recursive:true,mode:0o700}); const userR=await fetch('https://server.xiaoshu.pro/parse/users/me',{headers:{'X-Parse-Application-Id':env.XIAOSHU_PARSE_APP_ID,'X-Parse-Session-Token':token}}),me=await userR.json();if(!userR.ok)throw Error('Invalid operator'); const paths=process.argv.slice(2).length?process.argv.slice(2):['dashboard','members','stores','schedule','coaches','payroll','lessons','learning-reports','support-requests','vocabulary','special-training','privacy-requests','operator-accounts']; const browser=await chromium.launch({headless:true,executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'}),results=[]; try{for(const path of paths){ const ctx=await browser.newContext({viewport:{width:1440,height:1000}});await ctx.addInitScript(auth=>localStorage.setItem('xiaoshu_admin_session_v1',JSON.stringify(auth)),{objectId:me.objectId,sessionToken:token,username:'性能验收',roles:me.roles,operationsRole:'ops-manager',isSuperAdmin:true}); const page=await ctx.newPage(),requests=[],pending=new Set(),errors=[];let start,lastFinished=0; await page.addInitScript(()=>{window.__perfLong=[];new PerformanceObserver(list=>window.__perfLong.push(...list.getEntries().map(e=>({start:e.startTime,duration:e.duration})))).observe({type:'longtask',buffered:true});}); page.on('pageerror',e=>errors.push(e.message)); page.on('request',r=>{if(!r.url().includes('/cloud-functions/'))return;const p=r.postDataJSON()?.params||{},entry={operation:p.operation||p.action,path:new URL(r.url()).pathname,startMs:Date.now()-start,params:p};requests.push(entry);r.__entry=entry;pending.add(r);}); page.on('requestfinished',async r=>{if(!r.__entry)return;const entry=r.__entry;try{const resp=await r.response(),body=await resp.body(),data=JSON.parse(body.toString()),t=r.timing();Object.assign(entry,{elapsedMs:Date.now()-start-entry.startMs,ttfbMs:Math.round(t.responseStart-t.requestStart),downloadMs:Math.round(t.responseEnd-t.responseStart),bytes:body.length,status:resp.status(),success:data.success!==false&&data.retcode!==-1,total:data.data?.total??null});}catch(e){entry.error=String(e.message).slice(0,100);}finally{lastFinished=Date.now();pending.delete(r);}}); page.on('requestfailed',r=>{if(r.__entry){r.__entry.failed=true;pending.delete(r);lastFinished=Date.now();}}); start=Date.now();await page.goto(origin+'/admin/'+path,{waitUntil:'domcontentloaded'}); while(Date.now()-start<35000){await page.waitForTimeout(100);if(requests.length&&pending.size===0&&Date.now()-lastFinished>300)break;} await page.evaluate(()=>new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)))); const first={settledMs:Date.now()-start,requests:requests.map(r=>({...r})),pending:pending.size,errors:[...errors],longTasks:await page.evaluate(()=>window.__perfLong),resources:await page.evaluate(()=>performance.getEntriesByType('resource').map(r=>({name:new URL(r.name).pathname,duration:Math.round(r.duration),transfer:r.transferSize,decoded:r.decodedBodySize}))),alertCount:await page.getByRole('alert').count()}; let back; if(path!=='dashboard' && !process.env.FIRST_ONLY){ await page.locator('#admin-sidebar a[href="/admin/dashboard"]').last().click();await page.waitForTimeout(1200); while(pending.size&&Date.now()-lastFinished<22000)await page.waitForTimeout(100); const count=requests.length,begin=Date.now();await page.locator('#admin-sidebar a[href="/admin/'+path+'"]').last().click();await page.waitForTimeout(500); while(pending.size&&Date.now()-begin<25000)await page.waitForTimeout(100); back={settledMs:Date.now()-begin,requests:requests.slice(count)}; } results.push({path,first,back});writeFileSync(out+'/survey.json',JSON.stringify({at:new Date().toISOString(),results},null,2),{mode:0o600});console.log(JSON.stringify({path,firstMs:first.settledMs,requests:first.requests.map(r=>({op:r.operation,ms:r.elapsedMs,ttfb:r.ttfbMs,bytes:r.bytes,status:r.status})),returnMs:back?.settledMs,returnRequests:back?.requests.map(r=>r.operation),errors}));await ctx.close(); }}finally{await browser.close();}