diff --git a/.gitignore b/.gitignore index e69de29..ce380e8 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1,11 @@ +__pycache__/ +.vscode/ +.DS_Store +logs +users.json +db.json +uploads/ +uploads/* +update_db.py +.idea/ +*.log \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..fb91eef --- /dev/null +++ b/app.py @@ -0,0 +1,305 @@ +from flask import ( + Flask, + render_template, + session, + request, + url_for, + send_from_directory, + flash +) +from utils import redirect, dbload, dbsave, udbload, udbsave +from werkzeug.exceptions import RequestEntityTooLarge +from werkzeug.utils import secure_filename + +import sys +import os +import random +import threading +import time + +import logging +import logging.handlers + + +app = Flask('adrive', static_folder='static', template_folder='templates') +app.config['UPLOAD_DIRECTORY'] = 'uploads/' +app.config['MAX_CONTENT_LENGTH'] = 1000000 * 1024 * 1024 +app.config['SECRET_KEY'] = str(random.randint(99999, 9999999)) + +# Redirect logs to the file 'logs' +handler = logging.handlers.RotatingFileHandler('logs', maxBytes=1024 * 1024) +logging.getLogger('werkzeug').setLevel(logging.DEBUG) +logging.getLogger('werkzeug').addHandler(handler) +app.logger.setLevel(logging.WARNING) +app.logger.addHandler(handler) + +@app.route('/') +def index(): + return redirect(url_for('upload')) + +@app.route('/dashboard') +def dashboard(): + if session.get('loggedIn', False) == False: + flash('You must be signed in to view the dashboard!', 'error') + return redirect(url_for('upload')) + db = dbload() + udb = udbload() + username = session.get('username') + userfiles = [] + quota_usage_gb = 0 + if not username: + flash('An error occurred. Please sign in again.', 'error') + session['loggedIn'] = False + session.pop('username', None) + return redirect(url_for('upload')) + for file in db['files']: + if db['files'][file].get('owner') == username: + db['files'][file]['file'] = file + db['files'][file]['code'] = file.split('_')[-1] + userfiles.append(db['files'][file]) + for userfile in userfiles: + usf_mb = userfile['size_megabytes'] + if usf_mb: + quota_usage_gb += usf_mb / 1024 + 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) +@app.route('/logout') +def logout(): + session['loggedIn'] = False + session.pop('username', None) + flash('Successfully signed out!', 'info') + return redirect(url_for('upload')) + +@app.route('/login', methods=['GET', 'POST']) +def login(): + udb = udbload() + + if request.method == 'GET': + return render_template('login.html') + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + for user in udb: + if user['username'] == username and user['password'] == password: + session['loggedIn'] = True + session['username'] = username + flash('Successfully signed in!', 'info') + return redirect(url_for('upload')) + flash('Invalid username or password!', 'error') + return redirect(url_for('login')) + +@app.route('/register', methods=['GET', 'POST']) +def register(): + udb = udbload() + + if request.method == 'GET': + return render_template('register.html') + if request.method == 'POST': + username = request.form.get('username') + password = request.form.get('password') + + for user in udb: + if user['username'] == username: + flash('Username already taken!', 'error') + return redirect(url_for('register')) + + udb.append({ + 'username': username, + 'password': password, + 'quota_gb': 3 + }) + udbsave(udb) + flash('Successfully registered! You can now sign in.', 'info') + return redirect(url_for('login')) + +@app.route('/upload') +def upload(): + loggedIn = session.get('loggedIn', False) + username = session.get('username', '') + quota_gb = None + quota_usage_gb = 0.0 + try: + if loggedIn and username: + db = dbload() + udb = udbload() + # find user record in udb + user_rec = None + for u in udb: + if u.get('username') == username: + user_rec = u + break + if user_rec: + quota_gb = user_rec.get('quota_gb', 0) + else: + quota_gb = 0 + userfiles = [] + for file in db.get('files', {}): + if db['files'][file].get('owner') == username: + userfiles.append(db['files'][file]) + for userfile in userfiles: + usf_mb = userfile.get('size_megabytes', 0) + if usf_mb: + quota_usage_gb += usf_mb / 1024 + quota_usage_gb = round(quota_usage_gb, 1) + else: + # not logged in: treat as guest with 1GB manageable quota + quota_gb = 5.0 + quota_usage_gb = 0.0 + except Exception: + quota_gb = quota_gb or 0 + quota_usage_gb = quota_usage_gb or 0.0 + + return render_template('upload.html', loggedIn=loggedIn, username=username, quota_gb=quota_gb, quota_usage=quota_usage_gb) + +@app.route('/sendfile', methods=['POST']) +def sendfile(): + db = dbload() + file = request.files['file'] + reusable = request.form.get('reusable') + loggedIn = session.get('loggedIn', False) + username = session.get('username', '') + + + if file: + try: + extension = os.path.splitext(file.filename)[1].lower() + + fileid = str(random.randint(100000, 99999999)) + dest_name = secure_filename(file.filename) + f'_{fileid}' + dest_path = os.path.join(app.config['UPLOAD_DIRECTORY'], dest_name) + + # save file first, then inspect size on disk + file.save(dest_path) + print(dest_name) + + # get file size in bytes and convert to MB/GB + try: + size_bytes = os.path.getsize(dest_path) + except Exception: + size_bytes = 0 + size_megabytes = round(size_bytes / (1024 * 1024), 1) + file_gb = size_megabytes / 1024 + + # determine user's remaining quota + remaining_gb = None + if loggedIn and username: + try: + udb = udbload() + # find user record + user_rec = None + for u in udb: + if u.get('username') == username: + user_rec = u + break + user_quota_gb = user_rec.get('quota_gb', 0) if user_rec else 0 + # compute current usage + usage_gb = 0.0 + for fkey, fval in db.get('files', {}).items(): + if fval.get('owner') == username: + usf_mb = fval.get('size_megabytes', 0) + if usf_mb: + usage_gb += usf_mb / 1024 + usage_gb = round(usage_gb, 1) + remaining_gb = max(0.0, user_quota_gb - usage_gb) + except Exception: + remaining_gb = 0.0 + else: + # guests: they don't get owner set; treat remaining as 0 so owner won't be set + remaining_gb = 0.0 + + # build DB entry; only set owner if user is logged in AND file fits remaining quota + entry = {"reusable": True if reusable else False, "size_megabytes": size_megabytes} + if loggedIn and username and file_gb <= remaining_gb: + entry["owner"] = username + + db["files"][dest_name] = entry + + if reusable: + flash('Download code: ' + fileid, 'info') + else: + flash('1-Time Download code: ' + fileid, 'info') + + dbsave(db) + return redirect(url_for('upload')) + + except RequestEntityTooLarge: + return 'File is larger than the size limit.' + +@app.route('/delete/', methods=['GET', 'POST']) +def delete(code): + db = dbload() + for file in db['files']: + if file.split('_')[-1] == code: + if db['files'][file].get('owner') != session.get('username'): + flash('You do not own this file and cannot delete it.', 'error') + return redirect(url_for('upload')) + try: + os.remove('uploads/' + file) + except FileNotFoundError: + print('couldnt delete from os bc file not found.') + try: + os.remove('uploads/' + file.split('_')[0]) + except FileNotFoundError: + print('couldnt delete from os bc file not found. (2)') + db['files'].pop(file) + dbsave(db) + flash('File with code ' + code + ' has been deleted.', 'info') + return redirect(url_for('dashboard')) + +@app.route('/download') +def download_without_code(): + flash('No code provided!', 'error') + return redirect(url_for('upload')) + +@app.route('/download/', methods=['GET', 'POST']) +def download(code): + db = dbload() + try: + found = False + filename = '' + files = os.listdir('uploads') + for file in files: + if file.split('_')[-1] == code: + found = True + filename = file + + if not found: + print(filename) + # Search db for the entry with this code and remove it + for file_entry in list(db['files'].keys()): + if file_entry.split('_')[-1] == code: + print(f"Deleting: {file_entry}") + print + db['files'].pop(file_entry) + dbsave(db) + print(f"Deleted! DB now has {len(db['files'])} files") + break + 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')) + else: + os.rename('uploads/' + filename, 'uploads/' + filename.replace(f'_{code}', '')) + def deleteFile(): + db['files'].pop(filename) + dbsave(db) + def backRename(): + time.sleep(1) + os.rename('uploads/' + filename.replace(f'_{code}', ''), 'uploads/' + filename) + if db['files'][filename]['reusable']: + backToNameFunc = threading.Thread(target=backRename) + backToNameFunc.start() + if db['files'][filename]['reusable'] == False: + deleteFunc = threading.Thread(target=deleteFile) + deleteFunc.start() + + return send_from_directory(app.config['UPLOAD_DIRECTORY'], filename.replace(f'_{code}', ''), as_attachment=True) + + except Exception as e: + print(f"Error in download: {e}") + print(f"Type: {type(e)}") + import traceback + traceback.print_exc() + 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')) + +app.run(debug=True, port=3133) diff --git a/instructions.txt b/instructions.txt new file mode 100644 index 0000000..15b7f5b --- /dev/null +++ b/instructions.txt @@ -0,0 +1,2 @@ +run 'screen' then hit enter 2 times +run 'sh run.sh' \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..83b9117 --- /dev/null +++ b/run.sh @@ -0,0 +1,2 @@ +echo "Running app in debug mode WARNING - disable debug mode for production" +python3 app.py \ No newline at end of file diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..8ed8f96 --- /dev/null +++ b/static/script.js @@ -0,0 +1,119 @@ +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() { + const form = document.getElementById('uploadForm'); + if (!form) return; + + const fileInput = document.getElementById('fileUpload'); + const progress = document.getElementById('uploadProgress'); + const progressText = document.getElementById('uploadProgressText'); + const uploadBtn = document.getElementById('uploadBtn'); + + form.addEventListener('submit', function(e) { + e.preventDefault(); + if (!fileInput || !fileInput.files || fileInput.files.length === 0) { + return; + } + + const file = fileInput.files[0]; + const formData = new FormData(); + formData.append('file', file); + // include reusable checkbox if present + const reusable = form.querySelector('input[name="reusable"]'); + if (reusable && reusable.checked) formData.append('reusable', 'on'); + + const xhr = new XMLHttpRequest(); + xhr.open('POST', form.getAttribute('action')); + + xhr.upload.addEventListener('progress', function(ev) { + if (ev.lengthComputable) { + const percent = Math.round((ev.loaded / ev.total) * 100); + if (progress) { + progress.style.display = 'block'; + progress.value = percent; + } + if (progressText) { + progressText.style.display = 'block'; + progressText.textContent = percent + '%'; + } + if (uploadBtn) { + uploadBtn.classList.add('yellowBtn'); + uploadBtn.value = '· · ·'; + uploadBtn.disabled = true; + } + } + }); + + xhr.addEventListener('load', function() { + if (xhr.status >= 200 && xhr.status < 400) { + // Try to extract flash messages from the returned HTML and inject them + try { + const tmp = document.createElement('div'); + tmp.innerHTML = xhr.responseText; + const newFlashes = tmp.querySelector('ul.flashes'); + const oldFlashes = document.querySelector('ul.flashes'); + if (newFlashes) { + if (oldFlashes) oldFlashes.replaceWith(newFlashes); + else document.body.insertBefore(newFlashes, document.body.firstChild); + } else { + // fallback to full reload if no flashes present + window.location.href = '/upload'; + return; + } + } catch (e) { + window.location.href = '/upload'; + return; + } finally { + if (uploadBtn) { + uploadBtn.disabled = false; + uploadBtn.classList.remove('yellowBtn'); + uploadBtn.value = 'Upload'; + } + if (progress) { progress.style.display = 'none'; progress.value = 0; } + if (progressText) { progressText.style.display = 'none'; progressText.textContent = '0%'; } + // clear file input and reusable checkbox after successful upload + if (fileInput) fileInput.value = ''; + if (reusable && reusable.checked) reusable.checked = false; + } + } else { + // error + if (progressText) progressText.textContent = 'Upload failed'; + if (uploadBtn) { + uploadBtn.disabled = false; + uploadBtn.classList.remove('yellowBtn'); + uploadBtn.value = 'Upload'; + } + if (fileInput) fileInput.value = ''; + if (progress) { progress.style.display = 'none'; progress.value = 0; } + if (progressText) progressText.style.display = 'none'; + } + }); + + xhr.addEventListener('error', function() { + if (progressText) progressText.textContent = 'Upload error'; + if (uploadBtn) { + uploadBtn.disabled = false; + uploadBtn.classList.remove('yellowBtn'); + uploadBtn.value = 'Upload'; + } + if (fileInput) fileInput.value = ''; + if (progress) { progress.style.display = 'none'; progress.value = 0; } + if (progressText) progressText.style.display = 'none'; + }); + + xhr.send(formData); + }); +}); \ No newline at end of file diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..93ec7d3 --- /dev/null +++ b/static/styles.css @@ -0,0 +1,336 @@ +:root { + --black: #000000; + --almost-black: #080708; + --blue: #3772FF; + --blue-light: #3787ff; + --red: #DF2935; + --yellow: #FDCA40; + --light: #E6E8E6; + --white: #FFFFFF; +} + +* { + position: relative; + box-sizing: border-box; +} + +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + font-size: 22px; + line-height: 1.5; + color: var(--white); + background: var(--black); + background-image: linear-gradient(to bottom right, var(--almost-black), var(--black)); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +body,html { + width: 100%; height: 100%; +} + +.app { + display: flex; + flex-direction: column; + align-items: flex-start; + width: 50%; + border: 1px solid rgb(40, 40, 40); + padding-bottom: 40px; +} + +.form-section { + padding: 15px 0 0 15px; + width: 50%; + color: var(--light); + flex-shrink: 0; +} + +@media screen and (max-width: 1200px) { + .app { + flex-direction: column; + } + + .form-section { + padding-right: 15px; + padding-bottom: 15px; + width: 100%; + } + + .images-section { + padding-top: 0; + width: 100%; + height: auto; + } +} + +@media screen and (max-width: 800px) { + .images-section a { + padding-bottom: 60%; + width: calc(100% - 15px); + } +} + +/* Forms */ +form { + width: 100%; + flex-direction: column; + display: flex; +} + +input[type='file'], input[type='text'], input[type='password'] { + margin: 0 0 12px; + padding: 10px 10px; + border-radius: 4px; + font-size: 22px; + color: var(--almost-black); + background: var(--white); +} + +input[type='submit'], +button[type='button'], +.sign-in-button { + padding: 13px 20px; + font-size: 22px; + font-weight: bold; + color: var(--white); + border: none; + border-radius: 4px; + outline: none; + box-shadow: none; + cursor: pointer; + background: var(--blue); + background-image: linear-gradient(to bottom, var(--blue-light), var(--blue)); + transition: box-shadow .2s ease; + margin-bottom: 30px; +} + +input[type='submit']:hover, +button[type='button']:hover, +.sign-in-button:hover { + box-shadow: inset 0 -25px 25px var(--blue-light); +} + +.flashes { + display: flex; + flex-direction: column; + gap: 15px; +} +.flashes li { + list-style-type: none; + margin: 0; + padding: 0; + border-radius: 5px; + padding: 10px; + font-size: 20px; +} +.message { + border: 1px solid gray; +} +.error { + border: 1px solid rgb(168, 50, 50); +} +.info { + border: 1px solid rgb(30, 199, 199); +} +.warning, .warn { + border: 1px solid rgb(232, 148, 1); +} +.container { + padding-left: 50px; +} + +.yellowBtn { + padding: 13px 20px; + font-size: 22px; + font-weight: bold; + color: var(--white); + border: none; + border-radius: 4px; + outline: none; + box-shadow: none; + cursor: pointer; + background: var(yellow) !important; + background-image: linear-gradient(to bottom, var(--yellow), var(--yellow)) !important; + transition: box-shadow .2s ease; + margin-bottom: 30px; +} + +.yellowBtn:hover { + box-shadow: inset 0 -25px 25px var(--yellow) !important; +} + +.dashboard-container { + padding: 15px 15px 15px 15px; + width: 100%; + color: var(--light); + flex-shrink: 0; +} +/* Table Styling */ +table { + width: 100%; + 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 th { + 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 */ +.fancy-select, +select.fancy-select { + margin: 0 0 12px; + padding: 10px 44px 10px 12px; + border-radius: 4px; + font-size: 22px; + color: var(--almost-black); + background-color: var(--white); + border: 1px solid rgba(0,0,0,0.12); + background-image: linear-gradient(to bottom, #ffffff, #f7f7f7), url("data:image/svg+xml;utf8,"); + background-repeat: no-repeat; + background-position: right 12px center; + background-size: 18px; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + cursor: pointer; + transition: box-shadow .12s ease, border-color .12s ease, transform .06s ease; +} + +.fancy-select:focus, +select.fancy-select:focus { + outline: none; + border-color: var(--blue-light); + box-shadow: 0 0 0 4px rgba(55,114,255,0.09); + transform: translateY(-1px); +} + +/* hide default arrow in IE */ +select.fancy-select::-ms-expand { + display: none; +} + +/* smaller devices: reduce font-size and padding */ +@media (max-width: 520px) { + .fancy-select, + select.fancy-select { + font-size: 18px; + padding-right: 40px; + } +} + +/* Quota donut styles */ +.dashboard-top { + display: flex; + align-items: center; + justify-content: space-between; + gap: 18px; + flex-wrap: wrap; + margin-bottom: 10px; +} +.dashboard-meta { + flex: 1 1 260px; + min-width: 180px; +} +.quota-block { + margin: 8px 0 18px; + flex: 0 0 auto; +} +.quota-donut { + position: relative; + width: 110px; + height: 110px; + display: inline-block; +} +.quota-donut svg { + display: block; + transform: rotate(-90deg); + width: 100%; + height: 100%; +} +.donut-ring { + stroke: rgba(255,255,255,0.06); + stroke-linecap: round; + vector-effect: non-scaling-stroke; + shape-rendering: geometricPrecision; +} + +.donut-fill { + stroke: var(--blue); + stroke-linecap: round; + vector-effect: non-scaling-stroke; + shape-rendering: geometricPrecision; + transition: stroke-dashoffset 600ms cubic-bezier(.22,.9,.3,1), stroke 200ms ease; + transform-origin: center; +} +.quota-center { + position: absolute; + left: 0; top: 0; right: 0; bottom: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + pointer-events: none; + color: var(--white); +} +.quota-percent { + font-size: 18px; + font-weight: 900; + line-height: 1; +} +.quota-number { + font-size: 12px; + font-weight: 700; + color: var(--light); +} +.quota-label { + font-size: 11px; + color: gray; +} + +@media (max-width: 520px) { + .quota-donut { width: 64px; height: 64px; } + .quota-number { font-size: 12px; } + .quota-percent { font-size: 14px; } +} \ No newline at end of file diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..be6e906 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,132 @@ + + + + + + + ADrive Share + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} + +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} +
+
+
+

Dashboard of {{username}}

+

+ You are managing {{ quota_usage }}GB out of {{ quota_gb }}GB. +

+
+ + + {% set r = 40 %} + {% set c = (2 * 3.141592653589793 * r) %} + {% if quota_gb and quota_gb > 0 %} + {% set used = quota_usage %} + {% set pct = (used / quota_gb) if quota_gb > 0 else 0 %} + {% else %} + {% set used = 0 %} + {% set pct = 0 %} + {% endif %} + + {% if pct >= 0.9 %} + {% set stroke_color = '#DF2935' %} + {% elif pct >= 0.6 %} + {% set stroke_color = '#FDCA40' %} + {% else %} + {% set stroke_color = '#3772FF' %} + {% endif %} + +
+ +
+
+ + + + + + + + + {% for file in files %} + + + + + + + + {% endfor %} +
FilenameCodeReusableDownloadDelete
{{ file.file.rsplit('_', 1)[0] }}{{ file.code }}{{ 'Yes' if file.reusable else 'No' }} + {% if file.reusable %} + Download + {% else %} + Download and Delete + {% endif %} + + {% if file.reusable %} + Delete + {% endif %} +
+ + + +
+ + +
+ + + + + + \ No newline at end of file diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..4d77b9a --- /dev/null +++ b/templates/login.html @@ -0,0 +1,74 @@ + + + + + + + ADrive Share + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} + +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + +
+

Sign In

+

+ Enter your credentials to access your dashboard and manage your files. +

+
+ Username: + Passphrase: + +
+ + + + +

Signing in is not required. You can at any time register and sign in to manage your existing codes for free.

+ +
+ + +
+ + + + + + \ No newline at end of file diff --git a/templates/register.html b/templates/register.html new file mode 100644 index 0000000..9767a6d --- /dev/null +++ b/templates/register.html @@ -0,0 +1,76 @@ + + + + + + + ADrive Share + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} + +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + +
+

Sign Up

+

+ 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. +

+
+ Username: + Passphrase: + Managable Storage Quota: + +
+ + + +

Signing in is not required. You can at any time register and sign in to manage your existing codes for free.

+ +
+ + +
+ + + + + + \ No newline at end of file diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..a1cee0e --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,152 @@ + + + + + + + ADrive Share + + + + +
+ {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} + +
  • {{ message }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + +
+
+ {% if loggedIn %} +

You are currently logged in as {{ username }}.

+ {% else %} +

You are currently not logged in.

+ {% endif %} + + + Finding Nearby Mirror + + + + Location Not Found + + + + + + +
+ + Reusable? +
+ +
+ +
+ + +
+ {% if not loggedIn %} + +
+

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.

+ {% else %} + + + {% endif %} + +
+ + +
+ + + + + + + + \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..098a783 --- /dev/null +++ b/test.py @@ -0,0 +1,5 @@ +from utils import * + +db = dbload() +db['files'].pop("I_Love_You_-_Boy_Next_Door_1.mp3_45475777") +dbsave(db) \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..4d332ee --- /dev/null +++ b/utils.py @@ -0,0 +1,21 @@ +from flask import redirect as fredirect +import json + +def redirect(url): + return fredirect(url) + +def dbload(): + with open('db.json', 'r') as f: + return json.load(f) + +def dbsave(obj): + with open('db.json', 'w') as f: + json.dump(obj, f) + +def udbload(): + with open('users.json', 'r') as f: + return json.load(f) + +def udbsave(obj): + with open('users.json', 'w') as f: + json.dump(obj, f) \ No newline at end of file