wo-base-webserver/server.js

194 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs')
const path = require('path')
const os = require('os')
function config(){
const commander = require('commander')
const deepmerge = require('deepmerge')
var Config={}
// 读取配置文件
try {
let configFile
if (fs.existsSync(configFile = path.join(process.cwd(), './ConfigBasic.js'))) {
Config=require(configFile)
console.info(`${configFile} loaded`)
}else {
console.info(`Missing and omitting ${configFile}`)
}
if (fs.existsSync(configFile = path.join(process.cwd(), './ConfigCustom.js'))) { // 如果存在,覆盖掉 ConfigBasic 里的默认参数
Config=deepmerge(Config, require(configFile)) // 注意objectMerge后产生了一个新的对象而不是在原来的Config里添加
console.info(`${configFile} loaded`)
}else {
console.info(`Missing and omitting ${configFile}`)
}
if (fs.existsSync(configFile = path.join(process.cwd(), './ConfigSecret.js'))) { // 如果存在,覆盖掉 ConfigBasic 和 ConfigCustom 里的参数
Config=deepmerge(Config, require(configFile))
console.info(`${configFile} loaded`)
}else {
console.info(`Missing and omitting ${configFile}`)
}
}catch(err){
console.error(`Loading config files failed: ${err.message}`)
}
// 载入命令行参数
commander
.version(Config.VERSION, '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
.option('-e, --env <env>', 'Environment. Default to ' + (Config.env || process.env.NODE_ENV))
.option('-H, --host <host>', `Host ip or domain name. Default to ${Config.host}`)
.option('-P, --protocol <protocol>', `Web server protocol http|https|httpall|http2https. Default to ${Config.protocol}`)
.option('-p, --port <port>', `Server port. Default to ${Config.port?Config.port:'80|443'}`)
.option('-f, --from <from>', `Distribution folder to serve as webroot. Default to ${Config.from}`)
.option('--sslType <type>', `SSL provider type. Default to ${Config.sslType}`)
.option('--sslCert <cert>', `SSL certificate file. Default to ${Config.sslCert}`)
.option('--sslKey <key>', `SSL private key file. Default to ${Config.sslKey}`)
.option('--sslCA <ca>', 'SSL ca bundle file')
.parse(process.argv)
// 把命令行参数 合并入配置。
Config.env = commander.env || Config.env || process.env.NODE_ENV
if (Config.env === 'production') {
Config = deepmerge(Config, Config.production)
}
delete Config.production
Config.host=commander.host || Config.host
Config.protocol=commander.protocol || Config.protocol || 'http'
Config.port=parseInt(commander.port) || parseInt(Config.port) || (Config.protocol==='http'?80:Config.protocol==='https'?443:undefined) // 端口默认为 http:80, https:443, httpall: 80|443
Config.from = commander.from || Config.from || 'dist'
Config.sslType = commander.sslType || Config.sslType
Config.sslCert=commander.sslCert || Config.sslCert
Config.sslKey=commander.sslKey || Config.sslKey
Config.sslCA=commander.sslCA || Config.sslCA
console.info('Configuration is ready.')
return Config
}
async function init(){ /*** 设置全局对象 ***/
global.wo={} // wo 代表 world或是当前的命名空间把各种类都放在这里防止和其他库的冲突。
wo.Config=config() // 依次载入系统默认配置、用户配置文件、命令行参数
}
(async function start(){
await init()
const express=require('express')
const server = express()
const greenlock = (['https', 'httpall'].indexOf(wo.Config.protocol)>=0 && wo.Config.sslType === '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.Config.sslDomainList,
configDir: path.resolve(process.cwd(), 'ssl'),
app: server,
}) : null
/*** 通用中间件 ***/
server.use(require('morgan')('development'===server.get('env')?'dev':'combined'))
server.use(require('body-parser').json())
server.use(require('body-parser').urlencoded({ extended: false }))
server.use(require('cookie-parser')())
server.use(require('compression')())
/*** 路由 ***/
// vhost 匹配了域名就执行不匹配就next()
// express.static 找到了具体文件就返回找不到就next()
// 所以,如果 vhost匹配了域名且static找到了文件就结束了。如果 vhost 匹配了域名但static找不到文件就继续往下。
if (!wo.Config.vhosts) {
server.use(express.static(path.join(process.cwd(), wo.Config.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 wo.Config.vhosts) {
for (let domain of h.domainList) {
server.use(vhost(domain, express.static(path.join(process.cwd(), h.webroot), {index: h.webindex})))
}
}
}
/*** 路由 ***/
//var router = express.Router()
//router.get('/path', function(req,res) { res.redirect('target') })
//server.use(router)
let ipv4 = getMyIp()
/*** 启动 Web 服务 ***/
let webServer
if ('http' === wo.Config.protocol) {
webServer = require('http').createServer(server).listen(wo.Config.port, function (err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on ${wo.Config.protocol}://${wo.Config.host}:${wo.Config.port} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
} else if ('https' === wo.Config.protocol) {
webServer = require('https').createServer(wo.Config.sslType === 'greenlock' ? greenlock.httpsOptions : {
key: fs.readFileSync(wo.Config.sslKey),
cert: fs.readFileSync(wo.Config.sslCert),
// ca: [ fs.readFileSync(wo.Config.sslCA) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener
}, server).listen(wo.Config.port, function (err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on ${wo.Config.protocol}://${wo.Config.host}:${wo.Config.port} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
} else if ('httpall' === wo.Config.protocol) {
let portHttp = wo.Config.port ? wo.Config.port : 80 // 如果port参数已设置使用它否则默认为80
let portHttps = (wo.Config.port && wo.Config.port !== 80) ? wo.Config.port + 443 : 443 // 如果port参数已设置使用它+443否则默认为443
if (wo.Config.sslType === 'greenlock') {
webServer = greenlock.listen(portHttp, portHttps, function (err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on [${wo.Config.protocol}] http=>https://${wo.Config.host}:${portHttp}=>${portHttps} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
} else {
require('http').createServer(server.all('*', function (ask, reply) {
reply.redirect(`https://${wo.Config.host}:${portHttps}`)
})).listen(portHttp, function(err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on [${wo.Config.protocol}] http://${wo.Config.host}:${portHttp} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
webServer = require('https').createServer({
key: fs.readFileSync(wo.Config.sslKey),
cert: fs.readFileSync(wo.Config.sslCert),
// ca: [ fs.readFileSync(wo.Config.sslCA) ] // only for self-signed certificate: https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener
}, server).listen(portHttps, function (err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on [${wo.Config.protocol}] https://${wo.Config.host}:${portHttps} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
}
} else if ('http2https' === wo.Config.protocol) {
wo.Config.port = wo.Config.port || 80
webServer = require('http').createServer(server.all('*', function (ask, reply) { /* 错误的API调用进入这里。*/
reply.redirect(`https://${wo.Config.host}`)
})).listen(wo.Config.port, function (err) {
if (err) console.log(err)
else console.log(`[${new Date().toJSON()}] Server listening on ${wo.Config.protocol}://${wo.Config.host}:${wo.Config.port} with IPv4=${ipv4} for ${server.settings.env} environment`)
})
}
})()
// ====================
function getMyIp() {
var ipv4=null
try {
var 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
}