├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── README-CN.md
├── README.md
├── build
├── build.js
├── check-versions.js
├── dev-client.js
├── dev-server.js
├── utils.js
├── vue-loader.conf.js
├── webpack.base.conf.js
├── webpack.box.config.js
├── webpack.dev.conf.js
├── webpack.gui-server.config.js
└── webpack.prod.conf.js
├── config
└── index.js
├── lib
├── .DS_Store
├── handler
│ └── DataTransactionHandler.js
├── index.js
├── routes
│ ├── api.js
│ ├── demo.js
│ └── rpc.js
├── services
│ ├── DatasourceService.js
│ ├── FaucetService.js
│ ├── GXChainService.js
│ ├── IPFSService.js
│ ├── LevelDBService.js
│ ├── MerchantService.js
│ └── TimeoutService.js
├── tasks
│ ├── DatasourceTask.js
│ └── MerchantTask.js
└── utils
│ ├── ConfigStore.js
│ ├── constants.js
│ └── validator.js
├── package.json
├── script
├── start.cmd
└── start.sh
├── server
├── index.js
├── routes
│ └── api.js
├── services
│ ├── AccountService.js
│ ├── BoxService.js
│ ├── ConfigStore.js
│ ├── ConnectService.js
│ ├── DataService.js
│ └── ZipArchive.js
└── utils
│ └── dictionary_en.js
├── src
├── app.vue
├── libs
│ ├── china_regions
│ │ ├── area.js
│ │ ├── city.js
│ │ ├── index.js
│ │ └── province.js
│ ├── handler.js
│ └── util.js
├── main.js
├── router.js
├── template
│ └── index.ejs
├── vendors.js
└── views
│ ├── 404.vue
│ ├── Console.vue
│ ├── Init.vue
│ ├── League.vue
│ ├── Market.vue
│ ├── Product.vue
│ ├── Setting.vue
│ ├── common
│ ├── Footer.vue
│ └── Header.vue
│ └── components
│ ├── AccountCertification.vue
│ ├── AccountConfig.vue
│ ├── AccountCreate.vue
│ ├── AccountImage.vue
│ ├── AccountType.vue
│ ├── ConsoleManage.vue
│ ├── EnvType.vue
│ ├── GxbBoxStart.vue
│ ├── SettingAccount.vue
│ ├── SettingApi.vue
│ ├── SettingArchive.vue
│ └── SettingConfig.vue
├── static
└── img
│ ├── gxb-box.png
│ ├── init.svg
│ └── logo.png
├── test
├── connect.js
├── data_product.js
├── encrypt_decrypt.js
├── faucet.js
├── ipfs.js
├── league.js
└── leveldb.js
└── upgrade.sh
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["env", "stage-2"],
3 | "plugins": []
4 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | charset = utf-8
4 | indent_style = space
5 | indent_size = 4
6 | end_of_line = lf
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 | test
3 | config
4 | server/utils/dictionary_en.js
5 | src/vendors.js
6 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "standard",
3 | "root": true,
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "sourceType": "module",
7 | "ecmaFeatures": {
8 | "experimentalObjectRestSpread": true
9 | }
10 | },
11 | "env": {
12 | "node": true,
13 | "browser": true,
14 | "es6": true,
15 | "amd": true
16 | },
17 | "plugins": ["html", "vue", "standard"],
18 | "rules": {
19 | "indent": ["error", 4, { "SwitchCase": 1 }],
20 | "quotes": ["error", "single"],
21 | "semi": ["error", "always", { "omitLastInOneLineBlock": true }],
22 | "no-debugger": ["error"],
23 | "no-console": ["off"],
24 | "no-new": ["off"],
25 | "eqeqeq": ["off"],
26 | "no-extend-native": ["off"],
27 | "camelcase": ["off"],
28 | "vue/jsx-uses-vars": 2
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | .idea
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
20 | .grunt
21 |
22 | # node-waf configuration
23 | .lock-wscript
24 |
25 | # Compiled binary addons (http://nodejs.org/api/addons.html)
26 | es5
27 | dist
28 |
29 | # Dependency directory
30 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
31 | node_modules
32 |
33 | # development
34 | release
35 | .data
36 | sample
37 |
38 | # prod
39 | .DS_Store
40 | */.DS_Store
41 | archive
42 |
--------------------------------------------------------------------------------
/README-CN.md:
--------------------------------------------------------------------------------
1 | # 产品介绍 ([For Eglish](README.md))
2 |
3 | 公信宝数据交易客户端GXB-Box是基于Nodejs开发的一个部署在商户和数据源本地的客户端,商户和数据源可以通过本地调用的方式购买和出售数据,数据交易的全程请求参数和回传数据都是经过加密处理的,而GXB-BOX简化了这样的一个流程。
4 |
5 | ## 环境依赖
6 |
7 | 必要环境: Node 6+
8 |
9 | 建议系统: OSX、Linux
10 |
11 | ## Node环境安装
12 |
13 | 建议使用NVM([Node Version Manager](https://github.com/creationix/nvm))进行安装:
14 |
15 | ```
16 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.30.2/install.sh | bash
17 | . ~/.nvm/nvm.sh
18 | nvm install v6
19 | nvm use v6
20 | ```
21 |
22 | ## 克隆项目
23 |
24 | ```
25 | git clone https://github.com/gxchain/gxb-box.git
26 | ```
27 | ## 快速开始
28 |
29 | ```
30 | npm install
31 | npm run build
32 | npm run gui
33 | ```
34 |
35 | ## 开发模式启动
36 |
37 | 开发模式依赖于babel-node, 在克隆的工程下执行以下命令安装依赖:
38 |
39 | ```
40 | npm install -g babel-node
41 | npm install
42 | npm start
43 | ```
44 |
45 | ## 常见问题
46 |
47 | ### Q: 在设置了多重签名后,数据交易失败了
48 | A: 多重签名涉及到效率问题, 数据交易采用活跃权限进行单重签名, 请勿在参与数据交易的账户中设置多重签名,以免造成签名验证失败
49 |
50 | ### Q: 发生错误:"获取初始信息失败,请检查:账号(merchant或者datasource)是否正确配置"
51 | A: 检查config/config.json文件是否配置了错误的merchant或者datasource账户名, 如果是商户则**不需要**datasource配置,如果是数据源则**不一定需要**商户配置
52 |
53 | 商户配置示例:
54 |
55 | ```
56 | {
57 | "common": {
58 | "port":"3000",
59 | "ipfs_addr": "/ip4/139.196.138.193/tcp/5001",
60 | "witnesses": [
61 | "wss://node1.gxb.io","wss://node5.gxb.io","wss://node8.gxb.io","wss://node11.gxb.io"
62 | ],
63 | "faucet_url": "https://opengateway.gxb.io"
64 | },
65 | "merchant":{
66 | "account_name": "sample_user",
67 | "private_key":"5Ka9YjFQtfUUX2DdnqkaPWH1rVeSeby7Cj2VdjRt79S9kKLvXR7",
68 | "callback_url":"http://localhost:3000/demo/callback",
69 | "privacy_request_timeout":120000,
70 | "default_timeout":8000
71 | }
72 | }
73 | ```
74 |
75 | 数据源配置示例:
76 |
77 | ```
78 | {
79 | "common": {
80 | "port":"3000",
81 | "ipfs_addr": "/ip4/139.196.138.193/tcp/5001",
82 | "witnesses": [
83 | "wss://node1.gxb.io","wss://node5.gxb.io","wss://node8.gxb.io","wss://node11.gxb.io"
84 | ],
85 | "faucet_url": "https://opengateway.gxb.io"
86 | },
87 | "datasource": {
88 | "account_name": "sample_datasource",
89 | "private_key": "5JLL3mqAFt2YHVJf8W3h9oUPP2sjceLYSSyEbSt1yMjeucxGH98",
90 | "service": "http://localhost:3000/demo/call",
91 | "subscribed_data_product": [
92 | "1.17.1"
93 | ]
94 | }
95 | }
96 | ```
97 |
98 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Introduction ([中文](README-CN.md))
2 | GXB-BOX is a client-side, deployed at local merchant and datasource, and based on Node.js.
3 | Merchant and datasource can trade data via locally call, data transfer is all the way encrypted, and GXB-BOX simplified this process.
4 | ## System requirement
5 |
6 | (Required): Node 6+
7 |
8 | (Operation system): OSX、Linux
9 |
10 | ## Install under Node environment
11 |
12 | Recommend to use NVM([Node Version Manager](https://github.com/creationix/nvm)) for installation:
13 |
14 | ```
15 | curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.30.2/install.sh | bash
16 | . ~/.nvm/nvm.sh
17 | nvm install v6
18 | nvm use v6
19 | ```
20 |
21 | ## Clone project
22 |
23 | ```
24 | git clone https://github.com/gxchain/gxb-box.git
25 | ```
26 |
27 | ## Quick start
28 |
29 | ```
30 | npm install
31 | npm run build
32 | npm run gui
33 | ```
34 |
35 | ## Development mode start
36 |
37 | Development mode depends on babel-node, execute the following commands to install dependencies under cloned engineering mode:
38 |
39 | ```
40 | npm install -g babel-node
41 | npm install
42 | npm start
43 | ```
44 |
45 | ## FAQ
46 |
47 | ### Q: Data transaction failed after set multi-signature
48 | A: We recommend to use single signature for data transactions, due to the efficiency limitation of multi-signature
49 | ### Q: Error: "failed to get initial information, please check configuration of merchant account and datasource account".
50 | A: Check config/config.json to see if the name of merchant or datasource is correct. For merchant, NO need for datasource configuration; for datasource, do not have to perform merchant configuration.
51 |
52 | merchant configuration sample:
53 |
54 | ```
55 | {
56 | "common": {
57 | "port":"3000",
58 | "ipfs_addr": "/ip4/139.196.138.193/tcp/5001",
59 | "witnesses": [
60 | "wss://node1.gxb.io","wss://node5.gxb.io","wss://node8.gxb.io","wss://node11.gxb.io"
61 | ],
62 | "faucet_url": "https://opengateway.gxb.io"
63 | },
64 | "merchant":{
65 | "account_name": "sample_user",
66 | "private_key":"5Ka9YjFQtfUUX2DdnqkaPWH1rVeSeby7Cj2VdjRt79S9kKLvXR7",
67 | "callback_url":"http://localhost:3000/demo/callback",
68 | "privacy_request_timeout":120000,
69 | "default_timeout":8000
70 | }
71 | }
72 | ```
73 |
74 | Datasource configuration sample:
75 |
76 | ```
77 | {
78 | "common": {
79 | "port":"3000",
80 | "ipfs_addr": "/ip4/139.196.138.193/tcp/5001",
81 | "witnesses": [
82 | "wss://node1.gxb.io","wss://node5.gxb.io","wss://node8.gxb.io","wss://node11.gxb.io"
83 | ],
84 | "faucet_url": "https://opengateway.gxb.io"
85 | },
86 | "datasource": {
87 | "account_name": "sample_datasource",
88 | "private_key": "5JLL3mqAFt2YHVJf8W3h9oUPP2sjceLYSSyEbSt1yMjeucxGH98",
89 | "service": "http://localhost:3000/demo/call",
90 | "subscribed_data_product": [
91 | "1.17.1"
92 | ]
93 | }
94 | }
95 | ```
96 |
--------------------------------------------------------------------------------
/build/build.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')();
2 |
3 | process.env.NODE_ENV = 'production';
4 |
5 | var ora = require('ora');
6 | var rm = require('rimraf');
7 | var path = require('path');
8 | var chalk = require('chalk');
9 | var webpack = require('webpack');
10 | var config = require('../config');
11 | var webpackConfig = require('./webpack.prod.conf');
12 |
13 | var spinner = ora('building for production...');
14 | spinner.start();
15 |
16 | rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
17 | if (err) throw err;
18 | webpack(webpackConfig, function (err, stats) {
19 | spinner.stop();
20 | if (err) throw err;
21 | process.stdout.write(stats.toString({
22 | colors: true,
23 | modules: false,
24 | children: false,
25 | chunks: false,
26 | chunkModules: false
27 | }) + '\n\n');
28 |
29 | console.log(chalk.cyan(' Build complete.\n'));
30 | console.log(chalk.yellow(
31 | ' Tip: built files are meant to be served over an HTTP server.\n' +
32 | ' Opening index.html over file:// won\'t work.\n'
33 | ));
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/build/check-versions.js:
--------------------------------------------------------------------------------
1 | var chalk = require('chalk');
2 | var semver = require('semver');
3 | var packageConfig = require('../package.json');
4 | var shell = require('shelljs');
5 |
6 | function exec (cmd) {
7 | return require('child_process').execSync(cmd).toString().trim();
8 | }
9 |
10 | var versionRequirements = [
11 | {
12 | name: 'node',
13 | currentVersion: semver.clean(process.version),
14 | versionRequirement: packageConfig.engines.node
15 | }
16 | ];
17 |
18 | module.exports = function () {
19 | var warnings = [];
20 | for (var i = 0; i < versionRequirements.length; i++) {
21 | var mod = versionRequirements[i];
22 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
23 | warnings.push(mod.name + ': ' +
24 | chalk.red(mod.currentVersion) + ' should be ' +
25 | chalk.green(mod.versionRequirement)
26 | );
27 | }
28 | }
29 |
30 | if (warnings.length) {
31 | console.log('');
32 | console.log(chalk.yellow('To use this template, you must update following to modules:'));
33 | console.log();
34 | for (var i = 0; i < warnings.length; i++) {
35 | var warning = warnings[i];
36 | console.log(' ' + warning);
37 | }
38 | console.log();
39 | process.exit(1);
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/build/dev-client.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | require('eventsource-polyfill');
3 | var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true');
4 |
5 | hotClient.subscribe(function (event) {
6 | if (event.action === 'reload') {
7 | window.location.reload();
8 | }
9 | });
10 |
--------------------------------------------------------------------------------
/build/dev-server.js:
--------------------------------------------------------------------------------
1 | require('./check-versions')();
2 |
3 | var config = require('../config');
4 | if (!process.env.NODE_ENV) {
5 | process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV);
6 | }
7 |
8 | var opn = require('opn');
9 | var path = require('path');
10 | var express = require('express');
11 | var webpack = require('webpack');
12 | var proxyMiddleware = require('http-proxy-middleware');
13 | var webpackConfig = require('./webpack.dev.conf');
14 |
15 | // default port where dev server listens for incoming traffic
16 | var port = process.env.PORT || config.dev.port;
17 | // automatically open browser, if not set will be false
18 | var autoOpenBrowser = !!config.dev.autoOpenBrowser;
19 | // Define HTTP proxies to your custom API backend
20 | // https://github.com/chimurai/http-proxy-middleware
21 | var proxyTable = config.dev.proxyTable;
22 |
23 | var app = express();
24 | var compiler = webpack(webpackConfig);
25 |
26 | var devMiddleware = require('webpack-dev-middleware')(compiler, {
27 | publicPath: webpackConfig.output.publicPath,
28 | quiet: true
29 | });
30 |
31 | var hotMiddleware = require('webpack-hot-middleware')(compiler, {
32 | log: () => {
33 | },
34 | heartbeat: 2000
35 | });
36 | // force page reload when html-webpack-plugin template changes
37 | compiler.plugin('compilation', function (compilation) {
38 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
39 | hotMiddleware.publish({action: 'reload'});
40 | cb();
41 | });
42 | });
43 |
44 | // proxy api requests
45 | Object.keys(proxyTable).forEach(function (context) {
46 | var options = proxyTable[context];
47 | if (typeof options === 'string') {
48 | options = {target: options};
49 | }
50 | app.use(proxyMiddleware(options.filter || context, options));
51 | });
52 |
53 | // handle fallback for HTML5 history API
54 | app.use(require('connect-history-api-fallback')());
55 |
56 | // serve webpack bundle output
57 | app.use(devMiddleware);
58 |
59 | // enable hot-reload and state-preserving
60 | // compilation error display
61 | app.use(hotMiddleware);
62 |
63 | // serve pure static assets
64 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory);
65 | app.use(staticPath, express.static('./static'));
66 |
67 | var uri = 'http://localhost:' + port;
68 |
69 | var _resolve;
70 | var readyPromise = new Promise(resolve => {
71 | _resolve = resolve;
72 | });
73 |
74 | console.log('> Starting dev server...');
75 | devMiddleware.waitUntilValid(() => {
76 | console.log('> Listening at ' + uri + '\n');
77 | // when env is testing, don't need open it
78 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
79 | opn(uri);
80 | }
81 | _resolve();
82 | });
83 |
84 | var server = app.listen(port);
85 |
86 | module.exports = {
87 | ready: readyPromise,
88 | close: () => {
89 | server.close();
90 | }
91 | };
92 |
--------------------------------------------------------------------------------
/build/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var config = require('../config');
3 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
4 |
5 | exports.assetsPath = function (_path) {
6 | var assetsSubDirectory = process.env.NODE_ENV === 'production'
7 | ? config.build.assetsSubDirectory
8 | : config.dev.assetsSubDirectory;
9 | return path.posix.join(assetsSubDirectory, _path);
10 | };
11 |
12 | exports.cssLoaders = function (options) {
13 | options = options || {};
14 |
15 | var cssLoader = {
16 | loader: 'css-loader',
17 | options: {
18 | minimize: process.env.NODE_ENV === 'production',
19 | sourceMap: options.sourceMap
20 | }
21 | };
22 |
23 | // generate loader string to be used with extract text plugin
24 | function generateLoaders (loader, loaderOptions) {
25 | var loaders = [cssLoader];
26 | if (loader) {
27 | loaders.push({
28 | loader: loader + '-loader',
29 | options: Object.assign({}, loaderOptions, {
30 | sourceMap: options.sourceMap
31 | })
32 | });
33 | }
34 |
35 | // Extract CSS when that option is specified
36 | // (which is the case during production build)
37 | if (options.extract) {
38 | return ExtractTextPlugin.extract({
39 | use: loaders,
40 | fallback: 'vue-style-loader'
41 | });
42 | } else {
43 | return ['vue-style-loader'].concat(loaders);
44 | }
45 | }
46 |
47 | // https://vue-loader.vuejs.org/en/configurations/extract-css.html
48 | return {
49 | css: generateLoaders(),
50 | postcss: generateLoaders(),
51 | less: generateLoaders('less'),
52 | sass: generateLoaders('sass', {indentedSyntax: true}),
53 | scss: generateLoaders('sass'),
54 | stylus: generateLoaders('stylus'),
55 | styl: generateLoaders('stylus')
56 | };
57 | };
58 |
59 | // Generate loaders for standalone style files (outside of .vue)
60 | exports.styleLoaders = function (options) {
61 | var output = [];
62 | var loaders = exports.cssLoaders(options);
63 | for (var extension in loaders) {
64 | var loader = loaders[extension];
65 | output.push({
66 | test: new RegExp('\\.' + extension + '$'),
67 | use: loader
68 | });
69 | }
70 | return output;
71 | };
72 |
--------------------------------------------------------------------------------
/build/vue-loader.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils');
2 | var config = require('../config');
3 | var isProduction = process.env.NODE_ENV === 'production';
4 |
5 | module.exports = {
6 | loaders: utils.cssLoaders({
7 | sourceMap: isProduction
8 | ? config.build.productionSourceMap
9 | : config.dev.cssSourceMap,
10 | extract: isProduction
11 | }),
12 | postcss: [
13 | require('autoprefixer')({
14 | browsers: ['last 7 versions']
15 | })
16 | ],
17 | transformToRequire: {
18 | video: 'src',
19 | source: 'src',
20 | img: 'src',
21 | image: 'xlink:href'
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/build/webpack.base.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var utils = require('./utils');
3 | var config = require('../config');
4 | var vueLoaderConfig = require('./vue-loader.conf');
5 |
6 | function resolve (dir) {
7 | return path.join(__dirname, '..', dir);
8 | }
9 |
10 | module.exports = {
11 | entry: {
12 | app: './src/main.js'
13 | },
14 | output: {
15 | path: config.build.assetsRoot,
16 | filename: '[name].js',
17 | publicPath: process.env.NODE_ENV === 'production'
18 | ? config.build.assetsPublicPath
19 | : config.dev.assetsPublicPath
20 | },
21 | resolve: {
22 | extensions: ['.js', '.vue', '.json'],
23 | alias: {
24 | 'vue$': 'vue/dist/vue.esm.js',
25 | '@': resolve('src')
26 | }
27 | },
28 | module: {
29 | rules: [
30 | {
31 | test: /\.(js|vue)$/,
32 | loader: 'eslint-loader',
33 | enforce: 'pre',
34 | include: [resolve('src'), resolve('test')],
35 | options: {
36 | formatter: require('eslint-friendly-formatter')
37 | }
38 | },
39 | {
40 | test: /\.vue$/,
41 | loader: 'vue-loader',
42 | options: vueLoaderConfig
43 | },
44 | {
45 | test: /\.js$/,
46 | loader: 'babel-loader',
47 | include: [resolve('src'), resolve('test')]
48 | },
49 | {
50 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
51 | loader: 'url-loader',
52 | options: {
53 | limit: 10000,
54 | name: utils.assetsPath('img/[name].[hash:7].[ext]')
55 | }
56 | },
57 | {
58 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
59 | loader: 'url-loader',
60 | options: {
61 | limit: 10000,
62 | name: utils.assetsPath('media/[name].[hash:7].[ext]')
63 | }
64 | },
65 | {
66 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
67 | loader: 'url-loader',
68 | options: {
69 | limit: 10000,
70 | name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
71 | }
72 | }
73 | ]
74 | }
75 | };
76 |
--------------------------------------------------------------------------------
/build/webpack.box.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var fs = require('fs');
3 | var path = require('path');
4 |
5 | var nodeModules = {};
6 | fs.readdirSync('node_modules')
7 | .filter(function(x) {
8 | return ['.bin'].indexOf(x) === -1;
9 | })
10 | .forEach(function(mod) {
11 | nodeModules[mod] = 'commonjs ' + mod;
12 | });
13 |
14 | module.exports = {
15 | entry: ['../lib/index.js'],
16 | output: {
17 | path: path.resolve(__dirname, '../dist/box'),
18 | filename: 'gxb-box.js'
19 | },
20 | target: 'node',
21 | externals: nodeModules,
22 | context: __dirname,
23 | module: {
24 | loaders: [{
25 | test: /\.js$/,
26 | loader: 'babel-loader',
27 | exclude: [
28 | path.resolve(__dirname, "../node_modules")
29 | ],
30 | query: {
31 | plugins: ['transform-runtime'],
32 | presets: ['es2015', 'stage-2'],
33 | }
34 | }, {
35 | test: /\.json$/,
36 | loader: 'json-loader'
37 | }]
38 | },
39 | resolve: {
40 | extensions: ['.js', '.json']
41 | },
42 | plugins: [
43 | // new webpack.optimize.UglifyJsPlugin({
44 | // exclude: /\.min\.js$/,
45 | // mangle: true,
46 | // output: {comments: false},
47 | // compress: {warnings: false}
48 | // })
49 | ]
50 | }
--------------------------------------------------------------------------------
/build/webpack.dev.conf.js:
--------------------------------------------------------------------------------
1 | var utils = require('./utils');
2 | var webpack = require('webpack');
3 | var config = require('../config');
4 | var merge = require('webpack-merge');
5 | var baseWebpackConfig = require('./webpack.base.conf');
6 | var HtmlWebpackPlugin = require('html-webpack-plugin');
7 | var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
8 |
9 | // add hot-reload related code to entry chunks
10 | Object.keys(baseWebpackConfig.entry).forEach(function (name) {
11 | baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]);
12 | });
13 |
14 | module.exports = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({sourceMap: config.dev.cssSourceMap})
17 | },
18 | // cheap-module-eval-source-map is faster for development
19 | devtool: '#cheap-module-eval-source-map',
20 | plugins: [
21 | new webpack.DefinePlugin({
22 | 'process.env': config.dev.env,
23 | '__witnesses__': JSON.stringify(config.dev.witnesses)
24 | }),
25 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage
26 | new webpack.HotModuleReplacementPlugin(),
27 | new webpack.NoEmitOnErrorsPlugin(),
28 | // https://github.com/ampedandwired/html-webpack-plugin
29 | new HtmlWebpackPlugin({
30 | filename: 'index.html',
31 | template: './src/template/index.ejs',
32 | inject: true
33 | }),
34 | new FriendlyErrorsPlugin()
35 | ]
36 | });
37 |
--------------------------------------------------------------------------------
/build/webpack.gui-server.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var fs = require('fs');
3 | var path = require('path');
4 |
5 | var nodeModules = {};
6 | fs.readdirSync('node_modules')
7 | .filter(function (x) {
8 | return ['.bin'].indexOf(x) === -1;
9 | })
10 | .forEach(function (mod) {
11 | nodeModules[mod] = 'commonjs ' + mod;
12 | });
13 |
14 | module.exports = {
15 | entry: ['../server/index.js'],
16 | output: {
17 | path: path.resolve(__dirname, '../dist/gui-server'),
18 | filename: 'index.js'
19 | },
20 | target: 'node',
21 | externals: nodeModules,
22 | context: __dirname,
23 | module: {
24 | loaders: [
25 | {
26 | test: /\.js$/,
27 | loader: 'eslint-loader',
28 | enforce: 'pre',
29 | exclude: [
30 | path.resolve(__dirname, '../node_modules')
31 | ],
32 | options: {
33 | formatter: require('eslint-friendly-formatter')
34 | }
35 | },
36 | {
37 | test: /\.js$/,
38 | loader: 'babel-loader',
39 | exclude: [
40 | path.resolve(__dirname, '../node_modules')
41 | ],
42 | query: {
43 | plugins: ['transform-runtime'],
44 | presets: ['es2015', 'stage-2']
45 | }
46 | },
47 | {
48 | test: /\.json$/,
49 | loader: 'json-loader'
50 | }
51 | ]
52 | },
53 | resolve: {
54 | extensions: ['.js', '.json']
55 | },
56 | plugins: [
57 | new webpack.optimize.UglifyJsPlugin({
58 | exclude: /\.min\.js$/,
59 | mangle: true,
60 | output: {comments: false},
61 | compress: {warnings: false}
62 | })
63 | ]
64 | };
65 |
--------------------------------------------------------------------------------
/build/webpack.prod.conf.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var utils = require('./utils');
3 | var webpack = require('webpack');
4 | var config = require('../config');
5 | var merge = require('webpack-merge');
6 | var baseWebpackConfig = require('./webpack.base.conf');
7 | var CopyWebpackPlugin = require('copy-webpack-plugin');
8 | var HtmlWebpackPlugin = require('html-webpack-plugin');
9 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
10 | var OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin');
11 |
12 | var env = config.build.env;
13 |
14 | var webpackConfig = merge(baseWebpackConfig, {
15 | module: {
16 | rules: utils.styleLoaders({
17 | sourceMap: config.build.productionSourceMap,
18 | extract: true
19 | })
20 | },
21 | devtool: config.build.productionSourceMap ? '#source-map' : false,
22 | output: {
23 | path: config.build.assetsRoot,
24 | filename: utils.assetsPath('js/[name].[chunkhash].js'),
25 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
26 | },
27 | plugins: [
28 | // http://vuejs.github.io/vue-loader/en/workflow/production.html
29 | new webpack.DefinePlugin({
30 | 'process.env': env,
31 | '__witnesses__': JSON.stringify(config.build.witnesses)
32 | }),
33 | new webpack.optimize.UglifyJsPlugin({
34 | compress: {
35 | warnings: false
36 | },
37 | sourceMap: true
38 | }),
39 | // extract css into its own file
40 | new ExtractTextPlugin({
41 | filename: utils.assetsPath('css/[name].[contenthash].css')
42 | }),
43 | // Compress extracted CSS. We are using this plugin so that possible
44 | // duplicated CSS from different components can be deduped.
45 | new OptimizeCSSPlugin({
46 | cssProcessorOptions: {
47 | safe: true
48 | }
49 | }),
50 | // generate dist index.html with correct asset hash for caching.
51 | // you can customize output by editing /index.html
52 | // see https://github.com/ampedandwired/html-webpack-plugin
53 | new HtmlWebpackPlugin({
54 | filename: config.build.index,
55 | template: './src/template/index.ejs',
56 | inject: true,
57 | minify: {
58 | removeComments: true,
59 | collapseWhitespace: true,
60 | removeAttributeQuotes: true
61 | // more options:
62 | // https://github.com/kangax/html-minifier#options-quick-reference
63 | },
64 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin
65 | chunksSortMode: 'dependency'
66 | }),
67 | // split vendor js into its own file
68 | new webpack.optimize.CommonsChunkPlugin({
69 | name: 'vendor',
70 | minChunks: function (module, count) {
71 | // any required modules inside node_modules are extracted to vendor
72 | return (
73 | module.resource &&
74 | /\.js$/.test(module.resource) &&
75 | module.resource.indexOf(
76 | path.join(__dirname, '../node_modules')
77 | ) === 0
78 | );
79 | }
80 | }),
81 | // extract webpack runtime and module manifest to its own file in order to
82 | // prevent vendor hash from being updated whenever app bundle is updated
83 | new webpack.optimize.CommonsChunkPlugin({
84 | name: 'manifest',
85 | chunks: ['vendor']
86 | }),
87 | // copy custom static assets
88 | new CopyWebpackPlugin([
89 | {
90 | from: path.resolve(__dirname, '../static'),
91 | to: config.build.assetsSubDirectory,
92 | ignore: ['.*']
93 | }
94 | ])
95 | ]
96 | });
97 |
98 | if (config.build.productionGzip) {
99 | var CompressionWebpackPlugin = require('compression-webpack-plugin');
100 |
101 | webpackConfig.plugins.push(
102 | new CompressionWebpackPlugin({
103 | asset: '[path].gz[query]',
104 | algorithm: 'gzip',
105 | test: new RegExp(
106 | '\\.(' +
107 | config.build.productionGzipExtensions.join('|') +
108 | ')$'
109 | ),
110 | threshold: 10240,
111 | minRatio: 0.8
112 | })
113 | );
114 | }
115 |
116 | if (config.build.bundleAnalyzerReport) {
117 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
118 | webpackConfig.plugins.push(new BundleAnalyzerPlugin());
119 | }
120 |
121 | module.exports = webpackConfig;
122 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: {
7 | NODE_ENV: '"production"'
8 | },
9 | index: path.resolve(__dirname, '../dist/gui/index.html'),
10 | assetsRoot: path.resolve(__dirname, '../dist/gui'),
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | productionSourceMap: false,
14 | witnesses: [
15 | "wss://node1.gxb.io",
16 | "wss://node5.gxb.io",
17 | "wss://node8.gxb.io",
18 | "wss://node11.gxb.io"
19 | ],
20 | faucet_url: "https://opengateway.gxb.io",
21 | referrer: "opengateway",
22 | // Gzip off by default as many popular static hosts such as
23 | // Surge or Netlify already gzip all static assets for you.
24 | // Before setting to `true`, make sure to:
25 | // npm install --save-dev compression-webpack-plugin
26 | productionGzip: false,
27 | productionGzipExtensions: ['js', 'css'],
28 | // Run the build command with an extra argument to
29 | // View the bundle analyzer report after build finishes:
30 | // `npm run build --report`
31 | // Set to `true` or `false` to always turn it on or off
32 | bundleAnalyzerReport: process.env.npm_config_report
33 | },
34 | dev: {
35 | env: {
36 | NODE_ENV: '"development"'
37 | },
38 | port: 8080,
39 | autoOpenBrowser: true,
40 | assetsSubDirectory: 'static',
41 | assetsPublicPath: '/',
42 | witnesses: [
43 | "ws://47.96.164.78:28090"
44 | ],
45 | faucet_url: "http://47.96.164.78:8888",
46 | referrer: "nathan",
47 | // CSS Sourcemaps off by default because relative paths are "buggy"
48 | // with this option, according to the CSS-Loader README
49 | // (https://github.com/webpack/css-loader#sourcemaps)
50 | // In our experience, they generally work as expected,
51 | // just be aware of this issue when enabling this option.
52 | cssSourceMap: false
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxchain/gxb-box/ec843dc6a92106477e8fa8fb1bfab1286eceeb33/lib/.DS_Store
--------------------------------------------------------------------------------
/lib/handler/DataTransactionHandler.js:
--------------------------------------------------------------------------------
1 | import ConfigStore from '../utils/ConfigStore';
2 | import MerchantTask from '../tasks/MerchantTask';
3 | import DatasourceTask from '../tasks/DatasourceTask';
4 | import {TRANSACTION_STATUS_MAP} from '../utils/constants';
5 |
6 | export default {
7 |
8 | /**
9 | * 数据交易调度函数
10 | * @param data_transaction
11 | */
12 | schedule (data_transaction) {
13 | let request_id = data_transaction.request_id;
14 | let product_id = data_transaction.product_id;
15 | let status = TRANSACTION_STATUS_MAP[data_transaction.status];
16 | let config = ConfigStore.config;
17 | let is_product_subscribed = config.datasource && config.datasource.subscribed_data_product && config.datasource.subscribed_data_product.find(function (prod) {
18 | return prod == product_id;
19 | });
20 | let is_request_in_queue = MerchantTask.exist(request_id);
21 |
22 | // 已确认的交易, 数据源可以回传数据
23 | if (status == TRANSACTION_STATUS_MAP.CONFIRMED && is_product_subscribed) {
24 | DatasourceTask.deal_with_data_transaction(data_transaction);
25 | }
26 | // 已确认的交易, 商户根据状态进行处理
27 | if (status == TRANSACTION_STATUS_MAP.CONFIRMED && is_request_in_queue) {
28 | MerchantTask.deal_with_data_transaction(data_transaction);
29 | }
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import logger from 'morgan';
3 | import bodyParser from 'body-parser';
4 | import http from 'http';
5 | import Promise from 'bluebird';
6 | import {Apis, Manager} from 'gxbjs-ws';
7 | import {ChainStore} from 'gxbjs';
8 | import DataTransactionHandler from './handler/DataTransactionHandler';
9 | import MerchantTask from './tasks/MerchantTask';
10 | import DatasourceTask from './tasks/DatasourceTask';
11 | import GXChainService from './services/GXChainService';
12 | import LevelDBService from './services/LevelDBService';
13 | import ConfigStore from './utils/ConfigStore';
14 |
15 | import figlet from 'figlet';
16 | import colors from 'colors/safe';
17 |
18 | let app = express();
19 | let connected = false;
20 |
21 | app.use(logger('dev'));
22 | app.use(bodyParser.json());
23 | app.use(bodyParser.urlencoded({extended: false}));
24 |
25 | app.use(function (req, res, next) {
26 | res.header('Access-Control-Allow-Origin', '*');
27 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
28 | next();
29 | });
30 |
31 | const connectedCheck = function (req, res, next) {
32 | if (connected) {
33 | next();
34 | } else {
35 | res.status(500).send({
36 | code: 'UNKNOWN_ERROR',
37 | message: '正在连接网络,请稍后再试'
38 | });
39 | }
40 | };
41 |
42 | app.use('/rpc', connectedCheck, require('./routes/rpc'));
43 | app.use('/demo', connectedCheck, require('./routes/demo'));
44 | app.use('/api', connectedCheck, require('./routes/api'));
45 |
46 | app.use(function (req, res, next) {
47 | var err = new Error('Not Found');
48 | err.status = 404;
49 | next(err);
50 | });
51 |
52 | if (app.get('env') === 'development') {
53 | app.use(function (err, req, res, next) {
54 | res.status(err.status || 500);
55 | res.send({
56 | message: err.message,
57 | error: err
58 | });
59 | });
60 | }
61 |
62 | app.use(function (err, req, res, next) {
63 | res.status(err.status || 500);
64 | res.send({
65 | message: err.message,
66 | error: {}
67 | });
68 | });
69 |
70 | const filterAndSortURLs = (latencies, witnesses) => {
71 | let us = witnesses
72 | .filter(a => {
73 | /* Only keep the nodes we were able to connect to */
74 | return !!latencies[a];
75 | })
76 | .sort((a, b) => {
77 | return latencies[a] - latencies[b];
78 | });
79 | return us;
80 | };
81 |
82 | Promise.all([
83 | ConfigStore.init(),
84 | MerchantTask.init(),
85 | DatasourceTask.init()
86 | ]).then((results) => {
87 | let config = results[0];
88 | let witnesses = (config && config.common && config.common.witnesses) || [];
89 | let connectionManager = new Manager({url: witnesses[0], urls: witnesses});
90 | if (witnesses.length == 0) {
91 | console.error('未配置启动节点,请先在config.json文件中配置common.witnesses');
92 | return;
93 | }
94 | /**
95 | * 连接witness
96 | * @param callback
97 | */
98 | let connect = function (callback) {
99 | connectionManager.checkConnections().then((resp) => {
100 | console.log('延迟\n', JSON.stringify(resp, null, '\t'));
101 | let urls = filterAndSortURLs(resp, witnesses);
102 | if (urls.length == 0) {
103 | // setTimeout(function () {
104 | // connect(callback);
105 | // }, 3000);
106 | console.log('witnesses', witnesses);
107 | connectionManager.url = witnesses[0];
108 | connectionManager.urls = witnesses;
109 |
110 | connectionManager.connectWithFallback(true).then(() => {
111 | console.log('已连接');
112 | connected = true;
113 | callback && callback();
114 | }).catch((ex) => {
115 | console.error('连接失败,3秒后重试', ex.message);
116 | setTimeout(function () {
117 | connect(callback);
118 | }, 3000);
119 | });
120 | } else {
121 | connectionManager.urls = urls;
122 | connectionManager.connectWithFallback(true).then(() => {
123 | console.log('已连接');
124 | connected = true;
125 | callback && callback();
126 | }).catch((ex) => {
127 | console.error('连接失败,3秒后重试', ex.message);
128 | setTimeout(function () {
129 | connect(callback);
130 | }, 3000);
131 | });
132 | }
133 | }).catch((ex) => {
134 | console.error('检查连接失败,3秒后重试', ex.message);
135 | setTimeout(function () {
136 | connect(callback);
137 | }, 3000);
138 | });
139 | };
140 |
141 | /**
142 | * 启动web服务
143 | */
144 | let serverStarted = false;
145 | let startServer = function () {
146 | if (serverStarted) {
147 | return;
148 | }
149 | serverStarted = true;
150 | let port = parseInt(config.common.port || '3000');
151 | app.set('port', port);
152 | let server = http.createServer(app);
153 | server.listen(port);
154 | server.on('error', onError);
155 | server.on('listening', () => {
156 | var addr = server.address();
157 | var bind = typeof addr === 'string'
158 | ? 'pipe ' + addr
159 | : 'port ' + addr.port;
160 | console.log('Listening on ' + bind);
161 | });
162 | figlet('GXB-BOX', 'ANSI Shadow', function (err, text) {
163 | if (err) {
164 | console.error(err);
165 | }
166 | console.log(colors.rainbow('\n=*=*=*=*=*=*=*=*=*==*=*= 公信宝数据交易客户端已启动 =*=*=*==*=*=*=*=*=*=*=*=\n'));
167 | console.log(colors.cyan(`${(text || '').split('\n').map(function (line) {
168 | return `\t${line}`;
169 | }).join('\n')}`));
170 | console.log(colors.rainbow('=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=**=*=*=*=*=*=*==*=*=*=\n'));
171 | });
172 | };
173 |
174 | /**
175 | * 数据交易消息订阅
176 | * @param data_transactions
177 | */
178 | let subscriber = function (data_transactions) {
179 | data_transactions.forEach(function (data_transaction) {
180 | DataTransactionHandler.schedule(data_transaction);
181 | });
182 | };
183 |
184 | /**
185 | * 初始化连接
186 | */
187 | let initConnection = function () {
188 | console.log('初始化数据缓存');
189 | let promises = [
190 | ChainStore.init()
191 | ];
192 | if (config.merchant && config.merchant.account_name) {
193 | promises.push(GXChainService.fetch_account(config.merchant.account_name));
194 | }
195 | if (config.datasource && config.datasource.account_name) {
196 | promises.push(GXChainService.fetch_account(config.datasource.account_name));
197 | }
198 | Promise.all(promises).then(function () {
199 | // 订阅数据交易广播
200 | // Apis.instance().db_api().exec('unsubscribe_from_transaction', [subscriber, true])
201 | Apis.instance().db_api().exec('set_data_transaction_subscribe_callback', [subscriber, true]);
202 | console.log('已订阅数据交易事件');
203 | startServer();
204 | MerchantTask.resume();
205 | }).catch((ex) => {
206 | let isNotSync = /ChainStore sync error/.test(ex.message);
207 | if (isNotSync) {
208 | console.error('获取初始信息失败,请检查:\n1. 节点数据是否同步 \n2. 系统时钟是否正确\n', ex);
209 | } else {
210 | console.error('获取初始信息失败,请检查:账号(merchant或者datasource)是否正确配置', ex);
211 | }
212 | });
213 | };
214 | // websocket 状态处理
215 | Apis.setRpcConnectionStatusCallback(function (status) {
216 | var statusMap = {
217 | open: '开启',
218 | closed: '关闭',
219 | error: '错误',
220 | reconnect: '重新连接'
221 | };
222 |
223 | console.log('witness当前状态:', statusMap[status] || status);
224 |
225 | if (status === 'reconnect') {
226 | console.log('断开重连');
227 | ChainStore.resetCache();
228 | } else if (connected && (status == 'closed' || status == 'error')) { // 出错重连
229 | connected = false;
230 | console.log('重新连接其他witness');
231 | connect(function () {
232 | ChainStore.subscribed = false;
233 | ChainStore.subError = null;
234 | ChainStore.clearCache();
235 | ChainStore.head_block_time_string = null;
236 | initConnection();
237 | });
238 | }
239 | });
240 | // 首次连接
241 | connect(function () {
242 | initConnection();
243 | });
244 | }).catch((ex) => {
245 | console.error('加载配置失败,请检查config.json', ex);
246 | });
247 |
248 | /**
249 | * Event listener for HTTP server "error" event.
250 | */
251 | function onError (error) {
252 | if (error.syscall !== 'listen') {
253 | throw error;
254 | }
255 |
256 | // handle specific listen errors with friendly messages
257 | switch (error.code) {
258 | case 'EACCES':
259 | console.error('port requires elevated privileges');
260 | process.exit(1);
261 | break;
262 | case 'EADDRINUSE':
263 | console.error('port is already in use');
264 | process.exit(1);
265 | break;
266 | default:
267 | throw error;
268 | }
269 | }
270 |
271 | process.stdin.resume();
272 |
273 | function exitHandler (reason, err) {
274 | if (err) console.log(err.stack);
275 | console.log('程序退出:', reason);
276 | Promise.all([MerchantTask.store(), LevelDBService.put('last-close', new Date().getTime())]).then(function () {
277 | process.exit();
278 | }).catch((ex) => {
279 | process.exit();
280 | });
281 | }
282 |
283 | // do something when app is closing
284 | process.on('exit', exitHandler.bind(null, 'exit'));
285 |
286 | // catches ctrl+c event
287 | process.on('SIGINT', exitHandler.bind(null, 'SIGINT'));
288 |
289 | // catches uncaught exceptions
290 | process.on('uncaughtException', exitHandler.bind(null, 'uncaughtException'));
291 |
--------------------------------------------------------------------------------
/lib/routes/api.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import LevelDBService from '../services/LevelDBService';
3 | import GXChainService from '../services/GXChainService';
4 |
5 | let router = express.Router();
6 |
7 | router.get('/request/:request_id', function (req, res) {
8 | GXChainService.get_data_transaction_by_request_id(req.params.request_id).then(function (data_transaction) {
9 | res.send(data_transaction);
10 | }).catch(ex => {
11 | res.status(500).send(ex.message);
12 | });
13 | });
14 |
15 | router.get('/request/:request_id/data', function (req, res) {
16 | let request_id = req.params.request_id;
17 | // 默认最多返回20条
18 | LevelDBService.find({prefix: `request-${request_id}-`}).then(function (results) {
19 | results = (results || []).map((result) => {
20 | return JSON.parse(result.value);
21 | }).reverse();
22 | res.send(results);
23 | }).catch(ex => {
24 | res.status(404).send({
25 | request_id: request_id,
26 | message: '未查询到结果'
27 | });
28 | });
29 | });
30 |
31 | router.get('/request/:request_id/delete', function (req, res) {
32 | let request_id = req.params.request_id;
33 | LevelDBService.find({prefix: `request-${request_id}-`}).then(function (results) {
34 | (results || []).forEach((result) => {
35 | console.log(result);
36 | LevelDBService.del(result.key);
37 | });
38 | res.send({});
39 | }).catch(ex => {
40 | console.error(ex);
41 | res.status(500).send({
42 | request_id: request_id,
43 | message: '删除失败'
44 | });
45 | });
46 | });
47 |
48 | module.exports = router;
49 |
--------------------------------------------------------------------------------
/lib/routes/demo.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 |
3 | let router = express.Router();
4 |
5 | /**
6 | * 商户-数据回调示例
7 | */
8 | router.all('/callback', function (req, res) {
9 | let params = Object.assign({}, req.body);
10 | console.log('数据回调:');
11 | console.log(JSON.stringify(params, null, '\t'));
12 | res.send({});
13 | });
14 |
15 | /**
16 | * 数据源-服务接口示例
17 | */
18 | router.all('/call', function (req, res) {
19 | console.log(Object.assign({}, req.query, req.body));
20 | res.send({
21 | code: 0,
22 | data: {
23 | result: true
24 | }
25 | });
26 | });
27 |
28 | module.exports = router;
29 |
--------------------------------------------------------------------------------
/lib/services/DatasourceService.js:
--------------------------------------------------------------------------------
1 | import superagent from 'superagent';
2 | import Promise from 'bluebird';
3 | import ConfigStore from '../utils/ConfigStore';
4 |
5 | export default {
6 | fetch_data (params) {
7 | let config = ConfigStore.config;
8 | return new Promise((resolve, reject) => {
9 | superagent.post(config.datasource.service).send(params).end((err, resp) => {
10 | if (err) {
11 | console.error('获取数据失败', resp && resp.text);
12 | reject(new Error('offline'));
13 | } else {
14 | try {
15 | let result = JSON.parse(resp.text);
16 | resolve(result);
17 | } catch (ex) {
18 | console.error('返回数据格式错误:', ex);
19 | reject(new Error('invalid_format'));
20 | }
21 | }
22 | });
23 | });
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/lib/services/FaucetService.js:
--------------------------------------------------------------------------------
1 | import ConfigStore from '../utils/ConfigStore';
2 | import superagent from 'superagent';
3 | import Promise from 'bluebird';
4 | import GXChainService from './GXChainService';
5 |
6 | import {Signature} from 'gxbjs';
7 |
8 | const sortJSON = function (json) {
9 | var keys = Object.keys(json);
10 | keys.sort();
11 | var result = {};
12 | keys.forEach(function (k) {
13 | result[k] = json[k];
14 | });
15 | return result;
16 | };
17 |
18 | const sign = function (body = '', pKey) {
19 | if (!pKey) {
20 | pKey = ConfigStore.get_merchant_private_key();
21 | }
22 | return Signature.sign(body, pKey).toHex();
23 | };
24 |
25 | export default {
26 |
27 | /**
28 | * 商户|数据源方法 - 联盟成员请求数据交换授权令牌
29 | * @param name 姓名
30 | * @param idcard 身份证号
31 | * @param mobile 手机号
32 | * @param request_id 请求id
33 | */
34 | request_for_a_token (requester, league_id, request_id, account_name, private_key) {
35 | let params = {
36 | league_id,
37 | request_id,
38 | requester
39 | };
40 | let config = ConfigStore.config;
41 |
42 | return new Promise((resolve, reject) => {
43 | GXChainService.fetch_account(account_name).then((account) => {
44 | params.account_id = account.get('id');
45 | params.signature = sign(JSON.stringify(sortJSON(params)), private_key);
46 | superagent.post(`${config.common.faucet_url}/chain/auth_token`).send(params).end(function (err, resp) {
47 | if (err) {
48 | reject(new Error(resp.body && (resp.body.message || resp.body.base[0])));
49 | } else {
50 | resolve(resp.body);
51 | }
52 | });
53 | }).catch((err) => {
54 | reject(err);
55 | });
56 | });
57 | },
58 |
59 | /**
60 | * 商户方法 - 发送短信授权验证
61 | * @param name
62 | * @param idcard
63 | * @param mobile
64 | * @param request_id
65 | */
66 | send_auth_msg (name, idcard, mobile, request_id) {
67 | let params = {
68 | name,
69 | idcard,
70 | mobile,
71 | request_id: request_id
72 | };
73 | let config = ConfigStore.config;
74 |
75 | return new Promise((resolve, reject) => {
76 | GXChainService.fetch_account(config.merchant.account_name).then((account) => {
77 | params.account_id = account.get('id');
78 | params.signature = sign(JSON.stringify(sortJSON(params)));
79 | superagent.post(`${config.common.faucet_url}/chain/auth_msg`).send(params).end(function (err, resp) {
80 | if (err) {
81 | console.error('发送授权短信失败:', resp.body && (resp.body.message || resp.body.base[0]));
82 | reject(new Error(resp && resp.body ? resp.body.message : '网络故障'));
83 | } else {
84 | resolve(resp.body);
85 | }
86 | });
87 | }).catch((err) => {
88 | reject(err);
89 | });
90 | });
91 | },
92 |
93 | /**
94 | * 数据源方法 - 保存数据hash
95 | * @param hash
96 | */
97 | store_data: function (data, request_id, data_hash) {
98 | let params = {
99 | data,
100 | request_id,
101 | data_hash
102 | };
103 | let config = ConfigStore.config;
104 | let datasource_private_key = ConfigStore.get_datasource_private_key();
105 |
106 | return new Promise((resolve, reject) => {
107 | GXChainService.fetch_account(config.datasource.account_name).then((account) => {
108 | params.account_id = account.get('id');
109 | params.signature = sign(JSON.stringify(sortJSON(params)), datasource_private_key);
110 | superagent.post(`${config.common.faucet_url}/chain/store_data`).send(params).end(function (err, resp) {
111 | if (err) {
112 | console.error('保存数据hash失败:', resp.body && (resp.body.message || resp.body.base[0]));
113 | reject(new Error(resp && resp.body ? resp.body.message || resp.body.base[0] : '网络故障'));
114 | } else {
115 | resolve(resp.body);
116 | }
117 | });
118 | }).catch((err) => {
119 | reject(err);
120 | });
121 | });
122 | },
123 |
124 | /**
125 | * 商户方法-通过request_id获取hash
126 | * @param request_id
127 | */
128 | fetch_ipfs_hash (request_id, datasource) {
129 | let params = {
130 | request_id,
131 | datasource
132 | };
133 | let config = ConfigStore.config;
134 |
135 | return new Promise((resolve, reject) => {
136 | GXChainService.fetch_account(config.merchant.account_name).then((account) => {
137 | params.account_id = account.get('id');
138 | params.signature = sign(JSON.stringify(sortJSON(params)));
139 | superagent.get(`${config.common.faucet_url}/chain/get_hash`).query(params).end(function (err, resp) {
140 | if (err) {
141 | console.error('获取ipfs_hash失败:', err.body);
142 | reject(new Error(resp.body && (resp.body.message || resp.body.base[0])));
143 | } else {
144 | resolve(resp.body);
145 | }
146 | });
147 | }).catch((err) => {
148 | reject(err);
149 | });
150 | });
151 | }
152 | };
153 |
--------------------------------------------------------------------------------
/lib/services/GXChainService.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import {Apis} from 'gxbjs-ws';
3 | import {Aes, ChainStore, FetchChain, hash, TransactionBuilder, TransactionHelper} from 'gxbjs';
4 | import ConfigStore from '../utils/ConfigStore';
5 | import Immutable from 'immutable';
6 |
7 | /**
8 | * 获取账户信息
9 | * @param account_name
10 | * @returns {*}
11 | */
12 | let account_cache = {};
13 | const fetch_account = function (account_name) {
14 | return new Promise(function (resolve, reject) {
15 | if (account_cache[account_name]) {
16 | return resolve(account_cache[account_name]);
17 | }
18 | if (!account_name) {
19 | resolve();
20 | }
21 | return FetchChain('getAccount', account_name).then((account) => {
22 | account_cache[account_name] = account;
23 | account_cache[account.get('id')] = account;
24 | resolve(account);
25 | }).catch((ex) => {
26 | reject(ex);
27 | });
28 | });
29 | };
30 |
31 | /**
32 | * 根据账号获取该账号active public_key
33 | * @param account_id
34 | */
35 | const get_public_key_by_id = function (account_id) {
36 | return new Promise(function (resolve, reject) {
37 | fetch_account(account_id).then(function (account) {
38 | let pubKey = account.toJS().active.key_auths[0][0];
39 | resolve(pubKey);
40 | }).catch((ex) => {
41 | reject(ex);
42 | });
43 | });
44 | };
45 |
46 | /**
47 | * 获取产品信息
48 | * @param prod_id
49 | */
50 | const fetch_data_product = function (prod_id) {
51 | // let start = new Date();
52 | return new Promise(function (resolve, reject) {
53 | let prod = ChainStore.objects_by_id.get(prod_id);
54 | if (prod) {
55 | prod = prod.toJS();
56 | prod.schema_contexts = prod.schema_contexts.map(function (schema) {
57 | if (typeof schema.schema_context == 'string') {
58 | schema.schema_context = JSON.parse(schema.schema_context);
59 | }
60 | return schema;
61 | });
62 | resolve(prod);
63 | } else {
64 | return Apis.instance().db_api().exec('get_objects', [[prod_id]]).then(function (resp) {
65 | // console.log('获取产品信息耗时:',new Date()-start);
66 | if (!resp || resp.length == 0) {
67 | reject(new Error('product not found'));
68 | } else {
69 | let prod = Object.assign({schema_contexts: []}, resp[0]);
70 | prod.schema_contexts = prod.schema_contexts.map(function (schema) {
71 | if (typeof schema.schema_context == 'string') {
72 | schema.schema_context = JSON.parse(schema.schema_context);
73 | }
74 | return schema;
75 | });
76 | ChainStore.objects_by_id.set(prod_id, Immutable.fromJS(prod));
77 | resolve(prod);
78 | }
79 | }).catch(function (ex) {
80 | reject(ex);
81 | });
82 | }
83 | });
84 | };
85 |
86 | /**
87 | * 通过request_id获取data transaction
88 | * @param request_id
89 | */
90 | const get_data_transaction_by_request_id = function (request_id) {
91 | return new Promise(function (resolve, reject) {
92 | return Apis.instance().db_api().exec('get_data_transaction_by_request_id', [request_id]).then(function (resp) {
93 | resolve(resp);
94 | }).catch(function (ex) {
95 | reject(ex);
96 | });
97 | });
98 | };
99 |
100 | /**
101 | * 加密Json
102 | * @param params
103 | * @param private_key
104 | * @param public_key
105 | * @returns {Buffer}
106 | */
107 | const encrypt_params = function (params, private_key, public_key) {
108 | let msg = JSON.stringify(params, null, 0);
109 | return Aes.encrypt_with_checksum(private_key, public_key, null, new Buffer(msg).toString('base64')).toString('base64');
110 | };
111 |
112 | /**
113 | * 解密消息体
114 | * @param msg
115 | * @param private_key
116 | */
117 | const decrypt_msg = function (msg, private_key, public_key_string) {
118 | let base64Str = Aes.decrypt_with_checksum(private_key, public_key_string || private_key.toPublicKey().toPublicKeyString(), null, new Buffer(msg, 'base64')).toString('utf-8');
119 | // let base64Str = Aes.decrypt_with_checksum(private_key, public_key_string?public_key_string:private_key.toPublicKey().toPublicKeyString(), null, msg).toString("utf-8");
120 | return new Buffer(base64Str, 'base64').toString();
121 | };
122 |
123 | /**
124 | * 生成request_id
125 | */
126 | const generate_request_id = function () {
127 | let merchant_private_key = ConfigStore.get_merchant_private_key();
128 | let nonce = TransactionHelper.unique_nonce_uint64();
129 | let request_id = hash.sha256(merchant_private_key.toPublicKey().toPublicKeyString() + nonce).toString('hex');
130 | return request_id;
131 | };
132 |
133 | /**
134 | * 创建数据交易请求
135 | * @param league_id
136 | * @param prod_id
137 | * @param version
138 | * @param params
139 | * @param account_id
140 | * @param token
141 | */
142 | const create_data_transaction = function (request_id, league_id, prod_id, version, encrypted_params, account_id) {
143 | let merchant_private_key = ConfigStore.get_merchant_private_key();
144 | return new Promise(function (resolve, reject) {
145 | let tr = new TransactionBuilder();
146 |
147 | let base64Str = encrypted_params.toString('base64');
148 | // let start = new Date();
149 | let operation = {
150 | request_id: request_id,
151 | product_id: prod_id,
152 | version: version,
153 | params: base64Str,
154 | fee: {
155 | amount: 0,
156 | asset_id: '1.3.0'
157 | },
158 | requester: account_id,
159 | create_date_time: new Date().toISOString().split('.')[0]
160 | };
161 | if (league_id) {
162 | operation.league_id = league_id;
163 | }
164 | tr.add_type_operation('data_transaction_create', operation);
165 | // start = new Date();
166 | tr.set_required_fees().then(() => {
167 | // console.log('获取费用耗时',new Date()-start);
168 | tr.add_signer(merchant_private_key);
169 | // start = new Date();
170 | tr.broadcast(function (result) {
171 | // console.log('广播耗时:',new Date()-start);
172 | resolve(request_id);
173 | }).catch((ex) => {
174 | console.error('transaction create broadcast:', ex);
175 | reject(ex);
176 | });
177 | }).catch(function (ex) {
178 | reject(ex);
179 | });
180 | });
181 | };
182 |
183 | /**
184 | * 支付数据交易
185 | * @param request_id
186 | * @param from
187 | * @param to
188 | * @param amount
189 | */
190 | const pay_data_transaction = function (request_id, from, to, amount) {
191 | let merchant_private_key = ConfigStore.get_merchant_private_key();
192 | return new Promise(function (resolve, reject) {
193 | let tr = new TransactionBuilder();
194 |
195 | tr.add_type_operation('data_transaction_pay', {
196 | request_id: request_id,
197 | from: from,
198 | to: to,
199 | amount: {
200 | amount: amount,
201 | asset_id: '1.3.0'
202 | },
203 | fee: {
204 | amount: 0,
205 | asset_id: '1.3.0'
206 | }
207 | });
208 |
209 | tr.set_required_fees().then(() => {
210 | tr.add_signer(merchant_private_key);
211 | tr.broadcast(function (result) {
212 | resolve(result);
213 | }).catch((ex) => {
214 | console.error('transaction pay broadcast:', ex);
215 | reject(ex);
216 | });
217 | }, (ex) => {
218 | console.error('transaction pay set required fees:', ex);
219 | reject(ex);
220 | });
221 | });
222 | };
223 |
224 | /**
225 | * 数据返回错误
226 | * @param request_id
227 | * @param datasource
228 | */
229 | const data_transaction_datasource_validate_error = function (request_id, datasource) {
230 | let datasource_private_key = ConfigStore.get_datasource_private_key();
231 | return new Promise(function (resolve, reject) {
232 | let tr = new TransactionBuilder();
233 | tr.add_type_operation('data_transaction_datasource_validate_error', {
234 | request_id: request_id,
235 | datasource: datasource,
236 | fee: {
237 | amount: 0,
238 | asset_id: '1.3.0'
239 | }
240 | });
241 |
242 | tr.set_required_fees().then(() => {
243 | tr.add_signer(datasource_private_key);
244 | tr.broadcast(function (result) {
245 | resolve(result);
246 | }).catch((ex) => {
247 | reject(ex);
248 | });
249 | }, (ex) => {
250 | console.error('transaction datasource validate error set required fees:', ex);
251 | reject(ex);
252 | });
253 | });
254 | };
255 |
256 | /**
257 | * 获取联盟信息
258 | * @param prod_id
259 | */
260 | const fetch_league = function (league_id) {
261 | return new Promise(function (resolve, reject) {
262 | return Apis.instance().db_api().exec('get_objects', [[league_id]]).then(function (resp) {
263 | if (!resp || resp.length == 0) {
264 | reject(new Error('league not found'));
265 | } else {
266 | resolve(resp[0]);
267 | }
268 | }).catch(function (ex) {
269 | reject(ex);
270 | });
271 | });
272 | };
273 |
274 | export default {
275 | fetch_league,
276 | fetch_account,
277 | get_public_key_by_id,
278 | fetch_data_product,
279 | encrypt_params,
280 | decrypt_msg,
281 | generate_request_id,
282 | create_data_transaction,
283 | pay_data_transaction,
284 | get_data_transaction_by_request_id,
285 | data_transaction_datasource_validate_error
286 | };
287 |
--------------------------------------------------------------------------------
/lib/services/IPFSService.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import ipfsAPI from 'ipfs-api';
3 |
4 | export default {
5 | /**
6 | * 上传数据到ipfs
7 | * @param data
8 | */
9 | upload: function (data, addrs) {
10 | if (!(addrs instanceof Array)) {
11 | addrs = [addrs];
12 | }
13 | return new Promise(function (resolve, reject) {
14 | const inner = (d, index) => {
15 | let ipfs_api = ipfsAPI(addrs[index]);
16 | let obj = {
17 | Data: new Buffer(d),
18 | Links: []
19 | };
20 | ipfs_api.object.put(obj, function (err, node) {
21 | if (err) {
22 | reject(err);
23 | } else {
24 | let nodeJSON = node.toJSON();
25 | resolve(nodeJSON.multihash);
26 | }
27 | });
28 | };
29 | inner(data, 0);
30 | });
31 | },
32 | /**
33 | * 通过hash从ipfs下载数据
34 | * @param hash
35 | */
36 | download: function (hash, addrs) {
37 | if (!(addrs instanceof Array)) {
38 | addrs = [addrs];
39 | }
40 | console.log('downloading:', hash, addrs);
41 | return new Promise(function (resolve, reject) {
42 | const inner = (h, index) => {
43 | let ipfs_api = ipfsAPI(addrs[index]);
44 | ipfs_api.object.data(h, function (err, data) {
45 | if (err) {
46 | console.error('download failed:', err);
47 | if (index == addrs.length - 1) {
48 | reject(err);
49 | } else {
50 | inner(h, index + 1);
51 | }
52 | } else {
53 | console.log('downloaded', data);
54 | resolve(data.toString());
55 | }
56 | });
57 | };
58 | inner(hash, 0);
59 | });
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/lib/services/LevelDBService.js:
--------------------------------------------------------------------------------
1 | import level from 'level';
2 | import Promise from 'bluebird';
3 | import async from 'async';
4 |
5 | let db = level('./.data');
6 |
7 | let tmp = {};
8 |
9 | export default {
10 |
11 | put (key, val) {
12 | tmp[key] = val;
13 | return new Promise(function (resolve, reject) {
14 | let keys = Object.keys(tmp);
15 | async.map(keys, function (k, cb) {
16 | db.put(k, tmp[k], function (err) {
17 | if (err) {
18 | cb(err);
19 | } else {
20 | delete tmp[k];
21 | cb(null, k);
22 | }
23 | });
24 | }, function (err) {
25 | if (err) {
26 | reject(err);
27 | } else {
28 | resolve(1);
29 | }
30 | });
31 | });
32 | },
33 |
34 | get (key) {
35 | return new Promise(function (resolve, reject) {
36 | db.get(key, function (err, result) {
37 | if (err) {
38 | reject(err);
39 | } else {
40 | resolve(result);
41 | }
42 | });
43 | });
44 | },
45 |
46 | del (key) {
47 | return new Promise(function (resolve, reject) {
48 | db.del(key, function (err) {
49 | if (err) {
50 | reject(err);
51 | } else {
52 | delete tmp[key];
53 | resolve(true);
54 | }
55 | });
56 | });
57 | },
58 |
59 | find (options) {
60 | return new Promise((resolve, reject) => {
61 | options = Object.assign({keys: true, values: true, limit: 20, fillCache: true}, options);
62 | if (options.prefix) {
63 | options.start = options.prefix;
64 | options.end = options.prefix.substring(0, options.prefix.length - 1) + String.fromCharCode(options.prefix[options.prefix.length - 1].charCodeAt() + 1);
65 | }
66 |
67 | let results = [];
68 | db.createReadStream(options).on('data', function (data) {
69 | results.push(data);
70 | }).on('error', function (err) {
71 | console.log('leveldb find error', err);
72 | return reject([]);
73 | }).on('close', function () {
74 | }).on('end', function () {
75 | return resolve(results);
76 | });
77 | });
78 | }
79 | };
80 |
--------------------------------------------------------------------------------
/lib/services/MerchantService.js:
--------------------------------------------------------------------------------
1 | import superagent from 'superagent';
2 | import ConfigStore from '../utils/ConfigStore';
3 | import MerchantTask from '../tasks/MerchantTask';
4 | import LevelDBService from '../services/LevelDBService';
5 | import {SYSTEM_ERROR_CODE} from '../utils/constants';
6 |
7 | export default {
8 | notify: function (params) {
9 | let config = ConfigStore.config;
10 | let isTimeout = params.body && params.body.code == SYSTEM_ERROR_CODE.DATASOURCE_OFFLINE;
11 | if (isTimeout) {
12 | MerchantTask.dequeue(params.request_id);
13 | }
14 | LevelDBService.put(`request-${params.request_id}-${new Date().getTime()}`, JSON.stringify(params));
15 | if (config.merchant.callback_url) {
16 | superagent.post(config.merchant.callback_url).send(params).end((err, resp) => {
17 | if (err) {
18 | console.error(err);
19 | }
20 | console.log('callback已提交');
21 | // MerchantTask.dequeue(params.request_id);
22 | });
23 | }
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/lib/services/TimeoutService.js:
--------------------------------------------------------------------------------
1 | import MerchantService from './MerchantService';
2 | import {SYSTEM_ERROR_CODE} from '../utils/constants';
3 |
4 | let timeoutCollection = {};
5 | export default {
6 | /**
7 | * 添加超时通知任务
8 | * @param request_id
9 | * @param timeout
10 | */
11 | add (request_id, timeout) {
12 | if (!timeoutCollection[request_id]) {
13 | timeoutCollection[request_id] = setTimeout(function () {
14 | MerchantService.notify({
15 | request_id,
16 | body: {
17 | code: SYSTEM_ERROR_CODE.DATASOURCE_OFFLINE,
18 | message: `${timeout / 1000}秒内无响应,可能原因:\n1. 数据源离线了\n2. 涉及个人隐私数据交易未得到本人授权`
19 | }
20 | });
21 | }, timeout);
22 | }
23 | },
24 |
25 | /**
26 | * 取消超时通知任务
27 | * @param request_id
28 | */
29 | remove (request_id) {
30 | if (timeoutCollection[request_id]) {
31 | clearTimeout(timeoutCollection[request_id]);
32 | delete timeoutCollection[request_id];
33 | }
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/lib/utils/ConfigStore.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import {PrivateKey} from 'gxbjs';
3 | import fs from 'fs';
4 | import path from 'path';
5 |
6 | export default {
7 | init () {
8 | let self = this;
9 | return new Promise((resolve, reject) => {
10 | try {
11 | self.config = {};
12 | let configPath = path.resolve(process.cwd(), './config/config.json');
13 | if (!fs.existsSync(configPath)) {
14 | console.log('不存在');
15 | configPath = path.resolve(process.cwd(), './dist/config/config.json');
16 | }
17 | self.config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
18 | resolve(self.config);
19 | } catch (ex) {
20 | reject(ex);
21 | }
22 | });
23 | },
24 | get_merchant_private_key () {
25 | let config = this.config;
26 | return config.merchant && config.merchant.private_key ? PrivateKey.fromWif(config.merchant.private_key) : '';
27 | },
28 | get_datasource_private_key () {
29 | let config = this.config;
30 | return config.datasource && config.datasource.private_key ? PrivateKey.fromWif(config.datasource.private_key) : '';
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/lib/utils/constants.js:
--------------------------------------------------------------------------------
1 | // 数据交易状态
2 | const TRANSACTION_STATUS_MAP = {
3 | '0': 'INITIAL',
4 | INITIAL: 'INITIAL',
5 | '1': 'CONFIRMED',
6 | CONFIRMED: 'CONFIRMED',
7 | '99': 'PRIVACY_REJECTED',
8 | PRIVACY_REJECTED: 'PRIVACY_REJECTED'
9 | };
10 | // 数据源状态
11 | const DATA_SOURCE_STATUS_MAP = {
12 | '0': 'INITIAL',
13 | INITIAL: 'INITIAL',
14 | '1': 'UPLOADED',
15 | UPLOADED: 'UPLOADED',
16 | '2': 'PAYED',
17 | PAYED: 'PAYED',
18 | '3': 'NO_DATA',
19 | NO_DATA: 'NO_DATA',
20 | '99': 'VALIDATE_FAIL',
21 | VALIDATE_FAIL: 'VALIDATE_FAIL'
22 | };
23 | // 支付状态
24 | const PAY_STATUS = {
25 | NOT_PAYED: 'NOT_PAYED',
26 | PAYING: 'PAYING',
27 | PAYED: 'PAYED',
28 | PAY_FAILED: 'PAY_FAILED'
29 | };
30 |
31 | // 下载状态
32 | const DOWNLOAD_STATUS = {
33 | NOT_DOWNLOADED: 'NOT_DOWNLOADED',
34 | DOWNLOADING: 'DOWNLOADING',
35 | DOWNLOADED: 'DOWNLOADED',
36 | DOWNLOAD_FAILED: 'DOWNLOAD_FAILED'
37 | };
38 |
39 | // 系统错误
40 | const SYSTEM_ERROR_CODE = {
41 | NOT_FOUND: 'NOT_FOUND',
42 | INVALID_PARAMS: 'INVALID_PARAMS',
43 | DATASOURCE_OFFLINE: 'DATASOURCE_OFFLINE',
44 | FORBIDDEN: 'FORBIDDEN',
45 | BALANCE_NOT_ENOUGH: 'BALANCE_NOT_ENOUGH',
46 | PRIVACY_REJECTED: 'PRIVACY_REJECTED',
47 | UNKNOWN_ERROR: 'UNKNOWN_ERROR'
48 | };
49 |
50 | export {
51 | TRANSACTION_STATUS_MAP,
52 | DATA_SOURCE_STATUS_MAP,
53 | PAY_STATUS,
54 | DOWNLOAD_STATUS,
55 | SYSTEM_ERROR_CODE
56 | };
57 |
--------------------------------------------------------------------------------
/lib/utils/validator.js:
--------------------------------------------------------------------------------
1 | export default {
2 |
3 | validate (params, schema) {
4 | let self = this;
5 | if (typeof params != 'object') {
6 | throw new Error(`params ${params} should be an object`);
7 | }
8 |
9 | if (typeof schema != 'object') {
10 | throw new Error(`schema ${schema} should be an object`);
11 | }
12 |
13 | let result = {};
14 |
15 | for (var key in schema) {
16 | let type = schema[key].type;
17 | let value = params[key];
18 | let defaultValue = schema[key].defaultsTo;
19 |
20 | // assign a default value if the value is not assigned
21 | if (typeof value == 'undefined' && typeof defaultValue != 'undefined') {
22 | value = defaultValue;
23 | result[key] = value;
24 | }
25 |
26 | // throw an error if value is required but not assigned
27 | if (typeof value == 'undefined' && schema[key].required) {
28 | throw new Error(`${key} in ${JSON.stringify(params, null, '\t')} is required`);
29 | }
30 |
31 | switch (type) {
32 | case 'integer':
33 | if (!/^\d+$/.test(value)) {
34 | throw new Error(`${key} should be a type of ${type}, but get a value of ${value}`);
35 | } else {
36 | result[key] = value;
37 | }
38 | break;
39 | case 'string':
40 | case 'boolean':
41 | case 'number':
42 | if (typeof value != type) {
43 | throw new Error(`In case of schema definition:${JSON.stringify(schema, null, '\t')},${key} in ${JSON.stringify(params, null, '\t')} should be a type of ${type}, but get a value of ${value} which indicated as type of ${typeof value}`);
44 | } else {
45 | result[key] = value;
46 | }
47 | break;
48 | case 'json':
49 | if (!(value instanceof Object)) {
50 | throw new Error(`${key} should be json, but get a value of ${value} which indicated as instance of ${value.constructor.name}`);
51 | }
52 | result[key] = self.validate(value, schema.fields || {});
53 | break;
54 | case 'array':
55 | if (!(value instanceof Array)) {
56 | throw new Error(`${key} should be an instance of array, but get a value of ${value} which indicated as instance of ${value.constructor.name}`);
57 | } else if (!schema[key].columns) {
58 | throw new Error(`columns definition should be assigned for ${key} since schema ${schema}[${key}] is type of array`);
59 | } else if (typeof schema[key].columns != 'object') {
60 | throw new Error(`${key} should be an instance of array, but get a value of ${value} which indicated as instance of ${value.constructor.name}`);
61 | } else {
62 | result[key] = [];
63 | value = value.forEach(function (item) {
64 | result[key].push(self.validate(item, schema[key].columns));
65 | });
66 | }
67 | break;
68 | case 'raw':
69 | if (!(value instanceof Object)) {
70 | throw new Error(`${key} should be json, but get a value of ${value} which indicated as instance of ${value.constructor.name}`);
71 | }
72 | result[key] = value;
73 | break;
74 | default:
75 | console.warn(`unknown type ${type} found in ${JSON.stringify(params, null, '\t')}, which is not supported at this time, will be ignored`);
76 |
77 | }
78 | }
79 | return result;
80 | }
81 | };
82 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gxb-box",
3 | "version": "1.0.1",
4 | "description": "gxchain box",
5 | "main": "index.js",
6 | "scripts": {
7 | "preinstall": "npm install pm2 -g",
8 | "start":"npm run gui:dev",
9 | "dev":"npm run gui:dev",
10 | "gui:dev": "npm run build && NODE_ENV=development nodemon server/index.js --exec babel-node --watch server",
11 | "box:dev": "nodemon lib/index.js --exec babel-node",
12 | "clear": "rm -rf dist/box",
13 | "prebuild": "npm run clear",
14 | "build": "webpack --progress --hide-modules --config build/webpack.box.config.js",
15 | "build:gui": "webpack --progress --hide-modules --config build/webpack.gui-server.config.js && prod=1 node build/build.js",
16 | "box": "pm2 start ./box/gxb-box.js --name gxb-box",
17 | "gui": "NODE_ENV=production pm2 start ./gui-server/index.js --name gxb-gui"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/gxchain/gxb-box.git"
22 | },
23 | "engines": {
24 | "node": ">= 6.0.0"
25 | },
26 | "keywords": [
27 | "gxchain",
28 | "box"
29 | ],
30 | "author": "",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/gxchain/gxb-box/issues"
34 | },
35 | "homepage": "https://github.com/gxchain/gxb-box#readme",
36 | "dependencies": {
37 | "archiver": "^2.0.0",
38 | "async": "^2.5.0",
39 | "axios": "^0.15.3",
40 | "bluebird": "^3.5.0",
41 | "body-parser": "~1.13.2",
42 | "colors": "^1.1.2",
43 | "connect-history-api-fallback": "^1.3.0",
44 | "debug": "~2.2.0",
45 | "express": "~4.13.1",
46 | "figlet": "^1.2.0",
47 | "gxbjs": "^1.2.59",
48 | "gxbjs-ws": "^1.1.17",
49 | "immutable": "^3.8.1",
50 | "ipfs-api": "^14.0.4",
51 | "iview": "^2.0.0-rc.13",
52 | "jdenticon": "^1.7.2",
53 | "js-sha256": "^0.7.1",
54 | "level": "^1.7.0",
55 | "morgan": "~1.6.1",
56 | "pm2": "^2.7.1",
57 | "qs": "^6.5.1",
58 | "require-ensure": "^1.0.2",
59 | "socket.io": "^2.0.4",
60 | "socket.io-client": "^2.0.4",
61 | "superagent": "^3.5.2",
62 | "vue": "^2.4.4",
63 | "vue-router": "^2.7.0",
64 | "vue-timeago": "^3.3.6",
65 | "vue-websocket": "^0.2.2",
66 | "vuex": "^2.2.1"
67 | },
68 | "devDependencies": {
69 | "@types/express": "^4.0.39",
70 | "autoprefixer": "^7.1.2",
71 | "babel-cli": "^6.24.1",
72 | "babel-core": "^6.22.1",
73 | "babel-eslint": "^7.1.1",
74 | "babel-loader": "^7.1.1",
75 | "babel-plugin-transform-runtime": "^6.22.0",
76 | "babel-preset-env": "^1.3.2",
77 | "babel-preset-stage-2": "^6.22.0",
78 | "babel-register": "^6.22.0",
79 | "chalk": "^2.0.1",
80 | "copy-webpack-plugin": "^4.0.1",
81 | "css-loader": "^0.28.0",
82 | "cssnano": "^3.10.0",
83 | "eslint": "^3.19.0",
84 | "eslint-config-standard": "^6.2.1",
85 | "eslint-friendly-formatter": "^3.0.0",
86 | "eslint-loader": "^1.7.1",
87 | "eslint-plugin-html": "^3.0.0",
88 | "eslint-plugin-promise": "^3.4.0",
89 | "eslint-plugin-standard": "^2.0.1",
90 | "eslint-plugin-vue": "^2.1.0",
91 | "eventsource-polyfill": "^0.9.6",
92 | "extract-text-webpack-plugin": "^2.0.0",
93 | "file-loader": "^0.11.1",
94 | "friendly-errors-webpack-plugin": "^1.1.3",
95 | "html-webpack-plugin": "^2.28.0",
96 | "nodemon": "^1.11.0",
97 | "opn": "^5.1.0",
98 | "optimize-css-assets-webpack-plugin": "^2.0.0",
99 | "ora": "^1.2.0",
100 | "rimraf": "^2.6.0",
101 | "semver": "^5.3.0",
102 | "shelljs": "^0.7.6",
103 | "url-loader": "^0.5.8",
104 | "vue-loader": "^13.0.5",
105 | "vue-style-loader": "^3.0.3",
106 | "vue-template-compiler": "^2.4.4",
107 | "webpack": "^2.6.1",
108 | "webpack-bundle-analyzer": "^2.2.1",
109 | "webpack-dev-middleware": "^1.10.0",
110 | "webpack-hot-middleware": "^2.18.0",
111 | "webpack-merge": "^4.1.0"
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/script/start.cmd:
--------------------------------------------------------------------------------
1 | npm install -production
2 | pm2 stop gxb-box
3 | pm2 start ./box/gxb-box.js --name gxb-box
4 |
--------------------------------------------------------------------------------
/script/start.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | npm install -production
3 | pm2 stop gxb-box
4 | pm2 start ./box/gxb-box.js --name gxb-box
5 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import io from 'socket.io';
3 | import logger from 'morgan';
4 | import bodyParser from 'body-parser';
5 | import http from 'http';
6 | import path from 'path';
7 | import fs from 'fs';
8 | import ConnectService from './services/ConnectService';
9 | import gui_config from '../config';
10 |
11 | require('debug')('gxb-box:server');
12 | let app = express();
13 | let devMiddleware = null;
14 | let hotMiddleware = null;
15 | let autoOpenBrowser = gui_config.dev.autoOpenBrowser;
16 |
17 | app.use(require('connect-history-api-fallback')({
18 | index: '/',
19 | rewrites: [
20 | {
21 | from: '/^abc$/',
22 | to: '/'
23 | },
24 | {
25 | from: '/api/*',
26 | to: function (options) {
27 | return options.parsedUrl.href;
28 | }
29 | }
30 | ]
31 | }));
32 |
33 | if (app.get('env') === 'development') {
34 | let webpackConfig = require('../build/webpack.dev.conf');
35 | let compiler = require('webpack')(webpackConfig);
36 |
37 | devMiddleware = require('webpack-dev-middleware')(compiler, {
38 | publicPath: webpackConfig.output.publicPath,
39 | quiet: true
40 | });
41 | hotMiddleware = require('webpack-hot-middleware')(compiler, {
42 | log: console.log,
43 | heartbeat: 2000
44 | });
45 | compiler.plugin('compilation', function (compilation) {
46 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
47 | hotMiddleware.publish({action: 'reload'});
48 | cb();
49 | });
50 | });
51 | app.use(logger('dev'));
52 | app.use(devMiddleware);
53 | app.use(hotMiddleware);
54 |
55 | let staticPath = path.posix.join(gui_config.dev.assetsPublicPath, gui_config.dev.assetsSubDirectory);
56 | app.use(staticPath, express.static('./static'));
57 | } else {
58 | app.use(logger('combined'));
59 | app.use(express.static('./gui'));
60 | }
61 |
62 | app.use(bodyParser.json({limit: '20mb'}));
63 | app.use(bodyParser.urlencoded({
64 | extended: false,
65 | limit: '20mb'
66 | }));
67 |
68 | const connectedCheck = function (req, res, next) {
69 | let witnesses = req.query.env === 'production' ? gui_config.build.witnesses : gui_config.dev.witnesses;
70 | ConnectService.connect(witnesses, false, function (connected) {
71 | if (connected) {
72 | next();
73 | } else {
74 | res.status(500).send({message: '正在初始化数据,请稍后再试'});
75 | }
76 | });
77 | };
78 |
79 | app.use('/api', connectedCheck, require('./routes/api'));
80 |
81 | app.use(function (err, req, res, next) {
82 | res.status(err.status || 500);
83 | res.send({
84 | message: err.message,
85 | error: {}
86 | });
87 | });
88 |
89 | /**
90 | * 启动web服务
91 | */
92 | let serverStarted = false;
93 | let port = parseInt(process.env.port || '3031');
94 | let startServer = function () {
95 | if (serverStarted) {
96 | return;
97 | }
98 | serverStarted = true;
99 | app.set('port', port);
100 | let server = http.createServer(app);
101 | let websocket = io(server);
102 | websocket.on('connection', function (socket) {
103 | socket.on('message', function (type, data) {
104 | websocket.emit('message', type, data);
105 | });
106 | socket.on('system', function (data) {
107 | websocket.emit('message', data);
108 | });
109 | });
110 | server.listen(port);
111 | server.on('error', onError);
112 | server.on('listening', () => {
113 | devMiddleware && devMiddleware.waitUntilValid(() => {
114 | let uri = `http://localhost:${port}`;
115 | let opn = require('opn');
116 | if (app.get('env') === 'development' && autoOpenBrowser) {
117 | opn(uri);
118 | }
119 | });
120 | ConnectService.get_ip_address().then((local_ip) => {
121 | console.log('公信宝数据盒子配置系统已启动');
122 | console.log('> 请使用浏览器访问:' + 'http://' + local_ip + ':' + port);
123 | });
124 | });
125 | };
126 |
127 | const mkdir = function (dirpath, dirname) {
128 | if (typeof dirname === 'undefined') {
129 | if (fs.existsSync(dirpath)) {
130 | return;
131 | } else {
132 | mkdir(dirpath, path.dirname(dirpath));
133 | }
134 | } else {
135 | if (dirname !== path.dirname(dirpath)) {
136 | mkdir(dirpath);
137 | return;
138 | }
139 | if (fs.existsSync(dirname)) {
140 | fs.mkdirSync(dirpath);
141 | } else {
142 | mkdir(dirname, path.dirname(dirname));
143 | fs.mkdirSync(dirpath);
144 | }
145 | }
146 | };
147 |
148 | /**
149 | * 初始化连接
150 | */
151 | let initConnection = function () {
152 | console.log('检查配置文件...');
153 | // 检查配置文件
154 | mkdir(path.resolve(process.cwd(), './dist/config'));
155 | let config_path = path.resolve(process.cwd(), './dist/config/config.json');
156 | fs.exists(config_path, function (exists) {
157 | if (exists) {
158 | startServer();
159 | } else {
160 | try {
161 | let _config = {};
162 | fs.writeFileSync(config_path, JSON.stringify(_config));
163 | startServer();
164 | } catch (ex) {
165 | console.error('获取配置信息失败,请检查:\n 请确认配置文件以及读写权限 \n', ex);
166 | }
167 | }
168 | });
169 | };
170 |
171 | /**
172 | * 首次连接
173 | */
174 | initConnection();
175 |
176 | /**
177 | * Event listener for HTTP server "error" event.
178 | */
179 | function onError (error) {
180 | if (error.syscall !== 'listen') {
181 | throw error;
182 | }
183 |
184 | let bind = typeof port === 'string'
185 | ? 'Pipe ' + port
186 | : 'Port ' + port;
187 |
188 | // handle specific listen errors with friendly messages
189 | switch (error.code) {
190 | case 'EACCES':
191 | console.error(bind + ' requires elevated privileges');
192 | process.exit(1);
193 | break;
194 | case 'EADDRINUSE':
195 | console.error(bind + ' is already in use');
196 | process.exit(1);
197 | break;
198 | default:
199 | throw error;
200 | }
201 | }
202 |
203 | process.stdin.resume();
204 |
205 | function exitHandler (reason, err) {
206 | if (err) console.log(err.stack);
207 | console.log('程序退出:', reason);
208 | process.exit();
209 | }
210 |
211 | // do something when app is closing
212 | process.on('exit', exitHandler.bind(null, 'exit'));
213 |
214 | // catches ctrl+c event
215 | process.on('SIGINT', exitHandler.bind(null, 'SIGINT'));
216 |
217 | // catches uncaught exceptions
218 | process.on('uncaughtException', exitHandler.bind(null, 'uncaughtException'));
219 |
--------------------------------------------------------------------------------
/server/routes/api.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import BoxService from '../services/BoxService';
3 | import AccountService from '../services/AccountService';
4 | import DataService from '../services/DataService';
5 | import ConfigStore from '../services/ConfigStore';
6 | import {get_box_prod_zip} from '../services/ZipArchive';
7 | import ConnectService from '../services/ConnectService';
8 | import fs from 'fs';
9 |
10 | let router = express.Router();
11 |
12 | /**
13 | * 读取配置文件
14 | */
15 |
16 | router.get('/fetch_config', function (req, res) {
17 | ConfigStore.init().then((config) => {
18 | res.send(config);
19 | }).catch((err) => {
20 | console.error(err);
21 | res.status(400).send(err);
22 | });
23 | });
24 |
25 | /**
26 | * 写入配置文件
27 | */
28 |
29 | router.post('/save_config', function (req, res) {
30 | ConfigStore.set(req.body.config).then((result) => {
31 | res.send(result);
32 | }).catch((err) => {
33 | console.error(err);
34 | res.status(400).send(err);
35 | });
36 | });
37 |
38 | /**
39 | * 切换配置环境
40 | */
41 |
42 | router.post('/change_config_env', function (req, res) {
43 | ConfigStore.change_config_env(req.query.env, req.body.config).then((result) => {
44 | res.send(result);
45 | }).catch((err) => {
46 | console.error(err);
47 | res.status(400).send(err);
48 | });
49 | });
50 |
51 | /**
52 | * 获取本机IP
53 | */
54 |
55 | router.get('/get_ip_address', function (req, res) {
56 | ConnectService.get_ip_address().then((box_ip) => {
57 | res.send(box_ip);
58 | }).catch((err) => {
59 | console.error(err);
60 | res.status(400).send(err);
61 | });
62 | });
63 |
64 | /**
65 | * 账户信息
66 | */
67 |
68 | router.get('/fetch_account/:account_id_or_name', function (req, res) {
69 | AccountService.fetch_account(req.params.account_id_or_name).then((account) => {
70 | res.send(account);
71 | }).catch(err => {
72 | console.error(err);
73 | res.status(400).send(err);
74 | });
75 | });
76 |
77 | /**
78 | * 创建账号
79 | */
80 |
81 | router.post('/create_account', function (req, res) {
82 | AccountService.create_account(req.query.env, req.body.type, req.body.name, req.protocol).then((account) => {
83 | res.send(account);
84 | }).catch((err) => {
85 | console.error(err);
86 | res.status(400).send(err);
87 | });
88 | });
89 |
90 | /**
91 | * 导入账号
92 | */
93 |
94 | router.post('/import_account', function (req, res) {
95 | AccountService.import_account(req.body.type, req.body.private_key).then((account) => {
96 | res.send(account);
97 | }).catch((err) => {
98 | console.error(err);
99 | res.status(400).send(err);
100 | });
101 | });
102 |
103 | /**
104 | * 申请认证商户
105 | */
106 |
107 | router.post('/apply_merchant', function (req, res) {
108 | AccountService.apply_merchant(req.query.env, req.body.apply_info, req.body.account_name, req.body.account_type, req.protocol).then((result) => {
109 | res.send(result);
110 | }).catch((err) => {
111 | console.error(err);
112 | res.status(400).send(err);
113 | });
114 | });
115 |
116 | /**
117 | * 申请认证数据源
118 | */
119 |
120 | router.post('/apply_datasource', function (req, res) {
121 | AccountService.apply_datasource(req.query.env, req.body.apply_info, req.body.account_name, req.body.account_type, req.protocol).then((result) => {
122 | res.send(result);
123 | }).catch((err) => {
124 | console.error(err);
125 | res.status(400).send(err);
126 | });
127 | });
128 |
129 | /**
130 | * 查询认证状态
131 | */
132 |
133 | router.get('/is_applying/:account_name', function (req, res) {
134 | AccountService.is_applying(req.query.env, req.params.account_name, req.protocol).then((result) => {
135 | res.send(result);
136 | }).catch((err) => {
137 | console.error(err);
138 | res.status(400).send(err);
139 | });
140 | });
141 |
142 | /**
143 | * 获取认证商户信息
144 | */
145 |
146 | router.get('/fetch_merchant/:account_name/:account_type', function (req, res) {
147 | AccountService.fetch_merchant(req.query.env, req.params.account_name, req.params.account_type, req.protocol).then((result) => {
148 | res.send(result);
149 | }).catch((err) => {
150 | console.error(err);
151 | res.status(400).send(err);
152 | });
153 | });
154 |
155 | /**
156 | * 获取数据市场二级栏目
157 | */
158 |
159 | router.get('/fetch_data_market_categories/:data_market_type', function (req, res) {
160 | DataService.fetch_data_market_categories(req.params.data_market_type).then((result) => {
161 | res.send(result);
162 | }).catch((err) => {
163 | console.error(err);
164 | res.status(400).send(err);
165 | });
166 | });
167 |
168 | /**
169 | * 获取栏目信息
170 | */
171 |
172 | router.get('/fetch_data_market_categories_info/:category_id', function (req, res) {
173 | DataService.fetch_data_market_categories_info(req.params.category_id).then((result) => {
174 | res.send(result);
175 | }).catch((err) => {
176 | console.error(err);
177 | res.status(400).send(err);
178 | });
179 | });
180 |
181 | /**
182 | * 获取自由市场产品列表
183 | */
184 |
185 | router.get('/fetch_free_data_products/:category_id/:page/:pageSize', function (req, res) {
186 | DataService.fetch_free_data_products(req.params.category_id, req.params.page, req.params.pageSize, req.params.keywords || '').then((result) => {
187 | res.send(result);
188 | }).catch((err) => {
189 | console.error(err);
190 | res.status(400).send(err);
191 | });
192 | });
193 |
194 | /**
195 | * 获取自由市场产品详情
196 | */
197 |
198 | router.get('/fetch_free_data_product_details/:product_id', function (req, res) {
199 | DataService.fetch_free_data_product_details(req.params.product_id).then((result) => {
200 | res.send(result);
201 | }).catch((err) => {
202 | console.error(err);
203 | res.status(400).send(err);
204 | });
205 | });
206 |
207 | /**
208 | * 获取联盟市场联盟列表
209 | */
210 |
211 | router.get('/fetch_league_list/:category_id/:page/:pageSize', function (req, res) {
212 | DataService.fetch_league_list(req.params.category_id, req.params.page, req.params.pageSize, req.params.keywords || '').then((result) => {
213 | res.send(result);
214 | }).catch((err) => {
215 | console.error(err);
216 | res.status(400).send(err);
217 | });
218 | });
219 |
220 | /**
221 | * 获取联盟信息
222 | */
223 |
224 | router.get('/fetch_league_info/:league_id', function (req, res) {
225 | DataService.fetch_league_info(req.params.league_id).then((result) => {
226 | res.send(result);
227 | }).catch((err) => {
228 | console.error(err);
229 | res.status(400).send(err);
230 | });
231 | });
232 |
233 | /**
234 | * 获取联盟成员列表
235 | */
236 |
237 | router.get('/fetch_league_members/:league_id', function (req, res) {
238 | AccountService.fetch_league_members(req.query.env, req.params.league_id, req.protocol).then((members) => {
239 | res.send(members);
240 | }).catch(err => {
241 | console.error(err);
242 | res.status(400).send(err);
243 | });
244 | });
245 |
246 | /**
247 | * 获取联盟产品列表
248 | */
249 |
250 | router.get('/fetch_league_data_products/:data_product_ids', function (req, res) {
251 | DataService.fetch_league_data_products(req.params.data_product_ids).then((result) => {
252 | res.send(result);
253 | }).catch((err) => {
254 | console.error(err);
255 | res.status(400).send(err);
256 | });
257 | });
258 |
259 | /**
260 | * 数据盒子服务 - 启动
261 | */
262 |
263 | router.get('/box_start', function (req, res) {
264 | BoxService.box_start().then((pm2) => {
265 | res.send(pm2);
266 | }).catch((err) => {
267 | console.error(err);
268 | res.status(400).send(err);
269 | });
270 | });
271 |
272 | /**
273 | * 数据盒子服务 - 停止
274 | */
275 |
276 | router.get('/box_stop', function (req, res) {
277 | BoxService.box_stop().then((pm2) => {
278 | res.send(pm2);
279 | }).catch((err) => {
280 | console.error(err);
281 | res.status(400).send(err);
282 | });
283 | });
284 |
285 | /**
286 | * 数据盒子服务 - 删除
287 | */
288 |
289 | router.get('/box_delete', function (req, res) {
290 | BoxService.box_delete().then((pm2) => {
291 | res.send(pm2);
292 | }).catch((err) => {
293 | console.error(err);
294 | res.status(400).send(err);
295 | });
296 | });
297 |
298 | /**
299 | * 数据盒子服务 - 重启
300 | */
301 |
302 | router.get('/box_restart', function (req, res) {
303 | BoxService.box_restart().then((pm2) => {
304 | res.send(pm2);
305 | }).catch((err) => {
306 | console.error(err);
307 | res.status(400).send(err);
308 | });
309 | });
310 |
311 | /**
312 | * 数据盒子服务 - 查询
313 | */
314 |
315 | router.get('/fetch_box', function (req, res) {
316 | BoxService.fetch_box().then((pm2) => {
317 | res.send(pm2);
318 | }).catch((err) => {
319 | console.error(err);
320 | res.status(400).send(err);
321 | });
322 | });
323 |
324 | /**
325 | * 生产环境 - 打包
326 | */
327 |
328 | router.get('/get_box_prod_zip/:visual', function (req, res) {
329 | get_box_prod_zip(req.params.visual).then((zip) => {
330 | res.send(zip);
331 | }).catch((err) => {
332 | console.error(err);
333 | res.status(400).send(err);
334 | });
335 | });
336 |
337 | router.get('/download/:filename', function (req, res) {
338 | let filename = req.params.filename;
339 | let path = 'archive/' + req.params.filename;
340 | fs.exists(path, function (exists) {
341 | if (exists) {
342 | res.download(path, filename);
343 | } else {
344 | res.status(404).send({
345 | status: 404,
346 | message: '请求错误'
347 | });
348 | }
349 | });
350 | });
351 |
352 | module.exports = router;
353 |
--------------------------------------------------------------------------------
/server/services/AccountService.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import {key, PrivateKey, Signature} from 'gxbjs';
3 | import {Apis} from 'gxbjs-ws';
4 | import Immutable from 'immutable';
5 | import ConfigStore from './ConfigStore';
6 | import dictionary from '../utils/dictionary_en';
7 | import request from 'superagent';
8 | import gui_config from '../../config';
9 |
10 | /**
11 | * 获取账户信息
12 | */
13 | const fetch_account = function (account_name) {
14 | return Apis.instance().db_api().exec('get_account_by_name', [account_name]);
15 | };
16 |
17 | /**
18 | * 创建账号
19 | */
20 | const create_account = function (env, account_type, new_account_name, protocol) {
21 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
22 | let referrer = env === 'production' ? gui_config.build.referrer : gui_config.dev.referrer;
23 | let brainkey = key.suggest_brain_key(dictionary.en);
24 | let private_key = key.get_brainPrivateKey(brainkey);
25 | let owner_pubkey = private_key.toPublicKey().toPublicKeyString();
26 | let active_pubkey = private_key.toPublicKey().toPublicKeyString();
27 | let body = {
28 | 'account': {
29 | 'name': new_account_name,
30 | 'owner_key': owner_pubkey,
31 | 'active_key': active_pubkey,
32 | 'memo_key': active_pubkey,
33 | 'refcode': '',
34 | 'referrer': referrer
35 | }
36 | };
37 | let account_config = {
38 | 'account_name': new_account_name,
39 | 'private_key': private_key.toWif()
40 | };
41 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
42 | return new Promise(function (resolve, reject) {
43 | request
44 | .post(faucetAddress + '/account/register')
45 | .send(body)
46 | .set('Accpet', 'application/json')
47 | .set('Content-Type', 'application/json')
48 | .end(function (err) {
49 | if (err) {
50 | reject(err);
51 | } else {
52 | resolve(account_config);
53 | }
54 | });
55 | });
56 | };
57 |
58 | /**
59 | * 导入账号
60 | */
61 | const import_account = function (account_type, private_key) {
62 | let public_key = PrivateKey.fromWif(private_key).toPublicKey().toPublicKeyString();
63 | return new Promise(function (resolve, reject) {
64 | Apis.instance().db_api().exec('get_key_references', [[public_key]])
65 | .then(function (vec_account_id) {
66 | let refs = Immutable.Set();
67 | vec_account_id = vec_account_id[0];
68 | refs = refs.withMutations(function (r) {
69 | for (let i = 0; i < vec_account_id.length; ++i) {
70 | r.add(vec_account_id[i]);
71 | }
72 | });
73 | return Apis.instance().db_api().exec('get_objects', [refs]);
74 | })
75 | .then((account) => {
76 | let account_config = {
77 | 'account_name': account[0].name,
78 | 'private_key': private_key
79 | };
80 | resolve(account_config);
81 | })
82 | .catch((err) => {
83 | reject(err);
84 | });
85 | });
86 | };
87 |
88 | /**
89 | * 获取序列
90 | */
91 | const sortJSON = function (json) {
92 | let keys = Object.keys(json);
93 | keys.sort();
94 | let result = {};
95 | keys.forEach(function (k) {
96 | result[k] = json[k];
97 | });
98 | return result;
99 | };
100 |
101 | /**
102 | * 获取签名
103 | */
104 | const getSign = function (body = '', type) {
105 | return new Promise(function (resolve, reject) {
106 | try {
107 | let private_key;
108 | if (type === 'merchant') {
109 | private_key = ConfigStore.get_merchant_private_key();
110 | } else {
111 | private_key = ConfigStore.get_datasource_private_key();
112 | }
113 | let signature = Signature.sign(body, private_key).toHex();
114 | resolve(signature);
115 | } catch (ex) {
116 | reject(ex);
117 | }
118 | });
119 | };
120 |
121 | /**
122 | * 获取商户信息
123 | */
124 | const fetch_merchant = function (env, account_name, account_type, protocol) {
125 | let body = {};
126 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
127 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
128 | return new Promise(function (resolve, reject) {
129 | fetch_account(account_name)
130 | .then((account) => {
131 | body.account_id = account.id;
132 | body = sortJSON(body);
133 | return getSign(JSON.stringify(body), account_type);
134 | })
135 | .then(function (signature) {
136 | body.signature = signature;
137 | request
138 | .get(faucetAddress + '/merchant/info')
139 | .query(body)
140 | .set('Accpet', 'application/json')
141 | .set('Content-Type', 'application/json')
142 | .end(function (err, res) {
143 | if (err) {
144 | reject(err);
145 | } else {
146 | resolve(res.body);
147 | }
148 | });
149 | })
150 | .catch(err => reject(err));
151 | });
152 | };
153 |
154 | /**
155 | * 申请认证商户
156 | */
157 | const apply_merchant = function (env, body, account_name, account_type, protocol) {
158 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
159 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
160 | return new Promise(function (resolve, reject) {
161 | fetch_account(account_name)
162 | .then((account) => {
163 | body.account_id = account.id;
164 | body = sortJSON(body);
165 | return getSign(JSON.stringify(body), account_type);
166 | })
167 | .then(function (signature) {
168 | body.signature = signature;
169 | request
170 | .post(faucetAddress + '/merchant/create')
171 | .send(body)
172 | .set('Accpet', 'application/json')
173 | .set('Content-Type', 'application/json')
174 | .end(function (err, res) {
175 | if (err) {
176 | reject(err);
177 | } else {
178 | resolve(res.body);
179 | }
180 | });
181 | })
182 | .catch(err => reject(err));
183 | });
184 | };
185 |
186 | /**
187 | * 申请认证数据源
188 | */
189 | const apply_datasource = function (env, body, account_name, account_type, protocol) {
190 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
191 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
192 | return new Promise(function (resolve, reject) {
193 | fetch_account(account_name)
194 | .then((account) => {
195 | body.account_id = account.id;
196 | body = sortJSON(body);
197 | return getSign(JSON.stringify(body), account_type);
198 | })
199 | .then(function (signature) {
200 | body.signature = signature;
201 | request
202 | .post(faucetAddress + '/dataSource/create')
203 | .send(body)
204 | .set('Accpet', 'application/json')
205 | .set('Content-Type', 'application/json')
206 | .end(function (err, res) {
207 | if (err) {
208 | reject(err);
209 | } else {
210 | resolve(res.body);
211 | }
212 | });
213 | })
214 | .catch(err => reject(err));
215 | });
216 | };
217 |
218 | /**
219 | * 获取申请状态
220 | */
221 | const is_applying = function (env, account_name, protocol) {
222 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
223 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
224 | return new Promise(function (resolve, reject) {
225 | fetch_account(account_name)
226 | .then((account) => {
227 | request
228 | .get(faucetAddress + '/account/apply_status')
229 | .query({account_id: account.id})
230 | .set('Accpet', 'application/json')
231 | .set('Content-Type', 'application/json')
232 | .end(function (err, res) {
233 | if (err) {
234 | reject(err);
235 | } else {
236 | resolve(res.body);
237 | }
238 | });
239 | })
240 | .catch(err => reject(err));
241 | });
242 | };
243 |
244 | const fetch_league_members = function (env, league_id, protocol) {
245 | let faucetAddress = env === 'production' ? gui_config.build.faucet_url : gui_config.dev.faucet_url;
246 | faucetAddress = protocol === 'https:' ? faucetAddress.replace(/http:\/\//, 'https://') : faucetAddress;
247 | return new Promise(function (resolve, reject) {
248 | request
249 | .get(faucetAddress + '/leagueDataSource/memberInfo')
250 | .query({league_id: league_id})
251 | .set('Accpet', 'application/json')
252 | .set('Content-Type', 'application/json')
253 | .end(function (err, res) {
254 | if (err) {
255 | reject(err);
256 | } else {
257 | resolve(res.body);
258 | }
259 | });
260 | });
261 | };
262 |
263 | export default {
264 | create_account,
265 | import_account,
266 | fetch_account,
267 | fetch_merchant,
268 | fetch_league_members,
269 | apply_merchant,
270 | apply_datasource,
271 | is_applying
272 | };
273 |
--------------------------------------------------------------------------------
/server/services/BoxService.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import path from 'path';
3 | import pm2 from 'pm2';
4 | import io from 'socket.io-client';
5 |
6 | let is_started = false;
7 | let port = parseInt(process.env.port || '3031');
8 | let url = 'http://localhost:' + port;
9 |
10 | /**
11 | * 数据盒子服务 - 启动
12 | */
13 | const box_start = function () {
14 | return new Promise(function (resolve, reject) {
15 | pm2.connect(function (err) {
16 | if (err) {
17 | reject(err);
18 | process.exit(2);
19 | }
20 | pm2.start({
21 | name: 'gxb-box-pm2',
22 | script: path.join(process.cwd(), 'dist/box/gxb-box.js'),
23 | exec_mode: 'fork',
24 | max_memory_restart: '100M'
25 | }, function (err, apps) {
26 | if (err) {
27 | console.log(path.join(process.cwd(), 'box/gxb-box.js'));
28 | pm2.start({
29 | name: 'gxb-box-pm2',
30 | script: path.join(process.cwd(), 'box/gxb-box.js'),
31 | exec_mode: 'fork',
32 | max_memory_restart: '100M'
33 | }, function (err, apps) {
34 | if (err) {
35 | reject(err);
36 | } else {
37 | resolve(apps);
38 | }
39 | });
40 | } else {
41 | if (apps.length == 0) {
42 | pm2.start({
43 | name: 'gxb-box-pm2',
44 | script: path.join(process.cwd(), 'box/gxb-box.js'),
45 | exec_mode: 'fork',
46 | max_memory_restart: '100M'
47 | }, function (err, apps) {
48 | if (err) {
49 | reject(err);
50 | } else {
51 | resolve(apps);
52 | }
53 | });
54 | } else {
55 | resolve(apps);
56 | }
57 | }
58 | });
59 | });
60 | });
61 | };
62 |
63 | /**
64 | * 数据盒子服务 - 停止
65 | */
66 | const box_stop = function () {
67 | return new Promise(function (resolve, reject) {
68 | pm2.connect(function (err) {
69 | if (err) {
70 | reject(err);
71 | process.exit(2);
72 | }
73 | pm2.stop('gxb-box-pm2', function (err) {
74 | if (err) {
75 | reject(err);
76 | } else {
77 | pm2.describe('gxb-box-pm2', function (err, processDescription) {
78 | if (err) {
79 | reject(err);
80 | } else {
81 | resolve(processDescription);
82 | }
83 | });
84 | }
85 | });
86 | });
87 | });
88 | };
89 |
90 | /**
91 | * 数据盒子服务 - 删除
92 | */
93 | const box_delete = function () {
94 | return new Promise(function (resolve, reject) {
95 | pm2.connect(function (err) {
96 | if (err) {
97 | reject(err);
98 | process.exit(2);
99 | }
100 | pm2.describe('gxb-box-pm2', function (err, processDescription) {
101 | if (err) {
102 | reject(err);
103 | } else {
104 | if (processDescription.length === 0) {
105 | resolve();
106 | } else {
107 | pm2.delete('gxb-box-pm2', function (err) {
108 | if (err) {
109 | reject(err);
110 | } else {
111 | resolve();
112 | }
113 | });
114 | }
115 | }
116 | });
117 | });
118 | });
119 | };
120 |
121 | /**
122 | * 数据盒子服务 - 重启
123 | */
124 | const box_restart = function () {
125 | return new Promise(function (resolve, reject) {
126 | pm2.connect(function (err) {
127 | if (err) {
128 | reject(err);
129 | process.exit(2);
130 | }
131 | pm2.restart('gxb-box-pm2', function (err) {
132 | if (err) {
133 | reject(err);
134 | } else {
135 | pm2.describe('gxb-box-pm2', function (err, processDescription) {
136 | if (err) {
137 | reject(err);
138 | } else {
139 | resolve(processDescription);
140 | }
141 | });
142 | }
143 | });
144 | });
145 | });
146 | };
147 |
148 | /**
149 | * 数据盒子服务 - 查询
150 | */
151 | const fetch_box = function () {
152 | return new Promise(function (resolve, reject) {
153 | pm2.connect(function (err) {
154 | if (err) {
155 | reject(err);
156 | process.exit(2);
157 | }
158 | pm2.describe('gxb-box-pm2', function (err, processDescription) {
159 | if (err) {
160 | reject(err);
161 | } else {
162 | // 启动消息BUS,监听日志
163 | if ((!is_started) && (processDescription && processDescription.length > 0)) {
164 | let websocket = io.connect(url);
165 |
166 | setInterval(function () {
167 | pm2.describe('gxb-box-pm2', function (processDescription) {
168 | websocket.emit('system', processDescription);
169 | });
170 | }, 1000);
171 |
172 | pm2.launchBus(function (err, bus) {
173 | if (err) return reject(err);
174 | is_started = true;
175 | bus.on('log:out', function (packet) {
176 | websocket.emit('message', 'out', packet.data);
177 | });
178 | bus.on('log:err', function (packet) {
179 | websocket.emit('message', 'err', packet.data);
180 | });
181 | return resolve(processDescription);
182 | });
183 | } else {
184 | resolve(processDescription);
185 | }
186 | }
187 | });
188 | });
189 | });
190 | };
191 |
192 | export default {
193 | box_start,
194 | box_stop,
195 | box_delete,
196 | box_restart,
197 | fetch_box
198 | };
199 |
--------------------------------------------------------------------------------
/server/services/ConfigStore.js:
--------------------------------------------------------------------------------
1 | import Promise from 'bluebird';
2 | import {PrivateKey} from 'gxbjs';
3 | import BoxService from './BoxService';
4 | import ConnectService from './ConnectService';
5 | import fs from 'fs';
6 | import path from 'path';
7 | import gui_config from '../../config';
8 |
9 | let config_path = path.resolve(process.cwd(), './dist/config/config.json');
10 |
11 | export default {
12 | init () {
13 | let self = this;
14 | return new Promise((resolve, reject) => {
15 | try {
16 | self.config = JSON.parse(fs.readFileSync(config_path, 'utf-8')) || {};
17 | resolve(self.config);
18 | } catch (ex) {
19 | reject(ex);
20 | }
21 | });
22 | },
23 | set (config) {
24 | let self = this;
25 | return new Promise((resolve, reject) => {
26 | try {
27 | fs.writeFileSync(config_path, JSON.stringify(config));
28 | self.config = config;
29 | resolve({
30 | 'message': '系统配置保存成功'
31 | });
32 | } catch (ex) {
33 | reject(ex);
34 | }
35 | });
36 | },
37 | change_config_env (env, config) {
38 | let self = this;
39 | let witnesses = env === 'production' ? gui_config.build.witnesses : gui_config.dev.witnesses;
40 | return new Promise((resolve, reject) => {
41 | // 停止GXB-BOX-PM2
42 | BoxService.box_delete().then(() => {
43 | ConnectService.connect(witnesses, true, function () {
44 | try {
45 | let _config = JSON.parse(fs.readFileSync(config_path, 'utf-8'));
46 | fs.writeFileSync(config_path, JSON.stringify(config));
47 | self.config = config;
48 | resolve({
49 | 'message': '系统切换配置成功',
50 | 'data': {
51 | 'old_config': _config,
52 | 'new_config': config
53 | }
54 | });
55 | } catch (ex) {
56 | reject(ex);
57 | }
58 | });
59 | }).catch((ex) => {
60 | reject(ex);
61 | });
62 | });
63 | },
64 | get_merchant_private_key () {
65 | let config = this.config;
66 | return config.merchant && config.merchant.private_key ? PrivateKey.fromWif(config.merchant.private_key) : '';
67 | },
68 | get_datasource_private_key () {
69 | let config = this.config;
70 | return config.datasource && config.datasource.private_key ? PrivateKey.fromWif(config.datasource.private_key) : '';
71 | }
72 | };
73 |
--------------------------------------------------------------------------------
/server/services/ConnectService.js:
--------------------------------------------------------------------------------
1 | import {Apis, Manager} from 'gxbjs-ws';
2 | import {ChainStore} from 'gxbjs';
3 | import os from 'os';
4 |
5 | let connected = false;
6 | let connectionManager = null;
7 | let _witnesses;
8 |
9 | /**
10 | * 连接witness
11 | * @param callback
12 | */
13 | let connect = function (witnesses, reconnect, callback) {
14 | _witnesses = witnesses || [];
15 | if (reconnect) {
16 | connected = false;
17 | connectionManager.url = witnesses[0];
18 | connectionManager.urls = witnesses;
19 | }
20 | if (connected) {
21 | return callback(connected);
22 | }
23 | if (!connectionManager) {
24 | connectionManager = new Manager({url: witnesses[0], urls: witnesses});
25 | }
26 | connectionManager.connectWithFallback(true).then(() => {
27 | ChainStore.subscribed = false;
28 | ChainStore.subError = null;
29 | ChainStore.clearCache();
30 | ChainStore.head_block_time_string = null;
31 | ChainStore.init().then(() => {
32 | callback && callback(connected);
33 | }).catch(ex => {
34 | console.error(ex);
35 | callback && callback(connected);
36 | });
37 | }).catch((ex) => {
38 | console.error(ex);
39 | });
40 | };
41 |
42 | const get_ip_address = function () {
43 | return new Promise((resolve, reject) => {
44 | try {
45 | let interfaces = os.networkInterfaces();
46 | for (let devName in interfaces) {
47 | if (interfaces.hasOwnProperty(devName)) {
48 | let iface = interfaces[devName];
49 | for (let i = 0; i < iface.length; i++) {
50 | let alias = iface[i];
51 | if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
52 | return resolve(alias.address);
53 | }
54 | }
55 | }
56 | }
57 | } catch (ex) {
58 | reject(ex);
59 | }
60 | });
61 | };
62 |
63 | /**
64 | * websocket 状态处理
65 | * @param status
66 | */
67 | Apis.setRpcConnectionStatusCallback(function (status) {
68 | let statusMap = {
69 | open: '开启',
70 | closed: '关闭',
71 | error: '错误',
72 | reconnect: '重新连接'
73 | };
74 | console.log('witness当前状态:', statusMap[status] || status);
75 | if (!connected && status === 'open') {
76 | connected = true;
77 | }
78 | if (status === 'reconnect') {
79 | console.log('断开重连');
80 | ChainStore.clearCache();
81 | } else if (connected && (status === 'closed' || status === 'error')) { // 出错重连
82 | connected = false;
83 | console.log('重新连接其他witness');
84 | connect(_witnesses, false, function () {});
85 | }
86 | });
87 |
88 | export default {
89 | connect,
90 | get_ip_address
91 | };
92 |
--------------------------------------------------------------------------------
/server/services/DataService.js:
--------------------------------------------------------------------------------
1 | import {Apis} from 'gxbjs-ws';
2 |
3 | /**
4 | * 获取数据市场二级栏目
5 | */
6 | const fetch_data_market_categories = function (data_market_type) {
7 | return Apis.instance().db_api().exec('list_data_market_categories', [data_market_type]).then(function (res) {
8 | let result = (res || []).filter(function (cate) {
9 | return cate.status === 1;
10 | });
11 | return result;
12 | });
13 | };
14 |
15 | /**
16 | * 获取栏目信息
17 | */
18 | const fetch_data_market_categories_info = function (category_id) {
19 | return Apis.instance().db_api().exec('get_data_market_categories', [[category_id]]).then(function (res) {
20 | let result = res && res.length > 0 ? res[0] : {};
21 | return result;
22 | });
23 | };
24 |
25 | /**
26 | * 获取自由市场产品列表
27 | */
28 | const fetch_free_data_products = function (category_id, page, pageSize, keywords) {
29 | return Apis.instance().db_api().exec('list_free_data_products', [category_id, page * pageSize, pageSize, '', keywords, false]).then(function (res) {
30 | let result = {
31 | list: res.data,
32 | total: res.filtered_total
33 | };
34 | return result;
35 | });
36 | };
37 |
38 | /**
39 | * 获取自由市场产品详情
40 | */
41 | const fetch_free_data_product_details = function (product_id) {
42 | return Apis.instance().db_api().exec('get_free_data_products', [[product_id]]).then(function (res) {
43 | let result = res && res.length > 0 ? res[0] : {};
44 | let schema_contexts = [];
45 | result.schema_contexts.forEach(function (schema) {
46 | let schemaJSON = JSON.parse(schema.schema_context);
47 | schema_contexts.push({
48 | version: schema.version,
49 | input: schemaJSON.input,
50 | output: schemaJSON.output,
51 | code: schemaJSON.code,
52 | privacy: schemaJSON.privacy
53 | });
54 | });
55 | let latestVersion = '';
56 | if (schema_contexts.length > 0) {
57 | latestVersion = schema_contexts[schema_contexts.length - 1].version;
58 | }
59 | let data = {
60 | product_name: result.product_name,
61 | brief_desc: result.brief_desc,
62 | datasource: result.datasource,
63 | status: result.status,
64 | total: result.total,
65 | version: latestVersion,
66 | latestVersion: latestVersion,
67 | category_id: result.category_id,
68 | schema_contexts: schema_contexts,
69 | price: result.price,
70 | icon: result.icon
71 | };
72 | return data;
73 | });
74 | };
75 |
76 | /**
77 | * 获取联盟市场联盟列表
78 | */
79 | const fetch_league_list = function (category_id, page, pageSize, keywords) {
80 | return Apis.instance().db_api().exec('list_leagues', [category_id, page * pageSize, pageSize, '', keywords, false]).then(function (res) {
81 | let result = {
82 | list: res.data,
83 | total: res.filtered_total
84 | };
85 | return result;
86 | });
87 | };
88 |
89 | /**
90 | * 获取联盟信息
91 | */
92 | const fetch_league_info = function (league_id) {
93 | return Apis.instance().db_api().exec('get_leagues', [[league_id]]).then(function (res) {
94 | let result = res && res.length > 0 ? res[0] : {};
95 | return result;
96 | });
97 | };
98 |
99 | /**
100 | * 获取联盟市场产品列表
101 | */
102 | const fetch_league_data_products = function (data_product_ids) {
103 | return Apis.instance().db_api().exec('get_league_data_products', [JSON.parse(data_product_ids)]);
104 | };
105 |
106 | export default {
107 | fetch_data_market_categories,
108 | fetch_data_market_categories_info,
109 | fetch_free_data_products,
110 | fetch_free_data_product_details,
111 | fetch_league_list,
112 | fetch_league_info,
113 | fetch_league_data_products
114 | };
115 |
--------------------------------------------------------------------------------
/server/services/ZipArchive.js:
--------------------------------------------------------------------------------
1 | import 'shelljs/global';
2 | import Promise from 'bluebird';
3 | import path from 'path';
4 | import fs from 'fs';
5 | import archiver from 'archiver';
6 |
7 | /**
8 | * 检查archive目录是否创建,未创建则创建
9 | */
10 | export const create_archive_folder = () => {
11 | return new Promise((resolve, reject) => {
12 | fs.exists(path.join(process.cwd(), './archive'), function (exists) {
13 | if (!exists) {
14 | fs.mkdir(path.join(process.cwd(), './archive'), function (err) {
15 | if (err) {
16 | reject(err);
17 | } else {
18 | resolve();
19 | }
20 | });
21 | } else {
22 | resolve();
23 | }
24 | });
25 | });
26 | };
27 |
28 | /**
29 | * 打包
30 | * @param visual 是否打包可视化模块
31 | */
32 | export const get_box_prod_zip = () => {
33 | return new Promise((resolve, reject) => {
34 | create_archive_folder().then(() => {
35 | let archiveFileName = 'gxb-box-' + new Date().valueOf() + '.zip';
36 | let archiveFilePath = path.join(process.cwd(), './archive/' + archiveFileName);
37 | let archive = archiver('zip');
38 | let output = fs.createWriteStream(archiveFilePath);
39 | output.on('close', function () {
40 | let zip_info = {
41 | name: archiveFileName,
42 | size: archive.pointer() + ' total bytes',
43 | time: new Date().valueOf()
44 | };
45 | resolve(zip_info);
46 | });
47 | archive.on('error', function (err) {
48 | reject(err);
49 | });
50 | archive.pipe(output);
51 | archive.file(path.join(process.cwd(), './script/start.sh'), {
52 | date: new Date(),
53 | name: 'start-box.sh'
54 | });
55 | archive.file(path.join(process.cwd(), './script/start.cmd'), {
56 | date: new Date(),
57 | name: 'start-box.cmd'
58 | });
59 | archive.directory(path.join(process.cwd(), './dist/box'), './box', {date: new Date()});
60 | archive.directory(path.join(process.cwd(), './dist/config'), './config', {date: new Date()});
61 | archive.file(path.join(process.cwd(), './package.json'), {date: new Date(), name: 'package.json'});
62 | archive.finalize();
63 | }).catch(reject);
64 | });
65 | };
66 |
--------------------------------------------------------------------------------
/src/app.vue:
--------------------------------------------------------------------------------
1 |
8 |
35 |
36 |
{{league_info.brief_desc}}
161 |ID:{{league_info.id}}
164 |状态:{{league_info.status}} 165 |
166 |数据产品:{{league_info.data_products_num}}个
169 |数据源:{{league_info.members_num}}个
170 |
84 |
确定是否要清空当前所有配置?
89 |请确保备份重要信息,清空后不可逆!
90 |