diff --git a/ConfigBasic.js b/ConfigBasic.js new file mode 100644 index 0000000..5b2606a --- /dev/null +++ b/ConfigBasic.js @@ -0,0 +1,19 @@ +module.exports={ + protocol:'http', + host:'localhost', + port:undefined, + // 如果使用 https 协议,必须填写以下内容,或在命令行参数中设置: + // sslKey: '/etc/letsencrypt/live/bittic.org/privkey.pem', // ssl key file, + // sslCert: '/etc/letsencrypt/live/bittic.org/cert.pem', // ssl cert file, + // sslCA: '../SSL/ca_bundle.crt', // ssl ca file, + + deploy:{ + host:'', // 待部署到的主机 + port:'22', // 带部署到的主机的 SSH 端口 + root:'/home/tic/node.console.web', // 待部署到的目录路径 + dist:'dist', // 待部署到的文件夹 + user:'tic', // 登录用户名 + password:'', // 登录用户密码 + key:`${process.env.HOME}/.ssh/id_rsa`, // 登录用户私钥文件 + }, +} \ No newline at end of file diff --git a/ConfigUser.js b/ConfigCustom.js similarity index 100% rename from ConfigUser.js rename to ConfigCustom.js diff --git a/ConfigSys.js b/ConfigSys.js deleted file mode 100644 index 1d9a922..0000000 --- a/ConfigSys.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports={ - protocol:'http', - host:'localhost', - port:undefined, - // 如果使用 https 协议,必须填写以下内容,或在命令行参数中设置: - // sslKey: '/etc/letsencrypt/live/bittic.org/privkey.pem', // ssl key file, - // sslCert: '/etc/letsencrypt/live/bittic.org/cert.pem', // ssl cert file, - // sslCA: '../SSL/ca_bundle.crt', // ssl ca file, -} \ No newline at end of file diff --git a/README.md b/README.md index 6f84c1f..ed3da2e 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,17 @@ npm install ### Compiles and hot-reloads for development ``` -npm run serve +npm run dev ``` ### Compiles and minifies for production ``` -npm run build +npm run dist +``` + +### Compiles and minifies for production +``` +npm run deploy -- -H 待部署主机的IP或域名 -r 待部署主机上的路径 -u 用户名 -p 密码 ``` ### Run your tests diff --git a/deploy.js b/deploy.js index f3b04b6..341af93 100644 --- a/deploy.js +++ b/deploy.js @@ -1,31 +1,55 @@ const fs = require('fs') const path = require('path') const ssh = new (require('node-ssh'))() + +/********************* 读取命令行以及配置文件里的参数 **********************/ const commander = require('commander') +const deepmerge = require('deepmerge') + +var Config={} + +// 读取配置文件 +try { + if (fs.existsSync('./ConfigBasic.js')) { + Config=require('./ConfigBasic.js') + console.info('ConfigBasic loaded') + } + if (fs.existsSync('./ConfigCustom.js')) { // 如果存在,覆盖掉 ConfigBasic 里的默认参数 + Config=deepmerge(Config, require('./ConfigCustom.js')) // 注意,objectMerge后,产生了一个新的对象,而不是在原来的Config里添加 + console.info('ConfigCustom loaded') + } + if (fs.existsSync('./ConfigSecret.js')) { // 如果存在,覆盖掉 ConfigBasic 和 ConfigCustom 里的参数 + Config=deepmerge(Config, require('./ConfigSecret.js')) + console.info('ConfigSecret loaded') + } +}catch(err){ + console.error('Loading config files failed: '+err.message) +} commander .version('1.0', '-v, --version') // 默认是 -V。如果要 -v,就要加 '-v --version' -.option('-H, --host ', 'domain name or ip address of the target server') -.option('-P, --port ', 'ssh port number of the target server') -.option('-r, --root ', 'root directory to deploy on the target server') -.option('-d, --dist ', 'dist folder to deploy on the target server') -.option('-u, --user ', 'user id to login the target server') -.option('-k, --key ', 'user key file to login the target server') -.option('-p, --password ', 'user password to login the target server. You may have to enclose the password in ""') +.option('-H, --host ', `Host IP or domain name of the target server. Default to ${Config.deploy.host}`) +.option('-P, --port ', `Ssh port number of the target server. Default to ${Config.deploy.port}`) +.option('-r, --root ', `Path to deploy on the target server. Default to ${Config.deploy.root}`) +.option('-d, --dist ', `Folder to deploy on the target server. Default to ${Config.deploy.dist}`) +.option('-u, --user ', `User id to login the target server. Default to ${Config.deploy.user}`) +.option('-k, --key ', `User private key file to login the target server. Default to ${Config.deploy.key}`) +.option('-p, --password ', `User password to login the target server. You may have to enclose it in "". Default to ${Config.deploy.password}`) .parse(process.argv) -const root=commander.root // 本地的项目目录。似乎该目录必须已经存在于服务器上 -const dist=commander.dist||'dist' // 新系统将发布在这个目录里。建议为dist,和npm run build产生的目录一致,这样既可以远程自动部署,也可以直接登录服务器手动部署。 +const root=commander.root||Config.deploy.root // 本地的项目目录。似乎该目录必须已经存在于服务器上 console.log(` root = ${root} `) +const dist=commander.dist||Config.deploy.dist||'dist' // 新系统将发布在这个目录里。建议为dist,和npm run build产生的目录一致,这样既可以远程自动部署,也可以直接登录服务器手动部署。 console.log(` dist = ${dist} `) +const privateKeyFile=commander.key||Config.deploy.key||`${process.env.HOME}/.ssh/id_rsa` +console.log(` privateKeyFile = ${privateKeyFile}`) -const privateKeyFile=commander.key||`${process.env.HOME}/.ssh/id_rsa` const connection = { - host: commander.host, - port: commander.port||22, - username: commander.user||'tic', + host: commander.host||Config.deploy.host, + port: commander.port||Config.deploy.port||22, + username: commander.user||Config.deploy.user, privateKey: fs.existsSync(privateKeyFile)?privateKeyFile:undefined, - password: commander.password, + password: commander.password||Config.deploy.password, tryKeyboard: true, onKeyboardInteractive: (name, instructions, instructionsLang, prompts, finish) => { // 不起作用 if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) { @@ -33,8 +57,9 @@ const connection = { } }, } -console.log(connection) +console.log(` connection = ${JSON.stringify(connection)}`) +/************************ 连接到待部署的主机,拷贝文件到指定路径 ***************/ function subDirs(path) { const dirs = [path] if (fs.statSync(path).isFile()) { @@ -57,13 +82,13 @@ const necessaryPath = (path) => { } ssh.connect(connection).then(async () => { - console.log(`[ ${root} > mv ${dist} ${dist}-backup-${new Date().toISOString()} ... ]`) + console.log(`[ mv ${dist} ${dist}-backup-${new Date().toISOString()} ... ]`) await ssh.execCommand(`mv ${dist} ${dist}-backup-${new Date().toISOString()}`, { cwd:root }) - console.log(`[ ${root} > mkdir ${dist} ... ]`) + console.log(`[ mkdir ${dist} ... ]`) await ssh.execCommand(`mkdir ${dist}`, { cwd:root }) const toCreate = necessaryPath('./dist') for (const name of toCreate) { - console.log(`[ ${root} > mkdir ${dist}/${name.join('/')} ... ]`) + console.log(`[ mkdir ${dist}/${name.join('/')} ... ]`) await ssh.execCommand(`mkdir ${dist}/${name.join('/')}`, { cwd:root }) } @@ -77,13 +102,13 @@ ssh.connect(connection).then(async () => { return !baseName.endsWith('.map'); }, tick: (localPath, remotePath, error) => { - console.log(`"${localPath}" ===> "${remotePath}" ... ${error || 'succeeded!'}`) + console.log(`Uploading "${localPath}" ===> "${remotePath}" ${error || 'succeeded!'}`) err = error }, }) ssh.dispose() if (err) { - console.log('[ Upload failed! ]') + console.log('[ Uploaded with error! ]') process.exit(1) } else { console.log('[ Uploaded successfully! ]') diff --git a/package.json b/package.json index d0615c0..4d05a95 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,13 @@ { - "name": "console", + "name": "tic.node.console", "version": "0.1.0", "private": true, "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", - "deploy": "node ./deploy.js -r '/home/tic/console.web.site' -u tic", + "dev": "vue-cli-service serve", + "dist": "vue-cli-service build", + "deploy": "node ./deploy.js", + "daemon:sup": "cross-env NODE_ENV=production supervisor -i data.log,node_modules,dist server.js", + "daemon": "cross-env NODE_ENV=production pm2 start server.js -n node.console.web", "lint": "vue-cli-service lint", "test:unit": "vue-cli-service test:unit" }, @@ -23,7 +25,7 @@ "morgan": "^1.9.0", "serve-favicon": "^2.4.5", "socket.io": "^2.1.1", - "tic.crypto": "git+https://git.faronear.org/tic/tic.crypto#20181205", + "tic.crypto": "git+https://git.faronear.org/tic/tic.crypto#20190109_preview", "vue": "^2.5.17", "vue-axios": "^2.1.3", "vue-i18n": "^8.1.0", @@ -42,6 +44,7 @@ "babel-core": "7.0.0-bridge.0", "babel-jest": "^23.0.1", "babel-plugin-transform-imports": "^1.5.1", + "cross-env": "^5.1.3", "node-sass": "^4.9.0", "node-ssh": "^5.1.2", "sass-loader": "^7.0.1", diff --git a/server.js b/server.js index 74e5a51..8020a9b 100644 --- a/server.js +++ b/server.js @@ -9,15 +9,15 @@ function config(){ // 读取配置文件 try { - if (fs.existsSync('./ConfigSys.js')) { - Config=require('./ConfigSys.js') - console.info('ConfigSys loaded') + if (fs.existsSync('./ConfigBasic.js')) { + Config=require('./ConfigBasic.js') + console.info('ConfigBasic loaded') } - if (fs.existsSync('./ConfigUser.js')) { // 如果存在,覆盖掉 ConfigSys 里的默认参数 - Config=deepmerge(Config, require('./ConfigUser.js')) // 注意,objectMerge后,产生了一个新的对象,而不是在原来的Config里添加 - console.info('ConfigUser loaded') + if (fs.existsSync('./ConfigCustom.js')) { // 如果存在,覆盖掉 ConfigBasic 里的默认参数 + Config=deepmerge(Config, require('./ConfigCustom.js')) // 注意,objectMerge后,产生了一个新的对象,而不是在原来的Config里添加 + console.info('ConfigCustom loaded') } - if (fs.existsSync('./ConfigSecret.js')) { // 如果存在,覆盖掉 ConfigSys 和 ConfigUser 里的参数 + if (fs.existsSync('./ConfigSecret.js')) { // 如果存在,覆盖掉 ConfigBasic 和 ConfigCustom 里的参数 Config=deepmerge(Config, require('./ConfigSecret.js')) console.info('ConfigSecret loaded') } @@ -28,11 +28,11 @@ try { // 载入命令行参数 commander .version(Config.VERSION, '-v, --version') // 默认是 -V。如果要 -v,就要加 '-v --version' - .option('-H, --host ', 'host ip or domain name') - .option('-P, --protocol ', 'Web server protocol http|https|httpall, default to httpall') - .option('-p, --port ', 'Server port, default to 80|443') - .option('--sslCert ', 'SSL cert file') - .option('--sslKey ', 'SSL privkey file') + .option('-H, --host ', 'Host ip or domain name. Default to ' + Config.host) + .option('-P, --protocol ', 'Web server protocol http|https|httpall|http2https. Default to ' + Config.protocol) + .option('-p, --port ', `Server port. Default to ${Config.port?Config.port:'80|443'}`) + .option('--sslCert ', 'SSL cert file. Default to ' + Config.sslCert) + .option('--sslKey ', 'SSL privkey file. Default to ' + Config.sslKey) .option('--sslCA ', 'SSL ca bundle file') .parse(process.argv) @@ -101,15 +101,22 @@ async function init(){ /*** 设置全局对象 ***/ }else if ('httpall'===wo.Config.protocol) { let portHttp=wo.Config.port?wo.Config.port:80 // 如果port参数已设置,使用它;否则默认为80 require('http').createServer(server).listen(portHttp, function(err) { - console.log('Server listening on %s://%s:%d for %s environment', 'httpall:http', wo.Config.host, portHttp, server.settings.env) + console.log('Server listening on [%s] http://%s:%d for %s environment', wo.Config.protocol, wo.Config.host, portHttp, server.settings.env) }) let portHttps=(wo.Config.port && wo.Config.port!==80)?wo.Config.port+443:443 // 如果port参数已设置,使用它+443;否则默认为443 require('https').createServer({ key: fs.readFileSync(wo.Config.sslKey), cert: fs.readFileSync(wo.Config.sslCert) // , ca: [ fs.readFileSync(wo.Config.sslCA) ] // https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener }, server).listen(portHttps, function(err){ - console.log('Server listening on %s://%s:%d for %s environment', 'httpall:https', wo.Config.host, portHttps, server.settings.env) + console.log('Server listening on [%s] https://%s:%d for %s environment', wo.Config.protocol, wo.Config.host, portHttps, server.settings.env) }) + }else if ('http2https'===wo.Config.protocol) { + wo.Config.port = wo.Config.port || 80 + require('http').createServer(express().all('*', function(ask, reply){ /* 错误的API调用进入这里。*/ + reply.redirect(`https://${wo.Config.host}`) + })).listen(wo.Config.port, function(err){ + console.log('Server listening on [%s] http://%s:%d for %s environment', wo.Config.protocol, wo.Config.host, wo.Config.port, server.settings.env) + }) } })() \ No newline at end of file diff --git a/src/i18n/en.js b/src/i18n/en.js index 3d2fe74..71e3546 100644 --- a/src/i18n/en.js +++ b/src/i18n/en.js @@ -1,6 +1,6 @@ -export default { +export default { login: { - title: 'TIC Node Console', + title: 'TIC Console', inputLabel: '请输入密语,例如:Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?', loginBtn: 'Sign In', secwordBtn: 'Create Secword', @@ -27,12 +27,12 @@ tableNone: '暂无数据', }, network: { - tableHeader: '当前邻居', - searchHolder: '请输入该输入的', - thIndex: '序号', - thAddress: '连接地址', - thStatus: '连接状态', - thPeerOwnerAddress: '邻居主人的账户地址', + tableHeader: 'Current neighbouring peer list', + searchHolder: 'Please input peer owner address or network url', + thIndex: 'Nr.', + thAccessPoint: 'Url', + thStatus: 'Status', + thOwnerAddress: 'Owner address', tableNone: '暂无数据', }, chain: { diff --git a/src/i18n/zh-CN.js b/src/i18n/zh-CN.js index e4b85a3..dd8795e 100644 --- a/src/i18n/zh-CN.js +++ b/src/i18n/zh-CN.js @@ -1,6 +1,6 @@ -export default { +export default { login: { - title: 'TIC Node 节点控制台', + title: 'TIC 控制台', inputLabel: '请输入密语,例如:Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?', loginBtn: '登录', secwordBtn: '新密语', @@ -27,12 +27,12 @@ tableNone: '暂无数据', }, network: { - tableHeader: '当前邻居', - searchHolder: '请输入该输入的', + tableHeader: '当前邻居节点列表', + searchHolder: '请输入主人地址或节点网址', thIndex: '序号', - thAddress: '连接地址', + thAccessPoint: '连接网址', + thOwnerAddress: '邻居主人地址', thStatus: '连接状态', - thPeerOwnerAddress: '邻居主人的账户地址', tableNone: '暂无数据', }, chain: { diff --git a/src/services/axios.js b/src/services/axios.js index bf4cb3b..413e834 100644 --- a/src/services/axios.js +++ b/src/services/axios.js @@ -1,5 +1,5 @@ -import axios from 'axios' +import axios from 'axios' // import app from './app' // 控制台应当默认访问控制台所在主机(localhost 或者 window.location.hostname)的 TIC后台,或者让用户在控制台启动时,自己设置一个节点主机。 -axios.defaults.baseURL = `http://${window.location.hostname}:6842/api/` +axios.defaults.baseURL = `${window.location.protocol}//${window.location.hostname}:${window.location.port}/api/` diff --git a/src/store/modules/chain.js b/src/store/modules/chain.js index 8103e1c..57f6403 100644 --- a/src/store/modules/chain.js +++ b/src/store/modules/chain.js @@ -1,4 +1,4 @@ -import axios from 'axios' +import axios from 'axios' export default { namespaced: true, @@ -13,10 +13,10 @@ export default { ], itemList: [ { - height: '1', - timestamp: '2011-11-20', - type: '0', - hash: 'null', + height: 'loading', + timestamp: 'loading', + type: 'loading', + hash: 'loading', }, ], fetching: false, diff --git a/src/store/modules/network.js b/src/store/modules/network.js index 49d1a05..9dd876d 100644 --- a/src/store/modules/network.js +++ b/src/store/modules/network.js @@ -1,4 +1,4 @@ -import axios from 'axios' +import axios from 'axios' export default { namespaced: true, @@ -7,17 +7,17 @@ export default { pageEntity: [5, 10, 25], headers: [ { text: 'network.thIndex', value: 'index', align: 'center' }, - { text: 'network.thAddress', value: 'address', align: 'center' }, + { text: 'network.thAccessPoint', value: 'accessPoint', align: 'center' }, + { text: 'network.thOwnerAddress', value: 'ownerAddress', align: 'center' }, { text: 'network.thStatus', value: 'status', align: 'center' }, - { text: 'network.thPeerOwnerAddress', value: 'peerOwnerAddress', align: 'center' }, ], itemList: [ - // { - // index: '', - // address: '', - // status: '', - // peerOwnerAddress: '', - // }, + { + index: 'loading', + accessPoint: 'loading', + ownerAddress: 'loading', + status: 'loading', + }, ], fetching: false, filter: '', @@ -42,7 +42,8 @@ export default { actions: { async updateNetInfo({ state, commit }) { - let result = await axios.post('Peer/sharePeer', {}) + let result = await axios.post('Peer/getPeerList', {}) + commit('setPeerNumber', (result && result.data) ? result.data.length : 0) commit('setItemList', result.data || []) diff --git a/src/store/modules/node.js b/src/store/modules/node.js index 8fad66f..9ebdcfa 100644 --- a/src/store/modules/node.js +++ b/src/store/modules/node.js @@ -12,10 +12,10 @@ export default { ], itemList: [ { - height: '1', - timestamp: '2011-11-20', - type: '0', - hash: 'null', + height: 'loading', + timestamp: 'loading', + type: 'loading', + hash: 'loading', }, ], fetching: false, diff --git a/src/views/Chain.vue b/src/views/Chain.vue index a3552d3..6395135 100644 --- a/src/views/Chain.vue +++ b/src/views/Chain.vue @@ -89,10 +89,11 @@ import Vue from 'vue' import store from '../store' import VueSocketio from 'vue-socket.io' import io from 'socket.io-client' +import axios from 'axios' const { mapState, mapGetters, mapActions, mapMutations } = createNamespacedHelpers('chain') -Vue.use(VueSocketio, io(`http://${window.location.hostname}:6842`), store) // todo: luk: 整个控制台应当依赖于同一个全局的节点地址变量。 +Vue.use(VueSocketio, io(axios.defaults.baseURL), store) export default { name: 'Chain', diff --git a/src/views/Home.vue b/src/views/Home.vue index d4f7b34..0355601 100644 --- a/src/views/Home.vue +++ b/src/views/Home.vue @@ -101,7 +101,7 @@ export default { display: flex; - $sideWidth: 260px; + $sideWidth: 240px; $headerHeight: 64px; .side-menus { &.collapsed { @@ -109,6 +109,7 @@ export default { } width: $sideWidth; + min-width: 120px; background: url('../assets/images/bg.jpg') center; background-size: cover; overflow-y: auto; diff --git a/src/views/Network.vue b/src/views/Network.vue index 4f46b2c..9777a9e 100644 --- a/src/views/Network.vue +++ b/src/views/Network.vue @@ -1,4 +1,4 @@ -