├── .babelrc
├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── files
└── GeoLite2-Country.mmdb
├── package.json
├── postcss.config.js
├── public
├── fonts
│ ├── Lato-Black.ttf
│ ├── Lato-BlackItalic.ttf
│ ├── Lato-Bold.ttf
│ ├── Lato-BoldItalic.ttf
│ ├── Lato-Hairline.ttf
│ ├── Lato-HairlineItalic.ttf
│ ├── Lato-Italic.ttf
│ ├── Lato-Light.ttf
│ ├── Lato-LightItalic.ttf
│ └── Lato-Regular.ttf
├── icons
│ └── icon.ico
├── index.html
└── styles
│ ├── Elements.postcss
│ ├── Footer.postcss
│ ├── Icons.postcss
│ ├── Main.postcss
│ ├── Results.postcss
│ └── Update.postcss
├── src
├── actions
│ ├── MainActions.js
│ ├── ResultsActions.js
│ └── UpdateActions.js
├── components
│ ├── Footer.jsx
│ ├── ResultsCountryItem.jsx
│ └── Root.jsx
├── constants
│ └── ActionTypes.js
├── containers
│ ├── Main.jsx
│ ├── Results.jsx
│ └── Update.jsx
├── core
│ ├── country.js
│ └── updater.js
├── index.js
├── misc
│ ├── other.js
│ └── text.js
├── renderer.dev.js
├── renderer.js
└── store
│ ├── index.js
│ └── reducers
│ ├── index.js
│ ├── results.js
│ └── update.js
├── webpack.config.base.js
├── webpack.config.main.babel.js
└── webpack.config.renderer.babel.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/env", "@babel/react"],
3 | "plugins": ["@babel/plugin-proposal-class-properties", "@babel/plugin-transform-runtime", ["@babel/plugin-proposal-object-rest-spread", { "useBuiltIns": false }]],
4 | "env": {
5 | "development": {
6 | "plugins": ["react-hot-loader/babel"]
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | public/styles/* linguist-vendored
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | /public/*.js
4 | /.vscode
5 | dist
6 | package-lock.json
7 | tests
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 assnctr
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | ## Features
8 | - Remove duplicates
9 | - Sort your proxy list by countries
10 | - Simple export in `ip` : `port`
11 | - Automatically checking for updates
12 |
13 | ## Updates
14 | Automatically checking for updates and notification if the latest version is available.
15 |
16 | #### Open Proxy Space
17 | [Premium](https://openproxy.space/premium) - Buy Proxy List
18 | [Free Proxy List](https://openproxy.space/list) - Always Updated Proxy Lists
19 |
--------------------------------------------------------------------------------
/files/GeoLite2-Country.mmdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/files/GeoLite2-Country.mmdb
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "unfx-proxy-to-country",
3 | "version": "1.0.0",
4 | "main": "public/main.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "build": "run-p build:*",
8 | "build:main": "cross-env NODE_ENV=production webpack -p --config webpack.config.main.babel.js",
9 | "build:renderer": "cross-env NODE_ENV=production webpack -p --config webpack.config.renderer.babel.js",
10 | "start": "run-p start:*",
11 | "start:main": "electron --require @babel/register src/index",
12 | "start:renderer": "cross-env NODE_ENV=development webpack-dev-server -d --config webpack.config.renderer.babel.js",
13 | "package": "npm run build && electron-builder --win",
14 | "publish": "npm run build && electron-builder --linux --win --publish always"
15 | },
16 | "devDependencies": {
17 | "@babel/cli": "^7.1.5",
18 | "@babel/core": "^7.1.6",
19 | "@babel/plugin-proposal-class-properties": "^7.1.0",
20 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
21 | "@babel/plugin-transform-runtime": "^7.1.0",
22 | "@babel/preset-env": "^7.1.6",
23 | "@babel/preset-react": "^7.0.0",
24 | "@babel/register": "^7.0.0",
25 | "@babel/runtime": "^7.1.5",
26 | "babel-loader": "^8.0.4",
27 | "cross-env": "^5.2.0",
28 | "css-loader": "^1.0.1",
29 | "electron": "^4.0.0",
30 | "electron-builder": "^20.36.2",
31 | "electron-devtools-installer": "^2.2.4",
32 | "electron-react-devtools": "^0.5.3",
33 | "file-loader": "^2.0.0",
34 | "js-flock": "^3.5.3",
35 | "mmdb-reader": "*",
36 | "npm-run-all": "^4.1.3",
37 | "postcss-color-mod-function": "^2.4.3",
38 | "postcss-loader": "^3.0.0",
39 | "postcss-preset-env": "^5.3.0",
40 | "react": "^16.6.3",
41 | "react-dom": "^16.6.3",
42 | "react-hot-loader": "^4.3.12",
43 | "react-markdown": "^4.0.3",
44 | "react-redux": "^5.1.1",
45 | "redux": "^4.0.1",
46 | "redux-thunk": "^2.3.0",
47 | "request-progress": "^3.0.0",
48 | "request-promise": "^4.2.2",
49 | "style-loader": "^0.23.1",
50 | "url-loader": "^1.1.2",
51 | "webpack": "^4.25.1",
52 | "webpack-cli": "^3.1.2",
53 | "webpack-dev-server": "^3.1.10"
54 | },
55 | "build": {
56 | "appId": "com.github.assnctr.unfxproxytocountry",
57 | "win": {
58 | "target": [
59 | {
60 | "target": "zip",
61 | "arch": [
62 | "x64",
63 | "ia32"
64 | ]
65 | },
66 | {
67 | "target": "nsis-web",
68 | "arch": [
69 | "x64",
70 | "ia32"
71 | ]
72 | },
73 | {
74 | "target": "portable",
75 | "arch": [
76 | "x64",
77 | "ia32"
78 | ]
79 | }
80 | ],
81 | "icon": "/public/icons/icon.ico",
82 | "artifactName": "${name}-v${version}-${arch}-${os}.${ext}"
83 | },
84 | "linux": {
85 | "target": [
86 | {
87 | "target": "zip",
88 | "arch": [
89 | "x64",
90 | "ia32",
91 | "arm64",
92 | "armv7l"
93 | ]
94 | }
95 | ],
96 | "icon": "/public/icons/icon.ico",
97 | "artifactName": "${name}-v${version}-${arch}-${os}.${ext}"
98 | },
99 | "publish": [
100 | {
101 | "provider": "github",
102 | "owner": "assnctr",
103 | "repo": "unfx-proxy-to-country",
104 | "private": false
105 | }
106 | ],
107 | "productName": "Unfx Proxy to Country",
108 | "copyright": "2019 assnctr (openproxy.space)",
109 | "extraResources": [
110 | "./files/**"
111 | ],
112 | "portable": {
113 | "artifactName": "${name}-v${version}-${arch}-${os}-portable.${ext}"
114 | },
115 | "nsisWeb": {
116 | "oneClick": false,
117 | "perMachine": true,
118 | "allowToChangeInstallationDirectory": true,
119 | "differentialPackage": true
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | 'postcss-preset-env': {
4 | stage: 4,
5 | features: {
6 | 'nesting-rules': true
7 | }
8 | },
9 | 'postcss-color-mod-function': {}
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/public/fonts/Lato-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Black.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-BlackItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Bold.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-BoldItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Hairline.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Hairline.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-HairlineItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-HairlineItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Italic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Light.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-LightItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/fonts/Lato-Regular.ttf
--------------------------------------------------------------------------------
/public/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openproxyspace/unfx-proxy-to-country/716ef4bc161a23cfce09ecab57db9c0d64cc795f/public/icons/icon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Unfx Proxy to Country
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/public/styles/Elements.postcss:
--------------------------------------------------------------------------------
1 | :root {
2 | --green-color: #31bc86;
3 | --grey-color: #8c99a7;
4 | --blue-color: #5396d8;
5 | }
6 |
7 | ::-webkit-scrollbar {
8 | width: 15px;
9 | border-radius: 0.25em;
10 | }
11 |
12 | ::-webkit-scrollbar-thumb {
13 | background-color: color-mod(var(--blue-color) alpha(15%));
14 | border-radius: 0.25em;
15 | }
16 |
17 | ::-webkit-scrollbar-track {
18 | background-color: #fafafa;
19 | }
20 |
21 | /* --------------------------------
22 | Buttons section
23 | -------------------------------- */
24 |
25 | button {
26 | font-weight: 900;
27 | font-size: 1em;
28 | border-radius: 1.75em;
29 | outline: 0;
30 | border: 0;
31 | cursor: pointer;
32 | padding: 0.75em 1.75em;
33 | margin-right: 2em;
34 | background-color: color-mod(var(--green-color) alpha(15%));
35 | color: color-mod(var(--green-color) alpha(75%));
36 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
37 | box-shadow: 0 0.25em 1em color-mod(var(--green-color) alpha(5%));
38 |
39 | &:hover {
40 | color: rgba(255, 255, 255, 0.75);
41 | background-color: rgba(49, 188, 135, 0.65);
42 | transform: translateY(-5%);
43 | }
44 |
45 | &:active {
46 | transform: translateY(7.5%);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/public/styles/Footer.postcss:
--------------------------------------------------------------------------------
1 | :root {
2 | --green-color: #31bc86;
3 | --grey-color: #8c99a7;
4 | --blue-color: #5396d8;
5 | }
6 |
7 | .footer {
8 | background-color: #fff;
9 | padding: 2em;
10 | border-radius: 0.5em;
11 | box-shadow: 0 0.25em 1em color-mod(var(--green-color) alpha(5%));
12 | display: flex;
13 |
14 | & .marks {
15 | display: flex;
16 | align-items: center;
17 | width: 60%;
18 |
19 | & .product-name {
20 | font-weight: 700;
21 | color: color-mod(var(--blue-color) alpha(50%));
22 | }
23 |
24 | & .version {
25 | color: color-mod(var(--blue-color) alpha(50%));
26 | }
27 |
28 | & a {
29 | font-weight: 900;
30 | margin-right: 1em;
31 | transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
32 |
33 | &:hover {
34 | text-decoration: underline;
35 | color: color-mod(var(--green-color) alpha(75%));
36 | }
37 |
38 | &.copyright {
39 | color: var(--grey-color);
40 | font-weight: 400;
41 | text-decoration: none;
42 |
43 | &:hover {
44 | color: #0088cc;
45 |
46 | & svg {
47 | fill: #0088cc;
48 | }
49 | }
50 |
51 | & svg {
52 | width: 1em;
53 | height: 1em;
54 | fill: #8c99a7;
55 | transition: all 0.7s cubic-bezier(0.23, 1, 0.32, 1);
56 | }
57 | }
58 | }
59 |
60 | & span,
61 | & a {
62 | color: color-mod(var(--green-color) alpha(50%));
63 | }
64 |
65 | & * {
66 | display: flex;
67 | align-content: center;
68 | align-items: center;
69 | margin-right: 0.5em;
70 | }
71 | }
72 |
73 | & .socials {
74 | display: flex;
75 | align-items: center;
76 | align-content: center;
77 | justify-content: flex-end;
78 | width: 40%;
79 |
80 | & a {
81 | display: flex;
82 | align-items: center;
83 | align-content: center;
84 |
85 | &:not(:last-child) {
86 | margin-right: 2em;
87 | }
88 |
89 | &.become-a-patron {
90 | cursor: pointer;
91 | font-weight: 700;
92 | user-select: none;
93 | text-decoration: none;
94 | color: #8c99a7;
95 |
96 | & svg {
97 | width: 1.2em;
98 | height: 1.2em;
99 | margin-left: 0.5em;
100 | }
101 |
102 | & span {
103 | margin: 0 0.12em;
104 | transition: all 0.7s cubic-bezier(0.23, 1, 0.32, 1);
105 | }
106 |
107 | &:hover svg {
108 | & circle {
109 | fill: #f96854;
110 | }
111 |
112 | & rect {
113 | fill: #052d49;
114 | }
115 | }
116 | }
117 |
118 | &.donate {
119 | width: 1.2em;
120 | height: 1.2em;
121 |
122 | &:hover {
123 | & path:nth-child(1) {
124 | fill: #ff3f2e;
125 | }
126 |
127 | & path:nth-child(2) {
128 | fill: #f2d1a5;
129 | }
130 |
131 | & path:nth-child(3) {
132 | fill: #1ea1e3;
133 | }
134 |
135 | & path:nth-child(4),
136 | & path:nth-child(5) {
137 | fill: #f2bb88;
138 | }
139 |
140 | & path:nth-child(6) {
141 | fill: #cf0404;
142 | }
143 | }
144 | }
145 | }
146 |
147 | & svg {
148 | height: 1.5em;
149 | width: 1.5em;
150 | cursor: pointer;
151 | fill: var(--grey-color);
152 |
153 | &:hover.github-svg path {
154 | fill: #333;
155 | }
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/public/styles/Main.postcss:
--------------------------------------------------------------------------------
1 | :root {
2 | --green-color: #31bc86;
3 | --grey-color: #8c99a7;
4 | --blue-color: #5396d8;
5 | }
6 |
7 | @font-face {
8 | font-family: 'Lato';
9 | font-style: normal;
10 | font-weight: 300;
11 | font-display: auto;
12 | src: local('Lato Light'), local('Lato-Light'), url('../fonts/Lato-Light.ttf') format('truetype');
13 | }
14 |
15 | @font-face {
16 | font-family: 'Lato';
17 | font-style: normal;
18 | font-weight: 400;
19 | font-display: auto;
20 | src: local('Lato Regular'), local('Lato-Regular'), url('../fonts/Lato-Regular.ttf') format('truetype');
21 | }
22 |
23 | @font-face {
24 | font-family: 'Lato';
25 | font-style: normal;
26 | font-weight: 700;
27 | font-display: auto;
28 | src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/Lato-Bold.ttf') format('truetype');
29 | }
30 |
31 | body {
32 | padding: 0;
33 | margin: 0;
34 | font-family: 'Lato';
35 | text-rendering: geometricPrecision;
36 | -webkit-font-smoothing: antialiased;
37 | -moz-font-smoothing: grayscale;
38 | font-smoothing: antialiased;
39 | }
40 |
41 | input,
42 | textarea,
43 | button {
44 | font-family: 'Lato';
45 | text-rendering: geometricPrecision;
46 | -webkit-font-smoothing: antialiased;
47 | -moz-font-smoothing: grayscale;
48 | font-smoothing: antialiased;
49 | }
50 |
51 | *,
52 | :after,
53 | :before {
54 | -webkit-box-sizing: border-box;
55 | -moz-box-sizing: border-box;
56 | box-sizing: border-box;
57 | }
58 |
59 | .main-page-container {
60 | position: fixed;
61 | display: flex;
62 | flex-flow: column wrap;
63 | align-items: center;
64 | padding: 2em;
65 | width: 100%;
66 | height: 100%;
67 | top: 0;
68 | left: 0;
69 | justify-content: center;
70 | overflow-y: auto;
71 | transition: all 0.7s cubic-bezier(0.23, 1, 0.32, 1);
72 | background-image: linear-gradient(129deg, color-mod(var(--green-color) alpha(35%)), color-mod(var(--blue-color) alpha(35%)));
73 | }
74 |
--------------------------------------------------------------------------------
/public/styles/Results.postcss:
--------------------------------------------------------------------------------
1 | :root {
2 | --green-color: #31bc86;
3 | --grey-color: #8c99a7;
4 | --blue-color: #5396d8;
5 | }
6 |
7 | .results-container {
8 | position: fixed;
9 | display: flex;
10 | flex-flow: column nowrap;
11 | align-items: center;
12 | padding: 2em;
13 | width: 100%;
14 | height: 100%;
15 | top: 0;
16 | left: 0;
17 | overflow-y: auto;
18 | background-color: #fafafa;
19 | transition: all 0.7s cubic-bezier(0.23, 1, 0.32, 1);
20 | opacity: 0;
21 | visibility: hidden;
22 |
23 | &.active {
24 | opacity: 1;
25 | visibility: visible;
26 |
27 | & .results-content {
28 | opacity: 1;
29 | transform: translateY(0%);
30 | }
31 | }
32 |
33 | & .results-content {
34 | max-width: 1200px;
35 | width: 100%;
36 | opacity: 0;
37 | transform: translateY(-20%);
38 | transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1);
39 | transition-delay: transform 0.3s;
40 |
41 | & .content-header {
42 | display: flex;
43 | flex-flow: row wrap;
44 | margin-bottom: 1em;
45 | background-color: #fff;
46 | padding: 2em;
47 | border-radius: 0.5em;
48 | box-shadow: 0 0.25em 1em color-mod(var(--green-color) alpha(5%));
49 |
50 | & .selected-counts {
51 | display: flex;
52 | align-items: center;
53 | color: #fff;
54 | font-weight: 700;
55 | user-select: none;
56 |
57 | & span {
58 | font-weight: 700;
59 | border-radius: 1.75em;
60 | margin-right: 1em;
61 | padding: 0.5em 1.25em;
62 | transition: background-color 0.7s cubic-bezier(0.23, 1, 0.32, 1);
63 |
64 | &:nth-child(odd) {
65 | background-color: color-mod(var(--green-color) alpha(75%));
66 | }
67 |
68 | &:nth-child(even) {
69 | background-color: color-mod(var(--blue-color) alpha(75%));
70 | }
71 | }
72 |
73 | &.no-items span {
74 | background-color: color-mod(var(--grey-color) alpha(65%));
75 | }
76 | }
77 |
78 | & .controls {
79 | margin-left: auto;
80 | align-items: center;
81 | display: flex;
82 |
83 | & svg {
84 | width: 1.5em;
85 | height: 1.5em;
86 | transition: fill 0.5s cubic-bezier(0.23, 1, 0.32, 1), transform 0.3s cubic-bezier(0.23, 1, 0.32, 1);
87 | fill: color-mod(var(--grey-color) alpha(35%));
88 | cursor: pointer;
89 | margin-left: 2em;
90 |
91 | &:hover {
92 | transform: translateY(-5%);
93 |
94 | &:nth-child(odd) {
95 | fill: color-mod(var(--green-color) alpha(75%));
96 | }
97 |
98 | &:nth-child(even) {
99 | fill: color-mod(var(--blue-color) alpha(75%));
100 | }
101 | }
102 |
103 | &:active {
104 | transform: translateY(7.5%);
105 | }
106 | }
107 | }
108 | }
109 |
110 | & .tip {
111 | display: flex;
112 | align-items: center;
113 | margin-top: 1em;
114 | margin-bottom: 1.25em;
115 | user-select: none;
116 | background-color: #fff;
117 | box-shadow: 0 0.25em 1em color-mod(var(--green-color) alpha(5%));
118 | padding: 2em;
119 | border-radius: 0.5em;
120 |
121 | & svg {
122 | width: 1.75em;
123 | height: 1.75em;
124 | fill: color-mod(var(--grey-color) alpha(75%));
125 | margin-right: 1em;
126 | }
127 |
128 | & span {
129 | font-size: 0.9em;
130 | color: color-mod(var(--grey-color) alpha(75%));
131 |
132 | & b {
133 | margin-right: 0.5em;
134 | }
135 | }
136 | }
137 |
138 | & .countries {
139 | display: flex;
140 | flex-flow: row wrap;
141 | justify-content: space-between;
142 | text-align: left;
143 | box-shadow: 0 0.25em 1em color-mod(var(--green-color) alpha(5%));
144 | background-color: #fff;
145 | padding: 2em 2em 1em 2em;
146 | border-radius: 0.5em;
147 |
148 | & .country-item {
149 | display: flex;
150 | user-select: none;
151 | cursor: pointer;
152 | width: 25%;
153 | margin-bottom: 1em;
154 | overflow: hidden;
155 | align-items: center;
156 | transition: color 0.5s cubic-bezier(0.23, 1, 0.32, 1);
157 | color: color-mod(var(--grey-color) alpha(50%));
158 |
159 | &.active {
160 | &:nth-child(odd) {
161 | color: color-mod(var(--green-color) alpha(75%));
162 |
163 | & .ico-wrap {
164 | border: 1px dashed color-mod(var(--green-color) alpha(65%));
165 | }
166 | }
167 |
168 | &:nth-child(even) {
169 | color: color-mod(var(--blue-color) alpha(75%));
170 |
171 | & .ico-wrap {
172 | border: 1px dashed color-mod(var(--blue-color) alpha(65%));
173 | }
174 | }
175 |
176 | & .count {
177 | color: color-mod(var(--grey-color) alpha(75%));
178 | }
179 | }
180 |
181 | & .ico-wrap {
182 | margin-right: 0.5em;
183 | border-radius: 1em;
184 | padding: 2px;
185 | border: 1px dashed transparent;
186 | transition: border-color 0.5s cubic-bezier(0.23, 1, 0.32, 1);
187 |
188 | & .ico {
189 | width: 40px;
190 | min-width: 40px;
191 | height: 26px;
192 | background-color: #fafafa;
193 | background-repeat: no-repeat;
194 | background-position: center;
195 | background-size: cover;
196 | border-radius: 1em;
197 | }
198 | }
199 |
200 | & .merge {
201 | width: calc(100% - 4em);
202 | }
203 |
204 | & .name {
205 | font-size: 0.9em;
206 | white-space: nowrap;
207 | overflow: hidden;
208 | text-overflow: ellipsis;
209 | font-weight: 700;
210 | }
211 |
212 | & .count {
213 | font-size: 0.75em;
214 | font-weight: 700;
215 | white-space: nowrap;
216 | overflow: hidden;
217 | text-overflow: ellipsis;
218 | }
219 | }
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/public/styles/Update.postcss:
--------------------------------------------------------------------------------
1 | :root {
2 | --green-color: #31bc86;
3 | --grey-color: #8c99a7;
4 | --blue-color: #5396d8;
5 | }
6 |
7 | .update-notify {
8 | position: fixed;
9 | width: 100%;
10 | height: 100%;
11 | display: flex;
12 | flex-flow: column nowrap;
13 | align-items: center;
14 | left: 0;
15 | top: 0;
16 | padding: 2em;
17 | overflow: auto;
18 | opacity: 1;
19 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
20 | background-color: #fff;
21 |
22 | &.closed {
23 | opacity: 0;
24 | visibility: hidden;
25 | }
26 |
27 | &.checking {
28 | width: 100%;
29 | height: 100%;
30 | left: 0;
31 | top: 0;
32 |
33 | & .lds-ripple {
34 | opacity: 1;
35 | }
36 | }
37 |
38 | &.downloading {
39 | width: 400px;
40 | height: 100px;
41 | left: calc(50% - 200px);
42 | top: calc(50% - 50px);
43 | overflow: hidden;
44 | border-radius: 0.75em;
45 |
46 | & .update-container {
47 | opacity: 0;
48 | }
49 | }
50 |
51 | & .lds-ripple {
52 | opacity: 0;
53 | position: fixed;
54 | width: 58px;
55 | height: 58px;
56 | left: calc(50% - 29px);
57 | top: calc(50% - 29px);
58 | border-radius: 100%;
59 |
60 | & div {
61 | position: absolute;
62 | border: 2px dashed color-mod(var(--green-color) alpha(50%));
63 | opacity: 1;
64 | border-radius: 50%;
65 | animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
66 |
67 | &:nth-child(2) {
68 | animation-delay: -0.5s;
69 | }
70 | }
71 | }
72 |
73 | & .update-container {
74 | max-width: 1600px;
75 | width: 100%;
76 | border-radius: 0.75em;
77 | transform: translateY(0);
78 | opacity: 1;
79 | transition: all 0.7s cubic-bezier(0.23, 1, 0.32, 1);
80 | animation-name: fade-one;
81 | animation-duration: 0.7s;
82 | animation-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
83 |
84 | & .section-name {
85 | display: inline-flex;
86 | font-weight: 100;
87 | font-size: 1em;
88 | color: color-mod(var(--green-color) alpha(75%));
89 | border: 1px dashed color-mod(var(--green-color) alpha(50%));
90 | padding: 0.25em 0.75em;
91 | border-radius: 1.25em;
92 | margin-bottom: 2em;
93 | }
94 |
95 | & .update-description {
96 | color: color-mod(var(--green-color) alpha(50%));
97 | margin-bottom: 4em;
98 | }
99 |
100 | & .release-notes {
101 | color: var(--grey-color);
102 | border-top: 1px dashed color-mod(var(--green-color) alpha(10%));
103 | border-bottom: 1px dashed color-mod(var(--green-color) alpha(10%));
104 | padding-top: 2em;
105 | margin-bottom: 2em;
106 |
107 | & .note {
108 | margin-bottom: 2em;
109 |
110 | & .version {
111 | display: inline-flex;
112 | color: #fff;
113 | padding: 0.25em 0.75em;
114 | border-radius: 1.25em;
115 | background-color: color-mod(var(--green-color) alpha(75%));
116 | }
117 | }
118 | }
119 |
120 | & .downloads {
121 | display: flex;
122 | flex-flow: column wrap;
123 | border-top: 1px dashed color-mod(var(--green-color) alpha(10%));
124 | padding-top: 2em;
125 | margin-bottom: 2em;
126 |
127 | & a {
128 | color: color-mod(var(--green-color) alpha(65%));
129 | text-decoration: none;
130 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
131 | margin: 0.15em 0;
132 | white-space: nowrap;
133 | overflow: hidden;
134 | text-overflow: ellipsis;
135 |
136 | &:hover {
137 | color: var(--green-color);
138 | }
139 |
140 | & span.size {
141 | margin-right: 0.5em;
142 | color: var(--grey-color);
143 | font-size: 0.85em;
144 | text-decoration: none;
145 | }
146 | }
147 |
148 | & p {
149 | color: var(--grey-color);
150 | font-weight: 700;
151 |
152 | &:first-child {
153 | margin-top: 0;
154 | }
155 | }
156 | }
157 | }
158 | }
159 |
160 | .download-progress {
161 | position: fixed;
162 | left: calc(50% - 200px + 2em);
163 | top: calc(50% - 5px);
164 | width: calc(400px - 4em);
165 | height: 10px;
166 | overflow: hidden;
167 | background-color: color-mod(var(--green-color) alpha(15%));
168 | border-radius: 0.25em;
169 | }
170 |
171 | .download-progress-bar {
172 | position: absolute;
173 | background-color: rgba(49, 188, 135, 0.75);
174 | height: 100%;
175 | border-radius: 0.25em;
176 | transition: all 0.5s cubic-bezier(0.23, 1, 0.32, 1);
177 | }
178 |
179 | @keyframes lds-ripple {
180 | 0% {
181 | top: 28px;
182 | left: 28px;
183 | width: 0;
184 | height: 0;
185 | opacity: 1;
186 | }
187 | 100% {
188 | top: -1px;
189 | left: -1px;
190 | width: 58px;
191 | height: 58px;
192 | opacity: 0;
193 | }
194 | }
195 |
196 | @keyframes fade-one {
197 | from {
198 | opacity: 0;
199 | transform: translateY(10%);
200 | }
201 | to {
202 | opacity: 1;
203 | transform: translateY(0);
204 | }
205 | }
--------------------------------------------------------------------------------
/src/actions/MainActions.js:
--------------------------------------------------------------------------------
1 | import { readFile } from 'fs';
2 | import { remote } from 'electron';
3 | import { showResult } from './ResultsActions';
4 | import util from 'util';
5 |
6 | const { dialog } = remote;
7 | const readFilePromisify = util.promisify(readFile);
8 |
9 | export const openFile = () => async dispatch => {
10 | const [readPath] = dialog.showOpenDialog({
11 | filters: [
12 | {
13 | name: 'Text Files',
14 | extensions: ['txt']
15 | }
16 | ]
17 | });
18 |
19 | if (readPath) {
20 | dispatch(showResult(await readFilePromisify(readPath, 'utf8')));
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/src/actions/ResultsActions.js:
--------------------------------------------------------------------------------
1 | import { sort } from 'js-flock';
2 | import { writeFile } from 'fs';
3 | import { lookup, codes } from '../core/country';
4 | import { uniq } from '../misc/other';
5 | import { remote } from 'electron';
6 | import { RESULTS_SHOW, RESULTS_TOGGLE_COUNTRY, CLOSE } from '../constants/ActionTypes';
7 |
8 | const { dialog } = remote;
9 |
10 | const findProxies = text => {
11 | try {
12 | return uniq(text.match(new RegExp('[1-2]?[0-9]{1,3}[.][1-2]?[0-9]{1,3}[.][1-2]?[0-9]{1,3}[.][1-2]?[0-9]{1,3}[:][1-9]?[0-9]{1,5}', 'g')));
13 | } catch (error) {
14 | throw new Error('No proxies found!');
15 | }
16 | };
17 |
18 | export const showResult = text => dispatch => {
19 | try {
20 | const res = [];
21 | const countries = {};
22 | const proxies = findProxies(text);
23 |
24 | proxies.forEach(item => {
25 | const [ip] = item.split(':');
26 | const code = lookup(ip);
27 |
28 | if (countries[code] == undefined) {
29 | countries[code] = {
30 | code,
31 | ...codes[code],
32 | items: [item]
33 | };
34 | } else {
35 | countries[code].items.push(item);
36 | }
37 | });
38 |
39 | Object.keys(countries).forEach(item => {
40 | res.push({
41 | ...countries[item],
42 | active: true
43 | });
44 | });
45 |
46 | dispatch({
47 | type: RESULTS_SHOW,
48 | proxiesByCountries: sort(res).desc(item => item.items.length)
49 | });
50 | } catch (error) {
51 | alert(error);
52 | }
53 | };
54 |
55 | export const toggleCountry = (code, state, all) => ({
56 | type: RESULTS_TOGGLE_COUNTRY,
57 | code,
58 | state,
59 | all
60 | });
61 |
62 | export const save = () => (dispatch, getState) => {
63 | const savePath = dialog.showSaveDialog({
64 | filters: [
65 | {
66 | name: 'Text Files',
67 | extensions: ['txt']
68 | }
69 | ]
70 | });
71 |
72 | if (savePath) {
73 | const {
74 | results: { proxiesByCountries }
75 | } = getState();
76 |
77 | const results = proxiesByCountries
78 | .filter(item => item.active)
79 | .map(item => item.items)
80 | .flat();
81 |
82 | writeFile(savePath, results.join('\r\n'), () => null);
83 | }
84 | };
85 |
86 | export const close = () => ({
87 | type: CLOSE
88 | });
89 |
--------------------------------------------------------------------------------
/src/actions/UpdateActions.js:
--------------------------------------------------------------------------------
1 | import request from 'request';
2 | import progress from 'request-progress';
3 | import { remote, shell } from 'electron';
4 | import { getLatestVersionInfo } from '../core/updater';
5 | import { createWriteStream } from 'fs';
6 | import { wait } from '../misc/other';
7 | import { UPDATE_CHANGE_STATE, UPDATE_UP_DOWNLOAD_PROGRESS, UPDATE_CLOSE } from '../constants/ActionTypes';
8 |
9 | const { dialog, getCurrentWindow } = remote;
10 |
11 | export const checkAtAvailable = () => async dispatch => {
12 | const details = await getLatestVersionInfo();
13 |
14 | await wait(500);
15 |
16 | if (details) {
17 | dispatch(
18 | changeUpdateState({
19 | isAvailable: true,
20 | isChecking: false,
21 | info: details
22 | })
23 | );
24 | } else {
25 | dispatch(
26 | changeUpdateState({
27 | active: false,
28 | isChecking: false
29 | })
30 | );
31 | }
32 | };
33 |
34 | const changeUpdateState = nextState => ({
35 | type: UPDATE_CHANGE_STATE,
36 | nextState
37 | });
38 |
39 | export const close = () => ({
40 | type: UPDATE_CLOSE
41 | });
42 |
43 | const upDownloadProgress = percent => ({
44 | type: UPDATE_UP_DOWNLOAD_PROGRESS,
45 | percent
46 | });
47 |
48 | export const download = e => async dispatch => {
49 | e.preventDefault();
50 |
51 | let savePath = dialog.showSaveDialog({
52 | defaultPath: e.target.title,
53 | filters: [
54 | {
55 | name: '.rar, .exe, .zip, .7z',
56 | extensions: ['rar', 'exe', 'zip', '7z']
57 | }
58 | ]
59 | });
60 |
61 | if (!savePath) return;
62 |
63 | dispatch(changeUpdateState({ onDownloading: true }));
64 |
65 | progress(request(e.target.href), {
66 | throttle: 100
67 | })
68 | .on('progress', state => {
69 | dispatch(upDownloadProgress(state.percent * 100));
70 | })
71 | .on('end', () => {
72 | shell.showItemInFolder(savePath);
73 | getCurrentWindow().close();
74 | })
75 | .pipe(createWriteStream(savePath));
76 | };
--------------------------------------------------------------------------------
/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shell } from 'electron';
3 | import { currentVersion } from '../core/updater';
4 |
5 | import '../../public/styles/Footer.postcss';
6 |
7 | const openLink = e => {
8 | e.preventDefault();
9 | shell.openExternal(e.currentTarget.href || e.currentTarget.getAttribute('xlink:href'));
10 | };
11 |
12 | const Footer = () => (
13 |
51 | );
52 |
53 | export default Footer;
--------------------------------------------------------------------------------
/src/components/ResultsCountryItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { splitByKK } from '../misc/text';
3 |
4 | export default class ResultsCountryItem extends React.PureComponent {
5 | toggle = () => {
6 | const { toggleCountry, code, active } = this.props;
7 | toggleCountry(code, !active);
8 | };
9 |
10 | toggleAll = () => {
11 | const { toggleCountry, code, active } = this.props;
12 | toggleCountry(code, !active, true);
13 | };
14 |
15 | render = () => {
16 | const { active, flag, name, items } = this.props;
17 |
18 | return (
19 |
20 |
23 |
24 |
{name}
25 |
Proxies: {splitByKK(items.length)}
26 |
27 |
28 | );
29 | };
30 | }
31 |
--------------------------------------------------------------------------------
/src/components/Root.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Main from '../containers/Main';
3 | import Update from '../containers/Update';
4 | import Results from '../containers/Results';
5 |
6 | const Root = () => (
7 |
8 |
9 |
10 |
11 |
12 | );
13 |
14 | export default Root;
15 |
--------------------------------------------------------------------------------
/src/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | //results
2 | export const RESULTS_SHOW = 'RESULTS_SHOW';
3 | export const RESULTS_TOGGLE_COUNTRY = 'RESULTS_TOGGLE_COUNTRY';
4 | export const CLOSE = 'CLOSE';
5 |
6 | //update
7 | export const UPDATE_CHANGE_STATE = 'UPDATE_CHANGE_STATE';
8 | export const UPDATE_UP_DOWNLOAD_PROGRESS = 'UPDATE_UP_DOWNLOAD_PROGRESS';
9 | export const UPDATE_CLOSE = 'UPDATE_CLOSE';
10 |
--------------------------------------------------------------------------------
/src/containers/Main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { openFile } from '../actions/MainActions';
4 |
5 | import '../../public/styles/Main.postcss';
6 | import '../../public/styles/Elements.postcss';
7 |
8 | const Main = ({ openFile }) => (
9 |
10 |
11 |
12 |
13 |
14 | );
15 |
16 | export default connect(
17 | null,
18 | { openFile }
19 | )(Main);
20 |
--------------------------------------------------------------------------------
/src/containers/Results.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { splitByKK } from '../misc/text';
4 | import { save, close, toggleCountry } from '../actions/ResultsActions';
5 | import ResultsCountryItem from '../components/ResultsCountryItem';
6 | import Footer from '../components/Footer';
7 |
8 | import '../../public/styles/Results.postcss';
9 | import '../../public/styles/Icons.postcss';
10 |
11 | const Results = ({ active, proxiesByCountries, save, close, toggleCountry }) => {
12 | const activeCountries = proxiesByCountries.filter(item => item.active);
13 | const selectedProxiesCount = activeCountries.reduce((prev, curr) => prev + curr.items.length, 0);
14 |
15 | return (
16 |
17 |
18 |
19 |
0 ? 'has-items' : 'no-items'}`}>
20 |
21 | Selected: {activeCountries.length} of {proxiesByCountries.length}
22 |
23 | Proxies: {splitByKK(selectedProxiesCount)}
24 |
25 |
26 |
33 |
37 |
38 |
39 |
40 | {proxiesByCountries.map(country => (
41 |
42 | ))}
43 |
44 |
45 |
78 |
79 | Double click select or deselect all
80 |
81 |
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | const mapStateToProps = state => ({
89 | ...state.results
90 | });
91 |
92 | const mapDispatchToProps = {
93 | save,
94 | close,
95 | toggleCountry
96 | };
97 |
98 | export default connect(
99 | mapStateToProps,
100 | mapDispatchToProps
101 | )(Results);
102 |
--------------------------------------------------------------------------------
/src/containers/Update.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactMarkdown from 'react-markdown';
3 | import { connect } from 'react-redux';
4 | import { checkAtAvailable, close, download } from '../actions/UpdateActions';
5 | import { bytesToSize } from '../misc/text';
6 |
7 | import '../../public/styles/Update.postcss';
8 |
9 | class Update extends React.PureComponent {
10 | componentWillMount = () => {
11 | const { checkAtAvailable } = this.props;
12 | checkAtAvailable();
13 | };
14 |
15 | render = () => {
16 | const { active, isAvailable, isChecking, onDownloading, downloadProgress, info, close, download } = this.props;
17 | const progress = {
18 | width: downloadProgress + '%'
19 | };
20 |
21 | return (
22 |
23 |
27 | {isAvailable && (
28 |
29 |
Update is available
30 |
31 | {info.releaseNotes.map(note => (
32 |
33 | Release Notes for v{note.version}
34 |
35 |
36 | ))}
37 |
38 |
Downloads
39 |
55 |
56 |
57 | )}
58 | {onDownloading && (
59 |
62 | )}
63 |
64 | );
65 | };
66 | }
67 |
68 | const mapStateToProps = state => ({
69 | ...state.update
70 | });
71 |
72 | const mapDispatchToProps = {
73 | checkAtAvailable,
74 | close,
75 | download
76 | };
77 |
78 | export default connect(
79 | mapStateToProps,
80 | mapDispatchToProps
81 | )(Update);
82 |
--------------------------------------------------------------------------------
/src/core/country.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import MMDBReader from 'mmdb-reader';
3 |
4 | const pathToMmdb = process.env.NODE_ENV !== 'production' ? './files/GeoLite2-Country.mmdb' : './resources/files/GeoLite2-Country.mmdb';
5 |
6 | const reader = new MMDBReader(path.resolve(pathToMmdb));
7 |
8 | export const codes = {
9 | AF: { flag: 'afghanistan', name: 'Afghanistan' },
10 | AL: { flag: 'albania', name: 'Albania' },
11 | DZ: { flag: 'algeria', name: 'Algeria' },
12 | DS: { flag: 'american-samoa', name: 'American Samoa' },
13 | AD: { flag: 'andorra', name: 'Andorra' },
14 | AO: { flag: 'angola', name: 'Angola' },
15 | AI: { flag: 'anguilla', name: 'Anguilla' },
16 | AQ: { flag: 'antarctica', name: 'Antarctica' },
17 | AG: { flag: 'antigua-and-barbuda', name: 'Antigua and Barbuda' },
18 | AR: { flag: 'argentina', name: 'Argentina' },
19 | AM: { flag: 'armenia', name: 'Armenia' },
20 | AW: { flag: 'aruba', name: 'Aruba' },
21 | AU: { flag: 'australia', name: 'Australia' },
22 | AT: { flag: 'austria', name: 'Austria' },
23 | AZ: { flag: 'azerbaijan', name: 'Azerbaijan' },
24 | BS: { flag: 'bahamas', name: 'Bahamas' },
25 | BH: { flag: 'bahrain', name: 'Bahrain' },
26 | BD: { flag: 'bangladesh', name: 'Bangladesh' },
27 | BB: { flag: 'barbados', name: 'Barbados' },
28 | BY: { flag: 'belarus', name: 'Belarus' },
29 | BE: { flag: 'belgium', name: 'Belgium' },
30 | BZ: { flag: 'belize', name: 'Belize' },
31 | BJ: { flag: 'benin', name: 'Benin' },
32 | BM: { flag: 'bermuda', name: 'Bermuda' },
33 | BT: { flag: 'bhutan', name: 'Bhutan' },
34 | BO: { flag: 'bolivia', name: 'Bolivia' },
35 | BA: { flag: 'bosnia-and-herzegovina', name: 'Bosnia and Herzegovina' },
36 | BW: { flag: 'botswana', name: 'Botswana' },
37 | BV: { flag: 'bouvet-island', name: 'Bouvet Island' },
38 | BR: { flag: 'brazil', name: 'Brazil' },
39 | IO: { flag: 'british-indian-ocean-territory', name: 'British Indian Ocean Territory' },
40 | BN: { flag: 'brunei-darussalam', name: 'Brunei Darussalam' },
41 | BG: { flag: 'bulgaria', name: 'Bulgaria' },
42 | BF: { flag: 'burkina-faso', name: 'Burkina Faso' },
43 | BI: { flag: 'burundi', name: 'Burundi' },
44 | KH: { flag: 'cambodia', name: 'Cambodia' },
45 | CM: { flag: 'cameroon', name: 'Cameroon' },
46 | CA: { flag: 'canada', name: 'Canada' },
47 | CV: { flag: 'cape-verde', name: 'Cape Verde' },
48 | KY: { flag: 'cayman-islands', name: 'Cayman Islands' },
49 | CF: { flag: 'central-african-republic', name: 'Central African Republic' },
50 | TD: { flag: 'chad', name: 'Chad' },
51 | CL: { flag: 'chile', name: 'Chile' },
52 | CN: { flag: 'china', name: 'China' },
53 | CX: { flag: 'christmas-island', name: 'Christmas Island' },
54 | CC: { flag: 'cocos-keeling-islands', name: 'Cocos (Keeling) Islands' },
55 | CO: { flag: 'colombia', name: 'Colombia' },
56 | KM: { flag: 'comoros', name: 'Comoros' },
57 | CG: { flag: 'congo', name: 'Congo' },
58 | CK: { flag: 'cook-islands', name: 'Cook Islands' },
59 | CR: { flag: 'costa-rica', name: 'Costa Rica' },
60 | HR: { flag: 'croatia-hrvatska', name: 'Croatia (Hrvatska)' },
61 | CU: { flag: 'cuba', name: 'Cuba' },
62 | CY: { flag: 'cyprus', name: 'Cyprus' },
63 | CZ: { flag: 'czech-republic', name: 'Czech Republic' },
64 | DK: { flag: 'denmark', name: 'Denmark' },
65 | DJ: { flag: 'djibouti', name: 'Djibouti' },
66 | DM: { flag: 'dominica', name: 'Dominica' },
67 | DO: { flag: 'dominican-republic', name: 'Dominican Republic' },
68 | TP: { flag: 'east-timor', name: 'East Timor' },
69 | EC: { flag: 'ecuador', name: 'Ecuador' },
70 | EG: { flag: 'egypt', name: 'Egypt' },
71 | SV: { flag: 'el-salvador', name: 'El Salvador' },
72 | GQ: { flag: 'equatorial-guinea', name: 'Equatorial Guinea' },
73 | ER: { flag: 'eritrea', name: 'Eritrea' },
74 | EE: { flag: 'estonia', name: 'Estonia' },
75 | ET: { flag: 'ethiopia', name: 'Ethiopia' },
76 | FK: { flag: 'falkland-islands-malvinas', name: 'Falkland Islands (Malvinas)' },
77 | FO: { flag: 'faroe-islands', name: 'Faroe Islands' },
78 | FJ: { flag: 'fiji', name: 'Fiji' },
79 | FI: { flag: 'finland', name: 'Finland' },
80 | FR: { flag: 'france', name: 'France' },
81 | FX: { flag: 'france-metropolitan', name: 'France, Metropolitan' },
82 | GF: { flag: 'french-guiana', name: 'French Guiana' },
83 | PF: { flag: 'french-polynesia', name: 'French Polynesia' },
84 | TF: { flag: 'french-southern-territories', name: 'French Southern Territories' },
85 | GA: { flag: 'gabon', name: 'Gabon' },
86 | GM: { flag: 'gambia', name: 'Gambia' },
87 | GE: { flag: 'georgia', name: 'Georgia' },
88 | DE: { flag: 'germany', name: 'Germany' },
89 | GH: { flag: 'ghana', name: 'Ghana' },
90 | GI: { flag: 'gibraltar', name: 'Gibraltar' },
91 | GK: { flag: 'guernsey', name: 'Guernsey' },
92 | GR: { flag: 'greece', name: 'Greece' },
93 | GL: { flag: 'greenland', name: 'Greenland' },
94 | GD: { flag: 'grenada', name: 'Grenada' },
95 | GP: { flag: 'guadeloupe', name: 'Guadeloupe' },
96 | GU: { flag: 'guam', name: 'Guam' },
97 | GT: { flag: 'guatemala', name: 'Guatemala' },
98 | GN: { flag: 'guinea', name: 'Guinea' },
99 | GW: { flag: 'guinea-bissau', name: 'Guinea-Bissau' },
100 | GY: { flag: 'guyana', name: 'Guyana' },
101 | HT: { flag: 'haiti', name: 'Haiti' },
102 | HM: { flag: 'heard-and-mc-donald-islands', name: 'Heard and Mc Donald Islands' },
103 | HN: { flag: 'honduras', name: 'Honduras' },
104 | HK: { flag: 'hong-kong', name: 'Hong Kong' },
105 | HU: { flag: 'hungary', name: 'Hungary' },
106 | IS: { flag: 'iceland', name: 'Iceland' },
107 | IN: { flag: 'india', name: 'India' },
108 | IM: { flag: 'isle-of-man', name: 'Isle of Man' },
109 | ID: { flag: 'indonesia', name: 'Indonesia' },
110 | IR: { flag: 'iran', name: 'Iran' },
111 | IQ: { flag: 'iraq', name: 'Iraq' },
112 | IE: { flag: 'ireland', name: 'Ireland' },
113 | IL: { flag: 'israel', name: 'Israel' },
114 | IT: { flag: 'italy', name: 'Italy' },
115 | CI: { flag: 'ivory-coast', name: 'Ivory Coast' },
116 | JE: { flag: 'jersey', name: 'Jersey' },
117 | JM: { flag: 'jamaica', name: 'Jamaica' },
118 | JP: { flag: 'japan', name: 'Japan' },
119 | JO: { flag: 'jordan', name: 'Jordan' },
120 | KZ: { flag: 'kazakhstan', name: 'Kazakhstan' },
121 | KE: { flag: 'kenya', name: 'Kenya' },
122 | KI: { flag: 'kiribati', name: 'Kiribati' },
123 | KP: { flag: 'korea-democratic-peoples-republic-of', name: "Korea, Democratic People's Republic of" },
124 | KR: { flag: 'korea', name: 'Korea' },
125 | XK: { flag: 'kosovo', name: 'Kosovo' },
126 | KW: { flag: 'kuwait', name: 'Kuwait' },
127 | KG: { flag: 'kyrgyzstan', name: 'Kyrgyzstan' },
128 | LA: { flag: 'laos', name: 'Laos' },
129 | LV: { flag: 'latvia', name: 'Latvia' },
130 | LB: { flag: 'lebanon', name: 'Lebanon' },
131 | LS: { flag: 'lesotho', name: 'Lesotho' },
132 | LR: { flag: 'liberia', name: 'Liberia' },
133 | LY: { flag: 'libyan-arab-jamahiriya', name: 'Libyan Arab Jamahiriya' },
134 | LI: { flag: 'liechtenstein', name: 'Liechtenstein' },
135 | LT: { flag: 'lithuania', name: 'Lithuania' },
136 | LU: { flag: 'luxembourg', name: 'Luxembourg' },
137 | MO: { flag: 'macau', name: 'Macau' },
138 | MK: { flag: 'macedonia', name: 'Macedonia' },
139 | MG: { flag: 'madagascar', name: 'Madagascar' },
140 | MW: { flag: 'malawi', name: 'Malawi' },
141 | MY: { flag: 'malaysia', name: 'Malaysia' },
142 | MV: { flag: 'maldives', name: 'Maldives' },
143 | ML: { flag: 'mali', name: 'Mali' },
144 | MT: { flag: 'malta', name: 'Malta' },
145 | MH: { flag: 'marshall-islands', name: 'Marshall Islands' },
146 | MQ: { flag: 'martinique', name: 'Martinique' },
147 | MR: { flag: 'mauritania', name: 'Mauritania' },
148 | MU: { flag: 'mauritius', name: 'Mauritius' },
149 | TY: { flag: 'mayotte', name: 'Mayotte' },
150 | MX: { flag: 'mexico', name: 'Mexico' },
151 | FM: { flag: 'micronesia-federated-states-of', name: 'Micronesia, Federated States of' },
152 | MD: { flag: 'moldova', name: 'Moldova' },
153 | MC: { flag: 'monaco', name: 'Monaco' },
154 | MN: { flag: 'mongolia', name: 'Mongolia' },
155 | ME: { flag: 'montenegro', name: 'Montenegro' },
156 | MS: { flag: 'montserrat', name: 'Montserrat' },
157 | MA: { flag: 'morocco', name: 'Morocco' },
158 | MZ: { flag: 'mozambique', name: 'Mozambique' },
159 | MM: { flag: 'myanmar', name: 'Myanmar' },
160 | NA: { flag: 'namibia', name: 'Namibia' },
161 | NR: { flag: 'nauru', name: 'Nauru' },
162 | NP: { flag: 'nepal', name: 'Nepal' },
163 | NL: { flag: 'netherlands', name: 'Netherlands' },
164 | AN: { flag: 'netherlands-antilles', name: 'Netherlands Antilles' },
165 | NC: { flag: 'new-caledonia', name: 'New Caledonia' },
166 | NZ: { flag: 'new-zealand', name: 'New Zealand' },
167 | NI: { flag: 'nicaragua', name: 'Nicaragua' },
168 | NE: { flag: 'niger', name: 'Niger' },
169 | NG: { flag: 'nigeria', name: 'Nigeria' },
170 | NU: { flag: 'niue', name: 'Niue' },
171 | NF: { flag: 'norfolk-island', name: 'Norfolk Island' },
172 | MP: { flag: 'northern-mariana-islands', name: 'Northern Mariana Islands' },
173 | NO: { flag: 'norway', name: 'Norway' },
174 | OM: { flag: 'oman', name: 'Oman' },
175 | PK: { flag: 'pakistan', name: 'Pakistan' },
176 | PW: { flag: 'palau', name: 'Palau' },
177 | PS: { flag: 'palestine', name: 'Palestine' },
178 | PA: { flag: 'panama', name: 'Panama' },
179 | PG: { flag: 'papua-new-guinea', name: 'Papua New Guinea' },
180 | PY: { flag: 'paraguay', name: 'Paraguay' },
181 | PE: { flag: 'peru', name: 'Peru' },
182 | PH: { flag: 'philippines', name: 'Philippines' },
183 | PN: { flag: 'pitcairn', name: 'Pitcairn' },
184 | PL: { flag: 'poland', name: 'Poland' },
185 | PT: { flag: 'portugal', name: 'Portugal' },
186 | PR: { flag: 'puerto-rico', name: 'Puerto Rico' },
187 | QA: { flag: 'qatar', name: 'Qatar' },
188 | RE: { flag: 'reunion', name: 'Reunion' },
189 | RO: { flag: 'romania', name: 'Romania' },
190 | RU: { flag: 'russian-federation', name: 'Russian Federation' },
191 | RW: { flag: 'rwanda', name: 'Rwanda' },
192 | KN: { flag: 'saint-kitts-and-nevis', name: 'Saint Kitts and Nevis' },
193 | LC: { flag: 'saint-lucia', name: 'Saint Lucia' },
194 | VC: { flag: 'saint-vincent-and-the-grenadines', name: 'Saint Vincent and the Grenadines' },
195 | WS: { flag: 'samoa', name: 'Samoa' },
196 | SM: { flag: 'san-marino', name: 'San Marino' },
197 | ST: { flag: 'sao-tome-and-principe', name: 'Sao Tome and Principe' },
198 | SA: { flag: 'saudi-arabia', name: 'Saudi Arabia' },
199 | SN: { flag: 'senegal', name: 'Senegal' },
200 | RS: { flag: 'serbia', name: 'Serbia' },
201 | SC: { flag: 'seychelles', name: 'Seychelles' },
202 | SL: { flag: 'sierra-leone', name: 'Sierra Leone' },
203 | SG: { flag: 'singapore', name: 'Singapore' },
204 | SK: { flag: 'slovakia', name: 'Slovakia' },
205 | SI: { flag: 'slovenia', name: 'Slovenia' },
206 | SB: { flag: 'solomon-islands', name: 'Solomon Islands' },
207 | SO: { flag: 'somalia', name: 'Somalia' },
208 | ZA: { flag: 'south-africa', name: 'South Africa' },
209 | GS: { flag: 'south-georgia-south-sandwich-islands', name: 'South Georgia South Sandwich Islands' },
210 | ES: { flag: 'spain', name: 'Spain' },
211 | LK: { flag: 'sri-lanka', name: 'Sri Lanka' },
212 | SH: { flag: 'st-helena', name: 'St. Helena' },
213 | PM: { flag: 'st-pierre-and-miquelon', name: 'St. Pierre and Miquelon' },
214 | SD: { flag: 'sudan', name: 'Sudan' },
215 | SR: { flag: 'suriname', name: 'Suriname' },
216 | SJ: { flag: 'svalbard-and-jan-mayen-islands', name: 'Svalbard and Jan Mayen Islands' },
217 | SZ: { flag: 'swaziland', name: 'Swaziland' },
218 | SE: { flag: 'sweden', name: 'Sweden' },
219 | CH: { flag: 'switzerland', name: 'Switzerland' },
220 | SY: { flag: 'syria', name: 'Syria' },
221 | TW: { flag: 'taiwan', name: 'Taiwan' },
222 | TJ: { flag: 'tajikistan', name: 'Tajikistan' },
223 | TZ: { flag: 'tanzania', name: 'Tanzania' },
224 | TH: { flag: 'thailand', name: 'Thailand' },
225 | TG: { flag: 'togo', name: 'Togo' },
226 | TK: { flag: 'tokelau', name: 'Tokelau' },
227 | TO: { flag: 'tonga', name: 'Tonga' },
228 | TT: { flag: 'trinidad-and-tobago', name: 'Trinidad and Tobago' },
229 | TN: { flag: 'tunisia', name: 'Tunisia' },
230 | TR: { flag: 'turkey', name: 'Turkey' },
231 | TM: { flag: 'turkmenistan', name: 'Turkmenistan' },
232 | TC: { flag: 'turks-and-caicos-islands', name: 'Turks and Caicos Islands' },
233 | TV: { flag: 'tuvalu', name: 'Tuvalu' },
234 | UG: { flag: 'uganda', name: 'Uganda' },
235 | UA: { flag: 'ukraine', name: 'Ukraine' },
236 | AE: { flag: 'united-arab-emirates', name: 'United Arab Emirates' },
237 | GB: { flag: 'united-kingdom', name: 'United Kingdom' },
238 | US: { flag: 'united-states', name: 'United States' },
239 | UM: { flag: 'united-states-minor-outlying-islands', name: 'United States minor outlying islands' },
240 | UY: { flag: 'uruguay', name: 'Uruguay' },
241 | UZ: { flag: 'uzbekistan', name: 'Uzbekistan' },
242 | VU: { flag: 'vanuatu', name: 'Vanuatu' },
243 | VA: { flag: 'vatican-city-state', name: 'Vatican City State' },
244 | VE: { flag: 'venezuela', name: 'Venezuela' },
245 | VN: { flag: 'vietnam', name: 'Vietnam' },
246 | VG: { flag: 'virgin-islands-british', name: 'Virgin Islands (British)' },
247 | VI: { flag: 'virgin-islands-us', name: 'Virgin Islands (U.S.)' },
248 | WF: { flag: 'wallis-and-futuna-islands', name: 'Wallis and Futuna Islands' },
249 | EH: { flag: 'western-sahara', name: 'Western Sahara' },
250 | YE: { flag: 'yemen', name: 'Yemen' },
251 | ZR: { flag: 'zaire', name: 'Zaire' },
252 | ZM: { flag: 'zambia', name: 'Zambia' },
253 | ZW: { flag: 'zimbabwe', name: 'Zimbabwe' },
254 | AS: { flag: 'american-samoa', name: 'American Samoa' },
255 | AX: { flag: 'aland-islands', name: 'Aland Islands' },
256 | BQ: { flag: 'bonaire-sint-eustatius-saba', name: 'Bonaire, Sint Eustatius, Saba' },
257 | CD: { flag: 'congo-the-democratic-republic-of-the', name: 'Congo The Democratic Republic of The' },
258 | CW: { flag: 'curacao', name: 'Curacao' },
259 | EU: { flag: 'european-union', name: 'European Union' },
260 | GG: { flag: 'guernsey', name: 'Guernsey' },
261 | MF: { flag: 'saint-martin', name: 'Saint Martin' },
262 | SS: { flag: 'south-sudan', name: 'South Sudan' },
263 | SX: { flag: 'sint-maarten', name: 'Sint Maarten' },
264 | TL: { flag: 'timor-leste', name: 'Timor-leste' },
265 | YT: { flag: 'mayotte', name: 'Mayotte' },
266 | ZZ: { flag: 'unknown', name: 'Unknown' }
267 | };
268 |
269 | export const lookup = ip => {
270 | try {
271 | return reader.lookup(ip).country.iso_code;
272 | } catch (error) {
273 | return 'ZZ';
274 | }
275 | };
276 |
--------------------------------------------------------------------------------
/src/core/updater.js:
--------------------------------------------------------------------------------
1 | import rp from 'request-promise';
2 | import { remote } from 'electron';
3 | import { sort } from 'js-flock';
4 |
5 | const {
6 | app: { getVersion }
7 | } = remote;
8 |
9 | export const currentVersion = getVersion();
10 |
11 | export const getLatestVersionInfo = async () => {
12 | try {
13 | const releases = await rp.get({
14 | url: 'https://api.github.com/repos/assnctr/unfx-proxy-to-country/releases',
15 | json: true,
16 | timeout: 5000,
17 | headers: {
18 | 'User-Agent': 'Unfx Version Lookup'
19 | }
20 | });
21 |
22 | const [latest] = releases;
23 | const version = latest.tag_name.slice(1);
24 |
25 | if (version > currentVersion) {
26 | const releaseNotes = releases
27 | .filter(item => item.tag_name.slice(1) > currentVersion)
28 | .map(item => ({
29 | version: item.tag_name.slice(1),
30 | body: item.body
31 | }));
32 |
33 | sort(latest.assets).desc(item => item.size);
34 |
35 | const assets = {
36 | windows: latest.assets.filter(asset => asset.name.match(/win|nsis/i)),
37 | linux: latest.assets.filter(asset => asset.name.match(/linux/i))
38 | };
39 |
40 | return {
41 | latestVersion: version,
42 | releaseNotes,
43 | assets
44 | };
45 | } else {
46 | return false;
47 | }
48 | } catch (error) {
49 | return false;
50 | }
51 | };
52 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import url from 'url';
3 | import { BrowserWindow, app } from 'electron';
4 | import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
5 |
6 | let window;
7 |
8 | const devWindow = () => {
9 | installExtension(REACT_DEVELOPER_TOOLS)
10 | .then(name => console.log('Added:', name))
11 | .catch(err => console.log('Error:', err));
12 |
13 | window = new BrowserWindow({
14 | width: 1650,
15 | height: 980,
16 | show: false
17 | });
18 |
19 | window.webContents.openDevTools();
20 | };
21 |
22 | const prodWindow = () => {
23 | window = new BrowserWindow({
24 | minWidth: 1000,
25 | minHeight: 680,
26 | width: 1180,
27 | height: 752,
28 | show: false,
29 | resizable: true
30 | });
31 |
32 | window.setMenu(null);
33 | };
34 |
35 | const createWindow = () => {
36 | process.env.NODE_ENV !== 'production' ? devWindow() : prodWindow();
37 |
38 | window.loadURL(
39 | process.env.NODE_ENV !== 'production'
40 | ? 'http://localhost:8080'
41 | : url.format({
42 | pathname: path.join(__dirname, 'index.html'),
43 | protocol: 'file:',
44 | slashes: true
45 | })
46 | );
47 |
48 | window.on('ready-to-show', () => {
49 | window.show();
50 | });
51 |
52 | window.on('closed', () => {
53 | window = null;
54 | });
55 | };
56 |
57 | app.on('ready', createWindow);
58 |
59 | app.on('window-all-closed', () => {
60 | if (process.platform !== 'darwin') {
61 | app.quit();
62 | }
63 | });
64 |
65 | app.on('activate', () => {
66 | if (window === null) {
67 | createWindow();
68 | }
69 | });
70 |
--------------------------------------------------------------------------------
/src/misc/other.js:
--------------------------------------------------------------------------------
1 | export const uniq = array => [...new Set([...array])];
2 |
3 | export const wait = ms => {
4 | return new Promise(resolve => setTimeout(resolve, ms));
5 | };
6 |
--------------------------------------------------------------------------------
/src/misc/text.js:
--------------------------------------------------------------------------------
1 | export const splitByKK = content => content.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
2 |
3 | export const bytesToSize = bytes => {
4 | if (bytes == 0) return '0 B';
5 |
6 | const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
7 | const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
8 | return `${Math.round(bytes / Math.pow(1024, i))} ${sizes[i]}`;
9 | };
10 |
--------------------------------------------------------------------------------
/src/renderer.dev.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Root from './components/Root';
4 | import { AppContainer } from 'react-hot-loader';
5 | import { Provider } from 'react-redux';
6 | import store from './store/index';
7 |
8 | const root = document.getElementById('root');
9 |
10 | const render = () => {
11 | ReactDOM.render(
12 |
13 |
14 |
15 |
16 | ,
17 | root
18 | );
19 | };
20 |
21 | render();
22 |
23 | if (module.hot) {
24 | module.hot.accept('./components/Root', render);
25 | }
26 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import Root from './components/Root';
4 | import { Provider } from 'react-redux';
5 | import store from './store/index';
6 |
7 | const root = document.getElementById('root');
8 |
9 | const render = () => {
10 | ReactDOM.render(
11 |
12 |
13 | ,
14 | root
15 | );
16 | };
17 |
18 | render();
19 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import rootReducer from './reducers/';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import thunkMiddleware from 'redux-thunk';
4 |
5 | const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
6 | const store = createStoreWithMiddleware(rootReducer);
7 |
8 | export default store;
9 |
--------------------------------------------------------------------------------
/src/store/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import results from './results';
3 | import update from './update';
4 |
5 | const rootReducer = combineReducers({
6 | results,
7 | update
8 | });
9 |
10 | export default rootReducer;
11 |
--------------------------------------------------------------------------------
/src/store/reducers/results.js:
--------------------------------------------------------------------------------
1 | import { RESULTS_SHOW, RESULTS_TOGGLE_COUNTRY, CLOSE } from '../../constants/ActionTypes';
2 |
3 | const initial = {
4 | active: false,
5 | proxiesByCountries: []
6 | };
7 |
8 | const results = (state = initial, action) => {
9 | switch (action.type) {
10 | case RESULTS_SHOW:
11 | return {
12 | active: true,
13 | proxiesByCountries: action.proxiesByCountries
14 | };
15 | case RESULTS_TOGGLE_COUNTRY:
16 | if (action.all) {
17 | return {
18 | ...state,
19 | proxiesByCountries: state.proxiesByCountries.map(item => {
20 | return {
21 | ...item,
22 | active: action.state
23 | };
24 | })
25 | };
26 | }
27 |
28 | return {
29 | ...state,
30 | proxiesByCountries: state.proxiesByCountries.map(item => {
31 | if (item.code == action.code) {
32 | return {
33 | ...item,
34 | active: action.state
35 | };
36 | }
37 |
38 | return item;
39 | })
40 | };
41 | case CLOSE:
42 | return initial;
43 | default:
44 | return state;
45 | }
46 | };
47 |
48 | export default results;
49 |
--------------------------------------------------------------------------------
/src/store/reducers/update.js:
--------------------------------------------------------------------------------
1 | import { UPDATE_CHANGE_STATE, UPDATE_UP_DOWNLOAD_PROGRESS, UPDATE_CLOSE } from '../../constants/ActionTypes';
2 |
3 | const initialState = {
4 | active: true,
5 | isAvailable: false,
6 | isChecking: true,
7 | onDownloading: false,
8 | downloadProgress: 0,
9 | info: null
10 | };
11 |
12 | const update = (state = initialState, action) => {
13 | switch (action.type) {
14 | case UPDATE_CHANGE_STATE:
15 | return {
16 | ...state,
17 | ...action.nextState
18 | };
19 | case UPDATE_UP_DOWNLOAD_PROGRESS:
20 | return {
21 | ...state,
22 | downloadProgress: action.percent
23 | };
24 | case UPDATE_CLOSE:
25 | return {
26 | ...state,
27 | active: false
28 | };
29 | default:
30 | return state;
31 | }
32 | };
33 |
34 | export default update;
35 |
--------------------------------------------------------------------------------
/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 |
4 | const getBabelLoader = () => {
5 | const baseOptions = JSON.parse(fs.readFileSync(path.join(__dirname, '.babelrc'), 'utf-8'));
6 | const options = {
7 | ...baseOptions,
8 | presets: baseOptions.presets.map(preset => (preset === '@babel/env' ? ['@babel/env', { modules: false }] : preset)),
9 | babelrc: false
10 | };
11 |
12 | return {
13 | loader: 'babel-loader',
14 | options
15 | };
16 | };
17 |
18 | export default {
19 | resolve: {
20 | extensions: ['.js', '.jsx', '.json'],
21 | modules: [path.join(__dirname, 'src'), 'node_modules']
22 | },
23 | output: {
24 | path: path.join(__dirname, 'public'),
25 | filename: '[name].js',
26 | publicPath: '/'
27 | },
28 | node: {
29 | __dirname: false,
30 | __filename: false
31 | },
32 | module: {
33 | rules: [
34 | {
35 | test: /\.jsx?$/,
36 | include: path.join(__dirname, 'src'),
37 | loader: getBabelLoader()
38 | },
39 | {
40 | test: /\.(png|woff|woff2|eot|ttf|svg)$/,
41 | loader: 'url-loader',
42 | options: {
43 | name: "[path][name].[ext]",
44 | }
45 | },
46 | {
47 | test: /\.(postcss|css)?$/,
48 | use: ['style-loader', {
49 | loader: 'css-loader',
50 | options: {
51 | importLoaders: 1
52 | }
53 | }, 'postcss-loader']
54 | }
55 | ]
56 | }
57 | };
58 |
--------------------------------------------------------------------------------
/webpack.config.main.babel.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import baseConfig from './webpack.config.base';
3 |
4 | export default {
5 | ...baseConfig,
6 | entry: {
7 | main: path.join(__dirname, 'src/index')
8 | },
9 | target: 'electron-main'
10 | };
11 |
--------------------------------------------------------------------------------
/webpack.config.renderer.babel.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import webpack from 'webpack';
3 | import baseConfig from './webpack.config.base';
4 |
5 | export default {
6 | ...baseConfig,
7 | entry: {
8 | renderer: ['react-hot-loader/patch', path.join(__dirname, process.env.NODE_ENV === 'production' ? 'src/renderer' : 'src/renderer.dev')]
9 | },
10 | devServer: {
11 | headers: {
12 | 'Access-Control-Allow-Origin': '*'
13 | },
14 | contentBase: baseConfig.output.path,
15 | publicPath: baseConfig.output.publicPath,
16 | historyApiFallback: true,
17 | hotOnly: true
18 | },
19 | ...(process.env.NODE_ENV === 'production'
20 | ? {}
21 | : {
22 | plugins: [new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin()]
23 | }),
24 | target: 'electron-renderer'
25 | };
26 |
--------------------------------------------------------------------------------