├── .babelrc
├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── LICENSE
├── README.md
├── files
└── GeoLite2-City.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
│ ├── Logo-ProxyScrape-colored.png
│ ├── Logo-ProxyScrape-colored.svg
│ ├── Logo-ProxyScrape-white.png
│ ├── Logo-ProxyScrape-white.svg
│ ├── icon.ico
│ └── icon.png
├── index.html
└── styles
│ ├── Checking.postcss
│ ├── Counter.postcss
│ ├── Elements.postcss
│ ├── Footer.postcss
│ ├── Icons.postcss
│ ├── Info.postcss
│ ├── Input.postcss
│ ├── Main.postcss
│ ├── Modal.postcss
│ ├── Notification.postcss
│ ├── OverlayIp.postcss
│ ├── OverlayJudges.postcss
│ ├── Result.postcss
│ ├── ResultCountries.postcss
│ ├── ResultExport.postcss
│ ├── ResultItemsHeader.postcss
│ ├── Settings.postcss
│ ├── Titlebar.postcss
│ └── Update.postcss
├── src
├── actions
│ ├── BlacklistActions.js
│ ├── CheckingActions.js
│ ├── CoreActions.js
│ ├── InputActions.js
│ ├── IpActions.js
│ ├── JudgesActions.js
│ ├── MainActions.js
│ ├── OverlayIpActions.js
│ ├── OverlayJudgesActions.js
│ ├── ResultActions.js
│ └── UpdateActions.js
├── components
│ ├── BlacklistAddNew.jsx
│ ├── BlacklistItem.jsx
│ ├── Counter.jsx
│ ├── CounterProtocol.jsx
│ ├── Footer.jsx
│ ├── Info.jsx
│ ├── JudgesAddNew.jsx
│ ├── JudgesItem.jsx
│ ├── LicenseModal.jsx
│ ├── Main.jsx
│ ├── Notification.jsx
│ ├── OverlayIp.jsx
│ ├── OverlayJudges.jsx
│ ├── ResultBlacklist.jsx
│ ├── ResultBlacklistItem.jsx
│ ├── ResultCountries.jsx
│ ├── ResultCountriesItem.jsx
│ ├── ResultExport.jsx
│ ├── ResultItemData.jsx
│ ├── ResultItemsHeader.jsx
│ ├── ResultListItem.jsx
│ ├── Settings.jsx
│ └── ui
│ │ ├── CheckIcon.jsx
│ │ ├── Checkbox.jsx
│ │ ├── CloseIcon.jsx
│ │ ├── DocIcon.jsx
│ │ ├── DropDocIcon.jsx
│ │ ├── FillPlusIcon.jsx
│ │ ├── GitIcon.jsx
│ │ ├── LicenseIcon.jsx
│ │ ├── LogoIcon.jsx
│ │ ├── PlusIcon.jsx
│ │ ├── SearchBarIcon.jsx
│ │ ├── SearchIcon.jsx
│ │ ├── SortIcon.jsx
│ │ ├── TimeIcon.jsx
│ │ └── XIcon.jsx
├── constants
│ ├── ActionTypes.js
│ ├── AppConstants.js
│ ├── SettingsConstants.js
│ └── UpdateConstants.js
├── containers
│ ├── Blacklist.jsx
│ ├── Checking.jsx
│ ├── Core.jsx
│ ├── Input.jsx
│ ├── Ip.jsx
│ ├── Judges.jsx
│ ├── Main.jsx
│ ├── Overlay.jsx
│ ├── Result.jsx
│ ├── Titlebar.jsx
│ └── Update.jsx
├── core
│ ├── blacklist.js
│ ├── checker.js
│ ├── country.js
│ ├── index.js
│ ├── ip.js
│ ├── judges.js
│ ├── misc.js
│ ├── settings.js
│ └── updater.js
├── index.js
├── misc
│ ├── FindMixedProxies.js
│ ├── array.js
│ ├── other.js
│ ├── regexes.js
│ ├── text.js
│ └── wait.js
├── renderer.dev.js
├── renderer.js
└── store
│ ├── index.js
│ ├── reducers
│ ├── blacklist.js
│ ├── checking.js
│ ├── core.js
│ ├── index.js
│ ├── input.js
│ ├── ip.js
│ ├── judges.js
│ ├── main.js
│ ├── overlay.js
│ ├── result.js
│ └── update.js
│ └── selectors
│ └── getFilteredProxies.js
├── webpack.config.base.js
├── webpack.config.main.babel.js
└── webpack.config.renderer.babel.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [["@babel/env", { "targets": { "electron": "20.1.3" } }], "@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
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Additional context**
14 | Add any other context about the problem here.
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: new feature
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | settings.proxyscrape.checker.json
4 | /public/*.js
5 | /.vscode
6 | dist
7 | package-lock.json
8 | tests
9 | yarn.lock
10 | yarn-error.log
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 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 |
8 |
9 | 
10 |
11 | Full description & Documentation for [Proxy Checker](https://proxyscrape.com/proxy-checker)
12 |
13 | #### ProxyScrape
14 | [Premium](https://proxyscrape.com/premium) - ProxyScrape premium
15 | [Free Proxy List](https://proxyscrape.com/free-proxy-list) - Free proxy list updated every 5 minutes
16 |
--------------------------------------------------------------------------------
/files/GeoLite2-City.mmdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/files/GeoLite2-City.mmdb
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "proxyscrape-proxy-checker",
3 | "version": "1.1.0",
4 | "main": "public/main.js",
5 | "license": "MIT",
6 | "author": {
7 | "name": "ProxyScrape",
8 | "email": "support@proxyscrape.com",
9 | "url": "https://proxyscrape.com/home"
10 | },
11 | "keywords": [
12 | "ProxyScrape",
13 | "proxy",
14 | "checker"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/ProxyScrape/proxy-checker"
19 | },
20 | "bugs": {
21 | "url": "https://github.com/ProxyScrape/proxy-checker/issues"
22 | },
23 | "homepage": "https://proxyscrape.com/proxy-checker",
24 | "scripts": {
25 | "build": "run-p build:*",
26 | "build:main": "cross-env NODE_ENV=production webpack -p --config webpack.config.main.babel.js",
27 | "build:renderer": "cross-env NODE_ENV=production webpack -p --config webpack.config.renderer.babel.js",
28 | "start": "run-p start:*",
29 | "start:main": "electron --require @babel/register src/index",
30 | "start:renderer": "cross-env NODE_ENV=development webpack-dev-server -d --config webpack.config.renderer.babel.js",
31 | "package": "npm run build && electron-builder --win",
32 | "package-linux": "npm run build && electron-builder --linux",
33 | "publish": "npm run build && electron-builder --win --publish always",
34 | "publish-linux": "npm run build && electron-builder --linux --publish always"
35 | },
36 | "devDependencies": {
37 | "@babel/cli": "^7.18.10",
38 | "@babel/core": "^7.19.1",
39 | "@babel/plugin-proposal-class-properties": "^7.18.6",
40 | "@babel/plugin-proposal-object-rest-spread": "^7.18.9",
41 | "@babel/plugin-transform-runtime": "^7.19.1",
42 | "@babel/preset-env": "^7.19.1",
43 | "@babel/preset-react": "^7.18.6",
44 | "@babel/register": "^7.18.9",
45 | "@babel/runtime": "^7.19.0",
46 | "babel-loader": "^8.2.5",
47 | "cross-env": "^7.0.3",
48 | "css-loader": "^3.2.0",
49 | "electron": "^20.1.4",
50 | "electron-builder": "^23.3.3",
51 | "electron-devtools-installer": "^3.2.0",
52 | "electron-react-devtools": "^0.5.3",
53 | "electron-updater": "^5.2.1",
54 | "fast-sort": "^3.2.0",
55 | "file-loader": "^4.2.0",
56 | "ip": "^1.1.5",
57 | "js-flock": "^3.14.0",
58 | "maxmind": "^3.1.2",
59 | "npm-run-all": "^4.1.5",
60 | "postcss-color-function": "^4.1.0",
61 | "postcss-css-variables": "^0.13.0",
62 | "postcss-easings": "^2.0.0",
63 | "postcss-loader": "^3.0.0",
64 | "postcss-preset-env": "^6.7.0",
65 | "react": "^18.2.0",
66 | "react-dom": "^18.2.0",
67 | "react-hot-loader": "^4.13.1",
68 | "react-markdown": "^4.2.2",
69 | "react-redux": "^8.0.2",
70 | "react-tabs": "^3.0.0",
71 | "redux": "^4.2.0",
72 | "redux-thunk": "^2.4.1",
73 | "request": "^2.88.2",
74 | "request-promise": "^4.2.6",
75 | "reselect": "^4.1.6",
76 | "socks-proxy-agent": "^4.0.2",
77 | "style-loader": "^1.0.0",
78 | "url-loader": "^2.1.0",
79 | "webpack": "^4.25.1",
80 | "webpack-cli": "^3.1.2",
81 | "webpack-dev-server": "^3.1.10",
82 | "axios": "^1.2.0",
83 | "electron-packager": "^17.1.1",
84 | "uuid": "^9.0.0"
85 | },
86 | "build": {
87 | "win": {
88 | "target": [
89 | {
90 | "target": "nsis",
91 | "arch": [
92 | "x64",
93 | "ia32"
94 | ]
95 | },
96 | {
97 | "target": "portable",
98 | "arch": [
99 | "x64",
100 | "ia32"
101 | ]
102 | }
103 | ],
104 | "icon": "/public/icons/icon.ico",
105 | "artifactName": "${name}-v${version}-${arch}-${os}-installer.${ext}"
106 | },
107 | "linux": {
108 | "icon": "./public/icons/icon.png",
109 | "target": [
110 | "AppImage"
111 | ],
112 | "category": "Network",
113 | "artifactName": "${name}-v${version}-${arch}-${os}.${ext}"
114 | },
115 | "publish": [
116 | {
117 | "provider": "github",
118 | "owner": "ProxyScrape",
119 | "repo": "proxyscrape-proxy-checker",
120 | "private": false
121 | }
122 | ],
123 | "productName": "ProxyScrape Proxy Checker",
124 | "copyright": "ProxyScrape",
125 | "extraResources": [
126 | "./files/**"
127 | ],
128 | "portable": {
129 | "artifactName": "${name}-v${version}-${arch}-${os}-portable.${ext}"
130 | },
131 | "nsis": {
132 | "oneClick": false,
133 | "perMachine": true,
134 | "allowToChangeInstallationDirectory": true,
135 | "differentialPackage": true
136 | }
137 | },
138 | "dependencies": {
139 | "bootstrap": "4.3.1",
140 | "electron-dl": "^3.5.0",
141 | "electron-squirrel-startup": "^1.0.0",
142 | "react-bootstrap": "^2.7.0"
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | 'postcss-css-variables': {
4 | variables: {
5 | '--green-color': {
6 | value: '#31bc86'
7 | },
8 | '--blue-color': {
9 | value: '#5396d8'
10 | },
11 | '--grey-color': {
12 | value: '#424a60'
13 | },
14 | '--red-color': {
15 | value: '#e74856'
16 | },
17 | '--back-black-color': {
18 | value: '#1e272e'
19 | },
20 | '--neo-black-color': {
21 | value: '#2C3A47'
22 | },
23 | '--white-color': {
24 | value: '#f5f6fa'
25 | },
26 | '--white-color-back': {
27 | value: '#f0f2fa'
28 | }
29 | }
30 | },
31 | 'postcss-preset-env': {
32 | stage: 4,
33 | features: {
34 | 'nesting-rules': true
35 | }
36 | },
37 | 'postcss-color-function': {},
38 | 'postcss-easings': {
39 | easings: { easeOne: 'cubic-bezier(0.23, 1, 0.32, 1)' }
40 | }
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/public/fonts/Lato-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Black.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-BlackItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Bold.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-BoldItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Hairline.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Hairline.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-HairlineItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-HairlineItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Italic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Light.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-LightItalic.ttf
--------------------------------------------------------------------------------
/public/fonts/Lato-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/fonts/Lato-Regular.ttf
--------------------------------------------------------------------------------
/public/icons/Logo-ProxyScrape-colored.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/icons/Logo-ProxyScrape-colored.png
--------------------------------------------------------------------------------
/public/icons/Logo-ProxyScrape-colored.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/icons/Logo-ProxyScrape-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/icons/Logo-ProxyScrape-white.png
--------------------------------------------------------------------------------
/public/icons/Logo-ProxyScrape-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/icons/icon.ico
--------------------------------------------------------------------------------
/public/icons/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ProxyScrape/proxyscrape-proxy-checker/e94e876fe8d319a1112dde333a5ef7355698b63e/public/icons/icon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ProxyScrape Proxy Checker
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/public/styles/Checking.postcss:
--------------------------------------------------------------------------------
1 | .checking-page {
2 | position: fixed;
3 | display: flex;
4 | visibility: hidden;
5 | flex-flow: column wrap;
6 | align-items: center;
7 | justify-content: center;
8 | width: 100%;
9 | height: calc(100% - 2em);
10 | top: 2em;
11 | opacity: 0;
12 | background-color: color(var(--white-color) alpha(95%));
13 | transition: all 0.5s easeOne;
14 |
15 | &.opened {
16 | visibility: visible;
17 | opacity: 1;
18 | }
19 |
20 | & .preparing-results {
21 | position: fixed;
22 | top: 0;
23 | left: 0;
24 | width: 100%;
25 | height: 100%;
26 | z-index: 1;
27 | display: flex;
28 | align-items: center;
29 | cursor: default;
30 | user-select: none;
31 | font-weight: 700;
32 | color: color(var(--grey-color) alpha(85%));
33 | opacity: 0;
34 | visibility: hidden;
35 | justify-content: center;
36 | background-color: var(--white-color);
37 | transition: all 0.5s easeOne;
38 |
39 | &.active {
40 | visibility: visible;
41 | opacity: 1;
42 | }
43 | }
44 | }
45 |
46 | .container.dark .checking-page {
47 | background-color: color(var(--neo-black-color) alpha(95%));
48 |
49 | & .preparing-results {
50 | color: #3464E0;
51 | background-color: color(var(--neo-black-color) alpha(98%));
52 | }
53 | }
--------------------------------------------------------------------------------
/public/styles/Counter.postcss:
--------------------------------------------------------------------------------
1 | .counter-container {
2 | display: flex;
3 | flex-flow: row wrap;
4 | width: 75%;
5 | max-width: 1600px;
6 | margin: auto;
7 | justify-content: center;
8 | background: #fff;
9 | border-radius: 10px;
10 | position: absolute;
11 | top: 5%;
12 | padding: 15px 25px 30px 25px;
13 |
14 | & .protocol-wrap {
15 | display: flex;
16 | width: 100%;
17 |
18 | & .protocol {
19 | display: inline-flex;
20 | margin-right: 80px;
21 |
22 | & .type {
23 | font-size: 12px;
24 | font-weight: 600;
25 | color: #757DA1;
26 | }
27 |
28 | & .count {
29 | margin-left: 20px;
30 | font-weight: 700;
31 | font-size: 13px;
32 | &:empty {
33 | padding: 0;
34 | margin-left: 0;
35 | }
36 | }
37 | }
38 | }
39 |
40 | & .progress {
41 | position: relative;
42 | width: 80%;
43 | height: 34px;
44 | margin-top: 30px;
45 | border-radius: 10px;
46 | overflow: hidden;
47 | background: #84AEFF;
48 | border: 1px solid #3464E0;
49 |
50 | & .bar {
51 | position: absolute;
52 | height: 100%;
53 | top: 0;
54 | left: 0;
55 | border-radius: 10px;
56 | transition: width 0.7s easeOne;
57 | background-color: #3464E0;
58 | }
59 |
60 | & h1 {
61 | display: flex;
62 | margin: 0;
63 | height: 100%;
64 | justify-content: center;
65 | align-items: center;
66 | position: absolute;
67 | width: 100%;
68 | font-size: 0.9em;
69 | color: var(--white-color);
70 | text-align: center;
71 | }
72 | }
73 |
74 | & button {
75 | margin: 30px 0 0 20px;
76 | width: calc(20% - 20px);
77 | }
78 | }
79 |
80 | .container.dark .counter-container {
81 | & .protocol {
82 | &.http.active {
83 | color: color(var(--grey-color) alpha(95%));
84 | }
85 |
86 | &.socks.active {
87 | color: color(var(--grey-color) alpha(95%));
88 | }
89 | }
90 |
91 | & .progress {
92 | & .bar {
93 | background-color: color(#ddd alpha(15%));
94 | }
95 |
96 | & h1 {
97 | color: var(--neo-black-color);
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/public/styles/Elements.postcss:
--------------------------------------------------------------------------------
1 | /* --------------------------------
2 | Buttons section
3 | -------------------------------- */
4 |
5 | button {
6 | font-weight: 700;
7 | font-size: 14px;
8 | border-radius: 10px;
9 | outline: 0;
10 | border: 0;
11 | cursor: pointer;
12 | padding: 0.5em 1.5em;
13 | margin-right: 2em;
14 | background-color: #3464E0;
15 | color: #fff;
16 | transition: color 0.3s easeOne, background-color 0.3s easeOne;
17 | margin-top: 20px;
18 |
19 | &.less {
20 | padding: 0;
21 | background-color: transparent;
22 | color: color(var(--blue-color) alpha(60%));
23 |
24 | &:hover {
25 | color: #3464E0;
26 | }
27 | }
28 |
29 | &:not(.less):hover {
30 | color: color(var(--white-color) alpha(85%));
31 | background-color: #3464E0;
32 | }
33 | }
34 | button.check-button{
35 | width: 100%;
36 | margin-top: 20px;
37 | }
38 |
39 | /* --------------------------------
40 | Inputs section
41 | -------------------------------- */
42 |
43 | textarea {
44 | font-weight: 400;
45 | border-radius: 0.5em;
46 | border: 0;
47 | outline: 0;
48 | padding: 1em;
49 | width: 100%;
50 | font-size: 1em;
51 | background-color: color(var(--blue-color) alpha(5%));
52 | color: var(--blue-color);
53 | resize: vertical;
54 | }
55 |
56 | input.field {
57 | width: 100%;
58 | border: 0;
59 | outline: 0;
60 | background-color: #84AEFF;
61 | font-weight: 600;
62 | padding: 4px 12px;
63 | border-radius: 10px;
64 | line-height: 24px;
65 | font-size: 12px;
66 | color: #fff;
67 | transition: color 0.5s easeOne, background-color 0.5s easeOne;
68 |
69 | &::-webkit-input-placeholder {
70 | color: #fff;
71 | }
72 |
73 | &:not([value='']) {
74 | background-color: #84AEFF;
75 | }
76 |
77 | &:focus {
78 | color: var(--white-color);
79 | background-color: #3464E0;
80 |
81 | &::-webkit-input-placeholder {
82 | color: transparent;
83 | }
84 | }
85 | }
86 |
87 | input[type='range'] {
88 | width: 100%;
89 |
90 | }
91 |
92 | /* --------------------------------
93 | Checkboxes section
94 | -------------------------------- */
95 |
96 | .checkbox {
97 | display: inline-flex;
98 | margin: auto;
99 | user-select: none;
100 | cursor: pointer;
101 | padding-top: 30px;
102 | padding-right: 2em;
103 |
104 | &:last-child {
105 | padding-right: 0;
106 | }
107 |
108 | & span.count {
109 | margin-left: 0.5em;
110 | transition: color 0.3s easeOne;
111 | color: color(var(--grey-color) alpha(25%));
112 | }
113 |
114 | & span:not(.count) {
115 | display: inline-block;
116 | vertical-align: middle;
117 | font-weight: 900;
118 | transform: translate3d(0, 0, 0);
119 | color: #3A4368;
120 | transition: all 0.3s easeOne;
121 |
122 | &:first-child {
123 | position: relative;
124 | width: 20px;
125 | height: 20px;
126 | border-radius: 1.25em;
127 | vertical-align: middle;
128 | transition: all 0.3s easeOne;
129 | border: 1px solid #3464E0;
130 |
131 | & svg {
132 | position: absolute;
133 | top: 3px;
134 | left: 2px;
135 | fill: none;
136 | stroke: var(--white-color);
137 | stroke-width: 1.1;
138 | stroke-linecap: round;
139 | stroke-linejoin: round;
140 | stroke-dasharray: 1em;
141 | stroke-dashoffset: 1em;
142 | transition: all 0.3s easeOne;
143 | transform: translate3d(0, 0, 0);
144 | }
145 |
146 | &:before {
147 | content: '';
148 | width: 100%;
149 | height: 100%;
150 | background-color: var(--blue-color);
151 | display: block;
152 | transform: scale(0);
153 | opacity: 1;
154 | border-radius: 50%;
155 | }
156 | }
157 |
158 | &:last-child {
159 | padding-left: 0.5em;
160 | font-size: 14px;
161 | }
162 | }
163 |
164 | &:hover span:first-child {
165 | border-color: var(--blue-color);
166 | }
167 | }
168 |
169 | .input-checkbox {
170 | display: none;
171 | }
172 |
173 | .input-checkbox:checked + .checkbox span.count {
174 | color: color(var(--grey-color) alpha(75%));
175 | }
176 |
177 | .input-checkbox:checked + .checkbox span:not(.count) {
178 | color: #3A4368;
179 | font-size: 14px;
180 | font-weight: 700;
181 |
182 | &:first-child {
183 | background-color: #3464E0;
184 | border-color: #3464E0;
185 |
186 | & svg {
187 | stroke-dashoffset: 0;
188 | }
189 |
190 | &:before {
191 | transform: scale(3.5);
192 | opacity: 0;
193 | transition: all 0.7s easeOne;
194 | }
195 | }
196 | }
197 |
198 | .container.dark {
199 | & ::-webkit-scrollbar-thumb {
200 | background-color: color(var(--neo-black-color) alpha(75%));
201 | }
202 |
203 | & ::-webkit-scrollbar-track {
204 | background-color: color(var(--back-black-color) alpha(15%));
205 | }
206 |
207 | & .checkbox span:not(.count):first-child svg {
208 | stroke: color(var(--neo-black-color) alpha(75%));
209 | }
210 |
211 | & span.count {
212 | color: color(#ddd alpha(25%));
213 | }
214 |
215 | & .input-checkbox:checked + .checkbox span.count {
216 | color: color(#ddd alpha(65%));
217 | }
218 | }
219 | .block-list-btn{
220 |
221 | margin: 0;
222 | padding: 0 3px;
223 | }
--------------------------------------------------------------------------------
/public/styles/Footer.postcss:
--------------------------------------------------------------------------------
1 | footer {
2 | padding-top: 30px;
3 | padding-left: 50px;
4 | padding-right: 50px;
5 | display: flex;
6 | flex-flow: row wrap;
7 | width: 100%;
8 | margin: auto;
9 | background-color: #E9F6FF;
10 | position: fixed;
11 | bottom: 0;
12 | font-size: 12px;
13 | & .links {
14 | margin-right: 1.5em;
15 | display: flex;
16 | flex-flow: row wrap;
17 | align-items: center;
18 |
19 | & a {
20 | display: flex;
21 | justify-content: center;
22 | align-items: center;
23 | margin-right: 2em;
24 | color: color(var(--grey-color) alpha(80%));
25 | text-decoration: none;
26 |
27 | &:hover {
28 | color: var(--grey-color);
29 |
30 | }
31 |
32 | & span {
33 | font-weight: 600;
34 | margin-left: 0.5em;
35 | transition: color 0.5s easeOne;
36 | }
37 |
38 | & svg {
39 | width: 20px;
40 | height: 20px;
41 | fill: #3464E0;
42 | transition: fill 0.5s easeOne;
43 | }
44 | }
45 | }
46 |
47 | & .get-em {
48 | display: flex;
49 | align-items: center;
50 | margin-left: auto;
51 | animation: fade-footer-get-em 0.7s easeOne both;
52 |
53 | & p {
54 | margin: 0;
55 | border-radius: 1.25em;
56 | color: var(--grey-color);
57 |
58 | & a {
59 | display: inline-flex;
60 | align-items: center;
61 | margin: 0 0.25em;
62 | text-decoration: underline;
63 | font-weight: 700;
64 | transition: color 0.5s easeOne;
65 | color: #3464E0;
66 |
67 | &:hover {
68 | color: var(--blue-color);
69 |
70 | & svg {
71 | fill: var(--blue-color);
72 | }
73 | }
74 |
75 | & svg {
76 | width: 1em;
77 | height: 1em;
78 | margin-left: 0.35em;
79 | transition: fill 0.5s easeOne;
80 | fill: color(var(--blue-color) alpha(70%));
81 | }
82 | }
83 | }
84 | }
85 |
86 | & .ops {
87 | display: flex;
88 | width: 100%;
89 | margin: 20px auto 0 auto;
90 | padding: 10px 30px;
91 | font-weight: 600;
92 | border-top-left-radius: 10px;
93 | border-top-right-radius: 10px;
94 | position: relative;
95 | color: #f0f2fa;
96 | background: #3A4368;
97 | align-items: center;
98 | justify-content: space-between;
99 | flex-direction: row-reverse;
100 |
101 |
102 | & .d {
103 | margin-left: auto;
104 | }
105 |
106 | &:before {
107 | content: attr(data-version);
108 | color: #fff;
109 | font-size: 12px;
110 | }
111 |
112 | & a {
113 | text-decoration: none;
114 | color: #f0f2fa;
115 | transition: color 0.5s cubic-bezier(0.23, 1, 0.32, 1);
116 | font-size: 15px;
117 | font-weight: 700;
118 |
119 | &:nth-child(1):hover {
120 | color: var(--blue-color);
121 |
122 | & svg {
123 | fill: var(--blue-color);
124 | }
125 | }
126 |
127 | &:nth-child(2):hover {
128 | color: var(--blue-color);
129 |
130 | & svg {
131 | fill: var(--blue-color);
132 | }
133 | }
134 |
135 | & svg {
136 | transition: fill 0.5s easeOne;
137 | fill: var(--white-color-back);
138 | }
139 | }
140 | }
141 | }
142 |
143 | @keyframes fade-footer-get-em {
144 | from {
145 | opacity: 0;
146 | transform: translateX(15%);
147 | }
148 | to {
149 | opacity: 1;
150 | transform: translateX(0);
151 | }
152 | }
153 |
154 | .container.dark footer {
155 | & .ops:before {
156 | background-color: color(var(--back-black-color) alpha(85%));
157 | }
158 |
159 | & .ops a {
160 | color: var(--back-black-color);
161 |
162 | &:nth-child(1):hover {
163 | color: var(--blue-color);
164 |
165 | & svg {
166 | fill: var(--blue-color);
167 | }
168 | }
169 |
170 | &:nth-child(2):hover {
171 | color: var(--blue-color);
172 |
173 | & svg {
174 | fill: var(--blue-color);
175 | }
176 | }
177 |
178 | & svg {
179 | fill: var(--back-black-color);
180 | }
181 | }
182 |
183 | & .links a {
184 | color: color(var(--blue-color) alpha(60%));
185 |
186 | &:hover {
187 | color: #3464E0;
188 |
189 | & svg {
190 | fill: var(--blue-color);
191 | }
192 | }
193 |
194 | & svg {
195 | fill: color(var(--blue-color) alpha(60%));
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/public/styles/Input.postcss:
--------------------------------------------------------------------------------
1 | .proxy-input {
2 | display: flex;
3 | flex-flow: column wrap;
4 | border-top: 1px solid var(--white-color-back);
5 |
6 | & section.load-file-area {
7 | display: flex;
8 | justify-content: space-between;
9 | user-select: none;
10 | margin-bottom: 20px;
11 | align-items: center;
12 | width: 76%;
13 | background: #fff;
14 | padding: 15px 25px 30px 25px;
15 | border-radius: 10px;
16 | font-size: 14px;
17 | @media screen and (max-width: 960px) {
18 | width: 100%;
19 | }
20 |
21 | & .wrap {
22 | width: calc(50% - 1em);
23 | & .title {
24 | color: #757DA1;
25 | padding-bottom: 30px;
26 | font-size: 12px;
27 | font-weight: 700;
28 | }
29 | & .text-underline{
30 | text-decoration: underline;
31 | }
32 | & .from-clipboard {
33 | padding: 8px;
34 | border-radius: 0.5rem;
35 | font-weight: 700;
36 | color: var(--white-color);
37 | white-space: nowrap;
38 | background-color: #84AEFF;
39 | margin-bottom: 20px;
40 | cursor: pointer;
41 | text-align: center;
42 | transition: background-color 0.5s easeOne, color 0.5s easeOne;
43 |
44 | &:hover {
45 | color: color(var(--white-color) alpha(95%));
46 | background-color: #3464E0;
47 |
48 | & svg {
49 | fill: color(var(--white-color) alpha(95%));
50 | }
51 | }
52 | }
53 |
54 | & .select-event {
55 | min-height: 11em;
56 | display: flex;
57 | background-color: #84AEFF;
58 | justify-content: center;
59 | align-items: center;
60 | border-radius: 0.5em;
61 | color: var(--white-color);
62 | font-weight: 700;
63 | cursor: pointer;
64 | transition: background-color 0.5s easeOne, color 0.5s easeOne;
65 |
66 | &:hover {
67 | color: color(var(--white-color) alpha(95%));
68 | background-color: #3464E0;
69 |
70 | & svg {
71 | fill: color(var(--white-color) alpha(95%));
72 | }
73 | }
74 |
75 | & svg {
76 | fill: var(--white-color);
77 | margin-right: 1em;
78 | transition: fill 0.5s easeOne;
79 | }
80 | }
81 | }
82 |
83 | & .data {
84 | width: calc(50% - 1em);
85 | animation: fade-left 0.7s easeOne both;
86 | overflow: hidden;
87 |
88 | & .stat {
89 | margin-top: 1em;
90 |
91 | & .item {
92 | padding-bottom: 0.5em;
93 |
94 | &:last-child {
95 | padding-bottom: 0;
96 | }
97 |
98 | & div {
99 |
100 | font-size: 0.95em;
101 |
102 |
103 |
104 | white-space: nowrap;
105 |
106 | &:last-child {
107 | overflow: hidden;
108 | white-space: nowrap;
109 | text-overflow: ellipsis;
110 | }
111 |
112 | &:nth-child(1) {
113 | margin-right: 0.5em;
114 | color: #757DA1;
115 | }
116 |
117 | &:nth-child(2) {
118 | color: #3464E0;
119 | font-weight: 700;
120 | font-size: 1em;
121 |
122 | }
123 | }
124 | }
125 | }
126 |
127 | & label.checkbox {
128 | padding: 0;
129 | }
130 | }
131 |
132 | & svg.metrics-svg {
133 | width: 20px;
134 | height: 20px;
135 | margin: auto;
136 | fill: #3464E0;
137 | }
138 |
139 | & .preview {
140 | width: calc(50% - 1em);
141 | }
142 | }
143 | }
144 |
145 | @keyframes fade-left {
146 | from {
147 | opacity: 0;
148 | transform: translateX(-5%);
149 | }
150 | to {
151 | opacity: 1;
152 | transform: translateX(0);
153 | }
154 | }
155 |
156 | .container.dark .proxy-input {
157 | border-top: 1px solid color(var(--white-color) alpha(5%));
158 |
159 | & section.load-file-area {
160 | & .from-clipboard {
161 | color: color(var(--neo-black-color) alpha(75%));
162 |
163 | &:hover {
164 | color: color(var(--neo-black-color) alpha(95%));
165 | }
166 | }
167 |
168 | & .select-event {
169 | color: color(var(--neo-black-color) alpha(75%));
170 |
171 | &:hover {
172 | color: color(var(--neo-black-color) alpha(95%));
173 |
174 | & svg {
175 | fill: color(var(--neo-black-color) alpha(95%));
176 | }
177 | }
178 |
179 | & svg {
180 | fill: color(var(--neo-black-color) alpha(75%));
181 | }
182 | }
183 |
184 | & .data .stat .item span {
185 | color: color(var(--neo-black-color) alpha(85%));
186 |
187 | &:nth-child(2) {
188 | color: color(var(--blue-color) alpha(80%));
189 | background-color: color(var(--grey-color) alpha(35%));
190 | }
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/public/styles/Main.postcss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Lato';
3 | font-style: normal;
4 | font-weight: 300;
5 | font-display: auto;
6 | src: local('Lato Light'), local('Lato-Light'), url('../fonts/Lato-Light.ttf') format('truetype');
7 | }
8 |
9 | @font-face {
10 | font-family: 'Lato';
11 | font-style: normal;
12 | font-weight: 400;
13 | font-display: auto;
14 | src: local('Lato Regular'), local('Lato-Regular'), url('../fonts/Lato-Regular.ttf') format('truetype');
15 | }
16 |
17 | @font-face {
18 | font-family: 'Lato';
19 | font-style: normal;
20 | font-weight: 700;
21 | font-display: auto;
22 | src: local('Lato Bold'), local('Lato-Bold'), url('../fonts/Lato-Bold.ttf') format('truetype');
23 | }
24 |
25 | body {
26 | margin: 0;
27 | font-family: 'Lato', monospace;
28 | text-rendering: geometricPrecision;
29 | -webkit-font-smoothing: antialiased;
30 | counter-reset: items-counter;
31 | }
32 |
33 | .no-select {
34 | user-select: none;
35 | }
36 |
37 | input,
38 | textarea,
39 | button {
40 | font-family: 'Lato', monospace;
41 | text-rendering: geometricPrecision;
42 | -webkit-font-smoothing: antialiased;
43 | }
44 |
45 | *,
46 | :after,
47 | :before {
48 | -webkit-box-sizing: border-box;
49 | -moz-box-sizing: border-box;
50 | box-sizing: border-box;
51 | }
52 |
53 | #root {
54 | position: relative;
55 | overflow: hidden;
56 | }
57 |
58 | .container {
59 | background-color: var(--white-color-back);
60 | }
61 |
62 | .main-page-container {
63 | width: 100%;
64 | height: calc(100% - 2em);
65 | top: 2em;
66 | left: 0;
67 | overflow-y: hidden;
68 | overflow-x: hidden;
69 | background-color: var(--white-color-back);
70 |
71 | & .main-page-content {
72 | width: 100%;
73 | margin-bottom: 105.25px;
74 | overflow-y: auto;
75 | height: calc(100vh - 2rem - 105.25px);
76 | padding: 50px 40px 0 40px;
77 | background-color: #f5f6fa;
78 | border-radius: 0 0 1em 1em;
79 | }
80 | }
81 |
82 | .container.dark {
83 | background-color: var(--grey-color);
84 |
85 | & .main-page-container {
86 | background-color: var(--back-black-color);
87 |
88 | & .main-page-content {
89 | background-color: var(--neo-black-color);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/public/styles/Modal.postcss:
--------------------------------------------------------------------------------
1 | .modal-info-wrap {
2 | position: fixed;
3 | width: 100%;
4 | height: calc(100% - 2em);
5 | top: 2em;
6 | left: 0;
7 | opacity: 0;
8 | visibility: hidden;
9 | overflow: auto;
10 | z-index: 5;
11 | background: rgba(0, 0, 0, 0.5);
12 | transition: opacity 0.7s easeOne, visibility 0.7s easeOne;
13 |
14 | &.active {
15 | opacity: 1;
16 | visibility: visible;
17 |
18 | & .modal-content {
19 | transform: translateX(0);
20 | opacity: 1;
21 | }
22 | }
23 |
24 | & .modal-content {
25 |
26 | width: 80%;
27 | position: absolute;
28 | transition: transform 0.5s easeOne, opacity 0.5s easeOne;
29 | transform: translateX(50px);
30 | opacity: 1;
31 | background: #fff;
32 | padding: 6em 2em 2em 2em;
33 | top: 5%;
34 | left: 10%;
35 | border-radius: 10px;
36 |
37 | & .close-svg {
38 | width: 20px;
39 | height: 20px;
40 | margin-right: 0.5em;
41 | fill: #3464E0;
42 | position:absolute;
43 | right: 20px;
44 | top: 20px;
45 |
46 | &:hover {
47 | cursor: pointer;
48 | }
49 | }
50 |
51 | & .section {
52 | align-items: center;
53 | margin: 0 auto 2em auto;
54 | float: left;
55 | color: #3A4368;
56 | font-size: 14px;
57 | &.rel {
58 | margin-top: 6em;
59 | }
60 |
61 | & svg {
62 | width: 3em;
63 | height: 3em;
64 | margin-right: 0.5em;
65 | fill: #3464E0;
66 | }
67 | & .title{
68 | display:flex;
69 | font-size: 12px;
70 | }
71 | & .title-name{
72 | font-size: 16px;
73 | font-weight: 700;
74 | color: black;
75 | }
76 |
77 | & .content{
78 | margin-top: 20px;
79 | }
80 | & .item-list{
81 | display: flex;
82 | padding: 3px;
83 |
84 | & div{
85 | margin-top: -3px;
86 | }
87 | }
88 | }
89 | & .section-description{
90 | width: 30%;
91 |
92 | }
93 | & .section-permissions{
94 | width: 20%;
95 | margin-left: 5%;
96 |
97 | & svg {
98 | width: 20px;
99 | height: 20px;
100 | min-width: 20px;
101 | margin-right: 8px;
102 | fill: none;
103 | stroke: #3464E0;
104 | stroke-width: 1.5;
105 | stroke-linecap: round;
106 | stroke-linejoin: round;
107 | stroke-dashoffset: 10px;
108 | }
109 | }
110 | & .section-limitations{
111 | width: 15%;
112 | margin-left: 5%;
113 | & svg {
114 | width: 20px;
115 | height: 20px;
116 | min-width: 20px;
117 | margin-right: 8px;
118 | fill: #3A4368;
119 | }
120 | }
121 | & .section-conditions{
122 | width: 20%;
123 | margin-left: 5%;
124 | & svg {
125 | width: 20px;
126 | height: 20px;
127 | min-width: 20px;
128 | margin-right: 8px;
129 | fill: #3464E0;
130 | }
131 | }
132 |
133 | & .section-footer{
134 | overflow-y: scroll;
135 | padding: 25px;
136 | margin-top: 20px;
137 | max-height: 250px;
138 | background: #3A4368;
139 | color: #fff;
140 | border-radius: 10px;
141 | }
142 |
143 | }
144 | }
--------------------------------------------------------------------------------
/public/styles/Notification.postcss:
--------------------------------------------------------------------------------
1 | .modal-notification-wrap {
2 | position: fixed;
3 | width: 350px;
4 | height: 150px;
5 | bottom: 50px;
6 | right: 50px;
7 | opacity: 0;
8 | visibility: hidden;
9 | overflow: auto;
10 | z-index: 5;
11 | background: #c1c1c1;
12 | border-radius: 10px;
13 | transition: opacity 0.7s easeOne, visibility 0.7s easeOne;
14 | padding: 10px 20px 0 20px;
15 |
16 | &.active {
17 | opacity: 1;
18 | visibility: visible;
19 |
20 | }
21 |
22 | & .modal-header{
23 | padding: 10px;
24 | }
25 | & .close-svg {
26 | width: 20px;
27 | with: 20px;
28 | position: absolute;
29 | top: 10px;
30 | right: 20px;
31 | fill: #3464E0;
32 | cursor: pointer;
33 | }
34 | & .modal-content {
35 | font-size: 14px;
36 | }
37 |
38 | & .modal-footer {
39 | & button{
40 | margin-right: 0;
41 | }
42 |
43 | & .disable-check{
44 | cursor: pointer;
45 | padding: 7px 20px;
46 | font-size: 14px;
47 | text-align: center;
48 | }
49 | }
50 |
51 |
52 | }
--------------------------------------------------------------------------------
/public/styles/OverlayIp.postcss:
--------------------------------------------------------------------------------
1 | .ip-lookup {
2 | position: fixed;
3 | visibility: hidden;
4 | display: flex;
5 | align-items: center;
6 | justify-content: center;
7 | opacity: 0;
8 | width: 100%;
9 | height: calc(100% - 2em);
10 | top: 2em;
11 | left: 0;
12 | transition: visibility 0.5s easeOne, opacity 0.5s easeOne;
13 | background-color: color(var(--white-color) alpha(85%));
14 |
15 | &.opened {
16 | visibility: visible;
17 | opacity: 1;
18 | }
19 |
20 | & .checking-status {
21 | font-weight: 700;
22 | color: var(--white-color);
23 |
24 | & svg {
25 | margin: 1.25em;
26 | fill: color(var(--blue-color) alpha(65%));
27 | width: 1.5em;
28 | height: 1.5em;
29 | }
30 |
31 | &.done {
32 | & .ip-address {
33 | opacity: 1;
34 | transform: translateY(0);
35 | }
36 | }
37 |
38 | & .ip-address {
39 | opacity: 0;
40 | padding: 1em 2em;
41 | border-radius: 1.5em;
42 | background-color: color(var(--blue-color) alpha(35%));
43 | transform: translateY(-10%);
44 | transition: opacity 0.87s easeOne, transform 0.87s easeOne;
45 | }
46 | }
47 | }
48 |
49 | .container.dark .ip-lookup {
50 | background-color: color(var(--neo-black-color) alpha(95%));
51 |
52 | & .ip-address {
53 | color: color(var(--neo-black-color) alpha(85%));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/public/styles/OverlayJudges.postcss:
--------------------------------------------------------------------------------
1 | .ping-judges-container {
2 | position: fixed;
3 | display: flex;
4 | flex-flow: column nowrap;
5 | align-items: center;
6 | padding: 2em;
7 | width: 100%;
8 | height: calc(100% - 2em);
9 | top: 2em;
10 | left: 0;
11 | overflow-y: auto;
12 | background-color: color(var(--white-color) alpha(95%));
13 | transition: opacity 0.7s easeOne, visibility 0.7s easeOne, z-index 0.7s easeOne;
14 | opacity: 0;
15 | visibility: hidden;
16 | z-index: -1;
17 |
18 | &.opened {
19 | opacity: 1;
20 | z-index: 3;
21 | visibility: visible;
22 |
23 | & .ping-judges-content {
24 | visibility: visible;
25 | opacity: 1;
26 | transform: translateY(0%);
27 | }
28 | }
29 |
30 | & .ping-judges-content {
31 | width: 80%;
32 | opacity: 0;
33 | transform: translateY(-10%);
34 | background: #fff;
35 | border-radius: 10px;
36 | padding: 15px 25px 10px 25px;
37 |
38 | & .ping-progress {
39 | display: inline-flex;
40 | font-size: 12px;
41 | font-weight: 600;
42 | user-select: none;
43 | border-radius: 10px;
44 | border: 1px solid transparent;
45 | color: #757DA1;
46 |
47 | & .ping-counter {
48 | padding: 0 20px;
49 | font-size: 13px;
50 | font-weight: 700;
51 | color: #3A4368;
52 | }
53 | }
54 |
55 | & .items {
56 | margin-top: 30px;
57 | }
58 |
59 | & .item {
60 | font-size: 14px;
61 | display: flex;
62 | flex-flow: row wrap;
63 | padding: 1.15em 0;
64 | height: 4em;
65 | align-items: center;
66 | font-weight: 700;
67 | transition: color 0.7s easeOne;
68 | color: color(var(--grey-color) alpha(65%));
69 | border-bottom: 1px dotted #C1C1C1;
70 |
71 | & svg {
72 | width: 24px;
73 | height: 24px;
74 | margin-right: 0.75em;
75 | fill: color(var(--grey-color) alpha(65%));
76 | transition: fill 0.7s easeOne;
77 | }
78 |
79 | &.success {
80 | color: #3464E0;
81 |
82 | & svg {
83 | fill: #3464E0;
84 | }
85 |
86 | & .response {
87 | color: var(--white-color);
88 | background-color: #3464E0;
89 | }
90 | }
91 |
92 | &.error {
93 | color: #3A4368;
94 |
95 | & svg {
96 | fill: #3A4368;
97 | }
98 |
99 | & .response {
100 | color: var(--white-color);
101 | background-color: #3A4368;
102 | }
103 | }
104 |
105 | &:last-child {
106 | border-bottom: none;
107 | }
108 |
109 | & .url {
110 | white-space: nowrap;
111 | overflow: hidden;
112 | text-overflow: ellipsis;
113 | margin-top: -9px;
114 | }
115 |
116 | & .status {
117 | margin-left: auto;
118 |
119 | & svg path {
120 | fill: color(var(--grey-color) alpha(65%));
121 | }
122 |
123 | & .response {
124 | display: inline-block;
125 | padding: 2px 20px;
126 | border-radius: 10px;
127 | font-size: 12px;
128 | line-height: 24px;
129 | font-weight: 700;
130 | animation: fade-judge-status 0.7s easeOne;
131 | text-align: center;
132 | }
133 | }
134 | }
135 | }
136 | }
137 |
138 | @keyframes fade-judge-status {
139 | from {
140 | opacity: 0;
141 | transform: translateY(50%);
142 | }
143 | to {
144 | opacity: 1;
145 | transform: translateY(0);
146 | }
147 | }
148 |
149 | .container.dark .ping-judges-container {
150 | background-color: color(var(--neo-black-color) alpha(95%));
151 |
152 | & .ping-judges-content {
153 | & .ping-progress {
154 | color: color(#ddd alpha(50%));
155 | background-color: #fff;
156 | }
157 |
158 | & .items {
159 | border-top: 1px solid color(var(--white-color) alpha(5%));
160 | }
161 |
162 | & .item {
163 | color: color(#ddd alpha(50%));
164 | border-bottom: 1px solid color(var(--white-color) alpha(5%));
165 |
166 | &:last-child {
167 | border-bottom: none;
168 | }
169 |
170 | & svg {
171 | fill: color(#ddd alpha(50%));
172 | }
173 |
174 | &.success {
175 | color: color(var(--blue-color) alpha(65%));
176 |
177 | & svg {
178 | fill: color(var(--blue-color) alpha(65%));
179 | }
180 | }
181 |
182 | &.error {
183 | color: color(var(--red-color) alpha(65%));
184 |
185 | & svg {
186 | fill: color(var(--red-color) alpha(65%));
187 | }
188 | }
189 | }
190 |
191 | & .response {
192 | color: color(var(--neo-black-color) alpha(85%));
193 | }
194 |
195 | & .status svg path {
196 | fill: color(#ddd alpha(50%));
197 | }
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/public/styles/ResultItemsHeader.postcss:
--------------------------------------------------------------------------------
1 | .result-container .result-list-header {
2 | display: flex;
3 | flex-flow: row nowrap;
4 | align-items: center;
5 | color: #757DA2;
6 | font-weight: 600;
7 | font-size: 12px;
8 | line-height: 24px;
9 | user-select: none;
10 | margin: 35px 0;
11 |
12 | & span {
13 | white-space: nowrap;
14 | text-overflow: ellipsis;
15 |
16 | &.main {
17 | display: flex;
18 | flex-flow: row nowrap;
19 | align-content: center;
20 | align-items: center;
21 | cursor: pointer;
22 | height: 4em;
23 |
24 | & span {
25 | transition: color 0.3s easeOne;
26 | }
27 |
28 | & svg.sort-svg {
29 | opacity: 0;
30 | transform: translateY(-35%);
31 | margin-left: 0.75em;
32 | transition: opacity 0.5s easeOne, transform 0.5s easeOne;
33 | }
34 |
35 | &:hover {
36 | color: #3464E0;
37 |
38 | & svg.sort-svg {
39 | opacity: 1;
40 | transform: translateY(0%);
41 |
42 | & path {
43 | fill: #3464E0;
44 | }
45 | }
46 | }
47 | }
48 | }
49 |
50 | & svg.count {
51 | width: 1.25em;
52 | height: 1.25em;
53 | fill: color(var(--grey-color) alpha(75%));
54 | }
55 |
56 | & svg {
57 | width: 1em;
58 | height: 1em;
59 |
60 | & path,
61 | & rect {
62 | transition: fill 0.5s easeOne;
63 | fill: color(var(--grey-color) alpha(75%));
64 | }
65 | }
66 |
67 | & .blacklist svg,
68 | & .timeout svg {
69 | width: 1.5em;
70 | height: 1.5em;
71 | }
72 |
73 | & .count {
74 | width: 5%;
75 | height: 4em;
76 | display: flex;
77 | align-items: center;
78 | }
79 |
80 | & .ip {
81 | width: 15%;
82 | }
83 |
84 | & .port {
85 | width: 10%;
86 | }
87 |
88 | & .protocols {
89 | width: 12.5%;
90 | }
91 |
92 | & .anon {
93 | width: 12.5%;
94 | }
95 |
96 | & .country {
97 | width: 20%;
98 | }
99 |
100 | & .empty-blacklist-placeholder {
101 | width: 5%;
102 | }
103 |
104 | & .blacklist {
105 | width: 5%;
106 | height: 4em;
107 | cursor: pointer;
108 | display: flex;
109 | align-items: center;
110 |
111 | &:hover {
112 | & svg path:nth-child(1) {
113 | fill: #f3705a;
114 | }
115 |
116 | & svg path:nth-child(2) {
117 | fill: #ffd15c;
118 | }
119 | }
120 | }
121 |
122 | & .empty-keep-alive-placeholder {
123 | width: 5%;
124 | }
125 |
126 | & .k-a {
127 | width: 5%;
128 |
129 | & svg {
130 | width: 20px;
131 | height: 20px;
132 |
133 | & path:nth-child(2) {
134 | fill: color(var(--grey-color) alpha(65%));
135 | }
136 | }
137 |
138 | &:hover path:nth-child(1) {
139 | fill: color(var(--blue-color) alpha(25%));
140 | }
141 | }
142 |
143 | & .server {
144 | width: 7.5%;
145 | }
146 |
147 | & .timeout {
148 | width: 7.5%;
149 | margin-left: auto;
150 |
151 | & svg {
152 | margin-left: auto;
153 | }
154 |
155 | &:hover {
156 | & svg path:nth-child(1) {
157 | fill: #7383bf;
158 | }
159 |
160 | & svg path:nth-child(2),
161 | & svg rect:nth-child(3),
162 | & svg rect:nth-child(5) {
163 | fill: #424a60;
164 | }
165 |
166 | & svg path:nth-child(4),
167 | & svg path:nth-child(6) {
168 | fill: #556080;
169 | }
170 |
171 | & svg path:nth-child(7),
172 | & svg path:nth-child(8) {
173 | fill: #7fabda;
174 | }
175 | }
176 | }
177 | }
178 |
179 | .container.dark .result-container .result-list-header {
180 | border-top: 1px solid color(var(--white-color) alpha(5%));
181 |
182 | & span.main {
183 | color: color(#ddd alpha(65%));
184 |
185 | &:hover {
186 | color: #3464E0;
187 | }
188 | }
189 |
190 | & svg.count {
191 | width: 1.25em;
192 | height: 1.25em;
193 | fill: color(#ddd alpha(65%));
194 | }
195 |
196 | & svg {
197 | & path,
198 | & rect {
199 | fill: color(#ddd alpha(65%));
200 | }
201 | }
202 |
203 | & .k-a svg path:nth-child(2) {
204 | fill: color(var(--grey-color) alpha(95%));
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/public/styles/Titlebar.postcss:
--------------------------------------------------------------------------------
1 | .titlebar {
2 | background-color: var(--white-color-back);
3 | height: 2em;
4 | display: flex;
5 | align-items: center;
6 | position: relative;
7 | z-index: 5;
8 | -webkit-app-region: drag;
9 | user-select: none;
10 | padding-left: 1em;
11 | color: var(--grey-color);
12 |
13 | &.dark {
14 | background-color: var(--back-black-color);
15 | }
16 |
17 | & svg {
18 | color: #4888C7;
19 | fill: #4888C7;
20 | }
21 |
22 | & span.title {
23 | font-size: 15px;
24 | display: inline-flex;
25 | animation: color-change 6s infinite;
26 | font-weight: 700;
27 | }
28 |
29 | & .misc {
30 | display: flex;
31 | align-items: center;
32 | height: 100%;
33 | margin-left: auto;
34 | }
35 | & ul.buttons {
36 | display: flex;
37 | list-style: none;
38 | height: 100%;
39 | position: absolute;
40 | right: 0;
41 |
42 | & svg {
43 | height: 0.75em;
44 |
45 | &.minimize {
46 | padding: 0.15em;
47 | }
48 | }
49 |
50 | & li {
51 | align-items: center;
52 | display: flex;
53 | justify-content: center;
54 | -webkit-app-region: no-drag;
55 | width: 3em;
56 |
57 | &:hover {
58 | background-color: color(var(--grey-color) a(5%));
59 | }
60 |
61 | &:nth-child(3):hover {
62 | background-color: color(var(--red-color));
63 |
64 | & svg polygon {
65 | fill: var(--white-color-back) !important;
66 | }
67 | }
68 | }
69 | }
70 |
71 | &.dark ul.buttons li {
72 | &:hover {
73 | background-color: color(var(--grey-color) a(35%)) !important;
74 | }
75 |
76 | &:nth-child(3):hover {
77 | background-color: color(var(--red-color)) !important;
78 |
79 | & svg polygon {
80 | fill: var(--back-black-color) !important;
81 | }
82 | }
83 | }
84 | }
85 |
86 | @keyframes color-change {
87 | 0% {
88 | color: #3A4368;
89 | fill: #3A4368;
90 | }
91 | 50% {
92 | color: #5396d8;
93 | fill: #5396d8;
94 | }
95 | 100% {
96 | color: #4888C7;
97 | fill: #4888C7;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/public/styles/Update.postcss:
--------------------------------------------------------------------------------
1 | .update-notify {
2 | position: fixed;
3 | width: 100%;
4 | display: flex;
5 | flex-flow: column nowrap;
6 | align-items: center;
7 | justify-content: center;
8 | left: 0;
9 | height: calc(100% - 2em);
10 | top: 2em;
11 | overflow: auto;
12 | opacity: 1;
13 | transition: opacity 0.7s easeOne, visibility 0.7s easeOne;
14 | background-color: var(--white-color-back);
15 |
16 | &.closed {
17 | opacity: 0;
18 | visibility: hidden;
19 | }
20 |
21 | &.checking {
22 | width: 100%;
23 | height: 100%;
24 | left: 0;
25 | top: 0;
26 |
27 | & .lds-ripple {
28 | opacity: 1;
29 | }
30 | }
31 |
32 | & .lds-ripple {
33 | opacity: 0;
34 | position: fixed;
35 | width: 58px;
36 | height: 58px;
37 | left: calc(50% - 29px);
38 | top: calc(50% - 29px);
39 | border-radius: 100%;
40 |
41 | & div {
42 | position: absolute;
43 | border: 4px solid color(var(--blue-color) alpha(80%));
44 | opacity: 1;
45 | border-radius: 50%;
46 | animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
47 |
48 | &:nth-child(2) {
49 | animation-delay: -0.5s;
50 | }
51 | }
52 | }
53 |
54 | & .transition-wrap {
55 | animation: fade-one 0.7s easeOne;
56 |
57 | & .update-container {
58 | width: 300px;
59 | align-items: center;
60 | justify-content: center;
61 | transform: translateY(0);
62 | border-radius: 0.75em;
63 | background-color: var(--white-color);
64 | background: #E9F6FE;
65 | opacity: 1;
66 | transition: transform 0.7s easeOne;
67 | padding: 2em;
68 | animation: background-color-change 6s infinite;
69 |
70 | & a {
71 | font-weight: 700;
72 | text-decoration: none;
73 | color: color(var(--blue-color) alpha(75%));
74 | background-color: color(var(--blue-color) alpha(25%));
75 | transition: color 0.7s easeOne, background-color 0.7s easeOne;
76 | border-radius: 1.25em;
77 | padding: 0.5em 1.25em;
78 |
79 | &:hover {
80 | color: var(--white-color);
81 | background-color: color(var(--blue-color) alpha(80%));
82 | }
83 | }
84 |
85 | & svg {
86 | width: 1.75em;
87 | height: 1.75em;
88 | margin-right: 0.75em;
89 | fill: var(--white-color);
90 | }
91 |
92 | & span {
93 | padding: 0.25em 1em;
94 | border-radius: 1em;
95 | font-weight: 700;
96 | background: linear-gradient(107deg, color(var(--white-color) alpha(10%)), transparent);
97 | color: var(--white-color);
98 | }
99 |
100 | & .update-info{
101 | display: block;
102 | width: 100%;
103 | padding: 10px 0;
104 | color: #424a60;
105 | font-size: 1rem;
106 | }
107 |
108 | & .update-label{
109 | font-size: .75rem;
110 | }
111 |
112 | & .update-progress{
113 | display: block;
114 | width: 100%;
115 | position: relative;
116 |
117 | & .progress {
118 | display: flex;
119 | overflow: hidden;
120 | font-size: .75rem;
121 | background-color: #e9ecef;
122 | border-radius: 0.25rem;
123 |
124 | & .progress-bar {
125 | display: flex;
126 | flex-direction: column;
127 | justify-content: center;
128 | color: #ffffff;
129 | text-align: center;
130 | white-space: nowrap;
131 | background-color: #3464E0;
132 | transition: width .6s ease;
133 | }
134 | }
135 | }
136 | }
137 | }
138 | }
139 |
140 | @keyframes lds-ripple {
141 | 0% {
142 | top: 28px;
143 | left: 28px;
144 | width: 0;
145 | height: 0;
146 | opacity: 1;
147 | }
148 | 100% {
149 | top: -1px;
150 | left: -1px;
151 | width: 58px;
152 | height: 58px;
153 | opacity: 0;
154 | }
155 | }
156 |
157 | @keyframes background-color-change {
158 | 0% {
159 | background-color: color(var(--blue-color) alpha(35%));
160 | }
161 | 50% {
162 | background-color: color(var(--blue-color) alpha(35%));
163 | }
164 | 100% {
165 | background-color: color(var(--blue-color) alpha(35%));
166 | }
167 | }
168 |
169 | .container.dark .update-notify {
170 | background-color: var(--back-black-color);
171 | }
172 |
--------------------------------------------------------------------------------
/src/actions/BlacklistActions.js:
--------------------------------------------------------------------------------
1 | import { ipcRenderer } from 'electron';
2 | import { BLACKLIST_CHANGE_ITEM_PATH, BLACKLIST_ADD_ITEM, BLACKLIST_REMOVE_ITEM, BLACKLIST_TOGGLE_OPTION, BLACKLIST_SET_ACTIVE_ITEM } from '../constants/ActionTypes';
3 |
4 | export const changePath = (title, path) => ({
5 | type: BLACKLIST_CHANGE_ITEM_PATH,
6 | title,
7 | path
8 | });
9 |
10 | export const selectPath = title => async dispatch => {
11 | const path = await ipcRenderer.invoke('choose-path', 'open');
12 |
13 | if (path) {
14 | dispatch(changePath(title, path));
15 | }
16 | };
17 |
18 | export const add = (title, path) => ({
19 | type: BLACKLIST_ADD_ITEM,
20 | title,
21 | path
22 | });
23 |
24 | export const setActive = (title, active) => ({
25 | type: BLACKLIST_SET_ACTIVE_ITEM,
26 | title,
27 | active
28 | });
29 |
30 | export const remove = title => ({
31 | type: BLACKLIST_REMOVE_ITEM,
32 | title
33 | });
34 |
35 | export const toggleOption = e => ({
36 | type: BLACKLIST_TOGGLE_OPTION,
37 | target: e.target.name
38 | });
39 |
--------------------------------------------------------------------------------
/src/actions/CheckingActions.js:
--------------------------------------------------------------------------------
1 | import Core from '../core';
2 | import Judges from '../core/judges';
3 | import { wait } from '../misc/wait';
4 | import { shuffle } from '../misc/array';
5 | import { isURL, isIP, isPath } from '../misc/regexes';
6 | import { IpLookup } from './OverlayIpActions';
7 | import Blacklist from '../core/blacklist';
8 | import { CHECKING_UP_COUNTER_STATUS, CHECKING_OPEN, CHECKING_OTHER_CHANGES } from '../constants/ActionTypes';
9 |
10 | const validateJudges = (judges, targetProtocols) => {
11 | if (targetProtocols.includes('https') && !judges.some(({ url }) => url.match(/https:\/\//))) {
12 | throw new Error('You have no judges for HTTPS');
13 | }
14 |
15 | if (targetProtocols.some(protocol => ['http'].includes(protocol)) && !judges.some(({ url }) => !url.match(/https:\/\//))) {
16 | throw new Error('You have no judges for HTTP');
17 | }
18 |
19 | if (judges.filter(({ active }) => active).length == 0) {
20 | throw new Error('You have no active judges');
21 | }
22 |
23 | if (judges.every(({ url }) => isURL(url))) {
24 | return true;
25 | }
26 |
27 | throw new Error('Judge URL is not correct');
28 | };
29 |
30 | const validateBlacklist = items => {
31 | if (items.every(({ path }) => isURL(path) || isPath(path))) {
32 | return true;
33 | }
34 |
35 | throw new Error('Blacklist path must be an local path or URL');
36 | };
37 |
38 | const validateInput = list => {
39 | if (list.length > 0) {
40 | return true;
41 | }
42 |
43 | throw new Error('No proxies found');
44 | };
45 |
46 | const transformProtocols = protocols => {
47 | const enabledProtocols = Object.keys(protocols).filter(protocol => protocols[protocol]);
48 |
49 | if (enabledProtocols.length > 0) {
50 | return enabledProtocols;
51 | }
52 |
53 | throw new Error('Select protocols');
54 | };
55 |
56 | export const start = () => async (dispatch, getState) => {
57 | try {
58 | const { core, judges, blacklist, ip, input, checking, overlay } = getState();
59 |
60 | if (overlay.judges.locked || overlay.ip.locked || checking.isOpened) {
61 | return;
62 | }
63 |
64 | const protocols = transformProtocols(core.protocols);
65 | const activeJudges = judges.items.filter(item => item.active);
66 |
67 | validateInput(input.list);
68 | validateJudges(activeJudges, protocols);
69 |
70 | if (blacklist.filter) {
71 | validateBlacklist(blacklist.items);
72 | }
73 |
74 | const proxyList = core.shuffle ? shuffle([...input.list]) : [...input.list];
75 | const initBlacklist = blacklist.filter ? await new Blacklist(blacklist.items) : false;
76 | const initJudges = await new Judges({ swap: judges.swap, items: activeJudges }, protocols);
77 |
78 | if (!initJudges) return;
79 |
80 | const chainCheck = ip => Core.start(proxyList, core, initJudges, protocols, ip, initBlacklist);
81 |
82 | if (isIP(ip.current)) {
83 | chainCheck(ip.current);
84 | } else {
85 | dispatch(IpLookup(chainCheck));
86 | }
87 | } catch (error) {
88 | alert(error);
89 | }
90 | };
91 |
92 | export const stop = () => async (dispatch, getState) => {
93 | const { checking } = getState();
94 |
95 | if (checking.preparing) {
96 | return;
97 | }
98 |
99 | dispatch(
100 | otherChanges({
101 | preparing: true
102 | })
103 | );
104 |
105 | await wait(300);
106 |
107 | Core.stop();
108 | };
109 |
110 | export const otherChanges = state => ({
111 | type: CHECKING_OTHER_CHANGES,
112 | state
113 | });
114 |
115 | export const openChecking = counter => ({
116 | type: CHECKING_OPEN,
117 | counter
118 | });
119 |
120 | export const upCounterStatus = counter => ({
121 | type: CHECKING_UP_COUNTER_STATUS,
122 | counter
123 | });
124 |
--------------------------------------------------------------------------------
/src/actions/CoreActions.js:
--------------------------------------------------------------------------------
1 | import { CORE_CHANGE_OPTION, CORE_TOGGLE_OPTION, CORE_TOGGLE_PROTOCOL } from '../constants/ActionTypes';
2 | import { getMaxThreads } from '../misc/other';
3 |
4 | export const changeOption = e => ({
5 | type: CORE_CHANGE_OPTION,
6 | target: e.target.name,
7 | value: e.target.value
8 | });
9 |
10 | export const toggleOption = e => ({
11 | type: CORE_TOGGLE_OPTION,
12 | target: e.target.name
13 | });
14 |
15 | export const toggleProtocol = e => (dispatch, getState) => {
16 | dispatch({
17 | type: CORE_TOGGLE_PROTOCOL,
18 | protocol: e.target.name
19 | });
20 |
21 | const { core } = getState();
22 | const maxThreads = getMaxThreads(core.protocols);
23 |
24 | if (core.threads > maxThreads) {
25 | dispatch(
26 | changeOption({
27 | target: {
28 | name: 'threads',
29 | value: maxThreads
30 | }
31 | })
32 | );
33 | }
34 | };
35 |
--------------------------------------------------------------------------------
/src/actions/InputActions.js:
--------------------------------------------------------------------------------
1 | import findMixedProxies from '../misc/FindMixedProxies.js';
2 | import { readFile } from 'fs/promises';
3 | import { ipcRenderer } from 'electron';
4 | import { uniq } from '../misc/array';
5 | import { INPUT_SET_LOADED_FILE_DATA } from '../constants/ActionTypes';
6 | import { parse } from 'path';
7 |
8 | export const setLoadedData = nextState => ({
9 | type: INPUT_SET_LOADED_FILE_DATA,
10 | nextState
11 | });
12 |
13 | const getResult = (text, event, getState) => {
14 | try {
15 | // if (event.ctrlKey) {
16 | // const { input } = getState();
17 |
18 | // const totalLines = text.split(/\r?\n/).filter(item => item.length > 0);
19 | // const uniqueLines = uniq([...totalLines, ...input.list]);
20 | // console.log(uniqueLines);
21 | // const { successed: list, failed: errors } = findMixedProxies(uniqueLines);
22 |
23 | // return {
24 | // list,
25 | // errors,
26 | // total: totalLines.length + input.list.length,
27 | // unique: uniqueLines.length
28 | // };
29 | // }
30 |
31 | const totalLines = text.split(/\r?\n/).filter(item => item.length > 0);
32 | const uniqueLines = uniq(totalLines);
33 | const { successed: list, failed: errors } = findMixedProxies(uniqueLines);
34 |
35 | return {
36 | list,
37 | errors,
38 | total: totalLines.length,
39 | unique: uniqueLines.length,
40 | size: text.length
41 | };
42 | } catch (error) {
43 | return {
44 | list: [],
45 | errors: [],
46 | total: 0,
47 | unique: 0
48 | };
49 | }
50 | };
51 |
52 | export const loadFromTxt = event => async (dispatch, getState) => {
53 |
54 | try {
55 | const paths = await ipcRenderer.invoke('choose-multi');
56 |
57 | if (paths) {
58 | let filesText;
59 | const names = [];
60 |
61 | for await (const path of paths) {
62 | filesText += await readFile(path, 'utf8');
63 | names.push(parse(path).base);
64 | }
65 |
66 | const { list, errors, total, unique, size } = getResult(filesText, event, getState);
67 |
68 | if (!list.length) throw new Error('No proxies found');
69 |
70 | dispatch(
71 | setLoadedData({
72 | loaded: true,
73 | list,
74 | errors,
75 | name: names.join(', '),
76 | total,
77 | unique,
78 | size
79 | })
80 | );
81 | }
82 | } catch (error) {
83 | alert(error);
84 | }
85 | };
86 |
87 | export const checkProxy = event => async (dispatch, getState) => {
88 |
89 |
90 | try {
91 |
92 | if (event.target.dataset.file != "") {
93 | let filesText;
94 | const names = [];
95 | let path = `${process.env.USERPROFILE}\\Downloads\\` + event.target.dataset.file;
96 |
97 | filesText = await readFile(path, 'utf8');
98 | names.push(parse(path).base);
99 |
100 |
101 | const { list, errors, total, unique, size } = getResult(filesText, event, getState);
102 |
103 | if (!list.length) throw new Error('No proxies found');
104 |
105 | dispatch(
106 | setLoadedData({
107 | loaded: true,
108 | list,
109 | errors,
110 | name: names.join(', '),
111 | total,
112 | unique,
113 | size
114 | })
115 | );
116 | }
117 | } catch (error) {
118 | alert(error);
119 | }
120 | };
121 |
122 | export const overrideEventDefaults = event => async (dispatch, getState) => {
123 | try {
124 | event.preventDefault();
125 | event.stopPropagation();
126 | } catch (error) {
127 | alert(error);
128 | }
129 | };
130 |
131 | export const onFileDrop = event => async (dispatch, getState) => {
132 |
133 | try {
134 | event.preventDefault();
135 | event.stopPropagation();
136 |
137 |
138 | if (event.dataTransfer.files.length) {
139 |
140 | let filesText;
141 | const names = [];
142 |
143 | for await (const file of event.dataTransfer.files) {
144 | filesText += await readFile(file.path, 'utf8');
145 | names.push(parse(file.path).base);
146 | }
147 |
148 | const { list, errors, total, unique, size } = getResult(filesText, event, getState);
149 |
150 | if (!list.length) throw new Error('No proxies found');
151 |
152 | dispatch(
153 | setLoadedData({
154 | loaded: true,
155 | list,
156 | errors,
157 | name: names.join(', '),
158 | total,
159 | unique,
160 | size
161 | })
162 | );
163 | }
164 |
165 | } catch (error) {
166 | alert(error);
167 | }
168 | };
169 |
170 | export const pasteFromClipboard = event => async (dispatch, getState) => {
171 | try {
172 | const text = await navigator.clipboard.readText();
173 | const { list, errors, total, unique, size } = getResult(text, event, getState);
174 |
175 | if (!list.length) throw new Error('No proxies found');
176 |
177 | dispatch(
178 | setLoadedData({
179 | loaded: true,
180 | list,
181 | errors,
182 | name: 'Clipboard',
183 | total,
184 | unique,
185 | size
186 | })
187 | );
188 | } catch (error) {
189 | alert(error);
190 | }
191 | };
192 |
--------------------------------------------------------------------------------
/src/actions/IpActions.js:
--------------------------------------------------------------------------------
1 | import { IP_CHANGE_OPTION, IP_SET } from '../constants/ActionTypes';
2 |
3 | export const changeOption = e => ({
4 | type: IP_CHANGE_OPTION,
5 | target: e.target.name,
6 | value: e.target.value
7 | });
8 |
9 | export const setIP = ip => ({
10 | type: IP_SET,
11 | ip
12 | });
13 |
--------------------------------------------------------------------------------
/src/actions/JudgesActions.js:
--------------------------------------------------------------------------------
1 | import { JUDGES_CHANGE, JUDGES_ADD, JUDGES_REMOVE, JUDGES_TOGGLE_OPTION } from '../constants/ActionTypes';
2 |
3 | export const change = (url, settings) => ({
4 | type: JUDGES_CHANGE,
5 | url,
6 | settings
7 | });
8 |
9 | export const add = url => ({
10 | type: JUDGES_ADD,
11 | url
12 | });
13 |
14 | export const remove = url => ({
15 | type: JUDGES_REMOVE,
16 | url
17 | });
18 |
19 | export const toggleOption = e => ({
20 | type: JUDGES_TOGGLE_OPTION,
21 | target: e.target.name
22 | });
23 |
--------------------------------------------------------------------------------
/src/actions/MainActions.js:
--------------------------------------------------------------------------------
1 | import { MAIN_TOGGLE_DARK, MAIN_SET_STATS } from '../constants/ActionTypes';
2 |
3 | export const toggleDark = () => ({
4 | type: MAIN_TOGGLE_DARK
5 | });
6 |
7 |
--------------------------------------------------------------------------------
/src/actions/OverlayIpActions.js:
--------------------------------------------------------------------------------
1 | import { getIP } from '../core/ip';
2 | import { wait } from '../misc/wait';
3 | import { isIP } from '../misc/regexes';
4 | import { OVERLAY_IP_CHANGE_LOOKUP_STATUS, OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL } from '../constants/ActionTypes';
5 | import { setIP } from './IpActions';
6 |
7 | export const changeIpLookupStatus = status => ({
8 | type: OVERLAY_IP_CHANGE_LOOKUP_STATUS,
9 | status
10 | });
11 |
12 | export const toInitialState = () => ({
13 | type: OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL
14 | });
15 |
16 | export const IpLookup = chainEvent => async (dispatch, getState) => {
17 | const { ip, overlay } = getState();
18 |
19 | if (overlay.ip.locked) {
20 | return;
21 | }
22 |
23 | dispatch(changeIpLookupStatus({ isActive: true, locked: true }));
24 |
25 | const onError = async () => {
26 | await wait(500);
27 | dispatch(
28 | changeIpLookupStatus({
29 | isLookupDone: true,
30 | isLookupSuccess: false
31 | })
32 | );
33 |
34 | await wait(3000);
35 | dispatch(changeIpLookupStatus({ isActive: false }));
36 | await wait(500);
37 | dispatch(toInitialState());
38 | };
39 |
40 | try {
41 | const response = await getIP(ip.lookupUrl);
42 |
43 | if (!isIP(response)) {
44 | return onError();
45 | }
46 |
47 | await wait(500);
48 | dispatch(
49 | changeIpLookupStatus({
50 | currentIP: response,
51 | isLookupDone: true,
52 | isLookupSuccess: true
53 | })
54 | );
55 |
56 | dispatch(setIP(response));
57 |
58 | await wait(1000);
59 | dispatch(changeIpLookupStatus({ isActive: false }));
60 |
61 | if (chainEvent) {
62 | chainEvent(response);
63 | }
64 |
65 | await wait(500);
66 | dispatch(toInitialState());
67 | } catch {
68 | onError();
69 | }
70 | };
71 |
--------------------------------------------------------------------------------
/src/actions/OverlayJudgesActions.js:
--------------------------------------------------------------------------------
1 | import { OVERLAY_JUDGES_CHANGE_STATE, OVERLAY_JUDGE_CHANGE_PING_STATE } from '../constants/ActionTypes';
2 |
3 | export const changeState = state => ({
4 | type: OVERLAY_JUDGES_CHANGE_STATE,
5 | state
6 | });
7 |
8 | export const startPing = () => (dispatch, getState) => {
9 | const { judges } = getState();
10 |
11 | const parsejudges = judges.items.filter(item => item.active).map(item => ({
12 | url: item.url,
13 | state: {
14 | checking: true,
15 | working: false
16 | }
17 | }));
18 |
19 | dispatch(changeState({ isActive: true, locked: true, items: parsejudges }));
20 | };
21 |
22 | export const changeJudgePingState = (url, state) => ({
23 | type: OVERLAY_JUDGE_CHANGE_PING_STATE,
24 | url,
25 | state
26 | });
27 |
--------------------------------------------------------------------------------
/src/actions/UpdateActions.js:
--------------------------------------------------------------------------------
1 | import { getLatestVersionInfo, sendOnlineInfo } from '../core/updater';
2 | import { wait } from '../misc/wait';
3 | import { UPDATE_CHANGE_STATE } from '../constants/ActionTypes';
4 |
5 | export const checkAtAvailable = () => async dispatch => {
6 | const details = await getLatestVersionInfo();
7 |
8 | sendOnlineInfo();
9 | await wait(500);
10 |
11 | dispatch(
12 | changeUpdateState({
13 | ...(details.available ? { ...details } : { available: false, active: false }),
14 | isChecking: false,
15 | releases: details.releases
16 | })
17 | );
18 | };
19 |
20 | const changeUpdateState = nextState => ({
21 | type: UPDATE_CHANGE_STATE,
22 | nextState
23 | });
24 |
--------------------------------------------------------------------------------
/src/components/BlacklistAddNew.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ipcRenderer } from 'electron';
3 | import DropDocIcon from './ui/DropDocIcon';
4 |
5 | export default class BlacklistAddNew extends React.PureComponent {
6 | state = {
7 | title: '',
8 | path: ''
9 | };
10 |
11 | changeTitle = e => this.setState({ title: e.target.value });
12 |
13 | changePath = e => this.setState({ path: e.target.value });
14 |
15 | selectPath = async () => {
16 | const path = await ipcRenderer.invoke('choose-path', 'open');
17 |
18 | if (path) {
19 | this.setState({ path });
20 | }
21 | };
22 |
23 | add = () => {
24 | if (this.state.title.length > 0 && this.state.path.length > 0) {
25 | const { add } = this.props;
26 | add(this.state.title, this.state.path);
27 | }
28 | };
29 |
30 | render = () => (
31 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/src/components/BlacklistItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Checkbox from './ui/Checkbox';
3 |
4 | import CloseIcon from './ui/CloseIcon';
5 | import DropDocIcon from './ui/DropDocIcon';
6 |
7 | export default class BlacklistItem extends React.PureComponent {
8 | setActive = () => {
9 | const { setActive, active, title, path } = this.props;
10 | const activeState = path.length > 0 ? !active : false;
11 | setActive(title, activeState);
12 | };
13 |
14 | changePath = e => {
15 | const { changePath, setActive, title } = this.props;
16 | const activeState = e.target.value.length > 0 ? true : false;
17 | changePath(title, e.target.value);
18 | setActive(title, activeState);
19 | };
20 |
21 | selectPath = () => {
22 | const { selectPath, title } = this.props;
23 | selectPath(title);
24 | };
25 |
26 | remove = () => {
27 | const { remove, title } = this.props;
28 | remove(title);
29 | };
30 |
31 | render = () => {
32 | const { title, active, path } = this.props;
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | );
48 | };
49 | }
50 |
--------------------------------------------------------------------------------
/src/components/Counter.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CounterProtocol from './CounterProtocol';
3 | import { splitByKK } from '../misc/text';
4 |
5 | import '../../public/styles/Counter.postcss';
6 |
7 | const Counter = ({ all, done, protocols: { http, https, socks4, socks5 } }) => {
8 | const progressStyle = {
9 | width: Math.floor((done / all) * 100) + '%'
10 | };
11 |
12 | return (
13 | <>
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Total Checked: {splitByKK(done)} of {splitByKK(all)}
24 |
25 |
26 | >
27 | );
28 | };
29 |
30 | export default Counter;
31 |
--------------------------------------------------------------------------------
/src/components/CounterProtocol.jsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import { splitByKK } from '../misc/text';
3 |
4 | const CounterProtocol = memo(({ count, name, className }) => {
5 | if (count == undefined) return null;
6 |
7 | return (
8 | 0 ? 'active' : ''}`}>
9 |
{name}:
10 |
{splitByKK(count)}
11 |
12 | );
13 | });
14 |
15 | export default CounterProtocol;
16 |
--------------------------------------------------------------------------------
/src/components/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { currentVersion } from '../core/updater';
3 | import { openLink } from '../misc/other';
4 | import LogoIcon from '../components/ui/LogoIcon';
5 | import GitIcon from '../components/ui/GitIcon';
6 | import DocIcon from '../components/ui/DocIcon';
7 | import LicenseIcon from '../components/ui/LicenseIcon';
8 |
9 | import WhiteLogo from "../../public/icons/Logo-ProxyScrape-white.png";
10 |
11 | import '../../public/styles/Footer.postcss';
12 |
13 | const Footer = ({ toggleModal }) => (
14 |
43 | );
44 |
45 | export default Footer;
46 |
--------------------------------------------------------------------------------
/src/components/JudgesAddNew.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { isURL } from '../misc/regexes';
3 | import FillPlusIcon from './ui/FillPlusIcon';
4 |
5 | export default class JudgesAddNew extends React.PureComponent {
6 | state = {
7 | url: ''
8 | };
9 |
10 | changeUrl = e => this.setState({ url: e.target.value });
11 |
12 | addUrl = () => {
13 | if (this.state.url.length > 0 && isURL(this.state.url)) {
14 | const { add } = this.props;
15 | add(this.state.url);
16 | }
17 | };
18 |
19 | render = () => (
20 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/JudgesItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Checkbox from './ui/Checkbox';
3 | import CloseIcon from './ui/CloseIcon';
4 |
5 | export default class JudgesItem extends React.PureComponent {
6 | toggleActive = () => {
7 | const { change, url, active } = this.props;
8 | change(url, { active: !active });
9 | };
10 |
11 | changeValidateString = e => {
12 | const { change, url } = this.props;
13 | change(url, { validate: e.target.value });
14 | };
15 |
16 | remove = () => {
17 | const { remove, url } = this.props;
18 | remove(url);
19 | };
20 |
21 | render = () => {
22 | const { url, active, validate } = this.props;
23 |
24 | return (
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/LicenseModal.jsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import ReactMarkdown from 'react-markdown';
3 | import { openLink } from '../misc/other';
4 |
5 | import '../../public/styles/Modal.postcss';
6 | import LicenseIcon from './ui/LicenseIcon';
7 | import CloseIcon from './ui/CloseIcon';
8 | import CheckIcon from './ui/CheckIcon';
9 | import XIcon from './ui/XIcon';
10 | import PlusIcon from './ui/PlusIcon';
11 |
12 | const LicenseModal = memo(({ show, toggleModal }) => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
ProxyScrape is licensed under the MIT License
21 |
22 |
23 | A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.
24 |
25 |
26 |
27 |
28 |
Permissions
29 |
43 |
44 |
45 |
46 |
Limitations
47 |
55 |
56 |
57 |
58 |
Conditions
59 |
60 |
61 |
License and copyright notice
62 |
63 |
64 |
65 |
66 |
67 |
MIT License
68 |
69 |
Copyright (c) 2018 assnctr
70 |
71 |
Permission is hereby granted, free of charge, to any person obtaining a copyof this software and associated documentation files (the "Software"), to dealin the Software without restriction, including without limitation the rightsto use, copy, modify, merge, publish, distribute, sublicense, and/or sellcopies of the Software, and to permit persons to whom the Software isfurnished to do so, subject to the following conditions:
72 |
73 |
The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
74 |
75 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
76 |
77 |
78 |
79 |
80 | );
81 | });
82 |
83 | export default LicenseModal;
84 |
--------------------------------------------------------------------------------
/src/components/Main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Settings from './Settings';
3 | import Input from '../containers/Input';
4 | import Checking from '../containers/Checking';
5 | import Overlay from '../containers/Overlay';
6 | import Update from '../containers/Update';
7 | import Footer from './Footer';
8 | import Result from '../containers/Result';
9 |
10 | import '../../public/styles/Main.postcss';
11 | import '../../public/styles/Elements.postcss';
12 |
13 | const Main = () => (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 |
29 | export default Main;
30 |
--------------------------------------------------------------------------------
/src/components/Notification.jsx:
--------------------------------------------------------------------------------
1 | import React, { memo } from 'react';
2 | import ReactMarkdown from 'react-markdown';
3 | import { openLink } from '../misc/other';
4 | import '../../public/styles/Notification.postcss';
5 | import Logo from '../../public/icons/Logo-ProxyScrape-colored.png';
6 | import CloseIcon from './ui/CloseIcon';
7 |
8 | const Notification = ({ show, toggleNotify, fileName, checkProxy, disable }) => {
9 | return (
10 |
11 |
12 |
13 |
14 |
15 |
16 | New proxy list "{fileName}" detected.
17 |
18 |
19 |
20 | Check
21 |
22 |
__Disable notifications__
23 |
24 |
25 | );
26 | };
27 |
28 | export default Notification;
29 |
--------------------------------------------------------------------------------
/src/components/OverlayIp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import '../../public/styles/OverlayIp.postcss';
4 |
5 | const OverlayIp = ({ isActive, currentIP, isLookupDone, isLookupSuccess }) => (
6 |
7 |
8 | {!isLookupDone && (
9 |
10 |
11 |
12 |
13 |
14 |
15 | )}
16 | {
{isLookupDone && (isLookupSuccess ? `Your IP address: ${currentIP}` : 'IP lookup error. Please try change lookup address.')}
}
17 |
18 |
19 | );
20 |
21 | export default OverlayIp;
22 |
--------------------------------------------------------------------------------
/src/components/OverlayJudges.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import '../../public/styles/OverlayJudges.postcss';
4 |
5 | import SearchBarIcon from './ui/SearchBarIcon';
6 |
7 | const OverlayJudges = ({ isActive, items }) => {
8 | const all = items.length;
9 | const done = items.filter(item => !item.state.checking).length;
10 |
11 | return (
12 |
13 |
14 |
15 | Total Checked: {done} of {all}
16 |
17 |
18 |
19 | {items.map(item => (
20 |
21 |
22 |
23 | {item.url}
24 |
25 |
26 | {item.state.checking ? (
27 |
28 |
29 |
30 |
31 |
32 |
33 | ) : (
34 |
{item.state.working ? `${item.state.timeout} ms` : 'Error'}
35 | )}
36 |
37 |
38 | ))}
39 |
40 |
41 |
42 |
43 | );
44 | };
45 |
46 | export default OverlayJudges;
47 |
--------------------------------------------------------------------------------
/src/components/ResultBlacklist.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ResultBlacklistItem from './ResultBlacklistItem';
3 |
4 | const ResultBlacklist = ({ inBlacklists, toggle }) => inBlacklists.map(item => );
5 |
6 | export default ResultBlacklist;
7 |
--------------------------------------------------------------------------------
/src/components/ResultBlacklistItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { CheckboxWithCount } from './ui/Checkbox';
3 |
4 | export default class ResultBlacklistItem extends React.PureComponent {
5 | toggle = () => {
6 | const { toggle, title } = this.props;
7 | toggle(title);
8 | };
9 |
10 | render = () => {
11 | const { active, title, count } = this.props;
12 |
13 | return ;
14 | };
15 | }
16 |
--------------------------------------------------------------------------------
/src/components/ResultCountriesItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class ResultCountriesItem extends React.PureComponent {
4 | toggle = () => {
5 | const { toggle, name, active } = this.props;
6 | toggle(name, false, !active);
7 | };
8 |
9 | toggleAll = () => {
10 | const { toggle, name, active } = this.props;
11 | toggle(name, true, !active);
12 | };
13 |
14 | render = () => {
15 | const { name, active, count, flag } = this.props;
16 |
17 | return (
18 |
19 |
22 |
23 |
{name}
24 |
Proxies: {count}
25 |
26 |
27 | );
28 | };
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/ResultExport.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { getResultsInIpPort, getResultsInProtocolIpPort } from '../actions/ResultActions';
3 |
4 | import CloseIcon from './ui/CloseIcon';
5 | import '../../public/styles/ResultExport.postcss';
6 |
7 | const ResultExport = ({ active, copy, items, type, authType, toggleExport, changeExportType, changeExportAuthType, save }) => {
8 | const hasItemsWithAuth = items.some(item => item.auth !== 'none');
9 |
10 | return (
11 |
12 |
13 |
14 |
15 |
Protocol Type
16 |
17 |
18 |
19 |
20 |
21 | Host :Port
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | Protocol ://Host :Port
31 |
32 |
33 |
34 |
35 |
36 |
37 | {hasItemsWithAuth ? (
38 |
39 |
Auth Type
40 |
41 |
42 |
43 |
44 |
45 | User:Pass @Host:Port
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | Host:Port :User:Pass
54 |
55 |
56 |
57 |
58 |
59 | ) : null}
60 |
61 | Save as .txt
62 | Copy To Clipboard
63 |
64 |
65 |
66 | );
67 | };
68 |
69 | export default ResultExport;
70 |
--------------------------------------------------------------------------------
/src/components/ResultItemData.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export default class ResultItemData extends React.Component {
4 | state = {
5 | full: false
6 | };
7 |
8 | viewFull = () => this.setState({ full: true });
9 |
10 | render = () => {
11 | const { data } = this.props;
12 |
13 | return data ? (
14 |
15 | {data.map(item => (
16 |
17 |
Details
18 |
19 |
Protocol
20 |
{item.protocol}
21 |
22 |
23 |
Anon
24 |
{item.anon}
25 |
26 |
27 |
Judge
28 |
{item.judge}
29 |
30 |
Timings
31 |
32 |
Lookup
33 |
{item.timings.lookup}
34 |
35 |
36 |
Connect
37 |
{item.timings.connect}
38 |
39 |
40 |
Socket
41 |
{item.timings.socket}
42 |
43 |
44 |
Response
45 |
{item.timings.response}
46 |
47 |
48 |
End
49 |
{item.timings.end}
50 |
51 | {this.state.full || item.response.body.length < 800 ? (
52 | <>
53 |
Response
54 |
55 | >
56 | ) : (
57 | <>
58 |
59 | Response
60 |
61 | View full
62 |
63 |
64 |
65 | >
66 | )}
67 |
Headers
68 | {Object.keys(item.response.headers).map(header => (
69 |
70 |
{header}
71 |
{item.response.headers[header]}
72 |
73 | ))}
74 |
75 | ))}
76 |
77 | ) : null;
78 | };
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/ResultListItem.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ResultItemData from './ResultItemData';
3 | import { splitByKK } from '../misc/text';
4 |
5 | export default class ResultListItem extends React.PureComponent {
6 | state = {
7 | isDataOpened: false
8 | };
9 |
10 | toggleOpenData = e => {
11 | e.stopPropagation();
12 |
13 | if (!e.ctrlKey) {
14 | this.setState({ isDataOpened: !this.state.isDataOpened });
15 | }
16 | };
17 |
18 | getClass = ([protocol]) => (protocol.match(/http/) ? 'http' : 'socks');
19 |
20 | render = () => {
21 | const { host, ip, port, protocols, anon, country, timeout, keepAlive, server, data, blacklist } = this.props;
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 | {host} {host !== ip ? {ip} : null}
31 |
32 |
{port}
33 |
34 | {protocols.map(protocol => (
35 | {protocol}
36 | ))}
37 |
38 |
39 | {anon}
40 |
41 |
42 |
45 |
46 |
{country.name}
47 |
48 | {country.city}
49 |
50 |
51 |
52 |
53 | {blacklist && (
54 |
55 | {blacklist.length}
56 |
57 | )}
58 |
59 |
{keepAlive && K-A }
60 |
{server && {server} }
61 |
{splitByKK(timeout)} ms
62 |
63 | {this.state.isDataOpened &&
}
64 |
65 | );
66 | };
67 | }
68 |
--------------------------------------------------------------------------------
/src/components/Settings.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
3 | import Judges from '../containers/Judges';
4 | import Core from '../containers/Core';
5 | import Ip from '../containers/Ip';
6 | import Blacklist from '../containers/Blacklist';
7 |
8 | import '../../public/styles/Settings.postcss';
9 |
10 | const Settings = () => (
11 |
12 |
13 |
14 | Core
15 | Judges
16 | Ip
17 | Blacklist
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | );
34 |
35 | export default Settings;
36 |
--------------------------------------------------------------------------------
/src/components/ui/CheckIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const CheckIcon = () => (
5 |
6 |
7 |
8 | );
9 |
10 | export default CheckIcon;
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/ui/Checkbox.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import CheckIcon from './CheckIcon';
3 |
4 | const Checkbox = ({ id, name, checked, onChange, text }) => (
5 | <>
6 |
7 |
8 |
9 |
10 |
11 | {text}
12 |
13 | >
14 | );
15 |
16 | export const CheckboxWithCount = ({ id, name, checked, onChange, text, count }) => (
17 | <>
18 |
19 |
20 |
21 |
22 |
23 |
24 | {text}
25 | {count}
26 |
27 |
28 | >
29 | );
30 |
31 | export default Checkbox;
32 |
--------------------------------------------------------------------------------
/src/components/ui/CloseIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const CloseIcon = ({ onChange }) => (
5 |
6 |
7 |
8 |
9 |
10 |
11 | );
12 |
13 | export default CloseIcon;
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/components/ui/DocIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const DocIcon = () => (
4 |
9 |
11 |
12 |
13 |
14 | );
15 |
16 | export default DocIcon;
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/ui/DropDocIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const DropDocIcon = ({ scale }) => (
4 |
9 |
11 |
26 |
27 |
28 |
29 | );
30 |
31 | export default DropDocIcon;
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/components/ui/FillPlusIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const FillPlusIcon = () => (
5 |
6 |
14 |
15 | );
16 |
17 | export default FillPlusIcon;
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/ui/GitIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const GitIcon = () => (
4 |
10 |
11 |
12 | );
13 |
14 | export default GitIcon;
15 |
--------------------------------------------------------------------------------
/src/components/ui/LicenseIcon.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const LicenseIcon = () => (
4 |
9 |
11 |
27 |
28 |
29 | );
30 |
31 | export default LicenseIcon;
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/components/ui/PlusIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const PlusIcon = () => (
5 |
6 |
15 |
16 | );
17 |
18 | export default PlusIcon;
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/components/ui/SearchBarIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const SearchBarIcon = () => (
5 |
6 |
16 |
17 | );
18 |
19 | export default SearchBarIcon;
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/ui/SearchIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const SearchIcon = () => (
5 |
6 |
12 |
13 | );
14 |
15 | export default SearchIcon;
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/ui/SortIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const SortIcon = () => (
5 |
6 |
12 |
13 | );
14 |
15 | export default SortIcon;
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/ui/TimeIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const TimeIcon = () => (
5 |
6 |
17 |
18 | );
19 |
20 | export default TimeIcon;
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/components/ui/XIcon.jsx:
--------------------------------------------------------------------------------
1 |
2 | import React from 'react';
3 |
4 | const XIcon = () => (
5 |
6 |
10 |
11 | );
12 |
13 | export default XIcon;
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/constants/ActionTypes.js:
--------------------------------------------------------------------------------
1 | //checking
2 | export const CHECKING_UP_COUNTER_STATUS = 'CHECKING_UP_COUNTER_STATUS';
3 | export const CHECKING_OPEN = 'CHECKING_OPEN';
4 | export const CHECKING_OTHER_CHANGES = 'CHECKING_OTHER_CHANGES';
5 |
6 | //input
7 | export const INPUT_SET_LOADED_FILE_DATA = 'INPUT_SET_LOADED_FILE_DATA';
8 |
9 | //result
10 | export const RESULT_SHOW = 'RESULT_SHOW';
11 | export const RESULT_TOGGLE_ANON = 'RESULT_TOGGLE_ANON';
12 | export const RESULT_TOGGLE_PROTOCOL = 'RESULT_TOGGLE_PROTOCOL';
13 | export const RESULT_TOGGLE_COUNTRY = 'RESULT_TOGGLE_COUNTRY';
14 | export const RESULT_TOGGLE_MISC = 'RESULT_TOGGLE_MISC';
15 | export const RESULT_SET_SEARCH = 'RESULT_SET_SEARCH';
16 | export const RESULT_LOAD_MORE = 'RESULT_LOAD_MORE';
17 | export const RESULT_CLOSE = 'RESULT_CLOSE';
18 | export const RESULT_TOGGLE_BLACKLIST = 'RESULT_TOGGLE_BLACKLIST';
19 | export const RESULT_TOGGLE_COUNTRIES = 'RESULT_TOGGLE_COUNTRIES';
20 | export const RESULT_SET_MAX_TIMEOUT = 'RESULT_SET_MAX_TIMEOUT';
21 | export const RESULT_CHANGE_PORTS_INPUT = 'RESULT_CHANGE_PORTS_INPUT';
22 | export const RESULT_SET_PORTS_ALLOW = 'RESULT_SET_PORTS_ALLOW';
23 | export const RESULT_SORT = 'RESULT_SORT';
24 | export const RESULT_EXPORT_TOGGLE = 'RESULT_EXPORT_TOGGLE';
25 | export const RESULT_EXPORT_CHANGE_TYPE = 'RESULT_EXPORT_CHANGE_TYPE';
26 | export const RESULT_EXPORT_CHANGE_AUTH_TYPE = 'RESULT_EXPORT_CHANGE_AUTH_TYPE';
27 |
28 | //judges
29 | export const JUDGES_CHANGE = 'JUDGES_CHANGE';
30 | export const JUDGES_ADD = 'JUDGES_ADD';
31 | export const JUDGES_REMOVE = 'JUDGES_REMOVE';
32 | export const JUDGES_TOGGLE_OPTION = 'JUDGES_TOGGLE_OPTION';
33 |
34 | //update
35 | export const UPDATE_CHANGE_STATE = 'UPDATE_CHANGE_STATE';
36 |
37 | //overlay ip
38 | export const OVERLAY_IP_CHANGE_LOOKUP_STATUS = 'OVERLAY_IP_CHANGE_LOOKUP_STATUS';
39 | export const OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL = 'OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL';
40 |
41 | //overlay judges
42 | export const OVERLAY_JUDGES_CHANGE_STATE = 'OVERLAY_JUDGES_CHANGE_STATE';
43 | export const OVERLAY_JUDGE_CHANGE_PING_STATE = 'OVERLAY_JUDGE_CHANGE_PING_STATE';
44 |
45 | //core
46 | export const CORE_CHANGE_OPTION = 'CORE_CHANGE_OPTION';
47 | export const CORE_TOGGLE_OPTION = 'CORE_TOGGLE_OPTION';
48 | export const CORE_TOGGLE_PROTOCOL = 'CORE_TOGGLE_PROTOCOL';
49 |
50 | //ip
51 | export const IP_CHANGE_OPTION = 'IP_CHANGE_OPTION';
52 | export const IP_SET = 'IP_SET';
53 |
54 | //blacklist
55 | export const BLACKLIST_CHANGE_ITEM_PATH = 'BLACKLIST_CHANGE_ITEM_PATH';
56 | export const BLACKLIST_ADD_ITEM = 'BLACKLIST_ADD_ITEM';
57 | export const BLACKLIST_REMOVE_ITEM = 'BLACKLIST_REMOVE_ITEM';
58 | export const BLACKLIST_TOGGLE_OPTION = 'BLACKLIST_TOGGLE_OPTION';
59 | export const BLACKLIST_SET_ACTIVE_ITEM = 'BLACKLIST_SET_ACTIVE_ITEM';
60 |
61 | //main
62 | export const MAIN_TOGGLE_DARK = 'MAIN_TOGGLE_DARK';
63 | export const MAIN_SET_STATS = 'MAIN_SET_STATS';
64 |
--------------------------------------------------------------------------------
/src/constants/AppConstants.js:
--------------------------------------------------------------------------------
1 | export const isPortable = process.env.PORTABLE_EXECUTABLE_DIR ? true : false;
2 | export const isDev = process.env.NODE_ENV === 'production' ? false : true;
3 |
--------------------------------------------------------------------------------
/src/constants/SettingsConstants.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import { ipcRenderer } from 'electron';
3 | import { isDev, isPortable } from './AppConstants';
4 |
5 | const userData = ipcRenderer.sendSync('getUserData');
6 |
7 | export const SETTINGS_FILE_NAME = 'settings.proxyscrape.checker.json';
8 | export const SETTINGS_FILE_PATH = isPortable
9 | ? path.resolve(process.env.PORTABLE_EXECUTABLE_DIR, SETTINGS_FILE_NAME)
10 | : isDev
11 | ? path.resolve(SETTINGS_FILE_NAME)
12 | : path.resolve(userData, SETTINGS_FILE_NAME);
13 |
14 | export const DEFAULT_CORE_SETTINGS = {
15 | threads: 350,
16 | keepAlive: false,
17 | timeout: 15000,
18 | retries: 0,
19 | shuffle: false,
20 | captureFullData: false,
21 | captureServer: false,
22 | protocols: {
23 | http: true,
24 | https: true,
25 | socks4: true,
26 | socks5: true
27 | }
28 | };
29 |
30 | export const DEFAULT_JUDGES_SETTINGS = {
31 | swap: true,
32 | items: [
33 | {
34 | active: true,
35 | url: 'http://judge1.api.proxyscrape.com',
36 | validate: 'AZ Environment variables'
37 | },
38 | {
39 | active: true,
40 | url: 'http://judge2.api.proxyscrape.com',
41 | validate: 'AZ Environment variables'
42 | },
43 | {
44 | active: true,
45 | url: 'http://judge3.api.proxyscrape.com',
46 | validate: 'AZ Environment variables'
47 | },
48 | {
49 | active: true,
50 | url: 'http://judge4.api.proxyscrape.com',
51 | validate: 'AZ Environment variables'
52 | },
53 | {
54 | active: true,
55 | url: 'http://judge5.api.proxyscrape.com',
56 | validate: 'AZ Environment variables'
57 | },
58 | {
59 | active: true,
60 | url: 'https://ssl-judge1.api.proxyscrape.com',
61 | validate: 'REMOTE_ADDR = (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
62 | },
63 | {
64 | active: true,
65 | url: 'https://ssl-judge2.api.proxyscrape.com',
66 | validate: 'REMOTE_ADDR = (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
67 | }
68 | ]
69 | };
70 |
71 | export const DEFAULT_IP_SETTINGS = {
72 | current: '',
73 | lookupUrl: 'https://api.proxyscrape.com/ip.php'
74 | };
75 |
76 | export const DEFAULT_BLACKLIST_SETTINGS = {
77 | filter: false,
78 | items: [
79 | {
80 | title: 'Spamhaus DROP',
81 | active: true,
82 | path: 'https://www.spamhaus.org/drop/drop.txt'
83 | },
84 | {
85 | title: 'Spamhaus EDROP',
86 | active: true,
87 | path: 'https://www.spamhaus.org/drop/edrop.txt'
88 | },
89 | {
90 | title: 'MYIP.MS General',
91 | active: true,
92 | path: 'https://myip.ms/files/blacklist/general/latest_blacklist.txt'
93 | }
94 | ]
95 | };
96 |
97 | export const DEFAULT_EXPORTING_SETTINGS = {
98 | type: 1
99 | };
100 |
101 | export const DEFAULT_MAIN_SETTINGS = {
102 | dark: false
103 | };
104 |
105 | export const MERGED_DEFAULT_SETTINGS = {
106 | core: DEFAULT_CORE_SETTINGS,
107 | judges: DEFAULT_JUDGES_SETTINGS,
108 | ip: DEFAULT_IP_SETTINGS,
109 | blacklist: DEFAULT_BLACKLIST_SETTINGS,
110 | exporting: DEFAULT_EXPORTING_SETTINGS,
111 | main: DEFAULT_MAIN_SETTINGS
112 | };
113 |
--------------------------------------------------------------------------------
/src/constants/UpdateConstants.js:
--------------------------------------------------------------------------------
1 | import {v4 as uuidv4} from 'uuid';
2 |
3 | const LATEST_RELEASES_API_PATH = 'https://api.github.com/repos/ProxyScrape/proxy-checker/releases';
4 | const CHECK_ONLINE_API_PATH = 'https://api.proxyscrape.com/v2/analytics/proxy-checker.php';
5 | const SESSION_ID = uuidv4();
6 |
7 | export const FETCH_CONFIG = {
8 | url: LATEST_RELEASES_API_PATH,
9 | json: true,
10 | timeout: 10000,
11 | headers: {
12 | 'User-Agent': 'ProxyScrape Version Lookup'
13 | }
14 | };
15 |
16 | export const CHECK_ONLINE = {
17 | url: CHECK_ONLINE_API_PATH,
18 | json: {
19 | user_online: true,
20 | session_id: SESSION_ID
21 | },
22 | timeout: 10000,
23 | headers: {
24 | 'User-Agent': 'ProxyScrape Version Lookup'
25 | }
26 | }
--------------------------------------------------------------------------------
/src/containers/Blacklist.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import BlacklistItem from '../components/BlacklistItem';
4 | import BlacklistAddNew from '../components/BlacklistAddNew';
5 | import { changePath, add, remove, setActive, toggleOption, selectPath } from '../actions/BlacklistActions';
6 | import Checkbox from '../components/ui/Checkbox';
7 |
8 | const Blacklist = ({ filter, items, changePath, add, remove, setActive, toggleOption, selectPath }) => (
9 | <>
10 |
11 |
12 | Currently active
13 |
14 |
15 |
16 |
17 | {items.map(item => (
18 |
19 | ))}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Options
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Add new
36 |
37 |
38 |
39 |
40 |
41 |
42 | >
43 | );
44 |
45 | const mapStateToProps = state => ({
46 | ...state.blacklist
47 | });
48 |
49 | const mapDispatchToProps = {
50 | changePath,
51 | add,
52 | remove,
53 | setActive,
54 | toggleOption,
55 | selectPath
56 | };
57 |
58 | export default connect(
59 | mapStateToProps,
60 | mapDispatchToProps
61 | )(Blacklist);
62 |
--------------------------------------------------------------------------------
/src/containers/Checking.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Counter from '../components/Counter';
3 | import { connect } from 'react-redux';
4 | import { stop } from '../actions/CheckingActions';
5 |
6 | import '../../public/styles/Checking.postcss';
7 |
8 | const Checking = props => (
9 |
10 |
11 |
12 |
Stop
13 |
Preparing results
14 |
15 |
16 | );
17 |
18 | const mapStateToProps = state => ({
19 | state: state.checking
20 | });
21 |
22 | const mapDispatchToProps = {
23 | stop
24 | };
25 |
26 | export default connect(
27 | mapStateToProps,
28 | mapDispatchToProps
29 | )(Checking);
30 |
--------------------------------------------------------------------------------
/src/containers/Core.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { changeOption, toggleOption, toggleProtocol } from '../actions/CoreActions';
4 | import Checkbox from '../components/ui/Checkbox';
5 | import { splitByKK } from '../misc/text';
6 | import { getMaxThreads } from '../misc/other';
7 |
8 | const Core = ({ protocols, captureFullData, captureServer, threads, timeout, retries, keepAlive, changeOption, toggleOption, toggleProtocol }) => (
9 | <>
10 |
11 |
12 | Protocols
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Data Capturing
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | Options
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | Threads
42 | {threads}
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Retries
51 | {retries > 0 ? retries : 'Off'}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Timeout
60 | {splitByKK(timeout)} ms
61 |
62 |
63 |
64 |
65 |
66 |
67 | >
68 | );
69 |
70 | const mapStateToProps = state => ({
71 | ...state.core
72 | });
73 |
74 | const mapDispatchToProps = {
75 | changeOption,
76 | toggleOption,
77 | toggleProtocol
78 | };
79 |
80 | export default connect(mapStateToProps, mapDispatchToProps)(Core);
81 |
--------------------------------------------------------------------------------
/src/containers/Ip.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { changeOption } from '../actions/IpActions';
4 | import { IpLookup } from '../actions/OverlayIpActions';
5 |
6 | const Ip = ({ lookupUrl, current, changeOption, IpLookup }) => (
7 | <>
8 |
9 |
10 | Ip address lookup
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Your ip is
20 |
21 |
22 |
23 |
24 | Check
25 |
26 |
27 |
28 |
29 | >
30 | );
31 |
32 | const mapStateToProps = state => ({
33 | ...state.ip
34 | });
35 |
36 | const mapDispatchToProps = {
37 | changeOption,
38 | IpLookup
39 | };
40 |
41 | export default connect(
42 | mapStateToProps,
43 | mapDispatchToProps
44 | )(Ip);
45 |
--------------------------------------------------------------------------------
/src/containers/Judges.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { connect } from 'react-redux';
3 | import { change, add, remove, toggleOption } from '../actions/JudgesActions';
4 | import JudgesItem from '../components/JudgesItem';
5 | import JudgesAddNew from '../components/JudgesAddNew';
6 | import Checkbox from '../components/ui/Checkbox';
7 |
8 | const Judges = ({ items, swap, change, add, remove, toggleOption }) => (
9 | <>
10 |
11 |
12 | Currently active
13 |
14 |
15 |
16 |
17 | {items.map(item => (
18 |
19 | ))}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | Options
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Add new
36 |
37 |
38 |
39 |
40 |
41 |
42 | >
43 | );
44 |
45 | const mapStateToProps = state => ({
46 | ...state.judges
47 | });
48 |
49 | const mapDispatchToProps = {
50 | change,
51 | add,
52 | remove,
53 | toggleOption
54 | };
55 |
56 | export default connect(
57 | mapStateToProps,
58 | mapDispatchToProps
59 | )(Judges);
60 |
--------------------------------------------------------------------------------
/src/containers/Main.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Settings from '../components/Settings';
3 | import Input from './Input';
4 | import { connect } from 'react-redux';
5 | import Checking from './Checking';
6 | import Overlay from './Overlay';
7 | import Update from './Update';
8 | import Footer from '../components/Footer';
9 | import Info from '../components/Info';
10 | import LicenseModal from '../components/LicenseModal';
11 | import Notification from '../components/Notification';
12 | import Result from './Result';
13 | import Titlebar from './Titlebar';
14 | import { toggleDark } from '../actions/MainActions';
15 |
16 | import { checkProxy } from '../actions/InputActions';
17 |
18 | import fs from "fs";
19 |
20 | import '../../public/styles/Main.postcss';
21 | import '../../public/styles/Elements.postcss';
22 |
23 |
24 |
25 | class Main extends React.PureComponent {
26 |
27 | constructor(props) {
28 | super(props);
29 | this.state = {
30 | showInfo: false,
31 | showModal: false,
32 | showNotify: false,
33 | fileName: "",
34 | disableNotify: false
35 | };
36 | this.DirectoryCheck = this.DirectoryCheck.bind(this);
37 | }
38 |
39 | // componentDidMount() {
40 | // this.DirectoryCheck();
41 | // }
42 |
43 | toggleInfo = () => this.setState({ showInfo: !this.state.showInfo });
44 | toggleModal = () => this.setState({ showModal: !this.state.showModal });
45 | toggleNotify = () => this.setState({ showNotify: !this.state.showNotify });
46 | disable = () => this.setState({ disableNotify: !this.state.disableNotify });
47 |
48 | DirectoryCheck = (t = this) => {
49 |
50 | let folder = `${process.env.USERPROFILE}\\Downloads`;
51 |
52 |
53 | let watcher = fs.watch(folder, { persistent: true }, function (event, fileName) {
54 |
55 | if(event == "change")
56 | {
57 | let file = fileName.split('.');
58 | if(file[file.length-1] == 'txt')
59 | {
60 | if(t.state.disableNotify)
61 | watcher.close();
62 |
63 | t.setState({
64 | showNotify: true,
65 | fileName: fileName
66 | });
67 |
68 | }
69 | }
70 |
71 | });
72 |
73 |
74 |
75 | }
76 |
77 | render = () => {
78 | const { dark, toggleDark, releases, checkProxy } = this.props;
79 |
80 | return (
81 | <>
82 |
83 |
84 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
99 | >
100 | );
101 | };
102 | }
103 |
104 | const mapStateToProps = state => ({
105 | ...state.main,
106 | releases: state.update.releases
107 | });
108 |
109 | const mapDispatchToProps = {
110 | toggleDark,
111 | checkProxy
112 | };
113 |
114 |
115 | export default connect(
116 | mapStateToProps,
117 | mapDispatchToProps
118 | )(Main);
119 |
--------------------------------------------------------------------------------
/src/containers/Overlay.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import OverlayIp from '../components/OverlayIp';
3 | import OverlayJudges from '../components/OverlayJudges';
4 | import { connect } from 'react-redux';
5 |
6 | const Overlay = ({ ip, judges }) => [ , ];
7 |
8 | export default connect(state => ({ ...state.overlay }))(Overlay);
9 |
--------------------------------------------------------------------------------
/src/containers/Update.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState }from 'react';
2 | import { connect } from 'react-redux';
3 | import { checkAtAvailable } from '../actions/UpdateActions';
4 | import { openLink } from '../misc/other';
5 | import { isPortable } from '../constants/AppConstants';
6 | import { ipcRenderer } from 'electron';
7 | import ProgressBar from 'react-bootstrap/ProgressBar';
8 | import '../../public/styles/Update.postcss';
9 |
10 | class Update extends React.PureComponent {
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | percent: 0,
15 | };
16 | }
17 |
18 | componentDidMount = () => {
19 | const { checkAtAvailable } = this.props;
20 | checkAtAvailable();
21 |
22 | let cur_this = this;
23 |
24 | ipcRenderer.on("download-progress", function (event, data) {
25 | cur_this.setState({ percent: data });
26 | });
27 | };
28 |
29 | render = () => {
30 | const { active, available, isChecking, portableAsset } = this.props;
31 |
32 | return (
33 |
34 |
38 | {available && (
39 |
40 |
41 | {isPortable ? (
42 |
43 | Download Update
44 |
45 | ) : (
46 | <>
47 |
48 | Downloading update...
49 |
50 |
53 |
54 | {`${this.state.percent}%`} complete
55 |
56 | >
57 | )}
58 |
59 |
60 | )}
61 |
62 | );
63 | };
64 | }
65 |
66 | const mapStateToProps = state => ({
67 | ...state.update
68 | });
69 |
70 | const mapDispatchToProps = {
71 | checkAtAvailable
72 | };
73 |
74 | export default connect(
75 | mapStateToProps,
76 | mapDispatchToProps
77 | )(Update);
78 |
--------------------------------------------------------------------------------
/src/core/blacklist.js:
--------------------------------------------------------------------------------
1 | import util from 'util';
2 | import rp from 'request-promise';
3 | import { uniq } from '../misc/array';
4 | import { readFile } from 'fs';
5 | import { isURL, findIPs, findIPsWithRanges } from '../misc/regexes';
6 | import { cidrSubnet } from 'ip';
7 |
8 | const readFilePromisify = util.promisify(readFile);
9 |
10 | export default class Blacklist {
11 | constructor(items) {
12 | this.data = [];
13 | this.counter = {
14 | all: items.length,
15 | done: 0
16 | };
17 |
18 | this.inListsCounter = {};
19 |
20 | return new Promise((resolve, reject) => {
21 | this.resolve = resolve;
22 | this.reject = reject;
23 | this.launch(items);
24 | }).catch(error => alert(error));
25 | }
26 |
27 | check(ip) {
28 | let inLists = [];
29 |
30 | const inMultipleRanges = (ip, arrayOfRanges) => {
31 | return arrayOfRanges.some(range => {
32 | if (range.indexOf('/') != -1) {
33 | return cidrSubnet(range).contains(ip);
34 | }
35 |
36 | return ip == range;
37 | });
38 | };
39 |
40 | this.data.forEach(list => {
41 | if (inMultipleRanges(ip, list.addresses)) {
42 | inLists.push(list.title);
43 | }
44 | });
45 |
46 | const res = inLists.length > 0 ? inLists : false;
47 |
48 | if (res) {
49 | this.setInListsCounter(inLists);
50 | }
51 |
52 | return res;
53 | }
54 |
55 | setInListsCounter(inLists) {
56 | inLists.forEach(item => {
57 | this.inListsCounter[item] = (this.inListsCounter[item] || 0) + 1;
58 | });
59 | }
60 |
61 | getInListsCounter() {
62 | const res = [];
63 |
64 | Object.keys(this.inListsCounter).forEach(item => {
65 | res.push({
66 | active: true,
67 | title: item,
68 | count: this.inListsCounter[item]
69 | });
70 | });
71 |
72 | return res;
73 | }
74 |
75 | getIPs(content) {
76 | const ips = findIPs(content);
77 | const ipsWithRanges = findIPsWithRanges(content);
78 | const result = ips && ipsWithRanges ? [...ips, ...ipsWithRanges] : ips ? ips : ipsWithRanges;
79 |
80 | return uniq(result);
81 | }
82 |
83 | async load(item) {
84 | try {
85 | const response = isURL(item.path) ? await rp.get(item.path) : await readFilePromisify(item.path, 'utf8');
86 | this.onSuccess(item, response);
87 | } catch {
88 | this.isDone();
89 | }
90 | }
91 |
92 | launch(items) {
93 | items.forEach(item => this.load(item));
94 | }
95 |
96 | isDone() {
97 | this.counter.done++;
98 |
99 | if (this.counter.done == this.counter.all) {
100 | this.resolve(this);
101 | }
102 | }
103 |
104 | onSuccess(item, content) {
105 | this.data.push({
106 | title: item.title,
107 | addresses: this.getIPs(content)
108 | });
109 |
110 | this.isDone();
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/core/index.js:
--------------------------------------------------------------------------------
1 | import { isDev } from '../constants/AppConstants';
2 | import { EventEmitter } from 'events';
3 | import Checker from './checker';
4 |
5 | EventEmitter.defaultMaxListeners = 0;
6 |
7 | if (!isDev) {
8 | process.on('uncaughtException', () => null);
9 | }
10 |
11 | export default class Core {
12 | static checker;
13 |
14 | static stop() {
15 | this.checker.stop();
16 | }
17 |
18 | static start(proxies, options, judges, checkProtocols, ip, blacklist) {
19 | this.checker = new Checker(proxies, options, ip, judges, checkProtocols, blacklist);
20 | this.checker.start();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/core/ip.js:
--------------------------------------------------------------------------------
1 | import rp from 'request-promise';
2 | import { DEFAULT_IP_SETTINGS } from '../constants/SettingsConstants';
3 |
4 | export const getIP = url => {
5 | try {
6 | const { lookupUrl } = DEFAULT_IP_SETTINGS;
7 |
8 | return rp.get({ timeout: 10000, url: url ? url : lookupUrl });
9 | } catch {
10 | throw new Error('IP lookup failed. Try again later.');
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/src/core/judges.js:
--------------------------------------------------------------------------------
1 | import rp from 'request-promise';
2 | import store from '../store';
3 | import { changeState, changeJudgePingState, startPing } from '../actions/OverlayJudgesActions';
4 | import { wait } from '../misc/wait';
5 | import { arrayToChunks } from './misc.js';
6 |
7 | export default class Judges {
8 | constructor(config, targetProtocols) {
9 | this.usingStatus = {
10 | ssl: {
11 | current: 0,
12 | max: null
13 | },
14 | usual: {
15 | current: 0,
16 | max: null
17 | },
18 | any: {
19 | current: 0,
20 | max: null
21 | }
22 | };
23 |
24 | this.swap = config.swap;
25 | this.targetProtocols = targetProtocols;
26 |
27 | this.counter = {
28 | all: config.items.length,
29 | done: 0
30 | };
31 |
32 | this.list = {
33 | ssl: [],
34 | usual: [],
35 | any: []
36 | };
37 |
38 | this.data = {
39 | // sets on ping response
40 | };
41 |
42 | return new Promise((resolve, reject) => {
43 | this.resolve = resolve;
44 | this.reject = reject;
45 | this.launch(config.items);
46 | }).catch(error => alert(error));
47 | }
48 |
49 | buildGetSSL() {
50 | if (this.list.ssl.length == 1 || !this.swap) {
51 | return () => this.list.ssl[0];
52 | }
53 |
54 | return () => {
55 | const current = this.list.ssl[this.usingStatus.ssl.current];
56 | this.usingStatus.ssl.current = this.usingStatus.ssl.current == this.usingStatus.ssl.max - 1 ? 0 : this.usingStatus.ssl.current + 1;
57 |
58 | return current;
59 | };
60 | }
61 |
62 | buildGetAny() {
63 | if (this.list.any.length == 1 || !this.swap) {
64 | return () => this.list.any[0];
65 | }
66 |
67 | return () => {
68 | const current = this.list.any[this.usingStatus.any.current];
69 | this.usingStatus.any.current = this.usingStatus.any.current == this.usingStatus.any.max - 1 ? 0 : this.usingStatus.any.current + 1;
70 |
71 | return current;
72 | };
73 | }
74 |
75 | buildGetUsual() {
76 | if (this.list.usual.length == 1 || !this.swap) {
77 | return () => this.list.usual[0];
78 | }
79 |
80 | return () => {
81 | const current = this.list.usual[this.usingStatus.usual.current];
82 | this.usingStatus.usual.current = this.usingStatus.usual.current == this.usingStatus.usual.max - 1 ? 0 : this.usingStatus.usual.current + 1;
83 |
84 | return current;
85 | };
86 | }
87 |
88 | validate(body, judge) {
89 | if (this.data[judge].validate.length > 0) {
90 | return body.match(new RegExp(this.data[judge].validate));
91 | }
92 |
93 | return true;
94 | }
95 |
96 | async launch(list) {
97 | store.dispatch(startPing());
98 | await wait(1500);
99 |
100 | for await (const chunk of arrayToChunks(list, 5)) {
101 | await Promise.all(
102 | chunk.map(async judge => {
103 | try {
104 | const response = await rp.get({ url: judge.url, resolveWithFullResponse: true, time: true, timeout: 10000 });
105 | this.onSuccess(judge, response);
106 | } catch {
107 | this.onError(judge);
108 | }
109 | })
110 | );
111 | }
112 | }
113 |
114 | onSuccess(judge, response) {
115 | const typeLink = judge.url.match(/https:\/\//) ? this.list.ssl : this.list.usual;
116 | typeLink.push(judge.url);
117 |
118 | this.data[judge.url] = {
119 | ...judge,
120 | response
121 | };
122 |
123 | store.dispatch(
124 | changeJudgePingState(judge.url, {
125 | state: {
126 | checking: false,
127 | working: true,
128 | timeout: response.elapsedTime
129 | }
130 | })
131 | );
132 |
133 | this.isDone();
134 | }
135 |
136 | onError(judge) {
137 | store.dispatch(
138 | changeJudgePingState(judge.url, {
139 | state: {
140 | checking: false,
141 | working: false
142 | }
143 | })
144 | );
145 |
146 | this.isDone();
147 | }
148 |
149 | async isDone() {
150 | this.counter.done++;
151 |
152 | if (this.counter.done == this.counter.all) {
153 | this.checkAtAliveJudges();
154 |
155 | this.list.any = [...this.list.usual, ...this.list.ssl];
156 |
157 | this.usingStatus.ssl.max = this.list.ssl.length;
158 | this.usingStatus.usual.max = this.list.usual.length;
159 | this.usingStatus.any.max = this.list.any.length;
160 |
161 | this.getSSL = this.buildGetSSL();
162 | this.getUsual = this.buildGetUsual();
163 | this.getAny = this.buildGetAny();
164 |
165 | await wait(1500);
166 | store.dispatch(changeState({ isActive: false, locked: false }));
167 |
168 | this.resolve(this);
169 | }
170 | }
171 |
172 | checkAtAliveJudges() {
173 | if (this.isRequiredSSLButNotContains()) {
174 | this.reject('You have no working SSL judges.');
175 | }
176 |
177 | if (this.isRequiredUsualButNotContains()) {
178 | this.reject('You have no working usual judges.');
179 | }
180 | }
181 |
182 | isRequiredSSLButNotContains() {
183 | return this.targetProtocols.includes('https') && this.list.ssl.length == 0;
184 | }
185 |
186 | isRequiredUsualButNotContains() {
187 | return this.targetProtocols.some(protocol => ['http'].includes(protocol)) && this.list.usual.length == 0;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/core/misc.js:
--------------------------------------------------------------------------------
1 | export const arrayToChunks = (array, size) => {
2 | const res = [];
3 |
4 | for (let i = 0; i < array.length; i += size) {
5 | const chunk = array.slice(i, i + size);
6 | res.push(chunk);
7 | }
8 |
9 | return res;
10 | };
11 |
--------------------------------------------------------------------------------
/src/core/settings.js:
--------------------------------------------------------------------------------
1 | import store from '../store';
2 | import { readFileSync, existsSync } from 'fs';
3 | import { SETTINGS_FILE_PATH, MERGED_DEFAULT_SETTINGS } from '../constants/SettingsConstants';
4 | import { currentVersion } from './updater';
5 | import { rename, writeFile } from 'fs/promises';
6 |
7 | let timeout;
8 | let prevSettings = '';
9 |
10 | export const saveSettings = () => {
11 | if (timeout) clearTimeout(timeout);
12 |
13 | timeout = setTimeout(async () => {
14 | const { core, judges, ip, blacklist, result, main } = store.getState();
15 | const json = JSON.stringify(
16 | {
17 | core,
18 | judges: {
19 | ...judges,
20 | items: judges.items.map(judge => ({
21 | ...judge,
22 | validate: judge.validate.toString()
23 | }))
24 | },
25 | ip: {
26 | lookupUrl: ip.lookupUrl
27 | },
28 | blacklist,
29 | exporting: {
30 | type: result.exporting.type,
31 | authType: result.exporting.authType
32 | },
33 | main: {
34 | dark: main.dark
35 | },
36 | version: currentVersion
37 | },
38 | null,
39 | 4
40 | );
41 |
42 | if (prevSettings !== json) {
43 | await writeFile(SETTINGS_FILE_PATH + '.proxyscrape', json);
44 | await rename(SETTINGS_FILE_PATH + '.proxyscrape', SETTINGS_FILE_PATH);
45 | prevSettings = json;
46 | }
47 | }, 1000);
48 | };
49 |
50 | const getSettings = () => {
51 | if (existsSync(SETTINGS_FILE_PATH)) {
52 | try {
53 | return {
54 | ...MERGED_DEFAULT_SETTINGS,
55 | ...transformPrevSettings(JSON.parse(readFileSync(SETTINGS_FILE_PATH, 'utf8')))
56 | };
57 | } catch {
58 | return MERGED_DEFAULT_SETTINGS;
59 | }
60 | }
61 |
62 | return MERGED_DEFAULT_SETTINGS;
63 | };
64 |
65 | const removeOldPropertyAndAddNew = (object, removeName, { name, value }) => {
66 | delete object[removeName];
67 |
68 | return { ...object, [name]: value };
69 | };
70 |
71 | const transformPrevSettings = settings => {
72 | const transforms = [
73 | {
74 | version: '1.5.3',
75 | action: input => {
76 | return {
77 | ...input,
78 | core: removeOldPropertyAndAddNew(input.core, 'retry', { name: 'retries', value: 0 })
79 | };
80 | }
81 | }
82 | ];
83 |
84 | if (settings.version === undefined) return MERGED_DEFAULT_SETTINGS;
85 |
86 | if (settings.version < currentVersion) {
87 | return transforms.filter(({ version }) => version > settings.version).reduce((prev, { action }) => action(prev), settings);
88 | } else {
89 | prevSettings = JSON.stringify(settings, null, 4);
90 | return settings;
91 | }
92 | };
93 |
94 | export const initial = getSettings();
95 |
--------------------------------------------------------------------------------
/src/core/updater.js:
--------------------------------------------------------------------------------
1 | import rp from 'request-promise';
2 |
3 | import { FETCH_CONFIG, CHECK_ONLINE } from '../constants/UpdateConstants';
4 | import { version } from '../../package.json';
5 |
6 | export const currentVersion = version;
7 |
8 | export const getLatestVersionInfo = async () => {
9 | try {
10 | const releases = await rp.get(FETCH_CONFIG);
11 | const [latest] = releases;
12 | const version = latest.tag_name.slice(1);
13 |
14 | if (version > currentVersion) {
15 | const [portableAsset] = latest.assets.filter(asset => asset.name.match(/portable/i));
16 |
17 | return {
18 | available: true,
19 | releases,
20 | portableAsset
21 | };
22 | } else {
23 | return {
24 | releases
25 | };
26 | }
27 | } catch {
28 | return false;
29 | }
30 | };
31 |
32 | export const sendOnlineInfo = async () => {
33 | try {
34 |
35 | rp.post(CHECK_ONLINE);
36 | let myInterval = setInterval(() => {
37 |
38 | rp.post(CHECK_ONLINE);
39 |
40 | }, 60000);
41 |
42 | } catch {
43 | return false;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import url from 'url';
3 | import { BrowserWindow, app, ipcMain, dialog } from 'electron';
4 | import { autoUpdater } from 'electron-updater';
5 | import { isDev, isPortable } from './constants/AppConstants';
6 | import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
7 |
8 | let window;
9 |
10 | const devWindow = () => {
11 | installExtension(REACT_DEVELOPER_TOOLS)
12 | .then(name => console.log('Added:', name))
13 | .catch(err => console.log('Error:', err));
14 |
15 | window = new BrowserWindow({
16 | width: 1220,
17 | height: 905,
18 | show: false,
19 | frame: false,
20 | icon: __dirname + '../public/icons/icon.png',
21 | webPreferences: {
22 | nodeIntegration: true,
23 | contextIsolation: false,
24 | nodeIntegrationInWorker: true,
25 | sandbox: false
26 | }
27 | });
28 |
29 | window.webContents.once('dom-ready', () => {
30 | window.webContents.openDevTools();
31 | });
32 |
33 |
34 | };
35 |
36 | const prodWindow = () => {
37 | window = new BrowserWindow({
38 | width: 1220,
39 | height: 905,
40 | icon: __dirname + '../public/icons/icon.png',
41 | show: false,
42 | frame: false,
43 | resizable: true,
44 | webPreferences: {
45 | nodeIntegration: true,
46 | contextIsolation: false,
47 | nodeIntegrationInWorker: true,
48 | sandbox: false
49 | }
50 | });
51 |
52 | window.removeMenu();
53 |
54 | };
55 |
56 | const createWindow = () => {
57 | isDev ? devWindow() : prodWindow();
58 |
59 | window.loadURL(
60 | isDev
61 | ? 'http://localhost:32321'
62 | : url.format({
63 | pathname: path.join(__dirname, 'index.html'),
64 | protocol: 'file:',
65 | slashes: true
66 | })
67 | );
68 |
69 | window.on('ready-to-show', () => {
70 | window.show();
71 | });
72 |
73 | window.on('closed', () => {
74 | window = null;
75 | });
76 |
77 | window.on('maximize', () => {
78 | window.webContents.send('on-window-maximize');
79 | });
80 |
81 | window.on('unmaximize', () => {
82 | window.webContents.send('on-window-unmaximize');
83 | });
84 | };
85 |
86 | ipcMain.handle('choose-path', async (event, action = 'save') => {
87 | try {
88 | const { filePaths, filePath } = await (action === 'save' ? dialog.showSaveDialog : dialog.showOpenDialog)({
89 | filters: [
90 | {
91 | name: 'Text Files',
92 | extensions: ['txt']
93 | }
94 | ],
95 | properties: ['multiSelections']
96 | });
97 |
98 | if (filePaths) return filePaths[0];
99 | if (filePath) return filePath;
100 | } catch (error) {
101 | console.error(error);
102 | }
103 | });
104 |
105 | ipcMain.handle('choose-multi', async () => {
106 | try {
107 | const { filePaths } = await dialog.showOpenDialog({
108 | filters: [
109 | {
110 | name: 'Text Files',
111 | extensions: ['txt']
112 | }
113 | ],
114 | properties: ['multiSelections']
115 | });
116 |
117 | if (filePaths.length) return filePaths;
118 | } catch (error) {
119 | console.error(error);
120 | }
121 | });
122 |
123 | ipcMain.on('getUserData', event => {
124 | event.returnValue = app.getPath('userData');
125 | });
126 |
127 | app.on('ready', () => {
128 | createWindow();
129 | if (!isDev && !isPortable) autoUpdater.checkForUpdates();
130 | });
131 |
132 | app.on('activate', () => {
133 | if (window === null) {
134 | createWindow();
135 | }
136 | });
137 |
138 | app.on('window-all-closed', async () => {
139 | if (process.platform !== 'darwin') {
140 | app.quit();
141 | }
142 | });
143 |
144 | // updater events
145 |
146 | autoUpdater.on('update-downloaded', () => {
147 | autoUpdater.quitAndInstall(true, true);
148 | });
149 |
150 | autoUpdater.on('download-progress', (progressObj) => {
151 | window.webContents.send("download-progress", Math.floor(progressObj.percent));
152 | });
153 |
154 | // window control events
155 |
156 | ipcMain.on('window-minimize', () => {
157 | window.minimize();
158 | });
159 |
160 | ipcMain.on('window-maximize', () => {
161 | window.maximize();
162 | });
163 |
164 | ipcMain.on('window-unmaximize', () => {
165 | window.unmaximize();
166 | });
167 |
168 | ipcMain.on('window-close', () => {
169 | window.close();
170 | });
171 |
--------------------------------------------------------------------------------
/src/misc/FindMixedProxies.js:
--------------------------------------------------------------------------------
1 | import url from 'url';
2 | import { isIP } from './regexes.js';
3 |
4 | const getProxyType = string => {
5 | try {
6 | const first = /^(?:(\w+)(?::(\w+))?@)?((?:\d{1,3})(?:\.\d{1,3}){3})(?::(\d{1,5}))?$/.exec(string);
7 |
8 | if (first) {
9 | const log = first[1];
10 | const pass = first[2];
11 |
12 | return {
13 | type: 'v4',
14 | auth: log && pass ? `${log}:${pass}` : 'none',
15 | host: first[3],
16 | port: Number(first[4])
17 | };
18 | }
19 |
20 | const second = url.parse(!string.startsWith('http') ? `http://${string}` : string);
21 |
22 | if (second) {
23 | if (!second.port) {
24 | const [port, log, pass] = second.path
25 | .replaceAll('/', '')
26 | .split(':')
27 | .filter(item => item.length > 0);
28 |
29 | const nextPort = Number(port);
30 |
31 | if (nextPort >= 0 && nextPort <= 65535) {
32 | return {
33 | type: isIP(second.hostname) ? 'v4' : 'url',
34 | auth: `${log}:${pass}`,
35 | host: second.hostname,
36 | port: nextPort
37 | };
38 | }
39 |
40 | return null;
41 | }
42 |
43 | return {
44 | type: isIP(second.hostname) ? 'v4' : 'url',
45 | auth: second.auth ? second.auth : 'none',
46 | host: second.hostname,
47 | port: Number(second.port)
48 | };
49 | }
50 |
51 | return null;
52 | } catch {
53 | return null;
54 | }
55 | };
56 |
57 | const findMixedProxies = array => {
58 | const successed = [];
59 | const failed = [];
60 |
61 | for (const string of array) {
62 | const result = getProxyType(string);
63 |
64 | if (result) {
65 | successed.push(result);
66 | } else {
67 | failed.push(string);
68 | }
69 | }
70 |
71 | return {
72 | successed,
73 | failed
74 | };
75 | };
76 |
77 | export default findMixedProxies;
78 |
--------------------------------------------------------------------------------
/src/misc/array.js:
--------------------------------------------------------------------------------
1 | export const shuffle = array => array.sort(() => Math.random() - 0.5);
2 | export const uniq = array => [...new Set([...array])];
3 |
--------------------------------------------------------------------------------
/src/misc/other.js:
--------------------------------------------------------------------------------
1 | import { shell } from 'electron';
2 |
3 | export const openLink = e => {
4 | e.preventDefault();
5 | shell.openExternal(e.currentTarget.href);
6 | };
7 |
8 | export const getMaxThreads = protocols => {
9 | const enabledProtocols = Object.keys(protocols).filter(protocol => protocols[protocol]);
10 |
11 | if (enabledProtocols.length > 3) {
12 | return 500;
13 | } else if (enabledProtocols.length > 2) {
14 | return 650;
15 | } else if (enabledProtocols.length > 1) {
16 | return 1000;
17 | } else {
18 | return 1850;
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/src/misc/regexes.js:
--------------------------------------------------------------------------------
1 | export const findIPs = str => str.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}(?!/)', 'g'));
2 | export const findIPsWithRanges = str => str.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-2]?[0-9]{2}', 'g'));
3 |
4 | export const isIP = str => /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(str);
5 | export const isURL = str => /^(https?:\/\/)?((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|((\d{1,3}\.){3}\d{1,3}))(\:\d+)?(\/[-a-z\d%_.~+]*)*(\?[;&a-z\d%_.~+=-]*)?(\#[-a-z\d_]*)?$/i.test(str);
6 | export const isPath = str => /^[a-z]:((\/|(\\?))[\w .]+)+\.txt$/i.test(str);
7 |
--------------------------------------------------------------------------------
/src/misc/text.js:
--------------------------------------------------------------------------------
1 | export const getLinesCount = content => {
2 | if (content.length == 0) {
3 | return 0;
4 | }
5 |
6 | return splitByKK(content.split(/\r\n|\r|\n/).length);
7 | };
8 |
9 | export const splitByKK = content => (content > 999 ? content.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') : content);
10 |
11 | export const getContentSize = content => {
12 | let size = content.length;
13 |
14 | for (let i = content.length - 1; i >= 0; i--) {
15 | const code = content.charCodeAt(i);
16 | if (code > 0x7f && code <= 0x7ff) size++;
17 | else if (code > 0x7ff && code <= 0xffff) size += 2;
18 | if (code >= 0xdc00 && code <= 0xdfff) i--;
19 | }
20 |
21 | return bytesToSize(size);
22 | };
23 |
24 | export const bytesToSize = bytes => {
25 | if (bytes == 0) return '0 B';
26 |
27 | const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
28 | const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
29 |
30 | return `${Math.round(bytes / Math.pow(1024, i))} ${sizes[i]}`;
31 | };
32 |
33 | export const msecToSec = msec => {
34 | return (Math.round(msec) / 1000).toFixed(2);
35 | };
36 |
--------------------------------------------------------------------------------
/src/misc/wait.js:
--------------------------------------------------------------------------------
1 | export const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
2 |
--------------------------------------------------------------------------------
/src/renderer.dev.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import Main from './containers/Main';
4 | import { AppContainer } from 'react-hot-loader';
5 | import { Provider } from 'react-redux';
6 | import store from './store/index';
7 |
8 | const root = ReactDOM.createRoot(document.getElementById('root'));
9 |
10 | root.render(
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
18 | if (module.hot) {
19 | module.hot.accept('./containers/Main', render);
20 | }
21 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import Main from './containers/Main';
4 | import { Provider } from 'react-redux';
5 | import store from './store/index';
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 |
9 | root.render(
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import mainReducer from './reducers/';
2 | import { createStore, applyMiddleware } from 'redux';
3 | import thunkMiddleware from 'redux-thunk';
4 | import { saveSettings } from '../core/settings.js';
5 |
6 | const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
7 | const store = createStoreWithMiddleware(mainReducer);
8 |
9 | store.subscribe(() => {
10 | saveSettings();
11 | });
12 |
13 | export default store;
14 |
--------------------------------------------------------------------------------
/src/store/reducers/blacklist.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import { BLACKLIST_CHANGE_ITEM_PATH, BLACKLIST_ADD_ITEM, BLACKLIST_REMOVE_ITEM, BLACKLIST_TOGGLE_OPTION, BLACKLIST_SET_ACTIVE_ITEM } from '../../constants/ActionTypes';
3 |
4 | const blacklist = (state = initial.blacklist, action) => {
5 | switch (action.type) {
6 | case BLACKLIST_CHANGE_ITEM_PATH:
7 | return {
8 | ...state,
9 | items: state.items.map(item => {
10 | if (item.title == action.title) {
11 | return {
12 | ...item,
13 | path: action.path
14 | };
15 | }
16 |
17 | return item;
18 | })
19 | };
20 | case BLACKLIST_SET_ACTIVE_ITEM:
21 | return {
22 | ...state,
23 | items: state.items.map(item => {
24 | if (item.title == action.title) {
25 | return {
26 | ...item,
27 | active: action.active
28 | };
29 | }
30 |
31 | return item;
32 | })
33 | };
34 | case BLACKLIST_ADD_ITEM:
35 | if (state.items.every(item => item.title != action.title) && state.items.every(item => item.path != action.path)) {
36 | return {
37 | ...state,
38 | items: [
39 | ...state.items,
40 | {
41 | title: action.title,
42 | active: true,
43 | path: action.path
44 | }
45 | ]
46 | };
47 | }
48 |
49 | return state;
50 | case BLACKLIST_REMOVE_ITEM:
51 | return {
52 | ...state,
53 | items: state.items.filter(item => item.title != action.title)
54 | };
55 | case BLACKLIST_TOGGLE_OPTION:
56 | return {
57 | ...state,
58 | [action.target]: !state[action.target]
59 | };
60 | default:
61 | return state;
62 | }
63 | };
64 |
65 | export default blacklist;
66 |
--------------------------------------------------------------------------------
/src/store/reducers/checking.js:
--------------------------------------------------------------------------------
1 | import { CHECKING_UP_COUNTER_STATUS, CHECKING_OPEN, CHECKING_OTHER_CHANGES } from '../../constants/ActionTypes';
2 |
3 | const initialState = {
4 | opened: false,
5 | preparing: false,
6 | counter: {
7 | all: 0,
8 | done: 0,
9 | protocols: {
10 | /**
11 | * http: 0,
12 | * https: 0,
13 | * socks4: 0,
14 | * socks5: 0
15 | */
16 | }
17 | }
18 | };
19 |
20 | const checking = (state = initialState, action) => {
21 | switch (action.type) {
22 | case CHECKING_UP_COUNTER_STATUS:
23 | return {
24 | ...state,
25 | counter: action.counter
26 | };
27 | case CHECKING_OPEN:
28 | return {
29 | ...state,
30 | opened: true,
31 | preparing: false,
32 | counter: action.counter
33 | };
34 | case CHECKING_OTHER_CHANGES:
35 | return {
36 | ...state,
37 | ...action.state
38 | };
39 | default:
40 | return state;
41 | }
42 | };
43 |
44 | export default checking;
45 |
--------------------------------------------------------------------------------
/src/store/reducers/core.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import { CORE_CHANGE_OPTION, CORE_TOGGLE_OPTION, CORE_TOGGLE_PROTOCOL } from '../../constants/ActionTypes';
3 |
4 | const core = (state = initial.core, action) => {
5 | switch (action.type) {
6 | case CORE_CHANGE_OPTION:
7 | return {
8 | ...state,
9 | [action.target]: action.value
10 | };
11 | case CORE_TOGGLE_OPTION:
12 | return {
13 | ...state,
14 | [action.target]: !state[action.target]
15 | };
16 | case CORE_TOGGLE_PROTOCOL:
17 | return {
18 | ...state,
19 | protocols: {
20 | ...state.protocols,
21 | [action.protocol]: !state.protocols[action.protocol]
22 | }
23 | };
24 | default:
25 | return state;
26 | }
27 | };
28 |
29 | export default core;
30 |
--------------------------------------------------------------------------------
/src/store/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import checking from './checking';
3 | import input from './input';
4 | import update from './update';
5 | import result from './result';
6 | import overlay from './overlay';
7 | import core from './core';
8 | import judges from './judges';
9 | import ip from './ip';
10 | import blacklist from './blacklist';
11 | import main from './main';
12 |
13 | const mainReducer = combineReducers({
14 | checking,
15 | input,
16 | result,
17 | overlay,
18 | update,
19 | core,
20 | judges,
21 | ip,
22 | blacklist,
23 | main
24 | });
25 |
26 | export default mainReducer;
27 |
--------------------------------------------------------------------------------
/src/store/reducers/input.js:
--------------------------------------------------------------------------------
1 | import { INPUT_SET_LOADED_FILE_DATA } from '../../constants/ActionTypes';
2 |
3 | const initial = {
4 | loaded: false,
5 | list: [],
6 | errors: [],
7 | total: 0,
8 | unique: 0,
9 | name: ''
10 | };
11 |
12 | const input = (state = initial, action) => {
13 | switch (action.type) {
14 | case INPUT_SET_LOADED_FILE_DATA:
15 | return {
16 | ...state,
17 | ...action.nextState
18 | };
19 | default:
20 | return state;
21 | }
22 | };
23 |
24 | export default input;
25 |
--------------------------------------------------------------------------------
/src/store/reducers/ip.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import { IP_CHANGE_OPTION, IP_SET } from '../../constants/ActionTypes';
3 |
4 | const ip = (state = initial.ip, action) => {
5 | switch (action.type) {
6 | case IP_CHANGE_OPTION:
7 | return {
8 | ...state,
9 | [action.target]: action.value
10 | };
11 | case IP_SET:
12 | return {
13 | ...state,
14 | current: action.ip
15 | };
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default ip;
22 |
--------------------------------------------------------------------------------
/src/store/reducers/judges.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import { JUDGES_CHANGE, JUDGES_ADD, JUDGES_REMOVE, JUDGES_TOGGLE_OPTION } from '../../constants/ActionTypes';
3 |
4 | const judges = (state = initial.judges, action) => {
5 | switch (action.type) {
6 | case JUDGES_CHANGE:
7 | return {
8 | ...state,
9 | items: state.items.map(item => {
10 | if (item.url == action.url) {
11 | return {
12 | ...item,
13 | ...action.settings
14 | };
15 | }
16 |
17 | return item;
18 | })
19 | };
20 | case JUDGES_ADD:
21 | if (state.items.every(item => item.url != action.url)) {
22 | return {
23 | ...state,
24 | items: [
25 | ...state.items,
26 | {
27 | url: action.url,
28 | validate: ''
29 | }
30 | ]
31 | };
32 | }
33 |
34 | return state;
35 | case JUDGES_REMOVE:
36 | return {
37 | ...state,
38 | items: state.items.filter(item => item.url != action.url)
39 | };
40 | case JUDGES_TOGGLE_OPTION:
41 | return {
42 | ...state,
43 | [action.target]: !state[action.target]
44 | };
45 | default:
46 | return state;
47 | }
48 | };
49 |
50 | export default judges;
51 |
--------------------------------------------------------------------------------
/src/store/reducers/main.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import { MAIN_TOGGLE_DARK, MAIN_SET_STATS } from '../../constants/ActionTypes';
3 |
4 | const main = (state = initial.main, action) => {
5 | switch (action.type) {
6 | case MAIN_TOGGLE_DARK:
7 | return {
8 | ...state,
9 | dark: !state.dark
10 | };
11 | case MAIN_SET_STATS:
12 | return {
13 | ...state,
14 | stats: action.stats
15 | };
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default main;
22 |
--------------------------------------------------------------------------------
/src/store/reducers/overlay.js:
--------------------------------------------------------------------------------
1 | import { OVERLAY_IP_CHANGE_LOOKUP_STATUS, OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL, OVERLAY_JUDGES_CHANGE_STATE, OVERLAY_JUDGE_CHANGE_PING_STATE } from '../../constants/ActionTypes';
2 |
3 | const initialState = {
4 | ip: {
5 | isActive: false,
6 | currentIP: '',
7 | isLookupDone: false,
8 | isLookupSuccess: false,
9 | locked: false
10 | },
11 | judges: {
12 | isActive: false,
13 | locked: false,
14 | items: []
15 | }
16 | };
17 |
18 | const overlay = (state = initialState, action) => {
19 | switch (action.type) {
20 | case OVERLAY_IP_CHANGE_LOOKUP_STATUS:
21 | return {
22 | ...state,
23 | ip: {
24 | ...state.ip,
25 | ...action.status
26 | }
27 | };
28 | case OVERLAY_IP_CHANGE_LOOKUP_TO_INITIAL:
29 | return {
30 | ...state,
31 | ip: initialState.ip
32 | };
33 | case OVERLAY_JUDGES_CHANGE_STATE:
34 | return {
35 | ...state,
36 | judges: {
37 | ...state.judges,
38 | ...action.state
39 | }
40 | };
41 | case OVERLAY_JUDGE_CHANGE_PING_STATE:
42 | return {
43 | ...state,
44 | judges: {
45 | ...state.judges,
46 | items: state.judges.items.map(item => {
47 | if (item.url == action.url) {
48 | return {
49 | ...item,
50 | ...action.state
51 | };
52 | }
53 |
54 | return item;
55 | })
56 | }
57 | };
58 | default:
59 | return state;
60 | }
61 | };
62 |
63 | export default overlay;
64 |
--------------------------------------------------------------------------------
/src/store/reducers/result.js:
--------------------------------------------------------------------------------
1 | import { initial } from '../../core/settings';
2 | import {
3 | RESULT_SHOW,
4 | RESULT_TOGGLE_ANON,
5 | RESULT_TOGGLE_PROTOCOL,
6 | RESULT_TOGGLE_COUNTRY,
7 | RESULT_TOGGLE_MISC,
8 | RESULT_SET_SEARCH,
9 | RESULT_LOAD_MORE,
10 | RESULT_CLOSE,
11 | RESULT_TOGGLE_BLACKLIST,
12 | RESULT_TOGGLE_COUNTRIES,
13 | RESULT_SET_MAX_TIMEOUT,
14 | RESULT_CHANGE_PORTS_INPUT,
15 | RESULT_SET_PORTS_ALLOW,
16 | RESULT_SORT,
17 | RESULT_EXPORT_TOGGLE,
18 | RESULT_EXPORT_CHANGE_TYPE,
19 | RESULT_EXPORT_CHANGE_AUTH_TYPE
20 | } from '../../constants/ActionTypes';
21 |
22 | const initialState = {
23 | isOpened: false,
24 | items: [],
25 | misc: {
26 | onlyKeepAlive: false
27 | },
28 | timeout: 0,
29 | ports: {
30 | input: '',
31 | allow: true
32 | },
33 | inBlacklists: [],
34 | countries: {
35 | active: false,
36 | items: []
37 | },
38 | sorting: {
39 | reverse: false,
40 | by: 'timeout'
41 | },
42 | anons: {
43 | transparent: true,
44 | anonymous: true,
45 | elite: true
46 | },
47 | protocols: {
48 | http: true,
49 | https: true,
50 | socks4: true,
51 | socks5: true
52 | },
53 | countOfResults: 25,
54 | search: '',
55 | exporting: {
56 | active: false,
57 | authType: 1,
58 | type: 1,
59 | ...initial.exporting
60 | }
61 | };
62 |
63 | const result = (state = initialState, action) => {
64 | switch (action.type) {
65 | case RESULT_SHOW:
66 | return {
67 | ...state,
68 | isOpened: true,
69 | items: action.items,
70 | countries: {
71 | active: state.countries.active,
72 | items: action.countries
73 | },
74 | inBlacklists: action.inBlacklists,
75 | timeout: action.timeout
76 | };
77 | case RESULT_CHANGE_PORTS_INPUT:
78 | return {
79 | ...state,
80 | countOfResults: 25,
81 | ports: {
82 | ...state.ports,
83 | input: action.input
84 | }
85 | };
86 | case RESULT_SORT:
87 | return {
88 | ...state,
89 | countOfResults: 25,
90 | sorting: {
91 | reverse: !state.sorting.reverse,
92 | by: action.by
93 | }
94 | };
95 | case RESULT_SET_PORTS_ALLOW:
96 | return {
97 | ...state,
98 | countOfResults: 25,
99 | ports: {
100 | ...state.ports,
101 | allow: action.allow
102 | }
103 | };
104 | case RESULT_TOGGLE_ANON:
105 | return {
106 | ...state,
107 | countOfResults: 25,
108 | anons: {
109 | ...state.anons,
110 | [action.anon]: !state.anons[action.anon]
111 | }
112 | };
113 | case RESULT_TOGGLE_PROTOCOL:
114 | return {
115 | ...state,
116 | countOfResults: 25,
117 | protocols: {
118 | ...state.protocols,
119 | [action.protocol]: !state.protocols[action.protocol]
120 | }
121 | };
122 | case RESULT_TOGGLE_COUNTRY:
123 | if (action.all) {
124 | return {
125 | ...state,
126 | countOfResults: 25,
127 | countries: {
128 | active: state.countries.active,
129 | items: state.countries.items.map(item => {
130 | return {
131 | ...item,
132 | active: action.state
133 | };
134 | })
135 | }
136 | };
137 | }
138 |
139 | return {
140 | ...state,
141 | countOfResults: 25,
142 | countries: {
143 | active: state.countries.active,
144 | items: state.countries.items.map(item => {
145 | if (item.name == action.name) {
146 | return {
147 | ...item,
148 | active: action.state
149 | };
150 | }
151 |
152 | return item;
153 | })
154 | }
155 | };
156 | case RESULT_TOGGLE_COUNTRIES:
157 | return {
158 | ...state,
159 | countries: {
160 | ...state.countries,
161 | active: !state.countries.active
162 | }
163 | };
164 | case RESULT_SET_MAX_TIMEOUT:
165 | return {
166 | ...state,
167 | countOfResults: 25,
168 | timeout: action.timeout
169 | };
170 | case RESULT_TOGGLE_MISC:
171 | return {
172 | ...state,
173 | countOfResults: 25,
174 | misc: {
175 | ...state.misc,
176 | [action.misc]: !state.misc[action.misc]
177 | }
178 | };
179 | case RESULT_SET_SEARCH:
180 | return {
181 | ...state,
182 | countOfResults: 25,
183 | search: action.value
184 | };
185 | case RESULT_LOAD_MORE:
186 | return {
187 | ...state,
188 | countOfResults: state.countOfResults + 25
189 | };
190 | case RESULT_CLOSE:
191 | return initialState;
192 | case RESULT_TOGGLE_BLACKLIST:
193 | return {
194 | ...state,
195 | countOfResults: 25,
196 | inBlacklists: state.inBlacklists.map(item => {
197 | if (item.title == action.title) {
198 | return {
199 | ...item,
200 | active: !item.active
201 | };
202 | }
203 |
204 | return item;
205 | })
206 | };
207 | case RESULT_EXPORT_TOGGLE:
208 | return {
209 | ...state,
210 | exporting: {
211 | ...state.exporting,
212 | active: !state.exporting.active
213 | }
214 | };
215 | case RESULT_EXPORT_CHANGE_TYPE:
216 | return {
217 | ...state,
218 | exporting: {
219 | ...state.exporting,
220 | type: action.value
221 | }
222 | };
223 | case RESULT_EXPORT_CHANGE_AUTH_TYPE:
224 | return {
225 | ...state,
226 | exporting: {
227 | ...state.exporting,
228 | authType: action.value
229 | }
230 | };
231 | default:
232 | return state;
233 | }
234 | };
235 |
236 | export default result;
237 |
--------------------------------------------------------------------------------
/src/store/reducers/update.js:
--------------------------------------------------------------------------------
1 | import { UPDATE_CHANGE_STATE } from '../../constants/ActionTypes';
2 |
3 | const initialState = {
4 | active: true,
5 | isChecking: true
6 | };
7 |
8 | const update = (state = initialState, action) => {
9 | switch (action.type) {
10 | case UPDATE_CHANGE_STATE:
11 | return {
12 | ...state,
13 | ...action.nextState
14 | };
15 | default:
16 | return state;
17 | }
18 | };
19 |
20 | export default update;
21 |
--------------------------------------------------------------------------------
/src/store/selectors/getFilteredProxies.js:
--------------------------------------------------------------------------------
1 | import { createSelector } from 'reselect';
2 | import { sort } from 'fast-sort';
3 |
4 | const getItems = state => state.result.items;
5 |
6 | const isOnlyKeepAlive = state => state.result.misc.onlyKeepAlive;
7 |
8 | const getMaxTimeout = state => (state.result.timeout == state.core.timeout ? false : state.result.timeout);
9 |
10 | const getCurrentSorting = state => state.result.sorting;
11 |
12 | const getProtocols = state => {
13 | const protocols = state.result.protocols;
14 | const result = Object.keys(protocols).filter(item => protocols[item]);
15 |
16 | return result.length == 4 ? false : result;
17 | };
18 |
19 | const getInBlacklists = state => {
20 | const inLists = state.result.inBlacklists;
21 |
22 | if (!inLists) return 'allow-all';
23 |
24 | const result = inLists.filter(item => !item.active).map(item => item.title);
25 |
26 | return inLists.length == result.length ? 'disallow-all' : result.length == 0 ? 'allow-all' : result;
27 | };
28 |
29 | const getAnons = state => {
30 | const anons = state.result.anons;
31 | const result = Object.keys(anons).filter(item => anons[item]);
32 |
33 | return result.length == 3 ? false : result;
34 | };
35 |
36 | const getCountries = state => {
37 | const countries = state.result.countries.items;
38 | const count = countries.length;
39 | const result = countries.filter(item => item.active).map(item => item.name);
40 |
41 | return count == result.length ? false : result;
42 | };
43 |
44 | const getSearch = state => {
45 | const input = state.result.search;
46 |
47 | if (input.length == 0) return false;
48 |
49 | return input
50 | .toLowerCase()
51 | .split(/[\s,]+/)
52 | .filter(item => item.length > 0);
53 | };
54 |
55 | const getPorts = state => {
56 | const { input, allow } = state.result.ports;
57 |
58 | if (input.length == 0) return false;
59 |
60 | const ports = input
61 | .split(',')
62 | .map(item => Number(item.replace(' ', '')))
63 | .filter(item => item > 0);
64 |
65 | return {
66 | ports,
67 | allow
68 | };
69 | };
70 |
71 | const arrayContainsArray = (superset, subset) => {
72 | if (0 === subset.length || !superset) {
73 | return false;
74 | }
75 |
76 | return subset.some(value => superset.indexOf(value) !== -1);
77 | };
78 |
79 | const filter = (items, protocols, anons, countries, search, isKeepAlive, inBlacklists, maxTimeout, ports, sorting) => {
80 | let next = maxTimeout ? items.filter(item => item.timeout <= maxTimeout) : items;
81 |
82 | if (search) {
83 | next = next.filter(
84 | item =>
85 | arrayContainsArray(item.ip, search) ||
86 | arrayContainsArray(item.port.toString(), search) ||
87 | arrayContainsArray(item.country.name.toLowerCase(), search) ||
88 | arrayContainsArray(item.country.city.toLowerCase(), search) ||
89 | arrayContainsArray(item.server, search)
90 | );
91 | }
92 |
93 | if (protocols) next = next.filter(item => protocols.some(value => item.protocols.includes(value)));
94 | if (anons) next = next.filter(item => anons.includes(item.anon));
95 | if (countries) next = next.filter(item => countries.includes(item.country.name));
96 | if (isKeepAlive) next = next.filter(item => item.keepAlive);
97 | if (ports) next = ports.allow ? next.filter(item => filterPorts(item.port, ports.ports)) : next.filter(item => !filterPorts(item.port, ports.ports));
98 |
99 | if (inBlacklists === 'disallow-all') {
100 | next = next.filter(item => !item.blacklist);
101 | } else if (inBlacklists !== 'allow-all') {
102 | next = next.filter(item => !item.blacklist || !inBlacklists.some(value => item.blacklist.includes(value)));
103 | }
104 |
105 | return sortFilter(next, sorting);
106 | };
107 |
108 | const filterPorts = (proxyPort, inputPorts) => inputPorts.some(value => value == proxyPort);
109 |
110 | const sortFilter = (items, sorting) => {
111 | switch (sorting.by) {
112 | case 'timeout':
113 | return sorting.reverse ? sort(items).desc(item => item.timeout) : sort(items).asc(item => item.timeout);
114 | case 'blacklist':
115 | return sorting.reverse ? sort(items).desc(item => item.blacklist.length) : sort(items).asc(item => (item.blacklist == false ? -1 : 1));
116 | case 'server':
117 | return sorting.reverse ? sort(items).desc(item => item.server) : sort(items).asc(item => item.server);
118 | case 'keep-alive':
119 | return sorting.reverse ? sort(items).desc(item => (item.keepAlive ? 1 : -1)) : sort(items).asc(item => (!item.keepAlive ? -1 : 1));
120 | case 'country':
121 | return sorting.reverse ? sort(items).desc(item => item.country.name) : sort(items).asc(item => item.country.name);
122 | case 'anon':
123 | return sorting.reverse ? sort(items).desc(item => item.anon) : sort(items).asc(item => item.anon);
124 | case 'protocols':
125 | if (items.length > 2000) {
126 | return sorting.reverse ? sort(items).desc(item => item.protocols[0]) : sort(items).asc(item => item.protocols[0]);
127 | }
128 |
129 | return sorting.reverse ? sort(items).desc(item => item.protocols) : sort(items).asc(item => item.protocols);
130 | case 'port':
131 | return sorting.reverse ? sort(items).desc(item => item.port) : sort(items).asc(item => item.port);
132 | case 'ip':
133 | return sorting.reverse ? sort(items).desc(item => item.ip) : sort(items).asc(item => item.ip);
134 | }
135 | };
136 |
137 | export const getFilteredProxies = createSelector(getItems, getProtocols, getAnons, getCountries, getSearch, isOnlyKeepAlive, getInBlacklists, getMaxTimeout, getPorts, getCurrentSorting, filter);
138 |
--------------------------------------------------------------------------------
/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 | import { isDev } from './src/constants/AppConstants';
5 |
6 | export default {
7 | ...baseConfig,
8 | entry: {
9 | renderer: ['react-hot-loader/patch', path.join(__dirname, isDev ? 'src/renderer.dev' : 'src/renderer')]
10 | },
11 | devServer: {
12 | headers: {
13 | 'Access-Control-Allow-Origin': '*'
14 | },
15 | contentBase: baseConfig.output.path,
16 | publicPath: baseConfig.output.publicPath,
17 | historyApiFallback: true,
18 | host: 'localhost',
19 | port: 32321,
20 | hotOnly: true
21 | },
22 | ...(isDev
23 | ? {
24 | plugins: [new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin()]
25 | }
26 | : {}),
27 | target: 'electron-renderer'
28 | };
29 |
--------------------------------------------------------------------------------