From 9f7568540cb642acd6d48ddea8eb61cb481b3c3a Mon Sep 17 00:00:00 2001 From: Pavel Umansky Date: Wed, 8 Apr 2026 17:17:20 +0200 Subject: [PATCH] main commit --- backend/Dockerfile | 6 + backend/app.py | 231 +++++++++++++++++++++++++++++++++++++++ backend/db/Dockerfile | 1 + backend/requirements.txt | 5 + docker-compose.yml | 18 +++ frontend/index.html | 122 +++++++++++++++++++++ nginx/Dockerfile | 2 + nginx/nginx.conf | 25 +++++ 8 files changed, 410 insertions(+) create mode 100644 backend/Dockerfile create mode 100644 backend/app.py create mode 100644 backend/db/Dockerfile create mode 100644 backend/requirements.txt create mode 100644 docker-compose.yml create mode 100644 frontend/index.html create mode 100644 nginx/Dockerfile create mode 100644 nginx/nginx.conf diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..215030a --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.12 +WORKDIR /usr/src/app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"] \ No newline at end of file diff --git a/backend/app.py b/backend/app.py new file mode 100644 index 0000000..59f5f5e --- /dev/null +++ b/backend/app.py @@ -0,0 +1,231 @@ +from flask import Flask, jsonify, request +from flask_restful import Api, Resource +from pymongo import MongoClient +import bcrypt + +app = Flask(__name__) +api = Api(app) +client = MongoClient("mongodb://db:27017") +db = client.BankAPI +users = db["Users"] + +def UserExist(username): + if users.find_one({"Username": username}) is not None: + return True + return False + +class Register(Resource): + def post(self): + postedData = request.get_json() + username = postedData["username"] + password = postedData["password"] + + if UserExist(username): + retJson = { + "status": 301, + "msg": "Invalid username" + } + return jsonify(retJson) + hashed_pw = bcrypt.hashpw(password.encode('utf8'), bcrypt.gensalt()) + + users.insert_one({ + "Username": username, + "Password": hashed_pw, + "Own": 0, + "Debt": 0 + }) + retJson = { + "status": 200, + "msg": "You successfully signed up for the API" + } + return jsonify(retJson) + +def verifyPw(username, password): + if not UserExist(username): + return False + hashed_pw = users.find_one({ + "Username": username + })["Password"] + if bcrypt.hashpw(password.encode('utf8'), hashed_pw) == hashed_pw: + return True + else: + return False + +def cashWithUser(username): + cash = users.find_one({"Username": username})["Own"] + return cash + +def debtWithUser(username): + debt = users.find_one({"Username": username})["Debt"] + return debt + +def generateReturnDictionary(status,msg): + retJson = { + "status": status, + "msg": msg + } + return retJson + +def verifyCredentials(username, password): + if not UserExist(username): + return generateReturnDictionary(301, "Invalid username"), True + + correct_pw = verifyPw(username, password) + if not correct_pw: + return generateReturnDictionary(302, "Invalid password"), True + return None, False + +def updateAccount(username, balance): + users.update_one({ + "Username": username + }, { + "$set": { + "Own": balance + } + }) + +def updateDebt(username, balance): + users.update_one({ + "Username": username + }, { + "$set": { + "Debt": balance + } + }) + + +class Add(Resource): + def post(self): + postedData = request.get_json() + username = postedData["username"] + + password = postedData["password"] + amount = postedData["amount"] + + retJson, error = verifyCredentials(username, password) + if error: + return jsonify(retJson) + + if amount <= 0: + return jsonify(generateReturnDictionary(304, "The money amount entered must be positive!")) + + cash = cashWithUser(username) + + amount -=1 + bank_cash = cashWithUser("BANK") + updateAccount("BANK", bank_cash + 1) + updateAccount(username, cash + amount) + + return jsonify(generateReturnDictionary(200, "Amount added successfully to account")) + + + +class Transfer(Resource): + def post(self): + postedData = request.get_json() + username = postedData["username"] + password = postedData["password"] + to = postedData["to"] + money = postedData["amount"] + + retJson, error = verifyCredentials(username, password) + if error: + return jsonify(retJson) + + cash = cashWithUser(username) + if cash <= 0: + return jsonify(generateReturnDictionary(304, "You`re out of money, please add or take a loan")) + + if not UserExist(to): + return jsonify(generateReturnDictionary(301, "Receiver username is invalid")) + + cash_from = cashWithUser(username) + cash_to = cashWithUser(to) + bank_cash = cashWithUser("BANK") + updateAccount("BANK", bank_cash + 1) + updateAccount(to, cash_to + money - 1) + updateAccount(username, cash_from - money) + + return jsonify(generateReturnDictionary(200, "Amount transferred successfully")) + +class Balance(Resource): + def post(self): + postedData = request.get_json() + username = postedData["username"] + password = postedData["password"] + retJson, error = verifyCredentials(username, password) + if error: + return jsonify(retJson) + + retJson = users.find_one({ + "Username": username + }, { + "Password": 0, + "_id": 0 + }) + + return jsonify(retJson) + +class TakeLoan(Resource): + def post(self): + postedData = request.get_json() + + username = postedData["username"] + password = postedData["password"] + money = postedData["amount"] + + retJson, error = verifyCredentials(username, password) + + if error: + return jsonify(retJson) + cash = cashWithUser(username) + debt = debtWithUser(username) + updateAccount(username, cash + money) + updateDebt(username, debt + money) + return jsonify(generateReturnDictionary(200, "Loan added successfully!")) + + +class PayLoan(Resource): + def post(self): + postedData = request.get_json() + + username = postedData["username"] + password = postedData["password"] + money = postedData["amount"] + + retJson, error = verifyCredentials(username, password) + if error: + return jsonify(retJson) + + cash = cashWithUser(username) + if cash < money: + return jsonify(generateReturnDictionary(303, "You don`t have enough money to pay the loan!")) + + debt = debtWithUser(username) + updateAccount(username, cash - money) + updateDebt(username, debt - money) + + return jsonify(generateReturnDictionary(200, "Loan paid successfully!")) + + +api.add_resource(Register, "/register") +api.add_resource(Add, "/add") +api.add_resource(Transfer, "/transfer") +api.add_resource(Balance, "/balance") +api.add_resource(TakeLoan, "/take_loan") +api.add_resource(PayLoan, "/pay_loan") + +def init_bank_account(): + if not UserExist("BANK"): + users.insert_one({ + "Username": "BANK", + "Password": bcrypt.hashpw("admin".encode('utf8'), bcrypt.gensalt()), + "Own": 0, + "Debt": 0 + }) + +init_bank_account() + +if __name__ == "__main__": + app.run(host='0.0.0.0', debug=True) + \ No newline at end of file diff --git a/backend/db/Dockerfile b/backend/db/Dockerfile new file mode 100644 index 0000000..ab771db --- /dev/null +++ b/backend/db/Dockerfile @@ -0,0 +1 @@ +FROM mongo:latest \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..e729913 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,5 @@ +Flask +flask_restful +pymongo +bcrypt +gunicorn \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c5015b8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + web: + build: + context: . + dockerfile: nginx/Dockerfile + ports: + - "80:80" + volumes: + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + command: [nginx-debug, '-g', 'daemon off;'] + db: + build: + context: ./backend/db + ports: + - "27017:27017" + backend: + build: + context: ./backend diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..504dea9 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,122 @@ + + + + + + + Bank API + + + + +

Bank API

+ +

Register

+ + + +
+ +

Add Money

+ + + + +
+ +

Transfer

+ + + + + +
+ +

Balance

+ + + +
+ +

Take Loan

+ + + + +
+ +

Pay Loan

+ + + + +
+ + + + + \ No newline at end of file diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..9e9b8c8 --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,2 @@ +FROM nginx:latest +COPY ./frontend/index.html /var/www/html/index.html diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..42df12e --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,25 @@ +events { + worker_connections 1024; +} +http { + upstream backend { + server backend:5000; +} +server { + listen 80 default_server; + listen [::]:80; + server_name backend; + + location /api/ { + proxy_pass http://backend/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location / { + root /var/www/html; + index index.html; + } +} +} \ No newline at end of file