├── .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 | [](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 |
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 |
About X Core
30 |
31 |
32 |
35 |
36 |
37 | Close
38 |
39 |
40 | `
41 | };
42 |
--------------------------------------------------------------------------------
/app/views/components/confirmationModal/ConfirmationModal.vue:
--------------------------------------------------------------------------------
1 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
80 |
81 |
82 |
83 | This will delete your node's folder and all its files. Are you sure?
84 |
85 |
86 |
87 |
97 |
98 |
99 |
100 |
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 |
17 |
18 | KB
19 | MB
20 | GB
21 | TB
22 |
23 |
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 |
72 |
73 |
74 |
75 |
76 |
80 |
81 |
82 |
83 | {{this.$attrs['message']}}
84 |
85 |
86 |
87 |
97 |
98 |
99 |
100 |
101 |
102 |
122 |
--------------------------------------------------------------------------------
/app/views/components/modal/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | props: ['show', 'id'],
5 | template: `
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
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 |
13 | ×
14 |
15 | {{note.name}}: {{note.message}}
16 |
17 |
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 |
14 | `
15 | };
16 |
--------------------------------------------------------------------------------
/app/views/components/updateModal/UpdateModal.vue:
--------------------------------------------------------------------------------
1 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
80 |
81 |
82 |
83 | A new version of X Core is available. Would you like to download it now?
84 |
85 |
86 |
87 |
97 |
98 |
99 |
100 |
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 | CONFIRM
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 | NodeID
76 | Status
77 |
78 | Location
79 | Uptime
80 | Restarts
81 | Peers
82 | Bridges
83 | Allocs
84 | Shared
85 | Delta
86 | Port
87 |
88 |
89 |
90 |
91 |
92 | Start
93 | Stop
94 | Restart
95 |
96 | Delete
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
109 |
110 |
111 | {{index}}
112 |
113 | {{share.id}}
114 |
115 |
116 | ON
117 | OFF
118 | Loading
119 |
120 |
121 |
122 |
123 | {{share.config.storagePath}}
124 | {{share.meta.uptimeReadable}}
125 | {{share.meta.numRestarts}}
126 | {{share.meta.farmerState.totalPeers}}
127 |
128 | Disconnected
129 | Connecting
130 | Confirming
131 | Connected
132 |
133 | {{share.meta.farmerState.contractCount}} ({{share.meta.farmerState.dataReceivedCount}} received)
134 | {{share.meta.farmerState.spaceUsed}} ({{share.meta.farmerState.percentUsed}}%)
135 |
136 | {{share.meta.farmerState.ntpStatus.delta}}
137 |
138 |
139 | {{share.meta.farmerState.ntpStatus.delta}}
140 |
141 | {{share.meta.farmerState.portStatus.listenPort}} {{share.meta.farmerState.portStatus.connectionType}}
142 |
143 |
144 |
145 |
146 |
147 | Start
148 | Stop
149 | Restart
150 | Logs
151 | Edit
152 |
153 | Delete
154 |
155 |
156 |
157 |
158 |
159 | Add Drive to Get Started
160 |
161 |
162 |
163 |
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 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | + Add Drive
32 |
33 |
34 |
35 |
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 |
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 |
54 |
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 | Port Number
84 |
89 |
90 | Reachable
91 |
92 | Random
93 | {{invalidPort.port === newShare.config.rpcPort ? 'Continue Anyways' : continueButtonText}}
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 | Next
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 |
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 |
Storage Sharing Terms
30 |
31 |
32 |
35 |
36 |
37 | I Accept
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 |
Update X Core
61 | No Update Available
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 |
Close
76 |
77 |
78 | Yes
79 |
80 |
81 | No
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 |
15 | `
16 | }
17 |
--------------------------------------------------------------------------------
/app/views/xcore/dashboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Network Data
4 |
26 |
Current Earnings
27 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/views/xcore/settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Node Information
4 |
5 |
8 | Are you sure ?
9 |
10 |
11 |
14 | New version available.
15 |
16 |
17 |
67 |
73 |
74 |
80 |
81 |
87 |
88 |
121 |
122 | Delete node
123 |
124 |
125 |
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 |
--------------------------------------------------------------------------------