Compare commits

...

13 Commits

7 changed files with 131 additions and 46 deletions

27
.gitignore vendored
View File

@ -1,3 +1,8 @@
# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,python
# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -50,6 +55,7 @@ coverage.xml
*.py,cover
.hypothesis/
.pytest_cache/
pytestdebug.log
# Translations
*.mo
@ -70,6 +76,7 @@ instance/
# Sphinx documentation
docs/_build/
doc/_build/
# PyBuilder
target/
@ -109,6 +116,7 @@ venv/
ENV/
env.bak/
venv.bak/
pythonenv*
# Spyder project settings
.spyderproject
@ -128,6 +136,25 @@ dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# profiling data
.prof
### VisualStudioCode ###
.vscode/*
!.vscode/tasks.json
!.vscode/launch.json
*.code-workspace
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,python
# imgupload custom
uploadkeys
savelog.log

View File

@ -31,29 +31,54 @@ First, fork [this repository](https://git.bbaovanc.com/bbaovanc/imgupload). If y
## Installation
1. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git`
2. Enter the imgupload directory: `cd imgupload`
3. Create a virtualenv: `python3 -m venv env`
4. Enter the virtualenv: `source env/bin/activate`
5. Install dependencies: `python3 -m pip install -r requirements.txt`
6. [Run the Flask app](#running-the-flask-app)
---
## Running the Flask app
### Using uWSGI
[https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html](https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html)
Note: replace `www-data` with whatever user your webserver runs as.
Instructions specific to imgupload are coming soon
1. Go to /srv: `cd /srv`
2. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git`
3. Change ownership of /srv/imgupload: `sudo chown www-data:www-data /srv/imgupload`
4. Enter www-data user: `sudo su www-data`
5. Change directories to /srv/imgupload: `cd /srv/imgupload`
6. Checkout the version you want (replace [version] with desired version tag: `git checkout [version]`
7. Enter the imgupload directory: `cd imgupload`
8. Create a virtualenv: `python3 -m venv env`
9. Enter the virtualenv: `source env/bin/activate`
10. Install dependencies: `python3 -m pip install -r requirements.txt`
11. Leave the www-data user: `exit`
12. Copy the default uWSGI configuration: `sudo cp /srv/imgupload/uwsgi.ini.default /etc/uwsgi/apps-available/imgupload.ini`
13. Modify `/etc/uwsgi/apps-available/imgupload.ini` to your preferences
14. Enable imgupload: `sudo ln -s /etc/uwsgi/apps-available/imgupload.ini /etc/uwsgi/apps-enabled/`
15. Restart uWSGI: `sudo systemctl restart uwsgi`
16. Set up your webserver to proxy the uwsgi.sock
Example NGINX location block:
```nginx
location /upload {
include uwsgi_params;
uwsgi_pass unix:/srv/imgupload/uwsgi.sock;
client_max_body_size 25M;
}
```
### Using Flask development server
#### Setup
```shell
$ source env/bin/activate # if you haven't already entered the virtualenv
$ export FLASK_APP=imgupload.py
$ flask run
git clone https://git.bbaovanc.com/bbaovanc/imgupload.git
cd imgupload
python3 -m venv env
source env/bin/activate
pip3 install -r requirements.txt
```
#### Run
```shell
export FLASK_APP=imgupload.py
flask run
```
---

View File

@ -35,7 +35,7 @@ unset_settings = [i for i in defaults.keys() if i not in dir(settings)]
if len(unset_settings) > 0:
for unset in unset_settings:
checksettings.remove(unset)
print("[!] {0} is unset. The default value is type {1} with value {2}".format(unset, deftypes[unset].__name__, defaults[unset]))
print(f"[!] {unset} is unset. The default value is type {deftypes[unset].__name__} with value {defaults[unset]}")
else:
print("[" + u"\u2713" + "] Found all required settings!")
@ -45,7 +45,7 @@ typesgood = True
typeswrong = []
for testtype in checksettings:
if type(getattr(settings, testtype)) is not deftypes[testtype]:
print("[!] {0} requires {1}, but is {2}".format(testtype, deftypes[testtype].__name__, type(getattr(settings, testtype)).__name__))
print(f"[!] {testtype} requires {deftypes[testtype].__name__}, but is {type(getattr(settings, testtype)).__name__}")
typeswrong.append(testtype)
typesgood = False
@ -63,7 +63,7 @@ if "ALLOWED_EXTENSIONS" in checksettings:
if len(invalid_exts) > 0:
print("[!] The following extensions listed in ALLOWED_EXTENSIONS are invalid:")
for e in invalid_exts:
print(" {0} is listed in ALLOWED_EXTENSIONS, but doesn't start with a .".format(e))
print(f" {e} is listed in ALLOWED_EXTENSIONS, but doesn't start with a .")
else:
print("[" + u"\u2713" + "] ALLOWED_EXTENSIONS is good!")
@ -73,7 +73,7 @@ uploadfolder_exists = True
if "UPLOAD_FOLDER" in checksettings:
if not os.path.isdir(settings.UPLOAD_FOLDER):
uploadfolder_exists = False
print("[!] The directory set in UPLOAD_FOLDER ('{0}') doesn't exist!".format(settings.UPLOAD_FOLDER))
print(f"[!] The directory set in UPLOAD_FOLDER ('{settings.UPLOAD_FOLDER}') doesn't exist!")
else:
print("[" + u"\u2713" + "] UPLOAD_FOLDER exists!")
@ -85,8 +85,6 @@ if "ROOTURL" in checksettings:
pass
else:
rooturl_good = False
print(settings.ROOTURL)
print(settings.ROOTURL.startswith("https://"))
print("[!] ROOTURL does not start with `http://` or `https://`! This may cause issues!")
if not settings.ROOTURL.endswith("/"):
rooturl_good = False
@ -94,14 +92,14 @@ if "ROOTURL" in checksettings:
if not rooturl_good:
print(" With your current settings, this is what a generated url would look like:")
print(" {0}example.png".format(settings.ROOTURL))
print(f" {settings.ROOTURL}example.png")
else:
print("[" + u"\u2713" + "] ROOTURL is good!")
# Ask the user if SAVELOG is the intended filename
if "SAVELOG" in checksettings:
print("[*] SAVELOG was interpreted to be {0}".format(settings.SAVELOG))
print(f"[*] SAVELOG was interpreted to be {settings.SAVELOG}")
print("[*] If this is not the intended filename, please fix it.")
@ -113,32 +111,32 @@ if len(unset_settings) > 0:
summarygood = False
print("Unset settings:")
for unset in unset_settings:
print(" {0}".format(unset))
print(f" {unset}")
if len(typeswrong) > 0:
summarygood = False
print("Incorrect types:")
for wtype in typeswrong:
print(" {0}".format(wtype))
print(f" {wtype}")
if len(invalid_exts) > 0:
summarygood = False
print("Invalid extensions:")
for wext in invalid_exts:
print(" '{0}'".format(wext))
print(f" '{wext}'")
if not uploadfolder_exists:
summarygood = False
print("UPLOAD_FOLDER ({0}) does not exist!".format(settings.UPLOAD_FOLDER))
print(f"UPLOAD_FOLDER ({settings.UPLOAD_FOLDER}) does not exist!")
if not rooturl_good:
summarygood = False
print("ROOTURL may cause issues!")
print("With current settings, this is what a generated URL would look like:")
print("{0}example.png".format(settings.ROOTURL))
print(f"{settings.ROOTURL}example.png")
if "SAVELOG" in checksettings:
print("[*] SAVELOG is {0}".format(settings.SAVELOG))
print(f"[*] SAVELOG is {settings.SAVELOG}")
if summarygood:
print("[" + u"\u2713" + "] This configuration passes all tests!")

View File

@ -5,11 +5,12 @@ imgupload.py
Flask application for processing images uploaded through POST requests.
"""
from flask import Flask, request, jsonify, Response
from flask import Flask, request, jsonify
from flask_api import status
from pathlib import Path
import os
import datetime
from PIL import Image
import settings # app settings (such as allowed extensions)
import functions # custom functions
@ -27,11 +28,11 @@ def allowed_extension(testext):
def log_savelog(key, ip, savedname):
if settings.SAVELOG_KEYPREFIX > 0:
with open(settings.SAVELOG, "a+") as slogf:
slogf.write("[{0}] {1}: {2} - {3}\n".format(datetime.datetime.now(), key[:settings.SAVELOG_KEYPREFIX], ip, savedname))
slogf.write(f"[{datetime.datetime.now()}] {key[:settings.SAVELOG_KEYPREFIX]}: {ip} - {savedname}\n")
os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
else:
with open(settings.SAVELOG, "a+") as slogf:
slogf.write("[{0}] {1} - {2}\n".format(datetime.datetime.now(), ip, savedname))
slogf.write(f"[{datetime.datetime.now()}] {ip} - {savedname}\n")
os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
@app.route("/upload", methods = ["POST"])
@ -67,14 +68,36 @@ def upload():
fext = Path(f.filename).suffix # get the uploaded extension
if allowed_extension(fext): # if the extension is allowed
print("Generating file with extension {0}".format(fext))
fname = functions.generate_name() + fext # generate file name
print("Generated name: {0}".format(fname))
if not "imageName" in request.form.keys():
print(f"Generating file with extension {fext}")
fname = functions.generate_name() + fext # generate file name
print(f"Generated name: {fname}")
else:
fname = request.form["imageName"]
if len(fname) > 0:
print(f"Request imageName: {fname}")
if not fname.lower().endswith(fext.lower()): # if requested name doesn't have the correct extension
fname += fext # add the extension
print(f"Added extension; new filename: {fname}")
else:
print("Requested filename is blank!")
fname = functions.generate_name() + fext # generate a valid filename
print(f"Generated name: {fname}")
if f: # if the uploaded image exists
print("Uploaded image exists")
f.save(os.path.join(settings.UPLOAD_FOLDER, fname)) # save the image
print("Saved to {0}".format(fname))
if Path(os.path.join(settings.UPLOAD_FOLDER, fname)).is_file():
print("Requested filename already exists!")
return jsonify({'status': 'error', 'error': 'FILENAME_TAKEN'}), status.HTTP_409_CONFLICT
f.save(f"/tmp/{fname}") # save the image temporarily (before removing EXIF)
image = Image.open(f"/tmp/{fname}")
data = list(image.getdata())
stripped = Image.new(image.mode, image.size)
stripped.putdata(data)
stripped.save(os.path.join(settings.UPLOAD_FOLDER, fname)) # save the image without EXIF
print(f"Saved to {fname}")
url = settings.ROOTURL + fname # construct the url to the image
if settings.SAVELOG != "/dev/null":
print("Saving to savelog")
@ -91,7 +114,7 @@ def upload():
else: # if the key was not valid
print("Key is invalid!")
print("Request key: {0}".format(request.form["uploadKey"]))
print(f"Request key: {request.form['uploadKey']}")
return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
else: # if uploadKey was not found in request body

View File

@ -31,7 +31,7 @@ def savekey(key):
logging.info("uploadkeys file doesn't exist, it will be created.")
with open("uploadkeys", "a+") as keyfile:
keyfile.write(str(key) + "\n") # add the key
logging.debug("Saved a key to uploadkeys: {0}".format(key))
logging.debug(f"Saved a key to uploadkeys: {key}")
def rmkey(delkey):
@ -85,14 +85,14 @@ def cmd_list(args):
if len(validkeys[i]) > 6:
showkey += "..." # add ellipses since the key was shortened in list
print(" [{0}] {1}".format(i+1, showkey))
print(f" [{i+1}] {showkey}")
def cmd_generate(args):
k = genkey(args.length)
logging.debug("Generated a new key: {0}".format(k))
logging.debug(f"Generated a new key: {k}")
savekey(k)
print("Your new key is: {0}".format(k))
print(f"Your new key is: {k}")
def cmd_add(args):
@ -104,7 +104,6 @@ def cmd_add(args):
print(ak)
if input("Is the above key correct? [y/N] ").lower() == "y":
logging.debug("Interpreted as yes")
ask_for_key = False
savekey(ak)
logging.info("Added.")
else:
@ -124,14 +123,14 @@ def cmd_dedupe(args):
for d in dupes:
r = rmkey(d)
logging.debug(r)
logging.info("Removed duplicate key: {0}".format(d))
logging.info(f"Removed duplicate key: {d}")
else:
logging.info("[" + u"\u2713" + "] No duplicate keys found!")
def cmd_show(args):
for k in get_keys():
if k[:6] == args.prefix:
print("Key: {0}".format(k))
print(f"Key: {k}")
break

View File

@ -1,2 +1,3 @@
Flask_API==2.0
Flask==1.1.2
Pillow==8.0.1

12
uwsgi.ini.default Normal file
View File

@ -0,0 +1,12 @@
[uwsgi]
socket = /srv/imgupload/uwsgi.sock
chmod-socket = 755
chdir = /srv/imgupload
venv = /srv/imgupload/env
master = true
module = imgupload:app
processes = 10
threads = 1
uid = www-data
gid = www-data
plugins = python3,logfile