Skip to content

Commit

Permalink
base structure added
Browse files Browse the repository at this point in the history
Co-authored-by: Усачева Дарья <[email protected]>
  • Loading branch information
VLADISLAV.BOBKOV and Dariiiii committed Nov 10, 2024
1 parent e0255ed commit b7921b7
Show file tree
Hide file tree
Showing 27 changed files with 601 additions and 0 deletions.
5 changes: 5 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt

Check failure on line 4 in backend/Dockerfile

View workflow job for this annotation

GitHub Actions / Проверка наличия тега 0.8 и работоспособности docker-compose

DL3042 warning: Avoid use of cache directory with pip. Use `pip install --no-cache-dir <package>`
CMD ["python", "-u", "app/__init__.py"]
16 changes: 16 additions & 0 deletions backend/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from flask import Flask
from mongoengine import connect
from routes import auth_routes_bp
from routes import request_routes_bp

app = Flask(__name__)

# Подключение к MongoDB
connect('database_name')

# Регистрация маршрутов
app.register_blueprint(auth_routes_bp)
app.register_blueprint(request_routes_bp)

if __name__ == '__main__':
app.run(debug=True)
5 changes: 5 additions & 0 deletions backend/app/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os

class Config:
SECRET_KEY = os.getenv("SECRET_KEY", "supersecret")
MONGO_URI = os.getenv("MONGO_URI", "mongodb://mongo:27017/mydatabase")
70 changes: 70 additions & 0 deletions backend/app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from mongoengine import (
Document, EmbeddedDocument, StringField, EmailField, DateField,
FloatField, IntField, BooleanField, ListField, EmbeddedDocumentField, ObjectIdField
)
from datetime import datetime

class CreditHistory(EmbeddedDocument):
_id = ObjectIdField(required=True)
loan_id = ObjectIdField(required=True)
status = StringField(choices=["opened", "expired", "closed"], required=True)
closing_date = DateField()

class Client(Document):
_id = ObjectIdField(required=True, primary_key=True)
name = StringField(required=True)
email = EmailField(required=True)
workplace = StringField()
sex = StringField()
password = StringField(required=True)
phone = StringField(max_length=10)
age = IntField()
birthdate = DateField()
salary = FloatField()
self_employment_status = StringField(choices=["self-employed", "entrepreneur", "idle"])
owned_property = ListField(StringField(max_length=10))
marital_status = StringField(choices=["married", "single"])
spouse_workplace = StringField()
spouse_salary = FloatField()
amount_of_children = IntField()
rating = FloatField()
credit_history = ListField(EmbeddedDocumentField(CreditHistory))

class InteractionHistory(EmbeddedDocument):
_id = ObjectIdField(required=True)
credit_request_id = ObjectIdField(required=True)
processing_date = DateField(default=datetime.utcnow)
decision = BooleanField()

class Admin(Document):
_id = ObjectIdField(required=True, primary_key=True)
name = StringField(required=True)
email = EmailField(required=True)
sex = StringField()
password = StringField(required=True)
age = IntField()
phone = StringField(max_length=10)
post = StringField()
birthdate = DateField()
interaction_history = ListField(EmbeddedDocumentField(InteractionHistory))

class Credit(Document):
_id = ObjectIdField(required=True, primary_key=True)
loan_name = StringField(required=True)
opening_date = DateField(default=datetime.utcnow)
expiration_time = IntField()
amount = FloatField()
interest_rate = FloatField()
monthly_payment = FloatField()
next_payment_date = DateField()
debt = FloatField()
payments_overdue = IntField()
co_borrowers = ListField(StringField(max_length=10))
deposit = FloatField()

class CreditRequest(Document):
_id = ObjectIdField(required=True, primary_key=True)
client_id = ObjectIdField(required=True)
loan_id = ObjectIdField(required=True)
request_time = DateField(default=datetime.utcnow)
status = StringField(choices=["processing", "approved", "rejected"], required=True)
5 changes: 5 additions & 0 deletions backend/app/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .auth_routes import auth_bp
from .request_routes import credit_request_bp

# Создание списка всех маршрутов для удобного подключения к основному приложению
__all__ = ["auth_bp", "request_bp"]
28 changes: 28 additions & 0 deletions backend/app/routes/auth_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from flask import Blueprint, request, jsonify
from models import Client, Admin
from werkzeug.security import check_password_hash
import mongoengine

bp = Blueprint('auth_routes', __name__)

@bp.route('/login/client', methods=['POST'])
def client_login():
data = request.get_json()
email = data.get('email')
password = data.get('password')

client = Client.objects(email=email).first()
if client and check_password_hash(client.password, password):
return jsonify({"message": "Client logged in successfully", "client_id": str(client.id)}), 200
return jsonify({"error": "Invalid email or password"}), 401

@bp.route('/login/admin', methods=['POST'])
def admin_login():
data = request.get_json()
email = data.get('email')
password = data.get('password')

admin = Admin.objects(email=email).first()
if admin and check_password_hash(admin.password, password):
return jsonify({"message": "Admin logged in successfully", "admin_id": str(admin.id)}), 200
return jsonify({"error": "Invalid email or password"}), 401
33 changes: 33 additions & 0 deletions backend/app/routes/request_routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from flask import Blueprint, request, jsonify
from models import CreditRequest
from mongoengine import ObjectIdField
from datetime import datetime

bp = Blueprint('credit_request_routes', __name__)

@bp.route('/credit_request', methods=['POST'])
def create_credit_request():
data = request.get_json()
client_id = data.get('client_id')
loan_id = data.get('loan_id')

if not client_id or not loan_id:
return jsonify({"error": "client_id and loan_id are required"}), 400

credit_request = CreditRequest(
client_id=client_id,
loan_id=loan_id,
request_time=datetime.utcnow(),
status="processing"
)
credit_request.save()
return jsonify({"message": "Credit request created successfully", "request_id": str(credit_request.id)}), 201

@bp.route('/credit_requests', methods=['GET'])
def get_all_credit_requests():
credit_requests = CreditRequest.objects()
result = [{"client_id": str(req.client_id),
"loan_id": str(req.loan_id),
"request_time": req.request_time,
"status": req.status} for req in credit_requests]
return jsonify(result), 200
44 changes: 44 additions & 0 deletions backend/app/utils/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
from datetime import datetime, timedelta
from flask import current_app, request
from functools import wraps

# Хеширование пароля
def hash_password(password):
return generate_password_hash(password)

# Проверка пароля
def verify_password(password, hashed_password):
return check_password_hash(hashed_password, password)

# Генерация JWT токена
def generate_token(user_id, expires_in=600):
payload = {
"user_id": user_id,
"exp": datetime.utcnow() + timedelta(seconds=expires_in)
}
return jwt.encode(payload, current_app.config["SECRET_KEY"], algorithm="HS256")

# Проверка JWT токена
def verify_token(token):
try:
payload = jwt.decode(token, current_app.config["SECRET_KEY"], algorithms=["HS256"])
return payload["user_id"]
except jwt.ExpiredSignatureError:
return None # Токен истек
except jwt.InvalidTokenError:
return None # Невалидный токен

# Декоратор для защиты маршрутов
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
token = request.headers.get("Authorization")
if not token:
return {"message": "Токен не найден"}, 401
user_id = verify_token(token)
if not user_id:
return {"message": "Неверный или истекший токен"}, 401
return f(user_id=user_id, *args, **kwargs)
return decorated_function
File renamed without changes.
9 changes: 9 additions & 0 deletions dev.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ENVIRONMENT="dev"
SENTRY_DSN=""
DATABASE_DSN="postgresql://postgres:postgres@postgres/postgres"
STORAGE_DIR="/app/storage/"
AUTO_RELOAD="true"
BACKEND_BASE_URL=""
APP_NAME="app"
QT_DEBUG_PLUGINS="1"
DISPLAY="0"
35 changes: 35 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
version: '3'

services:
backend:
container_name: backend
build: ./backend
ports:
- "5000:5000"
environment:
- MONGO_URI=mongodb://mongo:27017/mydatabase
depends_on:
- mongo
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro

frontend:
container_name: frontend
build: ./frontend
ports:
- "8080:8080"
depends_on:
- backend

mongo:
image: mongo
# ports:
# - "27017:27017"
volumes:
- mongo_data:/data/db
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro

volumes:
mongo_data:
5 changes: 5 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM node:14
WORKDIR /app
COPY . .
RUN npm install
CMD ["npm", "run", "serve"]
16 changes: 16 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "frontend",
"version": "1.0.0",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^0.21.1",
"vue": "^3.0.0",
"vue-router": "^4.0.0"
},
"devDependencies": {
"@vue/cli-service": "~4.5.0"
}
}
Binary file added frontend/public/favicon.ico
Binary file not shown.
31 changes: 31 additions & 0 deletions frontend/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ваше приложение</title>
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<script>
// Функция для проверки авторизации пользователя
function isAuthenticated() {
// Здесь вы можете использовать вашу логику для проверки авторизации
// Например, проверка наличия токена в localStorage
return localStorage.getItem('authToken') !== null;
}

// Перенаправление на нужную страницу
function redirectToPage() {
if (false) {
window.location.href = '/#/'; // Перенаправление на главную страницу (Main)
} else {
window.location.href = '/#/login'; // Перенаправление на страницу входа
}
}
// Выполняем перенаправление при загрузке страницы
window.onload = redirectToPage;
</script>
</head>
<body>
<div id="app"></div> <!-- Здесь будет монтироваться ваше Vue приложение -->
</body>
</html>
21 changes: 21 additions & 0 deletions frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div id="app">
<NavBar /> <!-- Ваш компонент навигации -->
<router-view /> <!-- Здесь будут отображаться компоненты в зависимости от маршрута -->
</div>
</template>

<script>
import NavBar from './components/NavBar.vue'; // Импортируем NavBar
export default {
name: 'App',
components: {
NavBar
}
};
</script>

<style>
/* Добавьте глобальные стили, если необходимо */
</style>
31 changes: 31 additions & 0 deletions frontend/src/components/Button.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<template>
<button @click="handleClick">{{ text }}</button>
</template>

<script>
export default {
props: {
text: {
type: String,
required: true
}
},
methods: {
handleClick() {
this.$emit('click');
}
}
};
</script>

<style scoped>
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
border: none;
background-color: #4CAF50;
color: white;
}
</style>

Loading

0 comments on commit b7921b7

Please sign in to comment.