- 공통 resolvePython: Windows venv → .venv/bin/python → PIERZAINER_PYTHON → python3/python - 지반 생성·수량산출 Excel·DWG 출력 서버가 같은 해석기 사용, ENOENT 안내 문구 보강 - WSL에서 번들 dwg2dxf.exe 호출 시 wslpath -w로 경로 변환, .exe 실행 권한 부여 - 실제 운곡교 DWG+Excel로 엔진/HTTP 경로 생성 확인(교각16·지형6,545점) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
25 lines
3.1 KiB
TypeScript
25 lines
3.1 KiB
TypeScript
import type {Plugin} from 'vite';
|
|
import {spawn} from 'node:child_process';
|
|
import {mkdir,readFile,writeFile} from 'node:fs/promises';
|
|
import {randomUUID} from 'node:crypto';
|
|
import path from 'node:path';
|
|
import {exportEdits,type ExportRequest} from './export.ts';
|
|
import {resolvePython,describeSpawnError} from '../pierzainer_ground/python-runtime.ts';
|
|
export function quantityPlugin(root:string):Plugin{
|
|
const handler=async(req:any,res:any)=>{
|
|
const reply=(status:number,value:unknown)=>{res.writeHead(status,{'Content-Type':'application/json; charset=utf-8'});res.end(JSON.stringify(value));};
|
|
const host=req.headers.host??'',remote=req.socket.remoteAddress??'';
|
|
if(req.method!=='POST'||req.headers['x-pierzainer-action']!=='quantity'||! /^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/.test(host)||!['127.0.0.1','::1','::ffff:127.0.0.1'].includes(remote)||req.headers.origin&&req.headers.origin!==`http://${host}`&&req.headers.origin!==`https://${host}`){reply(403,{error:'이 PC의 Pierzainer에서 출력하세요.'});return;}
|
|
try{let raw='';for await(const part of req){raw+=part;if(raw.length>2000000)throw new Error('출력 요청이 너무 큽니다.');}
|
|
const request=JSON.parse(raw) as ExportRequest&{piers?:ExportRequest[]};
|
|
const records=request.piers??[request];if(!records.length||records.length>100)throw new Error('출력 교각 수를 확인하세요.');
|
|
for(const key of ['specFooting','specBody','specBlinding','specBearing','specMortar'])if(new Set(records.map(r=>String(r.specs[key]??''))).size>1)throw new Error('교각별 재료 규격이 서로 다릅니다. 원본 집계표의 동일 공종·규격에 합산할 수 없어 규격별 집계 검토가 필요합니다.');
|
|
const payload={projects:records.map(r=>({...exportEdits(r),pier:r.pier})),bridge:records[0].project.bridge},folder=path.join(root,'수량산출/outputs',randomUUID());await mkdir(folder,{recursive:true});
|
|
const input=path.join(folder,'edits.json'),output=path.join(folder,'quantity.xlsx');await writeFile(input,JSON.stringify(payload),'utf8');
|
|
await new Promise<void>((resolve,reject)=>{const python=resolvePython(root),child=spawn(python,['-B','-X','utf8',path.join(root,'수량산출/tools/project_workbook.py'),input,output],{cwd:root,windowsHide:true});let logs='';child.stdout.on('data',b=>logs+=b);child.stderr.on('data',b=>logs+=b);const timer=setTimeout(()=>{child.kill();reject(new Error('Excel 출력 제한시간 초과'));},60000);child.once('error',e=>{clearTimeout(timer);reject(new Error(describeSpawnError(e,python)));});child.once('close',code=>{clearTimeout(timer);if(code!==0)reject(new Error(logs));else resolve();});});
|
|
res.writeHead(200,{'Content-Type':'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','Content-Disposition':'attachment; filename="quantity.xlsx"','Cache-Control':'no-store'});res.end(await readFile(output));
|
|
}catch(e){reply(422,{error:(e as Error).message});}
|
|
};
|
|
return {name:'pierzainer-quantity',configureServer(s){s.middlewares.use('/__pierzainer/quantity/export',(req,res)=>void handler(req,res));},configurePreviewServer(s){s.middlewares.use('/__pierzainer/quantity/export',(req,res)=>void handler(req,res));}};
|
|
}
|