Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
70e84ec8f0 | |||
b11c7c2845 | |||
86ec88db08 | |||
fb416d1c0d | |||
b20b78fd17 | |||
2b8330e32a | |||
63818bd371 | |||
67cb916ac9 | |||
9910bc279c | |||
f100732e4d | |||
46420eecda | |||
7b372a7b6f | |||
5570710432 | |||
ba68674e4e | |||
99ff2c68a3 | |||
dd069bf395 | |||
f21adfa04e | |||
3d5c55498f | |||
5e2be10434 | |||
7c1f449bce | |||
0dbcc0e380 | |||
b8b5a2518c | |||
805e545b39 | |||
|
9a117817f7 |
7
.github/README.md
vendored
Normal file
7
.github/README.md
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# imgupload
|
||||||
|
|
||||||
|
## Moving from GitHub to Gitea
|
||||||
|
|
||||||
|
**TL;DR: Please go to my Gitea instance instead of GitHub for anything related to imgupload. [https://git.bbaovanc.com/bbaovanc/imgupload](https://git.bbaovanc.com/bbaovanc/imgupload)**
|
||||||
|
|
||||||
|
This repository might not exist on GitHub in the future! Releases will not be released here in the future. Instead, they will be released on the repository on my Gitea instance, which you can find [here](https://git.bbaovanc.com/bbaovanc/imgupload). Issues and pull requests should also be created on Gitea. For now, commits will still be pushed to this repository, but that may change in the future.
|
27
.gitignore
vendored
27
.gitignore
vendored
@ -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
|
||||||
|
92
README.md
92
README.md
@ -1,26 +1,88 @@
|
|||||||
# imgupload
|
# imgupload
|
||||||
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/BBaoVanC/imgupload/master?color=purple) ![GitHub repo size](https://img.shields.io/github/repo-size/bbaovanc/imgupload?color=purple) ![GitHub All Releases](https://img.shields.io/github/downloads/bbaovanc/imgupload/total?color=purple) ![GitHub issues](https://img.shields.io/github/issues/bbaovanc/imgupload?color=purple) ![GitHub closed issues](https://img.shields.io/github/issues-closed/bbaovanc/imgupload?color=purple) ![GitHub](https://img.shields.io/github/license/bbaovanc/imgupload?color=purple)
|
|
||||||
|
|
||||||
### What is imgupload?
|
<!---![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/BBaoVanC/imgupload/master?color=purple)
|
||||||
|
![GitHub repo size](https://img.shields.io/github/repo-size/bbaovanc/imgupload?color=purple)
|
||||||
|
![GitHub All Releases](https://img.shields.io/github/downloads/bbaovanc/imgupload/total?color=purple)
|
||||||
|
![GitHub issues](https://img.shields.io/github/issues/bbaovanc/imgupload?color=purple)
|
||||||
|
![GitHub closed issues](https://img.shields.io/github/issues-closed/bbaovanc/imgupload?color=purple)
|
||||||
|
![GitHub](https://img.shields.io/github/license/bbaovanc/imgupload?color=purple)-->
|
||||||
|
|
||||||
|
## What is imgupload?
|
||||||
|
|
||||||
imgupload is a Flask + uWSGI application to serve as an all-purpose image/file uploader over POST requests.
|
imgupload is a Flask + uWSGI application to serve as an all-purpose image/file uploader over POST requests.
|
||||||
|
|
||||||
### Installation
|
---
|
||||||
1. Clone the repository: `git clone https://github.com/BBaoVanC/imgupload.git`
|
|
||||||
2. Enter the imgupload directory: `cd imgupload`
|
## FAQ
|
||||||
3. Create a virtualenv: `python3 -m venv env`
|
|
||||||
4. Enter the virtualenv: `source env/bin/activate`
|
**Where can I send bug reports and feature requests?**
|
||||||
5. Install dependencies: `python3 -m pip install -r requirements.txt`
|
|
||||||
6. Run the Flask app
|
You can create an issue [here](https://git.bbaovanc.com/bbaovanc/imgupload/issues).
|
||||||
|
|
||||||
|
**How do I use this program?**
|
||||||
|
|
||||||
|
See [Installation](#installation)
|
||||||
|
|
||||||
|
**I want to make a pull request. Where should I do that?**
|
||||||
|
|
||||||
|
First, fork [this repository](https://git.bbaovanc.com/bbaovanc/imgupload). If you don't have an account on my Gitea site yet, you can either create one, or sign in using your GitHub account. Commit your changes to your fork, and then create a pull request.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
## Running the Flask app
|
|
||||||
### Using uWSGI
|
### Using uWSGI
|
||||||
[https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html](https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html)
|
|
||||||
|
|
||||||
Instructions specific to imgupload are coming soon
|
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
|
||||||
|
|
||||||
|
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
|
### Using Flask development server
|
||||||
|
|
||||||
|
#### Setup
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ source env/bin/activate # if you haven't already entered the virtualenv
|
git clone https://git.bbaovanc.com/bbaovanc/imgupload.git
|
||||||
$ export FLASK_APP=imgupload.py
|
cd imgupload
|
||||||
$ flask run
|
python3 -m venv env
|
||||||
|
source env/bin/activate
|
||||||
|
pip3 install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Run
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export FLASK_APP=imgupload.py
|
||||||
|
flask run
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
_imgupload_ is licensed under the GPLv3 license. For more information, please refer to [`LICENSE`](https://git.bbaovanc.com/bbaovanc/imgupload/src/branch/master/LICENSE)
|
||||||
|
@ -4,6 +4,7 @@ configtest.py
|
|||||||
|
|
||||||
Tests the validity of your configuration in settings.py.
|
Tests the validity of your configuration in settings.py.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import settings as settings
|
import settings as settings
|
||||||
|
|
||||||
@ -34,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!")
|
||||||
|
|
||||||
@ -44,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
|
||||||
|
|
||||||
@ -62,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!")
|
||||||
|
|
||||||
@ -72,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!")
|
||||||
|
|
||||||
@ -84,8 +85,6 @@ if "ROOTURL" in checksettings:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
rooturl_good = False
|
rooturl_good = False
|
||||||
print(settings.ROOTURL)
|
|
||||||
print(settings.ROOTURL.startswith("https://"))
|
|
||||||
print("[!] ROOTURL does not start with `http://` or `https://`! This may cause issues!")
|
print("[!] ROOTURL does not start with `http://` or `https://`! This may cause issues!")
|
||||||
if not settings.ROOTURL.endswith("/"):
|
if not settings.ROOTURL.endswith("/"):
|
||||||
rooturl_good = False
|
rooturl_good = False
|
||||||
@ -93,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.")
|
||||||
|
|
||||||
|
|
||||||
@ -112,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!")
|
||||||
|
@ -4,6 +4,7 @@ functions.py
|
|||||||
|
|
||||||
Functions used by imgupload which can be easily customized.
|
Functions used by imgupload which can be easily customized.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import string
|
import string
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
58
imgupload.py
58
imgupload.py
@ -4,11 +4,13 @@ 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, abort, 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
|
||||||
@ -17,7 +19,7 @@ app = Flask(__name__) # app is the app
|
|||||||
|
|
||||||
|
|
||||||
def allowed_extension(testext):
|
def allowed_extension(testext):
|
||||||
if testext in settings.ALLOWED_EXTENSIONS:
|
if testext.lower() in settings.ALLOWED_EXTENSIONS:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -26,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"])
|
||||||
@ -49,6 +51,11 @@ def upload():
|
|||||||
if request.form["uploadKey"] in validkeys: # check if uploadKey is valid
|
if request.form["uploadKey"] in validkeys: # check if uploadKey is valid
|
||||||
print("Key is valid!")
|
print("Key is valid!")
|
||||||
|
|
||||||
|
if "verify" in request.form.keys():
|
||||||
|
if request.form["verify"] == "true":
|
||||||
|
print("Request is asking if key is valid (it is)")
|
||||||
|
return jsonify({'status': 'key_valid'})
|
||||||
|
|
||||||
if "imageUpload" in request.files: # check if image to upload was provided
|
if "imageUpload" in request.files: # check if image to upload was provided
|
||||||
f = request.files["imageUpload"] # f is the image to upload
|
f = request.files["imageUpload"] # f is the image to upload
|
||||||
else:
|
else:
|
||||||
@ -61,14 +68,36 @@ 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
|
||||||
print("Generating file with extension {0}".format(fext))
|
if not "imageName" in request.form.keys():
|
||||||
|
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:
|
||||||
|
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
|
if f: # if the uploaded image exists
|
||||||
print("Uploaded image exists")
|
print("Uploaded image exists")
|
||||||
f.save(os.path.join(settings.UPLOAD_FOLDER, fname)) # save the image
|
if Path(os.path.join(settings.UPLOAD_FOLDER, fname)).is_file():
|
||||||
print("Saved to {0}".format(fname))
|
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
|
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")
|
||||||
@ -81,21 +110,16 @@ def upload():
|
|||||||
|
|
||||||
else: # if the extension was invalid
|
else: # if the extension was invalid
|
||||||
print("Uploaded extension is invalid!")
|
print("Uploaded extension is invalid!")
|
||||||
abort(415)
|
return jsonify({'status': 'error', 'error': 'INVALID_EXTENSION'}), status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
|
||||||
|
|
||||||
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']}")
|
||||||
abort(401)
|
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
|
||||||
print("No uploadKey found in request!")
|
print("No uploadKey found in request!")
|
||||||
abort(401)
|
return jsonify({'status': 'error', 'error': 'UNAUTHORIZED'}), status.HTTP_401_UNAUTHORIZED
|
||||||
|
|
||||||
|
|
||||||
else: # if the request method wasn't post
|
|
||||||
print("Request method was not POST!")
|
|
||||||
abort(405)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print("Run with `flask` or a WSGI server!")
|
print("Run with `flask` or a WSGI server!")
|
||||||
|
61
keyctl.py
61
keyctl.py
@ -8,38 +8,43 @@ Command-line utility for easy management of the uploadkeys file.
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import random
|
import secrets
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
|
||||||
|
def read_keyfile():
|
||||||
|
with open("uploadkeys", "r") as keyfile: # open uploadkeys
|
||||||
|
keys = keyfile.readlines() # read all the keys
|
||||||
|
logging.debug("Read uploadkeys")
|
||||||
|
keys = [x.strip("\n") for x in keys] # strip newlines from keys
|
||||||
|
logging.debug("Stripped newlines from keys")
|
||||||
|
return keys
|
||||||
|
|
||||||
|
|
||||||
def genkey(length):
|
def genkey(length):
|
||||||
key = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(length))
|
key = ''.join(secrets.choice(string.ascii_letters + string.digits) for x in range(length))
|
||||||
return key
|
return key
|
||||||
|
|
||||||
|
|
||||||
def savekey(key):
|
def savekey(key):
|
||||||
if not Path("uploadkeys").is_file():
|
if not Path("uploadkeys").is_file(): # if uploadkeys doesn't exist, log an info message
|
||||||
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")
|
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):
|
||||||
removedkey = False
|
removedkey = False
|
||||||
with open("uploadkeys", "r") as keyfile:
|
allkeys = read_keyfile()
|
||||||
allkeys = keyfile.readlines()
|
if delkey in allkeys: # if the key to remove exists
|
||||||
logging.debug("Loaded all upload keys")
|
allkeys.remove(delkey) # remove the first instance of the key
|
||||||
allkeys = [x.strip("\n") for x in allkeys]
|
|
||||||
logging.debug("Stripped keys")
|
|
||||||
if delkey in allkeys:
|
|
||||||
allkeys.remove(delkey)
|
|
||||||
removedkey = True
|
removedkey = True
|
||||||
logging.debug("Removed one instance of the key")
|
logging.debug("Removed one instance of the key")
|
||||||
|
|
||||||
with open("uploadkeys", "w") as keyfile:
|
with open("uploadkeys", "w") as keyfile:
|
||||||
for k in allkeys:
|
for k in allkeys:
|
||||||
keyfile.write(k + "\n")
|
keyfile.write(k + "\n") # write the remaining keys
|
||||||
|
|
||||||
if removedkey:
|
if removedkey:
|
||||||
return True
|
return True
|
||||||
@ -48,11 +53,8 @@ def rmkey(delkey):
|
|||||||
|
|
||||||
|
|
||||||
def find_duplicates():
|
def find_duplicates():
|
||||||
with open("uploadkeys", "r") as keyfile:
|
allkeys = read_keyfile()
|
||||||
allkeys = keyfile.readlines()
|
|
||||||
logging.debug("Read all keys")
|
|
||||||
allkeys = [x.strip("\n") for x in allkeys]
|
|
||||||
logging.debug("Stripped newlines")
|
|
||||||
seen = set()
|
seen = set()
|
||||||
ukeys = []
|
ukeys = []
|
||||||
dupkeys = []
|
dupkeys = []
|
||||||
@ -66,11 +68,7 @@ def find_duplicates():
|
|||||||
|
|
||||||
|
|
||||||
def get_keys():
|
def get_keys():
|
||||||
with open("uploadkeys", "r") as keyfile: # load valid keys
|
validkeys = read_keyfile()
|
||||||
validkeys = keyfile.readlines()
|
|
||||||
logging.debug("Read uploadkeys")
|
|
||||||
validkeys = [x.strip("\n") for x in validkeys]
|
|
||||||
logging.debug("Stripped newlines from validkeys")
|
|
||||||
while "" in validkeys:
|
while "" in validkeys:
|
||||||
validkeys.remove("")
|
validkeys.remove("")
|
||||||
logging.debug("Removed blank keys")
|
logging.debug("Removed blank keys")
|
||||||
@ -85,16 +83,16 @@ def cmd_list(args):
|
|||||||
for i in range(len(validkeys)):
|
for i in range(len(validkeys)):
|
||||||
showkey = validkeys[i][:6]
|
showkey = validkeys[i][:6]
|
||||||
if len(validkeys[i]) > 6:
|
if len(validkeys[i]) > 6:
|
||||||
showkey += "..."
|
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):
|
||||||
@ -106,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:
|
||||||
@ -121,17 +118,19 @@ def cmd_remove(args):
|
|||||||
logging.info("No key was removed.")
|
logging.info("No key was removed.")
|
||||||
|
|
||||||
def cmd_dedupe(args):
|
def cmd_dedupe(args):
|
||||||
for d in find_duplicates():
|
dupes = find_duplicates()
|
||||||
|
if len(dupes) > 0:
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
Flask_API==2.0
|
Flask_API==2.0
|
||||||
Flask==1.1.2
|
Flask==1.1.2
|
||||||
|
Pillow==8.0.1
|
||||||
|
12
uwsgi.ini.default
Normal file
12
uwsgi.ini.default
Normal 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
|
Reference in New Issue
Block a user