Compare commits
13 Commits
v2.2.post1
...
master
Author | SHA1 | Date |
---|---|---|
BBaoVanC | 70e84ec8f0 | |
BBaoVanC | b11c7c2845 | |
BBaoVanC | 86ec88db08 | |
BBaoVanC | fb416d1c0d | |
BBaoVanC | b20b78fd17 | |
BBaoVanC | 2b8330e32a | |
BBaoVanC | 63818bd371 | |
BBaoVanC | 67cb916ac9 | |
BBaoVanC | 9910bc279c | |
BBaoVanC | f100732e4d | |
BBaoVanC | 46420eecda | |
BBaoVanC | 7b372a7b6f | |
BBaoVanC | 5570710432 |
|
@ -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
|
||||
|
|
57
README.md
57
README.md
|
@ -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
|
||||
```
|
||||
|
||||
---
|
||||
|
|
|
@ -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!")
|
||||
|
|
41
imgupload.py
41
imgupload.py
|
@ -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
|
||||
|
|
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("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
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
Flask_API==2.0
|
||||
Flask==1.1.2
|
||||
Pillow==8.0.1
|
||||
|
|
|
@ -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
|
Reference in New Issue