Files zipping update

This commit is contained in:
Profitroll 2023-01-19 13:35:27 +01:00
parent 3c245d8671
commit aa8be6006c
4 changed files with 84 additions and 64 deletions

View File

@ -9,6 +9,7 @@
"locations": {
"data": "data"
},
"compression": 5,
"messages": {
"key_expired": "API key expired",
"key_invalid": "Invalid API key",
@ -19,5 +20,25 @@
"user_already_exists": "User with this username already exists.",
"email_confirmed": "Email confirmed. You can now log in.",
"email_code_invalid": "Confirmation code is invalid."
},
"external_address": "localhost",
"registration_enabled": false,
"registration_requires_confirmation": false,
"mailer": {
"smtp": {
"host": "",
"port": 0,
"sender": "",
"login": "",
"password": "",
"use_ssl": true,
"use_tls": false
},
"messages": {
"registration_confirmation": {
"subject": "Email confirmation",
"message": "To confirm your email please follow this link: {0}"
}
}
}
}

View File

@ -1,19 +1,35 @@
from datetime import datetime
from io import BytesIO
from urllib.parse import quote_plus
from os import path, remove
from typing import Dict, List, Union
from zipfile import ZipFile
from os import makedirs, path, remove
from typing import Dict, List, Tuple, Union
from uuid import uuid4
from zipfile import ZipFile, ZIP_DEFLATED
from xmltodict import parse
from models.saves import StardewSave
from modules.app import app, get_api_key, user_by_key
from modules.utils import saveFile
from modules.utils import configGet, saveFile
from modules.database import col_devices, col_saves
from fastapi import HTTPException, Depends, UploadFile
from fastapi.responses import UJSONResponse, FileResponse, Response
from fastapi.openapi.models import APIKey
from starlette.status import HTTP_204_NO_CONTENT, HTTP_404_NOT_FOUND, HTTP_406_NOT_ACCEPTABLE
def zip_saves(save_filename: str, save_bytes: bytes, saveinfo_bytes: bytes) -> Tuple[str, str]:
save_uuid = str(uuid4())
makedirs(path.join(configGet("data", "locations"), "files", save_uuid))
#saveFile(save_bytes, filename=save_filename, dirname=zipname)
#saveFile(saveinfo_bytes, filename="SaveGameInfo", dirname=zipname)
with ZipFile(path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave"), 'w', ZIP_DEFLATED, compresslevel=configGet("compression")) as ziph:
ziph.writestr("SaveGameInfo", saveinfo_bytes)
ziph.writestr(save_filename, save_bytes)
return save_uuid, path.join(configGet("data", "locations"), "files", save_uuid, save_filename+".svsave")
def zipfiles(filenames, save_name: str) -> Response:
@ -29,7 +45,7 @@ def zipfiles(filenames, save_name: str) -> Response:
# Add file, at correct path
for entry in (list(col_saves.find({"files.save.uuid": fname})) + list(col_saves.find({"files.saveinfo.uuid": fname}))):
filename = entry["files"]["save"]["name"] if (entry["files"]["save"]["uuid"] == fname) else entry["files"]["saveinfo"]["name"]
filename = entry["file"]["save"]["name"] if (entry["file"]["save"]["uuid"] == fname) else entry["file"]["saveinfo"]["name"]
zf.write(fpath, filename)
# Must close zip for all contents to be written
@ -60,7 +76,7 @@ async def saves_get(device: Union[str, None] = None, apikey: APIKey = Depends(ge
out_entry = entry
del out_entry["_id"]
del out_entry["user"]
del out_entry["files"]
del out_entry["file"]
output.append(out_entry)
return UJSONResponse(output)
@ -79,7 +95,7 @@ async def saves_get_by_id(id: int, device: Union[str, None] = None, apikey: APIK
for entry in saves_entries:
out_entry = entry
del out_entry["_id"]
del out_entry["files"]
del out_entry["file"]
del out_entry["user"]
output.append(out_entry)
@ -91,7 +107,7 @@ async def saves_get_by_both_ids(id: int, save_date: int, device: Union[str, None
saves_entry = col_saves.find_one({"user": user_by_key(apikey), "id": id, "date": save_date}) if device is None else col_saves.find_one({"user": user_by_key(apikey), "id": id, "device": device, "date": save_date})
if saves_entry is not None:
del saves_entry["_id"]
del saves_entry["files"]
del saves_entry["file"]
return UJSONResponse(saves_entry)
else:
raise HTTPException(HTTP_404_NOT_FOUND, detail="Could not find save with such id.")
@ -103,8 +119,7 @@ async def saves_delete_by_id(id: int, apikey: APIKey = Depends(get_api_key)):
if col_saves.count_documents({"user": user, "id": id}) > 0:
saves_entries = list(col_saves.find({"user": user, "id": id}))
for entry in saves_entries:
remove(entry["files"]["save"]["path"])
remove(entry["files"]["saveinfo"]["path"])
remove(entry["file"]["path"])
col_saves.delete_many({"user": user, "id": id})
return Response(status_code=HTTP_204_NO_CONTENT)
else:
@ -115,18 +130,26 @@ async def saves_delete_by_id(id: int, apikey: APIKey = Depends(get_api_key)):
async def saves_delete_by_both_ids(id: int, save_date: int, apikey: APIKey = Depends(get_api_key)):
saves_entry = col_saves.find_one_and_delete({"id": id, "date": save_date})
if saves_entry is not None:
remove(saves_entry["files"]["save"]["path"])
remove(saves_entry["files"]["saveinfo"]["path"])
remove(saves_entry["file"]["path"])
return Response(status_code=HTTP_204_NO_CONTENT)
else:
raise HTTPException(HTTP_404_NOT_FOUND, detail="Could not find save with such id.")
@app.get("/saves/{id}/{save_date}/download", response_class=FileResponse, description="Get game save as .svsave file by its id and save date")
async def saves_download(id: int, save_date: int, apikey: APIKey = Depends(get_api_key)):
saves_entry = col_saves.find_one({"user": user_by_key(apikey), "id": id, "date": save_date})
async def saves_download(id: int, save_date: int, device: Union[str, None] = None, apikey: APIKey = Depends(get_api_key)):
saves_entry = col_saves.find_one({"user": user_by_key(apikey), "id": id, "date": save_date}) if device is None else col_saves.find_one({"user": user_by_key(apikey), "id": id, "device": device, "date": save_date})
if saves_entry is not None: # type: ignore
return zipfiles([saves_entry["files"]["save"]["path"], saves_entry["files"]["saveinfo"]["path"]], save_name=f'{saves_entry["data"]["farmer"]}_{saves_entry["id"]}')
with open(saves_entry["file"]["path"], "rb") as file:
response = Response(
file.read(),
media_type="application/x-zip-compressed",
headers={
'Content-Disposition': f'attachment;filename={quote_plus(saves_entry["file"]["name"])}.svsave'
}
)
return response
# return zipfiles([saves_entry["file"]["save"]["path"], saves_entry["file"]["saveinfo"]["path"]], save_name=f'{saves_entry["data"]["farmer"]}_{saves_entry["id"]}')
else:
raise HTTPException(HTTP_404_NOT_FOUND, detail="Could not find save with such id.")
@ -147,26 +170,26 @@ async def saves_post(device: str, files: List[UploadFile], apikey: APIKey = Depe
if col_devices.find_one({"user": user, "name": device}) is None:
raise HTTPException(HTTP_404_NOT_FOUND, detail="Could not find device with that name.")
save_info = save_data = save_info_file = save_data_filename = save_data_file = save_info_file_id = save_data_file_id = None
save_info = save_data = save_info_file = save_data_filename = save_data_file = None
for file in files:
if file.filename == "SaveGameInfo":
save_info_file = await file.read()
save_info_file_id = saveFile(save_info_file)
save_info = parse(save_info_file.decode("utf-8"))
if "Farmer" not in save_info:
return error_return
else:
save_data_filename = file.filename
save_data_file = await file.read()
save_data_file_id = saveFile(save_data_file)
save_data = parse(save_data_file.decode("utf-8"))
if "SaveGame" not in save_data:
return error_return
if save_info is None or save_data is None or save_info_file is None or save_data_filename is None or save_data_file is None or save_info_file_id is None or save_data_file_id is None:
if save_info is None or save_data is None or save_info_file is None or save_data_filename is None or save_data_file is None:
return error_return
zipped = zip_saves(save_data_filename, save_data_file, save_info_file)
save_date = int(datetime.utcnow().timestamp())
index = {
@ -183,17 +206,10 @@ async def saves_post(device: str, files: List[UploadFile], apikey: APIKey = Depe
"season": int(save_info["Farmer"]["seasonForSaveGame"]),
"day": int(save_info["Farmer"]["dayOfMonthForSaveGame"])
},
"files": {
"save": {
"name": save_data_filename,
"uuid": save_data_file_id[0],
"path": save_data_file_id[1]
},
"saveinfo": {
"name": "SaveGameInfo",
"uuid": save_info_file_id[0],
"path": save_info_file_id[1]
}
"file": {
"name": save_data_filename,
"uuid": zipped[0],
"path": zipped[1]
}
}
@ -202,7 +218,7 @@ async def saves_post(device: str, files: List[UploadFile], apikey: APIKey = Depe
del save_info, save_data
del index["user"]
del index["files"]
del index["file"]
del index["_id"]
col_devices.find_one_and_update({"user": user, "name": device}, {"$set": {"last_save": save_date}})

View File

@ -62,7 +62,7 @@ def configGet(key: str, *args: str) -> Any:
this_key = this_key[dict_key]
return this_key[key]
def saveFile(filebytes: bytes) -> Tuple[str, str]:
def saveFile(filebytes: bytes, filename: Union[str, None] = None, dirname: Union[str, None] = None) -> Tuple[str, str]:
"""Save some bytedata into random file and return its ID
### Args:
@ -71,8 +71,11 @@ def saveFile(filebytes: bytes) -> Tuple[str, str]:
### Returns:
* `Tuple[str, str]`: Tuple where first item is an ID and the second is an absolute path to file
"""
makedirs(path.join(configGet("data", "locations"), "files"), exist_ok=True)
filename = str(uuid4())
with open(path.join(configGet("data", "locations"), "files", filename), "wb") as file:
pathlist = [configGet("data", "locations"), "files"]
if dirname is not None:
pathlist.append(dirname)
makedirs(path.join(pathlist), exist_ok=True)
filename = str(uuid4()) if filename is None else filename
with open(path.join(pathlist+[filename], "wb")) as file:
file.write(filebytes)
return filename, path.join(configGet("data", "locations"), "files", filename)
return filename, path.join(pathlist+[filename])

View File

@ -13,15 +13,10 @@
"data.year",
"data.season",
"data.day",
"files",
"files.save",
"files.save.name",
"files.save.uuid",
"files.save.path",
"files.saveinfo",
"files.saveinfo.name",
"files.saveinfo.uuid",
"files.saveinfo.path"
"file",
"file.name",
"file.uuid",
"file.path"
],
"properties": {
"id": {
@ -60,31 +55,16 @@
"data.day": {
"bsonType": "int"
},
"files": {
"file": {
"bsonType": "object"
},
"files.save": {
"bsonType": "object"
},
"files.save.name": {
"file.name": {
"bsonType": "string"
},
"files.save.uuid": {
"file.uuid": {
"bsonType": "string"
},
"files.save.path": {
"bsonType": "string"
},
"files.saveinfo": {
"bsonType": "object"
},
"files.saveinfo.name": {
"bsonType": "string"
},
"files.saveinfo.uuid": {
"bsonType": "string"
},
"files.saveinfo.path": {
"file.path": {
"bsonType": "string"
}
}