main commit
This commit is contained in:
parent
5b7b51439c
commit
9f7568540c
6
backend/Dockerfile
Normal file
6
backend/Dockerfile
Normal 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
231
backend/app.py
Normal 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
1
backend/db/Dockerfile
Normal file
@ -0,0 +1 @@
|
||||
FROM mongo:latest
|
||||
5
backend/requirements.txt
Normal file
5
backend/requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Flask
|
||||
flask_restful
|
||||
pymongo
|
||||
bcrypt
|
||||
gunicorn
|
||||
18
docker-compose.yml
Normal file
18
docker-compose.yml
Normal 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
122
frontend/index.html
Normal 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
2
nginx/Dockerfile
Normal file
@ -0,0 +1,2 @@
|
||||
FROM nginx:latest
|
||||
COPY ./frontend/index.html /var/www/html/index.html
|
||||
25
nginx/nginx.conf
Normal file
25
nginx/nginx.conf
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user