main commit

This commit is contained in:
Pavel Umansky 2026-04-08 17:17:20 +02:00
parent 5b7b51439c
commit 9f7568540c
8 changed files with 410 additions and 0 deletions

6
backend/Dockerfile Normal file
View File

@ -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"]

231
backend/app.py Normal file
View File

@ -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)

1
backend/db/Dockerfile Normal file
View File

@ -0,0 +1 @@
FROM mongo:latest

5
backend/requirements.txt Normal file
View File

@ -0,0 +1,5 @@
Flask
flask_restful
pymongo
bcrypt
gunicorn

18
docker-compose.yml Normal file
View File

@ -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

122
frontend/index.html Normal file
View File

@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bank API</title>
<style>
body {
font-family: monospace;
max-width: 600px;
margin: 40px auto;
padding: 0 20px;
}
h1 {
font-size: 18px;
}
h2 {
font-size: 14px;
margin-top: 24px;
border-bottom: 1px solid #ccc;
padding-bottom: 4px;
}
input,
button {
font-family: monospace;
font-size: 13px;
padding: 4px 8px;
margin: 2px 0;
}
button {
cursor: pointer;
}
.result {
background: #f0f0f0;
padding: 8px;
margin-top: 8px;
white-space: pre-wrap;
font-size: 13px;
min-height: 20px;
}
</style>
</head>
<body>
<h1>Bank API</h1>
<h2>Register</h2>
<input id="reg-user" placeholder="username">
<input id="reg-pass" placeholder="password" type="password">
<button
onclick="api('/api/register', {username: v('reg-user'), password: v('reg-pass')}, 'reg-res')">Register</button>
<div class="result" id="reg-res"></div>
<h2>Add Money</h2>
<input id="add-user" placeholder="username">
<input id="add-pass" placeholder="password" type="password">
<input id="add-amount" placeholder="amount" type="number">
<button
onclick="api('/api/add', {username: v('add-user'), password: v('add-pass'), amount: n('add-amount')}, 'add-res')">Add</button>
<div class="result" id="add-res"></div>
<h2>Transfer</h2>
<input id="tr-user" placeholder="username">
<input id="tr-pass" placeholder="password" type="password">
<input id="tr-to" placeholder="to">
<input id="tr-amount" placeholder="amount" type="number">
<button
onclick="api('/api/transfer', {username: v('tr-user'), password: v('tr-pass'), to: v('tr-to'), amount: n('tr-amount')}, 'tr-res')">Transfer</button>
<div class="result" id="tr-res"></div>
<h2>Balance</h2>
<input id="bal-user" placeholder="username">
<input id="bal-pass" placeholder="password" type="password">
<button onclick="api('/api/balance', {username: v('bal-user'), password: v('bal-pass')}, 'bal-res')">Check</button>
<div class="result" id="bal-res"></div>
<h2>Take Loan</h2>
<input id="loan-user" placeholder="username">
<input id="loan-pass" placeholder="password" type="password">
<input id="loan-amount" placeholder="amount" type="number">
<button
onclick="api('/api/take_loan', {username: v('loan-user'), password: v('loan-pass'), amount: n('loan-amount')}, 'loan-res')">Take
Loan</button>
<div class="result" id="loan-res"></div>
<h2>Pay Loan</h2>
<input id="pay-user" placeholder="username">
<input id="pay-pass" placeholder="password" type="password">
<input id="pay-amount" placeholder="amount" type="number">
<button
onclick="api('/api/pay_loan', {username: v('pay-user'), password: v('pay-pass'), amount: n('pay-amount')}, 'pay-res')">Pay
Loan</button>
<div class="result" id="pay-res"></div>
<script>
function v(id) { return document.getElementById(id).value; }
function n(id) { return Number(document.getElementById(id).value); }
async function api(url, body, resId) {
const el = document.getElementById(resId);
el.textContent = 'Loading...';
try {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
const data = await res.json();
el.textContent = JSON.stringify(data, null, 2);
} catch (e) {
el.textContent = 'Error: ' + e.message;
}
}
</script>
</body>
</html>

2
nginx/Dockerfile Normal file
View File

@ -0,0 +1,2 @@
FROM nginx:latest
COPY ./frontend/index.html /var/www/html/index.html

25
nginx/nginx.conf Normal file
View File

@ -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;
}
}
}