Full Code of dzt/easy-proxy for AI

master 8323c70ee480 cached
16 files
48.9 KB
11.4k tokens
15 symbols
1 requests
Download .txt
Repository: dzt/easy-proxy
Branch: master
Commit: 8323c70ee480
Files: 16
Total size: 48.9 KB

Directory structure:
gitextract_h1rwq2co/

├── .gitignore
├── LICENSE
├── README.md
├── confg/
│   ├── nouserpass/
│   │   └── squid.conf
│   └── userpass/
│       └── squid.conf
├── create.js
├── index.js
├── package.json
├── scripts/
│   └── installer.js
├── settings-manager.js
└── static/
    ├── app.js
    ├── icons/
    │   └── logo.icns
    ├── index.html
    ├── main.css
    ├── settings.html
    └── settings.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
config.json
gs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

package-lock.json

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

dist
out

# macOS
.DS_Store

package-lock.json


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2017 Peter Soboyejo <http://petersoboyejo.com/>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 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 THE SOFTWARE.

================================================
FILE: README.md
================================================
# easy-proxy
Mass proxy distribution made easy with the DigitalOcean API

![screen](https://i.imgur.com/ZIabShH.png)

[DOWNLOADS CAN BE FOUND HERE](https://github.com/dzt/easy-proxy/releases)

[Community Discord Server](https://discord.gg/BkDxcjT)

### Setup (for development)

easy-proxy requires [Node.js](http://nodejs.org/).

Development Setup:

```sh
$ git clone https://github.com/dzt/easy-proxy.git
$ cd easy-proxy
$ npm install # sudo npm install if you're on macOS
$ npm run dev
```

# Videos
[Preview & Explanation Video](https://youtu.be/Uy0EpcAgaAs)

[RSA ID Setup on macOS](https://streamable.com/6gnpe)

### Who

Written by <a href="http://petersoboyejo.com/">@dzt</a>, made better by you.


## License

```
The MIT License (MIT)

Copyright (c) 2017 Peter Soboyejo <http://petersoboyejo.com/>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 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 THE SOFTWARE.
```


================================================
FILE: confg/nouserpass/squid.conf
================================================
http_port 3128
cache deny all
hierarchy_stoplist cgi-bin ?

access_log none
cache_store_log none
cache_log /dev/null

refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern . 0 20% 4320

acl localhost src 127.0.0.1/32 ::1
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1

acl SSL_ports port 1-65535
acl Safe_ports port 1-65535
acl CONNECT method CONNECT
acl siteblacklist dstdomain "/etc/squid/blacklist.acl"
http_access allow manager localhost
http_access deny manager

http_access deny !Safe_ports

http_access deny CONNECT !SSL_ports
http_access deny siteblacklist
#auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd

#auth_param basic children 5
#auth_param basic realm Squid proxy-caching web server
#auth_param basic credentialsttl 2 hours
#acl password proxy_auth REQUIRED
http_access allow localhost
http_access allow password
http_access allow all

forwarded_for off
request_header_access Allow allow all
request_header_access Authorization allow all
request_header_access WWW-Authenticate allow all
request_header_access Proxy-Authorization allow all
request_header_access Proxy-Authenticate allow all
request_header_access Cache-Control allow all
request_header_access Content-Encoding allow all
request_header_access Content-Length allow all
request_header_access Content-Type allow all
request_header_access Date allow all
request_header_access Expires allow all
request_header_access Host allow all
request_header_access If-Modified-Since allow all
request_header_access Last-Modified allow all
request_header_access Location allow all
request_header_access Pragma allow all
request_header_access Accept allow all
request_header_access Accept-Charset allow all
request_header_access Accept-Encoding allow all
request_header_access Accept-Language allow all
request_header_access Content-Language allow all
request_header_access Mime-Version allow all
request_header_access Retry-After allow all
request_header_access Title allow all
request_header_access Connection allow all
request_header_access Proxy-Connection allow all
request_header_access User-Agent allow all
request_header_access Cookie allow all
request_header_access All deny all


================================================
FILE: confg/userpass/squid.conf
================================================
http_port 3128
cache deny all
hierarchy_stoplist cgi-bin ?

access_log none
cache_store_log none
cache_log /dev/null

refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern . 0 20% 4320

acl localhost src 127.0.0.1/32 ::1
acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1

acl SSL_ports port 1-65535
acl Safe_ports port 1-65535
acl CONNECT method CONNECT
acl siteblacklist dstdomain "/etc/squid/blacklist.acl"
http_access allow manager localhost
http_access deny manager

http_access deny !Safe_ports

http_access deny CONNECT !SSL_ports
http_access deny siteblacklist
auth_param basic program /usr/lib64/squid/basic_ncsa_auth /etc/squid/passwd

auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours
acl password proxy_auth REQUIRED
http_access allow localhost
http_access allow password
http_access deny all

forwarded_for off
request_header_access Allow allow all
request_header_access Authorization allow all
request_header_access WWW-Authenticate allow all
request_header_access Proxy-Authorization allow all
request_header_access Proxy-Authenticate allow all
request_header_access Cache-Control allow all
request_header_access Content-Encoding allow all
request_header_access Content-Length allow all
request_header_access Content-Type allow all
request_header_access Date allow all
request_header_access Expires allow all
request_header_access Host allow all
request_header_access If-Modified-Since allow all
request_header_access Last-Modified allow all
request_header_access Location allow all
request_header_access Pragma allow all
request_header_access Accept allow all
request_header_access Accept-Charset allow all
request_header_access Accept-Encoding allow all
request_header_access Accept-Language allow all
request_header_access Content-Language allow all
request_header_access Mime-Version allow all
request_header_access Retry-After allow all
request_header_access Title allow all
request_header_access Connection allow all
request_header_access Proxy-Connection allow all
request_header_access User-Agent allow all
request_header_access Cookie allow all
request_header_access All deny all


================================================
FILE: create.js
================================================
const randomstring = require('randomstring')
const DigitalOcean = require('do-wrapper').default
const eSettings = require('electron-settings')
const request = require('request')
const _ = require('underscore')
const ipcMain = require('electron').ipcMain

var task = function(win, info, settings, no, callback) {
    var sender = win.webContents;
    var ssh_key_id = null;
    var id = null;
    var stopped = false;

    api = new DigitalOcean(eSettings.getSync('do_api_key'), '9999')
    var host = null;

    ipcMain.on('stopTasks', function(event) {
        if (stopped == false) {
            sender.send('updateMonitor', {
                no: no,
                msg: 'Cancelled',
                username: info.username,
                password: info.password,
                ip: 'n/a',
                error: true
            });
            stopped = true
        }
    });

    sender.send('updateMonitor', {
        no: no,
        msg: 'Started',
        username: info.username,
        password: info.password,
        ip: 'n/a',
        error: false
    });

    createDroplet();

    function createDroplet() {

        sender.send('updateMonitor', {
            no: no,
            msg: `Creating Droplet`,
            username: info.username,
            password: info.password,
            ip: 'n/a',
            error: false
        });

        var dropletName = randomstring.generate(14) + '-ep';

        var dropletData = {
            name: dropletName,
            region: info.region,
            size: '512mb',
            image: parseInt(info.slug),
            ssh_keys: [ssh_key_id],
            monitoring: true,
            user_data:
              '#!/bin/bash \n' +
              'yum install squid wget httpd-tools -y &&' +
              'touch /etc/squid/passwd &&' +
              `htpasswd -b /etc/squid/passwd ${info.username} ${info.password} &&` +
              'wget -O /etc/squid/squid.conf https://raw.githubusercontent.com/dzt/easy-proxy/master/confg/userpass/squid.conf --no-check-certificate &&' +
              'touch /etc/squid/blacklist.acl &&' +
              'systemctl restart squid.service && systemctl enable squid.service &&' +
              'iptables -I INPUT -p tcp --dport 3128 -j ACCEPT &&' +
              'iptables-save'
        };

/*
        var dropletData = {
            name: dropletName,
            region: info.region,
            size: '512mb',
            image: parseInt(info.slug),
            ssh_keys: [ssh_key_id],
            monitoring: true,
            user_data:
              '#!/bin/bash \n' +
              'yum install squid wget httpd-tools -y &&' +
              //'touch /etc/squid/passwd &&' +
              //`htpasswd -b /etc/squid/passwd ${info.username} ${info.password} &&` +
              'wget -O /etc/squid/squid.conf https://raw.githubusercontent.com/dzt/easy-proxy/master/confg/nouserpass/squid.conf --no-check-certificate &&' +
              'touch /etc/squid/blacklist.acl &&' +
              'systemctl restart squid.service && systemctl enable squid.service &&' +
              'iptables -I INPUT -p tcp --dport 3128 -j ACCEPT &&' +
              'iptables-save'
        };

*/
        console.log(dropletData);

        api.dropletsCreate(dropletData, function(err, resp, body) {
            if (err) {
                sender.send('updateMonitor', {
                    no: no,
                    msg: 'An error occured while trying to create your droplet.',
                    username: info.username,
                    password: info.password,
                    ip: 'n/a',
                    error: true
                });
                console.log(`[${no}] Error creating droplet.`);
                console.log(err);
                return callback(null, true);
            }

            setTimeout(function() {

                if (stopped) {
                    destroyDroplet(id, api, function(err, resp) {
                        if (err) {
                            return callback(null, true);
                        }
                        return callback(null, true);
                    });
                }

                api.dropletsGetAll({}, function(err, resp, body) {

                    id = _.findWhere(resp.body.droplets, {
                        name: dropletName
                    }).id

                    host = _.findWhere(resp.body.droplets, {
                        name: dropletName
                    }).networks.v4[0].ip_address

                    var para = null;

                    if (eSettings.getSync('ssh_passphrase') != null) {
                        para = eSettings.getSync('ssh_passphrase');
                    }

                    if (stopped) {
                        destroyDroplet(id, api, function(err, resp) {
                            if (err) {
                                return callback(null, true);
                            }
                            return callback(null, true);
                        });
                    }

                    if (stopped) {
                        destroyDroplet(id, api, function(err, resp) {
                            if (err) {
                                return callback(null, true);
                            }
                            return callback(null, true);
                        });
                    } else {
                        sender.send('updateMonitor', {
                            no: no,
                            msg: `Droplet Created`,
                            username: info.username,
                            password: info.password,
                            ip: host,
                            error: false
                        });
                    }

                        console.log("http://" + info.username + ":" + info.password + "@" + host + ":" + '3128')


                        var count = 119;
                        for (var i = 0; i < 119; i++) {

                          setTimeout(function() {
                            sender.send('updateMonitor', {
                                no: no,
                                msg: `Testing Proxy in ${count}s`,
                                username: info.username,
                                password: info.password,
                                ip: host,
                                error: false
                            });
                            count--;
                          }, 1000*i);

                        }


                        setTimeout(function() {
                            request({
                                method: 'get',
                                url: 'https://google.com/',
                                proxy: "http://" + info.username + ":" + info.password + "@" + host + ":" + '3128',
                                gzip: true,
                                headers: {
                                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3107.4 Safari/537.36'
                                }
                            }, (error, resp, body) => {

                                if (error) {
                                    console.log(error);
                                    sender.send('updateMonitor', {
                                        no: no,
                                        msg: `Proxy Invalid, destroying droplet.`,
                                        username: info.username,
                                        password: info.password,
                                        ip: host,
                                        error: true
                                    });

                                    destroyDroplet(id, api, function(err, resp) {
                                        if (err) {
                                            sender.send('updateMonitor', {
                                                no: no,
                                                msg: `Error Occured while destroying droplet due to bad proxy Connection.`,
                                                username: info.username,
                                                password: info.password,
                                                ip: host,
                                                error: true
                                            });
                                            return callback(null, true);
                                        }

                                        sender.send('updateMonitor', {
                                            no: no,
                                            msg: `Droplet Destroyed due to bad proxy connection.`,
                                            username: info.username,
                                            password: info.password,
                                            ip: host,
                                            error: true
                                        });

                                        return callback(null, true);

                                    });

                                } else {
                                    sender.send('updateMonitor', {
                                        no: no,
                                        msg: `Created!`,
                                        username: info.username,
                                        password: info.password,
                                        ip: host,
                                        error: false
                                    });

                                    console.log(`[${no}] Created!`);
                                    return callback(null, true);
                                }

                            });
                        }, 120000);

                });


            }, 60000);

        });


    }

}

function destroyDroplet(id, api, cb) {
    api.dropletsDelete(id, function(err, resp, body) {
        if (err) {
            return cb(true, null);
        }
        return cb(null, true)
    });
}

module.exports = {
    task: task
};


================================================
FILE: index.js
================================================
const electron = require('electron')
const {
    app,
    BrowserWindow,
    Menu
} = electron
const settings = require('./settings-manager')
const eSettings = require('electron-settings')
const create = require('./create')
const path = require('path')
const async = require('async')
const ChildProcess = require('child_process')
var DigitalOcean = require('do-wrapper').default,
    api = null;

var win, settingsWin;

const debug = /--debug/.test(process.argv[2])

// Launch Menu Spawn System

var createShortcut = function(callback) {
    spawnUpdate([
        '--createShortcut',
        path.basename(process.execPath),
        '--shortcut-locations',
        'StartMenu'
    ], callback)
}

var removeShortcut = function(callback) {
    spawnUpdate([
        '--removeShortcut',
        path.basename(process.execPath)
    ], callback)
}

var spawnUpdate = function(args, callback) {
    var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe')
    var stdout = ''
    var spawned = null

    try {
        spawned = ChildProcess.spawn(updateExe, args)
    } catch (error) {
        if (error && error.stdout == null)
            error.stdout = stdout
        process.nextTick(function() {
            callback(error)
        })
        return
    }

    var error = null

    spawned.stdout.on('data', function(data) {
        stdout += data
    })

    spawned.on('error', function(processError) {
        if (!error)
            error = processError
    })

    spawned.on('close', function(code, signal) {
        if (!error && code !== 0) {
            error = new Error('Command failed: ' + code + ' ' + signal)
        }
        if (error && error.code == null)
            error.code = code
        if (error && error.stdout == null)
            error.stdout = stdout
        callback(error)
    })
}

switch (process.argv[1]) {
    case '--squirrel-install':
        createShortcut(function() {
            app.quit()
        });
        break;
    case '--squirrel-uninstall':
        removeShortcut(function() {
            app.quit();
        });
        break;
    case '--squirrel-obsolete':
    case '--squirrel-updated':
        app.quit();
        break;
    default:
        init();
}

function init() {
    app.on('ready', () => {

        settings.init()
        app.ep = {
            settings
        }

        win = new BrowserWindow({
            width: 750,
            height: 670,
            minWidth: 750,
            minHeight: 670,
            resizable: true,
            maxWidth: 750,
            maxHeight: 640,
            fullscreenable: false,
            frame: true,
            show: true,
            icon: `${__dirname}/static/icon.png`
        })
        const menuTemplate = [{
                label: 'File',
                submenu: [{
                        label: 'Settings',
                        click() {
                            initSettings()
                        },
                        accelerator: 'CmdOrCtrl+,',
                    },
                    {
                        label: 'Quit',
                        click() {
                            app.quit()
                        },
                        accelerator: 'CmdOrCtrl+Q',
                    }
                ]
            },
            {
                label: 'Edit',
                submenu: [{
                        role: 'copy'
                    },
                    {
                        role: 'paste'
                    },
                    {
                        role: 'pasteandmatchstyle'
                    },
                    {
                        role: 'delete'
                    },
                    {
                        role: 'selectall'
                    }
                ]
            },
            {
                label: 'View',
                submenu: [{
                    label: 'Reload',
                    accelerator: 'CmdOrCtrl+R',
                    click(item, focusedWindow) {
                        if (focusedWindow) focusedWindow.reload()
                    }
                }]
            },
            {
                role: 'window',
                submenu: [{
                        role: 'minimize'
                    },
                    {
                        role: 'close'
                    }
                ]
            },
            {
                role: 'help',
                submenu: [{
                        label: 'Learn More about EasyProxy',
                        click() {
                            require('electron').shell.openExternal('github.com/dzt/easy-proxy')
                        }
                    },
                    {
                        label: 'Toggle Developer Tools',
                        accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
                        click(item, focusedWindow) {
                            if (focusedWindow) focusedWindow.webContents.toggleDevTools()
                        }
                    }
                ]
            }
        ]

        // If the platform is Mac OS, make some changes to the window management portion of the menu
        if (process.platform === 'darwin') {
            menuTemplate[2].submenu = [{
                    label: 'Close',
                    accelerator: 'CmdOrCtrl+W',
                    role: 'close'
                },
                {
                    label: 'Minimize',
                    accelerator: 'CmdOrCtrl+M',
                    role: 'minimize'
                },
                {
                    label: 'Zoom',
                    role: 'zoom'
                },
                {
                    type: 'separator'
                },
                {
                    label: 'Bring All to Front',
                    role: 'front'
                }
            ]
        }

        // Set menu template just created as the application menu
        const mainMenu = Menu.buildFromTemplate(menuTemplate)
        Menu.setApplicationMenu(mainMenu)
        win.setMenu(null);
        win.loadURL(`file://${__dirname}/static/index.html`);
    })
}

electron.ipcMain.on('create', function(event, args) {
    var tasks = []
    args.map(function(task, i) {
        tasks.push(function(cb) {
            create.task(win, task, settings, i + 1, (err, response) => {
                if (err) {
                    return (err)
                }
                return cb(null, response)
            })
        })
    })
    async.parallel(tasks, function(err, res) {
        if (err) {
            console.log('err', err)
        } else {
            console.log(res)
            // TODO: When Session Ends
            win.webContents.send('tasksEnded');
        }
    });
});

electron.ipcMain.on('openSettings', function(event, args) {
    initSettings();
});

electron.ipcMain.on('open-file-dialog', function(event) {
    require('electron').dialog.showOpenDialog({
        properties: ['openFile'],
        filters: [{
            name: 'All Files',
            extensions: ['*']
        }]
    }, function(filename) {
        if (filename) {
            console.log(filename[0]);
            event.sender.send('selected-file', filename[0]);
        }
    })
});

electron.ipcMain.on('wipeDroplets', function(event) {
    api = new DigitalOcean(eSettings.getSync('do_api_key'));
    var droplets = [];
    api.dropletsGetAll({}, function(err, resp, body) {
        if (err) {
            console.log(err);
            event.sender.send('errDestroy');
        }

        for (var i = 0; i < body.droplets.length; i++) {
            var id = body.droplets[i].id;
            var dropletName = body.droplets[i].name;
            if (dropletName.endsWith('-ep')) {
                api.dropletsDelete(id, function(err, resp, body) {});
            }
        }

        event.sender.send('wipe-complete');

        //console.log(body);
    });
});

electron.ipcMain.on('resetApp', (event, args) => {
    win.close()
    settingsWin.close()
    app.quit();
})

electron.ipcMain.on('refreshMainWindow', (event, args) => {
    win.webContents.send('refreshMain');
})

electron.ipcMain.on('fetchForImages', function(event) {

    var options = [];
    var regionDict = [];

    api = new DigitalOcean(eSettings.getSync('do_api_key'));

    function fetchFullRegionName(shortName) {
        for (var i = 0; i < regionDict.length; i++) {
            if (regionDict[i].slug == shortName) {
                return regionDict[i].fullName;
            }
        }
    }

    // Fetch for Regions and Slug Names
    api.regionsGetAll({}, function(err, resp, body) {
        if (err) {
            // Return Error to Window
            console.log('err', err);
            win.webContents.send('initError');
            return
        }

        for (var i = 0; i < body.regions.length; i++) {
            regionDict.push({
                fullName: body.regions[i].name,
                slug: body.regions[i].slug
            })
        }

        api.imagesGetAll({type: 'distribution'}, function(err, resp, body) {
            if (err) {
                // Return Error to Window
                win.webContents.send('initError');
                return
            }

            for (var i = 0; i < body.images.length; i++) {
                // Look for 64bit versions of CentOS 7
                if (body.images[i].distribution.indexOf('CentOS') > -1) {
                    if (body.images[i].name.split(' ')[0].startsWith('7')) {
                        for (var x = 0; x < body.images[i].regions.length; x++) {

                            if (fetchFullRegionName(body.images[i].regions[x]) != undefined) {
                                options.push({
                                    title: `CentOS ${body.images[i].name} - ${body.images[i].id} - (${fetchFullRegionName(body.images[i].regions[x])})`,
                                    region: body.images[i].regions[x],
                                    slug: body.images[i].id
                                })
                            }
                        }

                    }
                }
            }

            win.webContents.send('updateOptionList', options);

        });
    });

});

function initSettings() {
    settingsWin = new electron.BrowserWindow({
        backgroundColor: '#ffffff',
        center: true,
        fullscreen: false,
        height: 700,
        icon: `${__dirname}/static/icon.png`,
        maximizable: false,
        minimizable: false,
        resizable: false,
        show: false,
        skipTaskbar: true,
        title: 'Settings',
        useContentSize: true,
        width: 550
    })

    settingsWin.loadURL(`file://${__dirname}/static/settings.html`);
    // No menu on the About settingsWindow
    settingsWin.setMenu(null);
    //settingsWin.webContents.openDevTools()
    settingsWin.once('ready-to-show', function() {
        settingsWin.show()
    })

    settingsWin.once('closed', function() {
        aboutWin = null
    })

    return settingsWin.show()
}


================================================
FILE: package.json
================================================
{
    "name": "EasyProxy",
    "version": "2.1.2",
    "description": "Easy Proxy Creation",
    "main": "index.js",
    "scripts": {
        "start": "electron .",
        "dev": "electron . --debug",
        "dist:win": "build --windows --x64 --dir",
        "dist:mac": "build --mac",
        "installer": "node scripts/installer.js"
    },
    "author": "Peter Soboyejo <thepcmrtim@gmail.com>",
    "license": "MIT",
    "repository": {
        "type": "git",
        "url": "git://github.com/dzt/easy-proxy.git"
    },
    "bugs": {
        "url": "https://github.com/dzt/easy-proxy/issues"
    },
    "dependencies": {
        "async": "^2.6.0",
        "bootstrap": "^3.3.7",
        "console.table": "^0.8.0",
        "copy-paste": "^1.3.0",
        "do-wrapper": "^3.11.1",
        "electron-config": "^1.0.0",
        "electron-settings": "^2.2.2",
        "font-awesome": "^4.7.0",
        "jquery": "^3.2.1",
        "object-path": "^0.11.4",
        "path": "^0.12.7",
        "prompt": "^1.0.0",
        "randomstring": "^1.1.5",
        "request": "^2.83.0",
        "rimraf": "^2.5.4",
        "underscore": "^1.8.3"
    },
    "devDependencies": {
        "electron": "1.3.4",
        "electron-winstaller": "^2.5.0",
        "electron-builder": "^19.13.0"
    },
    "build": {
        "productName": "EasyProxy",
        "appId": "com.petersoboyejo.easyproxy",
        "mac": {
            "target": "default",
            "icon": "static/icons/logo.icns",
            "identity": null
        },
        "win": {
            "publisherName": "EasyProxy",
            "target": "squirrel",
            "icon": "static/icons/logo.ico"
        }
    }
}


================================================
FILE: scripts/installer.js
================================================
const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller
const path = require('path')
const rimraf = require('rimraf')

deleteOutputFolder()
  .then(getInstallerConfig)
  .then(createWindowsInstaller)
  .catch((error) => {
    console.log('err')
    console.error(error.message || error)
    process.exit(1)
  })

function getInstallerConfig () {
  const rootPath = path.join(__dirname, '..')
  const outPath = path.join(rootPath, 'dist')
  return Promise.resolve({
    appDirectory: path.join(outPath, 'win-unpacked'),
    noMsi: true,
    outputDirectory: path.join(outPath, 'windows-installer'),
    setupExe: 'EasyProxySetup.exe',
    setupIcon: path.join(rootPath, 'static', 'icons', 'logo.ico'),
    skipUpdateIcon: true
  })
}

function deleteOutputFolder () {
  return new Promise((resolve, reject) => {
    rimraf(path.join(__dirname, '..', 'out', 'windows-installer'), (error) => {
      error ? reject(error) : resolve()
    })
  })
}

================================================
FILE: settings-manager.js
================================================
const homedir = require('os').homedir
const app = require('electron').app
const settings = require('electron-settings')
const objectPath = require('object-path')

const DEFAULTS = {
    filePath: null,
    do_api_key: null,
    ip_auth: false,
    ips: null
};

// we need to sync every setting that can be modified externally
// e.g. the `openOnStartup` setting can be modified via
// macOS' System Preferences.app

function sync() {
  settings.setSync('openOnStartup', app.getLoginItemSettings().openAtLogin);
}

function init() {
  settings.defaults(DEFAULTS);
  settings.applyDefaultsSync();
  sync();
}

function get(key) {
  sync();
  return objectPath.get(key) || settings.getSync(key);
}

function getAll() {
  sync();
  return settings.getSync();
}

function set(key, value) {
  settings.setSync(key, value);
}

function observe(keyPath, handler) {
  return settings.observe(keyPath, handler);
}

module.exports = {
  get: get,
  getAll: getAll,
  set: set,
  observe: observe,
  init: init
};


================================================
FILE: static/app.js
================================================
require("copy-paste").global()

const remote = require('electron').remote
const app = remote.app
const $ = require('jquery')
const ipcRenderer = require('electron').ipcRenderer
const settings = require('electron-settings')
const randomstring = require('randomstring')

var settingsValues = app.ep.settings.getAll();

// Fetch for Images as soon as Window is loaded

if (settingsValues.do_api_key == null || settingsValues.do_api_key == "") {
    $("#fetching").html('<option value="fetching" id="fetching">Digital Ocean API Key Missing</option>');
} else {
    ipcRenderer.send('fetchForImages');
}

ipcRenderer.on('initError', function(event, data) {

    $("#fetching").html('<option value="fetching" id="fetching">An error has occured while trying to fetch data from DigitalOcean, try checking your API Key or refresh.</option>');

});

// When Images are Fetched return data to DOM
ipcRenderer.on('updateOptionList', function(event, data) {

    if (data.length == 0) {
        $("#fetching").html('<option value="fetching" id="fetching">No CentOS 7 Servers are available at the time.</option>');
    } else {
        for (var i = 0; i < data.length; i++) {
            $('#sel1').append(`<option value="${data[i].title}" region="${data[i].region}" slug="${data[i].slug}">${data[i].title}</option>`);
        }

        $("#fetching").remove();
        $("#sel1").prop("disabled", false);
        $("#createButton").prop("disabled", false);
    }

});

ipcRenderer.on('refreshMain', function(event) {
  remote.getCurrentWindow().reload();
});

// Create Proxies Button
$('#createButton').click(() => {

    if ($("#count").val() == "") {

        alert("You must set the number of proxies you want to create before performing this action.");

    } else if (settingsValues.do_api_key == null) {

        alert("You're missing crutial settings required to create proxies, please check your settings and try again.");

    } else {


        $("#results").empty();

        $("#createButton").prop("disabled", true);
        $("#sel1").prop("disabled", true);
        $("#count").prop("disabled", true);
        $("#clearLogsButton").prop("disabled", true);
        $("#createButton").text('Creating...');

        var proxyCount = parseInt($('#count').val())

        var tasks = [];

        for (var i = 0; i < proxyCount; i++) {

            var username = randomstring.generate({
                length: 6,
                charset: 'alphabetic',
                capitalization: 'lowercase'
            });

            var password = randomstring.generate({
                length: 6,
                charset: 'alphabetic',
                capitalization: 'lowercase'
            });

            tasks.push({
                username: username,
                password: password,
                slug: $('#sel1').find(":selected").attr('slug'),
                region: $('#sel1').find(":selected").attr('region')
            })
        }

        ipcRenderer.send('create', tasks);
    }
});

// Update List Items in realtime

ipcRenderer.on('updateMonitor', function(event, data) {
    // TODO: Add Timestamp
    if ($(`#${data.username}`).length) {
        // update exisiting item

        if (data.error) {
            var newlyAddedUpdate = `
            <tr id="${data.username}">
            <td id="no">#${data.no}</td>
            <td>${data.ip}</td>
            <td>3128</td>
            <td>${data.username}</td>
            <td>${data.password}</td>
            <td style="color: red;">${data.msg}</td>
          </tr>`
        } else {
            var newlyAddedUpdate = `
            <tr id="${data.username}">
            <td id="no">#${data.no}</td>
            <td>${data.ip}</td>
            <td>3128</td>
            <td>${data.username}</td>
            <td>${data.password}</td>
            <td style="color: green;">${data.msg}</td>
          </tr>`
        }
        $(`#${data.username}`).replaceWith(newlyAddedUpdate);
    } else {
        var newlyAddedUpdate = `
      <tr id="${data.username}">
        <td id="no">#${data.no}</td>
        <td>${data.ip}</td>
        <td>3128</td>
        <td>${data.username}</td>
        <td>${data.password}</td>
        <td style="color: green;">${data.msg}</td>
      </tr>
    `
        $('#results').append(newlyAddedUpdate);
    }
});

$('#refresh').click(() => {
    remote.getCurrentWindow().reload();
});

$('#copyToClipboardButton').click(() => {

    var content = [];
    $("tr").each(function(i) {
        if (i != 0) {
            if ($(this).has(`td:contains('Created!')`).length) {
                var ip = $(this).find('td').eq(1).text();
                var user = $(this).find('td').eq(3).text();
                var pass = $(this).find('td').eq(4).text();
                content.push(`${ip}:3128:${user}:${pass}`);
            }
        }
    });
    // use join function
    copy(content.join('\n'))
});

$('#clearLogsButton').click(() => {
    $("#results").empty();
});

$('#cancelTasksButton').click(() => {
    ipcRenderer.send('stopTasks');
});

$('#settingsButton').click(() => {
    ipcRenderer.send('openSettings');
});

// When all tasks are done doing there thing bring everything back to normal and purge old stuff

ipcRenderer.on('tasksEnded', function(event, data) {
    $("#createButton").prop("disabled", false);
    $("#sel1").prop("disabled", false);
    $("#count").prop("disabled", false);
    $("#clearLogsButton").prop("disabled", false);
    $("#createButton").text('Create');
});


================================================
FILE: static/index.html
================================================
<!doctype html>
<html lang="en">
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>EasyProxy</title>
      <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
      <link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
      <link rel="stylesheet" href="main.css">
   </head>
   <body>
      <div class="main">
         <center>
            <br>
            <hr width="100%">
            <div style="width: 80%;">
               <div class="form-group">
                  <label>Number of Proxies:</label>
                  <input type="number" placeholder="Example: 5" class="form-control" id="count">
               </div>
               <div class="form-group">
                  <select class="form-control" id="sel1" disabled>
                     <option value="fetching" id="fetching">Fetching Images...</option>
                  </select>
               </div>
               <button type="submit" class="btn btn-success" id="createButton" style="width: 100%; outline: none;" disabled>Create</button>
               <br><br>
               <div class="btn-group">
                  <button type="button" id="copyToClipboardButton" class="btn btn-default" aria-label="Left Align">Copy to Clipboard</button>
                  <button type="button" id="clearLogsButton" class="btn btn-default" aria-label="Center Align">Clear Logs</button>
                  <button type="button" id="cancelTasksButton" class="btn btn-default" aria-label="Right Align">Cancel Task(s)</button>
                  <button type="button" id="settingsButton" class="btn btn-default" aria-label="Justify">Settings</button> 
               </div>
               <hr style="width: 80%;">
            </div>
            <br>
            </hr>
            <table class="table table-condensed" style="width: 80%;">
               <thead>
                  <tr>
                     <th>#</th>
                     <th>IP/Host</th>
                     <th>Port</th>
                     <th>Username</th>
                     <th>Password</th>
                     <th>Status</th>
                  </tr>
               </thead>
               <tbody id="results">
                  <!-- <tr>
                     <td>1</td>
                     <td>108.20.181.185</td>
                     <td>3128</td>
                     <td>Z2IoAD0Wec</td>
                     <td>5095131428</td>
                     <td style="color: green;">Creating Instance</td>
                     </tr> -->
               </tbody>
            </table>
            <br><br>
         </center>
         <footer>
            <div class="status">
               <a class="fa fa-refresh" id="refresh" style="text-decoration: none; cursor: pointer;"></a>&nbsp;&nbsp; Refresh
            </div>
         </footer>
      </div>
      <script>require('./app.js')</script>
   </body>
</html>

================================================
FILE: static/main.css
================================================
body {
  background: white;
}
:not(input):not(textarea),
:not(input):not(textarea)::after,
:not(input):not(textarea)::before {
    -webkit-user-select: none;
    user-select: none;
    cursor: default;
}
input, button, textarea, :focus {
    outline: none; // You should add some other style for :focus to help UX/a11y
}

a:not([draggable=true]), img:not([draggable=true]) {
    -webkit-user-drag: none;
    user-drag: none; /* Technically not supported in Electron yet */
}
a[href^="http://"],
a[href^="https://"],
a[href^="ftp://"] {
    -webkit-user-drag: auto;
    user-drag: auto; /* Technically not supported in Electron yet */
}

.main footer {
    position: fixed;
    right: 0px;
    bottom: 0px;
    left: 0px;
    background-color: #EEEEEE;
    color: #666;
    border-style: solid;
    border-width: 1px;
    border-color: #fff #fff #fff #fff;
    font-size: 12px;
    padding: 9px 24px;
    overflow: hidden;
    z-index: 999;
}
.main footer .status div {
    display: none;
}
.main footer .status div:before {
    content: '';
    display: inline-block;
    width: 9px;
    height: 9px;
    background-color: #ccc;
    border-radius: 100px;
    margin-right: 6px;
}
.main footer .status .connected:before {
    background-color: #66bc66;
}
.main footer .status .connecting:before, .main footer .status .updating:before {
    background-color: #f04646;
    -webkit-animation: 3s indicator infinite;
}
.main footer .status .disconnected:before {
    background-color: #de7b7b;
}
.main footer .update {
    float: right;
}
.main footer .update a {
    display: inline;
    padding: 3px 9px;
}


================================================
FILE: static/settings.html
================================================
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">

    <link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
    <style>
      body {
        background-color: white;
        font-family: BlinkMacSystemFont, 'Helvetica Neue', Helvetica, sans-serif;
        text-align: center;
        overflow: hidden;
        outline: none;
        font-size: 16px;
        -webkit-user-select: none;
      }
      img {
        width: 100px;
        height: 100px;
      }
      h1 {
        font-size: 0.9em;
        -webkit-user-select: text;
      }
      p {
        font-size: 0.8em;
        -webkit-user-select: text;
      }
      input[type="radio"] {
        font-size: 0.8em;
      }
      input {
        outline: none;
      }
      a {
        text-decoration: none;
      }

      .btn-file {
        position: relative;
        overflow: hidden;
      }
      .btn-file input[type=file] {
        position: absolute;
        top: 0;
        right: 0;
        min-width: 100%;
        min-height: 100%;
        font-size: 100px;
        text-align: right;
        filter: alpha(opacity=0);
        opacity: 0;
        background: red;
        cursor: inherit;
        display: block;
      }
      input[readonly] {
        background-color: white !important;
        cursor: text !important;
      }

    </style>
  </head>
  <body>

    <hr>

    <img src="./icon.png">
    <p>
      Version <script>document.write(require('../package.json').version)</script>
      (<script>document.write(process.arch === 'x64' ? '64-bit' : '32-bit')</script>)
      <br><br>
      Written by <a href="https://github.com/dzt">@dzt</a></a>
    </p>

    <hr>

    <center>

        <ul class="nav nav-tabs" style="width: 400px;">
            <li role="presentation" class="active">
              <a data-toggle="pill" href="#menu1">General</a>
            </li>
            <li role="presentation">
              <a data-toggle="pill" href="#menu2">Forwarding</a>
            </li>
          </ul>

      <div class="tab-content">
        <div id="menu1" class="tab-pane fade in active">

            <h5>Digital Ocean API Key</h5>
            <input type="text" style="width: 400px;" class="form-control" id="doKey">

            <h5>Authorized IPs</h5>
            <textarea class="form-control" style="width: 400px;" rows="3" disabled>Coming Soon...</textarea>

            <br>

            <!-- <div class="form-check" style="font-size: 14px;">
              <input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
              Enable IP Authorization
            </div> -->

            <!-- <div class="form-check" style="font-size: 14px;">
              <input class="form-check-input" type="checkbox" value="" id="defaultCheck1">
              Disable User/Pass
            </div> -->

            <br>

            <button type="submit" class="btn btn-success" id="saveSettings" style="width: 400px; outline: none;">Save</button>
            <br><br>
            <button type="submit" class="btn btn-danger" id="destroy" style="width: 400px; outline: none;">Destroy All Droplets</button>
            <br><br>
            <button type="submit" class="btn btn-warning" id="reset" style="width: 400px; outline: none;">Reset to Defaults</button>

          </div>

          <div id="menu2" class="tab-pane fade">
              <h3>Coming Soon...</h3>
          </div>

        </div>

      </center>

  </body>
  <script>
      if (typeof module === 'object') {
        window.module = module;
        module = undefined;
      }
   </script>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  <script>require('./settings.js')</script>
</html>


================================================
FILE: static/settings.js
================================================
const remote = require('electron').remote;
const app = remote.app;
const settings = require('electron-settings');
const $ = require('jquery')
const Config = require('electron-config');
const config = new Config();
const dialog = remote.dialog;
const ipcRenderer = require('electron').ipcRenderer

var settingsValues = app.ep.settings.getAll();
var settingsVal = config.get('settingsVal')
var shell = require('electron').shell;


$("#filePathBtn").click(function(event){
    event.preventDefault();
    ipcRenderer.send('open-file-dialog');
});

$('#passphrase').val(settingsValues.ssh_passphrase);
$('#doKey').val(settingsValues.do_api_key);
$('#filePath').val(settingsValues.filePath);
$('#keyName').val(settingsValues.do_ssh_key_name);

ipcRenderer.on('selected-file', function(event, filename) {
    $('#filePath').val(filename);
    settingsValues.filePath = filename
    app.ep.settings.set('filePath', filename);
});

$('#passphrase').change(function() {
    settingsValues.ssh_passphrase = $(this).val()
    app.ep.settings.set('ssh_passphrase', $(this).val());
});

$('#keyName').change(function() {
    settingsValues.do_ssh_key_name = $(this).val()
    app.ep.settings.set('do_ssh_key_name', $(this).val());
});

$('#doKey').change(function() {
    settingsValues.do_api_key = $(this).val()
    app.ep.settings.set('do_api_key', $(this).val());
});

$('#saveSettings').click(() => {
    ipcRenderer.send('refreshMainWindow');
});

$('#destroy').click(() => {
  $("#destroy").prop("disabled", true);
  if (settingsValues.do_api_key == null || settingsValues.do_api_ke == "") {
    $('#destroy').text('No API Key was found');
    setTimeout(function(){
      $('#destroy').text('Destroy');
      $("#destroy").prop("disabled", false);
    }, 5000);
  } else {
    $('#destroy').text('Destroying...');
    ipcRenderer.send('wipeDroplets');
  }
});

$('#reset').click(() => {
  dialog.showMessageBox({
      "message": `Are you sure you want to reset?`,
      "detail": "You will not be able to recover any task data after you perform this action.",
      "buttons": ["Ok", "Cancel"],
  }, function(response) {
      switch (response) {
          case 0:
              ok();
          case 1:
              break;
      }
  });
  function ok() {
    settings.resetToDefaults()
    ipcRenderer.send('resetApp');
  }
});

ipcRenderer.on('errDestroy', function(event, data) {

  $('#destroy').text('Error Occured while destroying');

  setTimeout(function(){
    $('#destroy').text('Destroy');
    $("#destroy").prop("disabled", false);
  }, 5000);

});

ipcRenderer.on('wipe-complete', function(event, data) {
  $('#destroy').text('All Droplets have been destroyed');

  setTimeout(function(){
    $('#destroy').text('Destroy');
    $("#destroy").prop("disabled", false);
  }, 5000);
});
Download .txt
gitextract_h1rwq2co/

├── .gitignore
├── LICENSE
├── README.md
├── confg/
│   ├── nouserpass/
│   │   └── squid.conf
│   └── userpass/
│       └── squid.conf
├── create.js
├── index.js
├── package.json
├── scripts/
│   └── installer.js
├── settings-manager.js
└── static/
    ├── app.js
    ├── icons/
    │   └── logo.icns
    ├── index.html
    ├── main.css
    ├── settings.html
    └── settings.js
Download .txt
SYMBOL INDEX (15 symbols across 5 files)

FILE: create.js
  function createDroplet (line 42) | function createDroplet() {
  function destroyDroplet (line 264) | function destroyDroplet(id, api, cb) {

FILE: index.js
  function init (line 96) | function init() {
  function fetchFullRegionName (line 309) | function fetchFullRegionName(shortName) {
  function initSettings (line 366) | function initSettings() {

FILE: scripts/installer.js
  function getInstallerConfig (line 14) | function getInstallerConfig () {
  function deleteOutputFolder (line 27) | function deleteOutputFolder () {

FILE: settings-manager.js
  constant DEFAULTS (line 6) | const DEFAULTS = {
  function sync (line 17) | function sync() {
  function init (line 21) | function init() {
  function get (line 27) | function get(key) {
  function getAll (line 32) | function getAll() {
  function set (line 37) | function set(key, value) {
  function observe (line 41) | function observe(keyPath, handler) {

FILE: static/settings.js
  function ok (line 77) | function ok() {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (53K chars).
[
  {
    "path": ".gitignore",
    "chars": 653,
    "preview": "config.json\ngs\nlogs\n*.log\nnpm-debug.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n\npackage-lock.json\n\n# Directory for instrumen"
  },
  {
    "path": "LICENSE",
    "chars": 1108,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2017 Peter Soboyejo <http://petersoboyejo.com/>\n\nPermission is hereby granted, free"
  },
  {
    "path": "README.md",
    "chars": 1835,
    "preview": "# easy-proxy\nMass proxy distribution made easy with the DigitalOcean API\n\n![screen](https://i.imgur.com/ZIabShH.png)\n\n[D"
  },
  {
    "path": "confg/nouserpass/squid.conf",
    "chars": 2257,
    "preview": "http_port 3128\ncache deny all\nhierarchy_stoplist cgi-bin ?\n\naccess_log none\ncache_store_log none\ncache_log /dev/null\n\nre"
  },
  {
    "path": "confg/userpass/squid.conf",
    "chars": 2251,
    "preview": "http_port 3128\ncache deny all\nhierarchy_stoplist cgi-bin ?\n\naccess_log none\ncache_store_log none\ncache_log /dev/null\n\nre"
  },
  {
    "path": "create.js",
    "chars": 10233,
    "preview": "const randomstring = require('randomstring')\nconst DigitalOcean = require('do-wrapper').default\nconst eSettings = requir"
  },
  {
    "path": "index.js",
    "chars": 11142,
    "preview": "const electron = require('electron')\nconst {\n    app,\n    BrowserWindow,\n    Menu\n} = electron\nconst settings = require("
  },
  {
    "path": "package.json",
    "chars": 1671,
    "preview": "{\n    \"name\": \"EasyProxy\",\n    \"version\": \"2.1.2\",\n    \"description\": \"Easy Proxy Creation\",\n    \"main\": \"index.js\",\n   "
  },
  {
    "path": "scripts/installer.js",
    "chars": 973,
    "preview": "const createWindowsInstaller = require('electron-winstaller').createWindowsInstaller\nconst path = require('path')\nconst "
  },
  {
    "path": "settings-manager.js",
    "chars": 1003,
    "preview": "const homedir = require('os').homedir\nconst app = require('electron').app\nconst settings = require('electron-settings')\n"
  },
  {
    "path": "static/app.js",
    "chars": 5498,
    "preview": "require(\"copy-paste\").global()\n\nconst remote = require('electron').remote\nconst app = remote.app\nconst $ = require('jque"
  },
  {
    "path": "static/index.html",
    "chars": 2993,
    "preview": "<!doctype html>\n<html lang=\"en\">\n   <head>\n      <meta charset=\"utf-8\">\n      <meta name=\"viewport\" content=\"width=devic"
  },
  {
    "path": "static/main.css",
    "chars": 1604,
    "preview": "body {\n  background: white;\n}\n:not(input):not(textarea),\n:not(input):not(textarea)::after,\n:not(input):not(textarea)::be"
  },
  {
    "path": "static/settings.html",
    "chars": 4021,
    "preview": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-wid"
  },
  {
    "path": "static/settings.js",
    "chars": 2792,
    "preview": "const remote = require('electron').remote;\nconst app = remote.app;\nconst settings = require('electron-settings');\nconst "
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the dzt/easy-proxy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (48.9 KB), approximately 11.4k tokens, and a symbol index with 15 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!