├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── blockchain ├── .babelrc ├── contracts │ ├── EtherAuth.sol │ └── Migrations.sol ├── migrations │ ├── 01_migrations.js │ └── 02_etherauth.js ├── package-lock.json ├── package.json ├── rinkeby │ ├── EtherAuth.v1.address.txt │ └── contracts_EtherAuth_sol_EtherAuth.abi ├── test │ └── 01-etherauth.js ├── truffle-config.js └── truffle.js ├── example-auth-agent ├── .gitignore ├── README.md ├── config │ ├── .env.default │ ├── config.js │ ├── tsconfig.json │ └── tslint.json ├── package-lock.json ├── package.json ├── src │ ├── api │ │ ├── api.js │ │ ├── apiMock.js │ │ └── apiRequests.js │ ├── app │ │ ├── App.jsx │ │ ├── App.less │ │ ├── AppActions.js │ │ ├── AppContainer.js │ │ ├── AppReducer.js │ │ ├── admin │ │ │ ├── Admin.jsx │ │ │ └── Admin.less │ │ ├── common │ │ │ ├── loader.svg │ │ │ └── loader2.svg │ │ ├── login-form │ │ │ ├── LoginForm.jsx │ │ │ └── LoginForm.less │ │ ├── main │ │ │ ├── Main.jsx │ │ │ └── Main.less │ │ ├── metamask │ │ │ ├── Metamask.jsx │ │ │ └── Metamask.less │ │ ├── mining │ │ │ ├── Mining.jsx │ │ │ └── Mining.less │ │ └── register │ │ │ ├── Register.jsx │ │ │ └── Register.less │ ├── assets │ │ └── img │ │ │ └── metamask.svg │ ├── constants │ │ └── constants.js │ ├── helpers │ │ ├── detect-device.js │ │ ├── eth.js │ │ └── utils.js │ ├── index.html │ ├── index.js │ ├── index.less │ ├── store │ │ └── store.js │ └── styles │ │ ├── base.less │ │ ├── components.less │ │ ├── fonts.less │ │ ├── index.less │ │ ├── levels.less │ │ ├── mixins.less │ │ ├── normalize.less │ │ └── variables.less └── webpack.config.js └── front ├── .gitignore ├── README.md ├── config ├── .env.default ├── config.js ├── tsconfig.json └── tslint.json ├── package-lock.json ├── package.json ├── src ├── api │ ├── api.js │ ├── apiMock.js │ └── apiRequests.js ├── app │ ├── App.jsx │ ├── App.less │ ├── AppActions.js │ ├── AppContainer.js │ ├── AppReducer.js │ ├── admin │ │ ├── Admin.jsx │ │ └── Admin.less │ ├── common │ │ ├── loader.svg │ │ └── loader2.svg │ ├── main │ │ ├── Main.jsx │ │ └── Main.less │ ├── metamask │ │ ├── Metamask.jsx │ │ └── Metamask.less │ ├── mining │ │ ├── Mining.jsx │ │ └── Mining.less │ └── register │ │ ├── Register.jsx │ │ └── Register.less ├── assets │ └── img │ │ └── metamask.svg ├── constants │ └── constants.js ├── helpers │ ├── detect-device.js │ ├── eth.js │ └── utils.js ├── index.html ├── index.js ├── index.less ├── store │ └── store.js └── styles │ ├── base.less │ ├── components.less │ ├── fonts.less │ ├── index.less │ ├── levels.less │ ├── mixins.less │ ├── normalize.less │ └── variables.less └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | build 63 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/openzeppelin-solidity"] 2 | path = submodules/openzeppelin-solidity 3 | url = https://github.com/OpenZeppelin/openzeppelin-solidity 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MixBytes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Etherauth 2 | Ethereum smart-contract based engine for user authentication and keys management 3 | 4 | Etherauth is a simple and powerful authentication mechanism, allowing sites not to store critical security info about user, such as password hashes, that allows users to create their own identities in Etehreum blockchain, manage and use them for authentication purposes on any site, having only the read-only access to Ethereum blockchain 5 | 6 | Authentication is performed on the client-side, using Ethereum's web3.js. User indentity is controlled by user's master account, that is used for children accounts recovery or punishment. 7 | 8 | Public keys of accounts and implementation of all management logic are in Ethereum smart-contract, client-side code is a standalone static js file. 9 | 10 | ## Install 11 | 12 | 1) install Metamask extension or use any other web3 provider 13 | 2) open static file with auth js inside, login, manage 14 | 15 | -------------------------------------------------------------------------------- /blockchain/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "env", 4 | "react", 5 | "stage-0" 6 | ], 7 | "plugins": [ 8 | "transform-class-properties", 9 | "transform-decorators", 10 | "transform-react-constant-elements", 11 | "transform-react-inline-elements" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /blockchain/contracts/EtherAuth.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract EtherAuth { 4 | mapping (string => address) authAddr ; 5 | mapping (string => address) recoveryAddr ; 6 | 7 | event Create(string login); 8 | event AuthChange(string login, address from, address to); 9 | event RecoveryChange(string login, address from, address to); 10 | event Drop(string login, address by); 11 | 12 | function createAccount(string _login) public { 13 | require(bytes(_login).length <= 32); 14 | require(bytes(_login).length > 2); 15 | require(authAddr[_login] == address(0)); 16 | authAddr[_login] = msg.sender; 17 | recoveryAddr[_login] = msg.sender; 18 | //emit Create(bytes32ToString(_login)); 19 | emit Create(_login); 20 | } 21 | 22 | function authAddress(string _login) view public returns (address){ 23 | return authAddr[_login]; 24 | } 25 | 26 | function setAuthAddress(string _login, address _addr) public { 27 | require(authAddr[_login] == msg.sender || recoveryAddr[_login] == msg.sender); 28 | emit AuthChange(_login, authAddr[_login], _addr); 29 | authAddr[_login] = _addr; 30 | } 31 | 32 | function recoveryAddress(string _login) view public returns (address){ 33 | return recoveryAddr[_login]; 34 | } 35 | 36 | function setRecoveryAddress(string _login, address _addr) public { 37 | require(recoveryAddr[_login] == msg.sender); 38 | emit RecoveryChange(_login, authAddr[_login], _addr); 39 | recoveryAddr[_login] = _addr; 40 | } 41 | 42 | function dropAccount(string _login) public { 43 | require(recoveryAddr[_login] == msg.sender); 44 | delete authAddr[_login]; 45 | delete recoveryAddr[_login]; 46 | emit Drop(_login, msg.sender); 47 | } 48 | 49 | //function bytes32ToString (bytes32 data) pure internal returns (string) { 50 | // uint256 len = 0; 51 | // uint256 j; 52 | // byte char; 53 | // for (j=0; j<32; j++) { 54 | // char = byte(bytes32(uint(data) * 2 ** (8 * j))); 55 | // if (char == 0) { break; } 56 | // len ++; 57 | // } 58 | // bytes memory bytesString = new bytes(len); 59 | // for (j=0; j< len; j++) { 60 | // char = byte(bytes32(uint(data) * 2 ** (8 * j))); 61 | // bytesString[j] = char; 62 | // } 63 | // return string(bytesString); 64 | //} 65 | 66 | function signerAddress(bytes32 data, uint8 v, bytes32 r, bytes32 s) pure public returns (address) { 67 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 68 | bytes32 prefixed = keccak256(prefix, data); 69 | return ecrecover(prefixed, v, r, s); 70 | } 71 | function signerAddressRaw(bytes32 data, uint8 v, bytes32 r, bytes32 s) pure public returns (address) { 72 | return ecrecover(data, v, r, s); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /blockchain/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract Migrations { 4 | address public owner; 5 | 6 | // A function with the signature `last_completed_migration()`, returning a uint, is required. 7 | uint public last_completed_migration; 8 | 9 | modifier restricted() { 10 | if (msg.sender == owner) _; 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | // A function with the signature `setCompleted(uint)` is required. 18 | function setCompleted(uint completed) restricted public { 19 | last_completed_migration = completed; 20 | } 21 | 22 | function upgrade(address new_address) restricted public { 23 | Migrations upgraded = Migrations(new_address); 24 | upgraded.setCompleted(last_completed_migration); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /blockchain/migrations/01_migrations.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | // Deploy the Migrations contract as our only task 5 | deployer.deploy(Migrations); 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /blockchain/migrations/02_etherauth.js: -------------------------------------------------------------------------------- 1 | const Instance = artifacts.require("EtherAuth"); 2 | 3 | module.exports = function(deployer) { 4 | // deployment steps 5 | deployer.deploy(Instance); 6 | }; 7 | 8 | -------------------------------------------------------------------------------- /blockchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etherauth", 3 | "version": "1.0.0", 4 | "description": "EtherAuth - OAuth-like authentication with Ethereum network", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/truffle test", 8 | "develop": "./node_modules/.bin/truffle develop" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/mixbytes/etherauth.git" 13 | }, 14 | "keywords": [ 15 | "solidity", 16 | "ethereum" 17 | ], 18 | "author": "mxpaul", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/mixbytes/etherauth/issues" 22 | }, 23 | "homepage": "https://github.com/mixbytes/etherauth#readme", 24 | "devDependencies": { 25 | "babel-env": "^2.4.1", 26 | "babel-polyfill": "^6.26.0", 27 | "babel-preset-es2015": "^6.24.1", 28 | "babel-register": "^6.26.0", 29 | "chai": "^4.1.2", 30 | "chai-as-promised": "^7.1.1", 31 | "chai-bignumber": "^2.0.2", 32 | "solc": "^0.4.24", 33 | "truffle": "^4.1.8" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /blockchain/rinkeby/EtherAuth.v1.address.txt: -------------------------------------------------------------------------------- 1 | 0xe978736ad2b1a5663dbb11072de7bfeb7d05ed99 2 | -------------------------------------------------------------------------------- /blockchain/rinkeby/contracts_EtherAuth_sol_EtherAuth.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"_login","type":"bytes32"}],"name":"recoveryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_login","type":"bytes32"}],"name":"authAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"bytes32"},{"name":"_addr","type":"address"}],"name":"setRecoveryAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"bytes32"},{"name":"_addr","type":"address"}],"name":"setAuthAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"bytes32"}],"name":"dropAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"bytes32"}],"name":"createAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /blockchain/test/01-etherauth.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import expectThrow from '../../submodules/openzeppelin-solidity/test/helpers/expectThrow'; 4 | import expectEvent from '../../submodules/openzeppelin-solidity/test/helpers/expectEvent'; 5 | 6 | const BigNumber = web3.BigNumber; 7 | const chai =require('chai'); 8 | chai.use(require('chai-bignumber')(BigNumber)); 9 | chai.use(require('chai-as-promised')); // Order is important 10 | chai.should(); 11 | 12 | const EtherAuth = artifacts.require("EtherAuth"); 13 | 14 | contract('EtherAuth', function(accounts) { 15 | const acc = {owner: accounts[0], user: accounts[1], user2: accounts[2], user3: accounts[3], user4: accounts[4], anyone: accounts[9]}; 16 | const login = "login123"; 17 | const login2 = "login456"; 18 | const zero_address = "0x0000000000000000000000000000000000000000"; 19 | 20 | beforeEach(async function () { 21 | this.inst = await EtherAuth.new({from: acc.owner}); 22 | }); 23 | 24 | 25 | it('should return zero auth address for non-existing user', async function() { 26 | const storedAuthKey = await this.inst.authAddress(login, {from: acc.anyone}); 27 | storedAuthKey.should.be.equal(zero_address); 28 | }); 29 | 30 | it('should be able to create new account and get its authAddress', async function() { 31 | await this.inst.createAccount(login, {from: acc.user,}); 32 | const storedAuthKey = await this.inst.authAddress(login, {from: acc.anyone}); 33 | storedAuthKey.should.be.equal(acc.user); 34 | }); 35 | 36 | it('should fail to re-register existing account', async function() { 37 | await this.inst.createAccount(login, {from: acc.user,}); 38 | await expectThrow(this.inst.createAccount(login, {from: acc.user,})); 39 | }); 40 | 41 | it('should fail if login length > 32 bytes', async function() { 42 | const notToLongLogin = "11111111112222222222333333333344"; 43 | const toLongLogin = "111111111122222222223333333333444"; 44 | await this.inst.createAccount(notToLongLogin, {from: acc.user,}); 45 | await expectThrow(this.inst.createAccount(toLongLogin, {from: acc.user,})); 46 | }); 47 | 48 | it('should fail if login length < 2 bytes', async function() { 49 | const notToShortLogin = "111"; 50 | const toShortLogin = "11"; 51 | await this.inst.createAccount(notToShortLogin, {from: acc.user,}); 52 | await expectThrow(this.inst.createAccount(toShortLogin, {from: acc.user,})); 53 | }); 54 | 55 | it('should initially set recoveryAddress equal to authAddress', async function() { 56 | await this.inst.createAccount(login, {from: acc.user,}); 57 | const storedAuthAddress = await this.inst.authAddress(login, {from: acc.anyone}); 58 | const storedRecoveryAddress = await this.inst.recoveryAddress(login, {from: acc.anyone}); 59 | storedRecoveryAddress.should.be.equal(storedAuthAddress); 60 | }); 61 | 62 | it('should allow to change recoveryAddress', async function() { 63 | await this.inst.createAccount(login, {from: acc.user,}); 64 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 65 | const storedRecoveryAddress = await this.inst.recoveryAddress(login, {from: acc.anyone}); 66 | storedRecoveryAddress.should.be.equal(acc.user2); 67 | }); 68 | 69 | it('should not allow auth address to change recovery address', async function() { 70 | await this.inst.createAccount(login, {from: acc.user,}); 71 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 72 | await expectThrow(this.inst.setRecoveryAddress(login, acc.anyone, {from: acc.user})); 73 | }); 74 | 75 | it('should allow to change auth address using auth address', async function() { 76 | await this.inst.createAccount(login, {from: acc.user,}); 77 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 78 | 79 | await this.inst.setAuthAddress(login, acc.user2, {from: acc.user}); 80 | const storedAuthAddress = await this.inst.authAddress(login, {from: acc.anyone}); 81 | storedAuthAddress.should.be.equal(acc.user2); 82 | }); 83 | 84 | it('should allow to change auth address using recovery address', async function() { 85 | await this.inst.createAccount(login, {from: acc.user,}); 86 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 87 | 88 | await this.inst.setAuthAddress(login, acc.user2, {from: acc.user2}); 89 | const storedAuthAddress = await this.inst.authAddress(login, {from: acc.anyone}); 90 | storedAuthAddress.should.be.equal(acc.user2); 91 | }); 92 | 93 | it('should not allow other accounts to change auth address', async function() { 94 | await this.inst.createAccount(login, {from: acc.user,}); 95 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 96 | 97 | await expectThrow(this.inst.setAuthAddress(login, acc.anyone, {from: acc.anyone})); 98 | }); 99 | 100 | it('should allow recovery address to delete account', async function() { 101 | await this.inst.createAccount(login, {from: acc.user,}); 102 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 103 | 104 | await this.inst.dropAccount(login, {from: acc.user2}); 105 | const storedAuthAddress = await this.inst.authAddress(login, {from: acc.anyone}); 106 | storedAuthAddress.should.be.equal(zero_address); 107 | const storedRecoveryAddress = await this.inst.recoveryAddress(login, {from: acc.anyone}); 108 | storedRecoveryAddress.should.be.equal(zero_address); 109 | }); 110 | 111 | it('should not allow anyone to drop account', async function() { 112 | await this.inst.createAccount(login, {from: acc.user,}); 113 | await expectThrow(this.inst.dropAccount(login, {from: acc.anyone})); 114 | }); 115 | 116 | it('should not allow auth address to drop account', async function() { 117 | await this.inst.createAccount(login, {from: acc.user,}); 118 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 119 | 120 | await expectThrow(this.inst.dropAccount(login, {from: acc.user})); 121 | }); 122 | 123 | it('should allow create multiple accounts with different ', async function() { 124 | await this.inst.createAccount(login, {from: acc.user,}); 125 | await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 126 | await this.inst.createAccount(login2, {from: acc.user3,}); 127 | await this.inst.setRecoveryAddress(login2, acc.user4, {from: acc.user3}); 128 | 129 | const auth1 = await this.inst.authAddress(login, {from: acc.anyone}); 130 | auth1.should.be.equal(acc.user); 131 | const recov1 = await this.inst.recoveryAddress(login, {from: acc.anyone}); 132 | recov1.should.be.equal(acc.user2); 133 | 134 | const auth2 = await this.inst.authAddress(login2, {from: acc.anyone}); 135 | auth2.should.be.equal(acc.user3); 136 | const recov2 = await this.inst.recoveryAddress(login2, {from: acc.anyone}); 137 | recov2.should.be.equal(acc.user4); 138 | }); 139 | 140 | it('should send event on create', async function() { 141 | const trans = await this.inst.createAccount(login, {from: acc.user,}); 142 | const createEvent = await expectEvent.inTransaction(trans, "Create"); 143 | //console.dir(createEvent); 144 | createEvent.should.have.deep.property('args', {login: login}) 145 | }); 146 | 147 | it('should send event on change auth address', async function() { 148 | await this.inst.createAccount(login, {from: acc.user,}); 149 | const trans = await this.inst.setAuthAddress(login, acc.user2, {from: acc.user}); 150 | const Event = await expectEvent.inTransaction(trans, "AuthChange"); 151 | Event.should.have.deep.property('args', {login: login, from: acc.user, to: acc.user2}) 152 | }); 153 | 154 | it('should send event on change recovery address', async function() { 155 | await this.inst.createAccount(login, {from: acc.user,}); 156 | const trans = await this.inst.setRecoveryAddress(login, acc.user2, {from: acc.user}); 157 | const Event = await expectEvent.inTransaction(trans, "RecoveryChange"); 158 | Event.should.have.deep.property('args', {login: login, from: acc.user, to: acc.user2}) 159 | }); 160 | 161 | it('should send event on address drop', async function() { 162 | await this.inst.createAccount(login, {from: acc.user,}); 163 | const trans = await this.inst.dropAccount(login, {from: acc.user}); 164 | const Event = await expectEvent.inTransaction(trans, "Drop"); 165 | Event.should.have.deep.property('args', {login: login, by: acc.user}) 166 | }); 167 | }); 168 | 169 | -------------------------------------------------------------------------------- /blockchain/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | }; 5 | -------------------------------------------------------------------------------- /blockchain/truffle.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('babel-polyfill'); 3 | module.exports = { 4 | // See 5 | // to customize your Truffle configuration! 6 | networks: { 7 | development: { 8 | host: "127.0.0.1", 9 | port: 9545, 10 | network_id: "*" // match any network 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /example-auth-agent/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | /config/.env.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* -------------------------------------------------------------------------------- /example-auth-agent/README.md: -------------------------------------------------------------------------------- 1 | # hackathon-etherauth 2 | -------------------------------------------------------------------------------- /example-auth-agent/config/.env.default: -------------------------------------------------------------------------------- 1 | API_URL_DEV='http://localhost:3000/api' -------------------------------------------------------------------------------- /example-auth-agent/config/config.js: -------------------------------------------------------------------------------- 1 | export const requestsConfig = { 2 | "USE_MOCK": false, 3 | "COLLAPSED_LOG_REQUESTS": true, 4 | "API_URL": process.env.NODE_ENV === 'development' 5 | ? `${process.env.API_URL_DEV}` 6 | : window.location.origin + '/api' 7 | } 8 | -------------------------------------------------------------------------------- /example-auth-agent/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "module": "none", 5 | "moduleResolution": "node", 6 | "target": "es6", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "outDir": "./build/", 10 | "preserveConstEnums": true, 11 | "allowJs": false, 12 | "experimentalDecorators": true, 13 | "baseUrl": "./" 14 | }, 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } -------------------------------------------------------------------------------- /example-auth-agent/config/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-react" 5 | ], 6 | "rules": { 7 | "use-isnan": true, 8 | "indent": [ 9 | true, 10 | "spaces", 11 | 2 12 | ], 13 | "arrow-return-shorthand": false, 14 | "space-before-function-paren": [ 15 | true, 16 | { 17 | "anonymous": "always", 18 | "named": "never", 19 | "asyncArrow": "always" 20 | } 21 | ], 22 | "no-bitwise": false, 23 | "ban-types": [ 24 | true, 25 | [ 26 | "Object", 27 | "Use 'object' instead." 28 | ], 29 | [ 30 | "Boolean", 31 | "Use 'boolean' instead." 32 | ], 33 | [ 34 | "Number", 35 | "Use 'number' instead." 36 | ], 37 | [ 38 | "String", 39 | "Use 'string' instead." 40 | ] 41 | ], 42 | "object-literal-key-quotes": [ 43 | false 44 | ], 45 | "no-string-literal": false, 46 | "jsx-no-lambda": false, 47 | "jsx-no-multiline-js": false, 48 | "no-shadowed-variable": false, 49 | "prefer-const": false, 50 | "comment-format": [ 51 | false 52 | ], 53 | "no-namespace": false, 54 | "array-type": [ 55 | true, 56 | "array" 57 | ], 58 | "max-line-length": [ 59 | true, 60 | 180 61 | ], 62 | "new-parens": true, 63 | "no-arg": true, 64 | "no-conditional-assignment": false, 65 | "no-consecutive-blank-lines": [ 66 | true, 67 | 2 68 | ], 69 | "quotemark": [ 70 | true, 71 | "single", 72 | "jsx-double" 73 | ], 74 | "no-var-requires": false, 75 | "trailing-comma": [ 76 | true, 77 | { 78 | "multiline": "always", 79 | "singleline": "never" 80 | } 81 | ], 82 | "unified-signatures": false, 83 | "prefer-for-of": false, 84 | "curly": false, 85 | "max-classes-per-file": [ 86 | false 87 | ], 88 | "object-literal-sort-keys": false, 89 | "no-empty-interface": false, 90 | "ordered-imports": [ 91 | false 92 | ], 93 | "only-arrow-functions": [ 94 | false 95 | ], 96 | "no-console": [ 97 | false 98 | ], 99 | "callable-types": false, 100 | "member-ordering": [ 101 | true, 102 | { 103 | "order": [ 104 | "private-static-field", 105 | "protected-static-field", 106 | "public-static-field", 107 | "private-static-method", 108 | "protected-static-method", 109 | "public-static-method", 110 | "private-instance-field", 111 | "protected-instance-field", 112 | "public-instance-field", 113 | "constructor", 114 | "private-instance-method", 115 | "protected-instance-method", 116 | "public-instance-method" 117 | ] 118 | } 119 | ] 120 | }, 121 | "jsRules": { 122 | "max-line-length": [ 123 | true, 124 | 180 125 | ] 126 | } 127 | } -------------------------------------------------------------------------------- /example-auth-agent/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etherauth", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "./node_modules/.bin/webpack-dev-server -d --progress --env=development", 7 | "build": "./node_modules/.bin/webpack -p --progress --colors --env=production" 8 | }, 9 | "devDependencies": { 10 | "autoprefixer": "^6.7.7", 11 | "babel-core": "^6.26.0", 12 | "babel-loader": "^6.4.1", 13 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 14 | "babel-polyfill": "^6.26.0", 15 | "babel-preset-env": "^1.6.1", 16 | "babel-preset-react": "^6.24.1", 17 | "babel-preset-stage-0": "^6.24.1", 18 | "case-sensitive-paths-webpack-plugin": "^2.1.1", 19 | "clean-webpack-plugin": "^0.1.19", 20 | "copy-webpack-plugin": "^4.5.1", 21 | "css-loader": "^0.28.0", 22 | "extract-text-webpack-plugin": "^2.1.0", 23 | "file-loader": "^0.11.1", 24 | "html-loader": "^0.5.5", 25 | "html-webpack-plugin": "^2.28.0", 26 | "less-loader": "^4.1.0", 27 | "postcss-loader": "^1.3.3", 28 | "react-hot-loader": "^4.0.0", 29 | "redux-devtools": "^3.0.1", 30 | "resolve-url-loader": "^2.1.0", 31 | "style-loader": "^0.16.1", 32 | "uglifyjs-webpack-plugin": "^1.2.2", 33 | "url-loader": "^0.5.8", 34 | "webpack": "3.11.0", 35 | "webpack-config-utils": "^2.3.0", 36 | "webpack-dev-server": "^2.11.2", 37 | "webpack-dotenv-extended-plugin": "^1.0.0" 38 | }, 39 | "dependencies": { 40 | "axios": "^0.17.1", 41 | "axios-mock-adapter": "^1.15.0", 42 | "classnames": "^2.2.5", 43 | "ethereumjs-util": "^5.2.0", 44 | "ethjs": "^0.3.1", 45 | "ethjs-contract": "^0.1.9", 46 | "ethjs-query": "^0.3.2", 47 | "js-sha256": "^0.9.0", 48 | "less": "^3.0.4", 49 | "lodash": "^4.17.5", 50 | "react": "^16.2.0", 51 | "react-dom": "^16.2.0", 52 | "react-redux": "^5.0.2", 53 | "redux": "^3.6.0", 54 | "redux-thunk": "^2.2.0", 55 | "secp256k1": "^3.5.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /example-auth-agent/src/api/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import MockAdapter from 'axios-mock-adapter'; 4 | import subscribeMockRequests from './apiMock'; 5 | import { requestsConfig } from '../../config/config'; 6 | 7 | if (requestsConfig.USE_MOCK) { 8 | subscribeMockRequests(new MockAdapter(axios)); 9 | } 10 | 11 | function logFetch(promise) { 12 | 13 | console.clear(); 14 | 15 | function wrapper(url, data, method, mock) { 16 | 17 | const result = promise(url, data, method, mock); 18 | 19 | result 20 | .then(response => { 21 | 22 | const str = requestsConfig.USE_MOCK ? 23 | [ 24 | `%c MOCK %c %c ${method.toUpperCase()} %c ${url}`, 25 | 'border: 1px solid yellow; color: white;', '', 26 | 'background-color: green; color: white', '' 27 | ] : 28 | [ 29 | `%c ${method.toUpperCase()} `, 30 | 'background-color: green; color: white', 31 | `${url}` 32 | ]; 33 | 34 | if (requestsConfig.COLLAPSED_LOG_REQUESTS) { 35 | console.groupCollapsed(...str); 36 | } else { 37 | console.group(...str); 38 | } 39 | console.log('HANDLER: ', url); 40 | console.log('METHOD: ', method.toUpperCase()); 41 | if (data !== undefined) console.log('SENDING REQUEST OBJECT: ', data); 42 | console.log(`RESPONSE STATUS: ${response.status}`); 43 | console.log('RESPONSE DATA: ', response.data); 44 | console.groupEnd(); 45 | }) 46 | .catch(error => { 47 | console.group(`ERROR!!! REQUEST. HANDLER: ${url} %c Error `, 'background-color: red; color: white'); 48 | if (data !== undefined) console.log('SENDING REQUEST OBJECT: ', data); 49 | console.log('ERROR: ', error); 50 | console.groupEnd(); 51 | }); 52 | 53 | return result; 54 | } 55 | return wrapper; 56 | } 57 | 58 | const apiNew = (url = '/', data = undefined, method = 'post', mock = requestsConfig.USE_MOCK) => { 59 | const config = { 60 | method, 61 | baseURL: requestsConfig.API_URL, 62 | url, 63 | }; 64 | 65 | if (data !== undefined) { 66 | config['data'] = data; 67 | } 68 | 69 | return axios(config); 70 | }; 71 | 72 | export const fetch = process.env.NODE_ENV !== 'production' ? logFetch(apiNew) : apiNew; 73 | -------------------------------------------------------------------------------- /example-auth-agent/src/api/apiMock.js: -------------------------------------------------------------------------------- 1 | import * as constructors from './mock/constructors'; 2 | import * as instances from './mock/instances'; 3 | 4 | export default function subscribeMockRequests(mockApi) { 5 | mockApi 6 | // .onAny('/constructors').reply(200, constructors.constructorListProd) 7 | .onAny().passThrough(); 8 | 9 | } -------------------------------------------------------------------------------- /example-auth-agent/src/api/apiRequests.js: -------------------------------------------------------------------------------- 1 | import store from '../store/store'; 2 | import { fetch } from './api'; 3 | // import { 4 | // fetchCtorsRequest, 5 | // fetchCtorsFailure, 6 | // fetchCtorsSuccess, 7 | // fetchCtorParamsRequest, 8 | // fetchCtorParamsFailure, 9 | // fetchCtorParamsSuccess, 10 | // } from '../app/common/ctor-card/CtorsActions'; 11 | 12 | const { dispatch } = store; 13 | 14 | // ============================================================================= 15 | // Constructors 16 | // ============================================================================= 17 | 18 | export function getConstructors() { 19 | const result = fetch('/constructors', undefined, 'get'); 20 | 21 | dispatch(fetchCtorsRequest()); 22 | 23 | result 24 | .then(response => { 25 | if (response.status === 200) { 26 | dispatch(fetchCtorsSuccess(response.data)) 27 | } 28 | }) 29 | .catch(error => { 30 | dispatch(fetchCtorsFailure(error.message)) 31 | }); 32 | 33 | return result; 34 | }; 35 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { hot } from 'react-hot-loader' 3 | 4 | import { checkMetaMask } from '../helpers/eth'; 5 | import LoginForm from './login-form/LoginForm'; 6 | import Main from './main/Main'; 7 | 8 | import './App.less'; 9 | 10 | class App extends Component { 11 | render() { 12 | const { screen, login, isLoginWindow } = this.props; 13 | 14 | return ( 15 |
16 | {isLoginWindow && } 17 |
18 |
19 | ); 20 | } 21 | }; 22 | 23 | 24 | export default hot(module)(App); -------------------------------------------------------------------------------- /example-auth-agent/src/app/App.less: -------------------------------------------------------------------------------- 1 | @import '../styles/variables.less'; 2 | 3 | 4 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/AppActions.js: -------------------------------------------------------------------------------- 1 | export const changeScreen = (screen) => ({ 2 | type: 'CHANGE_SCREEN', 3 | screen 4 | }); 5 | 6 | export const setLogin = (login) => ({ 7 | type: 'LOGIN', 8 | login 9 | }); 10 | 11 | export const setLoginWindow = (isLoginWindow) => ({ 12 | type: 'SHOW_LOGIN', 13 | isLoginWindow 14 | }); 15 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/AppContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import App from './App'; 4 | 5 | export default connect(state => ({ 6 | screen: state.app.screen, 7 | login: state.app.login, 8 | isLoginWindow: state.app.isLoginWindow, 9 | }))(App); 10 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/AppReducer.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | app: '', 3 | screen: 'main-screen', 4 | login: '', 5 | isLoginWindow: false, 6 | }; 7 | 8 | const app = (state = initState, action) => { 9 | switch (action.type) { 10 | case 'CHANGE_SCREEN': 11 | return { ...state, screen: action.screen }; 12 | case 'LOGIN': 13 | return { ...state, login: action.login }; 14 | case 'SHOW_LOGIN': 15 | return { ...state, isLoginWindow: action.isLoginWindow }; 16 | 17 | default: 18 | return state; 19 | } 20 | } 21 | 22 | export default app; 23 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/admin/Admin.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import { ABI, ADDRESS } from '../../constants/constants'; 4 | import { web3 } from '../../helpers/eth'; 5 | import store from '../../store/store'; 6 | import { changeScreen } from '../AppActions'; 7 | 8 | import './Admin.less'; 9 | 10 | export default class Admin extends PureComponent { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | value: '', 16 | valide: true, 17 | authAddress: '---', 18 | recoveryAddress: '---', 19 | error: '', 20 | login: '' 21 | }; 22 | } 23 | 24 | onChange = (e) => { 25 | const { valide, value } = this.state; 26 | 27 | if (!valide) 28 | this.setState({ valide: true }) 29 | 30 | this.setState({ value: e.target.value }); 31 | } 32 | 33 | // get address 34 | componentDidMount() { 35 | const { value, login } = this.state; 36 | const instance = web3.eth.contract(ABI).at(ADDRESS); 37 | 38 | let result; 39 | try { 40 | result = instance['authAddress'].call(login, 41 | (error, result) => { 42 | if (!error) { 43 | this.setState({ authAddress: result }); 44 | } else { 45 | this.setState({ error }); 46 | 47 | console.log('Error search'); 48 | } 49 | }); 50 | } catch (error) { 51 | this.setState({ error }); 52 | 53 | console.error('global error', error); 54 | } 55 | 56 | try { 57 | result = instance['recoveryAddress'].call(login, 58 | (error, result) => { 59 | if (!error) { 60 | this.setState({ recoveryAddress: result }); 61 | } else { 62 | this.setState({ error }); 63 | 64 | console.log('Error search'); 65 | } 66 | }); 67 | } catch (error) { 68 | this.setState({ error }); 69 | 70 | console.error('global error', error); 71 | } 72 | } 73 | 74 | 75 | componentWillMount() { 76 | const login = localStorage.getItem('login'); 77 | 78 | if (login !== undefined || login !== null || login !== '') { 79 | this.setState({ login }); 80 | } 81 | } 82 | 83 | getReceipt(tx) { 84 | web3.eth.getTransactionReceipt(tx, (err, receipt) => { 85 | if (null == receipt) 86 | window.setTimeout(() => { this.getReceipt(tx) }, 500); 87 | else { 88 | console.log('done!!!!!!!!!!!!!!!!!'); 89 | // localStorage.setItem('login', this.state.value); 90 | store.dispatch(changeScreen('admin-screen')); 91 | } 92 | }); 93 | } 94 | 95 | // set Recovery 96 | onClick = () => { 97 | const { login, value, valide } = this.state; 98 | 99 | if (value !== '') { 100 | // if (web3.isAddress(value)) { 101 | if (!valide) 102 | this.setState({ valide: true }) 103 | 104 | const instance = web3.eth.contract(ABI).at(ADDRESS); 105 | 106 | let result; 107 | try { 108 | result = instance['setRecoveryAddress'](login, value, 109 | (error, result) => { 110 | if (!error) { 111 | this.getReceipt(result); 112 | store.dispatch(changeScreen('mining-screen')); 113 | } else { 114 | console.log('Error in send'); 115 | } 116 | }); 117 | } catch (error) { 118 | console.error('global error', error); 119 | } 120 | } else { 121 | this.setState({ valide: false }); 122 | } 123 | } 124 | 125 | render() { 126 | const { authAddress, error, recoveryAddress, login } = this.state; 127 | 128 | return ( 129 |
130 | {error && 131 |

{error}

132 | } 133 |
134 |

Warning: you key is same

135 |
136 |

Login: {login}

137 |

Auth address: {authAddress}

138 | {/* */} 145 |

Recovery address: {recoveryAddress}

146 | 153 | 158 |
159 | ); 160 | } 161 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/admin/Admin.less: -------------------------------------------------------------------------------- 1 | .admin { 2 | flex-direction: column; 3 | 4 | input { 5 | width: 100%; 6 | height: 60px; 7 | margin-bottom: 69px; 8 | padding: 0 15px; 9 | 10 | border: none; 11 | border-radius: 4px; 12 | outline: none; 13 | background-color: gold; 14 | 15 | font-size: 24px; 16 | } 17 | 18 | .no-valide { 19 | border: 2px solid red; 20 | } 21 | 22 | button { 23 | height: 40px; 24 | 25 | border: none; 26 | outline: none; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/common/loader.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 12 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/common/loader2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | ethereum 13 | ethereum,brand wb 14 | cc0 15 | vil5qg 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/login-form/LoginForm.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import { web3 } from '../../helpers/eth'; 4 | import { ABI, ADDRESS } from '../../constants/constants'; 5 | import { setLoginWindow, setLogin } from '../AppActions'; 6 | import store from '../../store/store'; 7 | 8 | import './LoginForm.less'; 9 | 10 | export default class LoginForm extends PureComponent { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | value: '', 16 | valide: true, 17 | error: '' 18 | }; 19 | } 20 | 21 | onChange = (e) => { 22 | const { valide, value, error } = this.state; 23 | 24 | if (!valide) 25 | 26 | this.setState({ valide: true }) 27 | 28 | if (error !== '') 29 | this.setState({ error: true }) 30 | 31 | this.setState({ value: e.target.value }); 32 | } 33 | 34 | onClick = () => { 35 | // back!!!!!!!!!!!!!!!!!!!!!!!!! 36 | const { value, valide, error } = this.state; 37 | 38 | if (value !== '') { 39 | if (!valide) 40 | this.setState({ valide: true }) 41 | 42 | // check authAddress 43 | const instance = web3.eth.contract(ABI).at(ADDRESS); 44 | 45 | let result; 46 | try { 47 | result = instance['authAddress'].call(value, 48 | (error, authAddress) => { 49 | if (!error) { 50 | // fake send 'secret' string - will be generate 51 | // sign 52 | if (authAddress === '0x0000000000000000000000000000000000000000') { 53 | this.setState({ error: 'User does not exists!' }); 54 | return; 55 | } else if (error !== '') { 56 | this.setState({ error: '' }); 57 | } 58 | 59 | var message = 'secretstring'; 60 | 61 | let h = web3.sha3(message); 62 | 63 | web3.eth.sign(authAddress, h, 64 | (error, signMsg) => { 65 | if (!error) { 66 | const sig = signMsg.slice(2); 67 | const r = `0x${sig.slice(0, 64)}`; 68 | const s = `0x${sig.slice(64, 128)}`; 69 | const v = `0x${sig.slice(128, 130)}`; 70 | 71 | instance['signerAddressRaw'].call(h, v, r, s, (error, res) => { 72 | if (!error) { 73 | if (res === authAddress) { 74 | store.dispatch(setLogin(value)); 75 | store.dispatch(setLoginWindow(false)); 76 | } 77 | 78 | } else { 79 | console.log('Error in sign!', error); 80 | 81 | } 82 | }); 83 | } else { 84 | console.log('Error in sign!'); 85 | } 86 | }) 87 | 88 | } else { 89 | console.log('Error search address'); 90 | } 91 | }); 92 | } catch (error) { 93 | console.error('global error', error); 94 | } 95 | 96 | } else { 97 | this.setState({ valide: false }); 98 | } 99 | } 100 | 101 | render() { 102 | const { error } = this.state; 103 | 104 | return ( 105 |
106 |
107 |
108 |

109 |
110 |

Sign in

111 |
112 |
113 |
114 | {error && 115 |

{error}

116 | } 117 |
118 |
119 |

Username:

120 |
121 | 129 | 134 |
135 |
136 |
137 | ); 138 | } 139 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/login-form/LoginForm.less: -------------------------------------------------------------------------------- 1 | .login-form { 2 | position: fixed; 3 | top: 0; 4 | right: 0; 5 | bottom: 0; 6 | left: 0; 7 | 8 | .overlay { 9 | position: fixed; 10 | top: 0; 11 | right: 0; 12 | bottom: 0; 13 | left: 0; 14 | 15 | opacity: .71; 16 | background-color: black; 17 | } 18 | 19 | 20 | .main { 21 | position: relative; 22 | z-index: 10; 23 | 24 | flex-direction: column; 25 | 26 | height: 320px; 27 | 28 | background-color: wheat; 29 | 30 | .x { 31 | position: absolute; 32 | top: 0px; 33 | right: 20px; 34 | 35 | cursor: pointer; 36 | 37 | font-size: 20px; 38 | } 39 | 40 | .title { 41 | width: 100%; 42 | padding-left: 30px; 43 | 44 | border-bottom: 1px solid #000000; 45 | background-color: lightcoral; 46 | 47 | font-size: 23px; 48 | } 49 | 50 | .content { 51 | flex: 1; 52 | flex-direction: column; 53 | 54 | .username { 55 | width: 100%; 56 | 57 | text-align: left; 58 | p { 59 | margin: 7px 0; 60 | padding: 0; 61 | 62 | font-size: 17px; 63 | } 64 | } 65 | 66 | input { 67 | width: 100%; 68 | height: 60px; 69 | margin-bottom: 30px; 70 | padding: 0 15px; 71 | 72 | border: 2px solid slategray; 73 | border-radius: 4px; 74 | outline: none; 75 | background-color: lightblue; 76 | 77 | font-size: 24px; 78 | } 79 | 80 | .no-valide { 81 | border: 2px solid tomato; 82 | } 83 | 84 | button { 85 | height: 40px; 86 | 87 | border: 1px solid gray; 88 | outline: none; 89 | 90 | font-size: 16px; 91 | } 92 | 93 | .error { 94 | width: 100%; 95 | height: 30px; 96 | margin-top: 16px; 97 | 98 | text-align: center; 99 | 100 | color: red; 101 | border-radius: 5px; 102 | // background-color: tomato; 103 | 104 | font-size: 22px; 105 | p { 106 | margin: 0; 107 | padding: 0; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/main/Main.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import store from '../../store/store'; 4 | import { setLoginWindow } from '../AppActions'; 5 | 6 | import './Main.less'; 7 | 8 | export default class Main extends PureComponent { 9 | onClick = (screen) => { 10 | store.dispatch(setLoginWindow(true)) 11 | } 12 | 13 | render() { 14 | const { login } = this.props; 15 | 16 | return ( 17 |
18 |
19 |

Site

20 | 28 | {login === '' 29 | ? 30 | :

Hi, {login}

31 | } 32 |
33 |
34 | 54 |
55 |

Some interesting story

56 |

57 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti neque eius a excepturi, explicabo sapiente. Vero quasi non quisquam deserunt sequi numquam ipsum veniam fuga iure saepe voluptatem praesentium dignissimos, aliquam obcaecati beatae perferendis recusandae incidunt tempore animi! Natus aliquid, asperiores sunt eveniet unde fuga facilis earum voluptas sit. 58 |

59 |

60 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Nam aliquam modi laudantium voluptatem illo temporibus minima recusandae saepe quam eum, sapiente consequatur provident. Necessitatibus molestiae error accusantium, quos natus architecto praesentium reiciendis aut aliquam possimus iste quae alias eaque odit eveniet ducimus rem labore nihil voluptatum? Asperiores unde architecto cupiditate maxime. Eius sapiente aut sed ducimus eaque nulla dolores fugit eum neque cum veniam praesentium sequi asperiores culpa omnis veritatis doloremque, nam debitis sunt optio dolorum tempore itaque nisi atque! Vel natus ex omnis officiis? Ratione nobis illum commodi, odio laboriosam nisi deserunt dolor cum eaque iusto nesciunt at mollitia quia iste nam iure deleniti dolorem facilis laudantium dicta maxime ullam rem! Quia hic iure cumque, provident temporibus similique! Illum a labore inventore, eveniet reiciendis accusantium, voluptas, commodi provident sed reprehenderit officia asperiores assumenda nihil quis cupiditate natus totam id similique doloribus. Tempore, dignissimos rem natus aperiam corrupti, magni dicta tenetur assumenda ea laborum, eveniet aliquam tempora blanditiis ex animi sint magnam modi maiores commodi pariatur inventore id totam quis. Neque, impedit. Hic dignissimos ut impedit. Ipsum accusantium amet quasi numquam beatae, aspernatur deserunt ea nam incidunt, maiores molestias vel natus repellendus quibusdam excepturi dolor quo voluptatem cumque quos? Quam, et blanditiis tempore, perferendis excepturi labore quisquam dolore nisi minus quod soluta minima, quas id eligendi iure velit officiis! Dolor veritatis qui cum labore repudiandae reiciendis aperiam deleniti. Quo illum excepturi eveniet quisquam provident totam autem id at tenetur doloribus, culpa distinctio hic est eligendi aliquam harum cupiditate iste repellendus? Temporibus enim quaerat necessitatibus ratione reiciendis excepturi explicabo, corporis nam nulla. Voluptates, minima aut temporibus, voluptatem, officiis reiciendis ratione quia quis ea accusantium esse repellat ullam possimus amet at dolor quod dolorem adipisci vel iure cumque perspiciatis repudiandae? Doloribus, facilis ad! Itaque explicabo laborum reprehenderit quidem aspernatur dolorem esse blanditiis? 61 |

62 |

63 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti neque eius a excepturi, explicabo sapiente. Vero quasi non quisquam deserunt sequi numquam ipsum veniam fuga iure saepe voluptatem praesentium dignissimos, aliquam obcaecati beatae perferendis recusandae incidunt tempore animi! Natus aliquid, asperiores sunt eveniet unde fuga facilis earum voluptas sit. 64 |

65 |

66 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti neque eius a excepturi, explicabo sapiente. Vero quasi non quisquam deserunt sequi numquam ipsum veniam fuga iure saepe voluptatem praesentium dignissimos, aliquam obcaecati beatae perferendis recusandae incidunt tempore animi! Natus aliquid, asperiores sunt eveniet unde fuga facilis earum voluptas sit. 67 |

68 |

69 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti neque eius a excepturi, explicabo sapiente. Vero quasi non quisquam deserunt sequi numquam ipsum veniam fuga iure saepe voluptatem praesentium dignissimos, aliquam obcaecati beatae perferendis recusandae incidunt tempore animi! Natus aliquid, asperiores sunt eveniet unde fuga facilis earum voluptas sit. 70 |

71 |

72 | Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti neque eius a excepturi, explicabo sapiente. Vero quasi non quisquam deserunt sequi numquam ipsum veniam fuga iure saepe voluptatem praesentium dignissimos, aliquam obcaecati beatae perferendis recusandae incidunt tempore animi! Natus aliquid, asperiores sunt eveniet unde fuga facilis earum voluptas sit. 73 |

74 |
75 |
76 |
77 | ); 78 | } 79 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/main/Main.less: -------------------------------------------------------------------------------- 1 | .main-screen { 2 | display: flex; 3 | overflow: hidden; 4 | flex-direction: column; 5 | 6 | width: 100vw; 7 | height: 100vh; 8 | 9 | .header { 10 | width: 100%; 11 | height: 120px; 12 | padding: 0 40px; 13 | 14 | border-bottom: 2px solid saddlebrown; 15 | background: radial-gradient(ellipse at center, rgb(255, 255, 255), rgb(23, 128, 147)); 16 | 17 | .logo { 18 | font-size: 50px; 19 | font-weight: bold; 20 | } 21 | 22 | .username { 23 | font-size: 50px; 24 | font-weight: bold; 25 | } 26 | 27 | .login { 28 | width: 200px; 29 | height: 60px; 30 | 31 | cursor: pointer; 32 | 33 | border: 2px solid gold; 34 | border-radius: 6px; 35 | outline: none; 36 | background-color: goldenrod; 37 | 38 | font-size: 22px; 39 | font-weight: bold; 40 | 41 | &:hover { 42 | background-color: tan; 43 | } 44 | } 45 | 46 | nav { 47 | flex: 1; 48 | 49 | ul { 50 | li { 51 | display: block; 52 | 53 | padding: 0 17px; 54 | 55 | list-style: none; 56 | 57 | font-size: 22px; 58 | } 59 | } 60 | } 61 | } 62 | 63 | .content { 64 | overflow: hidden; 65 | flex: 1; 66 | 67 | 68 | aside { 69 | width: 30%; 70 | height: 100%; 71 | padding: 40px; 72 | 73 | border-right: 2px solid saddlebrown; 74 | background-color: rgba(23, 147, 137, .9); 75 | } 76 | 77 | main { 78 | width: 70%; 79 | height: 100%; 80 | padding: 40px; 81 | 82 | background-color: rgba(23, 128, 147, .7); 83 | 84 | h1 { 85 | margin-bottom: 50px; 86 | 87 | text-align: center; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/metamask/Metamask.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import metamaskSvg from '../../assets/img/metamask.svg'; 4 | 5 | import './Metamask.less'; 6 | 7 | export default class Metamask extends PureComponent { 8 | render() { 9 | const { status } = this.props; 10 | 11 | let content; 12 | 13 | switch (status) { 14 | case 'unlockMetamask': 15 | content =

Please, unlock metamask

16 | break; 17 | case 'noMetamask': 18 | content = ( 19 |
20 |

Please, install metamask

21 | 23 | 24 | 25 |
26 | ); 27 | break; 28 | default: 29 | content =

Hmmmmmm, some problem!

30 | break; 31 | } 32 | return ( 33 |
34 |
35 | metamask 36 | {content} 37 | 38 |
39 |
40 | ); 41 | } 42 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/metamask/Metamask.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/variables.less'; 2 | 3 | .metamask { 4 | @firefox-size: 220px; 5 | 6 | position: relative; 7 | 8 | display: flex; 9 | overflow: hidden; /* clearfix */ 10 | 11 | width: 50%; 12 | margin: auto; 13 | // transform: scale(.5); 14 | // opacity: .7; 15 | // display: flex; 16 | // align-items: center; 17 | // border-radius: 50%; 18 | 19 | border-radius: 16px; 20 | background: silver; 21 | // background-image: linear-gradient(90deg, #125F6D 0%, #177E8F 30%, #177E8F 70%, #16728B 100%); 22 | background-color: #177E8F; 23 | box-shadow: 0px 0px 90px 0px rgba(50, 50, 50, .4); 24 | 25 | &::before { 26 | float: left; 27 | 28 | padding-top: 100%; 29 | 30 | content: ''; 31 | } 32 | .firefox { 33 | width: @firefox-size; 34 | height: @firefox-size; 35 | } 36 | 37 | p { 38 | margin: 0; 39 | padding: 0; 40 | 41 | letter-spacing: .3px; 42 | 43 | color: #FFFFFF; 44 | 45 | font-family: @font-primary; 46 | font-size: 15px; 47 | line-height: 19px; 48 | } 49 | 50 | .wrapper { 51 | position: absolute; 52 | 53 | flex-direction: column; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/mining/Mining.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import loaderSvg from '../common/loader2.svg'; 4 | 5 | import './Mining.less'; 6 | 7 | export default class Mining extends PureComponent { 8 | render() { 9 | return ( 10 |
11 |

Please wait

12 |

mining process...

13 | loader 14 |
15 | ); 16 | } 17 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/mining/Mining.less: -------------------------------------------------------------------------------- 1 | .mining-screen { 2 | position: relative; 3 | 4 | flex-direction: column; 5 | 6 | height: 60vh; 7 | 8 | img { 9 | position: absolute; 10 | } 11 | 12 | p { 13 | z-index: 10; 14 | 15 | margin: 17px 0; 16 | 17 | font-size: 28px; 18 | 19 | &:first-child { 20 | font-size: 50px; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example-auth-agent/src/app/register/Register.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import { getNetworkId, web3 } from '../../helpers/eth'; 4 | import { ABI, ADDRESS } from '../../constants/constants'; 5 | import store from '../../store/store'; 6 | import { changeScreen } from '../AppActions'; 7 | 8 | import './Register.less'; 9 | 10 | export default class Register extends PureComponent { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | value: '', 16 | valide: true 17 | }; 18 | } 19 | 20 | getReceipt(tx) { 21 | web3.eth.getTransactionReceipt(tx, (err, receipt) => { 22 | if (null == receipt) 23 | window.setTimeout(() => { this.getReceipt(tx) }, 500); 24 | else { 25 | console.log('done!!!!!!!!!!!!!!!!!'); 26 | localStorage.setItem('login', this.state.value); 27 | store.dispatch(changeScreen('admin-screen')); 28 | } 29 | }); 30 | } 31 | 32 | onCheck = () => { 33 | const { value, valide } = this.state; 34 | 35 | if (value !== '') { 36 | if (!valide) 37 | this.setState({ valide: true }) 38 | 39 | const instance = web3.eth.contract(ABI).at(ADDRESS); 40 | console.log('instance', instance); 41 | 42 | let result; 43 | try { 44 | result = instance['authAddress'].call(value, 45 | (error, result) => { 46 | if (!error) { 47 | console.log('result search: ', result); 48 | 49 | } else { 50 | console.log('Error search'); 51 | } 52 | }); 53 | } catch (error) { 54 | console.error('global error', error); 55 | } 56 | } else { 57 | this.setState({ valide: false }); 58 | } 59 | 60 | 61 | } 62 | 63 | onClick = () => { 64 | const { value, valide } = this.state; 65 | 66 | if (value !== '') { 67 | if (!valide) 68 | this.setState({ valide: true }) 69 | 70 | const instance = web3.eth.contract(ABI).at(ADDRESS); 71 | console.log('instance', instance); 72 | 73 | let result; 74 | try { 75 | result = instance['createAccount'](value, 76 | (error, result) => { 77 | if (!error) { 78 | this.getReceipt(result); 79 | store.dispatch(changeScreen('mining-screen')); 80 | } else { 81 | console.log('Error in send'); 82 | } 83 | }); 84 | } catch (error) { 85 | console.error('global error', error); 86 | } 87 | } else { 88 | this.setState({ valide: false }); 89 | } 90 | } 91 | 92 | onChange = (e) => { 93 | const { valide, value } = this.state; 94 | 95 | if (!valide) 96 | this.setState({ valide: true }) 97 | 98 | this.setState({ value: e.target.value }); 99 | } 100 | 101 | 102 | render() { 103 | return ( 104 |
105 | 112 | 115 |
116 | ); 117 | } 118 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/app/register/Register.less: -------------------------------------------------------------------------------- 1 | .register { 2 | flex-direction: column; 3 | 4 | input { 5 | width: 100%; 6 | height: 60px; 7 | margin-bottom: 69px; 8 | padding: 0 15px; 9 | 10 | border: none; 11 | border-radius: 4px; 12 | outline: none; 13 | background-color: gold; 14 | 15 | font-size: 24px; 16 | } 17 | 18 | .no-valide { 19 | border: 2px solid red; 20 | } 21 | 22 | button { 23 | height: 40px; 24 | 25 | border: none; 26 | outline: none; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /example-auth-agent/src/assets/img/metamask.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example-auth-agent/src/constants/constants.js: -------------------------------------------------------------------------------- 1 | export const ADDRESS = '0xd68bef63b58efb50c4a4b8625f7336ad73952dba'; 2 | 3 | export const ABI = [{ "constant": false, "inputs": [{ "name": "_login", "type": "string" }], "name": "dropAccount", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [{ "name": "_login", "type": "string" }], "name": "authAddress", "outputs": [{ "name": "", "type": "address" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": false, "inputs": [{ "name": "_login", "type": "string" }], "name": "createAccount", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [{ "name": "_login", "type": "string" }], "name": "recoveryAddress", "outputs": [{ "name": "", "type": "address" }], "payable": false, "stateMutability": "view", "type": "function" }, { "constant": true, "inputs": [{ "name": "data", "type": "bytes32" }, { "name": "v", "type": "uint8" }, { "name": "r", "type": "bytes32" }, { "name": "s", "type": "bytes32" }], "name": "signerAddressRaw", "outputs": [{ "name": "", "type": "address" }], "payable": false, "stateMutability": "pure", "type": "function" }, { "constant": true, "inputs": [{ "name": "data", "type": "bytes32" }, { "name": "v", "type": "uint8" }, { "name": "r", "type": "bytes32" }, { "name": "s", "type": "bytes32" }], "name": "signerAddress", "outputs": [{ "name": "", "type": "address" }], "payable": false, "stateMutability": "pure", "type": "function" }, { "constant": false, "inputs": [{ "name": "_login", "type": "string" }, { "name": "_addr", "type": "address" }], "name": "setRecoveryAddress", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": false, "inputs": [{ "name": "_login", "type": "string" }, { "name": "_addr", "type": "address" }], "name": "setAuthAddress", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "anonymous": false, "inputs": [{ "indexed": false, "name": "login", "type": "string" }], "name": "Create", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "name": "login", "type": "string" }, { "indexed": false, "name": "from", "type": "address" }, { "indexed": false, "name": "to", "type": "address" }], "name": "AuthChange", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "name": "login", "type": "string" }, { "indexed": false, "name": "from", "type": "address" }, { "indexed": false, "name": "to", "type": "address" }], "name": "RecoveryChange", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": false, "name": "login", "type": "string" }, { "indexed": false, "name": "by", "type": "address" }], "name": "Drop", "type": "event" }]; 4 | -------------------------------------------------------------------------------- /example-auth-agent/src/helpers/detect-device.js: -------------------------------------------------------------------------------- 1 | const userAgent = navigator.userAgent.toLowerCase(); 2 | 3 | export const IS_IOS = ['ipad', 'iphone', 'ipod'].indexOf((navigator.platform || '').toLowerCase()) >= 0; 4 | export const IS_ANDROID = /(android)/i.test(userAgent); 5 | export const IS_XIAOMI_BROWSER = /(MiuiBrowser)/i.test(userAgent); 6 | export const IS_MOBILE_OS = IS_IOS || IS_ANDROID; -------------------------------------------------------------------------------- /example-auth-agent/src/helpers/eth.js: -------------------------------------------------------------------------------- 1 | export var web3 = window.Web3 2 | ? new window.Web3(window.web3.currentProvider) 3 | : undefined; 4 | 5 | export const checkMetaMask = () => { 6 | if (!window.Web3) { 7 | return 'noMetamask'; 8 | } 9 | 10 | if (!web3.eth.accounts[0]) { 11 | return 'unlockMetamask'; 12 | } 13 | 14 | return 'okMetamask'; 15 | }; 16 | 17 | export const getNetworkId = cb => { 18 | web3.version.getNetwork((err, netId) => { 19 | err && console.log(err); 20 | netId && cb(netId); 21 | }); 22 | }; 23 | 24 | export const getNetworkName = netId => { 25 | switch (netId.toString()) { 26 | case "1": 27 | return "Mainnet"; 28 | case "3": 29 | return "Ropsten"; 30 | case "4": 31 | return "Rinkeby"; 32 | case "42": 33 | return "Kovan"; 34 | default: 35 | return "Error! Unknown or deprecated network"; 36 | } 37 | }; 38 | 39 | export const getNetworkEtherscanAddress = netId => { 40 | switch (netId.toString()) { 41 | case "1": 42 | return "https://etherscan.io"; 43 | case "3": 44 | return "https://ropsten.etherscan.io"; 45 | case "4": 46 | return "https://rinkeby.etherscan.io"; 47 | case "42": 48 | return "https://kovan.etherscan.io"; 49 | default: 50 | return "Error! Unknown or deprecated network"; 51 | } 52 | }; 53 | 54 | export const isAddress = (hash) => { 55 | if (typeof hash === 'string') { 56 | return /^0x([A-Fa-f0-9]{40})$/.test(hash); 57 | } else { 58 | return false; 59 | } 60 | }; 61 | 62 | export const isTx = (hash) => { 63 | if (typeof hash === 'string') { 64 | return /^0x([A-Fa-f0-9]{64})$/.test(hash); 65 | } else { 66 | return false; 67 | } 68 | }; -------------------------------------------------------------------------------- /example-auth-agent/src/helpers/utils.js: -------------------------------------------------------------------------------- 1 | export function generateId() { 2 | let text = ""; 3 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 4 | 5 | for (let i = 0; i < 5; i++) 6 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 7 | 8 | return text; 9 | } -------------------------------------------------------------------------------- /example-auth-agent/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Etherauth 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /example-auth-agent/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | 5 | import store from './store/store'; 6 | import App from './app/AppContainer'; 7 | 8 | import './index.less'; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); -------------------------------------------------------------------------------- /example-auth-agent/src/index.less: -------------------------------------------------------------------------------- 1 | @import './styles/index.less'; 2 | 3 | body { 4 | color: #000000; 5 | background: radial-gradient(ellipse at center, #A8EDF2, #558892); /*Standard*/ 6 | 7 | font-family: @font-primary; 8 | font-size: 12px; 9 | line-height: 17px; 10 | } 11 | 12 | * { 13 | box-sizing: border-box; 14 | } 15 | -------------------------------------------------------------------------------- /example-auth-agent/src/store/store.js: -------------------------------------------------------------------------------- 1 | import { combineReducers, createStore } from 'redux'; 2 | 3 | import app from '../app/AppReducer'; 4 | 5 | const reducers = combineReducers({ 6 | app, 7 | }); 8 | 9 | const store = createStore( 10 | reducers, 11 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 12 | ); 13 | 14 | export default store; 15 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/base.less: -------------------------------------------------------------------------------- 1 | @import './variables.less'; 2 | 3 | .flex { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | 9 | .flex-h { 10 | display: flex; 11 | justify-content: center; 12 | } 13 | 14 | .flex-v { 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .screen { 20 | position: absolute; 21 | top: 0; 22 | right: 0; 23 | bottom: 0; 24 | left: 0; 25 | } 26 | 27 | 28 | .btn-default { 29 | display: block; 30 | 31 | width: 340px; 32 | height: 90px; 33 | padding: 0 10px; 34 | 35 | cursor: pointer; 36 | text-decoration: none; 37 | text-transform: uppercase; 38 | 39 | border: none; 40 | border-radius: 5px; 41 | outline: none; 42 | 43 | font-family: @font-primary; 44 | } 45 | 46 | .form { 47 | overflow: hidden; 48 | 49 | width: 500px; 50 | 51 | border-radius: 8px; 52 | } 53 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/components.less: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/fonts.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mixbytes/etherauth/5bb6ad9b720b0ed39c766acca02682dc00b21e91/example-auth-agent/src/styles/fonts.less -------------------------------------------------------------------------------- /example-auth-agent/src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import './variables.less'; 2 | @import './base.less'; 3 | @import './normalize.less'; 4 | @import './mixins.less'; 5 | @import './fonts.less'; 6 | @import './components.less'; 7 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/levels.less: -------------------------------------------------------------------------------- 1 | // $level-low: 100000; 2 | // $level-medium: 10000000; 3 | // $level-high: 10000000000; 4 | 5 | // @mixin level($lvl: $level-low, $sublvl: 0) { 6 | // z-index: $lvl + $sublvl; 7 | // } 8 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/mixins.less: -------------------------------------------------------------------------------- 1 | .gradient-animation(@lg1, @lg2, @transTimeStart, @transTimeEnd) { 2 | position: relative; 3 | z-index: 100; 4 | 5 | background-image: linear-gradient(@lg1); 6 | background-size: 100%; 7 | &:before { 8 | position: absolute; 9 | z-index: -100; 10 | top: 0; 11 | left: 0; 12 | 13 | display: block; 14 | 15 | width: 100%; 16 | height: 100%; 17 | 18 | content: ''; 19 | transition: opacity @transTimeEnd; 20 | 21 | opacity: 0; 22 | border-radius: 4px; 23 | background-image: linear-gradient(@lg2); 24 | } 25 | &:hover, &:focus { 26 | &:before { 27 | transition: opacity @transTimeStart; 28 | 29 | opacity: 1; 30 | } 31 | } 32 | } 33 | 34 | .transitions(@transProp, @transDur) { 35 | transition-duration: @transDur; 36 | transition-property: @transProp; 37 | } 38 | 39 | 40 | // @mixin css3-prefix($property, $value) { 41 | // -webkit-#{$property}: #{$value}; 42 | // -khtml-#{$property}: #{$value}; 43 | // -moz-#{$property}: #{$value}; 44 | // -ms-#{$property}: #{$value}; 45 | // -o-#{$property}: #{$value}; 46 | // #{$property}: #{$value}; 47 | // } 48 | 49 | // @mixin unselectable() { 50 | // @include css3-prefix(user-select, none); 51 | // } 52 | 53 | // @mixin box-shadow($top: 0, $left: 0, $blur: 5px, $color: black, $inset: false) { 54 | // @if $inset { 55 | // @include css3-prefix(box-shadow, (inset $top $left $blur $color)); 56 | // } @else { 57 | // @include css3-prefix(box-shadow, ($top $left $blur $color)); 58 | // } 59 | // } 60 | 61 | // @mixin border-radius($radius: 5px) { 62 | // @include css3-prefix('border-radius', $radius); 63 | // } 64 | 65 | // @mixin background-gradient($startColor: #3C3C3C, $endColor: #999999) { 66 | // background-color: $startColor; 67 | // background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)); 68 | // background-image: -webkit-linear-gradient(top, $startColor, $endColor); 69 | // background-image: -moz-linear-gradient(top, $startColor, $endColor); 70 | // background-image: -ms-linear-gradient(top, $startColor, $endColor); 71 | // background-image: -o-linear-gradient(top, $startColor, $endColor); 72 | // background-image: linear-gradient(top, $startColor, $endColor); 73 | // filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#{$startColor}', endColorStr='#{$endColor}'); 74 | // } 75 | 76 | // @mixin background-horizontal($startColor: #3C3C3C, $endColor: #999999) { 77 | // background-color: $startColor; 78 | // background-image: -webkit-gradient(linear, left top, right top, from($startColor), to($endColor)); 79 | // background-image: -webkit-linear-gradient(left, $startColor, $endColor); 80 | // background-image: -moz-linear-gradient(left, $startColor, $endColor); 81 | // background-image: -ms-linear-gradient(left, $startColor, $endColor); 82 | // background-image: -o-linear-gradient(left, $startColor, $endColor); 83 | // background-image: linear-gradient(left, $startColor, $endColor); 84 | // filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#{$startColor}', endColorStr='#{$endColor}', gradientType='1'); 85 | // } 86 | 87 | // @mixin background-radial($startColor: #FFFFFF, $startPos: 0%, $endColor: #000000, $endPos:100%) { 88 | // background: -moz-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 89 | // background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop($startPos, $startColor), color-stop($endPos, $endColor)); 90 | // background: -webkit-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 91 | // background: -o-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 92 | // background: -ms-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 93 | // background: radial-gradient(ellipse at center, $startColor $startPos, $endColor $endPos); 94 | // } 95 | 96 | // @mixin background-opacity($color: #000, $opacity: 0.85) { 97 | // background: $color; 98 | // background: rgba($color, $opacity); 99 | // } 100 | 101 | // @mixin vh-stretch() { 102 | // position: absolute; 103 | // top: 0; 104 | // bottom: 0; 105 | // left: 0; 106 | // width: 100%; 107 | // } 108 | 109 | // @mixin clickable-icon() { 110 | // opacity: 0.5; 111 | // cursor: pointer; 112 | // &:hover { 113 | // opacity: 1; 114 | // } 115 | // } 116 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/normalize.less: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Correct the font size and margin on `h1` elements within `section` and 29 | * `article` contexts in Chrome, Firefox, and Safari. 30 | */ 31 | 32 | h1 { 33 | font-size: 2em; 34 | margin: 0.67em 0; 35 | } 36 | 37 | /* Grouping content 38 | ========================================================================== */ 39 | 40 | /** 41 | * 1. Add the correct box sizing in Firefox. 42 | * 2. Show the overflow in Edge and IE. 43 | */ 44 | 45 | hr { 46 | box-sizing: content-box; /* 1 */ 47 | height: 0; /* 1 */ 48 | overflow: visible; /* 2 */ 49 | } 50 | 51 | /** 52 | * 1. Correct the inheritance and scaling of font size in all browsers. 53 | * 2. Correct the odd `em` font sizing in all browsers. 54 | */ 55 | 56 | pre { 57 | font-family: monospace, monospace; /* 1 */ 58 | font-size: 1em; /* 2 */ 59 | } 60 | 61 | /* Text-level semantics 62 | ========================================================================== */ 63 | 64 | /** 65 | * Remove the gray background on active links in IE 10. 66 | */ 67 | 68 | a { 69 | background-color: transparent; 70 | } 71 | 72 | /** 73 | * 1. Remove the bottom border in Chrome 57- 74 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 75 | */ 76 | 77 | abbr[title] { 78 | border-bottom: none; /* 1 */ 79 | text-decoration: underline; /* 2 */ 80 | text-decoration: underline dotted; /* 2 */ 81 | } 82 | 83 | /** 84 | * Add the correct font weight in Chrome, Edge, and Safari. 85 | */ 86 | 87 | b, 88 | strong { 89 | font-weight: bolder; 90 | } 91 | 92 | /** 93 | * 1. Correct the inheritance and scaling of font size in all browsers. 94 | * 2. Correct the odd `em` font sizing in all browsers. 95 | */ 96 | 97 | code, 98 | kbd, 99 | samp { 100 | font-family: monospace, monospace; /* 1 */ 101 | font-size: 1em; /* 2 */ 102 | } 103 | 104 | /** 105 | * Add the correct font size in all browsers. 106 | */ 107 | 108 | small { 109 | font-size: 80%; 110 | } 111 | 112 | /** 113 | * Prevent `sub` and `sup` elements from affecting the line height in 114 | * all browsers. 115 | */ 116 | 117 | sub, 118 | sup { 119 | font-size: 75%; 120 | line-height: 0; 121 | position: relative; 122 | vertical-align: baseline; 123 | } 124 | 125 | sub { 126 | bottom: -0.25em; 127 | } 128 | 129 | sup { 130 | top: -0.5em; 131 | } 132 | 133 | /* Embedded content 134 | ========================================================================== */ 135 | 136 | /** 137 | * Remove the border on images inside links in IE 10. 138 | */ 139 | 140 | img { 141 | border-style: none; 142 | } 143 | 144 | /* Forms 145 | ========================================================================== */ 146 | 147 | /** 148 | * 1. Change the font styles in all browsers. 149 | * 2. Remove the margin in Firefox and Safari. 150 | */ 151 | 152 | button, 153 | input, 154 | optgroup, 155 | select, 156 | textarea { 157 | font-family: inherit; /* 1 */ 158 | font-size: 100%; /* 1 */ 159 | line-height: 1.15; /* 1 */ 160 | margin: 0; /* 2 */ 161 | } 162 | 163 | /** 164 | * Show the overflow in IE. 165 | * 1. Show the overflow in Edge. 166 | */ 167 | 168 | button, 169 | input { /* 1 */ 170 | overflow: visible; 171 | } 172 | 173 | /** 174 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 175 | * 1. Remove the inheritance of text transform in Firefox. 176 | */ 177 | 178 | button, 179 | select { /* 1 */ 180 | text-transform: none; 181 | } 182 | 183 | /** 184 | * Correct the inability to style clickable types in iOS and Safari. 185 | */ 186 | 187 | button, 188 | [type="button"], 189 | [type="reset"], 190 | [type="submit"] { 191 | -webkit-appearance: button; 192 | } 193 | 194 | /** 195 | * Remove the inner border and padding in Firefox. 196 | */ 197 | 198 | button::-moz-focus-inner, 199 | [type="button"]::-moz-focus-inner, 200 | [type="reset"]::-moz-focus-inner, 201 | [type="submit"]::-moz-focus-inner { 202 | border-style: none; 203 | padding: 0; 204 | } 205 | 206 | /** 207 | * Restore the focus styles unset by the previous rule. 208 | */ 209 | 210 | button:-moz-focusring, 211 | [type="button"]:-moz-focusring, 212 | [type="reset"]:-moz-focusring, 213 | [type="submit"]:-moz-focusring { 214 | outline: 1px dotted ButtonText; 215 | } 216 | 217 | /** 218 | * Correct the padding in Firefox. 219 | */ 220 | 221 | fieldset { 222 | padding: 0.35em 0.75em 0.625em; 223 | } 224 | 225 | /** 226 | * 1. Correct the text wrapping in Edge and IE. 227 | * 2. Correct the color inheritance from `fieldset` elements in IE. 228 | * 3. Remove the padding so developers are not caught out when they zero out 229 | * `fieldset` elements in all browsers. 230 | */ 231 | 232 | legend { 233 | box-sizing: border-box; /* 1 */ 234 | color: inherit; /* 2 */ 235 | display: table; /* 1 */ 236 | max-width: 100%; /* 1 */ 237 | padding: 0; /* 3 */ 238 | white-space: normal; /* 1 */ 239 | } 240 | 241 | /** 242 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 243 | */ 244 | 245 | progress { 246 | vertical-align: baseline; 247 | } 248 | 249 | /** 250 | * Remove the default vertical scrollbar in IE 10+. 251 | */ 252 | 253 | textarea { 254 | overflow: auto; 255 | } 256 | 257 | /** 258 | * 1. Add the correct box sizing in IE 10. 259 | * 2. Remove the padding in IE 10. 260 | */ 261 | 262 | [type="checkbox"], 263 | [type="radio"] { 264 | box-sizing: border-box; /* 1 */ 265 | padding: 0; /* 2 */ 266 | } 267 | 268 | /** 269 | * Correct the cursor style of increment and decrement buttons in Chrome. 270 | */ 271 | 272 | [type="number"]::-webkit-inner-spin-button, 273 | [type="number"]::-webkit-outer-spin-button { 274 | height: auto; 275 | } 276 | 277 | /** 278 | * 1. Correct the odd appearance in Chrome and Safari. 279 | * 2. Correct the outline style in Safari. 280 | */ 281 | 282 | [type="search"] { 283 | -webkit-appearance: textfield; /* 1 */ 284 | outline-offset: -2px; /* 2 */ 285 | } 286 | 287 | /** 288 | * Remove the inner padding in Chrome and Safari on macOS. 289 | */ 290 | 291 | [type="search"]::-webkit-search-decoration { 292 | -webkit-appearance: none; 293 | } 294 | 295 | /** 296 | * 1. Correct the inability to style clickable types in iOS and Safari. 297 | * 2. Change font properties to `inherit` in Safari. 298 | */ 299 | 300 | ::-webkit-file-upload-button { 301 | -webkit-appearance: button; /* 1 */ 302 | font: inherit; /* 2 */ 303 | } 304 | 305 | /* Interactive 306 | ========================================================================== */ 307 | 308 | /* 309 | * Add the correct display in Edge, IE 10+, and Firefox. 310 | */ 311 | 312 | details { 313 | display: block; 314 | } 315 | 316 | /* 317 | * Add the correct display in all browsers. 318 | */ 319 | 320 | summary { 321 | display: list-item; 322 | } 323 | 324 | /* Misc 325 | ========================================================================== */ 326 | 327 | /** 328 | * Add the correct display in IE 10+. 329 | */ 330 | 331 | template { 332 | display: none; 333 | } 334 | 335 | /** 336 | * Add the correct display in IE 10. 337 | */ 338 | 339 | [hidden] { 340 | display: none; 341 | } 342 | -------------------------------------------------------------------------------- /example-auth-agent/src/styles/variables.less: -------------------------------------------------------------------------------- 1 | // @project-border-radius: 2px; 2 | // @project-font-size: 12px; 3 | // @project-height: 35px; 4 | // @project-padding: 10px; 5 | // @project-animation-time: .35s; 6 | // @project-selected-color: @brand-primary; 7 | // @project-menu-height: 50px; 8 | // @project-border-color-1: @c-group-wg1-grey-2; 9 | // @project-background-color-0: white; 10 | // @project-background-color-1: @c-group-wg1-grey-0; 11 | // @project-background-color-2: darken(@c-group-wg1-grey-0, 2%); 12 | // @project-background-color-3: darken(@c-group-wg1-grey-0, 4%); 13 | // @project-delete-color: @c-red; 14 | // @project-modal-wrapper-color: rgba(0, 0, 0, .5); 15 | // @project-ac-1: #838383; 16 | // @project-ac-2: #999999; 17 | // @project-ac-3: lighten(@project-ac-2, 25%); 18 | // @project-ac-4: #C2DDB0; 19 | // @project-ac-5: #C2DDB0; 20 | 21 | /* ========================================================================== */ 22 | /* Fonts */ 23 | /* ========================================================================== */ 24 | 25 | @font-primary: sans-serif; 26 | @color-primary: #009999; 27 | @color-second: #1D7373; 28 | @color-third: #006363; 29 | 30 | -------------------------------------------------------------------------------- /example-auth-agent/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | const CopyWebpackPlugin = require('copy-webpack-plugin') 6 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 7 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 8 | const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin'); 9 | const autoprefixer = require('autoprefixer'); 10 | const DotenvPlugin = require('webpack-dotenv-extended-plugin'); 11 | const { getIfUtils, removeEmpty, propIf } = require('webpack-config-utils'); 12 | 13 | const PORT = 3000; 14 | const HOST = 'localhost' 15 | 16 | const sourcePath = path.join(__dirname); 17 | const appPath = path.join(__dirname, './src'); 18 | const buildPath = path.join(__dirname, './build'); 19 | 20 | module.exports = (env) => { 21 | const { ifDevelopment, ifProduction } = getIfUtils(env); 22 | 23 | return removeEmpty({ 24 | entry: removeEmpty({ 25 | app: removeEmpty([ 26 | ifDevelopment(`webpack-dev-server/client?http://${HOST}:${PORT}`), 27 | ifDevelopment('webpack/hot/only-dev-server'), 28 | './src/index', 29 | ]), 30 | }), 31 | 32 | output: removeEmpty({ 33 | filename: 'static/js/bundle-[hash:8].js', 34 | publicPath: '/', 35 | path: buildPath, 36 | }), 37 | 38 | devtool: propIf(env == 'development', 'eval', 'source-map'), 39 | 40 | devServer: ifDevelopment({ 41 | inline: true, 42 | host: HOST, 43 | port: PORT, 44 | historyApiFallback: true, 45 | hot: true, 46 | disableHostCheck: true, 47 | open: true, 48 | overlay: { 49 | warnings: true, 50 | errors: true 51 | } 52 | }), 53 | 54 | resolve: { 55 | extensions: ['.js', '.jsx'], 56 | modules: [ 57 | path.resolve(sourcePath, 'node_modules'), 58 | appPath 59 | ], 60 | }, 61 | 62 | module: { 63 | rules: removeEmpty([ 64 | { 65 | test: /\.(js|jsx)$/, 66 | include: appPath, 67 | use: { 68 | loader: 'babel-loader', 69 | options: removeEmpty({ 70 | babelrc: false, 71 | presets: [ 72 | ["env", { 73 | "targets": { 74 | "browsers": ["last 2 versions", "safari >= 7"] 75 | } 76 | }], 77 | "react", 78 | "stage-0" 79 | ], 80 | plugins: ifDevelopment(["react-hot-loader/babel"]), 81 | cacheDirectory: ifDevelopment(true), 82 | compact: ifProduction(true), 83 | }), 84 | } 85 | }, 86 | ifProduction({ 87 | test: /\.less$/, 88 | loader: ExtractTextPlugin.extract({ 89 | fallback: 'style-loader', 90 | use: [ 91 | 'css-loader', 92 | 'postcss-loader', 93 | 'less-loader' 94 | ] 95 | }) 96 | }), 97 | ifDevelopment({ 98 | test: /\.less$/, 99 | use: [ 100 | 'style-loader', 101 | 'css-loader?sourceMap', 102 | 'postcss-loader', 103 | 'less-loader?sourceMap' 104 | ] 105 | }), 106 | { 107 | test: /\.(jpg|jpeg|gif|png|svg)$/, 108 | loader: 'url-loader', 109 | options: { 110 | name: '[name].[ext]', 111 | outputPath: 'static/media/img/', 112 | limit: 10000 113 | } 114 | }, 115 | { 116 | test: /\.(ttf|eot|woff|woff2)$/, 117 | loader: 'file-loader', 118 | options: { 119 | name: 'static/media/fonts/[name].[ext]' 120 | } 121 | }, 122 | { 123 | test: /\.md$/, 124 | loader: 'file-loader', 125 | options: { 126 | name: 'static/media/md/[name].[ext]' 127 | } 128 | } 129 | ]) 130 | }, 131 | 132 | plugins: removeEmpty([ 133 | ifDevelopment(new CaseSensitivePathsWebpackPlugin()), 134 | ifDevelopment(new webpack.HotModuleReplacementPlugin()), 135 | ifProduction(new CleanWebpackPlugin(buildPath)), 136 | ifProduction(new UglifyJsPlugin({ 137 | parallel: true, 138 | sourceMap: true, 139 | uglifyOptions: { 140 | mangle: { safari10: true }, 141 | output: { comments: false } 142 | } 143 | })), 144 | ifProduction(new ExtractTextPlugin({ filename: 'static/css/bundle.css' })), 145 | new HtmlWebpackPlugin(removeEmpty({ 146 | path: propIf(env == 'development', appPath, buildPath), 147 | hash: ifDevelopment(true), 148 | template: path.join(appPath, 'index.html'), 149 | filename: 'index.html', 150 | minify: { 151 | removeComments: true, 152 | collapseWhitespace: true, 153 | removeRedundantAttributes: true, 154 | useShortDoctype: true, 155 | removeEmptyAttributes: true, 156 | removeStyleLinkTypeAttributes: true, 157 | keepClosingSlash: true, 158 | minifyJS: true, 159 | minifyCSS: true, 160 | minifyURLs: true, 161 | }, 162 | })), 163 | new webpack.LoaderOptionsPlugin(removeEmpty({ 164 | minimize: ifProduction(true), 165 | debug: ifProduction(false), 166 | options: { 167 | postcss: [ 168 | autoprefixer({ 169 | browsers: ['last 3 version', 'ie >= 10'] 170 | }) 171 | ], 172 | context: sourcePath 173 | } 174 | })), 175 | ifDevelopment(new webpack.NamedModulesPlugin()), 176 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 177 | new webpack.DefinePlugin({ 178 | 'process.env.NODE_ENV': JSON.stringify(env) 179 | }), 180 | new DotenvPlugin({ 181 | defaults: './config/.env.defaults', 182 | path: './config/.env.local' 183 | }) 184 | ]) 185 | }) 186 | } 187 | -------------------------------------------------------------------------------- /front/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | /config/.env.local 16 | 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* -------------------------------------------------------------------------------- /front/README.md: -------------------------------------------------------------------------------- 1 | # hackathon-etherauth 2 | -------------------------------------------------------------------------------- /front/config/.env.default: -------------------------------------------------------------------------------- 1 | API_URL_DEV='http://localhost:3000/api' -------------------------------------------------------------------------------- /front/config/config.js: -------------------------------------------------------------------------------- 1 | export const requestsConfig = { 2 | "USE_MOCK": false, 3 | "COLLAPSED_LOG_REQUESTS": true, 4 | "API_URL": process.env.NODE_ENV === 'development' 5 | ? `${process.env.API_URL_DEV}` 6 | : window.location.origin + '/api' 7 | } 8 | -------------------------------------------------------------------------------- /front/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "module": "none", 5 | "moduleResolution": "node", 6 | "target": "es6", 7 | "sourceMap": true, 8 | "noImplicitAny": false, 9 | "outDir": "./build/", 10 | "preserveConstEnums": true, 11 | "allowJs": false, 12 | "experimentalDecorators": true, 13 | "baseUrl": "./" 14 | }, 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } -------------------------------------------------------------------------------- /front/config/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended", 4 | "tslint-react" 5 | ], 6 | "rules": { 7 | "use-isnan": true, 8 | "indent": [ 9 | true, 10 | "spaces", 11 | 2 12 | ], 13 | "arrow-return-shorthand": false, 14 | "space-before-function-paren": [ 15 | true, 16 | { 17 | "anonymous": "always", 18 | "named": "never", 19 | "asyncArrow": "always" 20 | } 21 | ], 22 | "no-bitwise": false, 23 | "ban-types": [ 24 | true, 25 | [ 26 | "Object", 27 | "Use 'object' instead." 28 | ], 29 | [ 30 | "Boolean", 31 | "Use 'boolean' instead." 32 | ], 33 | [ 34 | "Number", 35 | "Use 'number' instead." 36 | ], 37 | [ 38 | "String", 39 | "Use 'string' instead." 40 | ] 41 | ], 42 | "object-literal-key-quotes": [ 43 | false 44 | ], 45 | "no-string-literal": false, 46 | "jsx-no-lambda": false, 47 | "jsx-no-multiline-js": false, 48 | "no-shadowed-variable": false, 49 | "prefer-const": false, 50 | "comment-format": [ 51 | false 52 | ], 53 | "no-namespace": false, 54 | "array-type": [ 55 | true, 56 | "array" 57 | ], 58 | "max-line-length": [ 59 | true, 60 | 180 61 | ], 62 | "new-parens": true, 63 | "no-arg": true, 64 | "no-conditional-assignment": false, 65 | "no-consecutive-blank-lines": [ 66 | true, 67 | 2 68 | ], 69 | "quotemark": [ 70 | true, 71 | "single", 72 | "jsx-double" 73 | ], 74 | "no-var-requires": false, 75 | "trailing-comma": [ 76 | true, 77 | { 78 | "multiline": "always", 79 | "singleline": "never" 80 | } 81 | ], 82 | "unified-signatures": false, 83 | "prefer-for-of": false, 84 | "curly": false, 85 | "max-classes-per-file": [ 86 | false 87 | ], 88 | "object-literal-sort-keys": false, 89 | "no-empty-interface": false, 90 | "ordered-imports": [ 91 | false 92 | ], 93 | "only-arrow-functions": [ 94 | false 95 | ], 96 | "no-console": [ 97 | false 98 | ], 99 | "callable-types": false, 100 | "member-ordering": [ 101 | true, 102 | { 103 | "order": [ 104 | "private-static-field", 105 | "protected-static-field", 106 | "public-static-field", 107 | "private-static-method", 108 | "protected-static-method", 109 | "public-static-method", 110 | "private-instance-field", 111 | "protected-instance-field", 112 | "public-instance-field", 113 | "constructor", 114 | "private-instance-method", 115 | "protected-instance-method", 116 | "public-instance-method" 117 | ] 118 | } 119 | ] 120 | }, 121 | "jsRules": { 122 | "max-line-length": [ 123 | true, 124 | 180 125 | ] 126 | } 127 | } -------------------------------------------------------------------------------- /front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etherauth", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "./node_modules/.bin/webpack-dev-server -d --progress --env=development", 7 | "build": "./node_modules/.bin/webpack -p --progress --colors --env=production" 8 | }, 9 | "devDependencies": { 10 | "autoprefixer": "^6.7.7", 11 | "babel-core": "^6.26.0", 12 | "babel-loader": "^6.4.1", 13 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 14 | "babel-polyfill": "^6.26.0", 15 | "babel-preset-env": "^1.6.1", 16 | "babel-preset-react": "^6.24.1", 17 | "babel-preset-stage-0": "^6.24.1", 18 | "case-sensitive-paths-webpack-plugin": "^2.1.1", 19 | "clean-webpack-plugin": "^0.1.19", 20 | "copy-webpack-plugin": "^4.5.1", 21 | "css-loader": "^0.28.0", 22 | "extract-text-webpack-plugin": "^2.1.0", 23 | "file-loader": "^0.11.1", 24 | "html-loader": "^0.5.5", 25 | "html-webpack-plugin": "^2.28.0", 26 | "less-loader": "^4.1.0", 27 | "postcss-loader": "^1.3.3", 28 | "react-hot-loader": "^4.0.0", 29 | "redux-devtools": "^3.0.1", 30 | "resolve-url-loader": "^2.1.0", 31 | "style-loader": "^0.16.1", 32 | "uglifyjs-webpack-plugin": "^1.2.2", 33 | "url-loader": "^0.5.8", 34 | "webpack": "3.11.0", 35 | "webpack-config-utils": "^2.3.0", 36 | "webpack-dev-server": "^2.11.2", 37 | "webpack-dotenv-extended-plugin": "^1.0.0" 38 | }, 39 | "dependencies": { 40 | "axios": "^0.17.1", 41 | "axios-mock-adapter": "^1.15.0", 42 | "classnames": "^2.2.5", 43 | "ethjs": "^0.3.1", 44 | "ethjs-contract": "^0.1.9", 45 | "ethjs-query": "^0.3.2", 46 | "less": "^3.0.4", 47 | "js-sha256": "^0.9.0", 48 | "lodash": "^4.17.5", 49 | "react": "^16.2.0", 50 | "react-dom": "^16.2.0", 51 | "react-redux": "^5.0.2", 52 | "redux": "^3.6.0", 53 | "redux-thunk": "^2.2.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /front/src/api/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import MockAdapter from 'axios-mock-adapter'; 4 | import subscribeMockRequests from './apiMock'; 5 | import { requestsConfig } from '../../config/config'; 6 | 7 | if (requestsConfig.USE_MOCK) { 8 | subscribeMockRequests(new MockAdapter(axios)); 9 | } 10 | 11 | function logFetch(promise) { 12 | 13 | console.clear(); 14 | 15 | function wrapper(url, data, method, mock) { 16 | 17 | const result = promise(url, data, method, mock); 18 | 19 | result 20 | .then(response => { 21 | 22 | const str = requestsConfig.USE_MOCK ? 23 | [ 24 | `%c MOCK %c %c ${method.toUpperCase()} %c ${url}`, 25 | 'border: 1px solid yellow; color: white;', '', 26 | 'background-color: green; color: white', '' 27 | ] : 28 | [ 29 | `%c ${method.toUpperCase()} `, 30 | 'background-color: green; color: white', 31 | `${url}` 32 | ]; 33 | 34 | if (requestsConfig.COLLAPSED_LOG_REQUESTS) { 35 | console.groupCollapsed(...str); 36 | } else { 37 | console.group(...str); 38 | } 39 | console.log('HANDLER: ', url); 40 | console.log('METHOD: ', method.toUpperCase()); 41 | if (data !== undefined) console.log('SENDING REQUEST OBJECT: ', data); 42 | console.log(`RESPONSE STATUS: ${response.status}`); 43 | console.log('RESPONSE DATA: ', response.data); 44 | console.groupEnd(); 45 | }) 46 | .catch(error => { 47 | console.group(`ERROR!!! REQUEST. HANDLER: ${url} %c Error `, 'background-color: red; color: white'); 48 | if (data !== undefined) console.log('SENDING REQUEST OBJECT: ', data); 49 | console.log('ERROR: ', error); 50 | console.groupEnd(); 51 | }); 52 | 53 | return result; 54 | } 55 | return wrapper; 56 | } 57 | 58 | const apiNew = (url = '/', data = undefined, method = 'post', mock = requestsConfig.USE_MOCK) => { 59 | const config = { 60 | method, 61 | baseURL: requestsConfig.API_URL, 62 | url, 63 | }; 64 | 65 | if (data !== undefined) { 66 | config['data'] = data; 67 | } 68 | 69 | return axios(config); 70 | }; 71 | 72 | export const fetch = process.env.NODE_ENV !== 'production' ? logFetch(apiNew) : apiNew; 73 | -------------------------------------------------------------------------------- /front/src/api/apiMock.js: -------------------------------------------------------------------------------- 1 | import * as constructors from './mock/constructors'; 2 | import * as instances from './mock/instances'; 3 | 4 | export default function subscribeMockRequests(mockApi) { 5 | mockApi 6 | // .onAny('/constructors').reply(200, constructors.constructorListProd) 7 | .onAny().passThrough(); 8 | 9 | } -------------------------------------------------------------------------------- /front/src/api/apiRequests.js: -------------------------------------------------------------------------------- 1 | import store from '../store/store'; 2 | import { fetch } from './api'; 3 | // import { 4 | // fetchCtorsRequest, 5 | // fetchCtorsFailure, 6 | // fetchCtorsSuccess, 7 | // fetchCtorParamsRequest, 8 | // fetchCtorParamsFailure, 9 | // fetchCtorParamsSuccess, 10 | // } from '../app/common/ctor-card/CtorsActions'; 11 | 12 | const { dispatch } = store; 13 | 14 | // ============================================================================= 15 | // Constructors 16 | // ============================================================================= 17 | 18 | export function getConstructors() { 19 | const result = fetch('/constructors', undefined, 'get'); 20 | 21 | dispatch(fetchCtorsRequest()); 22 | 23 | result 24 | .then(response => { 25 | if (response.status === 200) { 26 | dispatch(fetchCtorsSuccess(response.data)) 27 | } 28 | }) 29 | .catch(error => { 30 | dispatch(fetchCtorsFailure(error.message)) 31 | }); 32 | 33 | return result; 34 | }; 35 | -------------------------------------------------------------------------------- /front/src/app/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { hot } from 'react-hot-loader' 3 | 4 | import { checkMetaMask } from '../helpers/eth'; 5 | import Metamask from './metamask/Metamask'; 6 | import Register from './register/Register'; 7 | import Main from './main/Main'; 8 | import Admin from './admin/Admin'; 9 | import Mining from './mining/Mining'; 10 | 11 | import './App.less'; 12 | 13 | class App extends Component { 14 | render() { 15 | const { screen } = this.props; 16 | 17 | const status = checkMetaMask(); 18 | 19 | let content; 20 | 21 | if (status !== 'okMetamask') { 22 | content = 23 | } else { 24 | switch (screen) { 25 | case 'main-screen': 26 | content =
27 | break; 28 | case 'register-screen': 29 | content = 30 | break; 31 | case 'admin-screen': 32 | content = 33 | break; 34 | case 'mining-screen': 35 | content = 36 | break; 37 | 38 | default: 39 | break; 40 | } 41 | } 42 | 43 | return ( 44 |
45 | {content} 46 |
47 | ); 48 | } 49 | }; 50 | 51 | 52 | export default hot(module) (App); -------------------------------------------------------------------------------- /front/src/app/App.less: -------------------------------------------------------------------------------- 1 | @import '../styles/variables.less'; 2 | 3 | .app { 4 | background: radial-gradient(ellipse at center, rgb(255, 255, 255), rgb(23, 128, 147)); 5 | } 6 | -------------------------------------------------------------------------------- /front/src/app/AppActions.js: -------------------------------------------------------------------------------- 1 | export const changeScreen = (screen) => ({ 2 | type: 'CHANGE_SCREEN', 3 | screen 4 | }); 5 | -------------------------------------------------------------------------------- /front/src/app/AppContainer.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import App from './App'; 4 | 5 | export default connect(state => ({ 6 | screen: state.app.screen 7 | }))(App); 8 | -------------------------------------------------------------------------------- /front/src/app/AppReducer.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | app: '', 3 | screen: 'main-screen' 4 | // screen: 'register-screen' 5 | }; 6 | 7 | const app = (state = initState, action) => { 8 | switch (action.type) { 9 | case 'CHANGE_SCREEN': 10 | return { ...state, screen: action.screen }; 11 | 12 | default: 13 | return state; 14 | } 15 | } 16 | 17 | export default app; 18 | -------------------------------------------------------------------------------- /front/src/app/admin/Admin.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import { ABI, ADDRESS } from '../../constants/constants'; 4 | import { web3 } from '../../helpers/eth'; 5 | import store from '../../store/store'; 6 | import { changeScreen } from '../AppActions'; 7 | 8 | import './Admin.less'; 9 | 10 | export default class Admin extends PureComponent { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | value: '', 16 | valide: true, 17 | authAddress: '---', 18 | recoveryAddress: '---', 19 | error: '', 20 | login: '' 21 | }; 22 | } 23 | 24 | onChange = (e) => { 25 | const { valide, value } = this.state; 26 | 27 | if (!valide) 28 | this.setState({ valide: true }) 29 | 30 | this.setState({ value: e.target.value }); 31 | } 32 | 33 | // get address 34 | componentDidMount() { 35 | const { value, login } = this.state; 36 | const instance = web3.eth.contract(ABI).at(ADDRESS); 37 | 38 | let result; 39 | try { 40 | result = instance['authAddress'].call(login, 41 | (error, result) => { 42 | if (!error) { 43 | this.setState({ authAddress: result }); 44 | } else { 45 | this.setState({ error }); 46 | 47 | console.log('Error search'); 48 | } 49 | }); 50 | } catch (error) { 51 | this.setState({ error }); 52 | 53 | console.error('global error', error); 54 | } 55 | 56 | try { 57 | result = instance['recoveryAddress'].call(login, 58 | (error, result) => { 59 | if (!error) { 60 | this.setState({ recoveryAddress: result }); 61 | } else { 62 | this.setState({ error }); 63 | 64 | console.log('Error search'); 65 | } 66 | }); 67 | } catch (error) { 68 | this.setState({ error }); 69 | 70 | console.error('global error', error); 71 | } 72 | } 73 | 74 | 75 | componentWillMount() { 76 | const login = localStorage.getItem('login'); 77 | 78 | if (login !== undefined || login !== null || login !== '') { 79 | this.setState({ login }); 80 | } 81 | } 82 | 83 | getReceipt(tx) { 84 | web3.eth.getTransactionReceipt(tx, (err, receipt) => { 85 | if (null == receipt) 86 | window.setTimeout(() => { this.getReceipt(tx) }, 500); 87 | else { 88 | console.log('done!!!!!!!!!!!!!!!!!'); 89 | // localStorage.setItem('login', this.state.value); 90 | store.dispatch(changeScreen('admin-screen')); 91 | } 92 | }); 93 | } 94 | 95 | // set Recovery 96 | onClick = () => { 97 | const { login, value, valide } = this.state; 98 | 99 | if (value !== '') { 100 | if (!valide) 101 | this.setState({ valide: true }) 102 | 103 | const instance = web3.eth.contract(ABI).at(ADDRESS); 104 | 105 | let result; 106 | try { 107 | result = instance['setRecoveryAddress'](login, value, 108 | (error, result) => { 109 | if (!error) { 110 | this.getReceipt(result); 111 | store.dispatch(changeScreen('mining-screen')); 112 | } else { 113 | console.log('Error in send'); 114 | } 115 | }); 116 | } catch (error) { 117 | console.error('global error', error); 118 | } 119 | } else { 120 | this.setState({ valide: false }); 121 | } 122 | } 123 | 124 | render() { 125 | const { authAddress, error, recoveryAddress, login } = this.state; 126 | 127 | return ( 128 |
129 | {error && 130 |

{error}

131 | } 132 | {authAddress === recoveryAddress 133 | ?
134 |

Warning: Auth key = Recorery key

135 |

Change your Recovery key

136 |
137 | : null 138 | } 139 |
140 |

Login: {login}

141 |

Auth address: {authAddress}

142 |

Recovery address: {recoveryAddress}

143 |
144 | 151 | 156 |
157 | ); 158 | } 159 | }; -------------------------------------------------------------------------------- /front/src/app/admin/Admin.less: -------------------------------------------------------------------------------- 1 | .admin { 2 | flex-direction: column; 3 | 4 | width: 700px; 5 | 6 | .warning { 7 | width: 100%; 8 | padding-left: 30px; 9 | 10 | text-align: left; 11 | 12 | color: white; 13 | border-radius: 5px; 14 | background-color: tomato; 15 | } 16 | 17 | .description { 18 | width: 100%; 19 | 20 | text-align: left; 21 | p { 22 | font-size: 17px; 23 | } 24 | } 25 | 26 | button { 27 | height: 50px; 28 | 29 | border: none; 30 | outline: none; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /front/src/app/common/loader.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 12 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /front/src/app/common/loader2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | ethereum 13 | ethereum,brand wb 14 | cc0 15 | vil5qg 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /front/src/app/main/Main.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import store from '../../store/store'; 4 | import { changeScreen } from '../AppActions'; 5 | 6 | import './Main.less'; 7 | 8 | export default class Main extends PureComponent { 9 | onClick = (screen) => { 10 | return () => store.dispatch(changeScreen(screen)) 11 | } 12 | 13 | render() { 14 | return ( 15 |
16 | 21 | 26 |
27 | ); 28 | } 29 | }; -------------------------------------------------------------------------------- /front/src/app/main/Main.less: -------------------------------------------------------------------------------- 1 | .main-screen { 2 | display: flex; 3 | align-items: center; 4 | flex-direction: column; 5 | justify-content: space-between; 6 | 7 | button { 8 | &:first-child { 9 | margin-bottom: 30px; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /front/src/app/metamask/Metamask.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import metamaskSvg from '../../assets/img/metamask.svg'; 4 | 5 | import './Metamask.less'; 6 | 7 | export default class Metamask extends PureComponent { 8 | render() { 9 | const { status } = this.props; 10 | 11 | let content; 12 | 13 | switch (status) { 14 | case 'unlockMetamask': 15 | content =

Please, unlock metamask

16 | break; 17 | case 'noMetamask': 18 | content = ( 19 |
20 |

Please, install metamask

21 | 23 | 24 | 25 |
26 | ); 27 | break; 28 | default: 29 | content =

Hmmmmmm, some problem!

30 | break; 31 | } 32 | return ( 33 |
34 |
35 | metamask 36 | {content} 37 | 38 |
39 |
40 | ); 41 | } 42 | }; -------------------------------------------------------------------------------- /front/src/app/metamask/Metamask.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/variables.less'; 2 | 3 | .metamask { 4 | @firefox-size: 220px; 5 | 6 | position: relative; 7 | 8 | display: flex; 9 | overflow: hidden; /* clearfix */ 10 | 11 | width: 50%; 12 | margin: auto; 13 | // transform: scale(.5); 14 | // opacity: .7; 15 | // display: flex; 16 | // align-items: center; 17 | // border-radius: 50%; 18 | 19 | border-radius: 16px; 20 | background: silver; 21 | // background-image: linear-gradient(90deg, #125F6D 0%, #177E8F 30%, #177E8F 70%, #16728B 100%); 22 | background-color: #177E8F; 23 | box-shadow: 0px 0px 90px 0px rgba(50, 50, 50, .4); 24 | 25 | &::before { 26 | float: left; 27 | 28 | padding-top: 100%; 29 | 30 | content: ''; 31 | } 32 | .firefox { 33 | width: @firefox-size; 34 | height: @firefox-size; 35 | } 36 | 37 | p { 38 | margin: 0; 39 | padding: 0; 40 | 41 | letter-spacing: .3px; 42 | 43 | color: #FFFFFF; 44 | 45 | font-family: @font-primary; 46 | font-size: 15px; 47 | line-height: 19px; 48 | } 49 | 50 | .wrapper { 51 | position: absolute; 52 | 53 | flex-direction: column; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /front/src/app/mining/Mining.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import loaderSvg from '../common/loader2.svg'; 4 | 5 | import './Mining.less'; 6 | 7 | export default class Mining extends PureComponent { 8 | render() { 9 | return ( 10 |
11 |

Please wait

12 |

mining process...

13 | loader 14 |
15 | ); 16 | } 17 | }; -------------------------------------------------------------------------------- /front/src/app/mining/Mining.less: -------------------------------------------------------------------------------- 1 | .mining-screen { 2 | position: relative; 3 | 4 | flex-direction: column; 5 | 6 | height: 60vh; 7 | 8 | img { 9 | position: absolute; 10 | } 11 | 12 | p { 13 | z-index: 10; 14 | 15 | margin: 17px 0; 16 | 17 | font-size: 28px; 18 | 19 | &:first-child { 20 | font-size: 50px; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /front/src/app/register/Register.jsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | 3 | import { getNetworkId, web3 } from '../../helpers/eth'; 4 | import { ABI, ADDRESS } from '../../constants/constants'; 5 | import store from '../../store/store'; 6 | import { changeScreen } from '../AppActions'; 7 | 8 | import './Register.less'; 9 | 10 | export default class Register extends PureComponent { 11 | constructor(props) { 12 | super(props); 13 | 14 | this.state = { 15 | value: '', 16 | valide: true 17 | }; 18 | } 19 | 20 | getReceipt(tx) { 21 | web3.eth.getTransactionReceipt(tx, (err, receipt) => { 22 | if (null == receipt) 23 | window.setTimeout(() => { this.getReceipt(tx) }, 500); 24 | else { 25 | console.log('done!!!!!!!!!!!!!!!!!'); 26 | localStorage.setItem('login', this.state.value); 27 | store.dispatch(changeScreen('admin-screen')); 28 | } 29 | }); 30 | } 31 | 32 | onCheck = () => { 33 | const { value, valide } = this.state; 34 | 35 | if (value !== '') { 36 | if (!valide) 37 | this.setState({ valide: true }) 38 | 39 | const instance = web3.eth.contract(ABI).at(ADDRESS); 40 | console.log('instance', instance); 41 | 42 | let result; 43 | try { 44 | result = instance['authAddress'].call(value, 45 | (error, result) => { 46 | if (!error) { 47 | console.log('result search: ', result); 48 | 49 | } else { 50 | console.log('Error search'); 51 | } 52 | }); 53 | } catch (error) { 54 | console.error('global error', error); 55 | } 56 | } else { 57 | this.setState({ valide: false }); 58 | } 59 | 60 | 61 | } 62 | 63 | onClick = () => { 64 | const { value, valide } = this.state; 65 | 66 | if (value !== '') { 67 | if (!valide) 68 | this.setState({ valide: true }) 69 | 70 | const instance = web3.eth.contract(ABI).at(ADDRESS); 71 | console.log('instance', instance); 72 | 73 | let result; 74 | try { 75 | result = instance['createAccount'](value, 76 | (error, result) => { 77 | if (!error) { 78 | this.getReceipt(result); 79 | store.dispatch(changeScreen('mining-screen')); 80 | } else { 81 | console.log('Error in send'); 82 | } 83 | }); 84 | } catch (error) { 85 | console.error('global error', error); 86 | } 87 | } else { 88 | this.setState({ valide: false }); 89 | } 90 | } 91 | 92 | onChange = (e) => { 93 | const { valide, value } = this.state; 94 | 95 | if (!valide) 96 | this.setState({ valide: true }) 97 | 98 | this.setState({ value: e.target.value }); 99 | } 100 | 101 | 102 | render() { 103 | return ( 104 |
105 |
106 |

Username:

107 |
108 | 115 | 118 |
119 | ); 120 | } 121 | }; -------------------------------------------------------------------------------- /front/src/app/register/Register.less: -------------------------------------------------------------------------------- 1 | .register { 2 | flex-direction: column; 3 | 4 | .username { 5 | width: 100%; 6 | 7 | font-size: 18px; 8 | } 9 | 10 | .no-valide { 11 | border: 2px solid red; 12 | } 13 | 14 | button { 15 | height: 50px; 16 | 17 | border: none; 18 | outline: none; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /front/src/assets/img/metamask.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /front/src/constants/constants.js: -------------------------------------------------------------------------------- 1 | export const ADDRESS = '0xd68bef63b58efb50c4a4b8625f7336ad73952dba'; 2 | 3 | export const ABI = [{"constant":false,"inputs":[{"name":"_login","type":"string"}],"name":"dropAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_login","type":"string"}],"name":"authAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"string"}],"name":"createAccount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_login","type":"string"}],"name":"recoveryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"data","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"signerAddressRaw","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"data","type":"bytes32"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"signerAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"string"},{"name":"_addr","type":"address"}],"name":"setRecoveryAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_login","type":"string"},{"name":"_addr","type":"address"}],"name":"setAuthAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"login","type":"string"}],"name":"Create","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"login","type":"string"},{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"}],"name":"AuthChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"login","type":"string"},{"indexed":false,"name":"from","type":"address"},{"indexed":false,"name":"to","type":"address"}],"name":"RecoveryChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"login","type":"string"},{"indexed":false,"name":"by","type":"address"}],"name":"Drop","type":"event"}]; 4 | -------------------------------------------------------------------------------- /front/src/helpers/detect-device.js: -------------------------------------------------------------------------------- 1 | const userAgent = navigator.userAgent.toLowerCase(); 2 | 3 | export const IS_IOS = ['ipad', 'iphone', 'ipod'].indexOf((navigator.platform || '').toLowerCase()) >= 0; 4 | export const IS_ANDROID = /(android)/i.test(userAgent); 5 | export const IS_XIAOMI_BROWSER = /(MiuiBrowser)/i.test(userAgent); 6 | export const IS_MOBILE_OS = IS_IOS || IS_ANDROID; -------------------------------------------------------------------------------- /front/src/helpers/eth.js: -------------------------------------------------------------------------------- 1 | export var web3 = window.Web3 2 | ? new window.Web3(window.web3.currentProvider) 3 | : undefined; 4 | 5 | export const checkMetaMask = () => { 6 | if (!window.Web3) { 7 | return 'noMetamask'; 8 | } 9 | 10 | if (!web3.eth.accounts[0]) { 11 | return 'unlockMetamask'; 12 | } 13 | 14 | return 'okMetamask'; 15 | }; 16 | 17 | export const getNetworkId = cb => { 18 | web3.version.getNetwork((err, netId) => { 19 | err && console.log(err); 20 | netId && cb(netId); 21 | }); 22 | }; 23 | 24 | export const getNetworkName = netId => { 25 | switch (netId.toString()) { 26 | case "1": 27 | return "Mainnet"; 28 | case "3": 29 | return "Ropsten"; 30 | case "4": 31 | return "Rinkeby"; 32 | case "42": 33 | return "Kovan"; 34 | default: 35 | return "Error! Unknown or deprecated network"; 36 | } 37 | }; 38 | 39 | export const getNetworkEtherscanAddress = netId => { 40 | switch (netId.toString()) { 41 | case "1": 42 | return "https://etherscan.io"; 43 | case "3": 44 | return "https://ropsten.etherscan.io"; 45 | case "4": 46 | return "https://rinkeby.etherscan.io"; 47 | case "42": 48 | return "https://kovan.etherscan.io"; 49 | default: 50 | return "Error! Unknown or deprecated network"; 51 | } 52 | }; 53 | 54 | export const isAddress = (hash) => { 55 | if (typeof hash === 'string') { 56 | return /^0x([A-Fa-f0-9]{40})$/.test(hash); 57 | } else { 58 | return false; 59 | } 60 | }; 61 | 62 | export const isTx = (hash) => { 63 | if (typeof hash === 'string') { 64 | return /^0x([A-Fa-f0-9]{64})$/.test(hash); 65 | } else { 66 | return false; 67 | } 68 | }; -------------------------------------------------------------------------------- /front/src/helpers/utils.js: -------------------------------------------------------------------------------- 1 | export function generateId() { 2 | let text = ""; 3 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 4 | 5 | for (let i = 0; i < 5; i++) 6 | text += possible.charAt(Math.floor(Math.random() * possible.length)); 7 | 8 | return text; 9 | } -------------------------------------------------------------------------------- /front/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Etherauth 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /front/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | 5 | import store from './store/store'; 6 | import App from './app/AppContainer'; 7 | 8 | import './index.less'; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | , 14 | document.getElementById('root') 15 | ); -------------------------------------------------------------------------------- /front/src/index.less: -------------------------------------------------------------------------------- 1 | @import './styles/index.less'; 2 | 3 | body { 4 | color: #000000; 5 | background: radial-gradient(ellipse at center, #A8EDF2, #558892); /*Standard*/ 6 | 7 | font-family: @font-primary; 8 | font-size: 12px; 9 | line-height: 17px; 10 | } 11 | 12 | * { 13 | box-sizing: border-box; 14 | } 15 | -------------------------------------------------------------------------------- /front/src/store/store.js: -------------------------------------------------------------------------------- 1 | import { combineReducers, createStore } from 'redux'; 2 | 3 | import app from '../app/AppReducer'; 4 | 5 | const reducers = combineReducers({ 6 | app, 7 | }); 8 | 9 | const store = createStore( 10 | reducers, 11 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 12 | ); 13 | 14 | export default store; 15 | -------------------------------------------------------------------------------- /front/src/styles/base.less: -------------------------------------------------------------------------------- 1 | @import './variables.less'; 2 | 3 | .flex { 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | } 8 | 9 | .flex-h { 10 | display: flex; 11 | justify-content: center; 12 | } 13 | 14 | .flex-v { 15 | display: flex; 16 | align-items: center; 17 | } 18 | 19 | .screen { 20 | position: absolute; 21 | top: 0; 22 | right: 0; 23 | bottom: 0; 24 | left: 0; 25 | } 26 | 27 | 28 | .btn-default { 29 | display: block; 30 | 31 | width: 340px; 32 | height: 90px; 33 | padding: 0 10px; 34 | 35 | cursor: pointer; 36 | text-decoration: none; 37 | text-transform: uppercase; 38 | 39 | color: whitesmoke; 40 | border: none; 41 | border-radius: 5px; 42 | outline: none; 43 | background-color: rgba(23, 126, 143, .8); 44 | 45 | font-family: @font-primary; 46 | font-size: 16px; 47 | 48 | &:active { 49 | background-color: rgba(23, 126, 143, 1); 50 | } 51 | } 52 | 53 | .form { 54 | min-width: 500px; 55 | padding: 50px; 56 | 57 | border: 2px solid rgba(23, 126, 143, .2); 58 | border-radius: 8px; 59 | background-color: rgba(23, 126, 143, .4); 60 | } 61 | 62 | .form-input { 63 | width: 100%; 64 | height: 60px; 65 | margin-bottom: 50px; 66 | padding: 0 15px; 67 | 68 | opacity: .7; 69 | border: 2px solid gray; 70 | border-radius: 4px; 71 | outline: none; 72 | background-color: wheat; 73 | 74 | font-size: 24px; 75 | 76 | &:focus { 77 | opacity: 1; 78 | } 79 | } 80 | 81 | .no-valide { 82 | border: 2px solid red; 83 | } 84 | -------------------------------------------------------------------------------- /front/src/styles/components.less: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /front/src/styles/fonts.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mixbytes/etherauth/5bb6ad9b720b0ed39c766acca02682dc00b21e91/front/src/styles/fonts.less -------------------------------------------------------------------------------- /front/src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import './variables.less'; 2 | @import './base.less'; 3 | @import './normalize.less'; 4 | @import './mixins.less'; 5 | @import './fonts.less'; 6 | @import './components.less'; 7 | -------------------------------------------------------------------------------- /front/src/styles/levels.less: -------------------------------------------------------------------------------- 1 | // $level-low: 100000; 2 | // $level-medium: 10000000; 3 | // $level-high: 10000000000; 4 | 5 | // @mixin level($lvl: $level-low, $sublvl: 0) { 6 | // z-index: $lvl + $sublvl; 7 | // } 8 | -------------------------------------------------------------------------------- /front/src/styles/mixins.less: -------------------------------------------------------------------------------- 1 | .gradient-animation(@lg1, @lg2, @transTimeStart, @transTimeEnd) { 2 | position: relative; 3 | z-index: 100; 4 | 5 | background-image: linear-gradient(@lg1); 6 | background-size: 100%; 7 | &:before { 8 | position: absolute; 9 | z-index: -100; 10 | top: 0; 11 | left: 0; 12 | 13 | display: block; 14 | 15 | width: 100%; 16 | height: 100%; 17 | 18 | content: ''; 19 | transition: opacity @transTimeEnd; 20 | 21 | opacity: 0; 22 | border-radius: 4px; 23 | background-image: linear-gradient(@lg2); 24 | } 25 | &:hover, &:focus { 26 | &:before { 27 | transition: opacity @transTimeStart; 28 | 29 | opacity: 1; 30 | } 31 | } 32 | } 33 | 34 | .transitions(@transProp, @transDur) { 35 | transition-duration: @transDur; 36 | transition-property: @transProp; 37 | } 38 | 39 | 40 | // @mixin css3-prefix($property, $value) { 41 | // -webkit-#{$property}: #{$value}; 42 | // -khtml-#{$property}: #{$value}; 43 | // -moz-#{$property}: #{$value}; 44 | // -ms-#{$property}: #{$value}; 45 | // -o-#{$property}: #{$value}; 46 | // #{$property}: #{$value}; 47 | // } 48 | 49 | // @mixin unselectable() { 50 | // @include css3-prefix(user-select, none); 51 | // } 52 | 53 | // @mixin box-shadow($top: 0, $left: 0, $blur: 5px, $color: black, $inset: false) { 54 | // @if $inset { 55 | // @include css3-prefix(box-shadow, (inset $top $left $blur $color)); 56 | // } @else { 57 | // @include css3-prefix(box-shadow, ($top $left $blur $color)); 58 | // } 59 | // } 60 | 61 | // @mixin border-radius($radius: 5px) { 62 | // @include css3-prefix('border-radius', $radius); 63 | // } 64 | 65 | // @mixin background-gradient($startColor: #3C3C3C, $endColor: #999999) { 66 | // background-color: $startColor; 67 | // background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)); 68 | // background-image: -webkit-linear-gradient(top, $startColor, $endColor); 69 | // background-image: -moz-linear-gradient(top, $startColor, $endColor); 70 | // background-image: -ms-linear-gradient(top, $startColor, $endColor); 71 | // background-image: -o-linear-gradient(top, $startColor, $endColor); 72 | // background-image: linear-gradient(top, $startColor, $endColor); 73 | // filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#{$startColor}', endColorStr='#{$endColor}'); 74 | // } 75 | 76 | // @mixin background-horizontal($startColor: #3C3C3C, $endColor: #999999) { 77 | // background-color: $startColor; 78 | // background-image: -webkit-gradient(linear, left top, right top, from($startColor), to($endColor)); 79 | // background-image: -webkit-linear-gradient(left, $startColor, $endColor); 80 | // background-image: -moz-linear-gradient(left, $startColor, $endColor); 81 | // background-image: -ms-linear-gradient(left, $startColor, $endColor); 82 | // background-image: -o-linear-gradient(left, $startColor, $endColor); 83 | // background-image: linear-gradient(left, $startColor, $endColor); 84 | // filter: progid:DXImageTransform.Microsoft.gradient(startColorStr='#{$startColor}', endColorStr='#{$endColor}', gradientType='1'); 85 | // } 86 | 87 | // @mixin background-radial($startColor: #FFFFFF, $startPos: 0%, $endColor: #000000, $endPos:100%) { 88 | // background: -moz-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 89 | // background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop($startPos, $startColor), color-stop($endPos, $endColor)); 90 | // background: -webkit-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 91 | // background: -o-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 92 | // background: -ms-radial-gradient(center, ellipse cover, $startColor $startPos, $endColor $endPos); 93 | // background: radial-gradient(ellipse at center, $startColor $startPos, $endColor $endPos); 94 | // } 95 | 96 | // @mixin background-opacity($color: #000, $opacity: 0.85) { 97 | // background: $color; 98 | // background: rgba($color, $opacity); 99 | // } 100 | 101 | // @mixin vh-stretch() { 102 | // position: absolute; 103 | // top: 0; 104 | // bottom: 0; 105 | // left: 0; 106 | // width: 100%; 107 | // } 108 | 109 | // @mixin clickable-icon() { 110 | // opacity: 0.5; 111 | // cursor: pointer; 112 | // &:hover { 113 | // opacity: 1; 114 | // } 115 | // } 116 | -------------------------------------------------------------------------------- /front/src/styles/normalize.less: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Correct the font size and margin on `h1` elements within `section` and 29 | * `article` contexts in Chrome, Firefox, and Safari. 30 | */ 31 | 32 | h1 { 33 | font-size: 2em; 34 | margin: 0.67em 0; 35 | } 36 | 37 | /* Grouping content 38 | ========================================================================== */ 39 | 40 | /** 41 | * 1. Add the correct box sizing in Firefox. 42 | * 2. Show the overflow in Edge and IE. 43 | */ 44 | 45 | hr { 46 | box-sizing: content-box; /* 1 */ 47 | height: 0; /* 1 */ 48 | overflow: visible; /* 2 */ 49 | } 50 | 51 | /** 52 | * 1. Correct the inheritance and scaling of font size in all browsers. 53 | * 2. Correct the odd `em` font sizing in all browsers. 54 | */ 55 | 56 | pre { 57 | font-family: monospace, monospace; /* 1 */ 58 | font-size: 1em; /* 2 */ 59 | } 60 | 61 | /* Text-level semantics 62 | ========================================================================== */ 63 | 64 | /** 65 | * Remove the gray background on active links in IE 10. 66 | */ 67 | 68 | a { 69 | background-color: transparent; 70 | } 71 | 72 | /** 73 | * 1. Remove the bottom border in Chrome 57- 74 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 75 | */ 76 | 77 | abbr[title] { 78 | border-bottom: none; /* 1 */ 79 | text-decoration: underline; /* 2 */ 80 | text-decoration: underline dotted; /* 2 */ 81 | } 82 | 83 | /** 84 | * Add the correct font weight in Chrome, Edge, and Safari. 85 | */ 86 | 87 | b, 88 | strong { 89 | font-weight: bolder; 90 | } 91 | 92 | /** 93 | * 1. Correct the inheritance and scaling of font size in all browsers. 94 | * 2. Correct the odd `em` font sizing in all browsers. 95 | */ 96 | 97 | code, 98 | kbd, 99 | samp { 100 | font-family: monospace, monospace; /* 1 */ 101 | font-size: 1em; /* 2 */ 102 | } 103 | 104 | /** 105 | * Add the correct font size in all browsers. 106 | */ 107 | 108 | small { 109 | font-size: 80%; 110 | } 111 | 112 | /** 113 | * Prevent `sub` and `sup` elements from affecting the line height in 114 | * all browsers. 115 | */ 116 | 117 | sub, 118 | sup { 119 | font-size: 75%; 120 | line-height: 0; 121 | position: relative; 122 | vertical-align: baseline; 123 | } 124 | 125 | sub { 126 | bottom: -0.25em; 127 | } 128 | 129 | sup { 130 | top: -0.5em; 131 | } 132 | 133 | /* Embedded content 134 | ========================================================================== */ 135 | 136 | /** 137 | * Remove the border on images inside links in IE 10. 138 | */ 139 | 140 | img { 141 | border-style: none; 142 | } 143 | 144 | /* Forms 145 | ========================================================================== */ 146 | 147 | /** 148 | * 1. Change the font styles in all browsers. 149 | * 2. Remove the margin in Firefox and Safari. 150 | */ 151 | 152 | button, 153 | input, 154 | optgroup, 155 | select, 156 | textarea { 157 | font-family: inherit; /* 1 */ 158 | font-size: 100%; /* 1 */ 159 | line-height: 1.15; /* 1 */ 160 | margin: 0; /* 2 */ 161 | } 162 | 163 | /** 164 | * Show the overflow in IE. 165 | * 1. Show the overflow in Edge. 166 | */ 167 | 168 | button, 169 | input { /* 1 */ 170 | overflow: visible; 171 | } 172 | 173 | /** 174 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 175 | * 1. Remove the inheritance of text transform in Firefox. 176 | */ 177 | 178 | button, 179 | select { /* 1 */ 180 | text-transform: none; 181 | } 182 | 183 | /** 184 | * Correct the inability to style clickable types in iOS and Safari. 185 | */ 186 | 187 | button, 188 | [type="button"], 189 | [type="reset"], 190 | [type="submit"] { 191 | -webkit-appearance: button; 192 | } 193 | 194 | /** 195 | * Remove the inner border and padding in Firefox. 196 | */ 197 | 198 | button::-moz-focus-inner, 199 | [type="button"]::-moz-focus-inner, 200 | [type="reset"]::-moz-focus-inner, 201 | [type="submit"]::-moz-focus-inner { 202 | border-style: none; 203 | padding: 0; 204 | } 205 | 206 | /** 207 | * Restore the focus styles unset by the previous rule. 208 | */ 209 | 210 | button:-moz-focusring, 211 | [type="button"]:-moz-focusring, 212 | [type="reset"]:-moz-focusring, 213 | [type="submit"]:-moz-focusring { 214 | outline: 1px dotted ButtonText; 215 | } 216 | 217 | /** 218 | * Correct the padding in Firefox. 219 | */ 220 | 221 | fieldset { 222 | padding: 0.35em 0.75em 0.625em; 223 | } 224 | 225 | /** 226 | * 1. Correct the text wrapping in Edge and IE. 227 | * 2. Correct the color inheritance from `fieldset` elements in IE. 228 | * 3. Remove the padding so developers are not caught out when they zero out 229 | * `fieldset` elements in all browsers. 230 | */ 231 | 232 | legend { 233 | box-sizing: border-box; /* 1 */ 234 | color: inherit; /* 2 */ 235 | display: table; /* 1 */ 236 | max-width: 100%; /* 1 */ 237 | padding: 0; /* 3 */ 238 | white-space: normal; /* 1 */ 239 | } 240 | 241 | /** 242 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 243 | */ 244 | 245 | progress { 246 | vertical-align: baseline; 247 | } 248 | 249 | /** 250 | * Remove the default vertical scrollbar in IE 10+. 251 | */ 252 | 253 | textarea { 254 | overflow: auto; 255 | } 256 | 257 | /** 258 | * 1. Add the correct box sizing in IE 10. 259 | * 2. Remove the padding in IE 10. 260 | */ 261 | 262 | [type="checkbox"], 263 | [type="radio"] { 264 | box-sizing: border-box; /* 1 */ 265 | padding: 0; /* 2 */ 266 | } 267 | 268 | /** 269 | * Correct the cursor style of increment and decrement buttons in Chrome. 270 | */ 271 | 272 | [type="number"]::-webkit-inner-spin-button, 273 | [type="number"]::-webkit-outer-spin-button { 274 | height: auto; 275 | } 276 | 277 | /** 278 | * 1. Correct the odd appearance in Chrome and Safari. 279 | * 2. Correct the outline style in Safari. 280 | */ 281 | 282 | [type="search"] { 283 | -webkit-appearance: textfield; /* 1 */ 284 | outline-offset: -2px; /* 2 */ 285 | } 286 | 287 | /** 288 | * Remove the inner padding in Chrome and Safari on macOS. 289 | */ 290 | 291 | [type="search"]::-webkit-search-decoration { 292 | -webkit-appearance: none; 293 | } 294 | 295 | /** 296 | * 1. Correct the inability to style clickable types in iOS and Safari. 297 | * 2. Change font properties to `inherit` in Safari. 298 | */ 299 | 300 | ::-webkit-file-upload-button { 301 | -webkit-appearance: button; /* 1 */ 302 | font: inherit; /* 2 */ 303 | } 304 | 305 | /* Interactive 306 | ========================================================================== */ 307 | 308 | /* 309 | * Add the correct display in Edge, IE 10+, and Firefox. 310 | */ 311 | 312 | details { 313 | display: block; 314 | } 315 | 316 | /* 317 | * Add the correct display in all browsers. 318 | */ 319 | 320 | summary { 321 | display: list-item; 322 | } 323 | 324 | /* Misc 325 | ========================================================================== */ 326 | 327 | /** 328 | * Add the correct display in IE 10+. 329 | */ 330 | 331 | template { 332 | display: none; 333 | } 334 | 335 | /** 336 | * Add the correct display in IE 10. 337 | */ 338 | 339 | [hidden] { 340 | display: none; 341 | } 342 | -------------------------------------------------------------------------------- /front/src/styles/variables.less: -------------------------------------------------------------------------------- 1 | // @project-border-radius: 2px; 2 | // @project-font-size: 12px; 3 | // @project-height: 35px; 4 | // @project-padding: 10px; 5 | // @project-animation-time: .35s; 6 | // @project-selected-color: @brand-primary; 7 | // @project-menu-height: 50px; 8 | // @project-border-color-1: @c-group-wg1-grey-2; 9 | // @project-background-color-0: white; 10 | // @project-background-color-1: @c-group-wg1-grey-0; 11 | // @project-background-color-2: darken(@c-group-wg1-grey-0, 2%); 12 | // @project-background-color-3: darken(@c-group-wg1-grey-0, 4%); 13 | // @project-delete-color: @c-red; 14 | // @project-modal-wrapper-color: rgba(0, 0, 0, .5); 15 | // @project-ac-1: #838383; 16 | // @project-ac-2: #999999; 17 | // @project-ac-3: lighten(@project-ac-2, 25%); 18 | // @project-ac-4: #C2DDB0; 19 | // @project-ac-5: #C2DDB0; 20 | 21 | /* ========================================================================== */ 22 | /* Fonts */ 23 | /* ========================================================================== */ 24 | 25 | @font-primary: sans-serif; 26 | @color-primary: #009999; 27 | @color-second: #1D7373; 28 | @color-third: #006363; 29 | 30 | -------------------------------------------------------------------------------- /front/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | const CopyWebpackPlugin = require('copy-webpack-plugin') 6 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 7 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 8 | const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin'); 9 | const autoprefixer = require('autoprefixer'); 10 | const DotenvPlugin = require('webpack-dotenv-extended-plugin'); 11 | const { getIfUtils, removeEmpty, propIf } = require('webpack-config-utils'); 12 | 13 | const PORT = 3000; 14 | const HOST = 'localhost' 15 | 16 | const sourcePath = path.join(__dirname); 17 | const appPath = path.join(__dirname, './src'); 18 | const buildPath = path.join(__dirname, './build'); 19 | 20 | module.exports = (env) => { 21 | const { ifDevelopment, ifProduction } = getIfUtils(env); 22 | 23 | return removeEmpty({ 24 | entry: removeEmpty({ 25 | app: removeEmpty([ 26 | ifDevelopment(`webpack-dev-server/client?http://${HOST}:${PORT}`), 27 | ifDevelopment('webpack/hot/only-dev-server'), 28 | './src/index', 29 | ]), 30 | }), 31 | 32 | output: removeEmpty({ 33 | filename: 'static/js/bundle-[hash:8].js', 34 | publicPath: '/', 35 | path: buildPath, 36 | }), 37 | 38 | devtool: propIf(env == 'development', 'eval', 'source-map'), 39 | 40 | devServer: ifDevelopment({ 41 | inline: true, 42 | host: HOST, 43 | port: PORT, 44 | historyApiFallback: true, 45 | hot: true, 46 | disableHostCheck: true, 47 | open: true, 48 | overlay: { 49 | warnings: true, 50 | errors: true 51 | } 52 | }), 53 | 54 | resolve: { 55 | extensions: ['.js', '.jsx'], 56 | modules: [ 57 | path.resolve(sourcePath, 'node_modules'), 58 | appPath 59 | ], 60 | }, 61 | 62 | module: { 63 | rules: removeEmpty([ 64 | { 65 | test: /\.(js|jsx)$/, 66 | include: appPath, 67 | use: { 68 | loader: 'babel-loader', 69 | options: removeEmpty({ 70 | babelrc: false, 71 | presets: [ 72 | ["env", { 73 | "targets": { 74 | "browsers": ["last 2 versions", "safari >= 7"] 75 | } 76 | }], 77 | "react", 78 | "stage-0" 79 | ], 80 | plugins: ifDevelopment(["react-hot-loader/babel"]), 81 | cacheDirectory: ifDevelopment(true), 82 | compact: ifProduction(true), 83 | }), 84 | } 85 | }, 86 | ifProduction({ 87 | test: /\.less$/, 88 | loader: ExtractTextPlugin.extract({ 89 | fallback: 'style-loader', 90 | use: [ 91 | 'css-loader', 92 | 'postcss-loader', 93 | 'less-loader' 94 | ] 95 | }) 96 | }), 97 | ifDevelopment({ 98 | test: /\.less$/, 99 | use: [ 100 | 'style-loader', 101 | 'css-loader?sourceMap', 102 | 'postcss-loader', 103 | 'less-loader?sourceMap' 104 | ] 105 | }), 106 | { 107 | test: /\.(jpg|jpeg|gif|png|svg)$/, 108 | loader: 'url-loader', 109 | options: { 110 | name: '[name].[ext]', 111 | outputPath: 'static/media/img/', 112 | limit: 10000 113 | } 114 | }, 115 | { 116 | test: /\.(ttf|eot|woff|woff2)$/, 117 | loader: 'file-loader', 118 | options: { 119 | name: 'static/media/fonts/[name].[ext]' 120 | } 121 | }, 122 | { 123 | test: /\.md$/, 124 | loader: 'file-loader', 125 | options: { 126 | name: 'static/media/md/[name].[ext]' 127 | } 128 | } 129 | ]) 130 | }, 131 | 132 | plugins: removeEmpty([ 133 | ifDevelopment(new CaseSensitivePathsWebpackPlugin()), 134 | ifDevelopment(new webpack.HotModuleReplacementPlugin()), 135 | ifProduction(new CleanWebpackPlugin(buildPath)), 136 | ifProduction(new UglifyJsPlugin({ 137 | parallel: true, 138 | sourceMap: true, 139 | uglifyOptions: { 140 | mangle: { safari10: true }, 141 | output: { comments: false } 142 | } 143 | })), 144 | ifProduction(new ExtractTextPlugin({ filename: 'static/css/bundle.css' })), 145 | new HtmlWebpackPlugin(removeEmpty({ 146 | path: propIf(env == 'development', appPath, buildPath), 147 | hash: ifDevelopment(true), 148 | template: path.join(appPath, 'index.html'), 149 | filename: 'index.html', 150 | minify: { 151 | removeComments: true, 152 | collapseWhitespace: true, 153 | removeRedundantAttributes: true, 154 | useShortDoctype: true, 155 | removeEmptyAttributes: true, 156 | removeStyleLinkTypeAttributes: true, 157 | keepClosingSlash: true, 158 | minifyJS: true, 159 | minifyCSS: true, 160 | minifyURLs: true, 161 | }, 162 | })), 163 | new webpack.LoaderOptionsPlugin(removeEmpty({ 164 | minimize: ifProduction(true), 165 | debug: ifProduction(false), 166 | options: { 167 | postcss: [ 168 | autoprefixer({ 169 | browsers: ['last 3 version', 'ie >= 10'] 170 | }) 171 | ], 172 | context: sourcePath 173 | } 174 | })), 175 | ifDevelopment(new webpack.NamedModulesPlugin()), 176 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 177 | new webpack.DefinePlugin({ 178 | 'process.env.NODE_ENV': JSON.stringify(env) 179 | }), 180 | new DotenvPlugin({ 181 | defaults: './config/.env.defaults', 182 | path: './config/.env.local' 183 | }) 184 | ]) 185 | }) 186 | } 187 | --------------------------------------------------------------------------------