From 6cc40dda52b513679ef58327da336db8572f1fc6 Mon Sep 17 00:00:00 2001 From: Luk Lu Date: Sat, 19 Jun 2021 22:00:33 +0800 Subject: [PATCH] u --- package.json | 2 + server.js | 163 +++++++++++++++++++++++++++++---------------------- 2 files changed, 95 insertions(+), 70 deletions(-) diff --git a/package.json b/package.json index d8f0439..c4b0ec7 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,13 @@ "deepmerge": "^2.1.1", "errorhandler": "^1.5.0", "express": "^4.16.2", + "http-proxy": "^1.17.0", "method-override": "^2.3.10", "morgan": "^1.9.0", "multer": "^1.4.2", "serve-favicon": "^2.5.0", "sol.enviconfig": "git+https://git.faronear.org/npm/sol.enviconfig", + "sol.tool": "git+https://git.faronear.org/npm/sol.tool", "sol.webtoken": "git+https://git.faronear.org/npm/sol.webtoken", "vhost": "^3.0.2" }, diff --git a/server.js b/server.js index 62d5377..21a75d2 100644 --- a/server.js +++ b/server.js @@ -1,38 +1,54 @@ const fs = require('fs') const path = require('path') +const tool = require('sol.tool') +const webtoken = require('sol.webtoken') +const express = require('express') -const Config = require('sol.enviconfig')({ - commanderOptions: [ - // 命令行里可以接受的参数。将传给 config.js 里的 commander。每个参数的定义格式是 [参数名,参数键,描述],后两者用于传给commander,取值后覆盖掉Config里的同名变量。 - ['protocol', '-P, --protocol ', 'Web Server protocol: http|https|httpall.'], - ['host', '-H, --host ', 'Host IP or domain name, default to localhost.'], - ['port', '-p, --port ', 'HTTP port number.'], - ['from', '-f, --from ', 'Path to serve as website'], - ], - // 最基础的必须的默认配置,如果用户什么也没有提供 - protocol: 'http', - host: 'localhost', - from: './dist', // local path to serve as webroot - // 如果使用虚拟主机 - /* - vhosts: [ - { webroot: 'dist', webindex: 'index.html', domainList: ['']} +const wo = (global.wo = { + envi : require('sol.enviconfig')({ + commanderOptions: [ + // 命令行里可以接受的参数。将传给 config.js 里的 commander。每个参数的定义格式是 [参数名,参数键,描述],后两者用于传给commander,取值后覆盖掉Config里的同名变量。 + ['protocol', '-P, --protocol ', 'Web Server protocol: http|https|httpall.'], + ['host', '-H, --host ', 'Host IP or domain name, default to localhost.'], + ['port', '-p, --port ', 'HTTP port number.'], + ['from', '-f, --from ', 'Path to serve as website'], + ['ssl', '--ssl ', 'SSL options in JSON string.'], ], - */ + // 最基础的必须的默认配置,如果用户什么也没有提供 + protocol: 'http', + host: 'localhost', + from: './dist', // local path to serve as webroot + // 如果使用虚拟主机 + /* + vhosts: [ + { webroot: 'dist', webindex: 'index.html', domainList: ['']} + ], + */ + }) }) -if (typeof Config.ssl === 'string') { - Config.ssl = eval(`(${Config.ssl})`) +if (typeof wo.envi.ssl === 'string') { + wo.envi.ssl = eval(`(${wo.envi.ssl})`) } ;(function serve() { console.log('★★★★★★★★ Starting Server ★★★★★★★★') - const express = require('express') const server = express() - const webtoken = require('sol.webtoken') + +// const greenlock = require('greenlock-express').create({ +// version: 'draft-11', +// server: 'https://acme-v02.api.letsencrypt.org/directory', // for test: acme-staging-v02 +// agreeTos: true, +// communityMember: false, +// store: require('greenlock-store-fs'), +// email: 'ssl@faronear.org', +// approvedDomains: wo.wo.envi.sslDomains, +// configDir: path.resolve(__dirname, 'ssl'), +// app: server, +// }) /*** 通用中间件 ***/ - server.use(require('morgan')('development' === Config.prodev ? 'dev' : 'combined')) + server.use(require('morgan')('development' === wo.envi.prodev ? 'dev' : 'combined')) server.use(require('body-parser').json()) server.use(require('body-parser').urlencoded({ extended: false })) server.use(require('cookie-parser')()) @@ -50,7 +66,7 @@ if (typeof Config.ssl === 'string') { filename: function (req, file, cb) { // 注意,req.body 也许还没有信息,因为这取决于客户端发送body和file的顺序。 let ext = file.originalname.replace(/^.*\.(\w+)$/, '$1') - let _passtokenSource = webtoken.verifyToken(req.headers._passtoken, Config.tokenKey) || {} + let _passtokenSource = webtoken.verifyToken(req.headers._passtoken, wo.envi.tokenKey) || {} let filename = `${req.path.replace(/^\/api\d*/, '')}_${_passtokenSource.uuid}_${Date.now()}.${ext}` cb(null, filename) }, @@ -64,16 +80,16 @@ if (typeof Config.ssl === 'string') { // vhost 匹配了域名,就执行;不匹配,就next() // express.static 找到了具体文件,就返回;找不到,就next() // 所以,如果 vhost匹配了域名,且static找到了文件,就结束了。如果 vhost 匹配了域名,但static找不到文件,就继续往下。 - if (!Config.vhosts) { + if (!wo.envi.vhosts) { server.use( - express.static(path.join(process.cwd(), Config.from), { + express.static(path.join(process.cwd(), wo.envi.from), { index: 'index.html', }) ) // server.use(require('serve-favicon')(path.join(process.cwd(), 'public', 'favicon.ico'))) // uncomment after placing your favicon in /public } else { let vhost = require('vhost') - for (let h of Config.vhosts) { + for (let h of wo.envi.vhosts) { for (let domain of h.domainList) { server.use( vhost( @@ -94,26 +110,27 @@ if (typeof Config.ssl === 'string') { /*** 启动 Web 服务 ***/ let webServer - let ipv4 = getMyIp() - let portHttp = Config.port || 80 - let portHttps = Config.port || 443 - if ('http' === Config.protocol) { + let ipv4 = tool.getMyIp() + let portHttp = wo.envi.port || 80 + let portHttps = wo.envi.port || 443 + if ('http' === wo.envi.protocol) { webServer = require('http') .createServer(server) .listen(portHttp, function (err) { if (err) console.log(err) else console.log( - `[${new Date().toJSON()}] Server listening on ${Config.protocol}://${Config.host}:${portHttp} with IPv4=${ipv4} for ${Config.prodev} environment` + `[${new Date().toJSON()}] Server listening on ${wo.envi.protocol}://${wo.envi.host}:${portHttp} with IPv4=${ipv4} for ${wo.envi.prodev} environment` ) }) - } else if ('https' === Config.protocol) { + } else if ('https' === wo.envi.protocol) { webServer = require('https') .createServer( +// wo.wo.envi.ssl.type==='greenlock' ? greenlock.httpsOptions : { - key: fs.readFileSync(Config.ssl.file.key), - cert: fs.readFileSync(Config.ssl.file.cert), - // ca: [ fs.readFileSync(Config.ssl.file.ca) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener + key: fs.readFileSync(wo.envi.ssl.file.key), + cert: fs.readFileSync(wo.envi.ssl.file.cert), + // ca: [ fs.readFileSync(wo.envi.ssl.file.ca) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener }, server ) @@ -121,32 +138,38 @@ if (typeof Config.ssl === 'string') { if (err) console.log(err) else console.log( - `[${new Date().toJSON()}] Server listening on ${Config.protocol}://${Config.host}:${portHttps} with IPv4=${ipv4} for ${Config.prodev} environment` + `[${new Date().toJSON()}] Server listening on ${wo.envi.protocol}://${wo.envi.host}:${portHttps} with IPv4=${ipv4} for ${wo.envi.prodev} environment` ) }) - } else if ('httpall' === Config.protocol) { + } else if ('httpall' === wo.envi.protocol) { portHttp = 80 - require('http') +// if (wo.wo.envi.ssl.type==='greenlock') { +// greenlock.listen(portHttp, portHttps, function (err) { +// if (err) console.log(err) +// else console.log(`Server listening on [${wo.wo.envi.protocol}] http=>https://${wo.wo.envi.host}:${portHttp}=>${portHttps} for ${server.settings.env} environment`) +// }) +// }else { + require('http') .createServer( server.all('*', function (ask, reply) { - reply.redirect(`https://${Config.host}:${portHttps}`) + reply.redirect(`https://${wo.envi.host}:${portHttps}`) }) ) .listen(portHttp, function (err) { if (err) console.log(err) else console.log( - `[${new Date().toJSON()}] Server redirecting from [${Config.protocol}] http://${Config.host}:${portHttp} with IPv4=${ipv4} for ${ - Config.prodev + `[${new Date().toJSON()}] Server redirecting from [${wo.envi.protocol}] http://${wo.envi.host}:${portHttp} with IPv4=${ipv4} for ${ + wo.envi.prodev } environment` ) }) webServer = require('https') .createServer( { - key: fs.readFileSync(Config.ssl.file.key), - cert: fs.readFileSync(Config.ssl.file.cert), - // ca: [ fs.readFileSync(Config.ssl.file.ca) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener + key: fs.readFileSync(wo.envi.ssl.file.key), + cert: fs.readFileSync(wo.envi.ssl.file.cert), + // ca: [ fs.readFileSync(wo.envi.ssl.file.ca) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener }, server ) @@ -154,47 +177,47 @@ if (typeof Config.ssl === 'string') { if (err) console.log(err) else console.log( - `[${new Date().toJSON()}] Server listening on [${Config.protocol}] https://${Config.host}:${portHttps} with IPv4=${ipv4} for ${ - Config.prodev + `[${new Date().toJSON()}] Server listening on [${wo.envi.protocol}] https://${wo.envi.host}:${portHttps} with IPv4=${ipv4} for ${ + wo.envi.prodev } environment` ) }) - } else if ('http2https' === Config.protocol) { +// } + } else if ('http2https' === wo.envi.protocol) { webServer = require('http') .createServer( server.all('*', function (ask, reply) { - /* 错误的API调用进入这里。*/ reply.redirect(`https://${Config.host}`) + reply.redirect(`https://${wo.envi.host}`) }) + // 或者 require('redirect-https')() // https://www.npmjs.com/package/redirect-https ) .listen(portHttp, function (err) { if (err) console.log(err) else console.log( - `[${new Date().toJSON()}] Server listening on ${Config.protocol}://${Config.host}:${portHttp} with IPv4=${ipv4} for ${Config.prodev} environment` + `[${new Date().toJSON()}] Server listening on ${wo.envi.protocol}://${wo.envi.host}:${portHttp} with IPv4=${ipv4} for ${wo.envi.prodev} environment` ) }) + }else if ('proxy' === wo.envi.protocol) { + var proxy=require('http-proxy').createProxyServer({ + ssl: { + key: fs.readFileSync(wo.envi.ssl.file.key), + cert: fs.readFileSync(wo.envi.ssl.file.cert), + // ca: [ fs.readFileSync(wo.envi.sslCA) ] // https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener + }, + target: `http://127.0.0.1:${wo.envi.proxyPort}`, // iOS 的 AppStore 要求支持IPv6,只能用国外的vultr.com服务器,因此再代理回国内的solet主机。 +// secure: true, // proxying https to https + ws: true // proxying websockets + }) + proxy.on('error', function (err, req, res) { + res.writeHead(500, { 'Content-Type': 'text/plain' }) + res.end('Proxy Error.') + }) + wo.envi.port=443 + proxy.listen(wo.envi.port) + console.info('server listening on %s://%s:%d as proxy', wo.envi.protocol, wo.envi.host, wo.envi.port) } + return webServer })() - -// ==================== - -function getMyIp() { - const os = require('os') - let ipv4 = null - try { - let ifaces = os.networkInterfaces() - Object.keys(ifaces).forEach(function (ifname) { - ifaces[ifname].forEach(function (iface) { - if ('IPv4' === iface.family && iface.internal === false) { - // console.log('ip='+iface.address) - ipv4 = iface.address - } - }) - }) - } catch (e) { - console.log('ERROR in getMyIP(): ' + e.message) - } - return ipv4 -}