diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0249686e..6a37088a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -4,16 +4,24 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
+ {
+ "name": "MUIO Server",
+ "type": "debugpy",
+ "request": "launch",
+ "program": "${workspaceFolder}/API/app.py",
+ "console": "integratedTerminal",
+ "justMyCode": true
+ },
{
"name": "Python: Current File",
- "type": "python",
+ "type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python: Flask",
- "type": "python",
+ "type": "debugpy",
"request": "launch",
"module": "flask",
"env": {
@@ -24,7 +32,6 @@
},
"args": [
"run",
- //"--no-debugger",
"--no-reload"
],
"jinja": true
diff --git a/API/Classes/Base/Response.py b/API/Classes/Base/Response.py
new file mode 100644
index 00000000..0f803733
--- /dev/null
+++ b/API/Classes/Base/Response.py
@@ -0,0 +1,23 @@
+from flask import jsonify
+
+def api_response(success=True, message=None, data=None, error=None, status_code=200):
+ """
+ Standardized API response helper for MUIO.
+
+ Args:
+ success (bool): Whether the request was successful.
+ message (str, optional): A human-readable message.
+ data (dict|list, optional): The actual payload.
+ error (str|dict, optional): Detailed error information.
+ status_code (int): HTTP status code.
+
+ Returns:
+ tuple: (flask.Response, int) - A JSON response compatible with Flask's return type.
+ """
+ response = {
+ "success": success,
+ "message": message,
+ "data": data,
+ "error": error
+ }
+ return jsonify(response), status_code
diff --git a/API/Routes/Case/CaseRoute.py b/API/Routes/Case/CaseRoute.py
index 51e0ec29..4ad350dc 100644
--- a/API/Routes/Case/CaseRoute.py
+++ b/API/Routes/Case/CaseRoute.py
@@ -5,6 +5,7 @@
import pandas as pd
from Classes.Base import Config
from Classes.Base.FileClass import File
+from Classes.Base.Response import api_response
from Classes.Case.CaseClass import Case
from Classes.Case.UpdateCaseClass import UpdateCase
from Classes.Case.ImportTemplate import ImportTemplate
@@ -22,21 +23,22 @@ def initSyncS3():
syncS3.downloadSync(case, Config.DATA_STORAGE, Config.S3_BUCKET)
#downoload param file from S3 bucket
syncS3.downloadSync('Parameters.json', Config.DATA_STORAGE, Config.S3_BUCKET)
- response = {
- "message": "Cases syncronized with S3 bucket!",
- "status_code": "success"
- }
- return jsonify(response), 200
+ return api_response(
+ success=True,
+ message="Cases syncronized with S3 bucket!",
+ data={"status_code": "success"},
+ status_code=200
+ )
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/getCases", methods=['GET'])
def getCases():
try:
cases = [ f.name for f in os.scandir(Config.DATA_STORAGE) if f.is_dir() ]
- return jsonify(cases), 200
+ return api_response(success=True, data=cases, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/getResultCSV", methods=['POST'])
def getResultCSV():
@@ -48,9 +50,9 @@ def getResultCSV():
csvs = [ f.name for f in os.scandir(csvFolder) ]
else:
csvs = []
- return jsonify(csvs), 200
+ return api_response(success=True, data=csvs, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/getDesc", methods=['POST'])
def getDesc():
@@ -62,9 +64,9 @@ def getDesc():
"message": "Get model description success",
"desc": genData['osy-desc']
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/copyCase", methods=['POST'])
def copy():
@@ -81,6 +83,7 @@ def copy():
"message": 'Model '+ case + '_copy already exists, please rename existing model first!',
"status_code": "warning"
}
+ return api_response(success=False, message=response["message"], data=response, status_code=200)
else:
shutil.copytree(str(src), str(dest) )
#rename casename in genData
@@ -91,11 +94,11 @@ def copy():
"message": 'Model '+ case + ' copied!',
"status_code": "success"
}
- return(response)
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- raise IOError
+ return api_response(success=False, message="Error copying model", status_code=500)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error copying model", status_code=500)
@case_api.route("/deleteCase", methods=['POST'])
def deleteCase():
@@ -116,11 +119,11 @@ def deleteCase():
"message": 'Model '+ case + ' deleted!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="Error deleting case", status_code=500)
@case_api.route("/getResultData", methods=['POST'])
def getResultData():
@@ -131,12 +134,11 @@ def getResultData():
dataPath = Path(Config.DATA_STORAGE,casename,'view',dataJson)
data = File.readFile(dataPath)
response = data
-
else:
response = None
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/getParamFile", methods=['POST'])
def getParamFile():
@@ -144,10 +146,9 @@ def getParamFile():
dataJson = request.json['dataJson']
configPath = Path(Config.DATA_STORAGE, dataJson)
ConfigFile = File.readParamFile(configPath)
- response = ConfigFile
- return jsonify(response), 200
+ return api_response(success=True, data=ConfigFile, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/resultsExists", methods=['POST'])
def resultsExists():
@@ -168,9 +169,9 @@ def resultsExists():
else:
response = False
#response = True
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/saveParamFile", methods=['POST'])
def saveParamFile():
@@ -187,9 +188,9 @@ def saveParamFile():
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/saveScOrder", methods=['POST'])
def saveScOrder():
@@ -205,9 +206,9 @@ def saveScOrder():
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/updateData", methods=['POST'])
def updateData():
@@ -226,9 +227,9 @@ def updateData():
"message": "Your data has been saved!",
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/saveCase", methods=['POST'])
def saveCase():
@@ -376,15 +377,15 @@ def saveCase():
"message": "Your model configuration has been saved!",
"status_code": "created"
}
- else:
response = {
"message": "Model with same name already exists!",
"status_code": "exist"
}
+ return api_response(success=False, message=response["message"], data=response, status_code=200)
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('Error saving model IOError!'), 404
+ return api_response(success=False, message="Error saving model IOError!", status_code=404)
@case_api.route("/prepareCSV", methods=['POST'])
def prepareCSV():
@@ -410,10 +411,10 @@ def prepareCSV():
"message": 'CSV data downloaded!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/downloadCSV", methods=['GET'])
def downloadCSV():
@@ -425,7 +426,7 @@ def downloadCSV():
return send_file(dataFile.resolve(), as_attachment=True,mimetype='application/csv', max_age=0)
#return send_from_directory(dir, 'export.csv', as_attachment=True)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@case_api.route("/importTemplate", methods=['POST'])
def run():
@@ -434,11 +435,11 @@ def run():
template = ImportTemplate(data["osy-template"])
response = template.importProcess(data)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except(IndexError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="Index Error during import", status_code=500)
####################################################################################OBSOLETE AND SyncS3###################################################
diff --git a/API/Routes/Case/SyncS3Route.py b/API/Routes/Case/SyncS3Route.py
index f6041765..becf9478 100644
--- a/API/Routes/Case/SyncS3Route.py
+++ b/API/Routes/Case/SyncS3Route.py
@@ -4,6 +4,7 @@
import shutil
from Classes.Base import Config
from Classes.Base.SyncS3 import SyncS3
+from Classes.Base.Response import api_response
syncs3_api = Blueprint('SyncS3Route', __name__)
@@ -21,11 +22,11 @@ def deleteResultsPreSync():
"message": 'Case '+ case + ' deleted!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during sync deletion", status_code=500)
@syncs3_api.route("/uploadSync", methods=['POST'])
def uploadSync():
@@ -40,11 +41,11 @@ def uploadSync():
"message": 'Case '+ case + ' syncronized!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during sync upload", status_code=500)
@syncs3_api.route("/deleteSync", methods=['POST'])
def deleteSync():
@@ -58,11 +59,11 @@ def deleteSync():
"message": 'Case '+ case + ' deleted!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during sync deletion", status_code=500)
@syncs3_api.route("/updateSync", methods=['POST'])
def updateSync():
@@ -78,11 +79,11 @@ def updateSync():
"message": 'Case '+ case + ' deleted!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during sync update", status_code=500)
@syncs3_api.route("/updateSyncParamFile", methods=['GET'])
def updateSyncParamFile():
@@ -98,8 +99,8 @@ def updateSyncParamFile():
"message": 'Case '+ case + ' deleted!',
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during sync param update", status_code=500)
diff --git a/API/Routes/Case/ViewDataRoute.py b/API/Routes/Case/ViewDataRoute.py
index 6f442a9c..f50ed5a1 100644
--- a/API/Routes/Case/ViewDataRoute.py
+++ b/API/Routes/Case/ViewDataRoute.py
@@ -1,5 +1,6 @@
from flask import Blueprint, jsonify, request
from Classes.Case.OsemosysClass import Osemosys
+from Classes.Base.Response import api_response
viewdata_api = Blueprint('ViewDataRoute', __name__)
@@ -16,9 +17,9 @@ def viewData():
response = data
else:
response = None
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@viewdata_api.route("/viewTEData", methods=['POST'])
def viewTEData():
@@ -32,9 +33,9 @@ def viewTEData():
response = data
else:
response = None
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@viewdata_api.route("/updateViewData", methods=['POST'])
def updateViewData():
@@ -65,9 +66,9 @@ def updateViewData():
"status_code": "error"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@viewdata_api.route("/updateTEViewData", methods=['POST'])
def updateTEViewData():
@@ -94,6 +95,6 @@ def updateTEViewData():
"status_code": "error"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
diff --git a/API/Routes/DataFile/DataFileRoute.py b/API/Routes/DataFile/DataFileRoute.py
index 33709c09..bbf6342a 100644
--- a/API/Routes/DataFile/DataFileRoute.py
+++ b/API/Routes/DataFile/DataFileRoute.py
@@ -3,6 +3,7 @@
import shutil, datetime, time, os
from Classes.Case.DataFileClass import DataFile
from Classes.Base import Config
+from Classes.Base.Response import api_response
datafile_api = Blueprint('DataFileRoute', __name__)
@@ -19,9 +20,9 @@ def generateDataFile():
"message": "You have created data file!",
"status_code": "success"
}
- return jsonify(response), 200
+ return api_response(success=True, message=response["message"], data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/createCaseRun", methods=['POST'])
def createCaseRun():
@@ -34,9 +35,9 @@ def createCaseRun():
caserun = DataFile(casename)
response = caserun.createCaseRun(caserunname, data)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/updateCaseRun", methods=['POST'])
def updateCaseRun():
@@ -50,9 +51,9 @@ def updateCaseRun():
caserun = DataFile(casename)
response = caserun.updateCaseRun(caserunname, oldcaserunname, data)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/deleteCaseRun", methods=['POST'])
def deleteCaseRun():
@@ -75,24 +76,11 @@ def deleteCaseRun():
if casename != None:
caserun = DataFile(casename)
response = caserun.deleteCaseRun(caserunname, resultsOnly)
- return jsonify(response), 200
-
- # if casename == session.get('osycase'):
- # session['osycase'] = None
- # response = {
- # "message": 'Case '+ casename + ' deleted!',
- # "status_code": "success_session"
- # }
- # else:
- # response = {
- # "message": 'Case '+ casename + ' deleted!',
- # "status_code": "success"
- # }
- # return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error deleting case run", status_code=500)
@datafile_api.route("/deleteScenarioCaseRuns", methods=['POST'])
def deleteScenarioCaseRuns():
@@ -104,9 +92,9 @@ def deleteScenarioCaseRuns():
caserun = DataFile(casename)
response = caserun.deleteScenarioCaseRuns(scenarioId)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/saveView", methods=['POST'])
def saveView():
@@ -119,9 +107,9 @@ def saveView():
caserun = DataFile(casename)
response = caserun.saveView(data, param)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/updateViews", methods=['POST'])
def updateViews():
@@ -134,9 +122,9 @@ def updateViews():
caserun = DataFile(casename)
response = caserun.updateViews(data, param)
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/readDataFile", methods=['POST'])
def readDataFile():
@@ -149,9 +137,9 @@ def readDataFile():
response = data
else:
response = None
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/validateInputs", methods=['POST'])
def validateInputs():
@@ -164,9 +152,9 @@ def validateInputs():
response = validation
else:
response = None
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/downloadDataFile", methods=['GET'])
def downloadDataFile():
@@ -187,7 +175,7 @@ def downloadDataFile():
return send_file(dataFile.resolve(), as_attachment=True, max_age=0)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/downloadFile", methods=['GET'])
def downloadFile():
@@ -198,7 +186,7 @@ def downloadFile():
return send_file(dataFile.resolve(), as_attachment=True, max_age=0)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/downloadCSVFile", methods=['GET'])
def downloadCSVFile():
@@ -210,7 +198,7 @@ def downloadCSVFile():
return send_file(dataFile.resolve(), as_attachment=True, max_age=0)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/downloadResultsFile", methods=['GET'])
def downloadResultsFile():
@@ -221,7 +209,7 @@ def downloadResultsFile():
return send_file(dataFile.resolve(), as_attachment=True, max_age=0)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/run", methods=['POST'])
def run():
@@ -231,13 +219,10 @@ def run():
solver = request.json['solver']
txtFile = DataFile(casename)
response = txtFile.run(solver, caserunname)
- return jsonify(response), 200
- # except Exception as ex:
- # print(ex)
- # return ex, 404
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
@datafile_api.route("/batchRun", methods=['POST'])
def batchRun():
@@ -254,9 +239,9 @@ def batchRun():
response = txtFile.batchRun( 'CBC', cases)
end = time.time()
response['time'] = end-start
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('Error!'), 404
+ return api_response(success=False, message="Error during batch run", status_code=500)
@datafile_api.route("/cleanUp", methods=['POST'])
def cleanUp():
@@ -267,6 +252,6 @@ def cleanUp():
model = DataFile(modelname)
response = model.cleanUp()
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- return jsonify('Error!'), 404
\ No newline at end of file
+ return api_response(success=False, message="Error during cleanup", status_code=500)
\ No newline at end of file
diff --git a/API/Routes/Upload/UploadRoute.py b/API/Routes/Upload/UploadRoute.py
index 88dde7d6..4cf344ac 100644
--- a/API/Routes/Upload/UploadRoute.py
+++ b/API/Routes/Upload/UploadRoute.py
@@ -1,5 +1,6 @@
import shutil
from flask import Blueprint, request, jsonify, send_file, after_this_request
+from Classes.Base.Response import api_response
from zipfile import ZipFile
from pathlib import Path
from werkzeug.utils import secure_filename
@@ -199,7 +200,7 @@ def run(self):
def myfunc():
thread_a = Download(request.__copy__())
thread_a.start()
- return "Processing in background", 200
+ return api_response(success=True, message="Processing in background", status_code=200)
@upload_api.route("/backupCase", methods=['GET'])
def backupCase():
@@ -239,9 +240,9 @@ def backupCase():
return send_file(zippedFile.resolve(), as_attachment=True)
except(IOError):
- return jsonify('No existing cases!'), 404
+ return api_response(success=False, message="No existing cases!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during backup", status_code=500)
@upload_api.route('/uploadCaseUnchunked_old', methods=['POST'])
def uploadCaseUnchunked_old():
@@ -401,11 +402,11 @@ def uploadCaseUnchunked_old():
"response" :msg
}
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- raise IOError
+ return api_response(success=False, message="Error saving model IOError!", status_code=404)
except OSError:
- raise OSError
+ return api_response(success=False, message="OS Error during upload", status_code=500)
def handle_full_zip(file, filepath=None):
msg = []
@@ -439,7 +440,7 @@ def handle_full_zip(file, filepath=None):
"message": f"ZIP archive {case} is not valid archive!",
"status_code": "error"
})
- return jsonify({"response": msg}), 200
+ return api_response(success=False, message=f"ZIP archive {case} is not valid archive!", data={"response": msg}, status_code=200)
#for zippedfile in zf.namelist():
@@ -538,7 +539,7 @@ def handle_full_zip(file, filepath=None):
os.remove(filepath)
- return jsonify({"response": msg}), 200
+ return api_response(success=True, data={"response": msg}, status_code=200)
@upload_api.route('/uploadCase', methods=['POST'])
def uploadCase():
@@ -577,7 +578,7 @@ def uploadCase():
chunks_received = len(os.listdir(chunk_dir))
if chunks_received < dz_total_chunks:
- return jsonify({"status": f"received {chunks_received}/{dz_total_chunks}"}), 200
+ return api_response(success=True, message=f"received {chunks_received}/{dz_total_chunks}", data={"status": f"received {chunks_received}/{dz_total_chunks}"}, status_code=200)
# -------------------------------
# 4) Spajanje ZIP fajla
@@ -605,7 +606,7 @@ def uploadCase():
return handle_full_zip(None, final_zip)
except Exception as e:
- return jsonify({"error": str(e)}), 500
+ return api_response(success=False, message=str(e), status_code=500)
@upload_api.route('/uploadXls', methods=['POST'])
def uploadXls():
@@ -648,8 +649,8 @@ def uploadXls():
"response" :msg
}
- return jsonify(response), 200
+ return api_response(success=True, data=response, status_code=200)
except(IOError):
- raise IOError
+ return api_response(success=False, message="Error saving XLS IOError!", status_code=404)
except OSError:
- raise OSError
\ No newline at end of file
+ return api_response(success=False, message="OS Error during XLS upload", status_code=500)
\ No newline at end of file
diff --git a/API/app.py b/API/app.py
index f2fc8c47..5cb9c814 100644
--- a/API/app.py
+++ b/API/app.py
@@ -9,6 +9,7 @@
#import json
from Classes.Base import Config
+from Classes.Base.Response import api_response
# from API.Classes.Base.SyncS3 import SyncS3
from Routes.Upload.UploadRoute import upload_api
from Routes.Case.CaseRoute import case_api
@@ -58,7 +59,7 @@
def add_headers(response):
if Config.HEROKU_DEPLOY == 0:
#localhost
- response.headers.add('Access-Control-Allow-Origin', 'http://127.0.0.1')
+ response.headers.add('Access-Control-Allow-Origin', '*')
else:
#HEROKU
response.headers.add('Access-Control-Allow-Origin', 'https://osemosys.herokuapp.com/')
@@ -67,6 +68,34 @@ def add_headers(response):
#response.headers['Content-Type'] = 'application/javascript'
return response
+@app.errorhandler(404)
+def not_found_error(error):
+ # If the request expects JSON or is an API route, return JSON 404
+ if request.path.startswith('/api/') or request.accept_mimetypes.accept_json and not request.accept_mimetypes.accept_html:
+ return api_response(
+ success=False,
+ message="Resource not found",
+ status_code=404
+ )
+
+ # Otherwise fallback to the frontend (React/SPA) router
+ return render_template('index.html'), 404
+
+@app.errorhandler(500)
+@app.errorhandler(Exception)
+def internal_error(error):
+ # Depending on the exception, error.code might not exist
+ status_code = getattr(error, 'code', 500)
+ if status_code == 404:
+ return not_found_error(error)
+
+ return api_response(
+ success=False,
+ message="Internal server error",
+ data={"details": str(error)},
+ status_code=status_code
+ )
+
# @app.errorhandler(CustomException)
# def handle_invalid_usage(error):
# response = jsonify(error.to_dict())
@@ -86,6 +115,15 @@ def home():
# syncS3.downloadSync('Parameters.json', Config.DATA_STORAGE, Config.S3_BUCKET)
return render_template('index.html')
+@app.route("/health", methods=['GET'])
+def health_check():
+ """Simple backend health check endpoint."""
+ return jsonify({"status": "ok"}), 200
+
+@app.route("/mock-error", methods=['GET'])
+def mock_error():
+ """Endpoint to trigger a 500 Internal Server Error for testing."""
+ raise Exception("This is a mock error for testing 500 handler")
@app.route("/getSession", methods=['GET'])
def getSession():
@@ -124,7 +162,8 @@ def setSession():
#waitress server
#prod server
from waitress import serve
- serve(app, host='127.0.0.1', port=port)
+ print(f"Starting server on http://0.0.0.0:{port}")
+ serve(app, host='0.0.0.0', port=port)
else:
#HEROKU
app.run(host='0.0.0.0', port=port, debug=True)
diff --git a/requirements.txt b/requirements.txt
index f1a065dd..bad26ef8 100644
Binary files a/requirements.txt and b/requirements.txt differ