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