Skip to content

Commit 2c6bdb0

Browse files
committed
nanabox
0 parents  commit 2c6bdb0

18 files changed

+2140
-0
lines changed

.github/FUNDING.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patreon: axorax

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
*env/
2+
dist/
3+
build/
4+
node_modules/
5+
nanabox.json
6+
package-lock.json
7+
LICENSE.txt
8+
*.spec
9+
*.css
10+
*.pyd
11+
*.js
12+
*.c

LICENSE

Lines changed: 504 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<p align="center">
2+
<img src="./thumbnail.png" />
3+
</p>
4+
5+
## ✨ · Installation
6+
7+
### 💻 — Executable file:
8+
9+
1. Go to https://github.com/Axorax/nanabox/releases
10+
11+
2. Download a file according to your operating system.
12+
13+
### 🐍 — With Python:
14+
15+
1. Download the source code or run (in terminal):
16+
17+
```
18+
git clone https://github.com/Axorax/nanabox.git
19+
```
20+
21+
2. Change directory:
22+
23+
```
24+
cd nanabox
25+
```
26+
27+
3. Install Python dependencies:
28+
29+
```
30+
pip install -r requirements.txt
31+
```
32+
33+
4. Install JavaScript dependencies:
34+
35+
```
36+
npm install
37+
```
38+
39+
5. Run the app in dev mode:
40+
41+
```sh
42+
npm run dev
43+
```
44+
45+
## ❎ · File not uploading!
46+
47+
> [!IMPORTANT]
48+
> Nanabox uses other free third-party APIs to store the uploaded files. This does mean that the providers can use your uploaded content in any way they want so make sure to read their terms. You can change the API being used by clicking on `Select host` and changing it. Sometimes the APIs may go down then it won't work. You can also add your own API. For adding your own API, you need to provide a path that leads to the URL for the uploaded file in the JSON response, this is lambda. An example of it can be `lambda data: data['link']`
49+
50+
51+
---
52+
53+
<p align="center"><a href="https://www.patreon.com/axorax">Support me on Patreon</a> — <a href="https://github.com/axorax/socials">Check out my socials</a></p>

build.bat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@echo off
2+
3+
pyinstaller --name="nanabox" --onefile --paths=env\Lib\site-packages --add-data="static;static" --add-data="templates;templates" main.py --noconsole --icon=static/nanabox.ico

build.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyinstaller --name "nanabox" --onefile --paths env\Lib\site-packages --add-data "static;static" --add-data "templates;templates" main.py --noconsole --icon static\nanabox.ico

build.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/bash
2+
3+
pyinstaller --name="nanabox" --onefile --paths=env/Lib/site-packages --add-data="static:static" --add-data="templates:templates" main.py --noconsole --icon=static/nanabox.ico

main.py

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
from flask import Flask
2+
from flask import render_template
3+
from flask import request
4+
from flask import jsonify
5+
from flaskwebgui import FlaskUI
6+
import requests
7+
import json
8+
import concurrent.futures
9+
import multiprocessing
10+
import sys
11+
import logging
12+
13+
# ↓ Set to "False" when debugging then it will output to the terminal ↓
14+
15+
IN_APP_LOGS = True
16+
17+
class OutputCapturer:
18+
output = []
19+
20+
def __enter__(self):
21+
self._stdout = sys.stdout
22+
self._stderr = sys.stderr
23+
sys.stdout = self
24+
sys.stderr = self
25+
return self
26+
27+
def __exit__(self, exc_type, exc_val, exc_tb):
28+
sys.stdout = self._stdout
29+
sys.stderr = self._stderr
30+
31+
def write(self, text):
32+
self.output.append(text)
33+
34+
def flush(self):
35+
pass
36+
37+
app = Flask(__name__)
38+
39+
try:
40+
with open('nanabox.json', 'r') as file:
41+
json_file = json.load(file)
42+
except (FileNotFoundError, json.JSONDecodeError):
43+
json_file = {
44+
"hosts": {
45+
"fileio": {
46+
"url": "https://file.io",
47+
"link": "lambda data: data['link']"
48+
}
49+
},
50+
"history": {}
51+
}
52+
53+
hosts = json_file["hosts"]
54+
55+
def evaluate_function_string(func_str):
56+
def dynamic_function(data):
57+
return eval(func_str)
58+
return dynamic_function
59+
60+
for key, value in hosts.items():
61+
if isinstance(value, str):
62+
hosts[key] = evaluate_function_string(value)
63+
64+
@app.route('/')
65+
def home():
66+
return render_template('index.html')
67+
68+
# GET | Backend logs
69+
70+
@app.route('/logs')
71+
def send_logs():
72+
filtered_output = [line.decode() if isinstance(line, bytes) else line for line in OutputCapturer.output if line.strip()]
73+
return jsonify({'output': filtered_output})
74+
75+
# POST | Upload file(s)
76+
77+
def upload_file(url, file, host):
78+
print("[INFO] Uploading to host " + host)
79+
if host == "filebin":
80+
with requests.post(url, files={'file': (file.filename, file.read())}) as response:
81+
return response.json()
82+
else:
83+
with requests.post(url, files={'file': (file.filename, file.read())}) as response:
84+
return response.json()
85+
86+
@app.route('/upload', methods=['POST'])
87+
def upload():
88+
try:
89+
if 'file' not in request.files:
90+
print("[ERROR] No file provided")
91+
return jsonify({"error": "No file provided"}), 400
92+
93+
file = request.files['file']
94+
95+
if file.filename == '':
96+
print("[ERROR] No file selected")
97+
return jsonify({"error": "No file selected"}), 400
98+
99+
host = hosts.get(request.args.get('host'))
100+
url = host['url']
101+
102+
if not url:
103+
print("[ERROR] Invalid host!")
104+
return jsonify({"error": "Invalid host"}), 400
105+
106+
result_queue = multiprocessing.Queue()
107+
108+
with concurrent.futures.ThreadPoolExecutor() as executor:
109+
future = executor.submit(upload_file, url, file, request.args.get('host'))
110+
future.add_done_callback(lambda future: result_queue.put(future.result()))
111+
112+
data = result_queue.get()
113+
print("[SUCCESS] DATA RECEIVED: " + json.dumps(data))
114+
115+
file_link_fn_str = host['link']
116+
file_link = eval(file_link_fn_str)(data)
117+
file_name = file.filename
118+
119+
json_file["history"][file_name] = file_link
120+
121+
with open('nanabox.json', 'w') as file:
122+
json.dump(json_file, file)
123+
124+
return jsonify({
125+
"Link": file_link
126+
})
127+
except Exception as err:
128+
print(str(err))
129+
return jsonify({
130+
"error": "Host may be down! (check logs for more info)"
131+
})
132+
133+
# POST | Add host
134+
135+
@app.route('/add-host/<name>', methods=['POST'])
136+
def addHost(name):
137+
try:
138+
data = request.json
139+
json_file["hosts"][name] = data.get('get_link_from_json')
140+
141+
with open('nanabox.json', 'w') as file:
142+
json.dump(json_file, file)
143+
144+
return jsonify({
145+
"success": True
146+
})
147+
except Exception as err:
148+
print(str(err))
149+
return jsonify({
150+
"error": "Failed to add host!",
151+
"success": False
152+
})
153+
154+
# GET | Upload history
155+
156+
@app.route('/history', methods=['GET'])
157+
def send_history():
158+
try:
159+
with open('nanabox.json', 'r') as file:
160+
data = json.load(file)
161+
return jsonify(data["history"])
162+
except (FileNotFoundError, json.JSONDecodeError):
163+
return jsonify(["error"]), 404
164+
165+
# POST | Clear file upload history
166+
167+
@app.route('/clear', methods=['DELETE'])
168+
def clear_history():
169+
with open('nanabox.json', 'r+') as file:
170+
data = json.load(file)
171+
data["history"] = {}
172+
file.seek(0)
173+
json.dump(data, file)
174+
file.truncate()
175+
176+
return jsonify({"success": True})
177+
178+
# GET | Array of all hosts
179+
180+
@app.route('/hosts', methods=['GET'])
181+
def send_hosts():
182+
return jsonify(list(hosts.keys()))
183+
184+
if __name__ == '__main__':
185+
if (IN_APP_LOGS):
186+
app.logger.addHandler(logging.StreamHandler(sys.stderr))
187+
app.logger.setLevel(logging.INFO)
188+
189+
with OutputCapturer() as capturer:
190+
FlaskUI(app=app, server="flask", width=400, height=500).run()
191+
else:
192+
# app.run(debug=True) # Uncomment this line and comment the line below when developing (after ensuring "IN_APP_LOGS" is set to False)
193+
FlaskUI(app=app, server="flask", width=400, height=500).run()

0 commit comments

Comments
 (0)