Compare commits
No commits in common. "master" and "v2.3" have entirely different histories.
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,8 +1,3 @@
|
||||
|
||||
# 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]
|
||||
@ -55,7 +50,6 @@ coverage.xml
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
pytestdebug.log
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
@ -76,7 +70,6 @@ instance/
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
doc/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
@ -116,7 +109,6 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
pythonenv*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
@ -136,25 +128,6 @@ 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
|
||||
|
47
README.md
47
README.md
@ -35,25 +35,23 @@ First, fork [this repository](https://git.bbaovanc.com/bbaovanc/imgupload). If y
|
||||
|
||||
Note: replace `www-data` with whatever user your webserver runs as.
|
||||
|
||||
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
|
||||
1. Make /srv/imgupload: `sudo mkdir /srv/imgupload`
|
||||
2. Change ownership of /srv/imgupload: `sudo chown www-data:www-data /srv/imgupload`
|
||||
3. Enter www-data user: `sudo su www-data`
|
||||
4. Change directories to /srv/imgupload: `cd /srv/imgupload`
|
||||
5. Clone the repository: `git clone https://git.bbaovanc.com/bbaovanc/imgupload.git`
|
||||
6. Enter the imgupload directory: `cd imgupload`
|
||||
7. Create a virtualenv: `python3 -m venv env`
|
||||
8. Enter the virtualenv: `source env/bin/activate`
|
||||
9. Install dependencies: `python3 -m pip install -r requirements.txt`
|
||||
10. Leave the www-data user: `exit`
|
||||
11. Copy the default uWSGI configuration: `sudo cp /srv/imgupload/uwsgi.ini.default /etc/uwsgi/apps-available/imgupload.ini`
|
||||
12. Modify `/etc/uwsgi/apps-available/imgupload.ini` to your preferences
|
||||
13. Enable imgupload: `sudo ln -s /etc/uwsgi/apps-available/imgupload.ini /etc/uwsgi/apps-enabled/`
|
||||
14. Restart uWSGI: `sudo systemctl restart uwsgi`
|
||||
15. Set up your webserver to proxy the uwsgi.sock
|
||||
|
||||
Example NGINX location block:
|
||||
|
||||
```nginx
|
||||
location /upload {
|
||||
include uwsgi_params;
|
||||
@ -64,21 +62,22 @@ location /upload {
|
||||
|
||||
### Using Flask development server
|
||||
|
||||
|
||||
#### Setup
|
||||
|
||||
```shell
|
||||
git clone https://git.bbaovanc.com/bbaovanc/imgupload.git
|
||||
cd imgupload
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
$ 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
|
||||
$ export FLASK_APP=imgupload.py
|
||||
$ flask run
|
||||
```
|
||||
|
||||
---
|
||||
|
@ -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(f"[!] {unset} is unset. The default value is type {deftypes[unset].__name__} with value {defaults[unset]}")
|
||||
print("[!] {0} is unset. The default value is type {1} with value {2}".format(unset, deftypes[unset].__name__, 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(f"[!] {testtype} requires {deftypes[testtype].__name__}, but is {type(getattr(settings, testtype)).__name__}")
|
||||
print("[!] {0} requires {1}, but is {2}".format(testtype, deftypes[testtype].__name__, 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(f" {e} is listed in ALLOWED_EXTENSIONS, but doesn't start with a .")
|
||||
print(" {0} is listed in ALLOWED_EXTENSIONS, but doesn't start with a .".format(e))
|
||||
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(f"[!] The directory set in UPLOAD_FOLDER ('{settings.UPLOAD_FOLDER}') doesn't exist!")
|
||||
print("[!] The directory set in UPLOAD_FOLDER ('{0}') doesn't exist!".format(settings.UPLOAD_FOLDER))
|
||||
else:
|
||||
print("[" + u"\u2713" + "] UPLOAD_FOLDER exists!")
|
||||
|
||||
@ -92,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(f" {settings.ROOTURL}example.png")
|
||||
print(" {0}example.png".format(settings.ROOTURL))
|
||||
else:
|
||||
print("[" + u"\u2713" + "] ROOTURL is good!")
|
||||
|
||||
|
||||
# Ask the user if SAVELOG is the intended filename
|
||||
if "SAVELOG" in checksettings:
|
||||
print(f"[*] SAVELOG was interpreted to be {settings.SAVELOG}")
|
||||
print("[*] SAVELOG was interpreted to be {0}".format(settings.SAVELOG))
|
||||
print("[*] If this is not the intended filename, please fix it.")
|
||||
|
||||
|
||||
@ -111,32 +111,32 @@ if len(unset_settings) > 0:
|
||||
summarygood = False
|
||||
print("Unset settings:")
|
||||
for unset in unset_settings:
|
||||
print(f" {unset}")
|
||||
print(" {0}".format(unset))
|
||||
|
||||
if len(typeswrong) > 0:
|
||||
summarygood = False
|
||||
print("Incorrect types:")
|
||||
for wtype in typeswrong:
|
||||
print(f" {wtype}")
|
||||
print(" {0}".format(wtype))
|
||||
|
||||
if len(invalid_exts) > 0:
|
||||
summarygood = False
|
||||
print("Invalid extensions:")
|
||||
for wext in invalid_exts:
|
||||
print(f" '{wext}'")
|
||||
print(" '{0}'".format(wext))
|
||||
|
||||
if not uploadfolder_exists:
|
||||
summarygood = False
|
||||
print(f"UPLOAD_FOLDER ({settings.UPLOAD_FOLDER}) does not exist!")
|
||||
print("UPLOAD_FOLDER ({0}) does not exist!".format(settings.UPLOAD_FOLDER))
|
||||
|
||||
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(f"{settings.ROOTURL}example.png")
|
||||
print("{0}example.png".format(settings.ROOTURL))
|
||||
|
||||
if "SAVELOG" in checksettings:
|
||||
print(f"[*] SAVELOG is {settings.SAVELOG}")
|
||||
print("[*] SAVELOG is {0}".format(settings.SAVELOG))
|
||||
|
||||
if summarygood:
|
||||
print("[" + u"\u2713" + "] This configuration passes all tests!")
|
||||
|
30
imgupload.py
30
imgupload.py
@ -5,12 +5,11 @@ imgupload.py
|
||||
Flask application for processing images uploaded through POST requests.
|
||||
"""
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask import Flask, request, jsonify, Response
|
||||
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
|
||||
@ -28,11 +27,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(f"[{datetime.datetime.now()}] {key[:settings.SAVELOG_KEYPREFIX]}: {ip} - {savedname}\n")
|
||||
slogf.write("[{0}] {1}: {2} - {3}\n".format(datetime.datetime.now(), key[:settings.SAVELOG_KEYPREFIX], ip, savedname))
|
||||
os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
|
||||
else:
|
||||
with open(settings.SAVELOG, "a+") as slogf:
|
||||
slogf.write(f"[{datetime.datetime.now()}] {ip} - {savedname}\n")
|
||||
slogf.write("[{0}] {1} - {2}\n".format(datetime.datetime.now(), ip, savedname))
|
||||
os.chmod(settings.SAVELOG, settings.SAVELOG_CHMOD)
|
||||
|
||||
@app.route("/upload", methods = ["POST"])
|
||||
@ -69,35 +68,28 @@ def upload():
|
||||
fext = Path(f.filename).suffix # get the uploaded extension
|
||||
if allowed_extension(fext): # if the extension is allowed
|
||||
if not "imageName" in request.form.keys():
|
||||
print(f"Generating file with extension {fext}")
|
||||
print("Generating file with extension {0}".format(fext))
|
||||
fname = functions.generate_name() + fext # generate file name
|
||||
print(f"Generated name: {fname}")
|
||||
print("Generated name: {0}".format(fname))
|
||||
else:
|
||||
fname = request.form["imageName"]
|
||||
if len(fname) > 0:
|
||||
print(f"Request imageName: {fname}")
|
||||
print("Request imageName: {0}".format(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}")
|
||||
print("Added extension; new filename: {0}".format(fname))
|
||||
else:
|
||||
print("Requested filename is blank!")
|
||||
fname = functions.generate_name() + fext # generate a valid filename
|
||||
print(f"Generated name: {fname}")
|
||||
print("Generated name: {0}".format(fname))
|
||||
|
||||
if f: # if the uploaded image exists
|
||||
print("Uploaded image exists")
|
||||
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}")
|
||||
f.save(os.path.join(settings.UPLOAD_FOLDER, fname)) # save the image
|
||||
print("Saved to {0}".format(fname))
|
||||
url = settings.ROOTURL + fname # construct the url to the image
|
||||
if settings.SAVELOG != "/dev/null":
|
||||
print("Saving to savelog")
|
||||
@ -114,7 +106,7 @@ def upload():
|
||||
|
||||
else: # if the key was not valid
|
||||
print("Key is invalid!")
|
||||
print(f"Request key: {request.form['uploadKey']}")
|
||||
print("Request key: {0}".format(request.form["uploadKey"]))
|
||||
return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
else: # if uploadKey was not found in request body
|
||||
|
13
keyctl.py
13
keyctl.py
@ -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(f"Saved a key to uploadkeys: {key}")
|
||||
logging.debug("Saved a key to uploadkeys: {0}".format(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(f" [{i+1}] {showkey}")
|
||||
print(" [{0}] {1}".format(i+1, showkey))
|
||||
|
||||
|
||||
def cmd_generate(args):
|
||||
k = genkey(args.length)
|
||||
logging.debug(f"Generated a new key: {k}")
|
||||
logging.debug("Generated a new key: {0}".format(k))
|
||||
savekey(k)
|
||||
print(f"Your new key is: {k}")
|
||||
print("Your new key is: {0}".format(k))
|
||||
|
||||
|
||||
def cmd_add(args):
|
||||
@ -104,6 +104,7 @@ 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:
|
||||
@ -123,14 +124,14 @@ def cmd_dedupe(args):
|
||||
for d in dupes:
|
||||
r = rmkey(d)
|
||||
logging.debug(r)
|
||||
logging.info(f"Removed duplicate key: {d}")
|
||||
logging.info("Removed duplicate key: {0}".format(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(f"Key: {k}")
|
||||
print("Key: {0}".format(k))
|
||||
break
|
||||
|
||||
|
||||
|
@ -1,3 +1,2 @@
|
||||
Flask_API==2.0
|
||||
Flask==1.1.2
|
||||
Pillow==8.0.1
|
||||
|
Reference in New Issue
Block a user