web-statics/scripts/verify-nginx.mjs
2026-10-22 22:19:50 +08:00

39 lines
3.8 KiB
JavaScript

import fs from 'node:fs/promises';
import path from 'node:path';
import net from 'node:net';
import { spawn, execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { root, write, writeJSON, jsonFile } from './lib.mjs';
const exec=promisify(execFile);
const folder=path.join(root,'.cache','nginx-test');
const binary=process.env.NGINX_BINARY||path.join(root,'.cache','nginx','usr','sbin','nginx');
async function freePort(){const socket=net.createServer();await new Promise(resolve=>socket.listen(0,'127.0.0.1',resolve));const port=socket.address().port;await new Promise(resolve=>socket.close(resolve));return port;}
const ports=await Promise.all([freePort(),freePort(),freePort()]);
const configurations=[];
for(const [i,id] of ['jskcloud','scck'].entries()){
let config=await fs.readFile(path.join(root,id,'nginx.conf.example'),'utf8');
config=config.replace('listen 80;',`listen 127.0.0.1:${ports[i]};`).replace(`/var/www/${id}`,path.join(root,id));configurations.push(config);
}
let shared=await fs.readFile(path.join(root,'nginx-subdirectories.conf.example'),'utf8');shared=shared.replace('listen 80;',`listen 127.0.0.1:${ports[2]};`).replace('/var/www/web-statics',root);configurations.push(shared);
const config=`user root;\nworker_processes 1;\npid ${folder}/nginx.pid;\nerror_log stderr;\nevents { worker_connections 256; }\nhttp {\n access_log off;\n client_body_temp_path ${folder}/body;\n proxy_temp_path ${folder}/proxy;\n fastcgi_temp_path ${folder}/fastcgi;\n uwsgi_temp_path ${folder}/uwsgi;\n scgi_temp_path ${folder}/scgi;\n types { text/html html; text/css css; application/javascript js; application/json json; image/png png; image/jpeg jpg jpeg; image/gif gif; image/webp webp; image/svg+xml svg; image/x-icon ico; font/woff woff; font/woff2 woff2; font/ttf ttf; application/vnd.ms-fontobject eot; }\n${configurations.join('\n')}\n}`;
await write(path.join(folder,'nginx.conf'),config);
await exec(binary,['-t','-p',folder,'-c',path.join(folder,'nginx.conf')]);
console.log('nginx: all three example configurations passed syntax validation');
const processNginx=spawn(binary,['-p',folder,'-c',path.join(folder,'nginx.conf'),'-g','daemon off;'],{stdio:['ignore','ignore','pipe']});
const exit=new Promise(resolve=>processNginx.once('exit',resolve));let logs='';processNginx.stderr.on('data',chunk=>{logs+=chunk;});
const report={checkedAt:new Date().toISOString(),configurations:3,checks:0,errors:[]};
try{
for(let i=0;i<30;i++){try{await fetch(`http://127.0.0.1:${ports[0]}/`);break;}catch{await new Promise(resolve=>setTimeout(resolve,100));}}
for(const [i,id] of ['jskcloud','scck'].entries()){
const manifest=await jsonFile(path.join(root,id,'site-manifest.json'));
for(const base of [`http://127.0.0.1:${ports[i]}`,`http://127.0.0.1:${ports[2]}/${id}`]){
for(const item of manifest.pages){const r=await fetch(base+'/'+item.file);report.checks++;if(r.status!==200)report.errors.push({url:base+'/'+item.file,status:r.status});await r.arrayBuffer();}
for(const bad of ['console/','console/v1/common','clientarea/','cart/goods.htm?id=4','login.htm','regist.htm','finance.htm','plugin/27/ticket.htm','en/login.htm','en/console/','missing-page.html']){const r=await fetch(base+'/'+bad);report.checks++;if(r.status!==404)report.errors.push({url:base+'/'+bad,status:r.status,expected:404});await r.arrayBuffer();}
const css=await fetch(base+'/assets/site.css');report.checks++;if(!css.headers.get('content-type')?.startsWith('text/css'))report.errors.push({url:base+'/assets/site.css',message:'Incorrect CSS MIME type'});await css.arrayBuffer();
}
}
console.log(JSON.stringify(report,null,2));await writeJSON(path.join(root,'test-results','nginx.json'),report);
if(report.errors.length)process.exitCode=1;
}catch(e){console.error(logs);throw e;}finally{processNginx.kill('SIGTERM');await exit;}