From eb9f56933e39d8bb6d885d56a3a3eed91e2bd30f Mon Sep 17 00:00:00 2001 From: abhayrajjais01 Date: Sat, 21 Feb 2026 02:01:41 +0530 Subject: [PATCH 1/3] feat: Add /health endpoint for backend status (Fixes #29) --- API/app.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/API/app.py b/API/app.py index f2fc8c47..9f31e6f5 100644 --- a/API/app.py +++ b/API/app.py @@ -67,6 +67,32 @@ 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 jsonify({ + "status": "error", + "message": "Resource not found" + }), 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 jsonify({ + "status": "error", + "message": "Internal server error", + "details": str(error) + }), status_code + # @app.errorhandler(CustomException) # def handle_invalid_usage(error): # response = jsonify(error.to_dict()) @@ -86,6 +112,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(): From 964ff99d36f6c032f1b2e2303d216472355147ab Mon Sep 17 00:00:00 2001 From: linisha Date: Sat, 21 Feb 2026 04:24:38 +0530 Subject: [PATCH 2/3] Standardize API Response Format --- .vscode/launch.json | 13 ++++- API/Classes/Base/Response.py | 23 ++++++++ API/Routes/Case/CaseRoute.py | 83 ++++++++++++++------------- API/Routes/Case/SyncS3Route.py | 31 +++++----- API/Routes/Case/ViewDataRoute.py | 17 +++--- API/Routes/DataFile/DataFileRoute.py | 75 ++++++++++-------------- API/Routes/Upload/UploadRoute.py | 27 ++++----- API/app.py | 5 +- requirements.txt | Bin 1168 -> 710 bytes 9 files changed, 147 insertions(+), 127 deletions(-) create mode 100644 API/Classes/Base/Response.py 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..e2dfb1bd 100644 --- a/API/app.py +++ b/API/app.py @@ -58,7 +58,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/') @@ -124,7 +124,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 f1a065dda86d2a8fb87b4820198e4a23890f7c03..bad26ef84ecbfddd86581bc608aed4e8b459a57c 100644 GIT binary patch delta 42 wcmbQhd5m>~8RKLfW*0`2$@$DX84V_@vt&+gV3Oi9W6)zT24VvSUIs1(0P+wB<^TWy delta 455 zcmZvY!AiqW5JhhaE>v8)a^cd2RFbDnanZFXxbh3C#x|`@LK8tj=r0Ia`4=wz0>8^M z(^$HY$Ai4wGk5OH&(6p0R~oHyHLCTfu?kI;X{k~#{26hA&iuwY(4KCT!^|LqtLLZ* z_rleg7F5@&&>KyiFg13{>Tgk&n&V#Sj_)V<(W+}>=)jE%fCb3g#v&-)v?) Date: Sun, 22 Feb 2026 01:28:45 +0530 Subject: [PATCH 3/3] refactor: use api_response to standardize error handlers --- API/app.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/API/app.py b/API/app.py index 41817f7b..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 @@ -71,10 +72,11 @@ def add_headers(response): 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 jsonify({ - "status": "error", - "message": "Resource not found" - }), 404 + 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 @@ -87,11 +89,12 @@ def internal_error(error): if status_code == 404: return not_found_error(error) - return jsonify({ - "status": "error", - "message": "Internal server error", - "details": str(error) - }), status_code + 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):