Compare commits
22 Commits
022bc3109a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 69e7e66823 | |||
| 39917b7351 | |||
| 13d9cc9dc9 | |||
| db5a88972b | |||
| 98eb031c1c | |||
| 68a044a679 | |||
| ab84dea518 | |||
| 25617cb3c3 | |||
| 10b143193b | |||
| f9fca8c044 | |||
| 8298a4bae5 | |||
| 18a64279fd | |||
| 4629e12f67 | |||
|
|
0bdb09b23c | ||
| 763d360be6 | |||
|
|
f3f9979a0e | ||
|
|
bd93caa375 | ||
|
|
a48abc3b53 | ||
| c27377cd60 | |||
|
|
cc12b95c98 | ||
|
|
2be9e51805 | ||
|
|
b19fce5b4a |
15
.dockerignore
Normal file
15
.dockerignore
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.idea
|
||||||
|
__pycache__
|
||||||
|
.git
|
||||||
|
.dockerignore
|
||||||
|
Dockerfile
|
||||||
|
*.md
|
||||||
|
db.json
|
||||||
|
users.json
|
||||||
|
test.py
|
||||||
|
update_db.py
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
.vscode
|
||||||
|
uploads/
|
||||||
|
instructions.txt
|
||||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
FROM python:3.9.5
|
||||||
|
WORKDIR /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
COPY . .
|
||||||
|
COPY container/* .
|
||||||
|
RUN mkdir -p /app/uploads
|
||||||
|
EXPOSE 3133
|
||||||
|
|
||||||
|
CMD ["python", "app.py"]
|
||||||
96
app.py
96
app.py
@@ -5,17 +5,20 @@ from flask import (
|
|||||||
request,
|
request,
|
||||||
url_for,
|
url_for,
|
||||||
send_from_directory,
|
send_from_directory,
|
||||||
flash
|
flash,
|
||||||
|
jsonify
|
||||||
)
|
)
|
||||||
from utils import redirect, dbload, dbsave, udbload, udbsave
|
from utils import redirect, dbload, dbsave, udbload, udbsave
|
||||||
from werkzeug.exceptions import RequestEntityTooLarge
|
from werkzeug.exceptions import RequestEntityTooLarge
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import requests
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import logging.handlers
|
import logging.handlers
|
||||||
@@ -25,6 +28,7 @@ app = Flask('adrive', static_folder='static', template_folder='templates')
|
|||||||
app.config['UPLOAD_DIRECTORY'] = 'uploads/'
|
app.config['UPLOAD_DIRECTORY'] = 'uploads/'
|
||||||
app.config['MAX_CONTENT_LENGTH'] = 1000000 * 1024 * 1024
|
app.config['MAX_CONTENT_LENGTH'] = 1000000 * 1024 * 1024
|
||||||
app.config['SECRET_KEY'] = str(random.randint(99999, 9999999))
|
app.config['SECRET_KEY'] = str(random.randint(99999, 9999999))
|
||||||
|
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
|
||||||
|
|
||||||
# Redirect logs to the file 'logs'
|
# Redirect logs to the file 'logs'
|
||||||
handler = logging.handlers.RotatingFileHandler('logs', maxBytes=1024 * 1024)
|
handler = logging.handlers.RotatingFileHandler('logs', maxBytes=1024 * 1024)
|
||||||
@@ -33,38 +37,108 @@ logging.getLogger('werkzeug').addHandler(handler)
|
|||||||
app.logger.setLevel(logging.WARNING)
|
app.logger.setLevel(logging.WARNING)
|
||||||
app.logger.addHandler(handler)
|
app.logger.addHandler(handler)
|
||||||
|
|
||||||
|
@app.route('/get-location')
|
||||||
|
def get_location():
|
||||||
|
# Since you said /check-ip shows the correct IP, we use it directly
|
||||||
|
user_ip = request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
|
|
||||||
|
# Clean the IP if it's a list
|
||||||
|
if user_ip and ',' in user_ip:
|
||||||
|
user_ip = user_ip.split(',')[0].strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 1. We MUST send a User-Agent header or the API will block the request
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
# 2. Use ip-api.com (it's the most 'forgiving' for free testing)
|
||||||
|
response = requests.get(
|
||||||
|
f'http://ip-api.com/json/{user_ip}',
|
||||||
|
headers=headers,
|
||||||
|
timeout=5
|
||||||
|
)
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get('status') == 'success':
|
||||||
|
return jsonify({
|
||||||
|
"status": "success",
|
||||||
|
"city": data.get('city'),
|
||||||
|
"country": data.get('country')
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
# This helps you debug: what did the API actually say?
|
||||||
|
print(f"API Error Message: {data.get('message')}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Python Request Failed: {e}")
|
||||||
|
|
||||||
|
return jsonify({"status": "fail", "city": "Unknown", "country": "Location"})
|
||||||
|
|
||||||
|
@app.route('/check-ip')
|
||||||
|
def check_ip():
|
||||||
|
return {
|
||||||
|
"remote_addr": request.remote_addr,
|
||||||
|
"x_forwarded_for": request.headers.get('X-Forwarded-For'),
|
||||||
|
"actual_ip_used": request.headers.get('X-Forwarded-For', request.remote_addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.route('/ping')
|
||||||
|
def ping():
|
||||||
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return redirect(url_for('upload'))
|
return redirect(url_for('upload'))
|
||||||
|
|
||||||
@app.route('/dashboard')
|
@app.route('/dashboard')
|
||||||
def dashboard():
|
def dashboard():
|
||||||
|
# Load Databases
|
||||||
|
db = dbload()
|
||||||
|
udb = udbload()
|
||||||
|
|
||||||
|
# Check if user is a guest
|
||||||
if session.get('loggedIn', False) == False:
|
if session.get('loggedIn', False) == False:
|
||||||
flash('You must be signed in to view the dashboard!', 'error')
|
flash('You must be signed in to view the dashboard!', 'error')
|
||||||
return redirect(url_for('upload'))
|
return redirect(url_for('upload'))
|
||||||
db = dbload()
|
|
||||||
udb = udbload()
|
# Get user quota, usage, and files
|
||||||
username = session.get('username')
|
username = session.get('username')
|
||||||
userfiles = []
|
userfiles = []
|
||||||
quota_usage_gb = 0
|
quota_usage_gb = 0
|
||||||
|
|
||||||
if not username:
|
if not username:
|
||||||
flash('An error occurred. Please sign in again.', 'error')
|
flash('An error occurred. Please sign in again.', 'error')
|
||||||
session['loggedIn'] = False
|
session['loggedIn'] = False
|
||||||
session.pop('username', None)
|
session.pop('username', None)
|
||||||
return redirect(url_for('upload'))
|
return redirect(url_for('upload'))
|
||||||
|
|
||||||
for file in db['files']:
|
for file in db['files']:
|
||||||
if db['files'][file].get('owner') == username:
|
if db['files'][file].get('owner') == username:
|
||||||
db['files'][file]['file'] = file
|
db['files'][file]['file'] = file
|
||||||
db['files'][file]['code'] = file.split('_')[-1]
|
db['files'][file]['code'] = file.split('_')[-1]
|
||||||
userfiles.append(db['files'][file])
|
userfiles.append(db['files'][file])
|
||||||
|
|
||||||
for userfile in userfiles:
|
for userfile in userfiles:
|
||||||
usf_mb = userfile['size_megabytes']
|
usf_mb = userfile['size_megabytes']
|
||||||
if usf_mb:
|
if usf_mb:
|
||||||
quota_usage_gb += usf_mb / 1024
|
quota_usage_gb += usf_mb / 1024
|
||||||
|
|
||||||
quota_usage_gb = round(quota_usage_gb, 1)
|
quota_usage_gb = round(quota_usage_gb, 1)
|
||||||
return render_template('dashboard.html', files=userfiles, username=username, quota_gb=udb[[user['username'] for user in udb].index(username)]['quota_gb'], quota_usage=quota_usage_gb)
|
|
||||||
|
# Render template with variables
|
||||||
|
return render_template(
|
||||||
|
'dashboard.html',
|
||||||
|
files=userfiles,
|
||||||
|
username=username,
|
||||||
|
quota_gb=udb[[user['username'] for user in udb].index(username)]['quota_gb'],
|
||||||
|
quota_usage=quota_usage_gb
|
||||||
|
)
|
||||||
|
|
||||||
@app.route('/logout')
|
@app.route('/logout')
|
||||||
def logout():
|
def logout():
|
||||||
|
# Sign out user and redirect to upload page
|
||||||
session['loggedIn'] = False
|
session['loggedIn'] = False
|
||||||
session.pop('username', None)
|
session.pop('username', None)
|
||||||
flash('Successfully signed out!', 'info')
|
flash('Successfully signed out!', 'info')
|
||||||
@@ -72,43 +146,53 @@ def logout():
|
|||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
|
# Load User Database (UDB)
|
||||||
udb = udbload()
|
udb = udbload()
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# Get data from form
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
|
|
||||||
|
# Check if user exists and password matches, log them in
|
||||||
for user in udb:
|
for user in udb:
|
||||||
if user['username'] == username and user['password'] == password:
|
if user['username'] == username and user['password'] == password:
|
||||||
session['loggedIn'] = True
|
session['loggedIn'] = True
|
||||||
session['username'] = username
|
session['username'] = username
|
||||||
flash('Successfully signed in!', 'info')
|
flash('Successfully signed in!', 'info')
|
||||||
return redirect(url_for('upload'))
|
return redirect(url_for('upload'))
|
||||||
|
|
||||||
|
# If still here, login failed
|
||||||
flash('Invalid username or password!', 'error')
|
flash('Invalid username or password!', 'error')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@app.route('/register', methods=['GET', 'POST'])
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
|
# Load User Database (UDB)
|
||||||
udb = udbload()
|
udb = udbload()
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('register.html')
|
return render_template('register.html')
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
|
# Get data from form
|
||||||
username = request.form.get('username')
|
username = request.form.get('username')
|
||||||
password = request.form.get('password')
|
password = request.form.get('password')
|
||||||
|
|
||||||
|
# Check if username is already taken
|
||||||
for user in udb:
|
for user in udb:
|
||||||
if user['username'] == username:
|
if user['username'] == username:
|
||||||
flash('Username already taken!', 'error')
|
flash('Username already taken!', 'error')
|
||||||
return redirect(url_for('register'))
|
return redirect(url_for('register'))
|
||||||
|
|
||||||
|
# Create new user with default quota of 3GB
|
||||||
udb.append({
|
udb.append({
|
||||||
'username': username,
|
'username': username,
|
||||||
'password': password,
|
'password': password,
|
||||||
'quota_gb': 3
|
'quota_gb': 3
|
||||||
})
|
})
|
||||||
|
|
||||||
udbsave(udb)
|
udbsave(udb)
|
||||||
flash('Successfully registered! You can now sign in.', 'info')
|
flash('Successfully registered! You can now sign in.', 'info')
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
@@ -302,4 +386,4 @@ def download(code):
|
|||||||
flash('Invalid code! Check if you typed the correct code, and for one-time codes, make sure nobody else entered the code before you did.', 'error')
|
flash('Invalid code! Check if you typed the correct code, and for one-time codes, make sure nobody else entered the code before you did.', 'error')
|
||||||
return redirect(url_for('upload'))
|
return redirect(url_for('upload'))
|
||||||
|
|
||||||
app.run(debug=True, port=3133)
|
app.run(debug=True, port=3133, host='0.0.0.0')
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
Flask
|
Flask
|
||||||
|
requests
|
||||||
|
werkzeug
|
||||||
@@ -1,17 +1,4 @@
|
|||||||
if (localStorage.getItem("loaded")) {
|
|
||||||
document.getElementById("mirrorLogo").className = "fa-solid fa-circle-dot fa-2xs";
|
|
||||||
document.getElementById("mirrorText").textContent = "drive.fybe.dev";
|
|
||||||
document.getElementById("mirrorLogo").style.color = "#47cc00";
|
|
||||||
} else {
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById("mirrorLogo").style.color = "#47cc00";
|
|
||||||
document.getElementById("mirrorText").textContent = "drive.fybe.dev";
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById("mirrorLogo").className = "fa-solid fa-circle-dot fa-2xs";
|
|
||||||
localStorage.setItem("loaded", true);
|
|
||||||
}, 3000);
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const form = document.getElementById('uploadForm');
|
const form = document.getElementById('uploadForm');
|
||||||
@@ -19,6 +6,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
const fileInput = document.getElementById('fileUpload');
|
const fileInput = document.getElementById('fileUpload');
|
||||||
const progress = document.getElementById('uploadProgress');
|
const progress = document.getElementById('uploadProgress');
|
||||||
|
const progressContainer = document.getElementById('uploadProgressContainer');
|
||||||
const progressText = document.getElementById('uploadProgressText');
|
const progressText = document.getElementById('uploadProgressText');
|
||||||
const uploadBtn = document.getElementById('uploadBtn');
|
const uploadBtn = document.getElementById('uploadBtn');
|
||||||
|
|
||||||
@@ -42,8 +30,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (ev.lengthComputable) {
|
if (ev.lengthComputable) {
|
||||||
const percent = Math.round((ev.loaded / ev.total) * 100);
|
const percent = Math.round((ev.loaded / ev.total) * 100);
|
||||||
if (progress) {
|
if (progress) {
|
||||||
progress.style.display = 'block';
|
if (progressContainer) progressContainer.style.display = 'block';
|
||||||
progress.value = percent;
|
progress.style.width = percent + '%';
|
||||||
|
progress.setAttribute('aria-valuenow', percent);
|
||||||
|
progress.textContent = percent + '%';
|
||||||
}
|
}
|
||||||
if (progressText) {
|
if (progressText) {
|
||||||
progressText.style.display = 'block';
|
progressText.style.display = 'block';
|
||||||
@@ -82,7 +72,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
uploadBtn.classList.remove('yellowBtn');
|
uploadBtn.classList.remove('yellowBtn');
|
||||||
uploadBtn.value = 'Upload';
|
uploadBtn.value = 'Upload';
|
||||||
}
|
}
|
||||||
if (progress) { progress.style.display = 'none'; progress.value = 0; }
|
if (progress) {
|
||||||
|
if (progressContainer) progressContainer.style.display = 'none';
|
||||||
|
progress.style.width = '0%';
|
||||||
|
progress.setAttribute('aria-valuenow', 0);
|
||||||
|
progress.textContent = '0%';
|
||||||
|
}
|
||||||
if (progressText) { progressText.style.display = 'none'; progressText.textContent = '0%'; }
|
if (progressText) { progressText.style.display = 'none'; progressText.textContent = '0%'; }
|
||||||
// clear file input and reusable checkbox after successful upload
|
// clear file input and reusable checkbox after successful upload
|
||||||
if (fileInput) fileInput.value = '';
|
if (fileInput) fileInput.value = '';
|
||||||
@@ -110,7 +105,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
uploadBtn.value = 'Upload';
|
uploadBtn.value = 'Upload';
|
||||||
}
|
}
|
||||||
if (fileInput) fileInput.value = '';
|
if (fileInput) fileInput.value = '';
|
||||||
if (progress) { progress.style.display = 'none'; progress.value = 0; }
|
if (progress) {
|
||||||
|
if (progressContainer) progressContainer.style.display = 'none';
|
||||||
|
progress.style.width = '0%';
|
||||||
|
progress.setAttribute('aria-valuenow', 0);
|
||||||
|
progress.textContent = '0%';
|
||||||
|
}
|
||||||
if (progressText) progressText.style.display = 'none';
|
if (progressText) progressText.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
:root {
|
:root {
|
||||||
--black: #000000;
|
--black: #000000;
|
||||||
--almost-black: #080708;
|
--almostBlack: #888888;
|
||||||
--blue: #3772FF;
|
--blue: #3772FF;
|
||||||
--blue-light: #3787ff;
|
--blue-light: #3787ff;
|
||||||
--red: #DF2935;
|
--red: #DF2935;
|
||||||
@@ -19,9 +19,8 @@ body {
|
|||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: var(--white);
|
color: var(--black);
|
||||||
background: var(--black);
|
background: var(--white);
|
||||||
background-image: linear-gradient(to bottom right, var(--almost-black), var(--black));
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -35,15 +34,18 @@ body,html {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
width: 50%;
|
width: 80%;
|
||||||
border: 1px solid rgb(40, 40, 40);
|
border: 1px solid rgb(40, 40, 40);
|
||||||
|
border-radius: 8px;
|
||||||
padding-bottom: 40px;
|
padding-bottom: 40px;
|
||||||
|
padding: 50px;
|
||||||
|
color: var(--black) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-section {
|
.form-section {
|
||||||
padding: 15px 0 0 15px;
|
padding: 15px 0 0 15px;
|
||||||
width: 50%;
|
width: 100%;
|
||||||
color: var(--light);
|
color: var(--black);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +118,8 @@ button[type='button']:hover,
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.flashes li {
|
.flashes li {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
@@ -164,57 +168,16 @@ button[type='button']:hover,
|
|||||||
.dashboard-container {
|
.dashboard-container {
|
||||||
padding: 15px 15px 15px 15px;
|
padding: 15px 15px 15px 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--light);
|
color: var(--black);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
/* Table Styling */
|
|
||||||
|
/* Bootstrap-Friendly Table Design */
|
||||||
table {
|
table {
|
||||||
width: 100%;
|
font-size: 15px;
|
||||||
border-collapse: collapse;
|
|
||||||
margin: 20px 0;
|
|
||||||
background: rgba(20, 20, 20, 0.8);
|
|
||||||
border: 1px solid rgb(60, 60, 60);
|
|
||||||
border-radius: 6px;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
}
|
||||||
|
table tr td {
|
||||||
table th {
|
font-weight: light;
|
||||||
background: linear-gradient(to bottom, rgb(50, 50, 50), rgb(40, 40, 40));
|
|
||||||
padding: 14px 16px;
|
|
||||||
text-align: left;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--white);
|
|
||||||
border-bottom: 2px solid rgb(80, 80, 80);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td {
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-bottom: 1px solid rgb(40, 40, 40);
|
|
||||||
color: var(--light);
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:last-child td {
|
|
||||||
border-bottom: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table tr:hover {
|
|
||||||
background: rgba(55, 114, 255, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
table a {
|
|
||||||
color: var(--blue);
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
table a:hover {
|
|
||||||
color: var(--blue-light);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styled select for forms */
|
/* Styled select for forms */
|
||||||
@@ -276,6 +239,7 @@ select.fancy-select::-ms-expand {
|
|||||||
.quota-block {
|
.quota-block {
|
||||||
margin: 8px 0 18px;
|
margin: 8px 0 18px;
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
color: var(--almostBlack) !important;
|
||||||
}
|
}
|
||||||
.quota-donut {
|
.quota-donut {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -290,7 +254,8 @@ select.fancy-select::-ms-expand {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
.donut-ring {
|
.donut-ring {
|
||||||
stroke: rgba(255,255,255,0.06);
|
stroke: rgba(9, 9, 9, 0.1);
|
||||||
|
/* fill: black; */
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
vector-effect: non-scaling-stroke;
|
vector-effect: non-scaling-stroke;
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
@@ -312,7 +277,7 @@ select.fancy-select::-ms-expand {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: var(--white);
|
color: var(--almostBlack);
|
||||||
}
|
}
|
||||||
.quota-percent {
|
.quota-percent {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@@ -322,7 +287,7 @@ select.fancy-select::-ms-expand {
|
|||||||
.quota-number {
|
.quota-number {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--light);
|
color: var(--almostBlack);
|
||||||
}
|
}
|
||||||
.quota-label {
|
.quota-label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>ADrive Share</title>
|
<title>ADrive Share</title>
|
||||||
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -15,7 +16,19 @@
|
|||||||
<ul class="flashes">
|
<ul class="flashes">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<!-- category: message, error, info, warning -->
|
<!-- category: message, error, info, warning -->
|
||||||
<li class="{{ category }}"><strong>{{ message }}</strong></li>
|
{% if category == 'message' %}
|
||||||
|
{% set category = 'primary' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'error' %}
|
||||||
|
{% set category = 'danger' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'info' %}
|
||||||
|
{% set category = 'info' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'warning' %}
|
||||||
|
{% set category = 'warning' %}
|
||||||
|
{% endif %}
|
||||||
|
<li class="alert alert-{{ category }}">{{ message }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -64,7 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Filename</th>
|
<th>Filename</th>
|
||||||
<th>Code</th>
|
<th>Code</th>
|
||||||
@@ -85,9 +98,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if file.reusable %}
|
|
||||||
<a href="/delete/{{ file.code }}" style="color: red;">Delete</a>
|
<a href="/delete/{{ file.code }}" style="color: red;">Delete</a>
|
||||||
{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<title>ADrive Share</title>
|
<title>ADrive Share</title>
|
||||||
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
@@ -15,7 +16,19 @@
|
|||||||
<ul class="flashes">
|
<ul class="flashes">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<!-- category: message, error, info, warning -->
|
<!-- category: message, error, info, warning -->
|
||||||
<li class="{{ category }}"><strong>{{ message }}</strong></li>
|
{% if category == 'message' %}
|
||||||
|
{% set category = 'primary' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'error' %}
|
||||||
|
{% set category = 'danger' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'info' %}
|
||||||
|
{% set category = 'info' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'warning' %}
|
||||||
|
{% set category = 'warning' %}
|
||||||
|
{% endif %}
|
||||||
|
<li class="alert alert-{{ category }}">{{ message }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -27,8 +40,8 @@
|
|||||||
Enter your credentials to access your dashboard and manage your files.
|
Enter your credentials to access your dashboard and manage your files.
|
||||||
</p>
|
</p>
|
||||||
<form id="loginForm" action="/login" method="post">
|
<form id="loginForm" action="/login" method="post">
|
||||||
Username: <input type="text" name="username" id="username" placeholder="📋" required aria-required>
|
Username: <input type="text" class="form-control" name="username" id="username" placeholder="📋" required aria-required>
|
||||||
Passphrase: <input type="password" name="password" id="password" placeholder="🗝️" aria-required>
|
Passphrase: <input type="password" class="form-control" name="password" id="password" placeholder="🗝️" aria-required>
|
||||||
<input type="submit" value="Sign In" id="uploadBtn">
|
<input type="submit" value="Sign In" id="uploadBtn">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
@@ -70,5 +83,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<title>ADrive Share</title>
|
<title>ADrive Share</title>
|
||||||
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
@@ -15,7 +16,19 @@
|
|||||||
<ul class="flashes">
|
<ul class="flashes">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<!-- category: message, error, info, warning -->
|
<!-- category: message, error, info, warning -->
|
||||||
<li class="{{ category }}"><strong>{{ message }}</strong></li>
|
{% if category == 'message' %}
|
||||||
|
{% set category = 'primary' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'error' %}
|
||||||
|
{% set category = 'danger' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'info' %}
|
||||||
|
{% set category = 'info' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'warning' %}
|
||||||
|
{% set category = 'warning' %}
|
||||||
|
{% endif %}
|
||||||
|
<li class="alert alert-{{ category }}">{{ message }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -27,8 +40,8 @@
|
|||||||
With an account, you can manage up to 3GB in one dashboard. If you use up your limit, you can delete to free up space or upload files without the ability to manage them.
|
With an account, you can manage up to 3GB in one dashboard. If you use up your limit, you can delete to free up space or upload files without the ability to manage them.
|
||||||
</p>
|
</p>
|
||||||
<form id="loginForm" action="/register" method="post">
|
<form id="loginForm" action="/register" method="post">
|
||||||
Username: <input type="text" name="username" id="username" placeholder="📋" required aria-required>
|
Username: <input type="text" class="form-control" name="username" id="username" placeholder="📋" required aria-required>
|
||||||
Passphrase: <input type="password" name="password" id="password" placeholder="🗝️" aria-required>
|
Passphrase: <input type="password" class="form-control" name="password" id="password" placeholder="🗝️" aria-required>
|
||||||
Managable Storage Quota: <select name="quota_files" id="quota_files" class="fancy-select" disabled aria-disabled>
|
Managable Storage Quota: <select name="quota_files" id="quota_files" class="fancy-select" disabled aria-disabled>
|
||||||
<option value="3GB">3GB</option>
|
<option value="3GB">3GB</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -72,5 +85,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
@@ -7,7 +8,9 @@
|
|||||||
<title>ADrive Share</title>
|
<title>ADrive Share</title>
|
||||||
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
<script src="https://kit.fontawesome.com/972393379b.js" crossorigin="anonymous"></script>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="app">
|
<div class="app">
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
@@ -15,7 +18,19 @@
|
|||||||
<ul class="flashes">
|
<ul class="flashes">
|
||||||
{% for category, message in messages %}
|
{% for category, message in messages %}
|
||||||
<!-- category: message, error, info, warning -->
|
<!-- category: message, error, info, warning -->
|
||||||
<li class="{{ category }}"><strong>{{ message }}</strong></li>
|
{% if category == 'message' %}
|
||||||
|
{% set category = 'primary' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'error' %}
|
||||||
|
{% set category = 'danger' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'info' %}
|
||||||
|
{% set category = 'info' %}
|
||||||
|
{% endif %}
|
||||||
|
{% if category == 'warning' %}
|
||||||
|
{% set category = 'warning' %}
|
||||||
|
{% endif %}
|
||||||
|
<li class="alert alert-{{ category }}">{{ message }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -24,41 +39,56 @@
|
|||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<form id="uploadForm" action="/sendfile" method="post" enctype="multipart/form-data">
|
<form id="uploadForm" action="/sendfile" method="post" enctype="multipart/form-data">
|
||||||
{% if loggedIn %}
|
{% if loggedIn %}
|
||||||
<h4>You are currently logged in as {{ username }}.</h4>
|
<h1>ADrive File Sharing <span class="badge text-bg-primary">{{ username }}</span></h1>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h4>You are currently not logged in.</h4>
|
<h1>ADrive File Sharing <span class="badge text-bg-danger">Guest</span></h1>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span style="font-size: 20px;">
|
<!-- <span style="font-size: 20px;">
|
||||||
<i class="fa-solid fa-server fa-xs" style="line-height: 25px; font-size: 15px; display: inline-block;"></i>
|
<i class="fa-solid fa-wifi fa-xs"
|
||||||
<span id="mirrorText">Finding Nearby Mirror</span> <i class="fa-solid fa-circle-dot fa-beat-fade fa-2xs" style="color: #ff9500;" id="mirrorLogo"></i>
|
style="line-height: 25px; font-size: 15px; display: inline-block;"></i>
|
||||||
|
<span id="mirrorText">Ping: <span id="ping-value">CONNECTING</span>ms</span> <i
|
||||||
|
class="fa-solid fa-circle-dot fa-beat-fade fa-2xs" style="color: #ff9500;" id="mirrorLogo"></i>
|
||||||
|
</span> -->
|
||||||
|
<span style="font-size: 20px; padding-bottom: 7px;">
|
||||||
|
<i class="fa-solid fa-location fa-xs"
|
||||||
|
style="line-height: 25px; font-size: 15px; display: inline-block;"></i>
|
||||||
|
<span id="location-display" class="loading">Locating...</span> <i class="fa-solid fa-circle-dot fa-2xs" id="mirrorLogo" style="color: #c10101;"></i> <span class="badge text-bg-primary" id="ping-badge"><span id="ping-status"></span> <span id="ping-value">..</span>ms</span></span>
|
||||||
</span>
|
</span>
|
||||||
<span style="font-size: 20px;">
|
<input type="file" name="file" id="fileUpload" class="form-control" required aria-required>
|
||||||
<i class="fa-solid fa-server fa-xs" style="line-height: 25px; font-size: 15px; display: inline-block;"></i>
|
|
||||||
Location Not Found <i class="fa-solid fa-circle-dot fa-2xs" style="color: #c10101;"></i>
|
|
||||||
</span>
|
|
||||||
<input type="file" name="file" id="fileUpload" required aria-required>
|
|
||||||
<input type="hidden" id="quotaGB" value="{{ quota_gb }}">
|
<input type="hidden" id="quotaGB" value="{{ quota_gb }}">
|
||||||
<input type="hidden" id="quotaUsedGB" value="{{ quota_usage }}">
|
<input type="hidden" id="quotaUsedGB" value="{{ quota_usage }}">
|
||||||
<progress id="uploadProgress" value="0" max="100" style="display:none;width:100%;margin-top:8px"></progress>
|
<div class="progress" id="uploadProgressContainer" style="display:none;width:100%;margin-top:8px">
|
||||||
<div id="uploadProgressText" style="display:none;font-size:13px;margin-top:4px">0%</div>
|
<div id="uploadProgress" class="progress-bar bg-success" role="progressbar" style="width:0%" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
|
||||||
<div style="display: flex; flex-direction: row;">
|
|
||||||
<input type="checkbox" name="reusable" style="display: inline-block;">
|
|
||||||
<span>Reusable?</span>
|
|
||||||
</div>
|
</div>
|
||||||
<input type="submit" value="Upload" id="uploadBtn" onclick="uploadAnim()" style="transition-duration: 400ms;">
|
<div id="uploadProgressText" style="display:none;font-size:13px;margin-top:4px">0%</div>
|
||||||
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<input class="form-check-input mt-0" type="checkbox" value="" id="reusableCheck" name="reusable"
|
||||||
|
aria-label="Checkbox for following text input" style="border: 1px solid rgb(106, 106, 106);">
|
||||||
|
<label for="reusable" style="margin-left: 6px; margin-bottom: 0;">Reusable
|
||||||
|
Link</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="" value="Upload" id="uploadBtn" onclick="uploadAnim()"
|
||||||
|
style="transition-duration: 400ms;">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
<input type="text" id="codeInput">
|
<input type="text" class="form-control" id="codeInput">
|
||||||
<button type="button" value="Download" onclick="downloadClick()">Download</button>
|
<button type="button" value="Download" class="btn btn-primary" onclick="downloadClick()">Download</button>
|
||||||
</form>
|
</form>
|
||||||
{% if not loggedIn %}
|
{% if not loggedIn %}
|
||||||
<a href="/login" class="sign-in-button" style="text-decoration: none; background: rgb(0, 81, 139)">Sign In</a>
|
<a href="/login" class="sign-in-button" style="text-decoration: none; background: rgb(0, 81, 139)">Sign
|
||||||
<a href="/register" class="sign-in-button" style="text-decoration: none; background: rgb(32, 0, 139)">Register</a><br>
|
In</a>
|
||||||
<p style="color: gray; font-size: 11px; text-align: center;">Signing in is not required. You can at any time register and sign in to manage your existing codes for free. You can manage up to 1GB of files.</p>
|
<a href="/register" class="sign-in-button"
|
||||||
|
style="text-decoration: none; background: rgb(32, 0, 139)">Register</a><br><br>
|
||||||
|
<p style="color: gray; font-size: 11px; text-align: center;">Signing in is not required. You can at any time
|
||||||
|
register and sign in to manage your existing codes for free. You can manage up to 3GB of files.</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="/dashboard" class="sign-in-button" style="text-decoration: none; background: blue">View Dashboard</a>
|
<a href="/dashboard" class="sign-in-button" style="text-decoration: none; background: blue">View
|
||||||
<a href="/logout" class="sign-in-button" style="text-decoration: none; background: rgb(79, 92, 178)">Sign Out</a>
|
Dashboard</a>
|
||||||
|
<a href="/logout" class="sign-in-button" style="text-decoration: none; background: rgb(79, 92, 178)">Sign
|
||||||
|
Out</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -69,7 +99,7 @@
|
|||||||
<script>
|
<script>
|
||||||
//document.getElementById("uploadBtn").addEventListener("click", uploadAnim);
|
//document.getElementById("uploadBtn").addEventListener("click", uploadAnim);
|
||||||
|
|
||||||
function downloadClick(){
|
function downloadClick() {
|
||||||
const codeEl = document.getElementById("codeInput");
|
const codeEl = document.getElementById("codeInput");
|
||||||
const code = codeEl.value;
|
const code = codeEl.value;
|
||||||
// clear input when clicked
|
// clear input when clicked
|
||||||
@@ -95,16 +125,16 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Client-side quota check before uploading large files
|
// Client-side quota check before uploading large files
|
||||||
(function(){
|
(function () {
|
||||||
const fileInput = document.getElementById('fileUpload');
|
const fileInput = document.getElementById('fileUpload');
|
||||||
const uploadBtn = document.getElementById('uploadBtn');
|
const uploadBtn = document.getElementById('uploadBtn');
|
||||||
const quotaGB = parseFloat(document.getElementById('quotaGB').value) || 0;
|
const quotaGB = parseFloat(document.getElementById('quotaGB').value) || 0;
|
||||||
const usedGB = parseFloat(document.getElementById('quotaUsedGB').value) || 0;
|
const usedGB = parseFloat(document.getElementById('quotaUsedGB').value) || 0;
|
||||||
const remainingGB = Math.max(0, quotaGB - usedGB);
|
const remainingGB = Math.max(0, quotaGB - usedGB);
|
||||||
|
|
||||||
function ensureFlashesContainer(){
|
function ensureFlashesContainer() {
|
||||||
let flashes = document.querySelector('.flashes');
|
let flashes = document.querySelector('.flashes');
|
||||||
if (!flashes){
|
if (!flashes) {
|
||||||
flashes = document.createElement('ul');
|
flashes = document.createElement('ul');
|
||||||
flashes.className = 'flashes';
|
flashes.className = 'flashes';
|
||||||
const app = document.querySelector('.app');
|
const app = document.querySelector('.app');
|
||||||
@@ -113,12 +143,12 @@
|
|||||||
return flashes;
|
return flashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearClientFlash(){
|
function clearClientFlash() {
|
||||||
const flashes = document.querySelectorAll('.flashes li.client-quota-warning');
|
const flashes = document.querySelectorAll('.flashes li.client-quota-warning');
|
||||||
flashes.forEach(n => n.remove());
|
flashes.forEach(n => n.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
function showClientFlash(message){
|
function showClientFlash(message) {
|
||||||
clearClientFlash();
|
clearClientFlash();
|
||||||
const flashes = ensureFlashesContainer();
|
const flashes = ensureFlashesContainer();
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
@@ -129,14 +159,14 @@
|
|||||||
flashes.prepend(li);
|
flashes.prepend(li);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fileInput){
|
if (fileInput) {
|
||||||
fileInput.addEventListener('change', function(e){
|
fileInput.addEventListener('change', function (e) {
|
||||||
clearClientFlash();
|
clearClientFlash();
|
||||||
uploadBtn.disabled = false;
|
uploadBtn.disabled = false;
|
||||||
const f = fileInput.files && fileInput.files[0];
|
const f = fileInput.files && fileInput.files[0];
|
||||||
if (!f) return;
|
if (!f) return;
|
||||||
const fileGB = f.size / (1024 * 1024 * 1024);
|
const fileGB = f.size / (1024 * 1024 * 1024);
|
||||||
if (fileGB > remainingGB){
|
if (fileGB > remainingGB) {
|
||||||
showClientFlash('You do not have sufficient quota to manage this file. If you choose to upload this file, you will not be able to manage it through your dashboard.');
|
showClientFlash('You do not have sufficient quota to manage this file. If you choose to upload this file, you will not be able to manage it through your dashboard.');
|
||||||
// still allow upload; server will accept but will not set owner when quota insufficient
|
// still allow upload; server will accept but will not set owner when quota insufficient
|
||||||
} else {
|
} else {
|
||||||
@@ -147,6 +177,81 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function measurePing() {
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
fetch('/ping')
|
||||||
|
.then(() => {
|
||||||
|
const end = Date.now();
|
||||||
|
const latency = end - start;
|
||||||
|
document.getElementById('ping-value').innerText = latency;
|
||||||
|
if (latency > 200) {
|
||||||
|
document.getElementById('ping-status').innerText = "BAD";
|
||||||
|
document.getElementById('ping-badge').className = "badge text-bg-danger";
|
||||||
|
} else if (latency > 100) {
|
||||||
|
document.getElementById('ping-status').innerText = "GOOD";
|
||||||
|
document.getElementById('ping-badge').className = "badge text-bg-success";
|
||||||
|
} else if (latency <= 100) {
|
||||||
|
document.getElementById('ping-status').innerText = "BEST";
|
||||||
|
document.getElementById('ping-badge').className = "badge text-bg-primary";
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
document.getElementById('ping-value').innerText = "Error";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("fileUpload").disabled = true;
|
||||||
|
document.getElementById("uploadBtn").disabled = true;
|
||||||
|
document.getElementById("codeInput").disabled = true;
|
||||||
|
document.getElementById("reusableCheck").disabled = true;
|
||||||
|
// Measure ping immediately on load
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById("mirrorLogo").classList.remove("fa-beat-fade");
|
||||||
|
document.getElementById("mirrorLogo").style.color = "green";
|
||||||
|
measurePing();
|
||||||
|
document.getElementById("fileUpload").disabled = false;
|
||||||
|
document.getElementById("codeInput").disabled = false;
|
||||||
|
document.getElementById("uploadBtn").disabled = false;
|
||||||
|
document.getElementById("reusableCheck").disabled = false;
|
||||||
|
|
||||||
|
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
measurePing();
|
||||||
|
}, 1000); // Update every 30 seconds
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
async function loadLocation() {
|
||||||
|
try {
|
||||||
|
// This calls the API from the USER'S browser.
|
||||||
|
// The API will see the User's Real IP directly.
|
||||||
|
const response = await fetch('https://ipapi.co/json/');
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.city && data.country_name) {
|
||||||
|
document.getElementById('location-display').innerText =
|
||||||
|
`${data.city}, ${data.country_name}`;
|
||||||
|
document.getElementById('location-display').classList.remove('loading');
|
||||||
|
} else {
|
||||||
|
throw new Error("Missing data");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Location Error:", err);
|
||||||
|
document.getElementById('location-display').innerText = "Unknown Location";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadLocation();
|
||||||
|
</script>
|
||||||
|
|
||||||
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
<script src="{{ url_for('static', filename='script.js') }}"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user