nuwax-file-server 1.3.0-beta.8 → 1.3.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as ee}from"commander";import{createRequire as te}from"module";import f from"path";import U from"os";import c from"fs-extra";import{spawn as E}from"cross-spawn";import V from"tree-kill";import{fileURLToPath as q}from"url";import{execFileSync as R}from"child_process";import M from"http";import{createRequire as G}from"module";var I=f.dirname(q(import.meta.url)),H=G(import.meta.url),n={name:"nuwax-file-server",pidDir:f.join(U.tmpdir(),"nuwax-file-server"),pidFileName:"server.pid",lockFileName:"start.lock",defaultStopTimeout:3e4,defaultStartTimeout:3e4,staleLockTimeout:12e4,checkInterval:500};function $(){return f.join(n.pidDir,n.pidFileName)}function F(){return f.join(n.pidDir,n.lockFileName)}function J(){let e=[f.join(I,"..","server.js"),f.join(I,"server.js")];for(let t of e)if(c.existsSync(t))return t;return e[0]}function W(){let e=["../../package.json","../package.json"];for(let t of e)try{let o=H(t);if(o?.version)return o.version}catch{}return"unknown"}function x(){try{let e=$();if(!c.existsSync(e))return null;let t=c.readFileSync(e,"utf8"),o=JSON.parse(t);return!o||typeof o.pid!="number"?null:o}catch(e){return e.code!=="ENOENT"&&console.error(`Read PID file failed: ${e.message}`),null}}function B(e){try{let t=$();c.ensureDirSync(n.pidDir),c.writeFileSync(t,JSON.stringify(e,null,2)),console.debug(`PID file written: ${t}`)}catch(t){throw console.error(`Write PID file failed: ${t.message}`),t}}function v(){try{let e=$();c.existsSync(e)&&(c.removeSync(e),console.debug(`PID file deleted: ${e}`))}catch(e){console.error(`Delete PID file failed: ${e.message}`)}}function g(e){try{return process.kill(e,0),!0}catch(t){return t.code!=="ESRCH"}}function K(e){try{return k()?R("powershell",["-NoProfile","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${e}").CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim():R("ps",["-p",String(e),"-o","command="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return""}}function X(e){if(!g(e))return!1;let t=K(e);if(!t)return!1;let o=t.toLowerCase(),s=f.basename(f.join(I,"..","server.js")).toLowerCase(),i=n.name.toLowerCase();return o.includes(s)||o.includes(i)}function z(){c.ensureDirSync(n.pidDir);let e=F(),t=`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,10)}`,o=JSON.stringify({pid:process.pid,token:t,createdAt:new Date().toISOString()},null,2);try{let s=c.openSync(e,"wx");return c.writeSync(s,o),{fd:s,token:t}}catch(s){if(s.code!=="EEXIST")throw s;try{let i=c.readFileSync(e,"utf8"),r=JSON.parse(i),a=Number(r?.pid),u=r?.createdAt?new Date(r.createdAt).getTime():0,l=Date.now()-u,C=Number.isFinite(a)&&a>0&&g(a),p=!u||Number.isNaN(l)||l>n.staleLockTimeout;if(!C||p){c.removeSync(e);let S=c.openSync(e,"wx");return c.writeSync(S,o),console.warn("Detected stale start lock, auto cleaned"),{fd:S,token:t}}}catch{c.removeSync(e);let r=c.openSync(e,"wx");return c.writeSync(r,o),console.warn("Detected invalid start lock, auto cleaned"),{fd:r,token:t}}throw s}}function Q(e){let t=e?.fd,o=e?.token;try{t!=null&&c.closeSync(t)}catch{}try{let s=F();if(c.existsSync(s)){let i=!1;if(!o)i=!0;else try{let r=c.readFileSync(s,"utf8");i=JSON.parse(r)?.token===o}catch{i=!0}i&&c.removeSync(s)}}catch{}}function k(){return process.platform==="win32"}async function Y(e=n.defaultStopTimeout){let t=x();return t?X(t.pid)?(console.log(`Existing service process detected (PID: ${t.pid}), stopping before start...`),await h(t.pid,!1)&&await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped gracefully`}):(console.warn(`Graceful stop timeout or failed for PID ${t.pid}, force stop...`),await h(t.pid,!0)?await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped forcibly`}):{success:!1,message:`Existing process ${t.pid} did not exit after force stop`}:{success:!1,message:`Failed to stop existing process ${t.pid}`})):(console.log(`Found stale PID file (PID: ${t.pid}), clean it...`),v(),{success:!0,message:"Stale PID file cleaned"}):{success:!0,message:"No existing service process"}}async function h(e,t=!1){return new Promise(o=>{if(!g(e)){console.debug(`Process ${e} does not exist, already stopped`),o(!0);return}let s=t?"SIGKILL":"SIGTERM",i=k()?"taskkill":"tree-kill";if(console.debug(`Use ${i} to stop process ${e} (signal: ${s})`),k()){let r=t?["/F","/PID",String(e)]:["/PID",String(e)],a=E("taskkill",r,{stdio:["ignore","pipe","pipe"],windowsHide:!0}),u="";a.stdout.on("data",l=>{u+=l.toString()}),a.stderr.on("data",l=>{u+=l.toString()}),a.on("error",l=>{console.error(`Stop process failed: ${l.message}`),o(!1)}),a.on("close",l=>{l===0?(console.debug(`Process ${e} stopped`),o(!0)):(console.warn(`taskkill exit code: ${l}, output: ${u}`),t?o(!1):h(e,!0).then(o))})}else V(e,s,r=>{r?r.code==="ESRCH"?(console.debug(`Process ${e} does not exist`),o(!0)):(console.error(`Stop process failed: ${r.message}`),o(!1)):(console.debug(`Process ${e} stopped`),o(!0))})})}async function D(e,t=n.defaultStopTimeout){let o=Date.now();for(;g(e);){if(Date.now()-o>t)return console.warn(`Wait for process ${e} to stop timeout (${t}ms)`),!1;await new Promise(i=>setTimeout(i,n.checkInterval))}let s=Date.now()-o;return console.debug(`Process ${e} stopped after ${s}ms`),!0}async function Z(e,t=n.defaultStartTimeout){let o=Number(e);if(!Number.isFinite(o)||o<=0)return!1;let s=Date.now();for(;Date.now()-s<=t;){if(await new Promise(r=>{let a=M.get({host:"127.0.0.1",port:o,path:"/health",timeout:Math.min(2e3,n.checkInterval*4)},u=>{r(u.statusCode>=200&&u.statusCode<300),u.resume()});a.on("timeout",()=>{a.destroy(),r(!1)}),a.on("error",()=>r(!1))}))return!0;await new Promise(r=>setTimeout(r,n.checkInterval))}return!1}async function N(e={}){let{env:t,port:o,config:s}=e,i=null;console.log(`Start service ${n.name}...`);try{i=z()}catch(r){return r.code==="EEXIST"?{success:!1,pid:null,message:"Another start operation is in progress, please retry later"}:{success:!1,pid:null,message:`Acquire start lock failed: ${r.message}`}}try{let r=Number(e.timeout)||n.defaultStopTimeout,a=await Y(r);if(!a.success)return{success:!1,pid:null,message:`Service start blocked: ${a.message}`};let u={...process.env};t&&(u.NODE_ENV=t,console.log(`Environment: ${t}`)),o&&(u.PORT=o,console.log(`Port: ${o}`)),s&&(u.CONFIG_FILE=s,console.log(`Configuration file: ${s}`));let l=J(),p=E("node",[l,...[]],{env:u,stdio:["pipe","pipe","pipe"],detached:!0,cwd:process.cwd()});if(p.stdout.on("data",m=>{process.stdout.write(m)}),p.stderr.on("data",m=>{process.stderr.write(m)}),p.on("error",m=>{console.error(`Start service failed: ${m.message}`)}),await new Promise(m=>setTimeout(m,2e3)),!g(p.pid))return{success:!1,pid:null,message:"Service start failed"};let S={pid:p.pid,startedAt:new Date().toISOString(),env:t||process.env.NODE_ENV||"production",port:o||process.env.PORT||"60000",version:W(),platform:process.platform};B(S);let P=Number(e.startTimeout)||n.defaultStartTimeout;return await Z(S.port,P)?(p.unref(),console.log(`Service started (PID: ${p.pid})`),console.log(`Service address: http://localhost:${S.port}`),{success:!0,pid:p.pid,message:"Service started successfully"}):(console.error(`Service health check timeout (${P}ms), stop failed instance...`),await h(p.pid,!0),v(),{success:!1,pid:null,message:`Service health check timeout (${P}ms)`})}finally{Q(i)}}async function T(e={}){let{force:t=!1,timeout:o=n.defaultStopTimeout}=e;console.log(`Stop service ${n.name}...`);let s=x();if(!s)return{success:!1,message:"Service not found"};if(!g(s.pid))return console.log("Service process has stopped, clean PID file..."),v(),{success:!0,message:"Service has stopped (process has exited)"};if(!await h(s.pid,t))return{success:!1,message:"Stop service failed"};let r=await D(s.pid,o);return v(),r?{success:!0,message:"Service has stopped"}:{success:!1,message:"Service stop timeout"}}async function O(e={}){console.log(`Restart service ${n.name}...`);let t=await T(e);!t.success&&t.message!=="Service not found"&&console.warn(`Stop service failed: ${t.message}`),await new Promise(s=>setTimeout(s,2e3));let o=await N(e);return o.success?{success:!0,pid:o.pid,message:"Service has restarted"}:{success:!1,pid:null,message:`Restart failed: ${o.message}`}}function _(){let e=x();return e?g(e.pid)?{running:!0,pidInfo:e,message:"Service running"}:{running:!1,pidInfo:e,message:"Service process does not exist"}:{running:!1,pidInfo:null,message:"Service not running"}}function L(e){try{let t=new Date(e),o=new Date;if(isNaN(t.getTime()))return"unknown";let s=Math.floor((o-t)/1e3),i=Math.floor(s/3600),r=Math.floor(s%3600/60),a=s%60;return i>0?`${i} hours ${r} minutes ${a} seconds`:r>0?`${r} minutes ${a} seconds`:`${a} seconds`}catch{return"unknown"}}var Pe=te(import.meta.url),A="1.3.0-beta.8",d=new ee,w={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function j(e){console.error(`${w.red}ERROR: ${e}${w.reset}`)}function b(e){console.log(`${w.green}${e}${w.reset}`)}function oe(e){console.log(`${w.blue}${e}${w.reset}`)}function y(e){return async(...t)=>{try{let o=await e(...t);o&&o.success===!1?(j(o.message||"Command failed"),process.exitCode=1):process.exitCode=0}catch(o){j(o?.message||"Command failed with unexpected error"),process.exitCode=1}}}async function se(e){let t=await N({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(`Service started (PID: ${t.pid})`),t}async function re(e){let t=await T({force:e.force,timeout:Number(e.timeout)});return t.success&&b(t.message||"Service stopped"),t}async function ne(e){let t=await O({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(t.message||"Service restarted"),t}function ie(){let e=_();return oe(`${n.name} service status:`),console.log(""),console.log(` Service name: ${n.name}`),console.log(` Running status: ${e.running?"Running":"Stopped"}`),console.log(` Message: ${e.message}`),console.log(` PID file: ${$()}`),e.pidInfo&&(console.log(` Process ID: ${e.pidInfo.pid}`),console.log(` Environment: ${e.pidInfo.env||"Unknown"}`),console.log(` Port: ${e.pidInfo.port||"Unknown"}`),console.log(` Version: ${e.pidInfo.version||A}`),console.log(` Platform: ${e.pidInfo.platform||process.platform}`),console.log(` Started at: ${e.pidInfo.startedAt||"Unknown"}`),console.log(` Uptime: ${L(e.pidInfo.startedAt)}`)),console.log(""),{success:e.running,message:e.message}}function ce(){d.name("nuwax-file-server").description("Cross-platform file service deployment tool, supporting start/stop/restart/status").version(A,"-v, --version","Display version number").helpOption("-h, --help","Display help information"),d.command("start").allowUnknownOption().description("Start service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(se)),d.command("stop").description("Stop service").option("--force","Force stop").option("--timeout <ms>","\u505C\u6B62\u670D\u52A1\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).action(y(re)),d.command("restart").allowUnknownOption().description("Restart service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(ne)),d.command("status").description("View service status").action(y(ie)),d.command("help").description("Display help information").action(()=>{d.outputHelp()}),d.parse(process.argv),process.argv.slice(2).length||(d.outputHelp(),process.exitCode=0)}ce();
|
|
2
|
+
import{Command as ee}from"commander";import{createRequire as te}from"module";import f from"path";import U from"os";import c from"fs-extra";import{spawn as E}from"cross-spawn";import V from"tree-kill";import{fileURLToPath as q}from"url";import{execFileSync as R}from"child_process";import M from"http";import{createRequire as G}from"module";var I=f.dirname(q(import.meta.url)),H=G(import.meta.url),n={name:"nuwax-file-server",pidDir:f.join(U.tmpdir(),"nuwax-file-server"),pidFileName:"server.pid",lockFileName:"start.lock",defaultStopTimeout:3e4,defaultStartTimeout:3e4,staleLockTimeout:12e4,checkInterval:500};function $(){return f.join(n.pidDir,n.pidFileName)}function F(){return f.join(n.pidDir,n.lockFileName)}function J(){let e=[f.join(I,"..","server.js"),f.join(I,"server.js")];for(let t of e)if(c.existsSync(t))return t;return e[0]}function W(){let e=["../../package.json","../package.json"];for(let t of e)try{let o=H(t);if(o?.version)return o.version}catch{}return"unknown"}function x(){try{let e=$();if(!c.existsSync(e))return null;let t=c.readFileSync(e,"utf8"),o=JSON.parse(t);return!o||typeof o.pid!="number"?null:o}catch(e){return e.code!=="ENOENT"&&console.error(`Read PID file failed: ${e.message}`),null}}function B(e){try{let t=$();c.ensureDirSync(n.pidDir),c.writeFileSync(t,JSON.stringify(e,null,2)),console.debug(`PID file written: ${t}`)}catch(t){throw console.error(`Write PID file failed: ${t.message}`),t}}function v(){try{let e=$();c.existsSync(e)&&(c.removeSync(e),console.debug(`PID file deleted: ${e}`))}catch(e){console.error(`Delete PID file failed: ${e.message}`)}}function g(e){try{return process.kill(e,0),!0}catch(t){return t.code!=="ESRCH"}}function K(e){try{return k()?R("powershell",["-NoProfile","-Command",`(Get-CimInstance Win32_Process -Filter "ProcessId = ${e}").CommandLine`],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim():R("ps",["-p",String(e),"-o","command="],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim()}catch{return""}}function X(e){if(!g(e))return!1;let t=K(e);if(!t)return!1;let o=t.toLowerCase(),s=f.basename(f.join(I,"..","server.js")).toLowerCase(),i=n.name.toLowerCase();return o.includes(s)||o.includes(i)}function z(){c.ensureDirSync(n.pidDir);let e=F(),t=`${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2,10)}`,o=JSON.stringify({pid:process.pid,token:t,createdAt:new Date().toISOString()},null,2);try{let s=c.openSync(e,"wx");return c.writeSync(s,o),{fd:s,token:t}}catch(s){if(s.code!=="EEXIST")throw s;try{let i=c.readFileSync(e,"utf8"),r=JSON.parse(i),a=Number(r?.pid),u=r?.createdAt?new Date(r.createdAt).getTime():0,l=Date.now()-u,C=Number.isFinite(a)&&a>0&&g(a),p=!u||Number.isNaN(l)||l>n.staleLockTimeout;if(!C||p){c.removeSync(e);let S=c.openSync(e,"wx");return c.writeSync(S,o),console.warn("Detected stale start lock, auto cleaned"),{fd:S,token:t}}}catch{c.removeSync(e);let r=c.openSync(e,"wx");return c.writeSync(r,o),console.warn("Detected invalid start lock, auto cleaned"),{fd:r,token:t}}throw s}}function Q(e){let t=e?.fd,o=e?.token;try{t!=null&&c.closeSync(t)}catch{}try{let s=F();if(c.existsSync(s)){let i=!1;if(!o)i=!0;else try{let r=c.readFileSync(s,"utf8");i=JSON.parse(r)?.token===o}catch{i=!0}i&&c.removeSync(s)}}catch{}}function k(){return process.platform==="win32"}async function Y(e=n.defaultStopTimeout){let t=x();return t?X(t.pid)?(console.log(`Existing service process detected (PID: ${t.pid}), stopping before start...`),await h(t.pid,!1)&&await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped gracefully`}):(console.warn(`Graceful stop timeout or failed for PID ${t.pid}, force stop...`),await h(t.pid,!0)?await D(t.pid,e)?(v(),{success:!0,message:`Existing process ${t.pid} stopped forcibly`}):{success:!1,message:`Existing process ${t.pid} did not exit after force stop`}:{success:!1,message:`Failed to stop existing process ${t.pid}`})):(console.log(`Found stale PID file (PID: ${t.pid}), clean it...`),v(),{success:!0,message:"Stale PID file cleaned"}):{success:!0,message:"No existing service process"}}async function h(e,t=!1){return new Promise(o=>{if(!g(e)){console.debug(`Process ${e} does not exist, already stopped`),o(!0);return}let s=t?"SIGKILL":"SIGTERM",i=k()?"taskkill":"tree-kill";if(console.debug(`Use ${i} to stop process ${e} (signal: ${s})`),k()){let r=t?["/F","/PID",String(e)]:["/PID",String(e)],a=E("taskkill",r,{stdio:["ignore","pipe","pipe"],windowsHide:!0}),u="";a.stdout.on("data",l=>{u+=l.toString()}),a.stderr.on("data",l=>{u+=l.toString()}),a.on("error",l=>{console.error(`Stop process failed: ${l.message}`),o(!1)}),a.on("close",l=>{l===0?(console.debug(`Process ${e} stopped`),o(!0)):(console.warn(`taskkill exit code: ${l}, output: ${u}`),t?o(!1):h(e,!0).then(o))})}else V(e,s,r=>{r?r.code==="ESRCH"?(console.debug(`Process ${e} does not exist`),o(!0)):(console.error(`Stop process failed: ${r.message}`),o(!1)):(console.debug(`Process ${e} stopped`),o(!0))})})}async function D(e,t=n.defaultStopTimeout){let o=Date.now();for(;g(e);){if(Date.now()-o>t)return console.warn(`Wait for process ${e} to stop timeout (${t}ms)`),!1;await new Promise(i=>setTimeout(i,n.checkInterval))}let s=Date.now()-o;return console.debug(`Process ${e} stopped after ${s}ms`),!0}async function Z(e,t=n.defaultStartTimeout){let o=Number(e);if(!Number.isFinite(o)||o<=0)return!1;let s=Date.now();for(;Date.now()-s<=t;){if(await new Promise(r=>{let a=M.get({host:"127.0.0.1",port:o,path:"/health",timeout:Math.min(2e3,n.checkInterval*4)},u=>{r(u.statusCode>=200&&u.statusCode<300),u.resume()});a.on("timeout",()=>{a.destroy(),r(!1)}),a.on("error",()=>r(!1))}))return!0;await new Promise(r=>setTimeout(r,n.checkInterval))}return!1}async function N(e={}){let{env:t,port:o,config:s}=e,i=null;console.log(`Start service ${n.name}...`);try{i=z()}catch(r){return r.code==="EEXIST"?{success:!1,pid:null,message:"Another start operation is in progress, please retry later"}:{success:!1,pid:null,message:`Acquire start lock failed: ${r.message}`}}try{let r=Number(e.timeout)||n.defaultStopTimeout,a=await Y(r);if(!a.success)return{success:!1,pid:null,message:`Service start blocked: ${a.message}`};let u={...process.env};t&&(u.NODE_ENV=t,console.log(`Environment: ${t}`)),o&&(u.PORT=o,console.log(`Port: ${o}`)),s&&(u.CONFIG_FILE=s,console.log(`Configuration file: ${s}`));let l=J(),p=E("node",[l,...[]],{env:u,stdio:["pipe","pipe","pipe"],detached:!0,cwd:process.cwd()});if(p.stdout.on("data",m=>{process.stdout.write(m)}),p.stderr.on("data",m=>{process.stderr.write(m)}),p.on("error",m=>{console.error(`Start service failed: ${m.message}`)}),await new Promise(m=>setTimeout(m,2e3)),!g(p.pid))return{success:!1,pid:null,message:"Service start failed"};let S={pid:p.pid,startedAt:new Date().toISOString(),env:t||process.env.NODE_ENV||"production",port:o||process.env.PORT||"60000",version:W(),platform:process.platform};B(S);let P=Number(e.startTimeout)||n.defaultStartTimeout;return await Z(S.port,P)?(p.unref(),console.log(`Service started (PID: ${p.pid})`),console.log(`Service address: http://localhost:${S.port}`),{success:!0,pid:p.pid,message:"Service started successfully"}):(console.error(`Service health check timeout (${P}ms), stop failed instance...`),await h(p.pid,!0),v(),{success:!1,pid:null,message:`Service health check timeout (${P}ms)`})}finally{Q(i)}}async function T(e={}){let{force:t=!1,timeout:o=n.defaultStopTimeout}=e;console.log(`Stop service ${n.name}...`);let s=x();if(!s)return{success:!1,message:"Service not found"};if(!g(s.pid))return console.log("Service process has stopped, clean PID file..."),v(),{success:!0,message:"Service has stopped (process has exited)"};if(!await h(s.pid,t))return{success:!1,message:"Stop service failed"};let r=await D(s.pid,o);return v(),r?{success:!0,message:"Service has stopped"}:{success:!1,message:"Service stop timeout"}}async function O(e={}){console.log(`Restart service ${n.name}...`);let t=await T(e);!t.success&&t.message!=="Service not found"&&console.warn(`Stop service failed: ${t.message}`),await new Promise(s=>setTimeout(s,2e3));let o=await N(e);return o.success?{success:!0,pid:o.pid,message:"Service has restarted"}:{success:!1,pid:null,message:`Restart failed: ${o.message}`}}function _(){let e=x();return e?g(e.pid)?{running:!0,pidInfo:e,message:"Service running"}:{running:!1,pidInfo:e,message:"Service process does not exist"}:{running:!1,pidInfo:null,message:"Service not running"}}function L(e){try{let t=new Date(e),o=new Date;if(isNaN(t.getTime()))return"unknown";let s=Math.floor((o-t)/1e3),i=Math.floor(s/3600),r=Math.floor(s%3600/60),a=s%60;return i>0?`${i} hours ${r} minutes ${a} seconds`:r>0?`${r} minutes ${a} seconds`:`${a} seconds`}catch{return"unknown"}}var Pe=te(import.meta.url),A="1.3.0-beta.9",d=new ee,w={reset:"\x1B[0m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"};function j(e){console.error(`${w.red}ERROR: ${e}${w.reset}`)}function b(e){console.log(`${w.green}${e}${w.reset}`)}function oe(e){console.log(`${w.blue}${e}${w.reset}`)}function y(e){return async(...t)=>{try{let o=await e(...t);o&&o.success===!1?(j(o.message||"Command failed"),process.exitCode=1):process.exitCode=0}catch(o){j(o?.message||"Command failed with unexpected error"),process.exitCode=1}}}async function se(e){let t=await N({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(`Service started (PID: ${t.pid})`),t}async function re(e){let t=await T({force:e.force,timeout:Number(e.timeout)});return t.success&&b(t.message||"Service stopped"),t}async function ne(e){let t=await O({env:e.env,port:e.port,config:e.config,timeout:Number(e.timeout),startTimeout:Number(e.startTimeout)});return t.success&&b(t.message||"Service restarted"),t}function ie(){let e=_();return oe(`${n.name} service status:`),console.log(""),console.log(` Service name: ${n.name}`),console.log(` Running status: ${e.running?"Running":"Stopped"}`),console.log(` Message: ${e.message}`),console.log(` PID file: ${$()}`),e.pidInfo&&(console.log(` Process ID: ${e.pidInfo.pid}`),console.log(` Environment: ${e.pidInfo.env||"Unknown"}`),console.log(` Port: ${e.pidInfo.port||"Unknown"}`),console.log(` Version: ${e.pidInfo.version||A}`),console.log(` Platform: ${e.pidInfo.platform||process.platform}`),console.log(` Started at: ${e.pidInfo.startedAt||"Unknown"}`),console.log(` Uptime: ${L(e.pidInfo.startedAt)}`)),console.log(""),{success:e.running,message:e.message}}function ce(){d.name("nuwax-file-server").description("Cross-platform file service deployment tool, supporting start/stop/restart/status").version(A,"-v, --version","Display version number").helpOption("-h, --help","Display help information"),d.command("start").allowUnknownOption().description("Start service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(se)),d.command("stop").description("Stop service").option("--force","Force stop").option("--timeout <ms>","\u505C\u6B62\u670D\u52A1\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).action(y(re)),d.command("restart").allowUnknownOption().description("Restart service").option("--env <environment>","\u73AF\u5883: development|production|test","production").option("--port <port>","Service port").option("--config <path>","Custom configuration file path").option("--timeout <ms>","\u505C\u6B62\u65E7\u8FDB\u7A0B\u7B49\u5F85\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStopTimeout}`).option("--start-timeout <ms>","\u542F\u52A8\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",`${n.defaultStartTimeout}`).action(y(ne)),d.command("status").description("View service status").action(y(ie)),d.command("help").description("Display help information").action(()=>{d.outputHelp()}),d.parse(process.argv),process.argv.slice(2).length||(d.outputHelp(),process.exitCode=0)}ce();
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import{exec as
|
|
2
|
-
Command: pnpm install --prefer-offline${
|
|
3
|
-
`;e(
|
|
4
|
-
`;e(
|
|
5
|
-
Exit code: ${
|
|
6
|
-
${
|
|
7
|
-
`;return e(
|
|
8
|
-
${
|
|
9
|
-
`;e(
|
|
10
|
-
`;if(e(
|
|
11
|
-
${
|
|
1
|
+
import{exec as R,spawn as O}from"child_process";import g from"path";import r from"fs";import{log as i}from"../log/logUtils.js";function v(o){try{return r.statSync(o).mtimeMs}catch{return 0}}function W(o){const c=g.join(o,"package.json"),s=g.join(o,"package-lock.json"),l=g.join(o,"yarn.lock"),n=g.join(o,"node_modules");if(!r.existsSync(n))return!0;const e=v(c),k=Math.max(v(s),v(l)),t=v(n);return Math.max(e,k)>t}async function b(o,c=null){const s=g.join(o,"node_modules"),l=c||g.basename(o);let n=!1,a=!1,e=null;try{const t=await r.promises.lstat(s);n=!0,a=t.isSymbolicLink(),a&&(e=await r.promises.readlink(s))}catch{}if(n)if(a){i(l,"INFO","Found node_modules symlink, removing symlink only (preserving target as cache)",{projectPath:o,symlink:s,target:e});try{await r.promises.unlink(s),i(l,"INFO","Symlink removed, target preserved for cache reuse",{nodeModulesPath:s,target:e})}catch(t){i(l,"WARN",`Failed to remove node_modules symlink: ${t.message}`,{error:t.message})}}else{i(l,"INFO","Found node_modules folder, deleting",{projectPath:o,nodeModulesPath:s});try{await r.promises.rm(s,{recursive:!0,force:!0}),i(l,"INFO","node_modules folder deleted successfully",{projectPath:o,nodeModulesPath:s})}catch(t){i(l,"WARN",`Failed to delete node_modules folder: ${t.message}`,{projectPath:o,nodeModulesPath:s,error:t.message})}}const k=["package-lock.json","yarn.lock","pnpm-lock.yaml"];for(const t of k){const y=g.join(o,t);if(r.existsSync(y)){i(l,"INFO",`Found ${t} file, deleting`,{projectPath:o,lockFilePath:y});try{await r.promises.unlink(y),i(l,"INFO",`${t} file deleted successfully`,{projectPath:o,lockFilePath:y})}catch($){i(l,"WARN",`Failed to delete ${t} file: ${$.message}`,{projectPath:o,lockFilePath:y,error:$.message})}}}}async function T(o,c){const s=g.join(o,"node_modules"),l=c||g.basename(o);if(r.existsSync(s)){try{if((await r.promises.lstat(s)).isSymbolicLink()&&!r.existsSync(s)){i(l,"WARN","Broken node_modules symlink detected, removing before install",{projectPath:o,nodeModulesPath:s}),await r.promises.unlink(s);return}}catch(n){i(l,"WARN",`Failed to check node_modules symlink: ${n.message}`)}try{const n=g.join(s,".modules.yaml");if(r.existsSync(n)){const e=(await r.promises.readFile(n,"utf8")).match(/storeDir:\s*(.+)/);if(e){const k=e[1].trim(),t=process.env.npm_config_store_dir||process.env.PNPM_STORE_DIR||"";if(t&&k!==t){i(l,"WARN","Store path mismatch detected (node_modules from different store), removing .modules.yaml to let pnpm rewrite it (preserving node_modules)",{projectPath:o,recordedStoreDir:k,currentStoreDir:t});try{await r.promises.unlink(n),i(l,"INFO","Removed stale .modules.yaml, node_modules preserved",{modulesYamlPath:n})}catch{}}}}}catch(n){i(l,"WARN",`Pre-flight store check failed: ${n.message}`)}}}async function A(o,c,s,l={}){await T(s,c);const{outStream:n,tempOutStream:a,safeWrite:e}=l,k=g.join(s,"node_modules"),t=r.existsSync(k),y=process.env.npm_config_store_dir||process.env.PNPM_STORE_DIR||"",$=y?` --store-dir=${y}`:"";return n&&a&&e?new Promise((x,w)=>{const N=`cd ${s} && pnpm install --prefer-offline${$}`,f=Date.now(),M=`Start installing dependencies (${t?"incremental (node_modules from cache)":"full install"})
|
|
2
|
+
Command: pnpm install --prefer-offline${$}
|
|
3
|
+
`;e(n,M,"Main log"),e(a,M,"Temp log");let E=0;const h=setInterval(()=>{E++;const d=`Installing dependencies... (Elapsed time: ${Math.floor((Date.now()-f)/1e3)} seconds)
|
|
4
|
+
`;e(n,d,"Main log"),e(a,d,"Temp log")},5e3),S=O("sh",["-c",N],{cwd:s,env:process.env,stdio:["ignore","pipe","pipe"]});let D="",p="";S.stdout&&S.stdout.on("data",m=>{const d=m.toString();D+=d,e(n,d,"Main log"),e(a,d,"Temp log")}),S.stderr&&S.stderr.on("data",m=>{const d=m.toString();p+=d,e(n,d,"Main log"),e(a,d,"Temp log")}),S.on("exit",(m,d)=>{clearInterval(h);const _=((Date.now()-f)/1e3).toFixed(2);if(m!==0){const u=`Dependency installation failed (Elapsed time: ${_} seconds)
|
|
5
|
+
Exit code: ${m}, Signal: ${d}
|
|
6
|
+
${p||"No error information"}
|
|
7
|
+
`;return e(n,u,"Main log"),e(a,u,"Temp log"),w(new Error(`Dependency installation failed: Exit code ${m}, Signal ${d}
|
|
8
|
+
${p}`))}if(p&&p.includes("Error")&&!p.includes("warning")){const u=`Warning occurred during dependency installation: ${p}
|
|
9
|
+
`;e(n,u,"Main log"),e(a,u,"Temp log")}const I=`\u2713 Dependency installation successful (Elapsed time: ${_} seconds)
|
|
10
|
+
`;if(e(n,I,"Main log"),e(a,I,"Temp log"),D||p){const u=`Installation details:
|
|
11
|
+
${D||"(No standard output)"}${p?`
|
|
12
12
|
Warning information:
|
|
13
|
-
`+
|
|
14
|
-
`;e(
|
|
15
|
-
`;e(
|
|
16
|
-
`;e(
|
|
17
|
-
${M||
|
|
13
|
+
`+p:""}
|
|
14
|
+
`;e(n,u,"Main log"),e(a,u,"Temp log")}else{const u=`(Silent mode: No detailed output, dependency successfully linked from store to node_modules)
|
|
15
|
+
`;e(n,u,"Main log"),e(a,u,"Temp log")}x(D)}),S.on("error",m=>{clearInterval(h);const _=`Dependency installation process error (Elapsed time: ${((Date.now()-f)/1e3).toFixed(2)} seconds): ${m.message}
|
|
16
|
+
`;e(n,_,"Main log"),e(a,_,"Temp log"),w(new Error(`Dependency installation failed: ${m.message}`))})}):new Promise((x,w)=>{const N=`cd ${s} && pnpm install --prefer-offline${$} --reporter=silent --loglevel=error`;i(c,"INFO","Start executing dependency installation command",{command:N,projectPath:s,installMode:t?"incremental":"full"}),R(N,{maxBuffer:10*1024*1024,env:process.env},(f,F,M)=>{if(f)return i(c,"ERROR","Dependency installation failed",{error:f.message,code:f.code,stderr:M||f.message,stdout:F||""}),w(new Error(`Dependency installation failed: ${f.message}
|
|
17
|
+
${M||f.message}`));M&&M.includes("Error")&&!M.includes("warning")&&i(c,"WARN","Warning or error occurred during dependency installation",{stderr:M}),i(c,"INFO","Dependency installation completed",{stdout:F.substring(0,500)}),x(F)})})}export{v as getFileMtime,W as shouldInstallDeps,A as installDependencies,b as removeNodeModules,T as preflightCleanNodeModules};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import n from"fs";import
|
|
2
|
-
`);let t="",c="local";for(const
|
|
3
|
-
`:"",
|
|
1
|
+
import n from"fs";import m from"path";import{execSync as k}from"child_process";import{log as s}from"../log/logUtils.js";function $(){return process.env.DEPLOYMENT_MODE==="k8s"}function _(){if(process.env.TEMPLATE_CACHE_DIR)return process.env.TEMPLATE_CACHE_DIR;if(n.existsSync("/local-cache"))return"/local-cache/templates";const o=process.env.PROJECT_WORKSPACE_DIR||"/app/project_workspace";return m.join(m.dirname(o),".template-cache")}function F(o){try{const e=n.readFileSync("/proc/mounts","utf8").split(`
|
|
2
|
+
`);let t="",c="local";for(const a of e){const r=a.split(/\s+/);if(r.length<3)continue;const d=r[1],u=r[2];o.startsWith(d)&&d.length>t.length&&(t=d,c=u)}return c.startsWith("fuse")?"fuse":"local"}catch{return"local"}}function D(o){try{const l=m.join(o,"package.json");if(!n.existsSync(l))return null;const e=JSON.parse(n.readFileSync(l,"utf8")),t=(e.name||"").toLowerCase(),c=e.dependencies||{};if(t.includes("vue3")||t.includes("vue-vite"))return"vue3";if(t.includes("react-vite")||t.includes("react"))return"react";if(c.vue)return"vue3";if(c.react)return"react"}catch{}return null}async function T(o){const l=_(),e=$();s("CACHE","INFO",`Template cache base dir: ${l}`,{deploymentMode:e?"k8s":"docker-compose"}),s("CACHE","INFO",`pnpm store dir: ${process.env.npm_config_store_dir||process.env.PNPM_STORE_DIR||"(default: ~/.local/share/pnpm/store)"}`);const t=[{name:"vue3",zip:"vue3-vite-template.zip"},{name:"react",zip:"react-vite-template.zip"}];n.mkdirSync(l,{recursive:!0});for(const c of t){const a=m.join(l,c.name),r=m.join(a,".cache-ready");if(n.existsSync(r)){s("CACHE","INFO",`Template cache already ready: ${c.name}`,{cachePath:a});continue}const d=m.join(o,c.zip);if(!n.existsSync(d)){s("CACHE","WARN",`Template zip not found, skip: ${d}`);continue}s("CACHE","INFO",`Warming up template cache: ${c.name}`,{zipPath:d,cachePath:a,k8sMode:e});const u=Date.now();try{if(n.mkdirSync(a,{recursive:!0}),k(`unzip -o "${d}" -d "${a}"`,{stdio:"pipe"}),O(a),!e){const p=process.env.npm_config_store_dir||process.env.PNPM_STORE_DIR||"",v=p?`store-dir=${p}
|
|
3
|
+
`:"",N=p?` --store-dir=${p}`:"",i=`# Template cache npmrc - auto-generated
|
|
4
4
|
package-import-method=copy
|
|
5
5
|
auto-install-peers=true
|
|
6
6
|
registry=https://registry.npmmirror.com
|
|
7
|
-
${
|
|
7
|
+
${v}`;n.writeFileSync(m.join(a,".npmrc"),i,"utf8"),s("CACHE","INFO",`Running pnpm install for ${c.name} cache...`),k(`cd "${a}" && pnpm install --prefer-offline${N}`,{stdio:"pipe",timeout:18e4,env:process.env})}n.writeFileSync(r,new Date().toISOString(),"utf8");const f=((Date.now()-u)/1e3).toFixed(1);s("CACHE","INFO",`Template cache ready: ${c.name}`,{elapsed:`${f}s`,cachePath:a,k8sMode:e,hasNodeModules:!e})}catch(f){s("CACHE","ERROR",`Template cache failed: ${c.name}`,{error:f.message});try{n.rmSync(a,{recursive:!0,force:!0})}catch{}}}}function g(o){const l=process.env.NODE_MODULES_LOCAL_DIR||"/local-cache/node-modules";return m.join(l,o,"node_modules")}async function C(o,l){const e=l||m.basename(o),t=m.join(o,"node_modules");let c=!1;try{if(n.lstatSync(t).isSymbolicLink()){if(n.existsSync(t))return s(e,"INFO","node_modules is a valid symlink, skip cache copy"),{cached:!1,reason:"already-exists"};c=!0,s(e,"WARN","node_modules is a broken symlink, will recreate",{symlink:t})}else return s(e,"INFO","node_modules already exists, skip cache copy"),{cached:!1,reason:"already-exists"}}catch{}const a=g(l);if(n.existsSync(a)){const i=Date.now();if(c)try{n.unlinkSync(t),s(e,"INFO","Removed broken symlink before recreating")}catch{}n.symlinkSync(a,t,"dir");const h=((Date.now()-i)/1e3).toFixed(3);return s(e,"INFO","Reused existing node_modules cache via symlink",{cache:a,symlink:t,elapsed:`${h}s`}),{cached:!0,elapsed:`${h}s`,strategy:"reuse-cache"}}const r=D(o);if(!r)return s(e,"WARN","Cannot detect template type, skip cache copy",{projectPath:o}),{cached:!1,reason:"unknown-template"};const d=_(),u=m.join(d,r,"node_modules"),f=m.join(d,r,".cache-ready");if(!n.existsSync(f)||!n.existsSync(u))return s(e,"INFO",`Template cache not ready: ${r}`,{cacheNodeModules:u}),{cached:!1,reason:"cache-not-ready"};const p=Date.now();if(F(o)==="fuse"){const i=g(l),h=m.dirname(i);s(e,"INFO",`JuiceFS detected, using symlink strategy: ${r}`,{source:u,localTarget:i,symlink:t});try{if(n.mkdirSync(h,{recursive:!0}),k(`cp -a "${u}" "${i}"`,{timeout:6e4,stdio:"pipe"}),c)try{n.unlinkSync(t),s(e,"INFO","Removed broken symlink before creating new one")}catch(S){s(e,"WARN",`Failed to remove broken symlink: ${S.message}`)}n.symlinkSync(i,t,"dir");const y=((Date.now()-p)/1e3).toFixed(1);return s(e,"INFO","node_modules ready via symlink",{templateType:r,elapsed:`${y}s`,symlink:`${t} \u2192 ${i}`}),{cached:!0,templateType:r,elapsed:y,strategy:"symlink"}}catch(y){s(e,"WARN",`Failed to setup node_modules symlink: ${y.message}`,{templateType:r});try{n.rmSync(i,{recursive:!0,force:!0})}catch{}try{n.unlinkSync(t)}catch{}return{cached:!1,reason:"symlink-failed",error:y.message}}}else{s(e,"INFO",`Local filesystem, using direct copy: ${r}`,{source:u,target:t});try{k(`cp -a "${u}" "${t}"`,{timeout:6e4,stdio:"pipe"});const i=((Date.now()-p)/1e3).toFixed(1);return s(e,"INFO","node_modules copied from cache successfully",{templateType:r,elapsed:`${i}s`}),{cached:!0,templateType:r,elapsed:i,strategy:"copy"}}catch(i){s(e,"WARN",`Failed to copy node_modules from cache: ${i.message}`,{templateType:r});try{n.rmSync(t,{recursive:!0,force:!0})}catch{}return{cached:!1,reason:"copy-failed",error:i.message}}}}function O(o){try{const e=n.readdirSync(o,{withFileTypes:!0}).filter(t=>!t.name.startsWith(".")&&t.name!=="node_modules");if(e.length===1&&e[0].isDirectory()){const t=m.join(o,e[0].name),c=m.join(o,`..tmp_${Date.now()}`);n.renameSync(t,c);const a=n.readdirSync(c);for(const r of a)n.renameSync(m.join(c,r),m.join(o,r));n.rmdirSync(c)}}catch{}}export{_ as getCacheBaseDir,g as getLocalNodeModulesPath,F as detectFilesystemType,D as detectTemplateType,$ as isK8sMode,T as warmupTemplateCache,C as copyNodeModulesFromCache};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nuwax-file-server",
|
|
3
3
|
"displayName": "nuwax-file-server",
|
|
4
|
-
"version": "1.3.0-beta.
|
|
4
|
+
"version": "1.3.0-beta.9",
|
|
5
5
|
"description": "Cross-platform file service deployment tool with start/stop/restart CLI commands",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "index.js",
|