Compare commits

...

8 Commits
v2.3 ... master

6 changed files with 89 additions and 53 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 # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
@ -50,6 +55,7 @@ coverage.xml
*.py,cover *.py,cover
.hypothesis/ .hypothesis/
.pytest_cache/ .pytest_cache/
pytestdebug.log
# Translations # Translations
*.mo *.mo
@ -70,6 +76,7 @@ instance/
# Sphinx documentation # Sphinx documentation
docs/_build/ docs/_build/
doc/_build/
# PyBuilder # PyBuilder
target/ target/
@ -109,6 +116,7 @@ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
pythonenv*
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
@ -128,6 +136,25 @@ dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .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 # imgupload custom
uploadkeys uploadkeys
savelog.log savelog.log

View File

@ -35,23 +35,25 @@ First, fork [this repository](https://git.bbaovanc.com/bbaovanc/imgupload). If y
Note: replace `www-data` with whatever user your webserver runs as. Note: replace `www-data` with whatever user your webserver runs as.
1. Make /srv/imgupload: `sudo mkdir /srv/imgupload` 1. Go to /srv: `cd /srv`
2. Change ownership of /srv/imgupload: `sudo chown www-data:www-data /srv/imgupload` 2. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git`
3. Enter www-data user: `sudo su www-data` 3. Change ownership of /srv/imgupload: `sudo chown www-data:www-data /srv/imgupload`
4. Change directories to /srv/imgupload: `cd /srv/imgupload` 4. Enter www-data user: `sudo su www-data`
5. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git` 5. Change directories to /srv/imgupload: `cd /srv/imgupload`
6. Enter the imgupload directory: `cd imgupload` 6. Checkout the version you want (replace [version] with desired version tag: `git checkout [version]`
7. Create a virtualenv: `python3 -m venv env` 7. Enter the imgupload directory: `cd imgupload`
8. Enter the virtualenv: `source env/bin/activate` 8. Create a virtualenv: `python3 -m venv env`
9. Install dependencies: `python3 -m pip install -r requirements.txt` 9. Enter the virtualenv: `source env/bin/activate`
10. Leave the www-data user: `exit` 10. Install dependencies: `python3 -m pip install -r requirements.txt`
11. Copy the default uWSGI configuration: `sudo cp /srv/imgupload/uwsgi.ini.default /etc/uwsgi/apps-available/imgupload.ini` 11. Leave the www-data user: `exit`
12. Modify `/etc/uwsgi/apps-available/imgupload.ini` to your preferences 12. Copy the default uWSGI configuration: `sudo cp /srv/imgupload/uwsgi.ini.default /etc/uwsgi/apps-available/imgupload.ini`
13. Enable imgupload: `sudo ln -s /etc/uwsgi/apps-available/imgupload.ini /etc/uwsgi/apps-enabled/` 13. Modify `/etc/uwsgi/apps-available/imgupload.ini` to your preferences
14. Restart uWSGI: `sudo systemctl restart uwsgi` 14. Enable imgupload: `sudo ln -s /etc/uwsgi/apps-available/imgupload.ini /etc/uwsgi/apps-enabled/`
15. Set up your webserver to proxy the uwsgi.sock 15. Restart uWSGI: `sudo systemctl restart uwsgi`
16. Set up your webserver to proxy the uwsgi.sock
Example NGINX location block: Example NGINX location block:
```nginx ```nginx
location /upload { location /upload {
include uwsgi_params; include uwsgi_params;
@ -62,22 +64,21 @@ location /upload {
### Using Flask development server ### Using Flask development server
#### Setup #### Setup
```shell ```shell
$ git clone https://git.bbaovanc.com/bbaovanc/imgupload.git git clone https://git.bbaovanc.com/bbaovanc/imgupload.git
$ cd imgupload cd imgupload
$ python3 -m venv env python3 -m venv env
$ source env/bin/activate source env/bin/activate
$ pip3 install -r requirements.txt pip3 install -r requirements.txt
``` ```
#### Run #### Run
```shell ```shell
$ export FLASK_APP=imgupload.py export FLASK_APP=imgupload.py
$ flask run 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: if len(unset_settings) > 0:
for unset in unset_settings: for unset in unset_settings:
checksettings.remove(unset) 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: else:
print("[" + u"\u2713" + "] Found all required settings!") print("[" + u"\u2713" + "] Found all required settings!")
@ -45,7 +45,7 @@ typesgood = True
typeswrong = [] typeswrong = []
for testtype in checksettings: for testtype in checksettings:
if type(getattr(settings, testtype)) is not deftypes[testtype]: 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) typeswrong.append(testtype)
typesgood = False typesgood = False
@ -63,7 +63,7 @@ if "ALLOWED_EXTENSIONS" in checksettings:
if len(invalid_exts) > 0: if len(invalid_exts) > 0:
print("[!] The following extensions listed in ALLOWED_EXTENSIONS are invalid:") print("[!] The following extensions listed in ALLOWED_EXTENSIONS are invalid:")
for e in invalid_exts: 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: else:
print("[" + u"\u2713" + "] ALLOWED_EXTENSIONS is good!") print("[" + u"\u2713" + "] ALLOWED_EXTENSIONS is good!")
@ -73,7 +73,7 @@ uploadfolder_exists = True
if "UPLOAD_FOLDER" in checksettings: if "UPLOAD_FOLDER" in checksettings:
if not os.path.isdir(settings.UPLOAD_FOLDER): if not os.path.isdir(settings.UPLOAD_FOLDER):
uploadfolder_exists = False 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: else:
print("[" + u"\u2713" + "] UPLOAD_FOLDER exists!") print("[" + u"\u2713" + "] UPLOAD_FOLDER exists!")
@ -92,14 +92,14 @@ if "ROOTURL" in checksettings:
if not rooturl_good: if not rooturl_good:
print(" With your current settings, this is what a generated url would look like:") 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: else:
print("[" + u"\u2713" + "] ROOTURL is good!") print("[" + u"\u2713" + "] ROOTURL is good!")
# Ask the user if SAVELOG is the intended filename # Ask the user if SAVELOG is the intended filename
if "SAVELOG" in checksettings: 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.") print("[*] If this is not the intended filename, please fix it.")
@ -111,32 +111,32 @@ if len(unset_settings) > 0:
summarygood = False summarygood = False
print("Unset settings:") print("Unset settings:")
for unset in unset_settings: for unset in unset_settings:
print(" {0}".format(unset)) print(f" {unset}")
if len(typeswrong) > 0: if len(typeswrong) > 0:
summarygood = False summarygood = False
print("Incorrect types:") print("Incorrect types:")
for wtype in typeswrong: for wtype in typeswrong:
print(" {0}".format(wtype)) print(f" {wtype}")
if len(invalid_exts) > 0: if len(invalid_exts) > 0:
summarygood = False summarygood = False
print("Invalid extensions:") print("Invalid extensions:")
for wext in invalid_exts: for wext in invalid_exts:
print(" '{0}'".format(wext)) print(f" '{wext}'")
if not uploadfolder_exists: if not uploadfolder_exists:
summarygood = False 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: if not rooturl_good:
summarygood = False summarygood = False
print("ROOTURL may cause issues!") print("ROOTURL may cause issues!")
print("With current settings, this is what a generated URL would look like:") 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: if "SAVELOG" in checksettings:
print("[*] SAVELOG is {0}".format(settings.SAVELOG)) print(f"[*] SAVELOG is {settings.SAVELOG}")
if summarygood: if summarygood:
print("[" + u"\u2713" + "] This configuration passes all tests!") 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. 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 flask_api import status
from pathlib import Path from pathlib import Path
import os import os
import datetime import datetime
from PIL import Image
import settings # app settings (such as allowed extensions) import settings # app settings (such as allowed extensions)
import functions # custom functions import functions # custom functions
@ -27,11 +28,11 @@ def allowed_extension(testext):
def log_savelog(key, ip, savedname): def log_savelog(key, ip, savedname):
if settings.SAVELOG_KEYPREFIX > 0: if settings.SAVELOG_KEYPREFIX > 0:
with open(settings.SAVELOG, "a+") as slogf: 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) os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
else: else:
with open(settings.SAVELOG, "a+") as slogf: 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) os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
@app.route("/upload", methods = ["POST"]) @app.route("/upload", methods = ["POST"])
@ -68,28 +69,35 @@ def upload():
fext = Path(f.filename).suffix # get the uploaded extension fext = Path(f.filename).suffix # get the uploaded extension
if allowed_extension(fext): # if the extension is allowed if allowed_extension(fext): # if the extension is allowed
if not "imageName" in request.form.keys(): if not "imageName" in request.form.keys():
print("Generating file with extension {0}".format(fext)) print(f"Generating file with extension {fext}")
fname = functions.generate_name() + fext # generate file name fname = functions.generate_name() + fext # generate file name
print("Generated name: {0}".format(fname)) print(f"Generated name: {fname}")
else: else:
fname = request.form["imageName"] fname = request.form["imageName"]
if len(fname) > 0: if len(fname) > 0:
print("Request imageName: {0}".format(fname)) print(f"Request imageName: {fname}")
if not fname.lower().endswith(fext.lower()): # if requested name doesn't have the correct extension if not fname.lower().endswith(fext.lower()): # if requested name doesn't have the correct extension
fname += fext # add the extension fname += fext # add the extension
print("Added extension; new filename: {0}".format(fname)) print(f"Added extension; new filename: {fname}")
else: else:
print("Requested filename is blank!") print("Requested filename is blank!")
fname = functions.generate_name() + fext # generate a valid filename fname = functions.generate_name() + fext # generate a valid filename
print("Generated name: {0}".format(fname)) print(f"Generated name: {fname}")
if f: # if the uploaded image exists if f: # if the uploaded image exists
print("Uploaded image exists") print("Uploaded image exists")
if Path(os.path.join(settings.UPLOAD_FOLDER, fname)).is_file(): if Path(os.path.join(settings.UPLOAD_FOLDER, fname)).is_file():
print("Requested filename already exists!") print("Requested filename already exists!")
return jsonify({'status': 'error', 'error': 'FILENAME_TAKEN'}), status.HTTP_409_CONFLICT return jsonify({'status': 'error', 'error': 'FILENAME_TAKEN'}), status.HTTP_409_CONFLICT
f.save(os.path.join(settings.UPLOAD_FOLDER, fname)) # save the image
print("Saved to {0}".format(fname)) 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 url = settings.ROOTURL + fname # construct the url to the image
if settings.SAVELOG != "/dev/null": if settings.SAVELOG != "/dev/null":
print("Saving to savelog") print("Saving to savelog")
@ -106,7 +114,7 @@ def upload():
else: # if the key was not valid else: # if the key was not valid
print("Key is invalid!") 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 return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
else: # if uploadKey was not found in request body 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.") logging.info("uploadkeys file doesn't exist, it will be created.")
with open("uploadkeys", "a+") as keyfile: with open("uploadkeys", "a+") as keyfile:
keyfile.write(str(key) + "\n") # add the key 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): def rmkey(delkey):
@ -85,14 +85,14 @@ def cmd_list(args):
if len(validkeys[i]) > 6: if len(validkeys[i]) > 6:
showkey += "..." # add ellipses since the key was shortened in list 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): def cmd_generate(args):
k = genkey(args.length) k = genkey(args.length)
logging.debug("Generated a new key: {0}".format(k)) logging.debug(f"Generated a new key: {k}")
savekey(k) savekey(k)
print("Your new key is: {0}".format(k)) print(f"Your new key is: {k}")
def cmd_add(args): def cmd_add(args):
@ -104,7 +104,6 @@ def cmd_add(args):
print(ak) print(ak)
if input("Is the above key correct? [y/N] ").lower() == "y": if input("Is the above key correct? [y/N] ").lower() == "y":
logging.debug("Interpreted as yes") logging.debug("Interpreted as yes")
ask_for_key = False
savekey(ak) savekey(ak)
logging.info("Added.") logging.info("Added.")
else: else:
@ -124,14 +123,14 @@ def cmd_dedupe(args):
for d in dupes: for d in dupes:
r = rmkey(d) r = rmkey(d)
logging.debug(r) logging.debug(r)
logging.info("Removed duplicate key: {0}".format(d)) logging.info(f"Removed duplicate key: {d}")
else: else:
logging.info("[" + u"\u2713" + "] No duplicate keys found!") logging.info("[" + u"\u2713" + "] No duplicate keys found!")
def cmd_show(args): def cmd_show(args):
for k in get_keys(): for k in get_keys():
if k[:6] == args.prefix: if k[:6] == args.prefix:
print("Key: {0}".format(k)) print(f"Key: {k}")
break break

View File

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