├── .babelrc ├── .gitignore ├── .jshintrc ├── .nvmrc ├── .travis.yml ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── app ├── .babelrc ├── app.js ├── css │ ├── animations.scss │ ├── fonts.scss │ ├── style.scss │ ├── style_inxt.scss │ ├── style_new.scss │ └── xcore.scss ├── dev-app-update.yml ├── fonts │ ├── CerebriSans-Bold.ttf │ ├── CerebriSans-Book.ttf │ ├── CerebriSans-Regular.ttf │ ├── CerebriSans-SemiBold.ttf │ ├── ProximaNova-Black.otf │ ├── ProximaNova-Bold.otf │ ├── ProximaNova-Extrabld.otf │ ├── ProximaNova-Light.otf │ ├── ProximaNova-Medium.otf │ ├── ProximaNova-Regular.otf │ ├── ProximaNova-Semibold.otf │ ├── ProximaNova-Thin.otf │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff2 │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff2 │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff2 │ ├── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff │ └── open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff2 ├── imgs │ ├── icon-settings.svg │ ├── linux │ │ └── icon.png │ ├── logo-inxt.png │ ├── osx │ │ ├── trayHighlight.png │ │ ├── trayHighlight@2x.png │ │ └── trayTemplate@2x.png │ ├── windows │ │ └── tray.ico │ └── xcore │ │ ├── connection.png │ │ ├── info-icon.png │ │ ├── upload.png │ │ ├── xcircles.svg │ │ ├── xcore.png │ │ └── xicon.svg ├── index.html ├── lib │ ├── autolaunch │ │ ├── index.js │ │ ├── lin.js │ │ ├── mac.js │ │ └── win.js │ ├── config-migrate.js │ ├── fatal-exception-dialog.js │ ├── menu.js │ ├── mkdirpsync.js │ ├── platform.js │ ├── rpc-server.js │ ├── trayicon.js │ ├── updater.js │ └── userdata.js ├── main.js ├── package.json ├── renderer.js ├── routes.js ├── stores │ ├── logs.js │ ├── schema.json │ ├── share.js │ └── share_list.js ├── test │ ├── integration │ │ ├── about.integration.js │ │ ├── footer.integration.js │ │ ├── logs.integration.js │ │ ├── main.integration.js │ │ ├── setup.integration.js │ │ └── updater.integration.js │ └── unit │ │ └── updater.unit.js ├── views │ ├── about │ │ └── index.js │ ├── components │ │ ├── confirmationModal │ │ │ └── ConfirmationModal.vue │ │ ├── disk-allocator │ │ │ ├── index.js │ │ │ ├── metric-dropdown.js │ │ │ └── metric-input.js │ │ ├── external-anchor │ │ │ └── index.js │ │ ├── filters │ │ │ └── metrics.js │ │ ├── genericModal │ │ │ └── GenericModal.vue │ │ ├── modal │ │ │ └── index.js │ │ ├── notification │ │ │ └── index.js │ │ ├── numeric-input │ │ │ └── index.js │ │ ├── overlay │ │ │ └── index.js │ │ └── updateModal │ │ │ └── UpdateModal.vue │ ├── migration │ │ └── index.js │ ├── overview │ │ ├── footer.js │ │ ├── index.js │ │ ├── nav.js │ │ └── row.js │ ├── share-wizard │ │ ├── index.js │ │ ├── wizard0.js │ │ ├── wizard1.js │ │ ├── wizard2.js │ │ ├── wizard3.js │ │ ├── wizard4.js │ │ ├── wizard5.js │ │ └── wizard6.js │ ├── terms │ │ ├── content.js │ │ └── index.js │ ├── updater │ │ └── index.js │ ├── uploader │ │ └── index.js │ └── xcore │ │ ├── dashboard.vue │ │ ├── settings.vue │ │ └── welcome.vue ├── xApp.js ├── xIndex.html ├── xRenderer.js ├── xRoutes.js └── yarn.lock ├── appveyor.yml ├── gulpfile.js ├── package.json ├── resources ├── icon.png ├── installer.png ├── launch.png ├── linux │ ├── DEBIAN │ │ ├── control │ │ └── postinst │ └── app.desktop ├── osx │ ├── Info.plist │ ├── appdmg.json │ ├── dmg-background.png │ ├── dmg-background@2x.png │ ├── dmg-icon.icns │ ├── helper_apps │ │ ├── Info EH.plist │ │ ├── Info NP.plist │ │ └── Info.plist │ ├── icon.icns │ ├── iconHighlight.png │ ├── iconHighlight@2x.png │ └── notarize.js └── windows │ ├── icon.ico │ ├── installer.nsi │ ├── setup-banner.bmp │ └── setup-icon.ico ├── tasks ├── app_npm_install.js ├── build.js ├── release.js ├── release_linux.js ├── release_osx.js ├── release_windows.js ├── release_windows_old.js ├── start.js └── utils.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | *.sublime-workspace 4 | .DS_Store 5 | Thumbs.db 6 | /.vscode/ 7 | /build/ 8 | /releases/ 9 | /tmp/ 10 | typings/ 11 | coverage 12 | *.swp 13 | dist/ 14 | release-builds/ 15 | 16 | .env 17 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, 3 | "browser": true, 4 | "camelcase": false, 5 | "curly": true, 6 | "devel": false, 7 | "eqeqeq": true, 8 | "esnext": true, 9 | "freeze": true, 10 | "immed": true, 11 | "indent": 2, 12 | "latedef": "nofunc", 13 | "newcap": false, 14 | "noarg": true, 15 | "node": true, 16 | "noempty": true, 17 | "nonew": true, 18 | "quotmark": "single", 19 | "regexp": true, 20 | "smarttabs": false, 21 | "strict": true, 22 | "trailing": true, 23 | "undef": true, 24 | "unused": true, 25 | "maxparams": 4, 26 | "maxstatements": 20, 27 | "maxcomplexity": 9, 28 | "maxdepth": 3, 29 | "maxlen": false, 30 | "multistr": true, 31 | "predef": [ 32 | "after", 33 | "afterEach", 34 | "before", 35 | "beforeEach", 36 | "describe", 37 | "exports", 38 | "it", 39 | "module", 40 | "require" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | before_script: 5 | - export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start 6 | after_script: 7 | - npm run coverage 8 | - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls 9 | sudo: false 10 | env: 11 | - CXX=g++-4.8 12 | addons: 13 | apt: 14 | sources: 15 | - ubuntu-toolchain-r-test 16 | packages: 17 | - g++-4.8 18 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Versions 2 | 3 | Replace the values below with your own: 4 | 5 | - **GUI Version:** v1.0.3 6 | - **Operating System:** Ubuntu 14.04 7 | 8 | ### Expected Behavior 9 | 10 | Please describe the program's expected behavior. 11 | 12 | ``` 13 | 14 | ``` 15 | 16 | ### Actual Behavior 17 | 18 | Please describe the program's actual behavior. Please include any stack traces 19 | or log output in the back ticks below. 20 | 21 | ``` 22 | 23 | ``` 24 | 25 | ### Steps to Reproduce 26 | 27 | Please include the steps the reproduce the issue, numbered below. Include as 28 | much detail as possible. 29 | 30 | 1. ... 31 | 2. ... 32 | 3. ... 33 | 34 | ### Screenshots (Optional) 35 | 36 | If the error is graphical in nature it is helpful to provide a screenshot. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GitHub release](https://img.shields.io/github/release/Naereen/StrapDown.js.svg)](https://GitHub.com/internxt/X-Core/releases/) 2 | 3 | X Core 4 | ================ 5 | 6 | [X Core](http://internxt.com/core) is a cross-platform desktop application enabling users to earn money 7 | by sharing their extra hard drive space on the [Internxt](http://internxt.com) network. 8 | 9 | If you wish to build from source, follow the instructions below. 10 | 11 | ### Prerequisites 12 | 13 | * [Git](https://git-scm.org) 14 | * [Node.js 8.x.x](https://nodejs.org) 15 | * [node-gyp](https://github.com/nodejs/node-gyp) 16 | 17 | > If you do not have [Node.js](https://nodejs.org) installed already, install 18 | > it with [NVM](https://github.com/creationix/nvm). 19 | 20 | ### Setup 21 | 22 | Clone this repository and install dependencies with NPM. 23 | 24 | ```bash 25 | git clone https://github.com/internxt/x-core && cd x-core 26 | npm install 27 | ``` 28 | 29 | Then you can start the application. 30 | 31 | ```bash 32 | npm --production start 33 | ``` 34 | 35 | Development 36 | ----------- 37 | 38 | Unlike a traditional Node.js project, this one has 2 separate `package.json` 39 | files: `package.json` and `app/package.json`. The one in the root directory 40 | only contains dependencies for the [Electron](https://electronjs.org)-based 41 | build system. It is unlikely that you will need to modify this. 42 | 43 | Building 44 | -------- 45 | 46 | You can package a release for GNU/Linux, OSX, and Windows, by running the 47 | following from the project's root directory. 48 | 49 | ```bash 50 | npm run release 51 | ``` 52 | 53 | Once completed, your bundle will be placed in `releases/`. You can only bundle 54 | a release for the operating system on which you are running, so in order to 55 | build for all supported platforms, you will need to have access to each 56 | operating system. 57 | 58 | You can use [xdissent/ievms](https://github.com/xdissent/ievms) to setup a 59 | virtual machine for Windows if you are on GNU/Linux or OSX. If you are running 60 | GNU/Linux, there are a number of resources available for setting up a virtual 61 | machine for OSX. 62 | 63 | > On Windows, [NSIS](http://nsis.sourceforge.net/Main_Page) is used. You have 64 | > to install it (version 3.0), and add NSIS folder to PATH in environment 65 | > variables, so it is reachable to scripts in this project (path should look 66 | > something like `C:/Program Files (x86)/NSIS`). 67 | 68 | License 69 | ------- 70 | ``` 71 | X Core - Cross-platform desktop application fop sharing user's extra hard drive space. 72 | 73 | This program is free software: you can redistribute it and/or modify 74 | it under the terms of the GNU General Public License as published by 75 | the Free Software Foundation, either version 3 of the License, or 76 | (at your option) any later version. 77 | 78 | This program is distributed in the hope that it will be useful, 79 | but WITHOUT ANY WARRANTY; without even the implied warranty of 80 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 81 | GNU General Public License for more details. 82 | 83 | You should have received a copy of the GNU General Public License 84 | along with this program. If not, see http://www.gnu.org/licenses/. 85 | ``` 86 | -------------------------------------------------------------------------------- /app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | const { homedir } = require("os"); 3 | const VueRouter = require("vue-router"); 4 | const router = new VueRouter(require("./routes")); 5 | const utils = require("xcore-daemon").utils; 6 | 7 | module.exports = { 8 | router, 9 | el: "#app", 10 | data: window.Store.shareList, 11 | components: { 12 | updater: require('./views/updater'), 13 | about: require('./views/about'), 14 | terms: require('./views/terms') 15 | }, 16 | created: function() { 17 | this.actions.load(err => { 18 | this.actions.status(() => { 19 | //Check to see if any of the shares aren't using Ethereum addresses 20 | let usingBitcoinAddress = false; 21 | this.shares.forEach(share => { 22 | if (!utils.isValidEthereumAddress(share.config.paymentAddress)) { 23 | usingBitcoinAddress = true; 24 | } 25 | }); 26 | 27 | if (this.shares.length === 0) { 28 | router.replace('share-wizard'); 29 | } else if (usingBitcoinAddress) { 30 | router.replace('migration'); 31 | } else { 32 | router.replace('overview'); 33 | } 34 | }); 35 | }); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /app/css/animations.scss: -------------------------------------------------------------------------------- 1 | .fade-enter-active, .fade-leave-active { 2 | transition: opacity .25s ease-in-out; 3 | } 4 | .fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ { 5 | opacity: 0; 6 | } 7 | 8 | .fade-leave-to { 9 | display: none; 10 | } 11 | 12 | @keyframes rotate { 13 | from { 14 | transform: rotate(0deg); 15 | } 16 | to { 17 | transform: rotate(360deg); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/css/fonts.scss: -------------------------------------------------------------------------------- 1 | /* open-sans-300 - latin_cyrillic_latin-ext_cyrillic-ext */ 2 | @font-face { 3 | font-family: 'Open Sans'; 4 | font-style: normal; 5 | font-weight: 300; 6 | src: local('Open Sans Light'), local('OpenSans-Light'), 7 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 8 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 9 | } 10 | /* open-sans-regular - latin_cyrillic_latin-ext_cyrillic-ext */ 11 | @font-face { 12 | font-family: 'Open Sans'; 13 | font-style: normal; 14 | font-weight: 400; 15 | src: local('Open Sans'), local('OpenSans'), 16 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 17 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 18 | } 19 | /* open-sans-700 - latin_cyrillic_latin-ext_cyrillic-ext */ 20 | @font-face { 21 | font-family: 'Open Sans'; 22 | font-style: normal; 23 | font-weight: 700; 24 | src: local('Open Sans Bold'), local('OpenSans-Bold'), 25 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 26 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 27 | } 28 | /* open-sans-800 - latin_cyrillic_latin-ext_cyrillic-ext */ 29 | @font-face { 30 | font-family: 'Open Sans'; 31 | font-style: normal; 32 | font-weight: 800; 33 | src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), 34 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 35 | url('../fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 36 | } 37 | 38 | @font-face { 39 | font-family: 'Cerebri Sans'; 40 | font-style: normal; 41 | font-weight: 300; 42 | src: url('../fonts/CerebriSans-Book.ttf') format('truetype') 43 | } 44 | 45 | @font-face { 46 | font-family: 'Cerebri Sans'; 47 | font-style: normal; 48 | font-weight: 400; 49 | src: url('../fonts/CerebriSans-Regular.ttf') format('truetype') 50 | } 51 | 52 | @font-face { 53 | font-family: 'Cerebri Sans'; 54 | font-style: normal; 55 | font-weight: 700; 56 | src: url('../fonts/CerebriSans-SemiBold.ttf') format('truetype') 57 | } 58 | 59 | @font-face { 60 | font-family: 'Cerebri Sans'; 61 | font-style: normal; 62 | font-weight: 900; 63 | src: url('../fonts/CerebriSans-Bold.ttf') format('truetype') 64 | } -------------------------------------------------------------------------------- /app/css/style.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | background-color: #fff; 5 | } 6 | 7 | body { 8 | font-family: 'Cerebri Sans', sans-serif; 9 | line-height: 26px; 10 | } 11 | 12 | @media only screen and (min-width: 480px) and (max-width: 600px) { 13 | body { 14 | padding: 0 1.5em; 15 | } 16 | } 17 | 18 | @media only screen and (min-width: 600px) and (max-width: 768px) { 19 | body { 20 | padding: 0 3em; 21 | } 22 | } 23 | 24 | input { 25 | font-weight: 300; 26 | } 27 | 28 | label { 29 | font-size: 15px; 30 | padding-bottom: 4px; 31 | } 32 | 33 | label.disabled { 34 | cursor: not-allowed; 35 | } 36 | 37 | h1, h2, h3, h4, h5, h6, p, span, blockquote, footer { 38 | -webkit-font-feature-settings: "kern" 1; 39 | font-feature-settings: "kern" 1; 40 | text-rendering: optimizelegibility; 41 | font-kerning: normal; 42 | } 43 | 44 | h1, h2, h3, h4, h5, h6 { 45 | font-family: "Montserrat", sans-serif; 46 | } 47 | 48 | a { 49 | color: #2683FF; 50 | } 51 | 52 | a:hover, a:focus { 53 | color: #0068f2; 54 | text-decoration: none; 55 | outline: 0; 56 | } 57 | 58 | header { 59 | padding: 2em 0; 60 | } 61 | 62 | .logo { 63 | display: block; 64 | width: 160px; 65 | margin-top: 14px; 66 | } 67 | .logo-image { 68 | width: 160px; 69 | } 70 | 71 | .main.disabled { 72 | opacity: 0.5; 73 | pointer-events: none; 74 | cursor: default; 75 | } 76 | 77 | .form-control { 78 | box-shadow: none; 79 | height: 36px; 80 | background: #fff; 81 | border: 2px solid #ddd; 82 | } 83 | .form-control.margin-top { 84 | margin-top: 10px; 85 | } 86 | 87 | .form-group { 88 | margin: 1em auto; 89 | } 90 | 91 | .pull-right { 92 | line-height: 28px; 93 | } 94 | 95 | .pull-right p { 96 | margin: 0; 97 | } 98 | 99 | .btn { 100 | font-family: "Montserrat", sans-serif; 101 | color: #fff; 102 | font-size: 1.4em; 103 | padding: 0.6em; 104 | margin: 1.2em auto; 105 | text-transform: uppercase; 106 | letter-spacing: 1px; 107 | outline: 0; 108 | position: relative; 109 | background-color: #88C425; 110 | border: 2px solid #88C425; 111 | } 112 | 113 | .btn:hover { 114 | color: #fff; 115 | background: #96d72b; 116 | border-color: #96d72b; 117 | outline: 0; 118 | } 119 | 120 | .btn:hover:focus { 121 | color: #fff; 122 | outline: 0; 123 | } 124 | 125 | .btn:focus { 126 | color: #fff; 127 | outline: 0; 128 | } 129 | 130 | .btn:active { 131 | color: #fff; 132 | outline: 0; 133 | } 134 | 135 | .btn-blue { 136 | font-size: 13px; 137 | padding: 7px 0; 138 | border: 2px solid #2683FF; 139 | margin: 0; 140 | background: transparent; 141 | color: #2683FF; 142 | } 143 | 144 | .btn-blue:hover { 145 | color: #fff; 146 | background: #2683FF; 147 | border-color: #2683FF; 148 | } 149 | 150 | .btn-blue:focus { 151 | color: #2683FF; 152 | } 153 | 154 | .btn-drives { 155 | font-size: 14px; 156 | color: #fff; 157 | background: #88C425; 158 | border-color: #88C425; 159 | margin-bottom: 0; 160 | min-width: 160px; 161 | } 162 | 163 | .btn-drives .caret { 164 | margin-left: 0.5em; 165 | } 166 | 167 | .dropdown-menu>.active>a, 168 | .dropdown-menu>.active>a:focus, 169 | .dropdown-menu>.active>a:hover { 170 | background: #2683FF; 171 | } 172 | 173 | #size-unit { 174 | font-family: "Montserrat", sans-serif; 175 | color: #2683FF; 176 | letter-spacing: 1px; 177 | } 178 | 179 | footer { 180 | padding: 0.5em 0 1em; 181 | font-weight: 300; 182 | } 183 | 184 | footer p { 185 | text-transform: uppercase; 186 | font-size: 12px; 187 | letter-spacing: 1px; 188 | } 189 | 190 | footer a:hover { 191 | border-bottom: 1px dashed #0068f2; 192 | } 193 | 194 | .spinner { 195 | display: none; 196 | margin-top: 21px; 197 | height: 30px; 198 | } 199 | 200 | .spinner > div { 201 | background-color: #88C425; 202 | height: 100%; 203 | width: 6px; 204 | display: inline-block; 205 | -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; 206 | animation: sk-stretchdelay 1.2s infinite ease-in-out; 207 | } 208 | 209 | .spinner .rect2 { 210 | -webkit-animation-delay: -1.1s; 211 | animation-delay: -1.1s; 212 | } 213 | 214 | .spinner .rect3 { 215 | -webkit-animation-delay: -1s; 216 | animation-delay: -1s; 217 | } 218 | 219 | .spinner .rect4 { 220 | -webkit-animation-delay: -0.9s; 221 | animation-delay: -0.9s; 222 | } 223 | 224 | .spinner .rect5 { 225 | -webkit-animation-delay: -0.8s; 226 | animation-delay: -0.8s; 227 | } 228 | 229 | @-webkit-keyframes sk-stretchdelay { 230 | 0%, 40%, 100% { 231 | -webkit-transform: scaleY(0.4); 232 | } 233 | 20% { 234 | -webkit-transform: scaleY(1); 235 | } 236 | } 237 | 238 | @keyframes sk-stretchdelay { 239 | 0%, 40%, 100% { 240 | transform: scaleY(0.4); 241 | -webkit-transform: scaleY(0.4); 242 | } 243 | 20% { 244 | transform: scaleY(1); 245 | -webkit-transform: scaleY(1); 246 | } 247 | } 248 | 249 | #about .modal-dialog, 250 | #updater .modal-dialog, 251 | #loading .modal-dialog { 252 | top: 5em; 253 | } 254 | 255 | #settings .modal-header { 256 | border-bottom: 0; 257 | } 258 | 259 | .modal-body { 260 | padding: 1em; 261 | } 262 | 263 | .modal-footer { 264 | text-align: center; 265 | } 266 | 267 | .modal-footer .btn { 268 | margin: 1em 0; 269 | padding: 1em 2em; 270 | font-size: 14px; 271 | } 272 | 273 | .spinner-modal { 274 | display: block; 275 | margin-bottom: 21px; 276 | } 277 | 278 | .modal .nav-tabs { 279 | padding-left: 2em; 280 | } 281 | 282 | .modal .alert { 283 | margin-bottom: 10px; 284 | } 285 | 286 | #terms { 287 | padding: 0 !important; 288 | } 289 | #terms .modal-dialog{ 290 | overflow-y: initial !important 291 | } 292 | #terms .modal-body{ 293 | height: 380px; 294 | overflow-y: auto; 295 | } 296 | 297 | #modalLogs { 298 | top: 0; 299 | } 300 | 301 | #modalLogs .modal-body { 302 | padding: 0; 303 | } 304 | 305 | #modalLogs #modalLogsCode { 306 | border: 0; 307 | margin: 0; 308 | padding: 1.5em; 309 | font-size: 12px; 310 | line-height: 18px; 311 | min-height: 375px; 312 | } 313 | 314 | .fa { 315 | display: inline-block; 316 | font: normal normal normal 14px/1 FontAwesome; 317 | font-size: inherit; 318 | text-rendering: auto; 319 | -webkit-font-smoothing: antialiased; 320 | -moz-osx-font-smoothing: grayscale; 321 | } 322 | 323 | .fa-remove:before, 324 | .fa-close:before, 325 | .fa-times:before { 326 | content: "\f00d"; 327 | } 328 | 329 | .fa-check:before{ 330 | content: "\f00c"; 331 | } 332 | 333 | .start { 334 | background-color: #FFA500; 335 | border-color: #FFA500; 336 | } 337 | 338 | .start:hover { 339 | background-color: #ffbd44; 340 | border-color: #ffbd44; 341 | } 342 | 343 | .transitioning, .transitioning:hover { 344 | background-color: #d2d2d2; 345 | border-color: #d2d2d2; 346 | } 347 | 348 | .stop { 349 | background-color: #88C425; 350 | border-color: #88C425; 351 | } 352 | 353 | .stop:hover { 354 | background-color: #96d72b; 355 | border-color: #96d72b; 356 | } 357 | 358 | .passwd-linux { 359 | margin: auto; 360 | width: 85%; 361 | } 362 | 363 | .passwd-linux-btn { 364 | padding: 12px; 365 | } 366 | 367 | .remove { 368 | display: inline-block; 369 | margin: -4px 0 0 12px; 370 | color: #fff; 371 | opacity: 0.8; 372 | } 373 | 374 | .remove:hover { 375 | opacity: 1; 376 | cursor: pointer; 377 | } 378 | 379 | .error { 380 | color: red; 381 | } 382 | 383 | .btn-settings { 384 | font-size: 14px; 385 | color: #fff; 386 | background: #88C425; 387 | border-color: #88C425; 388 | margin-bottom: 0; 389 | } 390 | 391 | .dropdown-menu>label{ 392 | display:block; 393 | padding:3px 20px; 394 | clear:both; 395 | font-weight:400; 396 | line-height:1.42857143; 397 | color:#333; 398 | white-space:nowrap 399 | } 400 | 401 | #logs { 402 | height: 241px; 403 | border-radius: 3px; 404 | background: #333; 405 | overflow: auto; 406 | margin: 0; 407 | padding: 12px; 408 | color: #e2e2e2; 409 | font-family: monospace; 410 | } 411 | 412 | #logs > div { 413 | word-break: break-all; 414 | } 415 | 416 | #logs .ts { 417 | color: #999999; 418 | } 419 | 420 | #logs .error { 421 | color: red; 422 | } 423 | 424 | #logs .warn { 425 | color: yellow; 426 | } 427 | 428 | #logs .info { 429 | color: cyan; 430 | } 431 | 432 | #logs .debug { 433 | color: magenta; 434 | } 435 | 436 | .progress { 437 | height: 36px; 438 | margin-bottom: 0; 439 | background: #eee; 440 | border: 2px solid #ddd; 441 | box-shadow: none; 442 | } 443 | 444 | .progress-bar { 445 | line-height: 36px; 446 | background-color: #2683ff; 447 | } 448 | 449 | .progress-percentage { 450 | position: absolute; 451 | top: 0; 452 | left: 0; 453 | right: 0; 454 | bottom: 0; 455 | text-shadow: 0 0 3px #000; 456 | } 457 | -------------------------------------------------------------------------------- /app/css/style_inxt.scss: -------------------------------------------------------------------------------- 1 | .logo-horizontal, .logo-wizard { 2 | width: 50px; 3 | } -------------------------------------------------------------------------------- /app/css/style_new.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | background-color: #fff; 5 | } 6 | 7 | body { 8 | font-family: 'Cerebri Sans', sans-serif; 9 | line-height: 26px; 10 | } 11 | 12 | .container { 13 | max-width: 1600px; 14 | } 15 | 16 | * { 17 | text-rendering: optimizeLegibility; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | -webkit-font-feature-settings: "kern" 1; 21 | font-feature-settings: "kern" 1; 22 | font-kerning: normal; 23 | } 24 | 25 | h1, h2, h3, h4, h5, h6 { 26 | font-family: 'Cerebri Sans', sans-serif; 27 | font-weight: 300; 28 | color: #444; 29 | margin-bottom: 0.4em; 30 | } 31 | 32 | p { 33 | color: #555; 34 | font-size: 21px; 35 | font-weight: 300; 36 | } 37 | 38 | a { 39 | color: #2683FF; 40 | border-bottom: 1px solid #2683FF; 41 | } 42 | 43 | a:hover, a:focus { 44 | color: #0068f2; 45 | text-decoration: none; 46 | outline: 0; 47 | } 48 | 49 | .logo { 50 | display: block; 51 | margin: 2em auto; 52 | text-align: center; 53 | width: 140px; 54 | -webkit-user-select: none; 55 | user-select: none; 56 | } 57 | .logo-horizontal { 58 | display: block; 59 | margin: 1em 0; 60 | width: 190px; 61 | -webkit-user-select: none; 62 | user-select: none; 63 | } 64 | 65 | .btn { 66 | font-family: 'Cerebri Sans', sans-serif; 67 | font-weight: 800; 68 | color: #fff; 69 | font-size: 1em; 70 | padding: 1em 2.4em; 71 | margin-top: -1px; 72 | text-transform: uppercase; 73 | letter-spacing: 1px; 74 | outline: 0; 75 | position: relative; 76 | background-color: #7ED321; 77 | border: 1px solid #7ED321; 78 | min-width: 160px; 79 | cursor: pointer; 80 | } 81 | .btn:hover{ 82 | color: #fff; 83 | background: #96d72b; 84 | border-color: #96d72b; 85 | outline: 0; 86 | } 87 | .btn:hover:focus { 88 | color: #fff; 89 | outline: 0; 90 | } 91 | .btn:focus { 92 | color: #fff; 93 | outline: 0; 94 | } 95 | .btn:active { 96 | color: #fff; 97 | outline: 0; 98 | } 99 | .btn[disabled] { 100 | pointer-events: none; 101 | opacity: 0.5; 102 | } 103 | .btn-blue { 104 | background-color: #2683ff; 105 | border-color: #2683ff; 106 | } 107 | .btn-blue:hover { 108 | background: #173AE8; 109 | border-color: #173AE8; 110 | } 111 | .btn-secondary { 112 | border: 1px solid #eee; 113 | background: #fff; 114 | color: #2683ff; 115 | } 116 | .btn-secondary:hover { 117 | background: #2683ff; 118 | border-color: #2683ff; 119 | color: #fff; 120 | } 121 | .btn-secondary:focus { 122 | color: #2683ff; 123 | border: 1px solid #eee; 124 | box-shadow: none; 125 | } 126 | .btn-import { 127 | padding: 1em 1.4em; 128 | } 129 | 130 | /* 131 | Wizard 132 | */ 133 | 134 | .wizard-nav { 135 | padding-top: 1em; 136 | padding-bottom: 1em; 137 | } 138 | .wizard-nav small { 139 | color: #ccc; 140 | font-weight: 700; 141 | } 142 | .wizard-nav a { 143 | border-bottom: 0; 144 | } 145 | .wizard-nav a small:hover { 146 | color: #2683ff; 147 | border-bottom: 1px solid #2683ff; 148 | } 149 | 150 | input { 151 | border-radius: 4px; 152 | font-size: 1em; 153 | padding: 1em; 154 | line-height: 20px; 155 | margin-right: 1em; 156 | letter-spacing: 1px; 157 | border: 1px solid #eee; 158 | outline: 0; 159 | font-family: 'Cerebri Sans', sans-serif; 160 | } 161 | input:hover { 162 | border: 1px solid #ddd; 163 | } 164 | input:active, input:focus { 165 | border: 1px solid #2683ff; 166 | } 167 | 168 | ::-webkit-input-placeholder { 169 | color: #ddd; 170 | } 171 | 172 | input.address { 173 | min-width: 500px; 174 | } 175 | input[type="file"] { 176 | letter-spacing: 0px; 177 | color: white; 178 | } 179 | 180 | .select-metric { 181 | height: 3.4em !important; 182 | } 183 | 184 | .port-number { 185 | min-width: 160px; 186 | width: 120px; 187 | margin-left: 1em; 188 | } 189 | 190 | .error-text { 191 | color: #ff0033; 192 | } 193 | 194 | .nat-checkbox { 195 | display: inline; 196 | margin: 0px 20px 0px 10px; 197 | } 198 | 199 | 200 | /* 201 | Wizard Storage Size Slider 202 | */ 203 | .storage-slider input[type=range]{ 204 | -webkit-appearance: none; 205 | width: 100%; 206 | height: 14px; 207 | border-radius: 10px; 208 | background: #fff; 209 | background: linear-gradient(90deg,#7ED321 50%,#fff 50%); 210 | border: 1px solid #ddd; 211 | outline: none; 212 | padding: 0; 213 | margin: 0; 214 | } 215 | 216 | .storage-slider input[type=range]::-webkit-slider-runnable-track { 217 | height: 14px; 218 | background: none; 219 | border: none; 220 | } 221 | 222 | .storage-slider input[type=range]::-webkit-slider-thumb { 223 | -webkit-appearance: none; 224 | appearance: none; 225 | width: 28px; 226 | height: 28px; 227 | border-radius: 50%; 228 | background: #fff; 229 | border: 1px solid #D5D5D5; 230 | cursor: pointer; 231 | -webkit-transition: background .15s ease-in-out; 232 | transition: background .15s ease-in-out; 233 | box-shadow: 0 1px 4px 0 rgba(0,0,0,0.16); 234 | margin-top: -7px; 235 | } 236 | 237 | .storage-slider input[type=range]:focus, .storage-slider input[type=range]:active { 238 | outline: none; 239 | border: 1px solid #ddd; 240 | } 241 | 242 | .storage-slider input[type=range]::-webkit-slider-thumb:hover, .storage-slider input[type=range]::-webkit-slider-thumb:active { 243 | background: #7ED321; 244 | border: 1px solid #7ED321; 245 | } 246 | 247 | .storage-slider input[type=range]::-webkit-slider-thumb:after { 248 | content: 'II'; 249 | font-size: 11px; 250 | color: #444; 251 | position: absolute; 252 | top: 0; 253 | right: 0; 254 | } 255 | 256 | .range-slider { 257 | margin: 20px 0 0 0%; 258 | width: 100%; 259 | } 260 | 261 | .range-slider__value { 262 | color: #444; 263 | } 264 | .range-slider__info { 265 | text-transform: uppercase; 266 | font-weight: 700; 267 | font-size: 14px; 268 | color: #ccc; 269 | } 270 | 271 | .range-slider__unit-selector { 272 | margin-left: 0.4rem; 273 | display: inline-block; 274 | width: 3.5rem; 275 | height: 1.5rem !important; 276 | font-family: 'Cerebri Sans', sans-serif; 277 | font-weight: bold; 278 | font-size: 14px; 279 | } 280 | 281 | 282 | /* 283 | Overview 284 | */ 285 | 286 | nav { 287 | background: #fff; 288 | padding-top: 1em; 289 | padding-bottom: 1em; 290 | } 291 | 292 | .table { 293 | border: 1px solid #eee; 294 | } 295 | .table td, .table th { 296 | padding: 1.2rem 0.8rem; 297 | } 298 | .table .checkbox { 299 | margin-right: 0; 300 | cursor: pointer; 301 | } 302 | 303 | .storj { 304 | color: #7ED321; 305 | font-weight: 800; 306 | } 307 | 308 | .node-off { 309 | color: #C8CFDD; 310 | } 311 | 312 | .node-status-on { 313 | color: #7ED321; 314 | font-weight: 800; 315 | } 316 | .node-status-on:before { 317 | background: #7ED321; 318 | display: inline-block; 319 | width: 12px; 320 | height: 12px; 321 | border-radius: 6px; 322 | margin-right: 4px; 323 | content: ''; 324 | } 325 | 326 | .node-status-off { 327 | color: #C8CFDD; 328 | font-weight: 800; 329 | } 330 | .node-status-off:before { 331 | background: #C8CFDD; 332 | display: inline-block; 333 | width: 12px; 334 | height: 12px; 335 | border-radius: 6px; 336 | margin-right: 4px; 337 | content: ''; 338 | } 339 | 340 | .node-status-warning { 341 | color: #F6A623; 342 | font-weight: 800; 343 | } 344 | .node-status-warning:before { 345 | background: #F6A623; 346 | display: inline-block; 347 | width: 12px; 348 | height: 12px; 349 | border-radius: 6px; 350 | margin-right: 4px; 351 | content: ''; 352 | } 353 | 354 | .node-status-error { 355 | color: #fa6e50; 356 | font-weight: 800; 357 | } 358 | .node-status-error:before { 359 | background: #fa6e50; 360 | display: inline-block; 361 | width: 12px; 362 | height: 12px; 363 | border-radius: 6px; 364 | margin-right: 4px; 365 | content: ''; 366 | } 367 | 368 | .node-status-loading { 369 | color: #2683ff; 370 | font-weight: 800; 371 | text-transform: uppercase; 372 | } 373 | .node-status-loading:before { 374 | content: ''; 375 | width: 12px; 376 | height: 12px; 377 | display: inline-block; 378 | margin-right: 4px; 379 | border:solid 4px #2683ff; 380 | border-radius: 50%; 381 | border-right-color: transparent; 382 | border-bottom-color: transparent; 383 | 384 | transition: all 0.5s ease-in; 385 | animation-name: rotate; 386 | animation-duration: 1.0s; 387 | animation-iteration-count: infinite; 388 | animation-timing-function: linear; 389 | } 390 | 391 | .connection[status="0"] { 392 | color: #7ED321; 393 | } 394 | 395 | .connection[status="1"] { 396 | color: #F6A623; 397 | } 398 | 399 | .connection[status="2"] { 400 | color: #fa6e50; 401 | } 402 | 403 | .node-options { 404 | border: 1px solid #eee; 405 | border-radius: 4px; 406 | padding: 0.1em 0.5em 0.3em; 407 | text-transform: uppercase; 408 | font-weight: 800; 409 | letter-spacing: 1px; 410 | } 411 | .node-options:hover:focus { 412 | outline: 0; 413 | } 414 | .node-options:focus { 415 | outline: 0; 416 | } 417 | .node-options:active { 418 | outline: 0; 419 | } 420 | 421 | .dropdown-toggle { 422 | margin: 0; 423 | line-height: 0; 424 | min-width: 50px; 425 | padding: 0.4em 0.5em 0.3em; 426 | } 427 | 428 | .footer { 429 | margin-top: 2em; 430 | width: 100%; 431 | height: 60px; 432 | line-height: 60px; 433 | } 434 | .footer a { 435 | color: #aaa; 436 | border-color: #aaa; 437 | } 438 | 439 | .error-stream { 440 | z-index: 1000; 441 | position: fixed; 442 | width: 100%; 443 | border-radius: 0; 444 | border: 0; 445 | padding: 1em 2em 0; 446 | } 447 | .close { 448 | border: 0; 449 | } 450 | 451 | .show { 452 | display: block; 453 | } 454 | 455 | .modal.fade.show { 456 | background: rgba(0,0,0,0.5); 457 | } 458 | .modal-body{ 459 | max-height: calc(100vh - 210px); 460 | overflow-y: auto; 461 | } 462 | 463 | #terms .modal-body p { 464 | font-size: 14px; 465 | line-height: 1.4rem; 466 | } 467 | #terms .modal-body h4 { 468 | font-size: 18px; 469 | } 470 | 471 | .loader { 472 | display: block; 473 | /* background-image: url(../imgs/logo-inxt.png); */ 474 | background-color: rgba(255, 255, 255, 0.9); 475 | width: 100%; 476 | height: 100%; 477 | background-size: 100px 100px; 478 | background-repeat: no-repeat; 479 | background-position: center center; 480 | border: none; 481 | margin: auto; 482 | position: fixed; 483 | top: 0; 484 | left: 0; 485 | } 486 | 487 | .loader.inline { 488 | width: 50px; 489 | height: 50px; 490 | background-size: 50px 50px; 491 | } 492 | -------------------------------------------------------------------------------- /app/css/xcore.scss: -------------------------------------------------------------------------------- 1 | body { 2 | background: #18171f; 3 | color: #e4e4eb; 4 | margin: 0; 5 | padding: 0; 6 | } 7 | 8 | body, input { 9 | font-family: "Cerebri Sans", Arial, sans-serif; 10 | } 11 | 12 | #xApp { 13 | margin: 18px 26px; 14 | } 15 | 16 | ::-webkit-scrollbar { 17 | background-color: #18171f; 18 | width: .5em; 19 | } 20 | 21 | ::-webkit-scrollbar-thumb:window-inactive, 22 | ::-webkit-scrollbar-thumb { 23 | background: #100f14; 24 | border-radius: 10px; 25 | } 26 | 27 | 28 | #xVersion { 29 | position: absolute; 30 | font-size: 12px; 31 | color: #403f4e; 32 | top: 10px; 33 | right: 25px; 34 | } 35 | 36 | header { 37 | display: flex; 38 | justify-content: space-between; 39 | } 40 | 41 | #xIcon { 42 | height: 32px; 43 | width: 32px; 44 | } 45 | 46 | h1 { 47 | font-size: 23px; 48 | margin-top: 20px; 49 | font-weight: 900; 50 | color: #e4e4eb; 51 | margin-bottom: 0px; 52 | } 53 | 54 | h3 { 55 | margin-bottom: 5px; 56 | margin-top: 5px; 57 | } 58 | 59 | .subtitle { 60 | color: #7f7f86; 61 | margin: 0px; 62 | font-size: 13px; 63 | padding-bottom: 30px; 64 | } 65 | 66 | .db-widget-container { 67 | display: flex; 68 | flex-flow: row wrap; 69 | justify-content: space-between; 70 | margin-bottom: 16px; 71 | 72 | &:first-of-type { 73 | margin-top: 26px; 74 | } 75 | 76 | .error-message { 77 | font-size: 13px; 78 | margin: 5px 0; 79 | color: red; 80 | width: 100%; 81 | } 82 | } 83 | 84 | .db-widget__double, .db-widget__triple { 85 | display: flex; 86 | flex-direction: row; 87 | justify-content: space-between; 88 | } 89 | 90 | .db-widget__double { 91 | .db-widget { 92 | width: 169px; 93 | } 94 | } 95 | 96 | .db-widget__triple { 97 | .db-widget { 98 | width: 112px; 99 | } 100 | } 101 | 102 | .db-widget { 103 | width: 100%; 104 | border-bottom: 1px solid #3e3d42; 105 | } 106 | 107 | .db-widget-small { 108 | width: 25%; 109 | border-bottom: 2px solid #5b4ade; 110 | } 111 | 112 | .db-widget-long { 113 | width: 100%; 114 | display: flex; 115 | flex-direction: column; 116 | border-bottom: 1px solid #5b4ade; 117 | padding-bottom: 12px; 118 | 119 | .db-widget-long__upload { 120 | display: flex; 121 | flex-direction: row; 122 | justify-content: space-between; 123 | align-items: center; 124 | } 125 | } 126 | 127 | .db-title { 128 | color: #e4e4eb; 129 | font-size: 17px; 130 | letter-spacing: 0px; 131 | margin-bottom: 16px; 132 | font-weight: 700; 133 | } 134 | 135 | .db-data { 136 | color: #b7b6ba; 137 | font-size: 13px; 138 | letter-spacing: 0px; 139 | margin-bottom: 16px; 140 | } 141 | 142 | .input-field { 143 | background: #18171f; 144 | color: white; 145 | border: none; 146 | width: 100%; 147 | font-size: 13px; 148 | } 149 | 150 | .input-field:focus { 151 | outline: none; 152 | } 153 | 154 | #createNode, 155 | button { 156 | text-align: center; 157 | justify-content: center; 158 | background-color: #5b4ade; 159 | border: none; 160 | border-radius: 3px; 161 | width: 100%; 162 | height: 38px; 163 | font-size: 13px; 164 | color: white; 165 | font-weight: 300; 166 | letter-spacing: 0; 167 | margin-top: 21px; 168 | } 169 | 170 | #connectionImg { 171 | position: absolute; 172 | left: 382px; 173 | top: 400px; 174 | } 175 | 176 | #portSetup { 177 | position: absolute; 178 | left: 159px; 179 | top: 374px; 180 | width: 25px; 181 | height: 25px; 182 | } 183 | 184 | #storagePath { 185 | color: gray; 186 | font-size: 13px; 187 | } 188 | 189 | .hide { 190 | visibility: hidden; 191 | } 192 | 193 | /* 194 | toggle button animation 195 | */ 196 | 197 | .switch { 198 | position: relative; 199 | display: inline-block; 200 | width: 40px; 201 | height: 20px; 202 | } 203 | 204 | .switch .sliderInput { 205 | display: none; 206 | } 207 | 208 | .slider { 209 | position: absolute; 210 | cursor: pointer; 211 | top: 0; 212 | left: 0; 213 | right: 0; 214 | bottom: 0; 215 | background-color: #5b4ade; 216 | -webkit-transition: 0.4s; 217 | transition: 0.4s; 218 | } 219 | 220 | .slider:before { 221 | position: absolute; 222 | content: ""; 223 | height: 14px; 224 | width: 14px; 225 | left: 4px; 226 | bottom: 3px; 227 | background-color: white; 228 | -webkit-transition: 0.4s; 229 | transition: 0.4s; 230 | } 231 | 232 | .sliderInput:checked + .slider { 233 | background-color: #5b4ade; 234 | } 235 | 236 | .sliderInput:focus + .slider { 237 | box-shadow: 0 0 1px #5b4ade; 238 | } 239 | 240 | .sliderInput:checked + .slider:before { 241 | -webkit-transform: translateX(20px); 242 | -ms-transform: translateX(20px); 243 | transform: translateX(20px); 244 | } 245 | 246 | /* Rounded sliders */ 247 | .slider.round { 248 | border-radius: 34px; 249 | } 250 | 251 | .slider.round:before { 252 | border-radius: 50%; 253 | } 254 | 255 | #fileInput { 256 | background: url(../imgs/xcore/upload.png); 257 | background-repeat: no-repeat; 258 | height: 40px; 259 | width: 40px; 260 | } 261 | -------------------------------------------------------------------------------- /app/dev-app-update.yml: -------------------------------------------------------------------------------- 1 | owner: internxt 2 | repo: X-Core 3 | provider: github 4 | updaterCacheDirName: xcore-updater 5 | -------------------------------------------------------------------------------- /app/fonts/CerebriSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/CerebriSans-Bold.ttf -------------------------------------------------------------------------------- /app/fonts/CerebriSans-Book.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/CerebriSans-Book.ttf -------------------------------------------------------------------------------- /app/fonts/CerebriSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/CerebriSans-Regular.ttf -------------------------------------------------------------------------------- /app/fonts/CerebriSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/CerebriSans-SemiBold.ttf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Black.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Black.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Bold.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Extrabld.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Extrabld.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Light.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Medium.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Regular.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Semibold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Semibold.otf -------------------------------------------------------------------------------- /app/fonts/ProximaNova-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/ProximaNova-Thin.otf -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-300.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-700.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-800.woff2 -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff -------------------------------------------------------------------------------- /app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/fonts/open-sans-v13-latin_cyrillic_latin-ext_cyrillic-ext-regular.woff2 -------------------------------------------------------------------------------- /app/imgs/icon-settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Settings 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/imgs/linux/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/linux/icon.png -------------------------------------------------------------------------------- /app/imgs/logo-inxt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/logo-inxt.png -------------------------------------------------------------------------------- /app/imgs/osx/trayHighlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/osx/trayHighlight.png -------------------------------------------------------------------------------- /app/imgs/osx/trayHighlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/osx/trayHighlight@2x.png -------------------------------------------------------------------------------- /app/imgs/osx/trayTemplate@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/osx/trayTemplate@2x.png -------------------------------------------------------------------------------- /app/imgs/windows/tray.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/windows/tray.ico -------------------------------------------------------------------------------- /app/imgs/xcore/connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/xcore/connection.png -------------------------------------------------------------------------------- /app/imgs/xcore/info-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/xcore/info-icon.png -------------------------------------------------------------------------------- /app/imgs/xcore/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/xcore/upload.png -------------------------------------------------------------------------------- /app/imgs/xcore/xcircles.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/imgs/xcore/xcore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/imgs/xcore/xcore.png -------------------------------------------------------------------------------- /app/imgs/xcore/xicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | X Core 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/lib/autolaunch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const { app } = require('electron'); 5 | const win = require('./win'); 6 | const mac = require('./mac'); 7 | const lin = require('./lin'); 8 | const platformCheckFailed = new Error('OS-specific startup options not found'); 9 | 10 | /** 11 | * Cross-platform interface for Application Boot Options 12 | */ 13 | class AutoLaunch { 14 | 15 | constructor(optsObj) { 16 | assert(optsObj.name, 'Application name must be specified'); 17 | this.opts = { 18 | appName: optsObj.name, 19 | isHiddenOnLaunch: !!optsObj.isHidden, 20 | appPath: (optsObj.path) ? 21 | optsObj.path : 22 | app.getPath('exe') 23 | }; 24 | this.api = (/^win/.test(process.platform)) ? win : 25 | (/^darwin/.test(process.platform)) ? mac : 26 | (/^linux/.test(process.platform)) ? lin : null; 27 | } 28 | 29 | enable() { 30 | return new Promise((resolve, reject) => { 31 | if (!this.api) { 32 | return reject(platformCheckFailed); 33 | } 34 | 35 | this.api.enable(this.opts).then(resolve, reject).catch(err => { console.log(err); }); 36 | }); 37 | } 38 | 39 | disable() { 40 | return new Promise((resolve, reject) => { 41 | if (!this.api) { 42 | return reject(platformCheckFailed); 43 | } 44 | 45 | this.api.disable(this.opts).then(resolve, reject).catch(err => { console.log(err); }); 46 | }); 47 | } 48 | 49 | isEnabled() { 50 | return new Promise((resolve, reject) => { 51 | if (!this.api) { 52 | return reject(platformCheckFailed); 53 | } 54 | 55 | this.api.isEnabled(this.opts).then(resolve, reject).catch(err => { console.log(err); }); 56 | }); 57 | } 58 | 59 | } 60 | 61 | module.exports = AutoLaunch; 62 | -------------------------------------------------------------------------------- /app/lib/autolaunch/lin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const mkdirp = require('mkdirp'); 5 | const untildify = require('untildify'); 6 | const configDir = untildify('~/.xcore/autostart/'); 7 | const { existsSync } = require('xcore-daemon').utils; 8 | const path = require('path'); 9 | 10 | module.exports.enable = function (opts) { 11 | const file = path.join(configDir, opts.appName + '.desktop'); 12 | const data = [ 13 | '[Desktop Entry]', 14 | 'Type=Application', 15 | 'Vestion=1.0', 16 | `Name=${opts.appName}`, 17 | `Comment=${opts.appName} startup script`, 18 | `Exec=${opts.appPath}`, 19 | 'StartupNotify=false', 20 | 'Terminal=false' 21 | ].join('\n'); 22 | 23 | return new Promise((resolve, reject) => { 24 | mkdirp.sync(configDir); 25 | fs.writeFile(file, data, (err, data) => { 26 | if (err) { 27 | return reject(err); 28 | } 29 | 30 | return resolve(data); 31 | }); 32 | }); 33 | }; 34 | 35 | module.exports.disable = function (opts) { 36 | const file = path.join(configDir, opts.appName + '.desktop'); 37 | 38 | return new Promise((resolve) => { 39 | if (existsSync(file)) { 40 | fs.unlinkSync(file); 41 | resolve(); 42 | } 43 | }); 44 | }; 45 | 46 | module.exports.isEnabled = function (opts) { 47 | const file = path.join(configDir, opts.appName + '.desktop'); 48 | 49 | return new Promise((resolve) => resolve(existsSync(file))); 50 | }; 51 | -------------------------------------------------------------------------------- /app/lib/autolaunch/mac.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require('assert'); 4 | const applescript = require('applescript'); 5 | const tellTo = 'tell application "System Events" to '; 6 | 7 | // NB: AppleScript doesn't use valid JSON 8 | // NB: instead, property names are not quoted 9 | function toAppleJSON(opts) { 10 | assert.ok(opts.appPath, 'Invalid `appPath`'); 11 | assert.ok(opts.appName, 'Invalid `appName`'); 12 | 13 | var props = { 14 | path: opts.appPath.split('/Contents/MacOS/XCore')[0], 15 | hidden: opts.isHiddenOnLaunch, 16 | name: opts.appName 17 | }; 18 | 19 | return `{name:"${props.name}",path:"${props.path}",hidden:${props.hidden}}`; 20 | } 21 | 22 | module.exports.enable = function (opts) { 23 | const props = toAppleJSON(opts); 24 | const command = `${tellTo} make login item at end with properties ${props}`; 25 | 26 | return new Promise((resolve, reject) => { 27 | applescript.execString(command, (err, resp) => { 28 | if (err) { 29 | return reject(err); 30 | } 31 | 32 | resolve(resp); 33 | }); 34 | }); 35 | }; 36 | 37 | module.exports.disable = function (opts) { 38 | const command = `${tellTo} delete login item "${opts.appName}"`; 39 | 40 | return new Promise((resolve, reject) => { 41 | applescript.execString(command, (err, resp) => { 42 | if (err) { 43 | return reject(err); 44 | } 45 | 46 | resolve(resp); 47 | }); 48 | }); 49 | }; 50 | 51 | module.exports.isEnabled = function (opts) { 52 | const command = `${tellTo} get the name of every login item`; 53 | 54 | return new Promise(function (resolve, reject) { 55 | applescript.execString(command, (err, loginItems) => { 56 | if (err || !loginItems) { 57 | return reject(err); 58 | } 59 | 60 | resolve(loginItems.indexOf(opts.appName) > -1); 61 | }); 62 | }); 63 | }; 64 | -------------------------------------------------------------------------------- /app/lib/autolaunch/win.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const WinReg = require('winreg'); 4 | const registry = new WinReg({ 5 | hive: WinReg.HKCU, 6 | key: '\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' 7 | }); 8 | 9 | module.exports.enable = function (opts) { 10 | return new Promise((resolve, reject) => { 11 | registry.set(opts.appName, WinReg.REG_SZ, `"${opts.appPath}"`, 12 | (err, resp) => { 13 | if (err) { 14 | return reject(err); 15 | } 16 | 17 | resolve(resp); 18 | } 19 | ); 20 | }); 21 | }; 22 | 23 | module.exports.disable = function (opts) { 24 | return new Promise((resolve, reject) => { 25 | registry.remove(opts.appName, (err, resp) => { 26 | if (err) { 27 | return reject(err); 28 | } 29 | 30 | resolve(resp); 31 | }); 32 | }); 33 | }; 34 | 35 | module.exports.isEnabled = function (opts) { 36 | return new Promise((resolve, reject) => { 37 | registry.get(opts.appName, function (err, resp) { 38 | if (err) { 39 | return reject(err); 40 | } 41 | 42 | resolve(!!resp); 43 | }); 44 | }); 45 | }; 46 | -------------------------------------------------------------------------------- /app/lib/config-migrate.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { homedir } = require('os'); 4 | const mkdirpsync = require('./mkdirpsync'); 5 | const path = require('path'); 6 | const { writeFileSync, readFileSync } = require('fs'); 7 | const logdir = path.join(homedir(), '.xcore/logs'); 8 | const storj = require('storj-lib'); 9 | 10 | 11 | /** 12 | * Read a config file, check if it's legacy 13 | * @function 14 | * @param {string} filePath 15 | */ 16 | exports.isLegacyConfig = function (filePath) { 17 | let config = null; 18 | 19 | try { 20 | config = JSON.parse(readFileSync(filePath).toString()); 21 | } catch (e) { 22 | return false; 23 | } 24 | 25 | return Object.keys(config).includes('tabs'); 26 | }; 27 | 28 | /** 29 | * Convert the config file into the new format in place 30 | * @function 31 | * @param {string} filePath 32 | * @returns {string[]} 33 | */ 34 | exports.convertLegacyConfig = function (filePath) { 35 | const dir = path.dirname(filePath); 36 | const config = JSON.parse(readFileSync(filePath).toString()); 37 | 38 | function convertTab(tabConf) { 39 | const id = storj.KeyPair(tabConf.key).getNodeID(); 40 | 41 | mkdirpsync(tabConf.storage.dataDir); 42 | mkdirpsync(logdir); 43 | 44 | return { 45 | paymentAddress: tabConf.address, 46 | opcodeSubscriptions: [ 47 | '0f01020202', 48 | '0f02020202', 49 | '0f03020202' 50 | ], 51 | bridgeUri: 'https://api.internxt.io', 52 | seedList: [], 53 | rpcAddress: tabConf.network.hostname, 54 | rpcPort: tabConf.network.port, 55 | doNotTraverseNat: tabConf.network.nat === 'true' 56 | ? false 57 | : true, 58 | maxTunnels: 3, 59 | maxConnections: 150, 60 | tunnelGatewayRange: { 61 | min: tabConf.tunnels.startPort, 62 | max: tabConf.tunnels.endPort 63 | }, 64 | joinRetry: { 65 | times: 3, 66 | interval: 5000 67 | }, 68 | offerBackoffLimit: 4, 69 | networkPrivateKey: tabConf.key, 70 | loggerVerbosity: 3, 71 | loggerOutputFile: logdir, 72 | storagePath: tabConf.storage.dataDir, 73 | storageAllocation: `${tabConf.storage.size}${tabConf.storage.unit}` 74 | }; 75 | } 76 | 77 | return config.tabs.map((tabConf) => { 78 | let converted = convertTab(tabConf); 79 | let file = path.join(dir, `${tabConf.id}.json`); 80 | 81 | writeFileSync(file, JSON.stringify(converted, null, 2)); 82 | 83 | return file; 84 | }); 85 | }; 86 | -------------------------------------------------------------------------------- /app/lib/fatal-exception-dialog.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { dialog } = require('electron'); 4 | 5 | class FatalExceptionDialog { 6 | 7 | /** 8 | * Builds fatal dialog on error for application restart. 9 | * Will not display until render() is called. 10 | * @constructor 11 | */ 12 | constructor(appRoot, appRootWindow, errObj) { 13 | this.app = appRoot; 14 | this.window = appRootWindow; 15 | this.err = errObj; 16 | } 17 | 18 | /** 19 | * Builds and renders the dialog 20 | */ 21 | render() { 22 | dialog.showMessageBox( 23 | this.window, 24 | { 25 | title: "Fatal Exception", 26 | type: 'error', 27 | buttons: ['Quit'], 28 | detail: this.err.message, 29 | checkboxLabel: 'Attempt to Restart', 30 | checkboxChecked: true 31 | }, 32 | this._handleClose.bind(this) 33 | ); 34 | } 35 | 36 | /** 37 | * handles closing and restarting of the application 38 | */ 39 | _handleClose(resp, isRestartChecked) { 40 | if (isRestartChecked) { 41 | this.app.relaunch(); 42 | } 43 | 44 | this.app.exit(0); 45 | } 46 | } 47 | 48 | module.exports = FatalExceptionDialog; 49 | -------------------------------------------------------------------------------- /app/lib/menu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { app, Menu, ipcMain: ipc, BrowserWindow } = require('electron'); 4 | 5 | class ApplicationMenu { 6 | 7 | /** 8 | * Dynamically builds menu based on application state 9 | */ 10 | constructor() { 11 | ipc.on('processStarted', () => this.render()); 12 | ipc.on('processTerminated', () => this.render()); 13 | this.render(); 14 | } 15 | 16 | /** 17 | * Returns the focused window or the first open window 18 | * if none are focused 19 | */ 20 | getWindow() { 21 | var win = BrowserWindow.getFocusedWindow(); 22 | 23 | if (!win) { 24 | win = BrowserWindow.getAllWindows()[0]; 25 | } 26 | 27 | return win; 28 | } 29 | 30 | /** 31 | * Builds and renders the application menu 32 | */ 33 | render() { 34 | return Menu.setApplicationMenu( 35 | Menu.buildFromTemplate(this._getMenuTemplate()) 36 | ); 37 | } 38 | 39 | /** 40 | * Returns the menu template 41 | */ 42 | _getMenuTemplate() { 43 | const self = this; 44 | 45 | let file = { 46 | label: 'File', 47 | submenu: [ 48 | { 49 | label: 'Quit', 50 | accelerator: 'CmdOrCtrl+Q', 51 | click: function () { 52 | app.exit(); 53 | } 54 | } 55 | ] 56 | }; 57 | 58 | let edit = { 59 | label: 'Edit', 60 | submenu: [ 61 | { 62 | label: 'Undo', 63 | accelerator: 'CmdOrCtrl+Z', 64 | click: function () { 65 | self.getWindow().webContents.undo(); 66 | } 67 | }, { 68 | label: 'Redo', 69 | accelerator: 'CmdOrCtrl+Y', 70 | click: function () { 71 | self.getWindow().webContents.redo(); 72 | } 73 | }, { 74 | type: 'separator' 75 | }, { 76 | label: 'Cut', 77 | accelerator: 'CmdOrCtrl+X', 78 | click: function () { 79 | self.getWindow().webContents.cut(); 80 | } 81 | }, { 82 | label: 'Copy', 83 | accelerator: 'CmdOrCtrl+C', 84 | click: function () { 85 | self.getWindow().webContents.copy(); 86 | } 87 | }, { 88 | label: 'Paste', 89 | accelerator: 'CmdOrCtrl+V', 90 | click: function () { 91 | self.getWindow().webContents.paste(); 92 | } 93 | }, { 94 | label: 'Select All', 95 | accelerator: 'CmdOrCtrl+A', 96 | click: function () { 97 | self.getWindow().webContents.selectAll(); 98 | } 99 | } 100 | ] 101 | }; 102 | 103 | let editMacOS = { 104 | label: 'Edit', 105 | submenu: [ 106 | { role: 'undo' }, 107 | { role: 'redo' }, 108 | { type: 'separator' }, 109 | { role: 'cut' }, 110 | { role: 'copy' }, 111 | { role: 'paste' }, 112 | { role: 'pasteandmatchstyle' }, 113 | { role: 'delete' }, 114 | { role: 'selectall' } 115 | ] 116 | }; 117 | 118 | 119 | 120 | let view = { 121 | label: 'View', 122 | submenu: [ 123 | { 124 | label: 'Developer Tools', 125 | accelerator: 'Shift+CmdOrCtrl+J', 126 | click: function () { 127 | self.getWindow().toggleDevTools(); 128 | } 129 | } 130 | ] 131 | }; 132 | 133 | let help = { 134 | label: 'Help', 135 | submenu: [ 136 | // { 137 | // label: 'Check for Updates', 138 | // click: function() { 139 | // self.getWindow().webContents.send('checkForUpdates'); 140 | // } 141 | // }, 142 | { 143 | label: 'About X Core', 144 | click: function () { 145 | self.getWindow().webContents.send('showAboutDialog'); 146 | } 147 | } 148 | ] 149 | }; 150 | 151 | let editMenu = process.platform === 'darwin' ? editMacOS : edit; 152 | 153 | return [file, editMenu, view, help]; 154 | } 155 | 156 | } 157 | 158 | module.exports = ApplicationMenu; 159 | -------------------------------------------------------------------------------- /app/lib/mkdirpsync.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | 6 | module.exports = function mkdirPSync(dirpath, made) { 7 | if (!made) { 8 | made = null; 9 | } 10 | 11 | dirpath = path.normalize(dirpath); 12 | try { 13 | fs.mkdirSync(dirpath); 14 | made = made || dirpath; 15 | } catch (err) { 16 | if (err.code === 'ENOENT') { 17 | made = mkdirPSync(path.dirname(dirpath), made); 18 | mkdirPSync(dirpath, made); 19 | } else { 20 | let stat; 21 | try { 22 | stat = fs.statSync(dirpath); 23 | if (!stat.isDirectory()) { 24 | throw err; 25 | } 26 | } 27 | catch (err) { 28 | throw err; 29 | } 30 | } 31 | } 32 | return made; 33 | }; 34 | -------------------------------------------------------------------------------- /app/lib/platform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @static {String} platform 5 | */ 6 | module.exports = 7 | (/^win/.test(process.platform)) ? 'win' : 8 | (/^darwin/.test(process.platform)) ? 'mac' : 9 | (/^linux/.test(process.platform)) ? 'lin' : null; 10 | -------------------------------------------------------------------------------- /app/lib/rpc-server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict'; 4 | 5 | const storjshare = require('xcore-daemon'); 6 | const dnode = require('dnode'); 7 | let api = new storjshare.RPC(); 8 | 9 | dnode(api.methods).listen(45015, () => { 10 | process.send({ state: 'init' }); 11 | }); 12 | 13 | process.on('uncaughtException', (err) => { 14 | console.log('RPC Server error: ', err); 15 | process.send({ error: err.stack }); //'A Fatal Exception has occured in the xcore-daemon RPC server' 16 | }); 17 | -------------------------------------------------------------------------------- /app/lib/trayicon.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Menu, Tray } = require('electron'); 4 | const PLATFORM = require('./platform'); 5 | 6 | class TrayIcon { 7 | 8 | /** 9 | * Dynamically builds system tray context-menu based on application state. 10 | * Will not display until render() is called. 11 | * @constructor 12 | */ 13 | constructor(appRoot, appRootWindow, icoPath, userdata) { 14 | this.userData = userdata; 15 | this.rootWindow = appRootWindow; 16 | this.app = appRoot; 17 | this.trayIconPath = icoPath; 18 | } 19 | 20 | /** 21 | * Builds and renders the system tray and context menu 22 | */ 23 | render() { 24 | this.contextMenu = Menu.buildFromTemplate(this._getMenuTemplate()); 25 | 26 | if (typeof this.trayIcon === 'undefined') { 27 | let trayImage; 28 | 29 | switch (PLATFORM) { 30 | case 'lin': 31 | trayImage = this.trayIconPath + '/linux/icon.png'; 32 | this.trayIcon = new Tray(trayImage); 33 | this.trayIcon.setContextMenu(this.contextMenu); 34 | break; 35 | case 'win': 36 | trayImage = this.trayIconPath + '/windows/tray.ico'; 37 | this.trayIcon = new Tray(trayImage); 38 | this.trayIcon.on('click', () => { 39 | this._restoreAll(this.rootWindow); 40 | }); 41 | this.trayIcon.on('right-click', () => { 42 | this.trayIcon.popUpContextMenu(this.contextMenu); 43 | }); 44 | break; 45 | case 'mac': 46 | trayImage = this.trayIconPath + '/osx/trayTemplate.png'; 47 | this.trayIcon = new Tray(trayImage); 48 | this.trayIcon.setPressedImage( 49 | this.trayIconPath + '/osx/trayHighlight.png' 50 | ); 51 | this.trayIcon.on('click', () => { 52 | this._restoreAll(this.rootWindow); 53 | }); 54 | this.trayIcon.on('right-click', () => { 55 | this.trayIcon.popUpContextMenu(this.contextMenu); 56 | }); 57 | break; 58 | default: 59 | // NOOP 60 | } 61 | 62 | this.trayIcon.setToolTip('X Core'); 63 | } 64 | } 65 | 66 | /** 67 | * Kills the system tray and context menu 68 | */ 69 | destroy() { 70 | if (this.trayIcon) { 71 | this.trayIcon.removeAllListeners(); 72 | this.trayIcon.destroy(); 73 | this.trayIcon = undefined; 74 | } 75 | } 76 | 77 | /** 78 | * Returns the system tray menu template 79 | */ 80 | _getMenuTemplate() { 81 | return [ 82 | { 83 | label: 'Restore', 84 | click: () => this._restoreAll(this.rootWindow) 85 | }, 86 | { 87 | label: 'Quit', 88 | click: () => this.app.exit() 89 | } 90 | ]; 91 | } 92 | 93 | /** 94 | * Restores any application windows that are minimized 95 | */ 96 | _restoreAll() { 97 | this.rootWindow.show(); 98 | 99 | if (this.rootWindow.isMinimized()) { 100 | this.rootWindow.restore(); 101 | } 102 | } 103 | 104 | } 105 | 106 | module.exports = TrayIcon; 107 | -------------------------------------------------------------------------------- /app/lib/updater.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const about = require('../package'); 4 | const { EventEmitter } = require('events'); 5 | // const request = require('request'); 6 | const assert = require('assert'); 7 | const semver = require('semver'); 8 | const fetch = require('node-fetch'); 9 | 10 | const installerExtension = new Map([ 11 | ['win32', 'exe'], 12 | ['darwin', 'dmg'], 13 | ['linux', 'deb'] 14 | ]); 15 | 16 | class Updater extends EventEmitter { 17 | 18 | constructor() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Fetches remote package metadata and determines if update is available 24 | */ 25 | checkForUpdates() { 26 | const currentVersion = semver.valid(about.version); 27 | 28 | this._checkGithubUpdates().then(res => { 29 | const latestVersion = semver.valid(res.tag_name); 30 | 31 | if (semver.gt(latestVersion, currentVersion)) { 32 | // There is a new version of the app. 33 | // Now check if the current platform is supported. 34 | 35 | const currentPlatform = process.platform; 36 | 37 | var result = res.assets.filter(value => { 38 | return value.name.endsWith(installerExtension.get(currentPlatform)); 39 | }); 40 | 41 | if (result && result.length === 1) { 42 | const updateAvailable = { 43 | url: result[0].browser_download_url, 44 | version: latestVersion 45 | }; 46 | this.emit('new-update', updateAvailable); 47 | } else { 48 | this.emit('no-updates'); 49 | } 50 | } 51 | 52 | }).catch(err => { 53 | this.emit('error', err); 54 | }); 55 | } 56 | 57 | _checkGithubUpdates() { 58 | return new Promise((resolve, reject) => { 59 | fetch('https://api.github.com/repos/internxt/x-core/releases/latest') 60 | .then(res => res.json()) 61 | .then(res => { resolve(this._validateResponse(res)); }) 62 | .catch(err => { reject(err); }); 63 | }); 64 | } 65 | 66 | /** 67 | * Validates the response body from version check 68 | * @param {Object} body 69 | */ 70 | _validateResponse(body) { 71 | assert(typeof body === 'object'); 72 | assert(typeof body.html_url === 'string'); 73 | assert(typeof body.tag_name === 'string'); 74 | return body; 75 | } 76 | 77 | } 78 | 79 | module.exports = Updater; 80 | -------------------------------------------------------------------------------- /app/lib/userdata.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const merge = require('merge'); 4 | const { EventEmitter } = require('events'); 5 | 6 | class UserData extends EventEmitter { 7 | 8 | constructor() { 9 | super(); 10 | this._parsed = this._read(); 11 | this._sync(); 12 | } 13 | 14 | /** 15 | * Loads the userdata from storage 16 | */ 17 | _read() { 18 | return merge.recursive({}, UserData.DEFAULTS, JSON.parse( 19 | window.localStorage.getItem(UserData.STORAGE_KEY) 20 | )); 21 | } 22 | 23 | /** 24 | * Persists the state to storage 25 | */ 26 | _sync() { 27 | return window.localStorage.setItem( 28 | UserData.STORAGE_KEY, 29 | JSON.stringify(this._parsed) 30 | ); 31 | } 32 | 33 | /** 34 | * Returns a proxy object for auto updating config 35 | */ 36 | toObject() { 37 | return { 38 | appSettings: new Proxy(this._parsed.appSettings, { 39 | get: (target, name) => { 40 | if (typeof UserData.DEFAULTS.appSettings[name] === 'undefined') { 41 | return undefined; 42 | } 43 | return target[name]; 44 | }, 45 | set: (target, property, value) => { 46 | if (typeof UserData.DEFAULTS.appSettings[property] === 'undefined') { 47 | return undefined; 48 | } 49 | target[property] = value; 50 | this._sync(); 51 | this.emit('settingsUpdated', this._parsed); 52 | return value; 53 | } 54 | }) 55 | }; 56 | } 57 | 58 | } 59 | 60 | UserData.STORAGE_KEY = '__USER_DATA'; 61 | UserData.DEFAULTS = { 62 | appSettings: { 63 | launchOnBoot: false, 64 | runDrivesOnBoot: true, 65 | silentMode: false 66 | } 67 | }; 68 | 69 | module.exports = new UserData(); 70 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { connect } = require('net'); 4 | const path = require('path'); 5 | const { fork } = require('child_process'); 6 | const { app, BrowserWindow, ipcMain: ipc, Tray, Menu, shell } = require('electron'); 7 | const ApplicationMenu = require('./lib/menu'); 8 | const TrayIcon = require('./lib/trayicon'); 9 | const FatalExceptionDialog = require('./lib/fatal-exception-dialog'); 10 | const protocol = process.env.isTestNet === 'true' ? 'testnet' : ''; 11 | const os = require('os'); 12 | 13 | let main; 14 | let xCoreUI; 15 | let tray; 16 | let xTray; 17 | let menu; 18 | let userData; 19 | 20 | if (app.requestSingleInstanceLock()) { 21 | if (main) { 22 | if (main.isMinimized()) { 23 | main.restore(); 24 | } 25 | main.show(); 26 | } 27 | } 28 | 29 | app.on('second-instance', (event, argv, cwd) => { 30 | console.log('Second instance'); 31 | app.quit(); 32 | }); 33 | 34 | if (process.platform === 'darwin') { 35 | app.dock.hide(); 36 | } 37 | /** 38 | * Prevents application from exiting on close, instead hiding it 39 | */ 40 | function minimizeToSystemTray(event) { 41 | event.preventDefault(); 42 | main.hide(); 43 | } 44 | 45 | /** 46 | * Toggles main process options based on received updates for the 47 | * application settings from the renderer 48 | */ 49 | function updateSettings(ev, data) { 50 | userData = tray.userData = JSON.parse(data); 51 | /* 52 | if (userData.appSettings.launchOnBoot && !isCommandLaunched) { 53 | autoLauncher.enable(); 54 | } else if(!userData.appSettings.launchOnBoot && !isCommandLaunched) { 55 | autoLauncher.disable(); 56 | } 57 | */ 58 | } 59 | 60 | /** 61 | * Check if the daemon is online and starts it if not running 62 | */ 63 | function maybeStartDaemon(callback) { 64 | const sock = connect(45015); 65 | 66 | sock.on('connect', () => { 67 | sock.end(); 68 | sock.removeAllListeners(); 69 | callback(); 70 | }); 71 | 72 | sock.on('error', () => { 73 | sock.removeAllListeners(); 74 | initRPCServer(callback); 75 | }); 76 | } 77 | 78 | function initRPCServer(callback) { 79 | let RPCServer = fork(`${__dirname}/lib/rpc-server.js`, { 80 | env: { STORJ_NETWORK: 'INXT' } 81 | }); 82 | 83 | process.on('exit', () => { 84 | RPCServer.kill(); 85 | }); 86 | 87 | RPCServer.on('message', msg => { 88 | if (msg.state === 'init') { 89 | return callback(); 90 | } else { 91 | RPCServer.removeAllListeners(); 92 | let killMsg = new FatalExceptionDialog(app, main, new Error(msg.error)); 93 | if (tray && tray.destroy) { 94 | tray.destroy(); 95 | } 96 | killMsg.render(); 97 | } 98 | }); 99 | } 100 | 101 | function getWindowPosition() { 102 | let windowBounds = xCoreUI.getBounds(); 103 | let trayBounds = xTray.getBounds(); 104 | 105 | // Center window horizontally below the tray icon 106 | let x = Math.round( 107 | trayBounds.x + trayBounds.width / 2.7 - windowBounds.width / 2 108 | ); 109 | 110 | // Position window 4 pixels vertically below the tray icon 111 | let y; 112 | if (process.platform === "win32") { 113 | y = trayBounds.y - windowBounds.height; 114 | } else if (process.platform === "darwin") { 115 | y = Math.round(trayBounds.y + trayBounds.height + 4); 116 | } else { 117 | y = Math.round(trayBounds.y / 2 + trayBounds.height); 118 | } 119 | return { x: x, y: y }; 120 | } 121 | 122 | /** 123 | * Establishes the app window, menu, tray, and other components 124 | * Setup IPC listeners and handlers 125 | */ 126 | function initRenderer() { 127 | menu = new ApplicationMenu(); 128 | 129 | xCoreUI = new BrowserWindow({ 130 | webPreferences: { 131 | nodeIntegration: true 132 | }, 133 | width: 389, 134 | height: 660, 135 | show: true, 136 | frame: false, 137 | skipTaskbar: true, 138 | alwaysOnTop: true 139 | }); 140 | 141 | // tray = new TrayIcon(app, main, path.join(__dirname, 'imgs'), userData); 142 | xTray = new Tray(path.join(__dirname, 'imgs/osx/trayHighlight@2x.png')); 143 | const contextMenu = () => Menu.buildFromTemplate([ 144 | { label: (xCoreUI.isVisible() ? 'Hide' : 'Show') + ' X Core', click: showXCore }, 145 | { 146 | label: 'View logs', 147 | click: () => { 148 | var logPath = path.join(os.homedir(), '.xcore/logs'); 149 | shell.openItem(logPath); 150 | } 151 | }, 152 | { 153 | label: 'Help and Tips', 154 | click: () => { 155 | shell.openExternal('https://internxt.com/core/setup-and-tips'); 156 | } 157 | }, 158 | { role: 'quit' } 159 | ]); 160 | 161 | xTray.setToolTip('XCore'); 162 | 163 | xTray.setContextMenu(contextMenu()); 164 | 165 | xTray.on('double-click', () => { 166 | showXCore(); 167 | }); 168 | 169 | xTray.on('click', () => { 170 | if (xCoreUI.isVisible() && !xCoreUI.isFocused()) { 171 | xCoreUI.focus(); 172 | } else { 173 | showXCore(); 174 | } 175 | }); 176 | 177 | function showXCore(item, window, event) { 178 | if (xCoreUI.isVisible()) { 179 | xCoreUI.hide(); 180 | } else { 181 | let position = getWindowPosition(); 182 | xCoreUI.setPosition(position.x, position.y); 183 | xCoreUI.show(); 184 | xCoreUI.focus(); 185 | } 186 | xTray.setContextMenu(contextMenu()); 187 | } 188 | 189 | // main.on('close', (e) => minimizeToSystemTray(e)); 190 | // app.on('activate', () => xCoreUI.show()); 191 | ipc.on("appSettingsChanged", (event, data) => updateSettings(event, data)); 192 | // ipc.on('showApplicationWindow', () => xCoreUI.show()); 193 | 194 | // NB: Start the daemon if not running, then render the application 195 | maybeStartDaemon((/* err */) => { 196 | menu.render(); 197 | xCoreUI.loadURL("file://" + __dirname + "/xIndex.html"); 198 | // tray.render(); 199 | }); 200 | } 201 | 202 | app.on('ready', () => { 203 | initRenderer(); 204 | let { x, y } = getWindowPosition(); 205 | xCoreUI.setPosition(x, y); 206 | }); 207 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xcore", 3 | "productName": "X Core", 4 | "publisher": "Internxt SL", 5 | "identifier": "com.xcore.farmer", 6 | "description": "X Core", 7 | "version": "1.1.2", 8 | "author": "Internxt ", 9 | "main": "main.js", 10 | "dependencies": { 11 | "applescript": "^1.0.0", 12 | "async": "^3.1.0", 13 | "bootstrap": "^4.3.1", 14 | "bootstrap-vue": "^2.0.2", 15 | "bytes": "^3.1.0", 16 | "dnode": "^1.2.2", 17 | "elasticsearch": "^16.4.0", 18 | "electron-updater": "^4.1.2", 19 | "jquery": "^3.4.1", 20 | "merge": "^1.2.0", 21 | "node-fetch": "^2.6.0", 22 | "portscanner": "^2.2.0", 23 | "pretty-ms": "^5.0.0", 24 | "request": "^2.83.0", 25 | "restify-clients": "^2.6.6", 26 | "rimraf": "^2.7.1", 27 | "semver": "^6.1.1", 28 | "storj-lib": "github:internxt/core#v8.7.3-beta", 29 | "strip-json-comments": "^3.0.1", 30 | "untildify": "^4.0.0", 31 | "vue": "^2.6.10", 32 | "vue-router": "^3.1.3", 33 | "vueify": "^9.4.1", 34 | "winreg": "^1.2.4", 35 | "winston": "^3.2.1", 36 | "winston-logstash": "^0.4.0", 37 | "xcore-daemon": "github:internxt/core-daemon" 38 | }, 39 | "license": "AGPL-3.0", 40 | "repository": "https://github.com/internxt/X-Core" 41 | } 42 | -------------------------------------------------------------------------------- /app/renderer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dnode = require('dnode'); 4 | const {ipcRenderer: ipc} = require('electron'); 5 | const {EventEmitter} = require('events'); 6 | const UserData = require('./lib/userdata'); 7 | const VueRouter = require('vue-router'); 8 | const BootstrapVue = require('bootstrap-vue'); 9 | 10 | window.UserData = UserData.toObject(); 11 | window.Vue = require('./node_modules/vue/dist/vue.common.js'); 12 | window.ViewEvents = new EventEmitter(); // NB: For view-to-view communication 13 | window.Vue.use(VueRouter); 14 | window.Vue.use(BootstrapVue); 15 | 16 | // NB: When settings change, notify the main process 17 | UserData.on('settingsUpdated', (updatedSettings) => { 18 | ipc.send('appSettingsChanged', updatedSettings); 19 | }); 20 | 21 | window.daemonSocket = dnode.connect(45015, (rpc) => { 22 | // NB: Add global reference to the daemon RPC 23 | window.daemonRpc = rpc; 24 | // Set up any required view-model store instances 25 | window.Store = { 26 | shareList: new (require('./stores/share_list'))(rpc), 27 | newShare: new (require('./stores/share'))() 28 | //editShare: new (require('./stores/share'))() 29 | } 30 | window.app = new window.Vue(require('./app')); 31 | 32 | // NB: Check user data for application settings and signal appropriate 33 | // NB: messages to the main process 34 | if (!window.UserData.appSettings.silentMode) { 35 | ipc.send('showApplicationWindow'); 36 | } 37 | }); 38 | -------------------------------------------------------------------------------- /app/routes.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | routes: [ 3 | { 4 | path: '/share-wizard', //'/share-wizard', 5 | component: require('./views/share-wizard'), 6 | children: [ 7 | { 8 | path: '', 9 | component: require('./views/share-wizard/wizard0') 10 | }, 11 | { 12 | path: 'wizard1?:edit', 13 | component: require('./views/share-wizard/wizard1') 14 | }, 15 | { 16 | path: 'wizard1', 17 | component: require('./views/share-wizard/wizard1') 18 | }, 19 | { 20 | path: 'wizard2', 21 | component: require('./views/share-wizard/wizard2') 22 | }, 23 | { 24 | path: 'wizard3', 25 | component: require('./views/share-wizard/wizard3') 26 | }, 27 | { 28 | path: 'wizard4', 29 | component: require('./views/share-wizard/wizard4') 30 | }, 31 | { 32 | path: 'wizard5', 33 | component: require('./views/share-wizard/wizard5') 34 | }, 35 | { 36 | path: 'wizard6', 37 | component: require('./views/share-wizard/wizard6') 38 | } 39 | ] 40 | }, 41 | { 42 | path: '/migration', 43 | component: require('./views/migration') 44 | }, 45 | { 46 | path: '/overview', 47 | component: require('./views/overview') 48 | } 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /app/stores/logs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const Transform = require('stream').Transform; 5 | 6 | class Logs extends Transform { 7 | constructor(opts, share) { 8 | this.super(opts); 9 | 10 | this.share = share; 11 | this.separator = '\n'; 12 | this.logPath = path.normalize(this.share.config.loggerOutputFile); 13 | 14 | this.errors = []; 15 | this.actions = {}; 16 | 17 | this.actions.read = this._read; 18 | 19 | this.actions.clearErrors = () => { 20 | this.errors = []; 21 | }; 22 | } 23 | 24 | _read(size) { 25 | //this._flush 26 | 27 | } 28 | 29 | _write(chunk, encoding, callback) { 30 | 31 | } 32 | } 33 | 34 | module.exports = Share; 35 | -------------------------------------------------------------------------------- /app/stores/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "paymentAddress": "", 3 | "opcodeSubscriptions": [ 4 | "0f01020202", 5 | "0f02020202", 6 | "0f03020202" 7 | ], 8 | "bridges": [{ 9 | "url": "https://api.internxt.com", 10 | "extendedKey": "xpub69vZdaeqerAfCThNZu6Zifz5K8XvFiPxG6UHZck5NC9ezmo48D8WmwhKYWjyxuwQVA3otsBZ8NFqfKvSGfLdY1qvTC75ZYUeYhJQdgdCgWJ" 11 | }], 12 | "seedList": [ 13 | ], 14 | "rpcAddress": "", 15 | "rpcPort": 4000, 16 | "doNotTraverseNat": false, 17 | "maxTunnels": 0, 18 | "maxConnections": 150, 19 | "tunnelGatewayRange": { 20 | "min": 4001, 21 | "max": 4003 22 | }, 23 | "joinRetry": { 24 | "times": 3, 25 | "interval": 5000 26 | }, 27 | "offerBackoffLimit": 4, 28 | "networkPrivateKey": "", 29 | "loggerVerbosity": 3, 30 | "loggerOutputFile": "$HOME/.xcore/logs/[nodeid].log", 31 | "storagePath": "$HOME/.xcore", 32 | "storageAllocation": "" 33 | } 34 | -------------------------------------------------------------------------------- /app/stores/share.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const { homedir } = require('os'); 6 | const prettyms = require('pretty-ms'); 7 | const storjshare = require('xcore-daemon'); 8 | const storj = require('storj-lib'); 9 | const mkdirPSync = require('../lib/mkdirpsync'); 10 | const stripComments = require('strip-json-comments'); 11 | const filter = require('../views/components/filters/metrics.js'); 12 | const rpc = window.daemonRpc; 13 | const defaultConfig = fs.readFileSync( 14 | path.join(__dirname, 'schema.json') 15 | ).toString(); 16 | 17 | 18 | class Share { 19 | constructor() { 20 | this.errors = []; 21 | this.actions = {}; 22 | this.config; 23 | this.storageAvailable = 0; 24 | 25 | this.actions.createShareConfig = () => { 26 | let returnedPath = false; 27 | let configFileDescriptor; 28 | let storPath; 29 | this.config.networkPrivateKey = storj.KeyPair().getPrivateKey(); 30 | let nodeID = storj.KeyPair(this.config.networkPrivateKey).getNodeID(); 31 | let sharePath = path.join(homedir(), '.xcore/shares'); 32 | 33 | if (this.config.storagePath === undefined || this.config.storagePath === '') { 34 | storPath = path.join(sharePath, '/', nodeID); 35 | } else { 36 | storPath = path.join(this.config.storagePath, '/'); 37 | } 38 | 39 | this.config.storagePath = storPath; 40 | 41 | if (this.config.storageAllocation && 42 | !this.config.storageAllocation.toString().match( 43 | /[0-9]+([Tt]|[Mm]|[Gg]|[Kk])?[Bb]/g 44 | )) { 45 | this.config.storageAllocation = this.config.storageAllocation.toString() + 'MB'; 46 | } 47 | 48 | let logPath = path.join(homedir(), '.xcore/logs'); 49 | let configPath = path.join(homedir(), '.xcore/configs'); 50 | 51 | try { 52 | mkdirPSync(sharePath); 53 | mkdirPSync(logPath); 54 | mkdirPSync(configPath); 55 | } catch (err) { 56 | if (err.code !== 'EEXIST') { 57 | this.errors.push(err); 58 | } 59 | } 60 | 61 | this.config.loggerOutputFile = logPath; 62 | configPath = path.join(configPath, '/') + nodeID + '.json'; 63 | 64 | let configArray = JSON.stringify(this.config, null, 2).split("\n"); 65 | let defaultConfigArray = defaultConfig.split("\n"); 66 | let rawConfigIndex = 0; 67 | 68 | // Restores comments 69 | for (let i = 0; i < defaultConfigArray.length - 1; i++ , rawConfigIndex++) { 70 | if (defaultConfigArray[i].trim().startsWith("//")) { 71 | configArray.splice(rawConfigIndex, 0, defaultConfigArray[i]); 72 | } 73 | } 74 | 75 | let configBuffer = Buffer.from(configArray.join("\n")); 76 | 77 | try { 78 | storjshare.utils.validate(this.config); 79 | configFileDescriptor = fs.openSync(configPath, 'w'); 80 | fs.writeFileSync(configFileDescriptor, configBuffer); 81 | returnedPath = configPath; 82 | } catch (err) { 83 | this.errors.push(err); 84 | } finally { 85 | if (configFileDescriptor) { 86 | fs.closeSync(configFileDescriptor); 87 | } 88 | 89 | if (returnedPath) { 90 | this.config = {}; 91 | } 92 | 93 | return returnedPath; 94 | } 95 | }; 96 | 97 | this.actions.clearErrors = () => { 98 | this.errors = []; 99 | }; 100 | 101 | this.actions.reset = () => { 102 | this.config = new Proxy(JSON.parse(stripComments(defaultConfig)), this._validator()); 103 | this.actions.clearErrors(); 104 | }; 105 | 106 | this.actions.getFreeDiskSpace = (path, callback) => { 107 | storjshare.utils.getFreeSpace(path, (err, free) => { 108 | if (err) { 109 | this.errors.push(err); 110 | return callback(err); 111 | } 112 | this.storageAvailable = free; 113 | return callback(null, free); 114 | }); 115 | }; 116 | 117 | this.actions.reset(); 118 | } 119 | 120 | _validator() { 121 | return { 122 | set: function (obj, prop, val) { 123 | let isValid = true; 124 | let propVal; 125 | 126 | if (typeof obj[prop] === 'undefined') { 127 | isValid = false; 128 | } 129 | 130 | switch (prop) { 131 | case 'rpcPort': 132 | propVal = Number(String(val).replace(/\d+/g, '$&')); 133 | break; 134 | default: 135 | propVal = val; 136 | break; 137 | } 138 | 139 | if (isValid) { 140 | obj[prop] = propVal; 141 | } 142 | 143 | return isValid; 144 | } 145 | }; 146 | } 147 | } 148 | 149 | module.exports = Share; 150 | -------------------------------------------------------------------------------- /app/stores/share_list.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const { homedir } = require('os'); 6 | const prettyms = require('pretty-ms'); 7 | const shell = require('electron').shell; 8 | const storjshare = require('xcore-daemon'); 9 | const configMigrate = require('../lib/config-migrate'); 10 | const async = require('async'); 11 | 12 | const mkdirPSync = require('../lib/mkdirpsync'); 13 | const BASE_PATH = path.join(homedir(), '.xcore'); 14 | const SNAPSHOT_PATH = path.join(BASE_PATH, 'gui.snapshot'); 15 | const LOG_PATH = path.join(BASE_PATH, 'logs'); 16 | const SHARE_PATH = path.join(BASE_PATH, 'shares'); 17 | 18 | 19 | class ShareList { 20 | constructor(rpc) { 21 | this.rpc = rpc; 22 | this.shares = []; 23 | this.errors = []; 24 | this.pollInterval = 10000; 25 | this.actions = {}; 26 | 27 | this._getShareById = (id) => { 28 | let share = false; 29 | this.shares.forEach((elem) => { 30 | if (elem.id === id) { 31 | share = elem; 32 | } 33 | }); 34 | 35 | return share; 36 | } 37 | 38 | this._getSharesById = (ids) => { 39 | let share = []; 40 | this.shares.forEach((elem) => { 41 | ids.forEach((id) => { 42 | if (elem.id === id) { 43 | share.push(elem); 44 | } 45 | }); 46 | }); 47 | 48 | return (share.length > 0) ? share : false; 49 | } 50 | 51 | this.actions.invalidate = (ids) => { 52 | this._getSharesById(ids).forEach((share) => { 53 | share.isValid = false; 54 | }); 55 | }; 56 | 57 | this.actions.status = (callback) => { 58 | this.rpc.status((err, shares) => { 59 | if (err) { 60 | this.errors.push(err); 61 | } 62 | 63 | this.shares = shares.map(_mapStatus); 64 | return callback(err, shares); 65 | }); 66 | }; 67 | 68 | this.actions.load = (callback) => { 69 | let returnedErr = null; 70 | 71 | this.rpc.load(SNAPSHOT_PATH, (err) => { 72 | let snapFileDescriptor; 73 | if (err) { 74 | try { 75 | if (err.message.includes('ENOENT')) { //TODO: change to return err.code === 'ENOENT'upstream in daemon 76 | mkdirPSync(BASE_PATH); 77 | snapFileDescriptor = fs.openSync(SNAPSHOT_PATH, 'w'); 78 | fs.writeFileSync(snapFileDescriptor, '[]'); 79 | this.actions.load(callback); 80 | } else { 81 | returnedErr = err; 82 | } 83 | } catch (failedToCreateSnapErr) { 84 | this.errors.push(failedToCreateSnapErr); 85 | returnedErr = failedToCreateSnapErr; 86 | } 87 | } 88 | 89 | return callback(returnedErr); 90 | }); 91 | }; 92 | 93 | this.actions.poll = () => { 94 | let timer; 95 | return { 96 | start: (interval) => { 97 | this.pollInterval = interval || this.pollInterval; 98 | timer = setInterval(() => { 99 | this.actions.status(() => { }); 100 | }, this.pollInterval); 101 | }, 102 | stop: () => { 103 | clearInterval(timer); 104 | } 105 | }; 106 | }; 107 | 108 | /** 109 | * Takes the current state of a share's configuration and writes it to the 110 | * configuration path for the share to persist it 111 | * @param {Number} id 112 | */ 113 | this.actions.update = (id) => { 114 | let share = this._getShareById(id); 115 | 116 | if (!share) { 117 | return this.errors.push(new Error('Cannot update configuration for invalid share')); 118 | } 119 | 120 | share.isValid = false; 121 | 122 | let configPath = share.path; 123 | let configBuffer = Buffer.from(JSON.stringify(share.config, null, 2)); 124 | 125 | try { 126 | storjshare.utils.validate(share.config); 127 | fs.writeFileSync(configPath, configBuffer); 128 | } catch (err) { 129 | return this.errors.push(err); 130 | } 131 | }; 132 | 133 | /** 134 | * Updates the snapshot file with the current ids of shares, this should 135 | * be called anytime a share is added or removed 136 | */ 137 | this.actions.save = () => { 138 | this.rpc.save(SNAPSHOT_PATH, (err) => { 139 | if (err) { 140 | return this.errors.push(err); 141 | } 142 | }); 143 | }; 144 | 145 | /** 146 | * Imports a share from the supplied configuration file path 147 | * @param {String} configPath 148 | */ 149 | this.actions.import = (configPath, callback) => { 150 | let handleStart = (err) => { 151 | if (err) { 152 | this.errors.push(err); 153 | } else { 154 | this.actions.save(); 155 | } 156 | return callback(err); 157 | }; 158 | 159 | if (typeof configPath === 'string') { 160 | configPath = [configPath]; 161 | } 162 | 163 | if (configMigrate.isLegacyConfig(configPath[0])) { 164 | let message = 'Configuration is in the legacy format. ' + 165 | ' Would you like to migrate it?'; 166 | 167 | if (window.confirm(message)) { 168 | configPath = configMigrate.convertLegacyConfig(configPath[0]); 169 | } else { 170 | let error = new Error('Invalid configuration supplied'); 171 | 172 | this.errors.push(error) 173 | return callback(error); 174 | } 175 | } 176 | 177 | async.each(configPath, (c, next) => { 178 | try { 179 | this.rpc.start(c, handleStart); 180 | } catch (err) { 181 | this.errors.push(err); 182 | return next(err); 183 | } 184 | }, callback); 185 | }; 186 | 187 | /** 188 | * Starts/Restarts the share with the given index 189 | * @param {String[]} id 190 | */ 191 | this.actions.start = (id) => { 192 | let list = []; 193 | if (typeof id === 'string') { 194 | list.push(id); 195 | } else if (Array.isArray(id)) { 196 | list = id; 197 | } 198 | 199 | this.actions.invalidate(list); 200 | 201 | list.forEach((id) => { 202 | this.rpc.restart(id, (err) => { 203 | if (err) { 204 | this.errors.push(err); 205 | } 206 | }); 207 | }); 208 | }; 209 | 210 | /** 211 | * Stops the running share at the given index 212 | * @param {String[]} id 213 | */ 214 | this.actions.stop = (id) => { 215 | let list = []; 216 | if (typeof id === 'string') { 217 | list.push(id); 218 | } else if (Array.isArray(id)) { 219 | list = id; 220 | } 221 | 222 | this.actions.invalidate(list); 223 | 224 | list.forEach((id) => { 225 | this.rpc.stop(id, (err) => { 226 | if (err) { 227 | this.errors.push(err); 228 | } 229 | }); 230 | }); 231 | }; 232 | 233 | /** 234 | * Removes the share at the given index from the snapshot 235 | * @param {String[]} id 236 | */ 237 | this.actions.destroy = (id) => { 238 | let list = []; 239 | if (typeof id === 'string') { 240 | list.push(id); 241 | } else if (Array.isArray(id)) { 242 | list = id; 243 | } 244 | 245 | this.actions.invalidate(list); 246 | 247 | list.forEach((id) => { 248 | this.rpc.destroy(id, (err) => { 249 | if (err) { 250 | return this.errors.push(err); 251 | } 252 | this.actions.save(); 253 | }); 254 | }); 255 | }; 256 | 257 | this.actions.logs = (id) => { 258 | let share = this._getShareById(id); 259 | let loggerOutputFolder = path.normalize(share.config.loggerOutputFile); 260 | try { 261 | if (!fs.statSync(loggerOutputFolder).isDirectory()) { 262 | loggerOutputFolder = path.dirname(loggerOutputFolder); 263 | } 264 | } catch (err) { 265 | loggerOutputFolder = path.dirname(loggerOutputFolder); 266 | } 267 | 268 | if (share && share.config && loggerOutputFolder) { 269 | console.log(loggerOutputFolder); 270 | shell.showItemInFolder(loggerOutputFolder); 271 | } else { 272 | this.errors.push(new Error('Share is not configured to log output')); 273 | } 274 | }; 275 | 276 | this.actions.edit = (id) => { 277 | let share = this._getShareById(id); 278 | if (share && share.path) { 279 | shell.openItem(path.normalize(share.path)); 280 | } else { 281 | this.errors.push(new Error('Share path is configured incorrectly')); 282 | } 283 | }; 284 | 285 | this.actions.clearErrors = () => { 286 | this.errors = []; 287 | }; 288 | } 289 | } 290 | 291 | /** 292 | * Takes a single share status object and returns a view model's version of 293 | * the share status - this method is automatically applied in the status 294 | * polling results. 295 | * @private 296 | * @param {Object} shareStatus 297 | */ 298 | function _mapStatus(share) { 299 | share.isValid = true; 300 | share.isErrored = share.state === 2; 301 | share.isRunning = share.state === 1; 302 | share.isStopped = share.state === 0; 303 | share.meta.uptimeReadable = prettyms(share.meta.uptimeMs); 304 | 305 | return share; 306 | } 307 | 308 | 309 | module.exports = ShareList; 310 | -------------------------------------------------------------------------------- /app/test/integration/about.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/about.integration.js -------------------------------------------------------------------------------- /app/test/integration/footer.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/footer.integration.js -------------------------------------------------------------------------------- /app/test/integration/logs.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/logs.integration.js -------------------------------------------------------------------------------- /app/test/integration/main.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/main.integration.js -------------------------------------------------------------------------------- /app/test/integration/setup.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/setup.integration.js -------------------------------------------------------------------------------- /app/test/integration/updater.integration.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/test/integration/updater.integration.js -------------------------------------------------------------------------------- /app/test/unit/updater.unit.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const proxyquire = require('proxyquire'); 4 | const expect = require('chai').expect; 5 | 6 | describe('Updater', function() { 7 | 8 | describe('#checkForUpdates', function() { 9 | 10 | it('should fail if there is an error', function() { 11 | var updater = proxyquire('../../lib/updater', { 12 | request: function(url, callback) { 13 | callback(new Error('Unknown error'), {}, null); 14 | } 15 | }); 16 | updater.once('error', function(err) { 17 | expect(err.message).to.equal('Unknown error'); 18 | }); 19 | updater.checkForUpdates(); 20 | }); 21 | 22 | it('should fail if there is a non-200 status code returned', function() { 23 | var updater = proxyquire('../../lib/updater', { 24 | request: function(url, callback) { 25 | callback(null, { statusCode: 400 }, null); 26 | } 27 | }); 28 | updater.once('error', function(err) { 29 | expect(err.message).to.equal('Failed to check updates'); 30 | }); 31 | updater.checkForUpdates(); 32 | }); 33 | 34 | it('should fail if it cannot parse the body', function() { 35 | var updater = proxyquire('../../lib/updater', { 36 | request: function(url, callback) { 37 | callback(null, { statusCode: 200 }, 'NOT JSON'); 38 | } 39 | }); 40 | updater.once('error', function(err) { 41 | expect(err.message).to.equal('Failed to parse update info'); 42 | }); 43 | updater.checkForUpdates(); 44 | }); 45 | 46 | it('should not emit if version not greater than current', function(done) { 47 | var updater = proxyquire('../../lib/updater', { 48 | request: function(url, callback) { 49 | callback(null, { statusCode: 200 }, 50 | { html_url: '', tag_name: '0.0.0' } 51 | ); 52 | } 53 | }); 54 | updater.once('update_available', function() { 55 | throw new Error(); 56 | }); 57 | updater.checkForUpdates(); 58 | setImmediate(done); 59 | }); 60 | 61 | it('should emit if the version is greater than current', function(done) { 62 | var updater = proxyquire('../../lib/updater', { 63 | request: function(url, callback) { 64 | callback(null, { statusCode: 200 }, 65 | { html_url: '', tag_name: '100.0.0' } 66 | ); 67 | } 68 | }); 69 | updater.once('update_available', function() { 70 | done(); 71 | }); 72 | updater.checkForUpdates(); 73 | }); 74 | 75 | }); 76 | 77 | }); 78 | -------------------------------------------------------------------------------- /app/views/about/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const about = require('../../package'); 4 | const daemonPackage = require('xcore-daemon/package'); 5 | const {ipcRenderer: ipc} = require('electron'); 6 | 7 | module.exports = { 8 | components: { 9 | 'modal': require('../components/modal') 10 | }, 11 | data: function() { 12 | return { 13 | isVisible: false 14 | }; 15 | }, 16 | methods: { 17 | close: function() { 18 | this.isVisible = false; 19 | } 20 | }, 21 | created: function() { 22 | ipc.on('showAboutDialog', () => { 23 | this.isVisible = true; 24 | }); 25 | }, 26 | template: ` 27 | 28 |
29 | 30 |
31 | 32 |
33 | X Core section 34 |
35 | 36 |
37 | 38 |
39 |
40 | ` 41 | }; 42 | -------------------------------------------------------------------------------- /app/views/components/confirmationModal/ConfirmationModal.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 101 | 102 | 117 | -------------------------------------------------------------------------------- /app/views/components/disk-allocator/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const filters = require('../filters/metrics'); 4 | 5 | module.exports = { 6 | props: ['value', 'available'], 7 | filters: filters, 8 | data: function() { 9 | return { 10 | selectedMetric: 'MB', 11 | increments: { 12 | KB: 1000, 13 | MB: 100, 14 | GB: 1, 15 | TB: 0.0001 16 | } 17 | }; 18 | }, 19 | methods: { 20 | changeAllocation: function(val) { 21 | this.$emit('input', val); 22 | } 23 | }, 24 | watch: { 25 | selectedMetric: function(newVal, oldVal) { 26 | if(newVal !== oldVal) { 27 | let bVal = filters.toBytes(filters.toUnit(this.value, newVal)); 28 | this.$emit('input', bVal); 29 | } 30 | } 31 | }, 32 | components: { 33 | 'metric-input': require('./metric-input'), 34 | 'metric-dropdown': require('./metric-dropdown') 35 | }, 36 | template: ` 37 |
38 | 39 |
40 | 46 | 47 | 48 | 49 | of {{available | toUnit(selectedMetric)}} Available 50 |
51 | 52 |
53 | ` 54 | } 55 | -------------------------------------------------------------------------------- /app/views/components/disk-allocator/metric-dropdown.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const convert = require('bytes'); 4 | 5 | module.exports = { 6 | props: ['value'], 7 | methods: { 8 | updateMetric: function(format) { 9 | this.$emit('input', format); 10 | } 11 | }, 12 | template: ` 13 | 24 | ` 25 | } 26 | -------------------------------------------------------------------------------- /app/views/components/disk-allocator/metric-input.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const filters = require('../filters/metrics'); 4 | 5 | module.exports = { 6 | props: ['value', 'format', 'step', 'available'], 7 | filters: filters, 8 | data: function() { 9 | return { 10 | displayValue: this.parseUnit(this.value, this.format) 11 | }; 12 | }, 13 | watch: { 14 | value: function(newVal, oldVal) { 15 | if(newVal !== oldVal) { 16 | this.displayValue = this.parseUnit(newVal, this.format); 17 | } 18 | }, 19 | format: function(newVal, oldVal) { 20 | if(newVal !== oldVal) { 21 | this.displayValue = this.parseUnit(this.value, newVal); 22 | } 23 | } 24 | }, 25 | methods: { 26 | changeAllocation: function() { 27 | let unitString = this.$refs.input.value + this.format; 28 | this.$emit('input', filters.toBytes(unitString)); 29 | }, 30 | parseUnit: function(val, format) { 31 | return parseFloat(filters.toUnit(val, format)) 32 | } 33 | }, 34 | template: ` 35 | 43 | 44 | ` 45 | } 46 | -------------------------------------------------------------------------------- /app/views/components/external-anchor/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const {shell} = require('electron'); 3 | 4 | module.exports = { 5 | props: ['href'], 6 | methods: { 7 | openExternal: function(event) { 8 | shell.openExternal(this.href); 9 | } 10 | }, 11 | template: ` 12 | 13 | 14 | 15 | ` 16 | } 17 | -------------------------------------------------------------------------------- /app/views/components/filters/metrics.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const convert = require('bytes'); 3 | 4 | module.exports = { 5 | toUnit: function(bytes, unit) { 6 | return convert(Number(bytes), {unit: unit, decimalPlaces: 4}); 7 | }, 8 | toBytes: function(str) { 9 | return convert.parse(String(str)); 10 | }, 11 | parseFloat: function(string) { 12 | return parseFloat(string); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /app/views/components/genericModal/GenericModal.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 101 | 102 | 122 | -------------------------------------------------------------------------------- /app/views/components/modal/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | props: ['show', 'id'], 5 | template: ` 6 | 21 | ` 22 | } 23 | -------------------------------------------------------------------------------- /app/views/components/notification/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | props: ['notes'], 5 | created: function() { 6 | window.document.body.addEventListener('mouseup', eraseNotes.bind(this)); 7 | }, 8 | beforeDestroy: function() { 9 | window.document.body.removeEventListener('mouseup', eraseNotes.bind(this)); 10 | }, 11 | template: ` 12 | 18 | ` 19 | } 20 | 21 | function eraseNotes() { 22 | this.$emit('erase'); 23 | } 24 | -------------------------------------------------------------------------------- /app/views/components/numeric-input/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | props: ['value', 'max', 'min', 'step'], 5 | methods: { 6 | changeInput: function(val) { 7 | let numericVal = String(val).replace(/\D/g,''); 8 | numericVal = (numericVal > this.max) 9 | ? this.max 10 | : (numericVal < this.min ) 11 | ? this.min 12 | : numericVal; 13 | 14 | this.$forceUpdate(); 15 | this.$emit('input', numericVal); 16 | }, 17 | }, 18 | template: ` 19 | 27 | 28 | ` 29 | } 30 | -------------------------------------------------------------------------------- /app/views/components/overlay/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | props: ['class'], 5 | data: function() { 6 | return { 7 | overlayClass: ['overlay', this.class] 8 | }; 9 | }, 10 | template: ` 11 |
12 | 13 |
14 | ` 15 | }; 16 | -------------------------------------------------------------------------------- /app/views/components/updateModal/UpdateModal.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 101 | 102 | 117 | -------------------------------------------------------------------------------- /app/views/migration/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | data: function() { 5 | return { 6 | shareList: window.Store.shareList, 7 | ethAddress: '' 8 | }; 9 | }, 10 | components: { 11 | 'ext-a' : require('../components/external-anchor') 12 | }, 13 | methods: { 14 | checkEthereumAddress: function(address) { 15 | const utils = require('xcore-daemon').utils; 16 | return utils.isValidEthereumAddress(address); 17 | }, 18 | setEthAddressAndQuit: function() { 19 | for(let i = 0; i < this.shareList.shares.length; i++) { 20 | //Change the address 21 | let share = this.shareList.shares[i]; 22 | share.config.paymentAddress = this.ethAddress; 23 | //Writes the config changes, and stops the node. 24 | this.shareList.actions.update(share.id); 25 | this.shareList.actions.stop(share.id); 26 | } 27 | return this.$router.push({ path: '/overview' }); 28 | } 29 | }, 30 | template: ` 31 |
32 |
33 |
34 |
35 | < Overview 36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |
44 |
45 |

Ethereum Address Migration

46 |

X Core supports Ethereum ERC20 token. Please provide your Ethereum address from a supported wallet to continue receiving payments.

47 |

Parity · Mist · MyEtherWallet

48 |
49 |
50 |
51 |
52 | 53 | 54 |
55 |
56 |
57 |
58 | Your node will automatically stop after you apply these changes. Please restart your node for the changes to take effect. 59 |
60 |
61 |
62 |
63 | ` 64 | }; 65 | -------------------------------------------------------------------------------- /app/views/overview/footer.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | components: { 3 | 'ext-a' : require('../components/external-anchor') 4 | }, 5 | template: ` 6 | 12 | ` 13 | }; 14 | -------------------------------------------------------------------------------- /app/views/overview/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const isTestnet = process.env.isTestNet === 'true'; 3 | 4 | module.exports = { 5 | components: { 6 | 'overview-nav': require('./nav'), 7 | 'overview-footer': require('./footer'), 8 | 'error': require('../components/notification') 9 | }, 10 | name: 'overview', 11 | data: function() { 12 | return { 13 | store: window.Store.shareList, 14 | uiState: { 15 | selected: [] 16 | }, 17 | appState: { 18 | isTestnet: isTestnet 19 | } 20 | } 21 | }, 22 | 23 | computed: { 24 | listHasItems: function() { 25 | return this.uiState.selected.length > 0; 26 | } 27 | }, 28 | 29 | created: function() { 30 | this.store.actions.status(() => { 31 | this.store.actions.poll().start(); 32 | }); 33 | 34 | }, 35 | 36 | destroyed: function() { 37 | this.store.actions.poll().stop(); 38 | }, 39 | 40 | methods: { 41 | toggleAll: function() { 42 | if(this.listHasItems) { 43 | this.uiState.selected = []; 44 | } else { 45 | this.store.shares.forEach((share) => { 46 | this.uiState.selected.push(share.id); 47 | }); 48 | } 49 | } 50 | }, 51 | 52 | template: ` 53 | 54 |
55 | 56 |
57 | Currently Running X Core on Test Network... 58 |
59 | 60 | 61 |
62 |
63 |
64 |

Overview

65 |
66 |
67 |
68 |
69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 99 | 100 | 101 | 102 | 103 | 104 | 110 | 111 | 112 | 113 | 114 | 115 | 122 | 123 | 124 | 125 | 126 | 127 | 133 | 134 | 135 | 138 | 141 | 142 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 |
#NodeIDStatusLocationUptimeRestartsPeersBridgesAllocsSharedDeltaPort 88 | 89 | 90 | Options 91 | 92 | Start 93 | Stop 94 | Restart 95 | 96 | Delete 97 | 98 |
105 | 109 | {{index}}{{share.id}} 116 |
ON
117 |
OFF
118 |
Loading
119 | 120 | 121 |
{{share.config.storagePath}}{{share.meta.uptimeReadable}}{{share.meta.numRestarts}}{{share.meta.farmerState.totalPeers}} 128 |
Disconnected
129 |
Connecting
130 |
Confirming
131 |
Connected
132 |
{{share.meta.farmerState.contractCount}} ({{share.meta.farmerState.dataReceivedCount}} received){{share.meta.farmerState.spaceUsed}} ({{share.meta.farmerState.percentUsed}}%) 136 | {{share.meta.farmerState.ntpStatus.delta}} 137 | 139 | {{share.meta.farmerState.ntpStatus.delta}} 140 | {{share.meta.farmerState.portStatus.listenPort}} {{share.meta.farmerState.portStatus.connectionType}} 143 | 144 | 145 | Options 146 | 147 | Start 148 | Stop 149 | Restart 150 | Logs 151 | Edit 152 | 153 | Delete 154 | 155 |
Add Drive to Get Started
164 | 165 |
166 |
167 |
168 | 169 | 170 | 171 |
172 |
173 | ` 174 | }; 175 | -------------------------------------------------------------------------------- /app/views/overview/nav.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | components: { 3 | 'ext-a' : require('../components/external-anchor'), 4 | 'uploader': require('../uploader') 5 | }, 6 | data: function() { 7 | return window.Store.shareList; 8 | }, 9 | methods: { 10 | importShares: function(event) { 11 | Array.prototype.forEach.call(event.target.files, (file) => { 12 | this.actions.import(file.path, () => { 13 | if(this.errors.length === 0) { 14 | return this.$router.push({ path: '/overview' }); 15 | } 16 | }); 17 | }); 18 | } 19 | }, 20 | template: ` 21 | 36 | ` 37 | }; 38 | -------------------------------------------------------------------------------- /app/views/overview/row.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/app/views/overview/row.js -------------------------------------------------------------------------------- /app/views/share-wizard/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | components: { 5 | 'error': require('../components/notification') 6 | }, 7 | data: function() { 8 | return { 9 | newShare: window.Store.newShare, 10 | shareList: window.Store.shareList 11 | }; 12 | }, 13 | template: ` 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 |
28 | ` 29 | }; 30 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard0.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | components: { 5 | 'ext-a' : require('../components/external-anchor') 6 | }, 7 | template: ` 8 |
9 |
10 |
11 |
12 | X Core 13 |
14 |
15 | Setup Wizard 16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 |
25 |

Welcome to X Core!

26 |

Using X Core, you can earn INXT tokens
by renting out your extra hard drive space.

27 | Start Setup 28 |
29 | I'm an experienced user, skip setup 30 |
31 |
32 |
33 |
34 |
35 | ` 36 | }; 37 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | data: function() { 5 | return window.Store.newShare; 6 | }, 7 | components: { 8 | 'ext-a' : require('../components/external-anchor') 9 | }, 10 | created: function() { 11 | this.actions.reset(); 12 | //Set tunnel options to 0 to prepare for removal of tunneling 13 | this.$set(this.config, 'maxTunnels', 0); 14 | this.$set(this.config, 'tunnelGatewayRange', { 15 | min: 0, 16 | max: 0 17 | }); 18 | //Pre-fill their first payment address if they already have a share 19 | if(window.Store.shareList.shares.length > 0) { 20 | this.$set(this.config, 'paymentAddress', window.Store.shareList.shares[0].config.paymentAddress); 21 | } 22 | }, 23 | methods: { 24 | checkEthereumAddress: function(address) { 25 | const utils = require('xcore-daemon').utils; 26 | return utils.isValidEthereumAddress(address); 27 | } 28 | }, 29 | template: ` 30 |
31 |
32 |
33 |
34 | < Go Back 35 | < Go Back 36 |
37 |
38 | Step 1 of 5 39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 |
47 |
48 |

Step 1 - Payout Address

49 |

X Core uses an ERC20 token. X Core Alpha testing phase does not include INXT payments. Enter address only for testing purposes.

50 |

Parity · Mist · MyEtherWallet

51 |
52 |
53 |
54 |
55 | 56 | Next 57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | ` 66 | }; 67 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | data: function() { 6 | return { 7 | newShare: window.Store.newShare, 8 | shareList: window.Store.shareList, 9 | buttonText: 'Select Location' 10 | }; 11 | }, 12 | methods: { 13 | handleFileInput: function(event) { 14 | this.$set(this.newShare.config, 'storagePath', event.target.files[0].path); 15 | this.newShare.actions.getFreeDiskSpace(this.newShare.config.storagePath, () => {}); 16 | }, 17 | pathIsValid: function() { 18 | for(let i = 0; i < this.shareList.shares.length; i++) { 19 | let share = this.shareList.shares[i]; 20 | if(share.config.storagePath === this.newShare.config.storagePath) { 21 | this.buttonText = 'Location In Use'; 22 | return false; 23 | } 24 | } 25 | if(!path.isAbsolute(this.newShare.config.storagePath)) { 26 | this.buttonText = 'Invalid Location'; 27 | return false; 28 | } 29 | this.buttonText = 'Select Location'; 30 | return true; 31 | } 32 | }, 33 | template: ` 34 |
35 |
36 |
37 |
38 | < Go Back 39 |
40 |
41 | Step 2 of 5 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |

Step 2 - Storage Location

52 |

X Core uses the free space on your drive,
to save encrypted bits of files while you are online.

53 |
54 |
55 |
56 |
57 | 58 | {{buttonText}} 59 |
60 |
61 |
62 |
63 | 64 | ` 65 | }; 66 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const bytes = require('bytes'); 3 | 4 | module.exports = { 5 | data: function() { 6 | return { 7 | store: window.Store.newShare 8 | }; 9 | }, 10 | filters: require('../components/filters/metrics'), 11 | components: { 12 | 'disk-allocator' : require('../components/disk-allocator') 13 | }, 14 | created: function() { 15 | if(!this.store.storageAvailable) { 16 | this.store.errors.push(new Error('Invalid directory selected')); 17 | } 18 | }, 19 | methods: { 20 | validAllocation: function() { 21 | return this.store.config.storageAllocation <= this.store.storageAvailable; 22 | } 23 | }, 24 | template: ` 25 |
26 |
27 |
28 |
29 | < Go Back 30 |
31 |
32 | Step 3 of 5 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 |

Step 3 - Storage Sharing

43 |

X Core uses only the storage space you share.
The more storage you share, the more you can earn.

44 |
45 |
46 |
47 |
48 | 51 | 52 |
53 |
54 |
55 |
56 | Next 57 |
58 |
59 |
60 |
61 | ` 62 | }; 63 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard4.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | data: function() { 5 | return { 6 | newShare: window.Store.newShare, 7 | shareList: window.Store.shareList, 8 | MAXPORTNUM: 65536, 9 | MINPORTNUM: 1024, 10 | uiState: { 11 | isChecking: false 12 | }, 13 | invalidPort: { 14 | port: -1, 15 | message: '' 16 | }, 17 | continueButtonText: 'Next' 18 | }; 19 | }, 20 | components: { 21 | 'numeric-input': require('../components/numeric-input') 22 | }, 23 | created: function() { 24 | //If the user already has a share, start out their port number as the one after 25 | //the one their last share is using 26 | let numShares = this.shareList.shares.length; 27 | if(numShares > 0) { 28 | this.newShare.config.rpcPort = this.shareList.shares[numShares - 1].config.rpcPort + 1; 29 | } 30 | }, 31 | methods: { 32 | chooseRandomPort: function() { 33 | this.$set(this.newShare.config, 'rpcPort', this.getRandomValidPort()); 34 | }, 35 | getRandomValidPort: function() { 36 | return Math.floor(Math.random() * (this.MAXPORTNUM - this.MINPORTNUM)) + this.MINPORTNUM; 37 | }, 38 | portIsAvailable: function(port, callback) { 39 | const utils = require('xcore-daemon').utils; 40 | return utils.portIsAvailable(port, callback); 41 | }, 42 | checkPort: function() { 43 | this.continueButtonText = 'Checking...'; 44 | this.uiState.isChecking = true; 45 | let self = this; 46 | this.portIsAvailable(this.newShare.config.rpcPort, function(err, result) { 47 | if(err || !result) { 48 | self.invalidPort.port = self.newShare.config.rpcPort; 49 | self.invalidPort.message = err || `Port ${self.invalidPort.port} appears to be in use`; 50 | self.uiState.isChecking = false; 51 | self.continueButtonText = 'Next'; 52 | } else { 53 | self.uiState.isChecking = false; 54 | return self.$router.push({ path: '/share-wizard/wizard5' }); 55 | } 56 | }); 57 | } 58 | }, 59 | template: ` 60 |
61 |
62 |
63 |
64 | < Go Back 65 |
66 |
67 | Step 4 of 5 68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 |
77 |

Step 4 - Connection Settings

78 |

Port to bind for RPC server, make sure this is forwarded if behind
a NAT or firewall - otherwise X Core will try to punch out.

79 |
80 |
81 |
82 |
83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 |
95 |
96 |
97 |
98 |

{{invalidPort.message}}

99 |
100 |
101 |
102 |
103 | ` 104 | }; 105 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard5.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | data: function() { 5 | return { 6 | newShare: window.Store.newShare, 7 | shareList: window.Store.shareList, 8 | 9 | }; 10 | }, 11 | components: { 12 | 'ext-a' : require('../components/external-anchor') 13 | }, 14 | created: function() { 15 | //If the user has doNotTraverseNat set to false, skip this page to keep things simple 16 | if(!this.newShare.config.doNotTraverseNat) { 17 | return this.saveToDisk(); 18 | } 19 | //If the user already has a share, start out their address to be the one 20 | //they're currently using 21 | let numShares = this.shareList.shares.length; 22 | if(numShares > 0) { 23 | this.newShare.config.rpcAddress = this.shareList.shares[numShares - 1].config.rpcAddress; 24 | } 25 | }, 26 | methods: { 27 | saveToDisk: function() { 28 | let configPath = this.newShare.actions.createShareConfig(); 29 | if(configPath) { 30 | this.shareList.actions.import(configPath, (err) => { 31 | if(!err) { 32 | return this.$router.push({ path: '/share-wizard/wizard6' }); 33 | } 34 | }); 35 | } 36 | }, 37 | validAddress: function() { 38 | return this.newShare.config.rpcAddress && this.newShare.config.rpcAddress.length !== 0; 39 | } 40 | }, 41 | template: ` 42 |
43 |
44 |
45 |
46 | < Go Back 47 |
48 |
49 | Step 5 of 5 50 |
51 |
52 |
53 |
54 | 55 |
56 |
57 |
58 |
59 |

Step 5 - Host Address

60 |

Please enter your external public IP address or a valid hostname so that other nodes will be able to connect to you. It is recommended to use a hostname service like No-IP if your external public IP address is not static.

61 |

If you don't know what your external public IP address is, use a site like whatismyip.com.

62 |
63 |
64 |
65 |
66 | 67 | 68 | 69 |
70 |
71 |
72 |
73 | ` 74 | }; 75 | -------------------------------------------------------------------------------- /app/views/share-wizard/wizard6.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | template: ` 5 |
6 |
7 |
8 |
9 | X Core 10 |
11 |
12 | Finished 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |

You are amazing!

23 |

You have successfully configured X Core.
Keep on sharing and earning INXT.

24 | Finish 25 |
26 |
27 |
28 |
29 | ` 30 | }; 31 | -------------------------------------------------------------------------------- /app/views/terms/content.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | components: { 5 | 'ext-a': require('../components/external-anchor') 6 | }, 7 | template: `
8 | ` 9 | }; 10 | -------------------------------------------------------------------------------- /app/views/terms/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {localStorage: _localStorage} = window; 4 | const TERMS_KEY = '__TERMS_READ'; 5 | const path = require('path'); 6 | const {readFileSync} = require('fs'); 7 | 8 | module.exports = { 9 | components: { 10 | 'modal': require('../components/modal'), 11 | 'terms-contents': require('./content') 12 | }, 13 | data: function() { 14 | return { 15 | isVisible: false 16 | }; 17 | }, 18 | methods: { 19 | accepted: function() { 20 | _localStorage.setItem(TERMS_KEY, '1'); 21 | this.isVisible = false; 22 | } 23 | }, 24 | created: function() { 25 | }, 26 | template: ` 27 | 28 |
29 | 30 |
31 | 32 |
33 |

34 |
35 | 36 |
37 | 38 |
39 |
40 | ` 41 | }; 42 | -------------------------------------------------------------------------------- /app/views/updater/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const {app, shell, ipcRenderer: ipc} = require('electron'); 4 | const updater = require('../../lib/updater'); 5 | 6 | module.exports = { 7 | components: { 8 | 'modal': require('../components/modal') 9 | }, 10 | data: function() { 11 | return { 12 | update: false, 13 | releaseURL: '', 14 | releaseTag: '', 15 | show: false 16 | }; 17 | }, 18 | methods: { 19 | download: function(event) { 20 | if (event) { 21 | event.preventDefault(); 22 | } 23 | 24 | this.close(); 25 | 26 | if (window.confirm('You must quit X Core to upgrade. Continue?')) { 27 | shell.openExternal(this.releaseURL); 28 | app.quit(); 29 | } 30 | }, 31 | close: function() { 32 | this.show = false; 33 | } 34 | }, 35 | created: function() { 36 | const view = this; 37 | 38 | // ipc.on('checkForUpdates', function() { 39 | // updater.checkForUpdates(); 40 | // view.show = true; 41 | // }); 42 | 43 | // updater.checkForUpdates(); 44 | 45 | // updater.on('update_available', function(meta) { 46 | // view.update = true; 47 | // view.releaseTag = meta.releaseTag; 48 | // view.releaseURL = meta.releaseURL; 49 | 50 | // view.show = true; 51 | // }); 52 | 53 | // updater.on('error', function(err) { 54 | // console.log(err); 55 | // }); 56 | }, 57 | template: ` 58 | 59 |
60 | 61 | 62 |
63 | 64 |
65 |
66 |

You are already using the latest version.

67 |
68 |
69 |

X Core {{releaseTag}} is available!

70 |

Would you like to download the update now?

71 |
72 |
73 | 74 |
75 | 76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 |
85 |
86 | ` 87 | }; 88 | -------------------------------------------------------------------------------- /app/views/uploader/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | props: ['selectAction'], 5 | methods: { 6 | proxyFileInput: function(event) { 7 | document.getElementById('fileProxyTarget' + this._uid).click(); 8 | } 9 | }, 10 | template: ` 11 |
12 | 13 | 14 |
15 | ` 16 | } 17 | -------------------------------------------------------------------------------- /app/views/xcore/dashboard.vue: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /app/views/xcore/settings.vue: -------------------------------------------------------------------------------- 1 | 126 | 331 | -------------------------------------------------------------------------------- /app/xApp.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const { homedir } = require('os'); 3 | const VueRouter = require('vue-router'); 4 | const router = new VueRouter(require('./xRoutes')); 5 | const utils = require('xcore-daemon').utils; 6 | 7 | var vm = new Vue({ 8 | router, 9 | el: '#xApp', 10 | 11 | data: { 12 | shareList: window.Store.shareList, 13 | displaySlider: false 14 | }, 15 | components: { 16 | 'welcome': require('./views/xcore/welcome'), 17 | 'settings': require('./views/xcore/settings'), 18 | 'dashboard': require('./views/xcore/dashboard') 19 | }, 20 | methods: { 21 | changeView: function () { 22 | router.replace({ path: 'settings' }); 23 | } 24 | }, 25 | created: function () { 26 | this.shareList.actions.load((err) => { 27 | this.shareList.actions.status(() => { 28 | //Check to see if any of the shares aren't using Ethereum addresses 29 | if (this.shareList.shares.length === 0) { 30 | router.replace('welcome'); 31 | } else { 32 | router.replace('settings'); 33 | } 34 | }); 35 | }); 36 | } 37 | }); -------------------------------------------------------------------------------- /app/xIndex.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |

Version: 1.1.2

18 |
19 | 20 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/xRenderer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const dnode = require('dnode'); 4 | const { ipcRenderer: ipc } = require('electron'); 5 | const { EventEmitter } = require('events'); 6 | const UserData = require('./lib/userdata'); 7 | const VueRouter = require('vue-router'); 8 | const BootstrapVue = require('bootstrap-vue'); 9 | 10 | window.UserData = UserData.toObject(); 11 | window.Vue = require('./node_modules/vue/dist/vue.common.js'); 12 | window.ViewEvents = new EventEmitter(); // NB: For view-to-view communication 13 | window.Vue.use(VueRouter); 14 | window.Vue.use(BootstrapVue); 15 | 16 | // NB: When settings change, notify the main process 17 | UserData.on('settingsUpdated', (updatedSettings) => { 18 | ipc.send('appSettingsChanged', updatedSettings); 19 | }); 20 | 21 | window.daemonSocket = dnode.connect(45015, (rpc) => { 22 | 23 | // NB: Add global reference to the daemon RPC 24 | window.daemonRpc = rpc; 25 | 26 | // Set up any required view-model store instances 27 | window.Store = { 28 | shareList: new (require('./stores/share_list'))(rpc), 29 | newShare: new (require('./stores/share'))() 30 | } 31 | window.app = new window.Vue(require('./xApp')); 32 | 33 | // NB: Check user data for application settings and signal appropriate 34 | // NB: messages to the main process 35 | if (!window.UserData.appSettings.silentMode) { 36 | ipc.send('showApplicationWindow'); 37 | } 38 | }); -------------------------------------------------------------------------------- /app/xRoutes.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | routes: [ 3 | { 4 | path: '/welcome', 5 | component: require('./views/xcore/welcome') 6 | }, 7 | { 8 | path: '/dashboard', 9 | component: require('./views/xcore/dashboard') 10 | }, 11 | { 12 | path: '/settings', 13 | component: require('./views/xcore/settings') 14 | } 15 | ] 16 | }; -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - platform: x86 4 | nodejs_version: "10" 5 | arch: "32" 6 | - platform: x64 7 | nodejs_version: "10" 8 | arch: "64" 9 | 10 | install: 11 | - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version $env:platform) 12 | - npm -g install npm@2 13 | - set PATH=%APPDATA%\npm;%PATH% 14 | - set PATH="C:\Program Files (x86)\NSIS";%PATH% 15 | - npm install 16 | 17 | build: off 18 | 19 | test_script: 20 | # Output useful info for debugging. 21 | - node --version 22 | - npm --version 23 | # run tests 24 | #- npm test 25 | 26 | after_test: 27 | - npm run release 28 | - ps: "cd releases" 29 | - ren *.exe *.win%arch%.exe 30 | 31 | artifacts: 32 | - path: releases\*.win32.exe 33 | - path: releases\*.win64.exe 34 | 35 | deploy: 36 | - provider: GitHub 37 | tag: ChangeMe 38 | release: autobin draft release 39 | auth_token: 40 | secure: bPlHCdaxjgVJXAW2CooVSnyJr0pHg+qu2HdsS27HgVDRRXlTO+mgMk7V3+N/OTe+ 41 | artifact: /.*\.exe/ 42 | draft: true 43 | on: 44 | branch: master 45 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('./tasks/build'); 4 | require('./tasks/release'); 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "asar": "^0.13.1", 4 | "babel-core": "^6.26.3", 5 | "babel-plugin-transform-runtime": "^6.23.0", 6 | "babel-preset-es2015": "^6.24.1", 7 | "chai": "^4.2.0", 8 | "coveralls": "^3.0.3", 9 | "electron": "^5.0.11", 10 | "electron-builder": "^21.2.0", 11 | "electron-installer-windows": "^2.0.0", 12 | "electron-mocha": "^8.1.2", 13 | "electron-notarize": "^0.1.1", 14 | "electron-packager": "^14.0.6", 15 | "elemon": "^5.0.3", 16 | "fs-jetpack": "^2.2.2", 17 | "gulp": "^4.0.2", 18 | "gulp-sass": "^4.0.2", 19 | "gulp-sass-variables": "^1.2.0", 20 | "gulp-util": "^3.0.6", 21 | "gulp-vueify": "0.0.3", 22 | "istanbul": "^0.4.5", 23 | "jshint": "^2.10.2", 24 | "proxyquire": "^2.1.3", 25 | "q": "^1.4.1", 26 | "sinon": "^7.5.0", 27 | "yargs": "^14.0.0" 28 | }, 29 | "optionalDependencies": { 30 | "appdmg": "^0.3.2", 31 | "devuan": "^0.0.2", 32 | "rcedit": "^0.3.0" 33 | }, 34 | "scripts": { 35 | "postinstall": "node ./tasks/app_npm_install", 36 | "installdeps": "electron-builder install-app-deps", 37 | "app-install": "node ./tasks/app_npm_install", 38 | "build": "./node_modules/.bin/gulp build && npm run installdeps", 39 | "release": "NODE_ENV=production npm run build && electron-builder", 40 | "publish": "./node_modules/.bin/electron-builder --publish always", 41 | "formerRelease": "./node_modules/.bin/gulp release", 42 | "start": "./node_modules/.bin/electron ./build", 43 | "test": "npm run test-unit && npm run test-integration", 44 | "test-unit": "ELECTRON_PATH=./node_modules/.bin/electron ./node_modules/.bin/electron-mocha ./app/test/unit/**", 45 | "test-integration": "ELECTRON_PATH=./node_modules/.bin/electron ./node_modules/.bin/electron-mocha --renderer ./app/test/integration/**", 46 | "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/electron-mocha ./app/test/unit/** -- --recursive", 47 | "jshint": "./node_modules/.bin/jshint --config .jshintrc --exclude ./app/node_modules ./app" 48 | }, 49 | "dependencies": { 50 | "dotenv": "^8.1.0", 51 | "electron-reload": "^1.4.0", 52 | "natives": "^1.1.6" 53 | }, 54 | "build": { 55 | "appId": "com.farmer.xcore", 56 | "productName": "X Core", 57 | "directories": { 58 | "app": "build", 59 | "output": "dist" 60 | }, 61 | "publish": { 62 | "provider": "github" 63 | }, 64 | "linux": { 65 | "category": "Network", 66 | "target": "deb" 67 | }, 68 | "win": { 69 | "target": "nsis", 70 | "icon": "resources/windows/icon.ico" 71 | }, 72 | "nsis": { 73 | "oneClick": false, 74 | "perMachine": false 75 | }, 76 | "mac": { 77 | "icon": "resources/osx/icon.icns", 78 | "hardenedRuntime": true, 79 | "gatekeeperAssess": false, 80 | "entitlements": "./resources/osx/Info.plist", 81 | "entitlementsInherit": "./resources/osx/Info.plist" 82 | }, 83 | "dmg": { 84 | "sign": false 85 | }, 86 | "afterSign": "resources/osx/notarize.js" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/icon.png -------------------------------------------------------------------------------- /resources/installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/installer.png -------------------------------------------------------------------------------- /resources/launch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/launch.png -------------------------------------------------------------------------------- /resources/linux/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: {{name}} 2 | Version: {{version}} 3 | Maintainer: {{author}} 4 | Priority: optional 5 | Architecture: {{arch}} 6 | Installed-Size: {{size}} 7 | Description: {{description}} 8 | -------------------------------------------------------------------------------- /resources/linux/DEBIAN/postinst: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### 4 | # Any post-install logic on debian based systems goes here 5 | ### 6 | -------------------------------------------------------------------------------- /resources/linux/app.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Encoding=UTF-8 5 | Name={{productName}} 6 | Comment={{description}} 7 | Exec=/opt/{{name}}/{{name}} 8 | Icon=/opt/{{name}}/icon.png 9 | Terminal=false 10 | Categories=Application; 11 | -------------------------------------------------------------------------------- /resources/osx/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | com.apple.security.cs.allow-unsigned-executable-memory 8 | 9 | com.apple.security.cs.disable-executable-page-protection 10 | 11 | com.apple.security.cs.disable-library-validation 12 | 13 | com.apple.security.cs.allow-dyld-environment-variables 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /resources/osx/appdmg.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "{{productName}}", 3 | "icon": "{{dmgIcon}}", 4 | "background": "{{dmgBackground}}", 5 | "icon-size": 128, 6 | "contents": [ 7 | { "x": 410, "y": 220, "type": "link", "path": "/Applications" }, 8 | { "x": 130, "y": 220, "type": "file", "path": "{{appPath}}" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /resources/osx/dmg-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/dmg-background.png -------------------------------------------------------------------------------- /resources/osx/dmg-background@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/dmg-background@2x.png -------------------------------------------------------------------------------- /resources/osx/dmg-icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/dmg-icon.icns -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info EH.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | {{productName}} Helper EH 7 | CFBundleExecutable 8 | {{productName}} Helper EH 9 | CFBundleIdentifier 10 | {{identifier}}.helper.EH 11 | CFBundleName 12 | {{productName}} Helper EH 13 | CFBundlePackageType 14 | APPL 15 | DTSDKName 16 | macosx 17 | LSUIElement 18 | 19 | NSSupportsAutomaticGraphicsSwitching 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info NP.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | {{productName}} Helper NP 7 | CFBundleExecutable 8 | {{productName}} Helper NP 9 | CFBundleIdentifier 10 | {{identifier}}.helper.NP 11 | CFBundleName 12 | {{productName}} Helper NP 13 | CFBundlePackageType 14 | APPL 15 | DTSDKName 16 | macosx 17 | LSUIElement 18 | 19 | NSSupportsAutomaticGraphicsSwitching 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/osx/helper_apps/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | {{identifier}}.helper 7 | CFBundleName 8 | {{productName}} Helper 9 | CFBundlePackageType 10 | APPL 11 | DTSDKName 12 | macosx 13 | LSUIElement 14 | 15 | NSSupportsAutomaticGraphicsSwitching 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /resources/osx/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/icon.icns -------------------------------------------------------------------------------- /resources/osx/iconHighlight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/iconHighlight.png -------------------------------------------------------------------------------- /resources/osx/iconHighlight@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/osx/iconHighlight@2x.png -------------------------------------------------------------------------------- /resources/osx/notarize.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('dotenv').config(); 4 | 5 | const { notarize } = require('electron-notarize'); 6 | 7 | exports.default = async function notarizing(context) { 8 | 9 | console.log('Notarizing...'); 10 | const { electronPlatformName, appOutDir } = context; 11 | if (electronPlatformName !== 'darwin') { 12 | return; 13 | } 14 | 15 | const appName = context.packager.appInfo.productFilename; 16 | 17 | return await notarize({ 18 | appBundleId: 'com.xcore.farmer', 19 | appPath: `${appOutDir}/${appName}.app`, 20 | appleId: process.env.APPLEID, 21 | appleIdPassword: process.env.APPLEIDPASS, 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /resources/windows/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/windows/icon.ico -------------------------------------------------------------------------------- /resources/windows/installer.nsi: -------------------------------------------------------------------------------- 1 | ; NSIS packaging/install script 2 | ; Docs: http://nsis.sourceforge.net/Docs/Contents.html 3 | 4 | !include FileFunc.nsh 5 | !include LogicLib.nsh 6 | !include nsDialogs.nsh 7 | !include x64.nsh 8 | 9 | ; -------------------------------- 10 | ; Variables 11 | ; -------------------------------- 12 | 13 | !define dest "{{dest}}" 14 | !define src "{{src}}" 15 | !define name "{{name}}" 16 | !define productName "{{productName}}" 17 | !define publisher "{{publisher}}" 18 | !define version "{{version}}" 19 | !define icon "{{icon}}" 20 | !define setupIcon "{{setupIcon}}" 21 | !define banner "{{banner}}" 22 | !define is32bit "{{is32bit}}" 23 | 24 | !define exec "{{productName}}.exe" 25 | 26 | !define regkey "Software\${productName}" 27 | !define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}" 28 | 29 | !define envvars "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" 30 | 31 | !define uninstaller "uninstall.exe" 32 | 33 | SetCompressor /SOLID lzma 34 | 35 | Var Arch 36 | 37 | ; Create the shared function. 38 | !macro MYMACRO un 39 | Function ${un}init 40 | 41 | ${If} ${is32bit} == "true" 42 | StrCpy $InstDir "$ProgramFiles32\${productName}" 43 | StrCpy $Arch "32-bit" 44 | SetRegView 32 45 | ${Else} 46 | StrCpy $InstDir "$ProgramFiles64\${productName}" 47 | StrCpy $Arch "64-bit" 48 | SetRegView 64 49 | ${EndIf} 50 | 51 | FunctionEnd 52 | !macroend 53 | 54 | !insertmacro MYMACRO "un." 55 | !insertmacro MYMACRO "" 56 | 57 | ; -------------------------------- 58 | ; Installation 59 | ; -------------------------------- 60 | 61 | !ifdef INNER 62 | 63 | ; don't ask for admin privilages 64 | RequestExecutionLevel user 65 | OutFile "${src}\..\writeuninstaller.exe" 66 | 67 | Var Image 68 | Var ImageHandle 69 | !else 70 | 71 | !system "$\"${NSISDIR}\makensis$\" /DINNER installer.nsi" = 0 72 | 73 | !system "${src}\..\writeuninstaller.exe" = 2 74 | 75 | Name "${productName}" 76 | Icon "${setupIcon}" 77 | OutFile "${dest}" 78 | InstallDirRegKey HKLM "${regkey}" "" 79 | 80 | CRCCheck on 81 | SilentInstall normal 82 | 83 | XPStyle on 84 | ShowInstDetails nevershow 85 | AutoCloseWindow false 86 | WindowIcon off 87 | 88 | Caption "${productName} Setup" 89 | ; Don't add sub-captions to title bar 90 | SubCaption 3 " " 91 | SubCaption 4 " " 92 | 93 | Page custom welcome.confirm welcome.confirmOnLeave 94 | Page instfiles 95 | 96 | Var Image 97 | Var ImageHandle 98 | !endif 99 | 100 | Function .onInit 101 | 102 | ; Call variables for 32-bit / 64-bit 103 | Call init 104 | 105 | !ifdef INNER 106 | 107 | WriteUninstaller "${src}\uninstall.exe" 108 | 109 | Quit 110 | 111 | !else 112 | 113 | ; Extract banner image for welcome page 114 | InitPluginsDir 115 | ReserveFile "${banner}" 116 | File /oname=$PLUGINSDIR\banner.bmp "${banner}" 117 | 118 | !endif 119 | FunctionEnd 120 | 121 | !ifdef INNER 122 | Function un.onInit 123 | 124 | ; Extract banner image for welcome page 125 | InitPluginsDir 126 | ReserveFile "${banner}" 127 | File /oname=$PLUGINSDIR\banner.bmp "${banner}" 128 | 129 | FunctionEnd 130 | !endif 131 | 132 | !ifndef INNER 133 | 134 | Var AddFirewallRuleCheckbox 135 | Var AddFirewallRuleCheckbox_State 136 | 137 | ; Custom welcome page 138 | Function welcome.confirm 139 | 140 | nsDialogs::Create 1018 141 | 142 | ${NSD_CreateLabel} 185 1u 210 40u "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin." 143 | 144 | ${NSD_CreateBitmap} 0 0 170 210 "" 145 | Pop $Image 146 | ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle 147 | 148 | ${NSD_CreateCheckbox} 185 45u 210 10u "Add Windows Firewall Rule" 149 | Pop $AddFirewallRuleCheckbox 150 | 151 | nsDialogs::Show 152 | 153 | ${NSD_FreeImage} $ImageHandle 154 | FunctionEnd 155 | 156 | Function welcome.confirmOnLeave 157 | 158 | ; Save checkbox state on page leave 159 | ${NSD_GetState} $AddFirewallRuleCheckbox $AddFirewallRuleCheckbox_State 160 | 161 | FunctionEnd 162 | !endif 163 | 164 | ; Installation declarations 165 | Section "Install" 166 | 167 | !ifndef INNER 168 | 169 | ; Write application and uninstaller registry keys 170 | WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR" 171 | WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName} ($Arch)" 172 | WriteRegStr HKLM "${uninstkey}" "Publisher" "${publisher}" 173 | WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"' 174 | WriteRegStr HKLM "${uninstkey}" "DisplayVersion" "${version}" 175 | WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"' 176 | 177 | WriteRegStr HKLM "${envvars}" "STORJ_NETWORK" "INXT" 178 | WriteRegStr HKLM "${envvars}" "STORJ_BRIDGE" "https://api.internxt.com" 179 | 180 | ; Remove all application files copied by previous installation 181 | RMDir /r "$INSTDIR" 182 | 183 | SetOutPath $INSTDIR 184 | 185 | ; Sign all binaries if a code sign certificate is set 186 | !if "$%CERT_FILE%" != "${U+24}%CERT_FILE%" 187 | !system "signtool.exe sign /fd sha256 /td sha256 /tr http://timestamp.digicert.com /f $\"%CERT_FILE%$\" /p $\"%CERT_PASSWORD%$\" $\"${src}\*.exe$\"" 188 | !endif 189 | 190 | ; Include all files from /build directory 191 | File /r "${src}\*" 192 | 193 | ; Create start menu shortcut 194 | CreateShortCut "$SMPROGRAMS\${productName} ($Arch).lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico" 195 | 196 | ; Set Windows Firewall rule via PowerShell if user checked this option 197 | ${If} $AddFirewallRuleCheckbox_State == ${BST_CHECKED} 198 | nsExec::ExecToStack "powershell -Command $\"New-NetFirewallRule -DisplayName '${productName}' -Direction Inbound -Program '$INSTDIR\${exec}' -Action allow$\" " 199 | nsExec::ExecToStack "powershell -Command $\"New-NetFirewallRule -DisplayName '${productName}' -Direction Outbound -Program '$INSTDIR\${exec}' -Action allow$\" " 200 | ${EndIf} 201 | 202 | ; Write EstimatedSize uninstaller registry key 203 | ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2 204 | IntFmt $0 "0x%08X" $0 205 | WriteRegDWORD HKLM "${uninstkey}" "EstimatedSize" "$0" 206 | !endif 207 | SectionEnd 208 | 209 | Function .onInstSuccess 210 | Exec "$INSTDIR\${exec}" 211 | FunctionEnd 212 | 213 | !ifdef INNER 214 | ; -------------------------------- 215 | ; Uninstaller 216 | ; -------------------------------- 217 | 218 | ShowUninstDetails nevershow 219 | 220 | UninstallCaption "${productName} Uninstall" 221 | UninstallText "Don't like ${productName} anymore? Hit uninstall button." 222 | UninstallIcon "${icon}" 223 | 224 | UninstPage custom un.confirm un.confirmOnLeave 225 | UninstPage instfiles 226 | 227 | Var RemoveAppDataCheckbox 228 | Var RemoveAppDataCheckbox_State 229 | Var RemoveWindowsFirewallCheckbox 230 | Var RemoveWindowsFirewallCheckbox_State 231 | 232 | ; Custom uninstall confirm page 233 | Function un.confirm 234 | 235 | nsDialogs::Create 1018 236 | 237 | ${NSD_CreateLabel} 185 1u 210 40u "If you really want to remove ${productName} from your computer press the uninstall button." 238 | 239 | ${NSD_CreateBitmap} 0 0 170 210 "" 240 | Pop $Image 241 | ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle 242 | 243 | ${NSD_CreateCheckbox} 185 45u 210 10u "Remove my ${productName} personal data" 244 | Pop $RemoveAppDataCheckbox 245 | 246 | ${NSD_CreateCheckbox} 185 60u 210 10u "Remove Windows Firewall Rule" 247 | Pop $RemoveWindowsFirewallCheckbox 248 | 249 | nsDialogs::Show 250 | 251 | FunctionEnd 252 | 253 | Function un.confirmOnLeave 254 | 255 | ; Save checkbox state on page leave 256 | ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State 257 | ${NSD_GetState} $RemoveWindowsFirewallCheckbox $RemoveWindowsFirewallCheckbox_State 258 | 259 | FunctionEnd 260 | 261 | ; Uninstall declarations 262 | Section "Uninstall" 263 | 264 | ; Call variables for 32-bit / 64-bit 265 | Call un.init 266 | 267 | ; Remove all Registry keys 268 | DeleteRegKey HKLM "${uninstkey}" 269 | DeleteRegKey HKLM "${regkey}" 270 | 271 | Delete "$SMPROGRAMS\${productName} ($Arch).lnk" 272 | 273 | ; Remove whole directory from Program Files 274 | RMDir /r "$INSTDIR" 275 | 276 | ; Try to remove the Windows Firewall rule if user checked this option 277 | ${If} $RemoveWindowsFirewallCheckbox_State == ${BST_CHECKED} 278 | nsExec::ExecToStack "powershell -Command $\"Remove-NetFirewallRule -DisplayName '${productName}' -ErrorAction SilentlyContinue$\" " 279 | ${EndIf} 280 | 281 | ; Remove also appData directory generated by your app if user checked this option 282 | ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED} 283 | RMDir /r "$LOCALAPPDATA\${productName}" 284 | ${EndIf} 285 | 286 | SectionEnd 287 | !endif 288 | -------------------------------------------------------------------------------- /resources/windows/setup-banner.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/windows/setup-banner.bmp -------------------------------------------------------------------------------- /resources/windows/setup-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/internxt/core-gui/eb7b865897cc5d204771a1b35417c3fdc2650253/resources/windows/setup-icon.ico -------------------------------------------------------------------------------- /tasks/app_npm_install.js: -------------------------------------------------------------------------------- 1 | // This script allows you to install native modules (those which have 2 | // to be compiled) for your Electron app. 3 | // The problem is that 'npm install' compiles them against node.js you have 4 | // installed on your computer, NOT against node.js used in Electron 5 | // runtime we've downloaded to this project. 6 | 7 | 'use strict'; 8 | 9 | var childProcess = require('child_process'); 10 | var jetpack = require('fs-jetpack'); 11 | var argv = require('yargs').argv; 12 | var utils = require('./utils'); 13 | var electronVersion = utils.getElectronVersion(); 14 | var nodeModulesDir = jetpack.cwd(__dirname + '/../app/node_modules'); 15 | var dependenciesCompiledAgainst = nodeModulesDir.read('electron_version'); 16 | 17 | // When you raised version of Electron used in your project, the safest 18 | // thing to do is remove all installed dependencies and install them 19 | // once again (so they compile against new version if you use any 20 | // native package). 21 | if (electronVersion !== dependenciesCompiledAgainst) { 22 | nodeModulesDir.dir('.', { empty: true }); 23 | nodeModulesDir.write('electron_version', electronVersion); 24 | } 25 | 26 | // Tell the 'npm install' which is about to start that we want for it 27 | // to compile for Electron. 28 | process.env.npm_config_disturl = 'https://atom.io/download/atom-shell'; 29 | process.env.npm_config_target = electronVersion; 30 | process.env.npm_config_runtime = 'electron'; 31 | 32 | var params = ['install']; 33 | 34 | // Maybe there was name of package user wants to install passed as a parameter. 35 | if (argv._.length > 0) { 36 | params.push(argv._[0]); 37 | params.push('--save'); 38 | } 39 | 40 | var installCommand = null; 41 | 42 | if (process.platform === 'win32') { 43 | installCommand = 'npm.cmd'; 44 | } else { 45 | installCommand = 'npm'; 46 | } 47 | 48 | childProcess.spawn(installCommand, params, { 49 | cwd: __dirname + '/../app', 50 | env: process.env, 51 | stdio: 'inherit' 52 | }); 53 | -------------------------------------------------------------------------------- /tasks/build.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var gulp = require("gulp"); 4 | var jetpack = require("fs-jetpack"); 5 | var projectDir = jetpack; 6 | var destDir = projectDir.cwd("./build"); 7 | var sass = require('gulp-sass'); 8 | var vueify = require('gulp-vueify'); 9 | 10 | var paths = { 11 | copyFromAppDir: ["./**", "!app/views/**/*.vue", "!*.scss"] 12 | }; 13 | 14 | gulp.task('clean', function() { 15 | return destDir.dirAsync(".", { empty: true }); 16 | }); 17 | 18 | function copyTask() { 19 | return projectDir.copyAsync("app", destDir.path(), { 20 | overwrite: true, 21 | matching: paths.copyFromAppDir 22 | }); 23 | } 24 | 25 | gulp.task('sass', gulp.series('clean', () => gulp.src('app/**/*.scss') 26 | .pipe(sass.sync().on('error', sass.logError)) 27 | .pipe(gulp.dest(destDir.path())))); 28 | 29 | gulp.task('sass:watch', function () { 30 | gulp.watch('./css/**/*.scss', ['sass']); 31 | }); 32 | 33 | gulp.task('copy', copyTask); 34 | gulp.task('copy-watch', copyTask); 35 | 36 | gulp.task('vueify', 37 | gulp.series('copy', 38 | 39 | function () { return gulp.src("app/views/**/*.vue") 40 | .pipe(vueify()) 41 | .on('error', console.error.bind(console)) 42 | .pipe(gulp.dest(destDir.path() + '/views/')) 43 | } 44 | 45 | ) 46 | ); 47 | 48 | 49 | gulp.task("watch", function() { 50 | gulp.watch(paths.copyFromAppDir, { cwd: "app" }, ["copy-watch"]); 51 | gulp.watch('app/**/*.scss', ["sass:watch"]); 52 | }); 53 | 54 | gulp.task("build", gulp.series('sass', 'vueify')); 55 | -------------------------------------------------------------------------------- /tasks/release.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | var utils = require('./utils'); 5 | 6 | var releaseForOs = { 7 | osx: require('./release_osx'), 8 | linux: require('./release_linux'), 9 | windows: require('./release_windows_old'), 10 | }; 11 | 12 | gulp.task('release', gulp.series('build', function () { 13 | console.log('Return release for OS %s', utils.os()) 14 | return releaseForOs[utils.os()](); 15 | })); 16 | -------------------------------------------------------------------------------- /tasks/release_linux.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulpUtil = require('gulp-util'); 5 | var childProcess = require('child_process'); 6 | var jetpack = require('fs-jetpack'); 7 | var asar = require('asar'); 8 | var utils = require('./utils'); 9 | var fs = require('fs'); 10 | var devuan = require('devuan'); 11 | 12 | var projectDir; 13 | var releasesDir; 14 | var packName; 15 | var packDir; 16 | var tmpDir; 17 | var readyAppDir; 18 | var manifest; 19 | 20 | var init = function () { 21 | projectDir = jetpack; 22 | tmpDir = projectDir.dir('./tmp', { empty: true }); 23 | releasesDir = projectDir.dir('./releases'); 24 | manifest = projectDir.read('app/package.json', 'json'); 25 | packName = manifest.name + '_' + manifest.version; 26 | packDir = tmpDir.dir(packName); 27 | readyAppDir = packDir.cwd('opt', manifest.name); 28 | 29 | return Q(); 30 | }; 31 | 32 | var copyRuntime = function () { 33 | return projectDir.copy( 34 | 'node_modules/electron/dist', 35 | readyAppDir.path(), 36 | { overwrite: true } 37 | ); 38 | }; 39 | 40 | var packageBuiltApp = function () { 41 | var deferred = Q.defer(); 42 | 43 | asar.createPackage( 44 | projectDir.path('build'), 45 | readyAppDir.path('resources/app.asar'), 46 | function() { 47 | deferred.resolve(); 48 | } 49 | ); 50 | 51 | return deferred.promise; 52 | }; 53 | 54 | var finalize = function () { 55 | // Create .desktop file from the template 56 | var desktop = projectDir.read('resources/linux/app.desktop'); 57 | desktop = utils.replace(desktop, { 58 | name: manifest.name, 59 | productName: manifest.productName, 60 | description: manifest.description, 61 | version: manifest.version, 62 | author: manifest.author 63 | }); 64 | packDir.write( 65 | 'usr/share/applications/' + manifest.name + '.desktop', desktop 66 | ); 67 | 68 | // Copy icon 69 | projectDir.copy('resources/icon.png', readyAppDir.path('icon.png')); 70 | 71 | return Q(); 72 | }; 73 | 74 | var renameApp = function() { 75 | return readyAppDir.rename('electron', manifest.name); 76 | }; 77 | 78 | var packToDebFile = function () { 79 | var deferred = Q.defer(); 80 | 81 | var arch = devuan.ARCH; 82 | var debFileName = 'xcore-gui.' + arch + '.deb'; 83 | var debPath = releasesDir.path(debFileName); 84 | 85 | gulpUtil.log('Creating DEB package...'); 86 | 87 | // Counting size of the app in KiB 88 | var appSize = Math.round(readyAppDir.inspectTree('.').size / 1000); 89 | 90 | // Preparing debian control file 91 | var control = projectDir.read('resources/linux/DEBIAN/control'); 92 | control = utils.replace(control, { 93 | name: manifest.name, 94 | description: manifest.description, 95 | version: manifest.version, 96 | author: manifest.author, 97 | arch: arch, 98 | size: appSize 99 | }); 100 | packDir.write('DEBIAN/control', control); 101 | projectDir.copy( 102 | 'resources/linux/DEBIAN/postinst', 103 | packDir.path('DEBIAN/postinst') 104 | ); 105 | fs.chmodSync(packDir.path('DEBIAN/postinst'), '755'); 106 | 107 | // Build the package... 108 | childProcess.exec( 109 | 'fakeroot dpkg-deb -Zxz --build ' + packDir.path() + ' ' + debPath, 110 | function (error, stdout, stderr) { 111 | if (error || stderr) { 112 | console.log('ERROR while building DEB package:'); 113 | console.log(error); 114 | console.log(stderr); 115 | } else { 116 | gulpUtil.log('DEB package ready!', debPath); 117 | } 118 | deferred.resolve(); 119 | }); 120 | 121 | return deferred.promise; 122 | }; 123 | 124 | var cleanClutter = function () { 125 | return tmpDir.removeAsync('.'); 126 | }; 127 | 128 | module.exports = function () { 129 | return init() 130 | .then(copyRuntime) 131 | .then(packageBuiltApp) 132 | .then(finalize) 133 | .then(renameApp) 134 | .then(packToDebFile) 135 | .then(cleanClutter).catch(err => { console.log(err); }); 136 | }; 137 | -------------------------------------------------------------------------------- /tasks/release_osx.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var gulpUtil = require('gulp-util'); 5 | var jetpack = require('fs-jetpack'); 6 | var asar = require('asar'); 7 | var utils = require('./utils'); 8 | 9 | var projectDir; 10 | var releasesDir; 11 | var tmpDir; 12 | var finalAppDir; 13 | var manifest; 14 | 15 | var init = function () { 16 | projectDir = jetpack; 17 | tmpDir = projectDir.dir('./tmp', { empty: true }); 18 | releasesDir = projectDir.dir('./releases'); 19 | manifest = projectDir.read('app/package.json', 'json'); 20 | finalAppDir = tmpDir.cwd(manifest.productName + '.app'); 21 | return Q(); 22 | }; 23 | 24 | var copyRuntime = function () { 25 | return projectDir.copy('node_modules/electron/dist/Electron.app', 26 | finalAppDir.path()); 27 | }; 28 | 29 | var cleanupRuntime = function() { 30 | finalAppDir.remove('Contents/Resources/default_app'); 31 | finalAppDir.remove('Contents/Resources/atom.icns'); 32 | return Q(); 33 | } 34 | 35 | var packageBuiltApp = function () { 36 | var deferred = Q.defer(); 37 | 38 | asar.createPackage( 39 | projectDir.path('build'), 40 | finalAppDir.path('Contents/Resources/app.asar'), 41 | function() { 42 | deferred.resolve(); 43 | } 44 | ); 45 | 46 | return deferred.promise; 47 | }; 48 | 49 | var finalize = function () { 50 | // Prepare main Info.plist 51 | var info = projectDir.read('resources/osx/Info.plist'); 52 | info = utils.replace(info, { 53 | productName: manifest.productName, 54 | identifier: manifest.identifier, 55 | version: manifest.version 56 | }); 57 | finalAppDir.write('Contents/Info.plist', info); 58 | 59 | // Prepare Info.plist of Helper apps 60 | [' EH', ' NP', ''].forEach(function (helper_suffix) { 61 | info = projectDir.read( 62 | 'resources/osx/helper_apps/Info' + helper_suffix + '.plist' 63 | ); 64 | info = utils.replace(info, { 65 | productName: manifest.productName, 66 | identifier: manifest.identifier 67 | }); 68 | finalAppDir.write( 69 | 'Contents/Frameworks/Electron Helper' + helper_suffix + 70 | '.app/Contents/Info.plist', 71 | info 72 | ); 73 | }); 74 | 75 | // Copy icon 76 | projectDir.copy('resources/osx/icon.icns', 77 | finalAppDir.path('Contents/Resources/icon.icns')); 78 | 79 | return Q(); 80 | }; 81 | 82 | var renameApp = function() { 83 | 84 | // Rename application 85 | finalAppDir.rename('Contents/MacOS/Electron', manifest.productName); 86 | return Q(); 87 | } 88 | 89 | var packToDmgFile = function () { 90 | var deferred = Q.defer(); 91 | var appdmg = require('appdmg'); 92 | var dmgName = 'xcore-gui.osx64.dmg'; 93 | 94 | // Prepare appdmg config 95 | var dmgManifest = projectDir.read('resources/osx/appdmg.json'); 96 | dmgManifest = utils.replace(dmgManifest, { 97 | productName: manifest.productName, 98 | appPath: finalAppDir.path(), 99 | dmgIcon: projectDir.path("resources/osx/dmg-icon.icns"), 100 | dmgBackground: projectDir.path("resources/osx/dmg-background.png") 101 | }); 102 | tmpDir.write('appdmg.json', dmgManifest); 103 | 104 | // Delete DMG file with this name if already exists 105 | releasesDir.remove(dmgName); 106 | gulpUtil.log('Packaging to DMG file...'); 107 | 108 | var readyDmgPath = releasesDir.path(dmgName); 109 | 110 | appdmg({ 111 | source: tmpDir.path('appdmg.json'), 112 | target: readyDmgPath 113 | }) 114 | .on('error', function (err) { 115 | console.error(err); 116 | }) 117 | .on('finish', function () { 118 | gulpUtil.log('DMG file ready!', readyDmgPath); 119 | deferred.resolve(); 120 | }); 121 | 122 | return deferred.promise; 123 | }; 124 | 125 | var cleanClutter = function () { 126 | return tmpDir.remove('.'); 127 | }; 128 | 129 | module.exports = function () { 130 | return init() 131 | .then(copyRuntime) 132 | .then(cleanupRuntime) 133 | .then(packageBuiltApp) 134 | .then(finalize) 135 | .then(renameApp) 136 | .then(packToDmgFile) 137 | .then(cleanClutter).catch(err => { console.log(err); }); 138 | }; 139 | -------------------------------------------------------------------------------- /tasks/release_windows.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const packager = require('electron-packager') 4 | const installer = require('electron-installer-windows'); 5 | const appInfo = require('../app/package.json') 6 | 7 | const options = { 8 | src: 'dist/X Core-win32-x64', 9 | dest: 'releases/win32/installers' 10 | } 11 | 12 | function create_package() { 13 | return packager({ 14 | name: appInfo.productName, 15 | appBundleId: appInfo.identifier, 16 | appVersion: appInfo.version, 17 | dir: './app', 18 | platform: 'win32', 19 | arch: 'x64', 20 | out: 'dist/', 21 | icon: './resources/windows/icon.ico', 22 | overwrite: true 23 | }) 24 | } 25 | 26 | function create_installer(options) { 27 | return new Promise(async (resolve, reject) => { 28 | try { 29 | await installer(options); 30 | resolve(); 31 | } catch (err) { 32 | reject(err); 33 | } 34 | }); 35 | 36 | 37 | } 38 | 39 | ; 40 | 41 | module.exports = function () { 42 | console.log('Creating package...'); 43 | return create_package().then(() => { 44 | console.log('Package created. Creating installer...'); 45 | create_installer(options).then(() => { 46 | console.log('Installer finished'); 47 | }).catch(err => { 48 | console.log('Error', err); 49 | }); 50 | }); 51 | }; -------------------------------------------------------------------------------- /tasks/release_windows_old.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Q = require('q'); 4 | var os = require('os'); 5 | var gulpUtil = require('gulp-util'); 6 | var childProcess = require('child_process'); 7 | var jetpack = require('fs-jetpack'); 8 | var asar = require('asar'); 9 | var utils = require('./utils'); 10 | 11 | var projectDir; 12 | var tmpDir; 13 | var releasesDir; 14 | var readyAppDir; 15 | var manifest; 16 | 17 | var init = function () { 18 | projectDir = jetpack; 19 | tmpDir = projectDir.dir('./tmp', { empty: true }); 20 | releasesDir = projectDir.dir('./releases'); 21 | manifest = projectDir.read('app/package.json', 'json'); 22 | readyAppDir = tmpDir.cwd(manifest.name); 23 | return Q(); 24 | }; 25 | 26 | var copyRuntime = function () { 27 | return projectDir.copyAsync( 28 | 'node_modules/electron/dist', 29 | readyAppDir.path(), 30 | { overwrite: true } 31 | ); 32 | }; 33 | 34 | var cleanupRuntime = function () { 35 | return readyAppDir.removeAsync('resources/default_app'); 36 | }; 37 | 38 | var packageApp = function () { 39 | return projectDir.copyAsync( 40 | 'build', 41 | readyAppDir.path('resources/app'), 42 | { overwrite: true } 43 | ); 44 | }; 45 | 46 | var packageBuiltApp = function () { 47 | var deferred = Q.defer(); 48 | 49 | asar.createPackage( 50 | projectDir.path('build'), 51 | readyAppDir.path('resources/app.asar'), 52 | function() { 53 | deferred.resolve(); 54 | } 55 | ); 56 | 57 | return deferred.promise; 58 | }; 59 | 60 | var finalize = function () { 61 | var deferred = Q.defer(); 62 | projectDir.copy('resources/windows/icon.ico', readyAppDir.path('icon.ico')); 63 | 64 | // Replace Electron icon for your own. 65 | var rcedit = require('rcedit'); 66 | 67 | rcedit(readyAppDir.path('electron.exe'), { 68 | icon: projectDir.path('resources/windows/icon.ico'), 69 | 'version-string': { 70 | ProductName: manifest.productName, 71 | FileDescription: manifest.description, 72 | } 73 | }, function (err) { 74 | if (!err) { 75 | deferred.resolve(); 76 | } 77 | }); 78 | 79 | return deferred.promise; 80 | }; 81 | 82 | var renameApp = function () { 83 | return readyAppDir.renameAsync('electron.exe', manifest.productName + '.exe'); 84 | }; 85 | 86 | var createInstaller = function () { 87 | var deferred = Q.defer(); 88 | var finalPackageName = 'xcore-gui.exe'; 89 | var installScript = projectDir.read('resources/windows/installer.nsi'); 90 | installScript = utils.replace(installScript, { 91 | name: manifest.name, 92 | productName: manifest.productName, 93 | publisher: manifest.publisher, 94 | version: manifest.version, 95 | src: readyAppDir.path(), 96 | dest: releasesDir.path(finalPackageName), 97 | icon: readyAppDir.path('icon.ico'), 98 | setupIcon: projectDir.path('resources/windows/setup-icon.ico'), 99 | banner: projectDir.path('resources/windows/setup-banner.bmp'), 100 | is32bit: process.arch !== 'x64' || 101 | process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') 102 | }); 103 | tmpDir.write('installer.nsi', installScript); 104 | gulpUtil.log('Building installer with NSIS...'); 105 | 106 | // Remove destination file if already exists. 107 | releasesDir.remove(finalPackageName); 108 | 109 | // Note: NSIS have to be added to PATH (environment variables). 110 | var nsis = childProcess.spawn('makensis', [ 111 | tmpDir.path('installer.nsi') 112 | ], { 113 | stdio: 'inherit' 114 | }); 115 | nsis.on('error', function (err) { 116 | if (err.message === 'spawn makensis ENOENT') { 117 | throw new Error( 118 | 'Can\'t find NSIS. Have installed it and added to PATH?' 119 | ); 120 | } else { 121 | throw err; 122 | } 123 | }); 124 | nsis.on('close', function () { 125 | gulpUtil.log('Installer ready!', releasesDir.path(finalPackageName)); 126 | deferred.resolve(); 127 | }); 128 | 129 | return deferred.promise; 130 | }; 131 | 132 | var signInstaller = function () { 133 | if (process.env.Cert_File) { 134 | childProcess.execSync( 135 | 'signtool.exe sign ' 136 | + '/fd sha256 ' 137 | + '/td sha256 ' 138 | + '/tr http://timestamp.digicert.com ' 139 | + '/f "%CERT_FILE%" ' 140 | + '/p "%CERT_PASSWORD%" ' 141 | + '"' + releasesDir.path('*.exe') + '"', 142 | (err, stdout, stderr) => { 143 | if (error) { 144 | throw err; 145 | } 146 | } 147 | ); 148 | } 149 | }; 150 | 151 | var cleanClutter = function () { 152 | return tmpDir.removeAsync('.'); 153 | }; 154 | 155 | module.exports = function () { 156 | return init() 157 | .then(copyRuntime) 158 | .then(cleanupRuntime) 159 | .then(packageApp) 160 | .then(finalize) 161 | .then(renameApp) 162 | .then(createInstaller) 163 | .then(signInstaller) 164 | .then(cleanClutter).catch(err => { console.log(err); }); 165 | }; 166 | -------------------------------------------------------------------------------- /tasks/start.js: -------------------------------------------------------------------------------- 1 | /* global process */ 2 | 'use strict'; 3 | 4 | var Q = require('q'); 5 | var electron = require('electron'); 6 | var pathUtil = require('path'); 7 | var childProcess = require('child_process'); 8 | var utils = require('./utils'); 9 | var gulpPath = pathUtil.resolve('./node_modules/.bin/gulp'); 10 | 11 | process.on('unhandledRejection', (reason, p) => { 12 | console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); 13 | // application specific logging, throwing an error, or other logic here 14 | }); 15 | 16 | 17 | if (process.platform === 'win32') { 18 | gulpPath += '.cmd'; 19 | } 20 | 21 | var runBuild = function () { 22 | var deferred = Q.defer(); 23 | var build = childProcess.spawn(gulpPath, [ 24 | 'build', 25 | '--color' 26 | ], { 27 | stdio: 'inherit' 28 | }); 29 | 30 | build.on('close', function() { 31 | deferred.resolve(); 32 | }); 33 | 34 | return deferred.promise; 35 | }; 36 | 37 | var runApp = function () { 38 | var args = []; 39 | var isTestNet = process.env.NODE_ENV !== 'production'; 40 | 41 | if (isTestNet) { 42 | args.push('--debug=5858'); 43 | } 44 | 45 | args.push('./build'); 46 | childProcess.spawn(electron, args, 47 | { 48 | stdio: 'inherit', 49 | env: Object.assign({ isTestNet: isTestNet }, process.env) 50 | } 51 | ).on( 52 | 'close', 53 | function () { 54 | process.exit(); 55 | } 56 | ); 57 | }; 58 | 59 | runBuild().then(function () { 60 | runApp(); 61 | }).catch(err => { console.log(err); }); 62 | -------------------------------------------------------------------------------- /tasks/utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var argv = require('yargs').argv; 4 | var os = require('os'); 5 | var jetpack = require('fs-jetpack'); 6 | 7 | module.exports.os = function () { 8 | switch (os.platform()) { 9 | case 'darwin': 10 | return 'osx'; 11 | case 'linux': 12 | return 'linux'; 13 | case 'win32': 14 | return 'windows'; 15 | default: 16 | return 'unsupported'; 17 | } 18 | }; 19 | 20 | module.exports.replace = function (str, patterns) { 21 | Object.keys(patterns).forEach(function (pattern) { 22 | var matcher = new RegExp('{{' + pattern + '}}', 'g'); 23 | str = str.replace(matcher, patterns[pattern]); 24 | }); 25 | return str; 26 | }; 27 | 28 | module.exports.getElectronVersion = function () { 29 | var manifest = jetpack.read(__dirname + '/../package.json', 'json'); 30 | return manifest.devDependencies['electron'].substring(1); 31 | }; 32 | --------------------------------------------------------------------------------