├── README.md ├── app.js ├── build ├── build.js ├── check-versions.js ├── dev-client.js ├── dev-server.js ├── utils.js ├── vue-loader.conf.js ├── webpack.base.conf.js ├── webpack.dev.conf.js ├── webpack.prod.conf.js └── webpack.test.conf.js ├── config ├── dev.env.js ├── index.js ├── prod.env.js └── test.env.js ├── contracts ├── immla.claims.sol ├── immla.deals.sol ├── immla.offers.sol └── immla.utils.sol ├── dist ├── index.html └── static │ ├── css │ ├── app.2db33b735b267b5d74c48a398e399adc.css │ └── app.2db33b735b267b5d74c48a398e399adc.css.map │ ├── immla-bg.png │ ├── js │ ├── app.1bfe6787ac5a4568f1a0.js │ ├── app.1bfe6787ac5a4568f1a0.js.map │ ├── manifest.82dccd34fac2fc9321cc.js │ ├── manifest.82dccd34fac2fc9321cc.js.map │ ├── vendor.824c427e2388497c1c41.js │ └── vendor.824c427e2388497c1c41.js.map │ ├── logo-mini.png │ ├── logo.png │ └── web3.min.js ├── index.html ├── mongo.js ├── package.json ├── src ├── App.vue ├── app │ ├── modules │ │ ├── auth │ │ │ ├── actions.js │ │ │ ├── auth.vue │ │ │ ├── index.js │ │ │ └── mutations.js │ │ ├── form │ │ │ ├── avatar.vue │ │ │ └── form.vue │ │ ├── main │ │ │ └── main.vue │ │ ├── offers │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── mutations.js │ │ │ └── offers.vue │ │ ├── orders │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── mutations.js │ │ │ └── orders.vue │ │ ├── profile │ │ │ ├── actions.js │ │ │ ├── index.js │ │ │ ├── mutations.js │ │ │ └── profile.vue │ │ └── search │ │ │ ├── actions.js │ │ │ ├── cities.js │ │ │ ├── index.js │ │ │ ├── mutations.js │ │ │ └── search.vue │ ├── router │ │ └── index.js │ ├── select │ │ ├── comp │ │ │ └── Select.vue │ │ └── mixins │ │ │ ├── ajax.js │ │ │ ├── index.js │ │ │ ├── pointerScroll.js │ │ │ └── typeAheadPointer.js │ ├── store │ │ └── index.js │ └── uikit │ │ └── application.scss └── main.js ├── static ├── .gitkeep ├── immla-bg.png ├── logo-mini.png ├── logo.png └── web3.min.js ├── test ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js └── unit │ ├── .eslintrc │ ├── index.js │ ├── karma.conf.js │ └── specs │ └── Hello.spec.js └── usersStorage.js /README.md: -------------------------------------------------------------------------------- 1 | #### Setup of the prototype 2 | 3 | Start in prod mode: 4 | 5 | ``` 6 | node app.js 7 | ``` 8 | 9 | Dev commands: 10 | 11 | ``` bash 12 | # install dependencies 13 | npm install 14 | 15 | # serve with hot reload at localhost:8080 16 | npm run dev 17 | 18 | # build for production with minification 19 | npm run build 20 | 21 | # build for production and view the bundle analyzer report 22 | npm run build --report 23 | 24 | # run unit tests 25 | npm run unit 26 | 27 | # run e2e tests 28 | npm run e2e 29 | 30 | # run all tests 31 | npm test 32 | ``` 33 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | let port = 5005; 2 | let express = require('express'); 3 | let app = express(); 4 | var http = require('http').Server(app); 5 | let usersStorage = require("./usersStorage"); 6 | 7 | let bodyParser = require('body-parser'); 8 | app.use(bodyParser.urlencoded({ extended: false })); 9 | app.use(bodyParser.json()); 10 | app.use(express.static('dist')); 11 | // app.use(express.static('static')); 12 | 13 | app.use('/test', function root(req, res) { 14 | res.json({ "success": true }); 15 | }); 16 | 17 | app.use('/auth/register', function root(req, res) { 18 | console.log("Request for register"); 19 | let user = { 20 | id: req.body.id, 21 | email: req.body.email, 22 | password: req.body.password, 23 | type: req.body.type, 24 | pubkey: req.body.pubkey 25 | }; 26 | usersStorage.saveUser(user).then((result) => { 27 | res.json(result); 28 | }); 29 | }); 30 | 31 | app.use('/auth/check', function root(req, res) { 32 | let token = req.query.token; 33 | usersStorage.getUserByToken(token).then((answer) => { 34 | if(answer == null) { 35 | return res.json({success: false, message: "token is not valid"}); 36 | } 37 | else { 38 | answer.password = "---"; 39 | return res.json({success: true, user: answer}); 40 | } 41 | }); 42 | }); 43 | 44 | app.use('/auth/about', function root(req, res) { 45 | let id = req.query.id; 46 | usersStorage.getUserById(id).then((answer) => { 47 | if(answer == null) { 48 | return res.json({success: false, message: "not found"}); 49 | } 50 | else { 51 | answer.password = "---"; 52 | answer.token = "---"; 53 | return res.json({success: true, user: answer}); 54 | } 55 | }); 56 | }); 57 | 58 | app.use('/auth/aboutkeys', function root(req, res) { 59 | let pubkeys = req.body.pubkeys; 60 | console.log("Request for user by keys"); 61 | usersStorage.getUsersByPubkeys(pubkeys).then((users) => { 62 | if(users == null) { 63 | 64 | return res.json({success: false, message: "not found anything"}); 65 | } 66 | else { 67 | let result = users.map((user) => { 68 | user.password = "---"; 69 | user.token = "---"; 70 | return user; 71 | }); 72 | return res.json({success: true, users: result}); 73 | } 74 | }); 75 | }); 76 | 77 | app.use('/auth/login', function root(req, res) { 78 | console.log('Login request'); 79 | usersStorage.login(req.body.id, req.body.password).then((result) => { 80 | res.json(result); 81 | }); 82 | }); 83 | 84 | http.listen(port, function() { 85 | console.log('listening on *:' + port); 86 | }); 87 | -------------------------------------------------------------------------------- /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 | function exec (cmd) { 6 | return require('child_process').execSync(cmd).toString().trim() 7 | } 8 | 9 | var versionRequirements = [ 10 | { 11 | name: 'node', 12 | currentVersion: semver.clean(process.version), 13 | versionRequirement: packageConfig.engines.node 14 | }, 15 | ] 16 | 17 | if (shell.which('npm')) { 18 | versionRequirements.push({ 19 | name: 'npm', 20 | currentVersion: exec('npm --version'), 21 | versionRequirement: packageConfig.engines.npm 22 | }) 23 | } 24 | 25 | module.exports = function () { 26 | var warnings = [] 27 | for (var i = 0; i < versionRequirements.length; i++) { 28 | var mod = versionRequirements[i] 29 | if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { 30 | warnings.push(mod.name + ': ' + 31 | chalk.red(mod.currentVersion) + ' should be ' + 32 | chalk.green(mod.versionRequirement) 33 | ) 34 | } 35 | } 36 | 37 | if (warnings.length) { 38 | console.log('') 39 | console.log(chalk.yellow('To use this template, you must update following to modules:')) 40 | console.log() 41 | for (var i = 0; i < warnings.length; i++) { 42 | var warning = warnings[i] 43 | console.log(' ' + warning) 44 | } 45 | console.log() 46 | process.exit(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /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 = process.env.NODE_ENV === 'testing' 14 | ? require('./webpack.prod.conf') 15 | : require('./webpack.dev.conf') 16 | 17 | // default port where dev server listens for incoming traffic 18 | var port = process.env.PORT || config.dev.port 19 | // automatically open browser, if not set will be false 20 | var autoOpenBrowser = !!config.dev.autoOpenBrowser 21 | // Define HTTP proxies to your custom API backend 22 | // https://github.com/chimurai/http-proxy-middleware 23 | var proxyTable = config.dev.proxyTable 24 | 25 | var app = express() 26 | var compiler = webpack(webpackConfig) 27 | 28 | var devMiddleware = require('webpack-dev-middleware')(compiler, { 29 | publicPath: webpackConfig.output.publicPath, 30 | quiet: true 31 | }) 32 | 33 | var hotMiddleware = require('webpack-hot-middleware')(compiler, { 34 | log: () => {}, 35 | heartbeat: 2000 36 | }) 37 | // force page reload when html-webpack-plugin template changes 38 | compiler.plugin('compilation', function (compilation) { 39 | compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) { 40 | hotMiddleware.publish({ action: 'reload' }) 41 | cb() 42 | }) 43 | }) 44 | 45 | // proxy api requests 46 | Object.keys(proxyTable).forEach(function (context) { 47 | var options = proxyTable[context] 48 | if (typeof options === 'string') { 49 | options = { target: options } 50 | } 51 | app.use(proxyMiddleware(options.filter || context, options)) 52 | }) 53 | 54 | // handle fallback for HTML5 history API 55 | app.use(require('connect-history-api-fallback')()) 56 | 57 | // serve webpack bundle output 58 | app.use(devMiddleware) 59 | 60 | // enable hot-reload and state-preserving 61 | // compilation error display 62 | app.use(hotMiddleware) 63 | 64 | // serve pure static assets 65 | var staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) 66 | app.use(staticPath, express.static('./static')) 67 | 68 | var uri = 'http://localhost:' + port 69 | 70 | var _resolve 71 | var readyPromise = new Promise(resolve => { 72 | _resolve = resolve 73 | }) 74 | 75 | console.log('> Starting dev server...') 76 | devMiddleware.waitUntilValid(() => { 77 | console.log('> Listening at ' + uri + '\n') 78 | // when env is testing, don't need open it 79 | if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') { 80 | opn(uri) 81 | } 82 | _resolve() 83 | }) 84 | 85 | var server = app.listen(port) 86 | 87 | module.exports = { 88 | ready: readyPromise, 89 | close: () => { 90 | server.close() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /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 | transformToRequire: { 13 | video: 'src', 14 | source: 'src', 15 | img: 'src', 16 | image: 'xlink:href' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /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.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 | }), 24 | // https://github.com/glenjamin/webpack-hot-middleware#installation--usage 25 | new webpack.HotModuleReplacementPlugin(), 26 | new webpack.NoEmitOnErrorsPlugin(), 27 | // https://github.com/ampedandwired/html-webpack-plugin 28 | new HtmlWebpackPlugin({ 29 | filename: 'index.html', 30 | template: 'index.html', 31 | inject: true 32 | }), 33 | new FriendlyErrorsPlugin() 34 | ] 35 | }) 36 | -------------------------------------------------------------------------------- /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 = process.env.NODE_ENV === 'testing' 13 | ? require('../config/test.env') 14 | : config.build.env 15 | 16 | var webpackConfig = merge(baseWebpackConfig, { 17 | module: { 18 | rules: utils.styleLoaders({ 19 | sourceMap: config.build.productionSourceMap, 20 | extract: true 21 | }) 22 | }, 23 | devtool: config.build.productionSourceMap ? '#source-map' : false, 24 | output: { 25 | path: config.build.assetsRoot, 26 | filename: utils.assetsPath('js/[name].[chunkhash].js'), 27 | chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') 28 | }, 29 | plugins: [ 30 | // http://vuejs.github.io/vue-loader/en/workflow/production.html 31 | new webpack.DefinePlugin({ 32 | 'process.env': env 33 | }), 34 | new webpack.optimize.UglifyJsPlugin({ 35 | compress: { 36 | warnings: false 37 | }, 38 | sourceMap: true 39 | }), 40 | // extract css into its own file 41 | new ExtractTextPlugin({ 42 | filename: utils.assetsPath('css/[name].[contenthash].css') 43 | }), 44 | // Compress extracted CSS. We are using this plugin so that possible 45 | // duplicated CSS from different components can be deduped. 46 | new OptimizeCSSPlugin({ 47 | cssProcessorOptions: { 48 | safe: true 49 | } 50 | }), 51 | // generate dist index.html with correct asset hash for caching. 52 | // you can customize output by editing /index.html 53 | // see https://github.com/ampedandwired/html-webpack-plugin 54 | new HtmlWebpackPlugin({ 55 | filename: process.env.NODE_ENV === 'testing' 56 | ? 'index.html' 57 | : config.build.index, 58 | template: 'index.html', 59 | inject: true, 60 | minify: { 61 | removeComments: true, 62 | collapseWhitespace: true, 63 | removeAttributeQuotes: true 64 | // more options: 65 | // https://github.com/kangax/html-minifier#options-quick-reference 66 | }, 67 | // necessary to consistently work with multiple chunks via CommonsChunkPlugin 68 | chunksSortMode: 'dependency' 69 | }), 70 | // split vendor js into its own file 71 | new webpack.optimize.CommonsChunkPlugin({ 72 | name: 'vendor', 73 | minChunks: function (module, count) { 74 | // any required modules inside node_modules are extracted to vendor 75 | return ( 76 | module.resource && 77 | /\.js$/.test(module.resource) && 78 | module.resource.indexOf( 79 | path.join(__dirname, '../node_modules') 80 | ) === 0 81 | ) 82 | } 83 | }), 84 | // extract webpack runtime and module manifest to its own file in order to 85 | // prevent vendor hash from being updated whenever app bundle is updated 86 | new webpack.optimize.CommonsChunkPlugin({ 87 | name: 'manifest', 88 | chunks: ['vendor'] 89 | }), 90 | // copy custom static assets 91 | new CopyWebpackPlugin([ 92 | { 93 | from: path.resolve(__dirname, '../static'), 94 | to: config.build.assetsSubDirectory, 95 | ignore: ['.*'] 96 | } 97 | ]) 98 | ] 99 | }) 100 | 101 | if (config.build.productionGzip) { 102 | var CompressionWebpackPlugin = require('compression-webpack-plugin') 103 | 104 | webpackConfig.plugins.push( 105 | new CompressionWebpackPlugin({ 106 | asset: '[path].gz[query]', 107 | algorithm: 'gzip', 108 | test: new RegExp( 109 | '\\.(' + 110 | config.build.productionGzipExtensions.join('|') + 111 | ')$' 112 | ), 113 | threshold: 10240, 114 | minRatio: 0.8 115 | }) 116 | ) 117 | } 118 | 119 | if (config.build.bundleAnalyzerReport) { 120 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 121 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 122 | } 123 | 124 | module.exports = webpackConfig 125 | -------------------------------------------------------------------------------- /build/webpack.test.conf.js: -------------------------------------------------------------------------------- 1 | // This is the webpack config used for unit tests. 2 | 3 | var utils = require('./utils') 4 | var webpack = require('webpack') 5 | var merge = require('webpack-merge') 6 | var baseConfig = require('./webpack.base.conf') 7 | 8 | var webpackConfig = merge(baseConfig, { 9 | // use inline sourcemap for karma-sourcemap-loader 10 | module: { 11 | rules: utils.styleLoaders() 12 | }, 13 | devtool: '#inline-source-map', 14 | resolveLoader: { 15 | alias: { 16 | // necessary to to make lang="scss" work in test when using vue-loader's ?inject option 17 | // see discussion at https://github.com/vuejs/vue-loader/issues/724 18 | 'scss-loader': 'sass-loader' 19 | } 20 | }, 21 | plugins: [ 22 | new webpack.DefinePlugin({ 23 | 'process.env': require('../config/test.env') 24 | }) 25 | ] 26 | }) 27 | 28 | // no need for app entry during tests 29 | delete webpackConfig.entry 30 | 31 | module.exports = webpackConfig 32 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /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: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: true, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'], 18 | // Run the build command with an extra argument to 19 | // View the bundle analyzer report after build finishes: 20 | // `npm run build --report` 21 | // Set to `true` or `false` to always turn it on or off 22 | bundleAnalyzerReport: process.env.npm_config_report 23 | }, 24 | dev: { 25 | env: require('./dev.env'), 26 | port: 8081, 27 | autoOpenBrowser: true, 28 | assetsSubDirectory: 'static', 29 | assetsPublicPath: '/', 30 | proxyTable: { 31 | '/auth': { 32 | target: 'http://localhost:5000' 33 | // changeOrigin: true, 34 | // pathRewrite: { 35 | // '/auth': '' 36 | // } 37 | } 38 | }, 39 | // CSS Sourcemaps off by default because relative paths are "buggy" 40 | // with this option, according to the CSS-Loader README 41 | // (https://github.com/webpack/css-loader#sourcemaps) 42 | // In our experience, they generally work as expected, 43 | // just be aware of this issue when enabling this option. 44 | cssSourceMap: false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /contracts/immla.claims.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | contract ClaimInterface { 4 | 5 | function getType() constant returns (string _type); 6 | function getDetails() constant returns (string _why); 7 | function getInitiator() constant returns (address _who); 8 | } 9 | 10 | contract DamageClaim is ClaimInterface { 11 | 12 | string private constant CLAIM_TYPE = "DAMAGE"; 13 | 14 | address private owned; 15 | string private details; 16 | 17 | function DamageClaim(string _details) { 18 | details = _details; 19 | owned = msg.sender; 20 | } 21 | 22 | function getType() public constant 23 | returns (string _type) { 24 | return CLAIM_TYPE; 25 | } 26 | 27 | function getDetails() public constant 28 | returns (string _why) { 29 | return details; 30 | } 31 | 32 | function getInitiator() public constant 33 | returns (address _who) { 34 | return owned; 35 | } 36 | } -------------------------------------------------------------------------------- /contracts/immla.deals.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import "./immla.utils.sol"; 4 | import "./immla.offers.sol"; 5 | 6 | /* @title - Deal between Client and Supplier */ 7 | contract Deal is LiveCycle { 8 | 9 | using Preconditions for *; 10 | 11 | Offer public offer; 12 | address public customer; 13 | 14 | byte constant public SUPPLIER = 0x1; 15 | byte constant public CLIENT = 0x2; 16 | 17 | byte public status = byte(0); 18 | 19 | ClaimInterface private recallClaim; 20 | 21 | /** 22 | * @dev - new deal instance between Client and Supplier 23 | * 24 | * TODO need to change customer to 'Order' 25 | */ 26 | function Deal(address _customer, Offer _offer) { 27 | require(!_offer.isInvalidAddr() && _offer.isRunning()); 28 | require(!_customer.isInvalidAddr()); 29 | 30 | offer = _offer; 31 | customer = _customer; 32 | 33 | // default state is Running 34 | changeState(Running); 35 | } 36 | 37 | /* @dev - get joint Deal content */ 38 | function getData() public constant 39 | returns (address, address, bool, byte) { 40 | return (offer, customer, isCompleted(), status); 41 | } 42 | 43 | /* @dev - complete the Order by the Supplier side */ 44 | function compete() public 45 | atState(Running) 46 | only(offer.getOwner()) 47 | { 48 | status |= SUPPLIER; 49 | if(isCompleted()) { 50 | changeState(Paused); 51 | close(); 52 | } 53 | } 54 | 55 | /* @dev - complete the Order by the Client side */ 56 | function confirm() public 57 | atState(Running) 58 | only(customer) 59 | { 60 | status |= CLIENT; 61 | if(isCompleted()) { 62 | changeState(Paused); 63 | close(); 64 | } 65 | } 66 | 67 | /* @dev - check is Order is completed */ 68 | function isCompleted() public constant 69 | returns (bool) { 70 | return (status == (SUPPLIER | CLIENT)); 71 | } 72 | 73 | /** 74 | * @dev recall the deal by some reasons 75 | * @param _recallClaim reason why the Deal was closed 76 | */ 77 | function recall(ClaimInterface _recallClaim) public 78 | atState(Running|Paused) 79 | anyOfTwo([offer.getOwner(), customer]) 80 | { 81 | require(!_recallClaim.isInvalidAddr()); 82 | recallClaim = _recallClaim; 83 | interrupt(); 84 | } 85 | 86 | /* @dev - Get details why Offer was closed */ 87 | function getRecallClaim() public constant 88 | atState(Interrupted) 89 | returns(ClaimInterface) { 90 | return recallClaim; 91 | } 92 | 93 | /* @dev - direct change state is not allowed */ 94 | function switchTo(byte _state) public { revert(); } 95 | 96 | // Contact Modifiers 97 | modifier only(address _who) { 98 | require(!_who.isInvalidAddr()); 99 | require(msg.sender == _who); 100 | _; 101 | } 102 | 103 | modifier anyOfTwo(address[2] memory _who) { 104 | bool res = false; 105 | for(uint i = 0; i < _who.length; i++) { 106 | require(!_who[i].isInvalidAddr()); 107 | if(msg.sender == _who[i]) { 108 | res = true; 109 | break; 110 | } 111 | } 112 | if(!res) revert(); 113 | _; 114 | } 115 | } 116 | 117 | /* @title - Registry of the created Deals */ 118 | contract DealsRegistry { 119 | 120 | using Preconditions for *; 121 | 122 | mapping(address => Deal[]) private byOwners; 123 | mapping(address => Deal[]) private bySuppliers; 124 | 125 | function DealsRegistry() {} 126 | 127 | function newDeal(Offer _offer) public { 128 | require(!_offer.isInvalidAddr()); 129 | 130 | Deal deal = new Deal(msg.sender, _offer); 131 | require(!deal.isInvalidAddr()); 132 | 133 | bySuppliers[_offer.getOwner()].push(deal); 134 | byOwners[msg.sender].push(deal); 135 | } 136 | 137 | function getMyOwnedDeal(uint _pos) public constant 138 | returns (Deal) { 139 | require(byOwners[msg.sender].length > _pos); 140 | return byOwners[msg.sender][_pos]; 141 | } 142 | 143 | function getMyOwnedDeals() public constant 144 | returns (Deal[]) { 145 | return byOwners[msg.sender]; 146 | } 147 | 148 | function getMyOwnedDealCount() public constant 149 | returns (uint) { 150 | return byOwners[msg.sender].length; 151 | } 152 | 153 | function getMySupplyingDeal(uint _pos) public constant 154 | returns (Deal) { 155 | require(bySuppliers[msg.sender].length > _pos); 156 | return bySuppliers[msg.sender][_pos]; 157 | } 158 | 159 | function getMySupplyingDeals() public constant 160 | returns (Deal[]) { 161 | return bySuppliers[msg.sender]; 162 | } 163 | 164 | function getMySupplyingDealCount() public constant 165 | returns (uint) { 166 | return bySuppliers[msg.sender].length; 167 | } 168 | } -------------------------------------------------------------------------------- /contracts/immla.offers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | import './immla.utils.sol'; 4 | import './immla.claims.sol'; 5 | 6 | contract Offer is AC, LiveCycle { 7 | 8 | address private supplier; 9 | 10 | string public details; 11 | string public from; 12 | string public to; 13 | 14 | /** 15 | * @dev - The new Offer 16 | * @param _supplier Offer's real owner (supplier) 17 | * @param _manager administrative person responsible for the Offer management 18 | * @param _details additional Offer's params (e.g cost, some sort of data) 19 | * @param _from Offer's route source 20 | * @param _to Offer's route destination 21 | * 22 | * NOTE The Offer's owner is a OffersRegistry, it allows to prevet not sync 23 | * data if supplier will be changed by some reasons (e.g lost keys) 24 | */ 25 | function Offer( 26 | address _supplier, 27 | address _manager, 28 | string _details, 29 | string _from, 30 | string _to 31 | ) 32 | AC(_manager) 33 | { 34 | supplier = _supplier; 35 | details = _details; 36 | from = _from; 37 | to = _to; 38 | state = Running; 39 | } 40 | 41 | /* @dev - get joint Offer content */ 42 | function getData() public constant 43 | returns (address, string, string, string, byte) { 44 | return (supplier, details, from, to, state); 45 | } 46 | 47 | /* @dev - get the Offer's owner (supplier) */ 48 | function getOwner() public constant 49 | returns (address) { 50 | return supplier; 51 | } 52 | 53 | /* @dev - get the Offer's manager */ 54 | function getManager() public constant 55 | returns(address) { 56 | return manager; 57 | } 58 | 59 | /** 60 | * @dev - Update Offer's details. 61 | * Method emits LogUpdated 62 | * @param _details Json with list of the Offer's options 63 | * 64 | * NOTE Method can be invoked only by Offer's Manager at 'Paused' state 65 | */ 66 | function update(string _details) public 67 | atState(Paused) 68 | onlyManager 69 | { 70 | details = _details; 71 | LogUpdated(msg.sender, details); 72 | } 73 | 74 | /** 75 | * @dev - change Offer's state. 76 | * list of available states can find in LiveCycle 77 | * @param _state new state 78 | * 79 | * NOTE Method can be invoked only by Offer's Manager at non final states 80 | */ 81 | function switchTo(byte _state) public 82 | atState(Running|Paused) 83 | onlyManager 84 | { 85 | changeState(_state); 86 | } 87 | 88 | /* @dev - close the Offer */ 89 | function recall() public 90 | atState(Paused) 91 | onlyManager 92 | { 93 | close(); 94 | } 95 | 96 | /* @dev - manager changing is not allowed yet */ 97 | function changeManager(address _manager) public { revert(); } 98 | 99 | // Contract Events 100 | event LogUpdated(address _who, string _what); 101 | } 102 | 103 | /* @title - cache of the newest Offers */ 104 | contract OffersCache { 105 | 106 | using Preconditions for *; 107 | 108 | uint public constant MAX_DEPTH = 20; 109 | uint private top = MAX_DEPTH; 110 | uint private currentDepth = 0; 111 | 112 | Offer[] cache; 113 | 114 | function OffersCache() { 115 | cache = new Offer[](MAX_DEPTH); 116 | } 117 | 118 | function push(Offer _offer) internal { 119 | require(!_offer.isInvalidAddr()); 120 | 121 | if(++top >= MAX_DEPTH) { 122 | top = 0; 123 | } 124 | if(currentDepth < MAX_DEPTH) 125 | currentDepth++; 126 | cache[top] = _offer; 127 | } 128 | 129 | function peek(uint _maxDepth) public constant 130 | returns(Offer[]) { 131 | require(currentDepth > 0); 132 | require((_maxDepth <= MAX_DEPTH) && (_maxDepth > 0)); 133 | 134 | uint recordsCount = (currentDepth < _maxDepth) 135 | ? currentDepth : _maxDepth; 136 | 137 | Offer[] memory res = new Offer[](recordsCount); 138 | for(uint8 i = 0; i < recordsCount; i++) 139 | res[i] = cache[i]; 140 | 141 | return res; 142 | } 143 | 144 | function peekTop() public constant 145 | returns(Offer) { 146 | return cache[top]; 147 | } 148 | } 149 | 150 | /* @title - Offers management Contact */ 151 | contract OffersRegistry is OffersCache { 152 | 153 | mapping(uint => Offer[]) private byRoute; 154 | mapping(address => Offer[]) private bySupplier; 155 | 156 | function OffersRegistry() {} 157 | 158 | using Preconditions for *; 159 | 160 | /** 161 | * @dev - Create new offers depends where 162 | * src and dst are the keys for further filtering 163 | * 164 | * TODO Contract needs to prevent access to entity other 165 | * than Suppliers 166 | */ 167 | function newOffer( 168 | string _details, 169 | string _from, 170 | string _to 171 | ) 172 | public 173 | //TODO: add AC 174 | { 175 | require(!_details.isEmpty()); 176 | require(!_from.isEmpty() && !_to.isEqual(_from)); 177 | 178 | //TODO: supplier ID and manager have to be recieved from the SuppliersRegistry 179 | address supplier = msg.sender; 180 | address manager = msg.sender; 181 | 182 | Offer offer = new Offer(supplier, manager, _details, _from, _to); 183 | require(!offer.isInvalidAddr()); 184 | 185 | uint route = uint(keccak256(_from, _to)); 186 | byRoute[route].push(offer); 187 | bySupplier[supplier].push(offer); 188 | 189 | push(offer); 190 | LogNewOffer(offer); 191 | } 192 | 193 | function getOffers() public constant 194 | returns (Offer[]) { 195 | return bySupplier[msg.sender]; 196 | } 197 | 198 | function getBySupplier(address _supplier) public constant 199 | returns (Offer[]) { 200 | require(!_supplier.isInvalidAddr()); 201 | return bySupplier[_supplier]; 202 | } 203 | 204 | function getByRoute(string _from, string _to) public constant 205 | returns (Offer[]) { 206 | require(!_from.isEmpty() && !_to.isEqual(_from)); 207 | 208 | uint route = uint(keccak256(_from, _to)); 209 | return byRoute[route]; 210 | } 211 | 212 | // Contract Events 213 | event LogNewOffer(Offer _offer); 214 | } 215 | -------------------------------------------------------------------------------- /contracts/immla.utils.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.15; 2 | 3 | 4 | /* @title - Access control utilities */ 5 | contract AC { 6 | 7 | /** 8 | * @dev - Contract provides roles management and access control. 9 | * There are 2 roles: owner - creator of the contract and 10 | * manager - person who is responsible for administrative facilities. 11 | * 12 | * TODO - need to implement multiple managers functionality 13 | */ 14 | 15 | using Preconditions for address; 16 | 17 | address internal owner; 18 | address internal manager; 19 | 20 | function AC(address _manager) { 21 | require(!_manager.isInvalidAddr()); 22 | owner = msg.sender; 23 | manager = _manager; 24 | } 25 | 26 | function setManager(address _manager) internal { 27 | require(!_manager.isInvalidAddr()); 28 | manager = _manager; 29 | } 30 | 31 | function changeManager(address _manager) public; 32 | 33 | modifier onlyOwner { require(msg.sender == owner); _; } 34 | modifier onlyManager { require(msg.sender == manager); _; } 35 | } 36 | 37 | 38 | /* @title - Life Cycle of the Contract */ 39 | contract LiveCycle { 40 | 41 | /** 42 | * @dev - There are 4 states of Contract exist: 43 | * Running - contact is avaliable for calling 44 | * Paused - Contract is suspended. Contact is temprorary blocked 45 | * and only administrative functions are available 46 | * Closed - contract is finally closed 47 | * Interrupted - contract is stopped suddenly by some reasons 48 | */ 49 | 50 | using Preconditions for byte; 51 | 52 | byte constant internal Paused = 0x1; 53 | byte constant internal Running = 0x2; 54 | byte constant internal Closed = 0x4; 55 | byte constant internal Interrupted = 0x8; 56 | 57 | byte public state = Paused; 58 | 59 | // Paused -> Running 60 | function isRunAvaliable() private constant 61 | returns(bool) { 62 | return (state == Paused); 63 | } 64 | 65 | // Running -> Paused 66 | function isSuspendAvailable() private constant 67 | returns(bool) { 68 | return (state == Running); 69 | } 70 | 71 | // Paused -> Closed 72 | function isCloseAvailable() private constant 73 | returns(bool) { 74 | return (state == Paused); 75 | } 76 | 77 | // Running|Paused -> Interrupted 78 | function isInterruptAvailable() private constant 79 | returns(bool) { 80 | return (state == Running) || (state == Paused); 81 | } 82 | 83 | function changeState(byte _state) internal 84 | returns(byte) { 85 | if(state == _state) 86 | return state; 87 | 88 | if(_state == Running) require(isRunAvaliable()); 89 | else if(_state == Paused) require(isSuspendAvailable()); 90 | else revert(); 91 | 92 | LogChangeState(state, _state); 93 | state = _state; 94 | return state; 95 | } 96 | 97 | function interrupt() internal { 98 | require(isInterruptAvailable()); 99 | 100 | LogChangeState(state, Interrupted); 101 | state = Interrupted; 102 | } 103 | 104 | function close() internal { 105 | require(isCloseAvailable()); 106 | 107 | LogChangeState(state, Closed); 108 | state = Closed; 109 | } 110 | 111 | function isRunning() public constant 112 | returns(bool) { 113 | return (state == Running); 114 | } 115 | 116 | function switchTo(byte _state) public; 117 | 118 | modifier atState(byte _states) { require(!Preconditions.isEmpty(_states & state)); _; } 119 | 120 | event LogChangeState(byte _from, byte _to); 121 | } 122 | 123 | 124 | /* @title - Preliminary data verifications */ 125 | library Preconditions { 126 | 127 | /** 128 | * @dev - Fundamental function to validate different data types 129 | * Functions are not generates exceptions and can be handled 130 | * on the client side 131 | */ 132 | 133 | /* @dev - check that byte is null */ 134 | function isEmpty(byte _val) constant internal 135 | returns(bool) { 136 | return (byte(0) == _val); 137 | } 138 | 139 | /* @dev - check that string is empty */ 140 | function isEmpty(string _val) constant internal 141 | returns(bool) { 142 | return (0 == bytes(_val).length); 143 | } 144 | 145 | /* @dev - are two strings equal */ 146 | function isEqual(string _a, string _b) constant internal 147 | returns (bool) { 148 | bytes memory a = bytes(_a); 149 | bytes memory b = bytes(_b); 150 | 151 | if(a.length != b.length) 152 | return false; 153 | 154 | for (uint i = 0; i < b.length; i++) 155 | if(a[i] != b[i]) 156 | return false; 157 | 158 | return true; 159 | } 160 | 161 | /* @dev - check the address correctness */ 162 | function isInvalidAddr(address _addr) constant internal 163 | returns (bool) { 164 | return (address(0) == _addr); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | Prototype
-------------------------------------------------------------------------------- /dist/static/immla-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapps-community/dapp-logistic-sample/058a3f6763fa3681d387b33bdca71e801e6c765f/dist/static/immla-bg.png -------------------------------------------------------------------------------- /dist/static/js/manifest.82dccd34fac2fc9321cc.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,a){for(var i,u,f,s=0,l=[];s 2 | 3 | 4 | 5 | Prototype 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /mongo.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | let db = mongoose.connection; 4 | 5 | module.exports.mongo = db; 6 | 7 | const connectionString = `mongodb://localhost:27017/immla`; 8 | 9 | const connect = function () { 10 | 11 | if (db.readyState !== 0) { 12 | return; 13 | } 14 | 15 | const opts = { 16 | server: { 17 | "auto_reconnect": true, 18 | poolSize: 10, 19 | socketOptions: { 20 | keepAlive: true 21 | } 22 | }, 23 | db: { 24 | numberOfRetries: 5, 25 | retryMiliSeconds: 1000 26 | } 27 | }; 28 | 29 | mongoose.connect(connectionString, opts); 30 | }; 31 | 32 | db.on('connected', function () { 33 | console.log('Connected to mongo database: ', connectionString); 34 | }); 35 | 36 | db.on('disconnected', function () { 37 | console.error('Disconnected from mongo database: ', connectionString); 38 | }); 39 | 40 | db.on('error', function (err) { 41 | console.error('Error from mongo db'); 42 | console.error(err.stack.toString()); 43 | process.exit(); 44 | }); 45 | 46 | db.on('open', function () { 47 | console.log('Open connection to mongo database: ', connectionString); 48 | }); 49 | 50 | connect(); 51 | 52 | const userScheme = mongoose.Schema({ 53 | id: String, 54 | password: String, 55 | type: {type: String, default: "client"}, 56 | email: String, 57 | token: String, 58 | pubkey: String 59 | }); 60 | 61 | module.exports.User = mongoose.model('User', userScheme); 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "immla-prototype", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "fw", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "start": "node build/dev-server.js", 10 | "build": "node build/build.js", 11 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run", 12 | "e2e": "node test/e2e/runner.js", 13 | "test": "npm run unit && npm run e2e", 14 | "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.16.2", 18 | "babel-preset-es2015": "^6.24.1", 19 | "bootstrap-vue": "^0.18.0", 20 | "mongoose": "^4.11.7", 21 | "tether": "^1.4.0", 22 | "vue": "^2.3.3", 23 | "vue-awesome": "^2.3.1", 24 | "vue-router": "^2.6.0", 25 | "vue-select": "github:sagalbot/vue-select", 26 | "vuex": "^2.3.1" 27 | }, 28 | "devDependencies": { 29 | "autoprefixer": "^7.1.2", 30 | "babel-core": "^6.22.1", 31 | "babel-eslint": "^7.1.1", 32 | "babel-loader": "^7.1.1", 33 | "babel-plugin-transform-runtime": "^6.22.0", 34 | "babel-preset-env": "^1.3.2", 35 | "babel-preset-stage-2": "^6.22.0", 36 | "babel-register": "^6.22.0", 37 | "chalk": "^2.0.1", 38 | "connect-history-api-fallback": "^1.3.0", 39 | "copy-webpack-plugin": "^4.0.1", 40 | "css-loader": "^0.28.0", 41 | "cssnano": "^3.10.0", 42 | "eslint": "^3.19.0", 43 | "eslint-friendly-formatter": "^3.0.0", 44 | "eslint-loader": "^1.7.1", 45 | "eslint-plugin-html": "^3.0.0", 46 | "eslint-config-standard": "^6.2.1", 47 | "eslint-plugin-promise": "^3.4.0", 48 | "eslint-plugin-standard": "^2.0.1", 49 | "eventsource-polyfill": "^0.9.6", 50 | "express": "^4.14.1", 51 | "extract-text-webpack-plugin": "^2.0.0", 52 | "file-loader": "^0.11.1", 53 | "friendly-errors-webpack-plugin": "^1.1.3", 54 | "html-webpack-plugin": "^2.28.0", 55 | "http-proxy-middleware": "^0.17.3", 56 | "webpack-bundle-analyzer": "^2.2.1", 57 | "cross-env": "^5.0.1", 58 | "karma": "^1.4.1", 59 | "karma-coverage": "^1.1.1", 60 | "karma-mocha": "^1.3.0", 61 | "karma-phantomjs-launcher": "^1.0.2", 62 | "karma-phantomjs-shim": "^1.4.0", 63 | "karma-sinon-chai": "^1.3.1", 64 | "karma-sourcemap-loader": "^0.3.7", 65 | "karma-spec-reporter": "0.0.31", 66 | "karma-webpack": "^2.0.2", 67 | "lolex": "^1.5.2", 68 | "mocha": "^3.2.0", 69 | "chai": "^3.5.0", 70 | "sinon": "^2.1.0", 71 | "sinon-chai": "^2.8.0", 72 | "inject-loader": "^3.0.0", 73 | "babel-plugin-istanbul": "^4.1.1", 74 | "phantomjs-prebuilt": "^2.1.14", 75 | "chromedriver": "^2.27.2", 76 | "cross-spawn": "^5.0.1", 77 | "nightwatch": "^0.9.12", 78 | "selenium-server": "^3.0.1", 79 | "semver": "^5.3.0", 80 | "shelljs": "^0.7.6", 81 | "opn": "^5.1.0", 82 | "optimize-css-assets-webpack-plugin": "^2.0.0", 83 | "ora": "^1.2.0", 84 | "rimraf": "^2.6.0", 85 | "url-loader": "^0.5.8", 86 | "vue-loader": "^12.1.0", 87 | "vue-style-loader": "^3.0.1", 88 | "vue-template-compiler": "^2.3.3", 89 | "webpack": "^2.6.1", 90 | "webpack-dev-middleware": "^1.10.0", 91 | "webpack-hot-middleware": "^2.18.0", 92 | "webpack-merge": "^4.1.0" 93 | }, 94 | "engines": { 95 | "node": ">= 4.0.0", 96 | "npm": ">= 3.0.0" 97 | }, 98 | "browserslist": [ 99 | "> 1%", 100 | "last 2 versions", 101 | "not ie <= 8" 102 | ] 103 | } 104 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 36 | 37 | 71 | -------------------------------------------------------------------------------- /src/app/modules/auth/actions.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const CHANGE_TOKEN = 'CHANGE_TOKEN'; 4 | export const SET_KEY = 'SET_KEY'; 5 | export const auth = (store, {login, password, key}) => { 6 | const {commit} = store; 7 | return axios.post('/auth/login', { 8 | id: login, 9 | password 10 | }).then((res) => { 11 | console.log(res.data); 12 | if (res.data.success) { 13 | window.localStorage.setItem('immla-front-token', res.data.token); 14 | window.localStorage.setItem('immla-network-key', key); 15 | commit(CHANGE_TOKEN, res.data.token); 16 | commit(SET_KEY, key); 17 | } 18 | return res; 19 | }); 20 | }; 21 | 22 | export const register = (store, {id, password, email, type}) => { 23 | const {commit} = store; 24 | let account = web3.eth.accounts.create(); 25 | return axios.post('/auth/register', { 26 | id, 27 | password, 28 | email, 29 | type, 30 | pubkey: account.address 31 | }).then((res) => { 32 | web3.eth.sendTransaction({from:"0x64665166273237b3b74838863dd9f0ff61960300", to: account.address, 33 | value: web3.utils.toWei(5)}).then(console.log); 34 | console.log(res); 35 | if (res.data.success) { 36 | window.localStorage.setItem('immla-front-token', res.data.token); 37 | res.data.key = account.privateKey; 38 | window.localStorage.setItem('immla-network-key', res.data.key); 39 | commit(SET_KEY, res.data.key); 40 | } 41 | return res; 42 | }); 43 | }; 44 | 45 | export const LOG_OFF = 'LOG_OFF'; 46 | export const logOff = (store) => { 47 | const {commit} = store; 48 | window.localStorage.setItem('immla-front-token', null); 49 | window.localStorage.setItem('immla-network-key', null); 50 | return commit(LOG_OFF); 51 | }; 52 | 53 | export const getUserInfo = () => { 54 | const token = window.localStorage.getItem('immla-front-token'); 55 | return axios.get('/auth/check?token=' + token); 56 | }; 57 | 58 | export const GET_USER_INFO = 'GET_USER_INFO'; 59 | export const getInfo = (store) => { 60 | return getUserInfo().then(res => { 61 | if (!res.data.success) 62 | store.commit(LOG_OFF); 63 | else store.commit(GET_USER_INFO, res.data.user); 64 | return res; 65 | }); 66 | }; 67 | -------------------------------------------------------------------------------- /src/app/modules/auth/auth.vue: -------------------------------------------------------------------------------- 1 | 91 | 92 | 182 | 183 | 413 | -------------------------------------------------------------------------------- /src/app/modules/auth/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions'; 2 | import { defaultState, mutations } from './mutations'; 3 | 4 | const state = defaultState; 5 | 6 | export default { 7 | module: { 8 | actions, 9 | state, 10 | mutations 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/app/modules/auth/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | CHANGE_TOKEN, 3 | LOG_OFF, 4 | GET_USER_INFO, 5 | SET_FRIENDS, 6 | SET_KEY 7 | } from './actions' 8 | 9 | export const defaultState = { 10 | token: null, 11 | name: '', 12 | type: null, 13 | privateKey: null 14 | } 15 | 16 | export const mutations = { 17 | [CHANGE_TOKEN] (state, token) { 18 | state.token = token 19 | }, 20 | [SET_KEY] (state, key) { 21 | state.privateKey = key 22 | }, 23 | [LOG_OFF] (state) { 24 | state.token = null; 25 | state.privateKey = null; 26 | }, 27 | [GET_USER_INFO] (state, payload) { 28 | state.name = payload.id || ''; 29 | state.type = payload.type || []; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/app/modules/form/avatar.vue: -------------------------------------------------------------------------------- 1 | 6 | 24 | -------------------------------------------------------------------------------- /src/app/modules/form/form.vue: -------------------------------------------------------------------------------- 1 | 2 | 70 | 71 | 125 | 126 | 281 | -------------------------------------------------------------------------------- /src/app/modules/main/main.vue: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 48 | 49 | 74 | -------------------------------------------------------------------------------- /src/app/modules/offers/actions.js: -------------------------------------------------------------------------------- 1 | export const SET_OFFERS = 'SET_OFFERS'; 2 | export const syncOffers = (store) => { 3 | const {commit} = store; 4 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 5 | let offersregistry = getOffersRegistry(offersRegistryAddress, account); 6 | return offersregistry.getOffers().then((offerIds) => { 7 | console.log(offerIds); 8 | let offerPromises = offerIds.map(offerId => getOffer(offerId, account).methods.getData().call()); 9 | return Promise.all(offerPromises).then((offersResults) => { 10 | console.log("Raw offers results: ", offersResults); 11 | let preparedOffers = offersResults.map((offerData, i) => { 12 | return { 13 | id: offerIds[i], 14 | forwarderAddress: offerData['0'], 15 | details: offerData['1'], 16 | from: offerData['2'], 17 | to: offerData['3'], 18 | state: offerData['4'] 19 | } 20 | }); 21 | commit(SET_OFFERS, preparedOffers); 22 | return preparedOffers; 23 | }); 24 | }); 25 | }; 26 | 27 | export const ADD_OFFER = 'ADD_OFFER'; 28 | export const addOffer = (store, offer) => { 29 | console.log(offer); 30 | const {commit} = store; 31 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 32 | let offersregistry = getOffersRegistry(offersRegistryAddress, account); 33 | return offersregistry.newOffer(offer.details, offer.from, offer.to).then((res) => { 34 | console.log('created offer res: ', res); 35 | }); 36 | }; 37 | 38 | export const runOffer = (store, offerId) => { 39 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 40 | return getOffer(offerId, account).switchTo("0x02"); 41 | }; 42 | 43 | export const closeOffer = (store, offerId) => { 44 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 45 | return getOffer(offerId, account).close(); 46 | }; 47 | 48 | export const pauseOffer = (store, offerId) => { 49 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 50 | return getOffer(offerId, account).switchTo("0x01"); 51 | }; -------------------------------------------------------------------------------- /src/app/modules/offers/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as actions from './actions'; 3 | import { defaultState, mutations } from './mutations'; 4 | 5 | const state = defaultState; 6 | 7 | export default { 8 | module: { 9 | actions, 10 | state, 11 | mutations 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/modules/offers/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_OFFERS 3 | } from './actions' 4 | 5 | export const defaultState = { 6 | offers: [] 7 | }; 8 | 9 | export const mutations = { 10 | [SET_OFFERS](state, offers) { 11 | state.offers = offers; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/modules/offers/offers.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 110 | 111 | 221 | 222 | 352 | -------------------------------------------------------------------------------- /src/app/modules/orders/actions.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const syncOrders = (store) => { 4 | const {commit} = store; 5 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 6 | let jobcontractregistry = getDealsRegistry(dealsRegistryAddress, account); 7 | if (store.rootState.auth.type === 'client') 8 | return jobcontractregistry.getMyOwnedDeals().then((jobIds) => { 9 | let jobsPromises = jobIds.map(jobId => getDeal(jobId, account).methods.getData().call()); 10 | return Promise.all(jobsPromises).then((jobResults) => { 11 | console.log("Raw job results: ", jobResults); 12 | let preparedOrders = jobResults.map((job, i) => { 13 | return { 14 | id: jobIds[i], 15 | offerId: job['0'], 16 | owner: job['1'], 17 | completed: job['2'], 18 | status: job['3'] 19 | } 20 | }); 21 | 22 | let offerIds = preparedOrders.map(order => order.offerId); 23 | let offerPromises = offerIds.map(offerId => getOffer(offerId, account).methods.getData().call()); 24 | let preparedOffers; 25 | return Promise.all(offerPromises).then((offersResults) => { 26 | console.log("Raw offers results: ", offersResults); 27 | preparedOffers = offersResults.map((offerData, i) => { 28 | return { 29 | id: offerIds[i], 30 | forwarderAddress: offerData['0'], 31 | details: offerData['1'], 32 | from: offerData['2'], 33 | to: offerData['3'], 34 | offerState: offerData['4'] 35 | } 36 | }); 37 | console.log("Prepared offers results: ", preparedOffers); 38 | return preparedOffers; 39 | }).then((offers) => { 40 | return preparedOrders.map((order) => { 41 | let offer = offers.filter(offer => offer.id === order.offerId)[0]; 42 | order.from = offer.from; 43 | order.to = offer.to; 44 | order.details = offer.details; 45 | order.forwarderAddress = offer.forwarderAddress; 46 | order.offerState = offer.offerState; 47 | return order; 48 | }); 49 | }).then((orders) => { 50 | let forwAddresses = orders.map(order => order.forwarderAddress); 51 | const {commit} = store; 52 | return axios.post('/auth/aboutkeys', { 53 | pubkeys: forwAddresses 54 | }).then((res) => { 55 | return orders.map((order) => { 56 | order.forwarderName = res.data.users.filter(owner => order.forwarderAddress === owner.pubkey)[0].id; 57 | return order; 58 | }); 59 | }); 60 | });; 61 | }); 62 | }); 63 | else 64 | return jobcontractregistry.getMySupplyingDeals().then((jobIds) => { 65 | let jobsPromises = jobIds.map(jobId => getDeal(jobId, account).methods.getData().call()); 66 | return Promise.all(jobsPromises).then((jobResults) => { 67 | console.log("Raw job results: ", jobResults); 68 | let preparedOrders = jobResults.map((job, i) => { 69 | return { 70 | id: jobIds[i], 71 | offerId: job['0'], 72 | owner: job['1'], 73 | completed: job['2'], 74 | status: job['3'] 75 | } 76 | }); 77 | let offerIds = preparedOrders.map(order => order.offerId); 78 | let offerPromises = offerIds.map(offerId => getOffer(offerId, account).methods.getData().call()); 79 | let preparedOffers; 80 | return Promise.all(offerPromises).then((offersResults) => { 81 | console.log("Raw offers results: ", offersResults); 82 | preparedOffers = offersResults.map((offerData, i) => { 83 | return { 84 | id: offerIds[i], 85 | forwarderAddress: offerData['0'], 86 | details: offerData['1'], 87 | from: offerData['2'], 88 | to: offerData['3'], 89 | offerState: offerData['4'] 90 | } 91 | }); 92 | return preparedOffers; 93 | }).then((offers) => { 94 | return preparedOrders.map((order) => { 95 | let offer = offers.filter(offer => offer.id === order.offerId)[0]; 96 | order.from = offer.from; 97 | order.to = offer.to; 98 | order.details = offer.details; 99 | order.forwarderAddress = offer.forwarderAddress; 100 | order.offerState = offer.offerState; 101 | return order; 102 | }); 103 | }).then((orders) => { 104 | let ownerAddresses = orders.map(order => order.owner); 105 | let ownersResults; 106 | const {commit} = store; 107 | return axios.post('/auth/aboutkeys', { 108 | pubkeys: ownerAddresses 109 | }).then((res) => { 110 | ownersResults = res.data.users; 111 | return orders.map((order) => { 112 | console.log(ownersResults.filter(owner => order.owner === owner.pubkey)[0]); 113 | order.ownerName = ownersResults.filter(owner => order.owner === owner.pubkey)[0].id; 114 | return order; 115 | }); 116 | }); 117 | }); 118 | }); 119 | }); 120 | }; 121 | 122 | export const closeOrder = (store, orderId) => { 123 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 124 | if (store.rootState.auth.type === 'client') 125 | return getDeal(orderId, account).confirm(); 126 | else 127 | return getDeal(orderId, account).compete(); 128 | }; -------------------------------------------------------------------------------- /src/app/modules/orders/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as actions from './actions'; 3 | import { defaultState, mutations } from './mutations'; 4 | 5 | const state = defaultState; 6 | 7 | export default { 8 | module: { 9 | actions, 10 | state, 11 | mutations 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/modules/orders/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_FILES, 3 | ADD_FILE 4 | } from './actions' 5 | 6 | export const defaultState = { 7 | files: [] 8 | } 9 | 10 | export const mutations = { 11 | [SET_FILES](state, payload) { 12 | state.files = payload.files; 13 | }, 14 | [ADD_FILE](state, payload) { 15 | state.files.push(payload.data); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/modules/orders/orders.vue: -------------------------------------------------------------------------------- 1 | 2 | 76 | 77 | 136 | 217 | -------------------------------------------------------------------------------- /src/app/modules/profile/actions.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/app/modules/profile/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as actions from './actions'; 3 | import { defaultState, mutations } from './mutations'; 4 | 5 | const state = defaultState; 6 | 7 | export default { 8 | module: { 9 | actions, 10 | state, 11 | mutations 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/modules/profile/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | SET_FILES, 3 | ADD_FILE 4 | } from './actions' 5 | 6 | export const defaultState = { 7 | files: [] 8 | } 9 | 10 | export const mutations = { 11 | [SET_FILES](state, payload) { 12 | state.files = payload.files; 13 | }, 14 | [ADD_FILE](state, payload) { 15 | state.files.push(payload.data); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/modules/profile/profile.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 46 | -------------------------------------------------------------------------------- /src/app/modules/search/actions.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | export const search = (store, {from, to}) => { 3 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 4 | let offersregistry = getOffersRegistry(offersRegistryAddress, account); 5 | return offersregistry.getByRoute(from, to).then(function (offerIds) { 6 | let offerPromises = offerIds.map(offerId => getOffer(offerId, account).methods.getData().call()); 7 | return Promise.all(offerPromises).then((offersResults) => { 8 | console.log("Raw search results: ", offersResults); 9 | let preparedOffers = offersResults.map((offerData, i) => { 10 | return { 11 | id: offerIds[i], 12 | forwarderAddress: offerData['0'], 13 | details: offerData['1'], 14 | from: offerData['2'], 15 | to: offerData['3'], 16 | offerState: offerData['4'] 17 | } 18 | }); 19 | console.log("Prepared search results: ", preparedOffers); 20 | return preparedOffers; 21 | }).then((options) => { 22 | let pubkeys = options.map(option => option.forwarderAddress); 23 | return axios.post('/auth/aboutkeys', { 24 | pubkeys: pubkeys 25 | }).then((res) => { 26 | console.log(res); 27 | return options.map((option) => { 28 | let forw = res.data.users.filter((user) => user.pubkey === option.forwarderAddress)[0]; 29 | if (forw) option.forwarderName = forw.id; 30 | return option; 31 | }); 32 | }); 33 | }); 34 | }); 35 | }; 36 | 37 | export const createOrder = (store, offerId) => { 38 | console.log(offerId); 39 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 40 | let jobcontractregistry = getDealsRegistry(dealsRegistryAddress, account); 41 | return jobcontractregistry.newDeal(offerId).then((res) => { 42 | console.log(res); 43 | }); 44 | }; 45 | 46 | export const getRandomOffers = (store, num) => { 47 | let account = web3.eth.accounts.privateKeyToAccount(store.rootState.auth.privateKey); 48 | let offersregistry = getOffersRegistry(offersRegistryAddress, account); 49 | return offersregistry.peek(num).then(function (offerIds) { 50 | let offerPromises = offerIds.map(offerId => getOffer(offerId, account).methods.getData().call()); 51 | return Promise.all(offerPromises).then((offersResults) => { 52 | console.log("Raw random search results: ", offersResults); 53 | let preparedOffers = offersResults.map((offerData, i) => { 54 | return { 55 | id: offerIds[i], 56 | forwarderAddress: offerData['0'], 57 | details: offerData['1'], 58 | from: offerData['2'], 59 | to: offerData['3'], 60 | offerState: offerData['4'] 61 | } 62 | }); 63 | console.log("Prepared random search results: ", preparedOffers); 64 | return preparedOffers; 65 | }).then((options) => { 66 | let pubkeys = options.map(option => option.forwarderAddress); 67 | return axios.post('/auth/aboutkeys', { 68 | pubkeys: pubkeys 69 | }).then((res) => { 70 | console.log(res); 71 | return options.map((option) => { 72 | let forw = res.data.users.filter((user) => user.pubkey === option.forwarderAddress)[0]; 73 | if (forw) option.forwarderName = forw.id; 74 | return option; 75 | }); 76 | }); 77 | }); 78 | }); 79 | } -------------------------------------------------------------------------------- /src/app/modules/search/index.js: -------------------------------------------------------------------------------- 1 | 2 | import * as actions from './actions'; 3 | import { defaultState, mutations } from './mutations'; 4 | 5 | const state = defaultState; 6 | 7 | export default { 8 | module: { 9 | actions, 10 | state, 11 | mutations 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/modules/search/mutations.js: -------------------------------------------------------------------------------- 1 | import { 2 | 3 | } from './actions' 4 | 5 | export const defaultState = { 6 | files: [] 7 | } 8 | 9 | export const mutations = { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/app/modules/search/search.vue: -------------------------------------------------------------------------------- 1 | 2 | 75 | 76 | 212 | 213 | 386 | -------------------------------------------------------------------------------- /src/app/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | import store from '../store' 5 | import {CHANGE_TOKEN, SET_KEY} from '../modules/auth/actions' 6 | 7 | import FormPage from '../modules/form/form.vue' 8 | import AuthPage from '../modules/auth/auth.vue' 9 | import Offers from '../modules/offers/offers.vue' 10 | import Orders from '../modules/orders/orders.vue' 11 | import Search from '../modules/search/search.vue' 12 | import Profile from '../modules/profile/profile.vue' 13 | 14 | import {getUserInfo} from '../modules/auth/actions.js' 15 | 16 | Vue.use(VueRouter); 17 | 18 | const Router = new VueRouter({ 19 | history: false, 20 | routes: [ 21 | { 22 | path: '/', 23 | name: 'start', 24 | redirect: {name: 'form-page'} 25 | }, 26 | { 27 | path: '/form', 28 | name: 'form-page', 29 | component: FormPage, 30 | children: [ 31 | { 32 | path: 'offers', 33 | name: 'offers', 34 | component: Offers 35 | }, 36 | { 37 | path: 'orders', 38 | name: 'orders', 39 | component: Orders 40 | }, 41 | { 42 | path: 'search', 43 | name: 'search', 44 | component: Search 45 | }, 46 | { 47 | path: 'profile', 48 | name: 'profile', 49 | component: Profile 50 | } 51 | ] 52 | }, 53 | { 54 | path: '/login', 55 | name: 'auth-page', 56 | component: AuthPage 57 | } 58 | ] 59 | }) 60 | 61 | Router.beforeEach((to, from, next) => { 62 | if (to.name === 'auth-page') 63 | next() 64 | const failure = () => next({ 65 | name: 'auth-page', 66 | query: {next: to.path} 67 | }) 68 | const token = window.localStorage.getItem('immla-front-token'); 69 | const key = window.localStorage.getItem('immla-network-key'); 70 | if (!token) { 71 | failure() 72 | return 73 | } 74 | getUserInfo().then((res) => { 75 | if (res.data.success) { 76 | next() 77 | store.commit(CHANGE_TOKEN, token) 78 | store.commit(SET_KEY, key); 79 | return 80 | } 81 | failure() 82 | }).catch(() => { 83 | failure() 84 | }) 85 | }) 86 | export default Router 87 | -------------------------------------------------------------------------------- /src/app/select/comp/Select.vue: -------------------------------------------------------------------------------- 1 | 319 | 320 | 373 | 374 | -------------------------------------------------------------------------------- /src/app/select/mixins/ajax.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | props: { 3 | /** 4 | * Toggles the adding of a 'loading' class to the main 5 | * .v-select wrapper. Useful to control UI state when 6 | * results are being processed through AJAX. 7 | */ 8 | loading: { 9 | type: Boolean, 10 | default: false 11 | }, 12 | 13 | /** 14 | * Accept a callback function that will be 15 | * run when the search text changes. 16 | * 17 | * loading() accepts a boolean value, and can 18 | * be used to toggle a loading class from 19 | * the onSearch callback. 20 | * 21 | * @param {search} String Current search text 22 | * @param {loading} Function(bool) Toggle loading class 23 | */ 24 | onSearch: { 25 | type: Function, 26 | default: function(search, loading){} 27 | } 28 | }, 29 | 30 | data() { 31 | return { 32 | mutableLoading: false 33 | } 34 | }, 35 | 36 | watch: { 37 | /** 38 | * If a callback & search text has been provided, 39 | * invoke the onSearch callback. 40 | */ 41 | search() { 42 | if (this.search.length > 0) { 43 | this.onSearch(this.search, this.toggleLoading) 44 | this.$emit('search', this.search, this.toggleLoading) 45 | } 46 | }, 47 | /** 48 | * Sync the loading prop with the internal 49 | * mutable loading value. 50 | * @param val 51 | */ 52 | loading(val) { 53 | this.mutableLoading = val 54 | } 55 | }, 56 | 57 | methods: { 58 | /** 59 | * Toggle this.loading. Optionally pass a boolean 60 | * value. If no value is provided, this.loading 61 | * will be set to the opposite of it's current value. 62 | * @param toggle Boolean 63 | * @returns {*} 64 | */ 65 | toggleLoading(toggle = null) { 66 | if (toggle == null) { 67 | return this.mutableLoading = !this.mutableLoading 68 | } 69 | return this.mutableLoading = toggle 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/app/select/mixins/index.js: -------------------------------------------------------------------------------- 1 | import ajax from './ajax' 2 | import pointer from './typeAheadPointer' 3 | import pointerScroll from './pointerScroll' 4 | 5 | export default { ajax, pointer, pointerScroll } 6 | -------------------------------------------------------------------------------- /src/app/select/mixins/pointerScroll.js: -------------------------------------------------------------------------------- 1 | // flow 2 | 3 | module.exports = { 4 | watch: { 5 | typeAheadPointer() { 6 | this.maybeAdjustScroll() 7 | } 8 | }, 9 | 10 | methods: { 11 | /** 12 | * Adjust the scroll position of the dropdown list 13 | * if the current pointer is outside of the 14 | * overflow bounds. 15 | * @returns {*} 16 | */ 17 | maybeAdjustScroll() { 18 | let pixelsToPointerTop = this.pixelsToPointerTop() 19 | let pixelsToPointerBottom = this.pixelsToPointerBottom() 20 | 21 | if ( pixelsToPointerTop <= this.viewport().top) { 22 | return this.scrollTo( pixelsToPointerTop ) 23 | } else if (pixelsToPointerBottom >= this.viewport().bottom) { 24 | return this.scrollTo( this.viewport().top + this.pointerHeight() ) 25 | } 26 | }, 27 | 28 | /** 29 | * The distance in pixels from the top of the dropdown 30 | * list to the top of the current pointer element. 31 | * @returns {number} 32 | */ 33 | pixelsToPointerTop() { 34 | let pixelsToPointerTop = 0 35 | if( this.$refs.dropdownMenu ) { 36 | for (let i = 0; i < this.typeAheadPointer; i++) { 37 | pixelsToPointerTop += this.$refs.dropdownMenu.children[i].offsetHeight 38 | } 39 | } 40 | return pixelsToPointerTop 41 | }, 42 | 43 | /** 44 | * The distance in pixels from the top of the dropdown 45 | * list to the bottom of the current pointer element. 46 | * @returns {*} 47 | */ 48 | pixelsToPointerBottom() { 49 | return this.pixelsToPointerTop() + this.pointerHeight() 50 | }, 51 | 52 | /** 53 | * The offsetHeight of the current pointer element. 54 | * @returns {number} 55 | */ 56 | pointerHeight() { 57 | let element = this.$refs.dropdownMenu ? this.$refs.dropdownMenu.children[this.typeAheadPointer] : false 58 | return element ? element.offsetHeight : 0 59 | }, 60 | 61 | /** 62 | * The currently viewable portion of the dropdownMenu. 63 | * @returns {{top: (string|*|number), bottom: *}} 64 | */ 65 | viewport() { 66 | return { 67 | top: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop: 0, 68 | bottom: this.$refs.dropdownMenu ? this.$refs.dropdownMenu.offsetHeight + this.$refs.dropdownMenu.scrollTop : 0 69 | } 70 | }, 71 | 72 | /** 73 | * Scroll the dropdownMenu to a given position. 74 | * @param position 75 | * @returns {*} 76 | */ 77 | scrollTo(position) { 78 | return this.$refs.dropdownMenu ? this.$refs.dropdownMenu.scrollTop = position : null 79 | }, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/app/select/mixins/typeAheadPointer.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data() { 3 | return { 4 | typeAheadPointer: -1 5 | } 6 | }, 7 | 8 | watch: { 9 | filteredOptions() { 10 | this.typeAheadPointer = 0 11 | } 12 | }, 13 | 14 | methods: { 15 | /** 16 | * Move the typeAheadPointer visually up the list by 17 | * subtracting the current index by one. 18 | * @return {void} 19 | */ 20 | typeAheadUp() { 21 | if (this.typeAheadPointer > 0) { 22 | this.typeAheadPointer-- 23 | if( this.maybeAdjustScroll ) { 24 | this.maybeAdjustScroll() 25 | } 26 | } 27 | }, 28 | 29 | /** 30 | * Move the typeAheadPointer visually down the list by 31 | * adding the current index by one. 32 | * @return {void} 33 | */ 34 | typeAheadDown() { 35 | if (this.typeAheadPointer < this.filteredOptions.length - 1) { 36 | this.typeAheadPointer++ 37 | if( this.maybeAdjustScroll ) { 38 | this.maybeAdjustScroll() 39 | } 40 | } 41 | }, 42 | 43 | /** 44 | * Select the option at the current typeAheadPointer position. 45 | * Optionally clear the search input on selection. 46 | * @return {void} 47 | */ 48 | typeAheadSelect() { 49 | if( this.filteredOptions[ this.typeAheadPointer ] ) { 50 | this.select( this.filteredOptions[ this.typeAheadPointer ] ); 51 | } else if (this.taggable && this.search.length){ 52 | this.select(this.search) 53 | } 54 | 55 | if( this.clearSearchOnSelect ) { 56 | this.search = ""; 57 | } 58 | }, 59 | } 60 | } -------------------------------------------------------------------------------- /src/app/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import auth from '../modules/auth' 5 | import offers from '../modules/offers' 6 | import orders from '../modules/orders' 7 | import search from '../modules/search' 8 | import profile from '../modules/profile' 9 | 10 | Vue.use(Vuex) 11 | 12 | const mutations = {} 13 | const state = {} 14 | const components = { 15 | auth, 16 | offers, 17 | orders, 18 | search, 19 | profile 20 | } 21 | const modules = {} 22 | const getters = {} 23 | const actions = {} 24 | 25 | Object.keys(components).forEach(key => { 26 | if (!components.hasOwnProperty(key)) 27 | return 28 | if (components[key].module) 29 | modules[key] = components[key].module 30 | if (components[key].getters) { 31 | Object.keys(components[key].getters).forEach(getter => { 32 | if (!components[key].getters.hasOwnProperty(getter)) 33 | return 34 | if (getters[getter]) 35 | throw `Getter ${getter} already exist` 36 | getters[getter] = components[key].getters[getter] 37 | }) 38 | } 39 | }) 40 | 41 | export default new Vuex.Store({ 42 | state, 43 | mutations, 44 | modules, 45 | getters, 46 | actions 47 | }) 48 | -------------------------------------------------------------------------------- /src/app/uikit/application.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; 6 | overflow: hidden; 7 | 8 | #react-root { 9 | height: 100%; 10 | } 11 | } 12 | .booster { 13 | -webkit-backface-visibility: hidden; 14 | -moz-backface-visibility: hidden; 15 | -ms-backface-visibility: hidden; 16 | backface-visibility: hidden; 17 | 18 | -webkit-perspective: 1000; 19 | -moz-perspective: 1000; 20 | -ms-perspective: 1000; 21 | perspective: 1000; 22 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import store from './app/store'; 3 | import Router from './app/router'; 4 | import App from './App.vue'; 5 | import BootstrapVue from 'bootstrap-vue'; 6 | import 'bootstrap-vue/dist/bootstrap-vue.css'; 7 | import 'bootstrap/dist/css/bootstrap.css'; 8 | import vSelect from './app/select/comp/Select.vue'; 9 | Vue.component('v-select', vSelect); 10 | 11 | 12 | let ethRpcUrl = "http://91.239.26.64:18545"; 13 | window.web3 = new Web3(new Web3.providers.HttpProvider(ethRpcUrl)); 14 | 15 | window.Vue = Vue; 16 | // Vue.prototype.$mysock = new sock('http://localhost:8080'); 17 | // var vueUI = require('vue-ui'); 18 | 19 | // Vue.use(vueUI); 20 | 21 | // import VueSocketio from 'vue-socket.io'; 22 | // Vue.use(VueSocketio, 'http://localhost:8000'); 23 | 24 | new Vue({ 25 | el: '#app', 26 | store, 27 | router: Router, 28 | render: h => h(App) 29 | }); 30 | 31 | Vue.use(BootstrapVue); 32 | 33 | window.offersRegistryAddress = "0x627f558d7f1a4c461e7e0126166395b9e63403db"; 34 | window.dealsRegistryAddress = "0xf8609f2bedd2f263c28d8084e5c27eab0dfdaeb6"; 35 | window.gasLimit = 3000000; 36 | 37 | window.getOffersRegistry = (address, account) => { 38 | var contract = new web3.eth.Contract( 39 | [{"constant":true,"inputs":[],"name":"getOffers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_supplier","type":"address"}],"name":"getBySupplier","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_maxDepth","type":"uint256"}],"name":"peek","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_from","type":"string"},{"name":"_to","type":"string"}],"name":"getByRoute","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"peekTop","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MAX_DEPTH","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_details","type":"string"},{"name":"_from","type":"string"},{"name":"_to","type":"string"}],"name":"newOffer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_offer","type":"address"}],"name":"LogNewOffer","type":"event"}] 40 | , address, {"from": account.address}); 41 | 42 | contract.getOffers = function () { 43 | return this.methods.getOffers().call(); 44 | } 45 | 46 | contract.getByRoute = function (_from, _to) { 47 | return this.methods.getByRoute(_from,_to).call(); 48 | } 49 | 50 | contract.peek = function (depth) { 51 | return this.methods.peek(depth).call(); 52 | } 53 | 54 | contract.newOffer = function (_details, _from, _to) { 55 | return account.signTransaction({"to": contract._address, "data":this.methods.newOffer(_details, _from, _to).encodeABI(), "gas":gasLimit}).then(function (res){ 56 | return web3.eth.sendSignedTransaction(res.rawTransaction); 57 | }) 58 | } 59 | 60 | return contract; 61 | } 62 | 63 | window.getOffer = (address, account) => { 64 | var contract = new web3.eth.Contract( 65 | [{"constant":true,"inputs":[],"name":"to","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isRunning","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getData","outputs":[{"name":"","type":"address"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"string"},{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_details","type":"string"}],"name":"update","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"details","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_manager","type":"address"}],"name":"changeManager","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"recall","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getManager","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"from","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_state","type":"bytes1"}],"name":"switchTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_supplier","type":"address"},{"name":"_manager","type":"address"},{"name":"_details","type":"string"},{"name":"_from","type":"string"},{"name":"_to","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_who","type":"address"},{"indexed":false,"name":"_what","type":"string"}],"name":"LogUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"bytes1"},{"indexed":false,"name":"_to","type":"bytes1"}],"name":"LogChangeState","type":"event"}] 66 | , address, {"from": account.address}); 67 | 68 | contract.switchTo = function (_state) { 69 | return account.signTransaction({"to": contract._address, "data":this.methods.switchTo(_state).encodeABI(), "gas":gasLimit}).then(function (res){ 70 | return web3.eth.sendSignedTransaction(res.rawTransaction); 71 | }) 72 | } 73 | 74 | contract.close = function () { 75 | return account.signTransaction({"to": contract._address, "data":this.methods.recall().encodeABI(), "gas":gasLimit}).then(function (res){ 76 | return web3.eth.sendSignedTransaction(res.rawTransaction); 77 | }) 78 | } 79 | 80 | return contract; 81 | }; 82 | 83 | 84 | window.getDealsRegistry = (address, account) => { 85 | var contract = new web3.eth.Contract( 86 | [{"constant":true,"inputs":[{"name":"_pos","type":"uint256"}],"name":"getMyOwnedDeal","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMySupplyingDealCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_pos","type":"uint256"}],"name":"getMySupplyingDeal","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMyOwnedDealCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_offer","type":"address"}],"name":"newDeal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getMySupplyingDeals","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMyOwnedDeals","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] 87 | , address, {"from": account.address}); 88 | 89 | contract.getMySupplyingDeals = function () { 90 | return this.methods.getMySupplyingDeals().call(); 91 | } 92 | 93 | contract.getMyOwnedDeals = function () { 94 | return this.methods.getMyOwnedDeals().call(); 95 | } 96 | 97 | contract.newDeal = function (_offerAddress) { 98 | return account.signTransaction({"to": contract._address, "data":this.methods.newDeal(_offerAddress).encodeABI(), "gas":gasLimit}).then(function (res){ 99 | return web3.eth.sendSignedTransaction(res.rawTransaction); 100 | }) 101 | } 102 | 103 | return contract; 104 | } 105 | 106 | window.getDeal = (address, account) => { 107 | var contract = new web3.eth.Contract( 108 | [{"constant":true,"inputs":[],"name":"status","outputs":[{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isRunning","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"customer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getData","outputs":[{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"bool"},{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRecallClaim","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"compete","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"confirm","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"CLIENT","outputs":[{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"offer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_recallClaim","type":"address"}],"name":"recall","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_state","type":"bytes1"}],"name":"switchTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"SUPPLIER","outputs":[{"name":"","type":"bytes1"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isCompleted","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_customer","type":"address"},{"name":"_offer","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_from","type":"bytes1"},{"indexed":false,"name":"_to","type":"bytes1"}],"name":"LogChangeState","type":"event"}] 109 | , address, {"from": account.address}); 110 | 111 | contract.compete = function () { 112 | return account.signTransaction({"to": contract._address, "data":this.methods.compete().encodeABI(), "gas":gasLimit}).then(function (res){ 113 | return web3.eth.sendSignedTransaction(res.rawTransaction); 114 | }) 115 | } 116 | 117 | contract.confirm = function () { 118 | return account.signTransaction({"to": contract._address, "data":this.methods.confirm().encodeABI(), "gas":gasLimit}).then(function (res){ 119 | return web3.eth.sendSignedTransaction(res.rawTransaction); 120 | }) 121 | } 122 | 123 | return contract; 124 | } 125 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapps-community/dapp-logistic-sample/058a3f6763fa3681d387b33bdca71e801e6c765f/static/.gitkeep -------------------------------------------------------------------------------- /static/immla-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapps-community/dapp-logistic-sample/058a3f6763fa3681d387b33bdca71e801e6c765f/static/immla-bg.png -------------------------------------------------------------------------------- /static/logo-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapps-community/dapp-logistic-sample/058a3f6763fa3681d387b33bdca71e801e6c765f/static/logo-mini.png -------------------------------------------------------------------------------- /static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dapps-community/dapp-logistic-sample/058a3f6763fa3681d387b33bdca71e801e6c765f/static/logo.png -------------------------------------------------------------------------------- /test/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count 11 | this.expected = count 12 | this.pass = function (val) { 13 | return val === this.expected 14 | } 15 | this.value = function (res) { 16 | return res.value 17 | } 18 | this.command = function (cb) { 19 | var self = this 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length 22 | }, [selector], function (res) { 23 | cb.call(self, res) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register') 2 | var config = require('../../config') 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing' 3 | var server = require('../../build/dev-server.js') 4 | 5 | server.ready.then(() => { 6 | // 2. run the nightwatch test suite against it 7 | // to run in additional browsers: 8 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 9 | // 2. add it to the --env flag below 10 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 11 | // For more information on Nightwatch's config file, see 12 | // http://nightwatchjs.org/guide#settings-file 13 | var opts = process.argv.slice(2) 14 | if (opts.indexOf('--config') === -1) { 15 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']) 16 | } 17 | if (opts.indexOf('--env') === -1) { 18 | opts = opts.concat(['--env', 'chrome']) 19 | } 20 | 21 | var spawn = require('cross-spawn') 22 | var runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }) 23 | 24 | runner.on('exit', function (code) { 25 | server.close() 26 | process.exit(code) 27 | }) 28 | 29 | runner.on('error', function (err) { 30 | server.close() 31 | throw err 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "expect": true, 7 | "sinon": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.config.productionTip = false 4 | 5 | // require all test files (files that ends with .spec.js) 6 | const testsContext = require.context('./specs', true, /\.spec$/) 7 | testsContext.keys().forEach(testsContext) 8 | 9 | // require all src files except main.js for coverage. 10 | // you can also change this to match only the subset of files that 11 | // you want coverage for. 12 | const srcContext = require.context('../../src', true, /^\.\/(?!main(\.js)?$)/) 13 | srcContext.keys().forEach(srcContext) 14 | -------------------------------------------------------------------------------- /test/unit/karma.conf.js: -------------------------------------------------------------------------------- 1 | // This is a karma config file. For more details see 2 | // http://karma-runner.github.io/0.13/config/configuration-file.html 3 | // we are also using it with karma-webpack 4 | // https://github.com/webpack/karma-webpack 5 | 6 | var webpackConfig = require('../../build/webpack.test.conf') 7 | 8 | module.exports = function (config) { 9 | config.set({ 10 | // to run in additional browsers: 11 | // 1. install corresponding karma launcher 12 | // http://karma-runner.github.io/0.13/config/browsers.html 13 | // 2. add it to the `browsers` array below. 14 | browsers: ['PhantomJS'], 15 | frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], 16 | reporters: ['spec', 'coverage'], 17 | files: ['./index.js'], 18 | preprocessors: { 19 | './index.js': ['webpack', 'sourcemap'] 20 | }, 21 | webpack: webpackConfig, 22 | webpackMiddleware: { 23 | noInfo: true 24 | }, 25 | coverageReporter: { 26 | dir: './coverage', 27 | reporters: [ 28 | { type: 'lcov', subdir: '.' }, 29 | { type: 'text-summary' } 30 | ] 31 | } 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /test/unit/specs/Hello.spec.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Hello from '@/components/Hello' 3 | 4 | describe('Hello.vue', () => { 5 | it('should render correct contents', () => { 6 | const Constructor = Vue.extend(Hello) 7 | const vm = new Constructor().$mount() 8 | expect(vm.$el.querySelector('.hello h1').textContent) 9 | .to.equal('Welcome to Your Vue.js App') 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /usersStorage.js: -------------------------------------------------------------------------------- 1 | const mongo = require('./mongo'); 2 | 3 | module.exports.registerUser = (user) => { 4 | console.log(user); 5 | }; 6 | 7 | module.exports.saveUser = (user) => { 8 | return mongo.User.findOne({"id": user.id}).exec().then((answer) => { 9 | // console.log(answer); 10 | if (answer === null) { 11 | console.log('registering new user'); 12 | user.token = Math.random().toString(36).slice(2); 13 | const userModel = new mongo.User(user); 14 | userModel.save(); 15 | return {success: true, token: user.token}; 16 | } 17 | else { 18 | console.log('already exists'); 19 | return {success: false, message: "already exists"}; 20 | } 21 | }); 22 | }; 23 | 24 | module.exports.getUsersByPubkeys = (pubkeys) => { 25 | return mongo.User.find({pubkey: {$in: pubkeys}}).exec(); 26 | }; 27 | 28 | 29 | module.exports.getUserByToken = (token) => { 30 | return mongo.User.findOne({"token": token}).exec(); 31 | }; 32 | 33 | module.exports.getUserById = (userId) => { 34 | return mongo.User.findOne({"id": userId}).exec(); 35 | }; 36 | 37 | module.exports.login = (userId, password) => { 38 | return mongo.User.findOne({"id": userId, "password": password}).exec().then((answer) => { 39 | if(answer === null) { 40 | console.log("User not found"); 41 | return {success: false, message: 'Wrong username/password'}; 42 | } 43 | else { 44 | console.log('Found user'); 45 | return {success: true, token: answer.token} 46 | } 47 | }); 48 | }; 49 | 50 | 51 | --------------------------------------------------------------------------------