diff --git a/.gitignore b/.gitignore index a9e18b0..143de91 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ .vscode .env node_modules -__pycache__ \ No newline at end of file +__pycache__ +*firebase-adminsdk*.json +temp \ No newline at end of file diff --git a/admin/main.py b/admin/main.py index 17ee41f..65bd011 100644 --- a/admin/main.py +++ b/admin/main.py @@ -1,33 +1,157 @@ import json +import PIL +import PIL.Image import boto3.session -from flask import Flask, render_template, request +from flask import Flask, render_template, request, Response +from werkzeug.utils import secure_filename from dotenv import load_dotenv import os import boto3 +from botocore.client import Config +from botocore.exceptions import ClientError, SSLError +import firebase_admin +from firebase_admin import firestore, credentials, exceptions load_dotenv() -app = Flask(__name__) +UPLOAD_FOLDER = "./temp" +ALLOWED_EXTENSIONS = {"png", "jpg", "jpeg"} -session = boto3.session.Session() -s3 = session.client( - service_name='s3', - aws_access_key_id=os.getenv('AWS_ACCESS_KEY_ID'), - aws_secret_access_key=os.getenv('AWS_SECRET_ACCESS_KEY'), - endpoint_url=os.getenv('AWS_ENDPOINT'), +app = Flask(__name__) +app.config["UPLOAD_FOLDER"] = UPLOAD_FOLDER + +s3 = boto3.client( + service_name="s3", + aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), + aws_secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"), + endpoint_url=os.getenv("AWS_ENDPOINT"), + config=Config(s3={"request_checksum_calculation": "when_required"}), ) -@app.route('/') +cred = credentials.Certificate(os.getenv("FIREBASE_SERVICE_ACCOUNT")) +firebase_admin.initialize_app(cred) +db = firestore.client() + + +@app.route("/") def Home(): + from urllib.request import urlopen + + print(urlopen("https://www.howsmyssl.com/a/check").read()) # objects = s3.list_objects(Bucket=os.getenv("AWS_BUCKET"))['Contents'] # return render_template("Index.html", objects=objects) return render_template("Index.html", page_title="Home") -@app.route('/upload/') + +@app.route("/upload/") def Upload(): return render_template("Upload.html", page_title="Upload") -@app.route('/api/upload/', methods=['POST']) + +def allowed_file(filename): + return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS + + +def upload_file(file, bucket, key, mimetype) -> bool | Exception: + CHUNK_SIZE = 1024 * 1024 * 4 + uploaded_parts = [] + try: + __upload = s3.create_multipart_upload( + ACL="public-read", + Bucket=bucket, + Key=key, + ContentType=mimetype, + ) + isFileRead = False + part_num = 1 + while not isFileRead: + chunk = file.read(CHUNK_SIZE) + if len(chunk) == 0: + isFileRead = True + s3.complete_multipart_upload( + Bucket=bucket, + Key=key, + UploadId=__upload["UploadId"], + MultipartUpload={"Parts": uploaded_parts}, + ) + break + + try: + part = s3.upload_part( + Bucket=bucket, + Key=key, + PartNumber=part_num, + ContentLength=len(chunk), + UploadId=__upload["UploadId"], + Body=chunk, + ) + uploaded_parts.append({"ETag": part["ETag"], "PartNumber": part_num}) + part_num += 1 + except (ClientError, SSLError) as e: + s3.abort_multipart_upload( + Bucket=bucket, + Key=key, + UploadId=__upload["UploadId"], + ) + return e + except (ClientError, SSLError) as e: + return e + + return True + + +@app.route("/api/upload/", methods=["POST"]) def ApiUpload(): - print(request.files['file']) - print(request.form) - return {"status": "ok", "message": "Uploaded"} \ No newline at end of file + tags = request.form.get("tags").split(",") + urls = [] + for url in request.form.get("urls").split(";"): + if url == "": + continue + urls.append(json.loads(url)) + + file = request.files.get("file") + + if file.filename == "": + return Response( + '{"status": "error", "message": "FLASK ERR: GET FILE ERROR"}', 500 + ) + if file and allowed_file(file.filename): + filename = secure_filename(file.filename) + file.save(os.path.join(app.config["UPLOAD_FOLDER"], filename)) + else: + return Response( + '{"status": "error", "message": "FLASK ERR: UNSUPPORTED EXTENSION"}', 500 + ) + + Image = PIL.Image.open(os.path.join(app.config["UPLOAD_FOLDER"], filename)) + TMP_FILE = open(os.path.join(app.config["UPLOAD_FOLDER"], filename), "rb") + file_path = filename.split(".")[0] + file_ext = filename.split(".")[-1] + + s3OrigFileResponse = upload_file( + TMP_FILE, os.getenv("AWS_BUCKET"), f"{file_path}/{filename}", file.mimetype + ) + if s3OrigFileResponse is not True: + return Response( + json.dumps({"status": "error", "message": f"S3 ERR: {s3OrigFileResponse}"}), + 500, + ) + + try: + db.collection(os.getenv("PREFIX")).add( + { + "mimetype": file.mimetype, + "width": Image.width, + "height": Image.height, + "date": int(request.form.get("date")), + "alt": request.form.get("alt") or "", + "tags": tags or [], + "urls": urls or [], + }, + request.files["file"].filename, + ) + except exceptions.CONFLICT: + return Response( + '{"status": "error", "message": "FIRESTORE ERR: CONFLICT"}', 400 + ) + + return {"status": "ok", "message": "Uploaded"} diff --git a/admin/requirements.txt b/admin/requirements.txt index 00dc2db..c923232 100644 --- a/admin/requirements.txt +++ b/admin/requirements.txt @@ -1,5 +1,8 @@ -python-dotenv +python-dotenv # Load environment variables from .env -Flask -boto3 -pillow +boto3==1.35.99 # AWS SDK for Python (S3) +boto3-stubs[essential] +firebase-admin # Firebase Admin SDK for Python (FireStore) + +Flask # Web framework +pillow # Image processing diff --git a/admin/templates/Upload.html b/admin/templates/Upload.html index c832d9e..96d271e 100644 --- a/admin/templates/Upload.html +++ b/admin/templates/Upload.html @@ -56,9 +56,9 @@