diff --git a/user-dashboard/src/models/chain.js b/user-dashboard/src/models/chain.js index c6bcfa8ab..fec6a7fc6 100644 --- a/user-dashboard/src/models/chain.js +++ b/user-dashboard/src/models/chain.js @@ -1,9 +1,13 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ import jsonfile from 'jsonfile' import rimraf from 'rimraf' import sleep from 'sleep-promise'; import config from '../config' import util from 'util' -const hfc = require('fabric-client'); const shell = require('shelljs'); const mongoose = require('mongoose'); const fs = require('fs-extra'); @@ -57,9 +61,10 @@ chainSchema.post('save', function(doc, next) { template.keyValueStore = `${chainRootDir}/client-kvs` template.CC_SRC_PATH = chainRootDir const txDir = `${chainRootDir}/tx` + const libDir = `${chainRootDir}/lib` + fs.ensureDirSync(libDir) shell.cp('-R', '/usr/app/src/src/config-template/cc_code/src', template.CC_SRC_PATH); - shell.cp('/usr/app/src/src/modules/helper.js', chainRootDir) - shell.cp('-R', '/usr/app/src/src/modules/fabric', chainRootDir) + shell.cp('-R', `/usr/app/src/src/modules/${type}/*`, libDir) fs.ensureDirSync(template.keyValueStore) fs.ensureDirSync(txDir) @@ -122,30 +127,19 @@ function initialFabric (doc) { if (shell.exec(`configtxgen -profile TwoOrgsChannel -channelID ${channelName} -outputCreateChannelTx ${channelConfigPath}/${channelName}.tx`).code !== 0) { return } - hfc.addConfigFile(`${chainRootDir}/network-config.json`); - const helper = require(`${chainRootDir}/helper.js`) + const helper = require(`${chainRootDir}/lib/helper.js`) helper.initialize(doc._template) - const channels = require(`${chainRootDir}/fabric/create-channel.js`); + const channels = require(`${chainRootDir}/lib/create-channel.js`); channels.initialize(doc._template) - const chaincodeVersion = "v0" - const chaincodePath = "github.com/example_cc" - const chaincodeName = `${clusterId}-${id}` function asyncInstallChainCode(arr) { return arr.reduce((promise, orgName) => { return promise.then((result) => { return new Promise((resolve, reject) => { - const join = require(`${chainRootDir}/fabric/join-channel.js`); + const join = require(`${chainRootDir}/lib/join-channel.js`); join.initialize(doc._template) join.joinChannel(channelName, peerNames, username, orgName) .then(function(message) { resolve() - // helper.setupChaincodeDeploy() - // const install = require(`${chainRootDir}/fabric/install-chaincode.js`); - // install.initialize(keyValueStore) - // install.installChaincode(peerNames, chaincodeName, chaincodePath, chaincodeVersion, username, orgName) - // .then(function(message) { - // resolve() - // }); }); }) }) @@ -195,4 +189,4 @@ chainSchema.post('remove', function(doc) { const model = mongoose.model('chain', chainSchema); -module.exports = model; \ No newline at end of file +module.exports = model; diff --git a/user-dashboard/src/models/chainCode.js b/user-dashboard/src/models/chainCode.js index 0ebf640d3..a4be14c12 100644 --- a/user-dashboard/src/models/chainCode.js +++ b/user-dashboard/src/models/chainCode.js @@ -1,3 +1,8 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ const mongoose = require('mongoose'); import rimraf from 'rimraf' const log4js = require('log4js'); diff --git a/user-dashboard/src/models/user.js b/user-dashboard/src/models/user.js index a41735acb..2fa9c59c4 100644 --- a/user-dashboard/src/models/user.js +++ b/user-dashboard/src/models/user.js @@ -1,3 +1,8 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ const mongoose = require('mongoose'); const log4js = require('log4js'); const logger = log4js.getLogger(__filename.slice(__dirname.length + 1)); diff --git a/user-dashboard/src/modules/chain.js b/user-dashboard/src/modules/chain.js index 4cace7fa8..8435c2492 100644 --- a/user-dashboard/src/modules/chain.js +++ b/user-dashboard/src/modules/chain.js @@ -8,17 +8,32 @@ SPDX-License-Identifier: Apache-2.0 * Created by lixuc on 2017/5/3. */ var rp = require("request-promise"); -var config = require("./configuration"); +var configuration = require("./configuration"); var dt = require("../kit/date-tool"); var Pagination = require("../kit/pagination"); +import Moment from 'moment' +import { extendMoment } from 'moment-range'; +import util from 'util' +import config from '../config' +import sleep from 'sleep-promise'; +const crypto = require("crypto"); +const mongoose = require('mongoose'); +const moment = extendMoment(Moment); +const log4js = require('log4js'); +const logger = log4js.getLogger(__filename.slice(__dirname.length + 1)); +const logLevel = process.env.DEV === "True" ? "DEBUG" : "INFO" +import ChainModel from '../models/chain' +import ChainCode from '../models/chainCode' +logger.setLevel(logLevel); -function chain(apikey) { +function chain(apikey, username) { this.apikey = apikey; + this.username = username; } chain.prototype = { - RESTfulURL: "http://" + config.RESTful_Server + config.RESTful_BaseURL, - PoolManagerURL: "http://" + config.PoolManager_Server + config.PoolManager_BaseURL, - LogURL: "http://" + config.Log_Server + config.Log_BaseURL, + RESTfulURL: "http://" + configuration.RESTful_Server + configuration.RESTful_BaseURL, + PoolManagerURL: "http://" + configuration.PoolManager_Server + configuration.PoolManager_BaseURL, + LogURL: "http://" + configuration.Log_Server + configuration.Log_BaseURL, amount: function() { return new Promise(function(resolve, reject) { rp({ @@ -39,94 +54,151 @@ chain.prototype = { }).catch(function(err) { reject({ success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); }, list: function(page) { + const username = this.username + const userId = this.apikey return new Promise(function(resolve, reject) { rp({ - uri: this.RESTfulURL + "cluster/list?user_id=" + this.apikey, + uri: this.RESTfulURL + "clusters?user_id=" + this.apikey, json: true }).then(function(response) { - if (response.success) { - var chains = []; - var clusters = response.result.clusters; - var pageNo = page || 1; - var pg = new Pagination(clusters); - clusters = pg.gotoPage(pageNo); - for (var i in clusters) { - var plugin = clusters[i]["consensus_plugin"]; - var size = clusters[i]["size"]; - var chaincodeNumber = clusters[i]["chaincodes"].length; - var cost = 0; - if (plugin == "noops") { - if (size == 4) { - cost = 40 + chaincodeNumber * 20; - } else if (size == 6) { - cost = 60 + chaincodeNumber * 30; - } - } else if (plugin == "pbft") { - if (size == 4) { - cost = 80 + chaincodeNumber * 40; - } else if (size == 6) { - cost = 120 + chaincodeNumber * 60; - } + if (response.status === "OK") { + let chains = []; + let clusters = response.data; + const pageNo = page || 1; + const pg = new Pagination(clusters); + clusters = pg.gotoPage(pageNo); + let promises = [] + clusters.forEach((cluster, i) => { + const plugin = cluster.consensus_plugin + const size = cluster.size + let p = new Promise((resolve, reject) => { + ChainModel.findOne({clusterId: cluster.id, user_id: userId}, function (err, chainDoc) { + const peers = cluster.containers.filter(container => container.includes(".peer")) + const applyTime = moment(cluster.apply_ts) + const nowTime = moment().add(8, "hours") + let runningHours = moment.range(applyTime, nowTime).diff("hours") + ChainCode.count({chain: chainDoc, status: "instantiated"}, function (err, result) { + chains.push({ + id: cluster.id, + dbId: chainDoc.id, + blocks: 0, + scNum: result, + initialized: chainDoc.initialized, + keyValueStore: chainDoc.keyValueStore, + status: chainDoc.initialized ? cluster.status : "initializing", + plugin, + size, + name: chainDoc.name, + template: chainDoc.template, + type: chainDoc.type, + peerNum: peers.length, + createTime: applyTime.format("YYYY/MM/DD HH:mm:ss"), + runningHours + }); + resolve() + }) + }) + }) + promises.push(p) + }); + function asyncQuery(arr) { + return arr.reduce((promise, chain) => { + return promise.then((result) => { + return new Promise((resolve, reject) => { + const chainRootDir = util.format(config.path.chain, username, chain.dbId) + if (chain.initialized) { + const helper = require(`${chainRootDir}/lib/helper`) + helper.initialize(chain.template) + helper.setupChaincodeDeploy() + const query = require(`${chainRootDir}/lib/query`) + query.initialize(chain.template) + query.getChannelHeight("peer1", username, "org1") + .then(function(message) { + const chainIndex = chains.findIndex(x => x.id === chain.id); + let chainItem = chains[chainIndex] + chainItem.blocks = parseInt(message) + chains[chainIndex] = chainItem + }).then(sleep(500)).then(() => { + resolve() + }) + } else { + resolve() } - chains.push({ - id: clusters[i]["cluster_id"], - name: clusters[i]["name"], - description: clusters[i]["description"], - plugin: plugin, - mode: clusters[i]["consensus_mode"], - size: size, - run_time: dt.diff2Now(clusters[i]["apply_time"]), - status: clusters[i]["status"], - chaincodes: chaincodeNumber, - cost: cost - }); - } + }) + }) + }, Promise.resolve()) + } + Promise.all(promises).then(() => { + asyncQuery(chains).then(() => { resolve({ - success: true, - chains: chains, - totalNumber: pg.getTotalRow(), - totalPage: pg.getTotalPage() + success: true, + chains: chains, + totalNumber: pg.getTotalRow(), + totalPage: pg.getTotalPage() }); - } else { - var e = new Error(response.message); - e.status = 503; - throw e; - } + }) + }) + } else { + var e = new Error(response.message); + e.status = 503; + throw e; + } }).catch(function(err) { reject({ success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); }, - apply: function(name, description, plugin, mode, size) { + apply: function(name, plugin, mode, size, applyType) { return new Promise(function(resolve, reject) { + const apikey = this.apikey + const username = this.username rp({ method: "POST", - uri: this.RESTfulURL + "cluster/apply", + uri: this.RESTfulURL + "cluster_op", body: { user_id: this.apikey, - name: name, - description: description, - consensus_plugin: plugin, - consensus_mode: mode, + action: "apply", + type: applyType, size: size }, json: true }).then(function(response) { - if (response.success) { - resolve({ - success: true, - id: response.result.cluster_id, - applyTime: response.result.apply_time - }); + if (response.status === "OK") { + const {data: {service_url, size}} = response + const chainId = mongoose.Types.ObjectId(); + const chainRootDir = util.format(config.path.chain, username, chainId) + const newChain = new ChainModel({ + _id: chainId, + keyValueStore: `${chainRootDir}/client-kvs`, + ccSrcPath: chainRootDir, + serviceUrl: service_url, + user_id: apikey, + username, + name, + size, + plugin, + mode, + type: applyType, + clusterId: response.data.id + }) + newChain.save(function(err, data){ + if(err){ return console.log(err) } + }) + resolve({ + success: true, + id: response.data.id, + dbId: newChain.id, + applyTime: response.data.apply_ts + }); } else { var e = new Error(response.message); e.status = 503; @@ -135,61 +207,87 @@ chain.prototype = { }).catch(function(err) { reject({ success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); }, - edit: function(id, name, description) { + edit: function(id, name) { return new Promise(function(resolve, reject) { - rp({ - method: "POST", - uri: this.RESTfulURL + "cluster/edit", - body: { - user_id: this.apikey, - cluster_id: id, - name: name, - description: description - }, - json: true - }).then(function(response) { - if (response.success) { - resolve({ success: true }); - } else { - var e = new Error(response.message); - e.status = 503; - throw e; - } - }).catch(function(err) { - reject({ - success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" - }); + try { + const query = {clusterId: id}; + ChainModel.findOneAndUpdate(query, {name}, {upsert: true}, function (err, doc) { + if (err) { + logger.error(err) + err.status = 503; + throw err; + } else { + resolve({success: true}) + } + }) + } catch (err) { + reject({ + success: false, + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); + } + // rp({ + // method: "POST", + // uri: this.RESTfulURL + "cluster/edit", + // body: { + // user_id: this.apikey, + // cluster_id: id, + // name: name, + // description: description + // }, + // json: true + // }).then(function(response) { + // if (response.success) { + // resolve({ success: true }); + // } else { + // var e = new Error(response.message); + // e.status = 503; + // throw e; + // } + // }).catch(function(err) { + // reject({ + // success: false, + // message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + // }); + // }); }.bind(this)); }, release: function(id) { return new Promise(function(resolve, reject) { rp({ method: "POST", - uri: this.RESTfulURL + "cluster/release", + uri: this.RESTfulURL + "cluster_op", body: { + action: "release", user_id: this.apikey, cluster_id: id }, json: true }).then(function(response) { - if (response.success) { - resolve({ success: true }); + if (response.status === "OK") { + ChainModel.findOne({clusterId: id}, function (err, doc) { + if (err) { + err.status = 503; + throw err; + } else { + doc.remove(function(err){logger.error(err)}); + resolve({success: true}) + } + }) } else { - var e = new Error(response.message); + let e = new Error(response.message); e.status = 503; throw e; } }).catch(function(err) { reject({ success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); @@ -205,7 +303,7 @@ chain.prototype = { }, json: true }).then(function(response) { - if (response.status.toLowerCase() == "ok") { + if (response.status.toLowerCase() === "ok") { resolve({ success: true }); } else { var e = new Error(response.error); @@ -215,7 +313,7 @@ chain.prototype = { }).catch(function(err) { reject({ success: false, - message: (err.status == 503 && err.message) || "System maintenance, please try again later!" + message: (err.status === 503 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); @@ -234,7 +332,7 @@ chain.prototype = { type: "Feature", geometry: { type: "Point", - coordinates: config["topology"][nodes[i]["id"]] + coordinates: configuration["topology"][nodes[i]["id"]] }, properties: nodes[i] }); @@ -274,8 +372,8 @@ chain.prototype = { geometry: { type: "LineString", coordinates: [ - config["topology"][links[i]["from"]], - config["topology"][links[i]["to"]] + configuration["topology"][links[i]["from"]], + configuration["topology"][links[i]["to"]] ] }, properties: links[i] diff --git a/user-dashboard/src/modules/fabric/create-channel.js b/user-dashboard/src/modules/fabric/create-channel.js new file mode 100644 index 000000000..6ffa15855 --- /dev/null +++ b/user-dashboard/src/modules/fabric/create-channel.js @@ -0,0 +1,78 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var util = require('util'); +var fs = require('fs'); +var path = require('path'); +var helper = require('./helper.js'); +var logger = helper.getLogger('Create-Channel'); + +function initialize (template) { + helper.initialize(template); +} +//Attempt to send a request to the orderer with the sendCreateChain method +var createChannel = function(channelName, channelConfigPath, username, orgName) { + logger.debug('\n====== Creating Channel \'' + channelName + '\' ======\n'); + var client = helper.getClientForOrg(orgName); + var channel = helper.getChannelForOrg(orgName); + + // read in the envelope for the channel config raw bytes + var envelope = fs.readFileSync(channelConfigPath); + // extract the channel config bytes from the envelope to be signed + var channelConfig = client.extractChannelConfig(envelope); + + //Acting as a client in the given organization provided with "orgName" param + return helper.getOrgAdmin(orgName).then((admin) => { + logger.debug(util.format('Successfully acquired admin user for the organization "%s"', orgName)); + // sign the channel config bytes as "endorsement", this is required by + // the orderer's channel creation policy + let signature = client.signChannelConfig(channelConfig); + + let request = { + config: channelConfig, + signatures: [signature], + name: channelName, + orderer: channel.getOrderers()[0], + txId: client.newTransactionID() + }; + + // send to orderer + return client.createChannel(request); + }, (err) => { + logger.error('Failed to enroll user \''+username+'\'. Error: ' + err); + throw new Error('Failed to enroll user \''+username+'\'' + err); + }).then((response) => { + logger.debug(' response ::%j', response); + if (response && response.status === 'SUCCESS') { + logger.debug('Successfully created the channel.'); + let response = { + success: true, + message: 'Channel \'' + channelName + '\' created Successfully' + }; + return response; + } else { + logger.error('\n!!!!!!!!! Failed to create the channel \'' + channelName + + '\' !!!!!!!!!\n\n'); + throw new Error('Failed to create the channel \'' + channelName + '\''); + } + }, (err) => { + logger.error('Failed to initialize the channel: ' + err.stack ? err.stack : + err); + throw new Error('Failed to initialize the channel: ' + err.stack ? err.stack : err); + }); +}; + +exports.initialize = initialize; +exports.createChannel = createChannel; diff --git a/user-dashboard/src/modules/fabric/helper.js b/user-dashboard/src/modules/fabric/helper.js new file mode 100644 index 000000000..ba4b32982 --- /dev/null +++ b/user-dashboard/src/modules/fabric/helper.js @@ -0,0 +1,352 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; +const log4js = require('log4js'); +const logger = log4js.getLogger('Helper'); +logger.setLevel('DEBUG'); + +const path = require('path'); +const util = require('util'); +const fs = require('fs-extra'); +const User = require('fabric-client/lib/User.js'); +const crypto = require('crypto'); +const copService = require('fabric-ca-client'); +const ChainModel = require('/usr/app/src/src/models/chain'); + +const hfc = require('fabric-client'); +hfc.setLogger(logger); +let ORGS = {}; + +let clients = {}; +let channels = {}; +let caClients = {}; +let globalTemplate = {}; +let chain = null; +let keyValueStore = "" + +// set up the client and channel objects for each org +function initialize (template) { + globalTemplate = template; + ORGS = template.network; + for (let key in ORGS) { + if (key.indexOf('org') === 0) { + let client = new hfc(); + + let cryptoSuite = hfc.newCryptoSuite(); + cryptoSuite.setCryptoKeyStore(hfc.newCryptoKeyStore({path: getKeyStoreForOrg(ORGS[key].name)})); + client.setCryptoSuite(cryptoSuite); + + let channel = client.newChannel(template.channelName); + channel.addOrderer(newOrderer(client)); + + clients[key] = client; + channels[key] = channel; + + setupPeers(channel, key, client); + + let caUrl = ORGS[key].ca; + caClients[key] = new copService(caUrl, null /*defautl TLS opts*/, '' /* default CA */, cryptoSuite); + } + } +} + +function setupCryptoSuite (channelName) { + for (let key in ORGS) { + if (key.indexOf('org') === 0) { + let client = new hfc(); + + let cryptoSuite = hfc.newCryptoSuite(); + cryptoSuite.setCryptoKeyStore(hfc.newCryptoKeyStore({path: getKeyStoreForOrg(ORGS[key].name)})); + client.setCryptoSuite(cryptoSuite); + + let channel = client.newChannel(channelName); + channel.addOrderer(newOrderer(client)); + + clients[key] = client; + channels[key] = channel; + + setupPeers(channel, key, client); + + let caUrl = ORGS[key].ca; + caClients[key] = new copService(caUrl, null /*defautl TLS opts*/, '' /* default CA */, cryptoSuite); + } + } + logger.debug("====================== setup crypto suite done =====================") +} + +function setupPeers(channel, org, client) { + for (let key in ORGS[org].peers) { + let data = fs.readFileSync(ORGS[org].peers[key]['tls_cacerts']); + let peer = client.newPeer( + ORGS[org].peers[key].requests, + { + pem: Buffer.from(data).toString(), + 'ssl-target-name-override': ORGS[org].peers[key]['server-hostname'] + } + ); + peer.setName(key); + + channel.addPeer(peer); + } +} + +function newOrderer(client) { + var caRootsPath = ORGS.orderer.tls_cacerts; + let data = fs.readFileSync(caRootsPath); + let caroots = Buffer.from(data).toString(); + return client.newOrderer(ORGS.orderer.url, { + 'pem': caroots, + 'ssl-target-name-override': ORGS.orderer['server-hostname'] + }); +} + +function readAllFiles(dir) { + var files = fs.readdirSync(dir); + var certs = []; + files.forEach((file_name) => { + let data = fs.readFileSync(path.join(dir, file_name)); + certs.push(data); + }); + return certs; +} + +function getOrgName(org) { + return ORGS[org].name; +} + +function getKeyStoreForOrg(org) { + // return hfc.getConfigSetting('keyValueStore') + '/' + org; + return globalTemplate.keyValueStore + '/' + org; +} + +function newRemotes(names, forPeers, userOrg) { + let client = getClientForOrg(userOrg); + + let targets = []; + // find the peer that match the names + for (let idx in names) { + let peerName = names[idx]; + if (ORGS[userOrg].peers[peerName]) { + // found a peer matching the name + logger.debug(userOrg, peerName, ORGS[userOrg].peers[peerName]) + let data = fs.readFileSync(ORGS[userOrg].peers[peerName]['tls_cacerts']); + let grpcOpts = { + pem: Buffer.from(data).toString(), + 'ssl-target-name-override': ORGS[userOrg].peers[peerName]['server-hostname'] + }; + + if (forPeers) { + targets.push(client.newPeer(ORGS[userOrg].peers[peerName].requests, grpcOpts)); + } else { + let eh = client.newEventHub(); + eh.setPeerAddr(ORGS[userOrg].peers[peerName].events, grpcOpts); + targets.push(eh); + } + } + } + + if (targets.length === 0) { + logger.error(util.format('Failed to find peers matching the names %s', names)); + } + + return targets; +} + +//-------------------------------------// +// APIs +//-------------------------------------// +var getChannelForOrg = function(org) { + return channels[org]; +}; + +var getClientForOrg = function(org) { + return clients[org]; +}; + +var newPeers = function(names, org) { + return newRemotes(names, true, org); +}; + +var newEventHubs = function(names, org) { + return newRemotes(names, false, org); +}; + +var getMspID = function(org) { + logger.debug('Msp ID : ' + ORGS[org].mspid); + return ORGS[org].mspid; +}; + +var getAdminUser = function(userOrg) { + var users = globalTemplate.admins; + var username = users[0].username; + var password = users[0].secret; + var member; + var client = getClientForOrg(userOrg); + + return hfc.newDefaultKeyValueStore({ + path: getKeyStoreForOrg(getOrgName(userOrg)) + }).then((store) => { + client.setStateStore(store); + // clearing the user context before switching + client._userContext = null; + return client.getUserContext(username, true).then((user) => { + if (user && user.isEnrolled()) { + logger.info('Successfully loaded member from persistence'); + return user; + } else { + let caClient = caClients[userOrg]; + // need to enroll it with CA server + return caClient.enroll({ + enrollmentID: username, + enrollmentSecret: password + }).then((enrollment) => { + logger.info('Successfully enrolled user \'' + username + '\''); + member = new User(username); + member.setCryptoSuite(client.getCryptoSuite()); + return member.setEnrollment(enrollment.key, enrollment.certificate, getMspID(userOrg)); + }).then(() => { + return client.setUserContext(member); + }).then(() => { + return member; + }).catch((err) => { + logger.error('Failed to enroll and persist user. Error: ' + err.stack ? + err.stack : err); + return null; + }); + } + }); + }); +}; + +var getRegisteredUsers = function(username, userOrg, isJson) { + var member; + var client = getClientForOrg(userOrg); + var enrollmentSecret = null; + return hfc.newDefaultKeyValueStore({ + path: getKeyStoreForOrg(getOrgName(userOrg)) + }).then((store) => { + client.setStateStore(store); + // clearing the user context before switching + client._userContext = null; + return client.getUserContext(username, true).then((user) => { + if (user && user.isEnrolled()) { + logger.info('Successfully loaded member from persistence'); + return user; + } else { + let caClient = caClients[userOrg]; + return getAdminUser(userOrg).then(function(adminUserObj) { + member = adminUserObj; + return caClient.register({ + enrollmentID: username, + affiliation: userOrg + '.department1' + }, member); + }).then((secret) => { + enrollmentSecret = secret; + logger.debug(username + ' registered successfully'); + return caClient.enroll({ + enrollmentID: username, + enrollmentSecret: secret + }); + }, (err) => { + logger.debug(username + ' failed to register'); + return '' + err; + //return 'Failed to register '+username+'. Error: ' + err.stack ? err.stack : err; + }).then((message) => { + if (message && typeof message === 'string' && message.includes( + 'Error:')) { + logger.error(username + ' enrollment failed'); + return message; + } + logger.debug(username + ' enrolled successfully'); + + member = new User(username); + member._enrollmentSecret = enrollmentSecret; + return member.setEnrollment(message.key, message.certificate, getMspID(userOrg)); + }).then(() => { + client.setUserContext(member); + return member; + }, (err) => { + logger.error(util.format('%s enroll failed: %s', username, err.stack ? err.stack : err)); + return '' + err; + });; + } + }); + }).then((user) => { + if (isJson && isJson === true) { + var response = { + success: true, + secret: user._enrollmentSecret, + message: username + ' enrolled Successfully', + }; + return response; + } + return user; + }, (err) => { + logger.error(util.format('Failed to get registered user: %s, error: %s', username, err.stack ? err.stack : err)); + return '' + err; + }); +}; + +var getOrgAdmin = function(userOrg) { + var admin = ORGS[userOrg].admin; + var keyPEM = Buffer.from(readAllFiles(admin.key)[0]).toString(); + var certPEM = readAllFiles(admin.cert)[0].toString(); + + var client = getClientForOrg(userOrg); + var cryptoSuite = hfc.newCryptoSuite(); + if (userOrg) { + cryptoSuite.setCryptoKeyStore(hfc.newCryptoKeyStore({path: getKeyStoreForOrg(getOrgName(userOrg))})); + client.setCryptoSuite(cryptoSuite); + } + + return hfc.newDefaultKeyValueStore({ + path: getKeyStoreForOrg(getOrgName(userOrg)) + }).then((store) => { + client.setStateStore(store); + + return client.createUser({ + username: 'peer'+userOrg+'Admin', + mspid: getMspID(userOrg), + cryptoContent: { + privateKeyPEM: keyPEM, + signedCertPEM: certPEM + } + }); + }); +}; + +var setupChaincodeDeploy = function() { + process.env.GOPATH = globalTemplate.CC_SRC_PATH; +}; + +var getLogger = function(moduleName) { + var logger = log4js.getLogger(moduleName); + logger.setLevel('DEBUG'); + return logger; +}; + +exports.getChannelForOrg = getChannelForOrg; +exports.getClientForOrg = getClientForOrg; +exports.getLogger = getLogger; +exports.setupChaincodeDeploy = setupChaincodeDeploy; +exports.getMspID = getMspID; +exports.ORGS = ORGS; +exports.newPeers = newPeers; +exports.newEventHubs = newEventHubs; +exports.getRegisteredUsers = getRegisteredUsers; +exports.getOrgAdmin = getOrgAdmin; +exports.setupCryptoSuite = setupCryptoSuite; +exports.initialize = initialize; \ No newline at end of file diff --git a/user-dashboard/src/modules/fabric/install-chaincode.js b/user-dashboard/src/modules/fabric/install-chaincode.js new file mode 100644 index 000000000..50be4084c --- /dev/null +++ b/user-dashboard/src/modules/fabric/install-chaincode.js @@ -0,0 +1,81 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; +var util = require('util'); +var helper = require('./helper.js'); +var logger = helper.getLogger('install-chaincode'); +var tx_id = null; + +function initialize (template) { + helper.initialize(template); +} +//function installChaincode(org) { +var installChaincode = function(peers, chaincodeName, chaincodePath, + chaincodeVersion, username, org) { + logger.debug( + '\n============ Install chaincode on organizations ============\n'); + helper.setupChaincodeDeploy(); + var channel = helper.getChannelForOrg(org); + var client = helper.getClientForOrg(org); + + return helper.getOrgAdmin(org).then((user) => { + var request = { + targets: helper.newPeers(peers, org), + chaincodePath: chaincodePath, + chaincodeId: chaincodeName, + chaincodeVersion: chaincodeVersion + }; + return client.installChaincode(request); + }, (err) => { + logger.error('Failed to enroll user \'' + username + '\'. ' + err); + throw new Error('Failed to enroll user \'' + username + '\'. ' + err); + }).then((results) => { + var proposalResponses = results[0]; + var proposal = results[1]; + var all_good = true; + for (var i in proposalResponses) { + let one_good = false; + if (proposalResponses && proposalResponses[i].response && + proposalResponses[i].response.status === 200) { + one_good = true; + logger.info('install proposal was good'); + } else { + logger.error('install proposal was bad'); + } + all_good = all_good & one_good; + } + if (all_good) { + logger.info(util.format( + 'Successfully sent install Proposal and received ProposalResponse: Status - %s', + proposalResponses[0].response.status)); + logger.debug('\nSuccessfully Installed chaincode on organization ' + org + + '\n'); + return 'Successfully Installed chaincode on organization ' + org; + } else { + logger.error( + 'Failed to send install Proposal or receive valid response. Response null or status is not 200. exiting...' + ); + return 'Failed to send install Proposal or receive valid response. Response null or status is not 200. exiting...'; + } + }, (err) => { + logger.error('Failed to send install proposal due to error: ' + err.stack ? + err.stack : err); + throw new Error('Failed to send install proposal due to error: ' + err.stack ? + err.stack : err); + }); +}; +exports.initialize = initialize; +exports.installChaincode = installChaincode; diff --git a/user-dashboard/src/modules/fabric/instantiate-chaincode.js b/user-dashboard/src/modules/fabric/instantiate-chaincode.js new file mode 100644 index 000000000..38acff9a6 --- /dev/null +++ b/user-dashboard/src/modules/fabric/instantiate-chaincode.js @@ -0,0 +1,168 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var hfc = require('fabric-client'); +var Peer = require('fabric-client/lib/Peer.js'); +var EventHub = require('fabric-client/lib/EventHub.js'); +var helper = require('./helper.js'); +var logger = helper.getLogger('instantiate-chaincode'); +// var ORGS = hfc.getConfigSetting('network'); +let ORGS = {}; +var tx_id = null; +var eh = null; + +function initialize (template) { + helper.initialize(template); + ORGS = template.network; +} + +var instantiateChaincode = function(channelName, chaincodeName, chaincodeVersion, functionName, args, username, org) { + logger.debug('\n============ Instantiate chaincode on organization ' + org + + ' ============\n'); + + var channel = helper.getChannelForOrg(org); + var client = helper.getClientForOrg(org); + + return helper.getOrgAdmin(org).then((user) => { + // read the config block from the orderer for the channel + // and initialize the verify MSPs based on the participating + // organizations + return channel.initialize(); + }, (err) => { + logger.error('Failed to enroll user \'' + username + '\'. ' + err); + throw new Error('Failed to enroll user \'' + username + '\'. ' + err); + }).then((success) => { + tx_id = client.newTransactionID(); + // send proposal to endorser + var request = { + chaincodeId: chaincodeName, + chaincodeVersion: chaincodeVersion, + args: args, + txId: tx_id + }; + + if (functionName) + request.fcn = functionName; + + return channel.sendInstantiateProposal(request); + }, (err) => { + logger.error('Failed to initialize the channel'); + throw new Error('Failed to initialize the channel'); + }).then((results) => { + var proposalResponses = results[0]; + var proposal = results[1]; + var all_good = true; + for (var i in proposalResponses) { + let one_good = false; + if (proposalResponses && proposalResponses[i].response && + proposalResponses[i].response.status === 200) { + one_good = true; + logger.info('instantiate proposal was good'); + } else { + logger.error('instantiate proposal was bad'); + } + all_good = all_good & one_good; + } + if (all_good) { + logger.info(util.format( + 'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', + proposalResponses[0].response.status, proposalResponses[0].response.message, + proposalResponses[0].response.payload, proposalResponses[0].endorsement + .signature)); + var request = { + proposalResponses: proposalResponses, + proposal: proposal + }; + // set the transaction listener and set a timeout of 30sec + // if the transaction did not get committed within the timeout period, + // fail the test + var deployId = tx_id.getTransactionID(); + + eh = client.newEventHub(); + let data = fs.readFileSync(ORGS[org].peers['peer1'][ + 'tls_cacerts' + ]); + eh.setPeerAddr(ORGS[org].peers['peer1']['events'], { + pem: Buffer.from(data).toString(), + 'ssl-target-name-override': ORGS[org].peers['peer1']['server-hostname'] + }); + eh.connect(); + + let txPromise = new Promise((resolve, reject) => { + let handle = setTimeout(() => { + eh.disconnect(); + reject(); + }, 30000); + + eh.registerTxEvent(deployId, (tx, code) => { + logger.info( + 'The chaincode instantiate transaction has been committed on peer ' + + eh._ep._endpoint.addr); + clearTimeout(handle); + eh.unregisterTxEvent(deployId); + eh.disconnect(); + + if (code !== 'VALID') { + logger.error('The chaincode instantiate transaction was invalid, code = ' + code); + reject(); + } else { + logger.info('The chaincode instantiate transaction was valid.'); + resolve(); + } + }); + }); + + var sendPromise = channel.sendTransaction(request); + return Promise.all([sendPromise].concat([txPromise])).then((results) => { + logger.debug('Event promise all complete and testing complete'); + return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call + }).catch((err) => { + logger.error( + util.format('Failed to send instantiate transaction and get notifications within the timeout period. %s', err) + ); + return 'Failed to send instantiate transaction and get notifications within the timeout period.'; + }); + } else { + logger.error( + 'Failed to send instantiate Proposal or receive valid response. Response null or status is not 200. exiting...' + ); + return 'Failed to send instantiate Proposal or receive valid response. Response null or status is not 200. exiting...'; + } + }, (err) => { + logger.error('Failed to send instantiate proposal due to error: ' + err.stack ? + err.stack : err); + return 'Failed to send instantiate proposal due to error: ' + err.stack ? + err.stack : err; + }).then((response) => { + if (response.status === 'SUCCESS') { + logger.info('Successfully sent transaction to the orderer.'); + return 'Chaincode Instantiation is SUCCESS'; + } else { + logger.error('Failed to order the transaction. Error code: ' + response.status); + return 'Failed to order the transaction. Error code: ' + response.status; + } + }, (err) => { + logger.error('Failed to send instantiate due to error: ' + err.stack ? err + .stack : err); + return 'Failed to send instantiate due to error: ' + err.stack ? err.stack : + err; + }); +}; +exports.initialize = initialize; +exports.instantiateChaincode = instantiateChaincode; diff --git a/user-dashboard/src/modules/fabric/invoke-transaction.js b/user-dashboard/src/modules/fabric/invoke-transaction.js new file mode 100644 index 000000000..ac7358a37 --- /dev/null +++ b/user-dashboard/src/modules/fabric/invoke-transaction.js @@ -0,0 +1,162 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'use strict'; +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var hfc = require('fabric-client'); +var Peer = require('fabric-client/lib/Peer.js'); +var helper = require('./helper.js'); +var logger = helper.getLogger('invoke-chaincode'); +var EventHub = require('fabric-client/lib/EventHub.js'); +var ORGS = hfc.getConfigSetting('network-config'); + +function initialize (template) { + helper.initialize(template); +} + +var invokeChaincode = function(peerNames, channelName, chaincodeName, fcn, args, username, org) { + logger.debug(util.format('\n============ invoke transaction on organization %s ============\n', org)); + var client = helper.getClientForOrg(org); + var channel = helper.getChannelForOrg(org); + var targets = (peerNames) ? helper.newPeers(peerNames, org) : undefined; + var tx_id = null; + + return helper.getRegisteredUsers(username, org).then((user) => { + tx_id = client.newTransactionID(); + logger.debug(util.format('Sending transaction "%j"', tx_id)); + // send proposal to endorser + var request = { + chaincodeId: chaincodeName, + fcn: fcn, + args: args, + chainId: channelName, + txId: tx_id + }; + + if (targets) + request.targets = targets; + + return channel.sendTransactionProposal(request); + }, (err) => { + logger.error('Failed to enroll user \'' + username + '\'. ' + err); + throw new Error('Failed to enroll user \'' + username + '\'. ' + err); + }).then((results) => { + var proposalResponses = results[0]; + var proposal = results[1]; + var all_good = true; + for (var i in proposalResponses) { + let one_good = false; + if (proposalResponses && proposalResponses[i].response && + proposalResponses[i].response.status === 200) { + one_good = true; + logger.info('transaction proposal was good'); + } else { + logger.error('transaction proposal was bad'); + } + all_good = all_good & one_good; + } + if (all_good) { + logger.debug(util.format( + 'Successfully sent Proposal and received ProposalResponse: Status - %s, message - "%s", metadata - "%s", endorsement signature: %s', + proposalResponses[0].response.status, proposalResponses[0].response.message, + proposalResponses[0].response.payload, proposalResponses[0].endorsement + .signature)); + var request = { + proposalResponses: proposalResponses, + proposal: proposal + }; + // set the transaction listener and set a timeout of 30sec + // if the transaction did not get committed within the timeout period, + // fail the test + var transactionID = tx_id.getTransactionID(); + var eventPromises = []; + + if (!peerNames) { + peerNames = channel.getPeers().map(function(peer) { + return peer.getName(); + }); + } + + var eventhubs = helper.newEventHubs(peerNames, org); + for (let key in eventhubs) { + let eh = eventhubs[key]; + eh.connect(); + + let txPromise = new Promise((resolve, reject) => { + let handle = setTimeout(() => { + eh.disconnect(); + reject(); + }, 30000); + + eh.registerTxEvent(transactionID, (tx, code) => { + clearTimeout(handle); + eh.unregisterTxEvent(transactionID); + eh.disconnect(); + + if (code !== 'VALID') { + logger.error( + 'The balance transfer transaction was invalid, code = ' + code); + reject(); + } else { + logger.info( + 'The balance transfer transaction has been committed on peer ' + + eh._ep._endpoint.addr); + resolve(); + } + }); + }); + eventPromises.push(txPromise); + }; + var sendPromise = channel.sendTransaction(request); + return Promise.all([sendPromise].concat(eventPromises)).then((results) => { + logger.debug(' event promise all complete and testing complete'); + return results[0]; // the first returned value is from the 'sendPromise' which is from the 'sendTransaction()' call + }).catch((err) => { + logger.error( + 'Failed to send transaction and get notifications within the timeout period.' + ); + return 'Failed to send transaction and get notifications within the timeout period.'; + }); + } else { + logger.error( + 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...' + ); + return 'Failed to send Proposal or receive valid response. Response null or status is not 200. exiting...'; + } + }, (err) => { + logger.error('Failed to send proposal due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send proposal due to error: ' + err.stack ? err.stack : + err; + }).then((response) => { + if (response.status === 'SUCCESS') { + logger.info('Successfully sent transaction to the orderer.'); + return tx_id.getTransactionID(); + } else { + logger.error('Failed to order the transaction. Error code: ' + response.status); + return 'Failed to order the transaction. Error code: ' + response.status; + } + }, (err) => { + logger.error('Failed to send transaction due to error: ' + err.stack ? err + .stack : err); + return 'Failed to send transaction due to error: ' + err.stack ? err.stack : + err; + }); +}; + +exports.initialize = initialize; +exports.invokeChaincode = invokeChaincode; diff --git a/user-dashboard/src/modules/fabric/join-channel.js b/user-dashboard/src/modules/fabric/join-channel.js new file mode 100644 index 000000000..83fcfc068 --- /dev/null +++ b/user-dashboard/src/modules/fabric/join-channel.js @@ -0,0 +1,137 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var util = require('util'); +var tx_id = null; +var helper = require('./helper.js'); +var logger = helper.getLogger('Join-Channel'); +//helper.hfc.addConfigFile(path.join(__dirname, 'network-config.json')); +var ORGS = helper.ORGS; +var allEventhubs = []; + +function initialize (template) { + helper.initialize(template); +} +// +//Attempt to send a request to the orderer with the sendCreateChain method +// +var joinChannel = function(channelName, peers, username, org) { + // on process exit, always disconnect the event hub + var closeConnections = function(isSuccess) { + if (isSuccess) { + logger.debug('\n============ Join Channel is SUCCESS ============\n'); + } else { + logger.debug('\n!!!!!!!! ERROR: Join Channel FAILED !!!!!!!!\n'); + } + logger.debug(''); + for (var key in allEventhubs) { + var eventhub = allEventhubs[key]; + if (eventhub && eventhub.isconnected()) { + //logger.debug('Disconnecting the event hub'); + eventhub.disconnect(); + } + } + }; + //logger.debug('\n============ Join Channel ============\n') + logger.debug(`channelName ${channelName} peers ${peers} username ${username} org ${org}`) + logger.info(util.format( + 'Calling peers in organization "%s" to join the channel', org)); + + var client = helper.getClientForOrg(org); + var channel = helper.getChannelForOrg(org); + var eventhubs = []; + + return helper.getOrgAdmin(org).then((admin) => { + logger.info(util.format('received member object for admin of the organization "%s": ', org)); + tx_id = client.newTransactionID(); + let request = { + txId : tx_id + }; + + return channel.getGenesisBlock(request); + }).then((genesis_block) => { + tx_id = client.newTransactionID(); + var request = { + targets: helper.newPeers(peers, org), + txId: tx_id, + block: genesis_block + }; + + eventhubs = helper.newEventHubs(peers, org); + for (let key in eventhubs) { + let eh = eventhubs[key]; + eh.connect(); + allEventhubs.push(eh); + } + + var eventPromises = []; + eventhubs.forEach((eh) => { + let txPromise = new Promise((resolve, reject) => { + let handle = setTimeout(reject, 30000); + eh.registerBlockEvent((block) => { + clearTimeout(handle); + // in real-world situations, a peer may have more than one channels so + // we must check that this block came from the channel we asked the peer to join + if (block.data.data.length === 1) { + // Config block must only contain one transaction + var channel_header = block.data.data[0].payload.header.channel_header; + if (channel_header.channel_id === channelName) { + resolve(); + } + else { + reject(); + } + } + }); + }); + eventPromises.push(txPromise); + }); + let sendPromise = channel.joinChannel(request); + return Promise.all([sendPromise].concat(eventPromises)); + }, (err) => { + logger.error('Failed to enroll user \'' + username + '\' due to error: ' + + err.stack ? err.stack : err); + throw new Error('Failed to enroll user \'' + username + + '\' due to error: ' + err.stack ? err.stack : err); + }).then((results) => { + logger.debug(util.format('Join Channel R E S P O N S E : %j', results)); + if (results[0] && results[0][0] && results[0][0].response && results[0][0] + .response.status == 200) { + logger.info(util.format( + 'Successfully joined peers in organization %s to the channel \'%s\'', + org, channelName)); + closeConnections(true); + let response = { + success: true, + message: util.format( + 'Successfully joined peers in organization %s to the channel \'%s\'', + org, channelName) + }; + return response; + } else { + logger.error(' Failed to join channel'); + closeConnections(); + throw new Error('Failed to join channel'); + } + }, (err) => { + logger.error('Failed to join channel due to error: ' + err.stack ? err.stack : + err); + closeConnections(); + throw new Error('Failed to join channel due to error: ' + err.stack ? err.stack : + err); + }); +}; +exports.initialize = initialize; +exports.joinChannel = joinChannel; diff --git a/user-dashboard/src/modules/fabric/query.js b/user-dashboard/src/modules/fabric/query.js new file mode 100644 index 000000000..8e19e1e18 --- /dev/null +++ b/user-dashboard/src/modules/fabric/query.js @@ -0,0 +1,293 @@ +/** + * Copyright 2017 IBM All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var path = require('path'); +var fs = require('fs'); +var util = require('util'); +var hfc = require('fabric-client'); +var Peer = require('fabric-client/lib/Peer.js'); +var EventHub = require('fabric-client/lib/EventHub.js'); +var helper = require('./helper.js'); +var logger = helper.getLogger('Query'); + +function initialize (template) { + helper.initialize(template); +} + +var queryChaincode = function(peer, channelName, chaincodeName, args, fcn, username, org) { + var channel = helper.getChannelForOrg(org); + var client = helper.getClientForOrg(org); + var target = buildTarget(peer, org); + return helper.getRegisteredUsers(username, org).then((user) => { + const tx_id = client.newTransactionID(); + // send query + var request = { + chaincodeId: chaincodeName, + txId: tx_id, + fcn: fcn, + args: args + }; + return channel.queryByChaincode(request, target); + }, (err) => { + logger.info('Failed to get submitter \''+username+'\''); + return 'Failed to get submitter \''+username+'\'. Error: ' + err.stack ? err.stack : + err; + }).then((response_payloads) => { + if (response_payloads) { + for (let i = 0; i < response_payloads.length; i++) { + logger.info(args[0]+' now has ' + response_payloads[i].toString('utf8') + + ' after the move'); + return args[0]+' now has ' + response_payloads[i].toString('utf8') + + ' after the move'; + } + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to end to end test with error:' + err.stack ? err.stack : + err); + return 'Failed to end to end test with error:' + err.stack ? err.stack : + err; + }); +}; +var getBlockByNumber = function(peer, blockNumber, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + + return helper.getRegisteredUsers(username, org).then((member) => { + return channel.queryBlock(parseInt(blockNumber), target); + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((response_payloads) => { + if (response_payloads) { + //logger.debug(response_payloads); + logger.debug(response_payloads); + return response_payloads; //response_payloads.data.data[0].buffer; + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; +var getTransactionByID = function(peer, trxnID, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + + return helper.getRegisteredUsers(username, org).then((member) => { + return channel.queryTransaction(trxnID, target); + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((response_payloads) => { + if (response_payloads) { + logger.debug(response_payloads); + return response_payloads; + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; +var getBlockByHash = function(peer, hash, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + + return helper.getRegisteredUsers(username, org).then((member) => { + return channel.queryBlockByHash(Buffer.from(hash), target); + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((response_payloads) => { + if (response_payloads) { + logger.debug(response_payloads); + return response_payloads; + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; +var getChainInfo = function(peer, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + + return helper.getRegisteredUsers(username, org).then((member) => { + return channel.queryInfo(target); + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((blockchainInfo) => { + if (blockchainInfo) { + // FIXME: Save this for testing 'getBlockByHash' ? + logger.debug('==========================================='); + logger.debug(blockchainInfo.currentBlockHash); + logger.debug('==========================================='); + //logger.debug(blockchainInfo); + return blockchainInfo; + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; +//getInstalledChaincodes +var getInstalledChaincodes = function(peer, type, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + var client = helper.getClientForOrg(org); + + return helper.getOrgAdmin(org).then((member) => { + if (type === 'installed') { + return client.queryInstalledChaincodes(target); + } else { + return channel.queryInstantiatedChaincodes(target); + } + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((response) => { + if (response) { + if (type === 'installed') { + logger.debug('<<< Installed Chaincodes >>>'); + } else { + logger.debug('<<< Instantiated Chaincodes >>>'); + } + var details = []; + for (let i = 0; i < response.chaincodes.length; i++) { + logger.debug('name: ' + response.chaincodes[i].name + ', version: ' + + response.chaincodes[i].version + ', path: ' + response.chaincodes[i].path + ); + details.push('name: ' + response.chaincodes[i].name + ', version: ' + + response.chaincodes[i].version + ', path: ' + response.chaincodes[i].path + ); + } + return details; + } else { + logger.error('response is null'); + return 'response is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; +var getChannels = function(peer, username, org) { + var target = buildTarget(peer, org); + var channel = helper.getChannelForOrg(org); + var client = helper.getClientForOrg(org); + logger.debug(`channel ${channel}`) + + return helper.getRegisteredUsers(username, org).then((member) => { + //channel.setPrimaryPeer(targets[0]); + return client.queryChannels(target); + }, (err) => { + logger.info('Failed to get submitter "' + username + '"'); + return 'Failed to get submitter "' + username + '". Error: ' + err.stack ? + err.stack : err; + }).then((response) => { + if (response) { + logger.debug('<<< channels >>>'); + var channelNames = []; + for (let i = 0; i < response.channels.length; i++) { + channelNames.push('channel id: ' + response.channels[i].channel_id); + } + logger.debug(channelNames); + return response; + } else { + logger.error('response_payloads is null'); + return 'response_payloads is null'; + } + }, (err) => { + logger.error('Failed to send query due to error: ' + err.stack ? err.stack : + err); + return 'Failed to send query due to error: ' + err.stack ? err.stack : err; + }).catch((err) => { + logger.error('Failed to query with error:' + err.stack ? err.stack : err); + return 'Failed to query with error:' + err.stack ? err.stack : err; + }); +}; + +function buildTarget(peer, org) { + var target = null; + if (typeof peer !== 'undefined') { + let targets = helper.newPeers([peer], org); + if (targets && targets.length > 0) target = targets[0]; + } + + return target; +} + +function getChannelHeight(peer, username, org){ + return getChainInfo(peer, username, org).then(response=>{ + if(response && response.height){ + logger.debug('<<<<<<<<<< channel height >>>>>>>>>') + logger.debug(response.height.low) + return response.height.low.toString() + } + }) +} + +exports.initialize = initialize; +exports.queryChaincode = queryChaincode; +exports.getBlockByNumber = getBlockByNumber; +exports.getTransactionByID = getTransactionByID; +exports.getBlockByHash = getBlockByHash; +exports.getChainInfo = getChainInfo; +exports.getInstalledChaincodes = getInstalledChaincodes; +exports.getChannels = getChannels; +exports.getChannelHeight = getChannelHeight; diff --git a/user-dashboard/src/modules/user.js b/user-dashboard/src/modules/user.js index 621da6aa0..8a1fd5af7 100644 --- a/user-dashboard/src/modules/user.js +++ b/user-dashboard/src/modules/user.js @@ -10,10 +10,84 @@ SPDX-License-Identifier: Apache-2.0 var rp = require("request-promise"); var uuid = require("node-uuid"); var config = require("./configuration"); +import UserModel from '../models/user' +const log4js = require('log4js'); +const logger = log4js.getLogger(__filename.slice(__dirname.length + 1)); +const logLevel = process.env.DEV === "True" ? "DEBUG" : "INFO" +logger.setLevel(logLevel); function user() {} user.prototype = { BaseURL: config.SV_BaseURL, + active: function (apikey) { + return new Promise(function (resolve, reject) { + rp({ + uri: this.BaseURL + "user/active/" + apikey, + json: true + }).then(function (response) { + resolve(response) + }).catch(function (err) { + reject({ + success: false, + message: err.message || "System maintenance, please try again later!" + }) + }) + }.bind(this)); + }, + search: function (username) { + return new Promise(function (resolve, reject) { + rp({ + uri: this.BaseURL + "user/search?username=" + username, + json: true + }).then(function (response) { + resolve(response) + }).catch(function (err) { + reject({ + success: false, + message: err.message || "System maintenance, please try again later!" + }) + }) + }.bind(this)); + }, + changePassword: function (apikey, origin_password, new_password) { + return new Promise(function (resolve, reject) { + rp({ + method: "POST", + uri: this.BaseURL + "user/password/change/" + apikey, + formData: { + origin_password, + new_password + }, + json: true + }).then(function (response) { + resolve(response) + }).catch(function (err) { + reject({ + success: false, + message: err.message || "System maintenance, please try again later!" + }) + }) + }.bind(this)); + }, + resetPassword: function (apikey, new_password) { + return new Promise(function (resolve, reject) { + rp({ + method: "POST", + uri: this.BaseURL + "user/password/reset/" + apikey, + formData: { + new_password + }, + json: true + }).then(function (response) { + resolve(response) + }).catch(function (err) { + reject({ + success: false, + message: err.message || "System maintenance, please try again later!" + }) + }) + }.bind(this)); + }, account: function(apikey) { return new Promise(function(resolve, reject) { rp({ @@ -21,13 +95,15 @@ user.prototype = { json: true }).then(function(response) { const {username, apikey, isActivated, balance} = response; - resolve({ + UserModel.findOneOrCreate({name: username, userId: apikey}, (err, user) => { + resolve({ success: true, username, apikey, isActivated, balance - }); + }); + }) }).catch(function(err) { reject({ success: false, @@ -60,7 +136,7 @@ user.prototype = { }).catch(function(err) { reject({ success: false, - message: (err.status == 403 && err.message) || "System maintenance, please try again later!" + message: (err.status === 403 && err.message) || "System maintenance, please try again later!" }); }); }.bind(this)); diff --git a/user-dashboard/src/routes/api/chain/index.js b/user-dashboard/src/routes/api/chain/index.js new file mode 100644 index 000000000..b8d889eeb --- /dev/null +++ b/user-dashboard/src/routes/api/chain/index.js @@ -0,0 +1,93 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ +import { Router } from 'express' +const Chain = require("../../../modules/chain"); +import ChainModel from '../../../models/chain' +import config from '../../../config' + +const router = new Router() + +router.get("/:apikey/list", function(req, res) { + const chain = new Chain(req.apikey, req.username); + chain.list(req.query.page).then(function(result) { + res.json({ + ...result, + limit: config.limit.chainNumber + }); + }).catch(function(err) { + res.json(err); + }); +}); + +router.get("/:apikey/db-list", function (req, res) { + ChainModel.find({user_id: req.apikey}, function (err, docs) { + if (err) res.json({success: false, err}) + const chains = docs.map((chain, i) => { + return { + id: chain.id, + name: chain.name + } + }) + res.json({ + success: true, + chains + }) + }) +}) + +router.get("/:id/stat", function (req, res) { + const id = req.params.id; + ChainModel.findOne({id}, function (err, chainDoc) { + if (err) res.json({success: false}) + res.json({ + success: true, + id, + initialized: chainDoc.initialized + }) + }) +}) +router.post("/:apikey/apply", function(req, res) { + const chain = new Chain(req.apikey, req.username); + chain.apply( + req.body.name, + '', + '', + req.body.config.size, + req.body.type + ).then(function (result) { + res.json(result) + }).catch(function (err) { + res.json(err) + }) + // chain.apply(req.body.name, + // req.body.description, + // req.body.plugin, + // req.body.mode, + // req.body.size) + // .then(function(result) { + // res.json(result); + // }).catch(function(err) { + // res.json(err); + // }); +}); +router.post("/:apikey/:id/release", function(req, res) { + const chain = new Chain(req.apikey, req.username); + chain.release(req.params.id).then(function(result) { + res.json(result); + }).catch(function(err) { + res.json(err); + }); +}); +router.post("/:apikey/:id/edit", function(req, res) { + const chain = new Chain(req.apikey, req.username); + chain.edit(req.params.id, req.body.name).then(function(result) { + res.json(result); + }).catch(function(err) { + res.json(err); + }); +}); + +export default router diff --git a/user-dashboard/src/routes/api/index.js b/user-dashboard/src/routes/api/index.js index a5639f8d6..caae58ac8 100644 --- a/user-dashboard/src/routes/api/index.js +++ b/user-dashboard/src/routes/api/index.js @@ -1,318 +1,22 @@ -/** - * Created by lixuc on 2017/5/2. - */ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ import login from './login' import register from './register' import profile from './profile' +import sendResetEmail from './send_reset_email' +import chain from './chain' const express = require("express"); -const multer = require("multer"); -const GridFsStorage = require("multer-gridfs-storage"); -const Profile = require("../../modules/profile"); -const Chain = require("../../modules/chain"); -const Chaincode = require("../../modules/chaincode"); -const mongoClient = require("../../modules/mongoclient"); -const Contract = require("../../modules/contract"); -const Analytics = require("../../modules/analytics"); const router = express.Router(); router.use("/login", login) router.use("/register", register) router.use("/profile", profile) +router.use("/send-reset-email", sendResetEmail) +router.use("/chain", chain) -router.get("/:apikey/chain/list", function(req, res) { - var chain = new Chain(req.params.apikey); - chain.list(req.query.page).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/chain/apply", function(req, res) { - var chain = new Chain(req.params.apikey); - chain.apply(req.body.name, - req.body.description, - req.body.plugin, - req.body.mode, - req.body.size) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/chain/:id/edit", function(req, res) { - var chain = new Chain(req.params.apikey); - chain.edit(req.params.id, req.body.name, req.body.description).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/chain/:id/release", function(req, res) { - var chain = new Chain(req.params.apikey); - chain.release(req.params.id).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/operate", function(req, res) { - var chain = new Chain(); - chain.operate(req.params.id, req.query.action).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/topology", function(req, res) { - var topology = {}; - var chain = new Chain(); - chain.topologyNodes(req.params.id).then(function(result) { - topology["nodes"] = result["geoData"]; - return chain.topologyLinks(req.params.id); - }).then(function(result) { - topology["links"] = result["geoData"]; - res.json(Object.assign(topology, { success: true })); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/topology/latency", function(req, res) { - var chain = new Chain(); - chain.topologyLatency(req.params.id).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/log/nodes", function(req, res) { - var chain = new Chain(); - chain.logNodes(req.params.id).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/log", function(req, res) { - var chain = new Chain(); - chain.log(req.params.id, - req.query.type, - req.query.node, - req.query.size, - req.query.time) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/blocks", function(req, res) { - var chain = new Chain(); - chain.blocks(req.params.id).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:chainId/block/:blockId", function(req, res) { - var chain = new Chain(); - chain.block(req.params.chainId, req.params.blockId).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/chaincode/list", function(req, res) { - var chaincode = new Chaincode(req.params.id); - chaincode.list(req.query.page).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/chain/:id/chaincode/invoke", function(req, res) { - var chaincode = new Chaincode(req.params.id); - chaincode.invoke(req.body.id, - req.body.func, - req.body.args) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/chain/:id/chaincode/query", function(req, res) { - var chaincode = new Chaincode(req.params.id); - chaincode.query(req.body.id, - req.body.func, - req.body.args) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/contract/upload", function(req, res) { - mongoClient.getGridFS().then(function(gfs) { - return new Promise(function(resolve, reject) { - var storage = GridFsStorage({ - gfs: gfs, - root: "smartcontract", - filename: function(req, file, cb) { - cb(null, file.originalname); - }, - metadata: function(req, file, cb) { - cb(null, { apikey: req.params.apikey }); - } - }); - var upload = multer({ storage: storage }).single("smartcontract"); - upload(req, res, function (err) { - if (err) { - reject(err); - } else { - res.json({ - result: true, - name: req.file.filename, - url: req.baseUrl + "/contract?id=" + req.file.id - }) - } - }); - }); - }).catch(function(err) { - res.json({ - result: false, - message: err.name + ": " + err.message - }); - }); -}); -router.get("/contract", function(req, res) { - mongoClient.getGridFS().then(function(gfs) { - return new Promise(function(resolve, reject) { - var readstream = gfs.createReadStream({ - _id: req.query.id, - root: "smartcontract" - }); - readstream.on("error", function (err) { - reject(err); - }); - readstream.pipe(res); - }); - }).catch(function(err) { - res.json({ - result: false, - message: err.name + ": " + err.message - }); - }); -}); -router.post("/:apikey/contract/create", function(req, res) { - var contract = new Contract(req.params.apikey); - contract.create(req.body.author, - req.body.name, - req.body.description, - req.body.version, - req.body.url) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/:apikey/contract/list/:group", function(req, res) { - var contract = new Contract(req.params.apikey); - contract.list(req.params.group, req.query.page).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/:apikey/contract/list", function(req, res) { - var contracts = {}; - var contract = new Contract(req.params.apikey); - contract.list("public", -1).then(function(result) { - contracts["public"] = result.contracts; - return contract.list("private", -1); - }).then(function(result) { - contracts["private"] = result.contracts; - res.json(Object.assign(contracts, { success: true })); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/contract/:id/deploy", function(req, res) { - var contract = new Contract(); - contract.deploy(req.body.chain, - req.params.id, - req.body.name, - req.body.func, - req.body.args) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/contract/:id/edit", function(req, res) { - var contract = new Contract(req.params.apikey); - contract.edit(req.params.id, - req.body.name, - req.body.description, - req.body.version, - req.body.author, - req.body.url) - .then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.post("/:apikey/contract/:id/delete", function(req, res) { - var contract = new Contract(req.params.apikey); - contract.delete(req.params.id).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/analytics", function(req, res) { - var analytics = new Analytics(req.params.id); - analytics.overview().then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/analytics/chaincode/list", function(req, res) { - var analytics = new Analytics(req.params.id); - analytics.chaincodeList().then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:chainId/analytics/chaincode/:chaincodeId/operations", function(req, res) { - var analytics = new Analytics(req.params.chainId); - analytics.chaincodeOperations(req.params.chaincodeId, req.query.timestamp).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/analytics/fabric", function(req, res) { - var analytics = new Analytics(req.params.id); - analytics.fabric(req.query.timestamp).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -router.get("/chain/:id/analytics/infrastructure", function(req, res) { - var analytics = new Analytics(req.params.id); - analytics.infrastructure(req.query.size).then(function(result) { - res.json(result); - }).catch(function(err) { - res.json(err); - }); -}); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/user-dashboard/src/routes/api/login/index.js b/user-dashboard/src/routes/api/login/index.js index cec8aed96..1061a1530 100644 --- a/user-dashboard/src/routes/api/login/index.js +++ b/user-dashboard/src/routes/api/login/index.js @@ -1,6 +1,16 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ import { Router } from 'express' import config from "../../../modules/configuration" import User from "../../../modules/user" +const log4js = require('log4js'); +const logger = log4js.getLogger(__filename.slice(__dirname.length + 1)); +const logLevel = process.env.DEV === "True" ? "DEBUG" : "INFO" +logger.setLevel(logLevel); +const jwt = require('jsonwebtoken'); const router = new Router() @@ -14,6 +24,12 @@ router.post("/", function(req, res) { apikey: result.apikey, isActivated: result.isActivated }; + res.cookie("CelloToken", jwt.sign({ + exp: Math.floor(Date.now() / 1000) + 36000, + username: result.username, + apikey: result.apikey, + isActivated: result.isActivated + }, req.app.get('secret'))); //将用户的blue points保存到session中,有效期24小时 req.session.balance = result.balance; //将用户信息保存到cookie,如果remember me,保存一年 diff --git a/user-dashboard/src/routes/api/profile/index.js b/user-dashboard/src/routes/api/profile/index.js index 0c0551fcd..a15d410e7 100644 --- a/user-dashboard/src/routes/api/profile/index.js +++ b/user-dashboard/src/routes/api/profile/index.js @@ -1,3 +1,8 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ import { Router } from 'express' import Profile from '../../../modules/profile' diff --git a/user-dashboard/src/routes/api/register/index.js b/user-dashboard/src/routes/api/register/index.js index 7ec4c1711..cc32d5111 100644 --- a/user-dashboard/src/routes/api/register/index.js +++ b/user-dashboard/src/routes/api/register/index.js @@ -1,3 +1,8 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ import { Router } from 'express' import config from "../../../modules/configuration" import User from "../../../modules/user" diff --git a/user-dashboard/src/routes/api/send_reset_email/index.js b/user-dashboard/src/routes/api/send_reset_email/index.js new file mode 100644 index 000000000..7db708b5b --- /dev/null +++ b/user-dashboard/src/routes/api/send_reset_email/index.js @@ -0,0 +1,57 @@ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ +import { Router } from 'express' +import config from "../../../modules/configuration" +import User from "../../../modules/user" +import nodemailer from 'nodemailer' + +const router = new Router() +const smtp_server = process.env.SMTP_SERVER +const smtp_port = process.env.SMTP_PORT +const email_auth_user = process.env.SMTP_AUTH_USERNAME +const email_auth_password = process.env.SMTP_AUTH_PASSWORD +const from_email = process.env.FROM_EMAIL +const webRoot = process.env.WEBROOT + +router.post("/", function(req, res) { + const user = new User(); + user.search(req.body.email).then(function (result) { + if (!result.user_exists) { + res.json({ + success: false, + userExists: false + }) + } else { + return result + } + }).then(function (result) { + let transporter = nodemailer.createTransport({ + host: smtp_server, + port: smtp_port, + secure: smtp_port === 465, + auth: { + user: email_auth_user, + pass: email_auth_password + }}); + const resetCode = new Buffer(result.apikey).toString('base64') + const mailOptions = { + from: from_email, + to: result.username, + subject: 'Reset Password for User', + text: `Reset password link is ${webRoot}/login#/reset-password?resetCode=${resetCode}` + }; + transporter.sendMail(mailOptions, (error, info) => { + if (error) { + console.error(error); + } + }); + res.json({ + success: true + }) + }) +}); + +export default router diff --git a/user-dashboard/src/routes/dashboard/filter.js b/user-dashboard/src/routes/dashboard/filter.js index 8d9252c95..a8528ba31 100644 --- a/user-dashboard/src/routes/dashboard/filter.js +++ b/user-dashboard/src/routes/dashboard/filter.js @@ -1,37 +1,31 @@ -/** - * Created by lixuc on 2017/5/3. - */ -var express = require("express"); -var config = require("../../modules/configuration"); -var Profile = require("../../modules/profile"); -var router = express.Router(); +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ +import { Router } from 'express' +import config from "../../modules/configuration" +import Profile from "../../modules/profile" + +const router = new Router(); router.get([ "/", - "/chain", - "/chain/:id", - "/contract", - "/analytics", - "/analytics/chaincode", - "/analytics/fabric", - "/analytics/infrastructure", - "/store" ], function(req, res, next) { - var userInfo = req.cookies[config.cookieName]; - if (userInfo) { - userInfo = JSON.parse(userInfo); + let userInfo = req.cookies[config.cookieName]; + userInfo = userInfo ? JSON.parse(userInfo) : {} + if (userInfo && userInfo.isActivated) { res.locals.username = userInfo.username || ""; next() - var profile = new Profile(userInfo.apikey); - profile.load().then(function(result) { - res.locals.username = result.result.name || userInfo.username.split("@")[0]; - next(); - }).catch(function(err) { - var e = new Error(err.message); - e.status = 500; - next(e); - }); + // const profile = new Profile(userInfo.apikey); + // profile.load().then(function(result) { + // res.locals.username = result.result.name || userInfo.username.split("@")[0]; + // next(); + // }).catch(function(err) { + // const e = new Error(err.message); + // e.status = 500; + // next(e); + // }); } else { res.cookie("referer", req.originalUrl); res.redirect("/"); diff --git a/user-dashboard/src/routes/dashboard/home.js b/user-dashboard/src/routes/dashboard/home.js index 6b165fcd9..2a58310a6 100644 --- a/user-dashboard/src/routes/dashboard/home.js +++ b/user-dashboard/src/routes/dashboard/home.js @@ -1,11 +1,11 @@ -/** - * Created by lixuc on 2017/5/3. - */ + +/* Copyright IBM Corp, All Rights Reserved. + + SPDX-License-Identifier: Apache-2.0 +*/ var express = require("express"); var config = require("../../modules/configuration"); var User = require("../../modules/user"); -var Chain = require("../../modules/chain"); -var Contract = require("../../modules/contract"); var router = express.Router(); @@ -16,7 +16,6 @@ router.get("/", function(req, res, next) { var userInfo = JSON.parse(req.cookies[config.cookieName]); var user = new User(); user.account(userInfo.apikey).then(function(result) { - //将用户的blue points保存到session中,有效期24小时 req.session.balance = result.balance; next(); }).catch(function(err) { @@ -29,11 +28,12 @@ router.get("/", function(req, res, next) { var renderer = { pointBalance: req.session.balance }; - var userInfo = JSON.parse(req.cookies[config.cookieName]); + let userInfo = JSON.parse(req.cookies[config.cookieName]); + userInfo["language"] = req.language renderer["chainNum"] = 1; renderer["contractNum"] = 1; - res.render("dashboard/home", renderer) - next() + res.render("dashboard", userInfo) + // next() // var chain = new Chain(userInfo.apikey); // var contract = new Contract(userInfo.apikey); // chain.amount().then(function(result) { diff --git a/user-dashboard/src/views/dashboard.html b/user-dashboard/src/views/dashboard.html new file mode 100644 index 000000000..54250074c --- /dev/null +++ b/user-dashboard/src/views/dashboard.html @@ -0,0 +1,36 @@ + + + +
+ + + + +