merge origin

This commit is contained in:
Nova
2019-02-10 13:07:31 +08:00
16 changed files with 143 additions and 90 deletions

19
ConfigBasic.js Normal file
View File

@@ -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`, // 登录用户私钥文件
},
}

View File

@@ -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,
}

View File

@@ -7,12 +7,17 @@ npm install
### Compiles and hot-reloads for development ### Compiles and hot-reloads for development
``` ```
npm run serve npm run dev
``` ```
### Compiles and minifies for production ### 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 ### Run your tests

View File

@@ -1,31 +1,55 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const ssh = new (require('node-ssh'))() const ssh = new (require('node-ssh'))()
/********************* 读取命令行以及配置文件里的参数 **********************/
const commander = require('commander') 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 commander
.version('1.0', '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version' .version('1.0', '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
.option('-H, --host <host>', 'domain name or ip address of the target server') .option('-H, --host <host>', `Host IP or domain name of the target server. Default to ${Config.deploy.host}`)
.option('-P, --port <port>', 'ssh port number of the target server') .option('-P, --port <port>', `Ssh port number of the target server. Default to ${Config.deploy.port}`)
.option('-r, --root <root>', 'root directory to deploy on the target server') .option('-r, --root <root>', `Path to deploy on the target server. Default to ${Config.deploy.root}`)
.option('-d, --dist <dist>', 'dist folder to deploy on the target server') .option('-d, --dist <dist>', `Folder to deploy on the target server. Default to ${Config.deploy.dist}`)
.option('-u, --user <user>', 'user id to login the target server') .option('-u, --user <user>', `User id to login the target server. Default to ${Config.deploy.user}`)
.option('-k, --key <key>', 'user key file to login the target server') .option('-k, --key <key>', `User private key file to login the target server. Default to ${Config.deploy.key}`)
.option('-p, --password <password>', 'user password to login the target server. You may have to enclose the password in ""') .option('-p, --password <password>', `User password to login the target server. You may have to enclose it in "". Default to ${Config.deploy.password}`)
.parse(process.argv) .parse(process.argv)
const root=commander.root // 本地的项目目录。似乎该目录必须已经存在于服务器上 const root=commander.root||Config.deploy.root // 本地的项目目录。似乎该目录必须已经存在于服务器上
const dist=commander.dist||'dist' // 新系统将发布在这个目录里。建议为dist和npm run build产生的目录一致这样既可以远程自动部署也可以直接登录服务器手动部署。
console.log(` root = ${root} `) console.log(` root = ${root} `)
const dist=commander.dist||Config.deploy.dist||'dist' // 新系统将发布在这个目录里。建议为dist和npm run build产生的目录一致这样既可以远程自动部署也可以直接登录服务器手动部署。
console.log(` dist = ${dist} `) 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 = { const connection = {
host: commander.host, host: commander.host||Config.deploy.host,
port: commander.port||22, port: commander.port||Config.deploy.port||22,
username: commander.user||'tic', username: commander.user||Config.deploy.user,
privateKey: fs.existsSync(privateKeyFile)?privateKeyFile:undefined, privateKey: fs.existsSync(privateKeyFile)?privateKeyFile:undefined,
password: commander.password, password: commander.password||Config.deploy.password,
tryKeyboard: true, tryKeyboard: true,
onKeyboardInteractive: (name, instructions, instructionsLang, prompts, finish) => { // 不起作用 onKeyboardInteractive: (name, instructions, instructionsLang, prompts, finish) => { // 不起作用
if (prompts.length > 0 && prompts[0].prompt.toLowerCase().includes('password')) { 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) { function subDirs(path) {
const dirs = [path] const dirs = [path]
if (fs.statSync(path).isFile()) { if (fs.statSync(path).isFile()) {
@@ -57,13 +82,13 @@ const necessaryPath = (path) => {
} }
ssh.connect(connection).then(async () => { 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 }) 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 }) await ssh.execCommand(`mkdir ${dist}`, { cwd:root })
const toCreate = necessaryPath('./dist') const toCreate = necessaryPath('./dist')
for (const name of toCreate) { 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 }) await ssh.execCommand(`mkdir ${dist}/${name.join('/')}`, { cwd:root })
} }
@@ -77,13 +102,13 @@ ssh.connect(connection).then(async () => {
return !baseName.endsWith('.map'); return !baseName.endsWith('.map');
}, },
tick: (localPath, remotePath, error) => { tick: (localPath, remotePath, error) => {
console.log(`"${localPath}" ===> "${remotePath}" ... ${error || 'succeeded!'}`) console.log(`Uploading "${localPath}" ===> "${remotePath}" ${error || 'succeeded!'}`)
err = error err = error
}, },
}) })
ssh.dispose() ssh.dispose()
if (err) { if (err) {
console.log('[ Upload failed! ]') console.log('[ Uploaded with error! ]')
process.exit(1) process.exit(1)
} else { } else {
console.log('[ Uploaded successfully! ]') console.log('[ Uploaded successfully! ]')

View File

@@ -1,11 +1,13 @@
{ {
"name": "console", "name": "tic.node.console",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"serve": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build": "vue-cli-service build", "dist": "vue-cli-service build",
"deploy": "node ./deploy.js -r '/home/tic/console.web.site' -u tic", "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", "lint": "vue-cli-service lint",
"test:unit": "vue-cli-service test:unit" "test:unit": "vue-cli-service test:unit"
}, },
@@ -23,7 +25,7 @@
"morgan": "^1.9.0", "morgan": "^1.9.0",
"serve-favicon": "^2.4.5", "serve-favicon": "^2.4.5",
"socket.io": "^2.1.1", "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": "^2.5.17",
"vue-axios": "^2.1.3", "vue-axios": "^2.1.3",
"vue-i18n": "^8.1.0", "vue-i18n": "^8.1.0",
@@ -42,6 +44,7 @@
"babel-core": "7.0.0-bridge.0", "babel-core": "7.0.0-bridge.0",
"babel-jest": "^23.0.1", "babel-jest": "^23.0.1",
"babel-plugin-transform-imports": "^1.5.1", "babel-plugin-transform-imports": "^1.5.1",
"cross-env": "^5.1.3",
"node-sass": "^4.9.0", "node-sass": "^4.9.0",
"node-ssh": "^5.1.2", "node-ssh": "^5.1.2",
"sass-loader": "^7.0.1", "sass-loader": "^7.0.1",

View File

@@ -9,15 +9,15 @@ function config(){
// 读取配置文件 // 读取配置文件
try { try {
if (fs.existsSync('./ConfigSys.js')) { if (fs.existsSync('./ConfigBasic.js')) {
Config=require('./ConfigSys.js') Config=require('./ConfigBasic.js')
console.info('ConfigSys loaded') console.info('ConfigBasic loaded')
} }
if (fs.existsSync('./ConfigUser.js')) { // 如果存在,覆盖掉 ConfigSys 里的默认参数 if (fs.existsSync('./ConfigCustom.js')) { // 如果存在,覆盖掉 ConfigBasic 里的默认参数
Config=deepmerge(Config, require('./ConfigUser.js')) // 注意objectMerge后产生了一个新的对象而不是在原来的Config里添加 Config=deepmerge(Config, require('./ConfigCustom.js')) // 注意objectMerge后产生了一个新的对象而不是在原来的Config里添加
console.info('ConfigUser loaded') console.info('ConfigCustom loaded')
} }
if (fs.existsSync('./ConfigSecret.js')) { // 如果存在,覆盖掉 ConfigSys 和 ConfigUser 里的参数 if (fs.existsSync('./ConfigSecret.js')) { // 如果存在,覆盖掉 ConfigBasic 和 ConfigCustom 里的参数
Config=deepmerge(Config, require('./ConfigSecret.js')) Config=deepmerge(Config, require('./ConfigSecret.js'))
console.info('ConfigSecret loaded') console.info('ConfigSecret loaded')
} }
@@ -28,11 +28,11 @@ try {
// 载入命令行参数 // 载入命令行参数
commander commander
.version(Config.VERSION, '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version' .version(Config.VERSION, '-v, --version') // 默认是 -V。如果要 -v就要加 '-v --version'
.option('-H, --host <host>', 'host ip or domain name') .option('-H, --host <host>', 'Host ip or domain name. Default to ' + Config.host)
.option('-P, --protocol <protocol>', 'Web server protocol http|https|httpall, default to httpall') .option('-P, --protocol <protocol>', 'Web server protocol http|https|httpall|http2https. Default to ' + Config.protocol)
.option('-p, --port <port>', 'Server port, default to 80|443') .option('-p, --port <port>', `Server port. Default to ${Config.port?Config.port:'80|443'}`)
.option('--sslCert <cert>', 'SSL cert file') .option('--sslCert <cert>', 'SSL cert file. Default to ' + Config.sslCert)
.option('--sslKey <key>', 'SSL privkey file') .option('--sslKey <key>', 'SSL privkey file. Default to ' + Config.sslKey)
.option('--sslCA <ca>', 'SSL ca bundle file') .option('--sslCA <ca>', 'SSL ca bundle file')
.parse(process.argv) .parse(process.argv)
@@ -101,14 +101,21 @@ async function init(){ /*** 设置全局对象 ***/
}else if ('httpall'===wo.Config.protocol) { }else if ('httpall'===wo.Config.protocol) {
let portHttp=wo.Config.port?wo.Config.port:80 // 如果port参数已设置使用它否则默认为80 let portHttp=wo.Config.port?wo.Config.port:80 // 如果port参数已设置使用它否则默认为80
require('http').createServer(server).listen(portHttp, function(err) { 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 let portHttps=(wo.Config.port && wo.Config.port!==80)?wo.Config.port+443:443 // 如果port参数已设置使用它+443否则默认为443
require('https').createServer({ 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 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){ }, 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)
}) })
} }

View File

@@ -1,6 +1,6 @@
export default { export default {
login: { login: {
title: 'TIC Node Console', title: 'TIC Console',
inputLabel: '请输入密语例如Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?', inputLabel: '请输入密语例如Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?',
loginBtn: 'Sign In', loginBtn: 'Sign In',
secwordBtn: 'Create Secword', secwordBtn: 'Create Secword',
@@ -27,12 +27,12 @@
tableNone: '暂无数据', tableNone: '暂无数据',
}, },
network: { network: {
tableHeader: '当前邻居', tableHeader: 'Current neighbouring peer list',
searchHolder: '请输入该输入的', searchHolder: 'Please input peer owner address or network url',
thIndex: '序号', thIndex: 'Nr.',
thAddress: '连接地址', thAccessPoint: 'Url',
thStatus: '连接状态', thStatus: 'Status',
thPeerOwnerAddress: '邻居主人的账户地址', thOwnerAddress: 'Owner address',
tableNone: '暂无数据', tableNone: '暂无数据',
}, },
chain: { chain: {

View File

@@ -1,6 +1,6 @@
export default { export default {
login: { login: {
title: 'TIC Node 节点控制台', title: 'TIC 控制台',
inputLabel: '请输入密语例如Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?', inputLabel: '请输入密语例如Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eos, molestias, quisquam?',
loginBtn: '登录', loginBtn: '登录',
secwordBtn: '新密语', secwordBtn: '新密语',
@@ -27,12 +27,12 @@
tableNone: '暂无数据', tableNone: '暂无数据',
}, },
network: { network: {
tableHeader: '当前邻居', tableHeader: '当前邻居节点列表',
searchHolder: '请输入该输入的', searchHolder: '请输入主人地址或节点网址',
thIndex: '序号', thIndex: '序号',
thAddress: '连接址', thAccessPoint: '连接址',
thOwnerAddress: '邻居主人地址',
thStatus: '连接状态', thStatus: '连接状态',
thPeerOwnerAddress: '邻居主人的账户地址',
tableNone: '暂无数据', tableNone: '暂无数据',
}, },
chain: { chain: {

View File

@@ -1,5 +1,5 @@
import axios from 'axios' import axios from 'axios'
// import app from './app' // import app from './app'
// 控制台应当默认访问控制台所在主机(localhost 或者 window.location.hostname)的 TIC后台或者让用户在控制台启动时自己设置一个节点主机。 // 控制台应当默认访问控制台所在主机(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/`

View File

@@ -1,4 +1,4 @@
import axios from 'axios' import axios from 'axios'
export default { export default {
namespaced: true, namespaced: true,
@@ -13,10 +13,10 @@ export default {
], ],
itemList: [ itemList: [
{ {
height: '1', height: 'loading',
timestamp: '2011-11-20', timestamp: 'loading',
type: '0', type: 'loading',
hash: 'null', hash: 'loading',
}, },
], ],
fetching: false, fetching: false,

View File

@@ -1,4 +1,4 @@
import axios from 'axios' import axios from 'axios'
export default { export default {
namespaced: true, namespaced: true,
@@ -7,17 +7,17 @@ export default {
pageEntity: [5, 10, 25], pageEntity: [5, 10, 25],
headers: [ headers: [
{ text: 'network.thIndex', value: 'index', align: 'center' }, { 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.thStatus', value: 'status', align: 'center' },
{ text: 'network.thPeerOwnerAddress', value: 'peerOwnerAddress', align: 'center' },
], ],
itemList: [ itemList: [
// { {
// index: '', index: 'loading',
// address: '', accessPoint: 'loading',
// status: '', ownerAddress: 'loading',
// peerOwnerAddress: '', status: 'loading',
// }, },
], ],
fetching: false, fetching: false,
filter: '', filter: '',
@@ -42,7 +42,8 @@ export default {
actions: { actions: {
async updateNetInfo({ state, commit }) { 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('setPeerNumber', (result && result.data) ? result.data.length : 0)
commit('setItemList', result.data || []) commit('setItemList', result.data || [])

View File

@@ -12,10 +12,10 @@ export default {
], ],
itemList: [ itemList: [
{ {
height: '1', height: 'loading',
timestamp: '2011-11-20', timestamp: 'loading',
type: '0', type: 'loading',
hash: 'null', hash: 'loading',
}, },
], ],
fetching: false, fetching: false,

View File

@@ -89,10 +89,11 @@ import Vue from 'vue'
import store from '../store' import store from '../store'
import VueSocketio from 'vue-socket.io' import VueSocketio from 'vue-socket.io'
import io from 'socket.io-client' import io from 'socket.io-client'
import axios from 'axios'
const { mapState, mapGetters, mapActions, mapMutations } = createNamespacedHelpers('chain') 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 { export default {
name: 'Chain', name: 'Chain',

View File

@@ -101,7 +101,7 @@ export default {
display: flex; display: flex;
$sideWidth: 260px; $sideWidth: 240px;
$headerHeight: 64px; $headerHeight: 64px;
.side-menus { .side-menus {
&.collapsed { &.collapsed {
@@ -109,6 +109,7 @@ export default {
} }
width: $sideWidth; width: $sideWidth;
min-width: 120px;
background: url('../assets/images/bg.jpg') center; background: url('../assets/images/bg.jpg') center;
background-size: cover; background-size: cover;
overflow-y: auto; overflow-y: auto;

View File

@@ -1,4 +1,4 @@
<template> <template>
<div class="network-page"> <div class="network-page">
<div class="card-content"> <div class="card-content">
<div class="app-card elevation-1"> <div class="app-card elevation-1">
@@ -36,9 +36,9 @@
</template> </template>
<template slot="items" slot-scope="props"> <template slot="items" slot-scope="props">
<td>{{ props.item.index }}</td> <td>{{ props.item.index }}</td>
<td>{{ props.item.address }}</td> <td>{{ props.item.accessPoint }}</td>
<td>{{ props.item.ownerAddress }}</td>
<td>{{ props.item.status }}</td> <td>{{ props.item.status }}</td>
<td>{{ props.item.peerOwnerAddress }}</td>
</template> </template>
<template slot="no-results"> <template slot="no-results">
{{ $t('network.tableNone') }} {{ $t('network.tableNone') }}