diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..808386f --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,2 @@ +.venv/ +__pycache__/ \ No newline at end of file diff --git a/backend/.pylintrc b/backend/.pylintrc new file mode 100644 index 0000000..b12f0a2 --- /dev/null +++ b/backend/.pylintrc @@ -0,0 +1,11 @@ +[MAIN] + +disable= + too-many-instance-attributes, + too-few-public-methods, + too-many-arguments, + missing-module-docstring, + missing-function-docstring, + missing-class-docstring, + too-few-public-methods, + line-too-long, diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..28e7a3a --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,4 @@ +flask~=2.3.3 +flask-smorest~=0.42.1 +marshmallow~=3.20.1 +gevent~=23.9.1 \ No newline at end of file diff --git a/backend/src/app.py b/backend/src/app.py new file mode 100644 index 0000000..719dc90 --- /dev/null +++ b/backend/src/app.py @@ -0,0 +1,34 @@ +# pylint: disable=too-few-public-methods + +from flask import Flask +from flask_smorest import Api +from config.api_settings import DefaultSettings + +class IsSingletonException(Exception): + pass + +class Application: + """ + This is a singleton that can be accessed using get_instance() + + It has 2 properties + + - app: Used for WSGI servers and such + - api: Used for Blueprints + """ + __instance = None + app = Flask(__name__) + app.config.from_object(DefaultSettings) + api = None + + @staticmethod + def get_instance(): + if Application.__instance is None: + Application() + return Application.__instance + + def __init__(self): + if Application.__instance is not None: + raise IsSingletonException("This class is a singleton") + Application.__instance = self + self.api = Api(self.app) diff --git a/backend/src/config/api_settings.py b/backend/src/config/api_settings.py new file mode 100644 index 0000000..4109e29 --- /dev/null +++ b/backend/src/config/api_settings.py @@ -0,0 +1,28 @@ +# pylint: disable=too-few-public-methods +import os +class DefaultSettings: + API_TITLE = "Support Organizer" + API_VERSION = 0.1 + OPENAPI_VERSION = "3.1.0" + + # openapi.json settings + OPENAPI_URL_PREFIX = "/" + OPENAPI_JSON_PATH = "openapi.json" + + # swagger settings + OPENAPI_SWAGGER_UI_PATH = "/swagger" + OPENAPI_SWAGGER_UI_URL = "https://cdn.jsdelivr.net/npm/swagger-ui-dist/" + SWAGGER_UI_DOC_EXPANSION = "list" + + # redoc settings + OPENAPI_REDOC_PATH = "/redoc" + OPENAPI_REDOC_URL = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js" + + # Info settings + API_SPEC_OPTIONS = { + 'info': { + 'description': 'Support Organizer for FGO' + } + } + + FILE_SAVE_DIRECTORY = f"{os.getcwd()}/temp" diff --git a/backend/src/config/config.ini b/backend/src/config/config.ini new file mode 100644 index 0000000..56ca039 --- /dev/null +++ b/backend/src/config/config.ini @@ -0,0 +1,2 @@ +[server] +port=5000 \ No newline at end of file diff --git a/backend/src/routes/__init__.py b/backend/src/routes/__init__.py new file mode 100644 index 0000000..b3dc1ab --- /dev/null +++ b/backend/src/routes/__init__.py @@ -0,0 +1,6 @@ +# pylint: disable=wrong-import-position,cyclic-import +from flask_smorest import Blueprint + +routes = Blueprint("support-organizer", "support-organizer", url_prefix="/", description="") + +from . import apiversion # avoids circular imports problem diff --git a/backend/src/routes/apiversion.py b/backend/src/routes/apiversion.py new file mode 100644 index 0000000..37a4b9a --- /dev/null +++ b/backend/src/routes/apiversion.py @@ -0,0 +1,15 @@ +from flask.views import MethodView +import marshmallow as ma + +from config.api_settings import DefaultSettings +from . import routes as blp + +class ApiVersionGet(ma.Schema): + version = ma.fields.String(example="0.1") + +@blp.route("/version") +class ApiVersion(MethodView): + @blp.doc(summary="Get the REST interface version identification.") + @blp.response(200, ApiVersionGet, description="Successful operation") + def get(self): + return { "version": DefaultSettings.API_VERSION } diff --git a/backend/src/server.py b/backend/src/server.py new file mode 100644 index 0000000..6cfaf1f --- /dev/null +++ b/backend/src/server.py @@ -0,0 +1,26 @@ +# pylint: disable=multiple-statements,wrong-import-position,wrong-import-order +from gevent.monkey import patch_all; patch_all() +from gevent.pywsgi import WSGIServer +import configparser + +from app import Application +from routes import routes + +instance = Application.get_instance() +app = instance.app +api = instance.api +api.register_blueprint(routes) + +config = configparser.ConfigParser() +config.read('config/config.ini') + +port = int(config['server']['port']) or 5000 + +if __name__ == '__main__': + http_Server = WSGIServer(("127.0.0.1", port), app) + try: + print(f"Server available on http://127.0.0.1:{port}/") + print(f"View docs on http://127.0.0.1:{port}/swagger") + http_Server.serve_forever() + except KeyboardInterrupt: + print("Keyboard interrupt received, stopping...")