first commit

This commit is contained in:
2026-06-13 21:46:37 +03:00
commit 650b69a97d
131 changed files with 5951 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

295
server/routes/auth.py Normal file
View File

@@ -0,0 +1,295 @@
from flask import Blueprint, request, jsonify
#from werkzeug.security import generate_password_hash, check_password_hash
from utils.email import send_email, send_gmail
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from flask_jwt_extended import decode_token
import datetime
import random
import os
from datetime import timezone
import hashlib
from models.users import Users
from utils.welcome_email import WelcomeMessage
from models.audit import Audit, AuditModel
auth_bp = Blueprint("auth", __name__)
audit = Audit()
def hash_password(password: str) -> bytes:
return hashlib.md5(password.encode('utf-8')).hexdigest()
@auth_bp.route("/register", methods=["POST"])
def register():
users = Users()
data = request.get_json()
email = data.get("email")
password = data.get("password")
workspace_id = data.get("workspace_id")
if not email or not password:
return jsonify({"error": "Missing required fields"}), 400
existing_user = users.get_user_by_email(email)
if existing_user:
entry = AuditModel(action=f"Attempt to register an existing email:{email}", status='409 - User already exists')
audit.new_entry(entry)
return jsonify({"error": "User already exists"}), 409
users.register_user(email, password, workspace_id)
welcome_message = WelcomeMessage(email)
welcome_message.send_email()
entry = AuditModel(action=f"User registered successfully:{email}", status='201 - User created')
audit.new_entry(entry)
return jsonify({"message": "User registered successfully!"}), 201
@auth_bp.route("/login", methods=["POST"])
def login():
users = Users()
data = request.get_json()
email = data.get("email", "").strip().lower()
password = data.get("password", "")
if not email or not password:
entry = AuditModel(user_id=user.id, action=f"Attempt to login: {email}", status='400 - Missing email or password')
audit.new_entry(entry)
return jsonify({"error": "Missing email or password"}), 400
user = users.get_user_by_email(email)
if not user or not hash_password(password)==user.password:
print(user.password, password)
entry = AuditModel(user_id=user.id ,action=f"Attempt to login: {email}", status='401 - Invalid credentials')
audit.new_entry(entry)
return jsonify({"error": "Invalid credentials"}), 401
if user.active != 1:
entry = AuditModel(user_id=user.id, action=f"Attempt to login: {email}", status='401 - User inactive')
audit.new_entry(entry)
return jsonify({"error": "Inactive user"}), 401
otp_code = str(random.randint(100000, 999999))
expiration = datetime.datetime.now(timezone.utc) + datetime.timedelta(minutes=10)
users.update_user_otp(user.id, otp_code, expiration)
send_gmail(
to_email=user.email,
subject="Your Login Verification Code",
body=f"Your login verification code is: {otp_code}"
)
entry = AuditModel(user_id=user.id, action=f"Attempt to login: {email}", status='200 - Verification code send!')
audit.new_entry(entry)
return jsonify({"message": "Verification code sent to your email."}), 200
@auth_bp.route("/verify_code", methods=["POST"])
def verify_code():
users = Users()
data = request.get_json()
email = data.get("email", "").strip().lower()
code = data.get("code", "")
if not email or not code:
entry = AuditModel(action=f"Attempt to verify code: {email}", status='400 - Missing email or verification code!')
audit.new_entry(entry)
return jsonify({"error": "Missing email or verification code"}), 400
user = users.get_user_by_email(email)
#-----------------------------------------------> for testing only remove in prod
#if email != 'test@test.com':
#-----------------------------------------------> for testing only remove in prod
if not user or user.otp_code != code:
entry = AuditModel(user_id=user.id, action=f"Attempt to verify code: {email}", status='401 - Invalid code!')
audit.new_entry(entry)
return jsonify({"error": "Invalid code"}), 401
exp = user.otp_expiration
# Normalize to aware UTC datetime for safe comparison across SQLite (string) and Postgres (datetime)
now_utc = datetime.datetime.now(timezone.utc)
if isinstance(exp, str):
try:
exp_dt = datetime.datetime.fromisoformat(exp)
except Exception:
return jsonify({"error": "Invalid expiration format"}), 500
if exp_dt.tzinfo is None:
exp_dt = exp_dt.replace(tzinfo=timezone.utc)
else:
# Assume a datetime object from DB driver
exp_dt = exp
if exp_dt is None:
entry = AuditModel(user_id=user.id, action=f"Attempt to verify code:{email}", status='500 - Missing expiration!')
audit.new_entry(entry)
return jsonify({"error": "Missing expiration"}), 500
if exp_dt.tzinfo is None:
exp_dt = exp_dt.replace(tzinfo=timezone.utc)
if now_utc > exp_dt:
entry = AuditModel(user_id=user.id, action=f"Attempt to verify code:{email}", status='403 - Verification code has expired!')
audit.new_entry(entry)
return jsonify({"error": "Verification code has expired"}), 403
users.clear_user_otp(user.id)
access_token = create_access_token(
identity=str(user.id),
expires_delta=datetime.timedelta(hours=12)
)
entry = AuditModel(user_id=user.id, action=f"Attempt to verify code:{email}", status='200 Ok - Login successful!')
audit.new_entry(entry)
return jsonify({
"message": "Login successful",
"access_token": access_token
}), 200
@auth_bp.route("/forgot_password", methods=["POST"])
def forgot_password():
users = Users()
data = request.get_json()
email = data.get("email", "").strip().lower()
if not email:
entry = AuditModel(action=f"Attempt to recover password", status='400 - Email is required!')
audit.new_entry(entry)
return jsonify({"error": "Email is required"}), 400
user = users.get_user_by_email(email)
if user:
reset_token = create_access_token(
identity=user["id"],
expires_delta=datetime.timedelta(minutes=15),
additional_claims={"purpose": "password_reset"}
)
send_gmail(
to_email=user["email"],
subject="Password Reset Request",
body=(
"Click the link to reset your password: "
f"{os.getenv('FRONTEND_BASE_URL', 'http://127.0.0.1:5100')}/reset_password?token={reset_token}"
)
)
entry = AuditModel(user_id=user.id, action=f"Attempt to recover password: {email}", status='200 Ok - If this email is registered, a reset link has been sent!')
audit.new_entry(entry)
return jsonify({"message": "If this email is registered, a reset link has been sent."}), 200
@auth_bp.route("/reset_password", methods=["POST"])
def reset_password():
users = Users()
data = request.get_json()
token = data.get("token", "")
new_password = data.get("new_password", "")
if not token or not new_password:
entry = AuditModel( action=f"Attempt to recover password", status='400 - Missing token or new password!')
audit.new_entry(entry)
return jsonify({"error": "Missing token or new password"}), 400
try:
decoded_token = decode_token(token)
if decoded_token.get("purpose") != "password_reset":
entry = AuditModel( action=f"Attempt to recover password", status='403 - Invalid token purpose!')
audit.new_entry(entry)
return jsonify({"error": "Invalid token purpose"}), 403
except Exception:
entry = AuditModel( action=f"Attempt to recover password", status='403 - Invalid or expired token!')
audit.new_entry(entry)
return jsonify({"error": "Invalid or expired token"}), 403
user_id = decoded_token["sub"]
user = users.get_user(user_id)
if not user:
entry = AuditModel( action=f"Attempt to recover password", status='404 - User not found!')
audit.new_entry(entry)
return jsonify({"error": "User not found"}), 404
users.update_password(user_id, new_password)
entry = AuditModel(user_id=user.id, action=f"Attempt to recover password:{user.email}", status='200 ok - Password has been reset successfully.')
audit.new_entry(entry)
return jsonify({"message": "Password has been reset successfully."}), 200
@auth_bp.route("/me", methods=["GET"])
@jwt_required()
def me():
users = Users()
user_id = get_jwt_identity()
user = users.get_user(user_id)
if not user:
entry = AuditModel( action=f"Get user data: ", status='404 - User not found.')
audit.new_entry(entry)
return jsonify({"error": "User not found"}), 404
entry = AuditModel(user_id=user.id, action=f"Get user data: {user.email}", status='200 Ok.')
audit.new_entry(entry)
return jsonify({
'id': user.id,
'workspace_id': user.workspace_id,
'first_name':user.first_name,
'last_name': user.last_name,
'email':user.email,
'address':user.address,
'profession':user.profession,
'role':user.role,
'status': user.status,
'profile_pic': user.profile_pic,
'created_at': user.created_at,
'otp_code': user.otp_code,
'otp_expiration': user.otp_expiration
}), 200
# Validate token endpoint
@auth_bp.route("/validate_token", methods=["GET"])
@jwt_required()
def validate_token():
users = Users()
user_id = get_jwt_identity()
user = users.get_user(user_id)
if not user:
entry = AuditModel(action=f"Get access token:", status='404 - User not found.')
audit.new_entry(entry)
return jsonify({"error": "User not found"}), 404
entry = AuditModel(user_id=user.id, action=f"Get access token: {user.email}", status='200 Ok - Token is valid')
audit.new_entry(entry)
return jsonify({"message": "Token is valid"}), 200
# @auth_bp.route("/temporary_password", methods=["POST"])
# @jwt_required()
# def change_passwd():
# data = request.get_json()
# if not data:
# entry = AuditModel(action=f"Get temporary password token:", status='404 - Password not found.')
# audit.new_entry(entry)
# return jsonify({"error": "Password not found"}), 404
# users = Users()
# user_id = get_jwt_identity()
# new_password_hash = generate_password_hash(data['password'])
# users.update_user_password(user_id, new_password_hash)
# users.update_temp_pass(user_id)
# entry = AuditModel(user_id=user_id, action=f"Get temporary password:", status='200 Ok - Password has been updated successfully.')
# audit.new_entry(entry)
# return jsonify({"message": "Password has been updated successfully."}), 200
@auth_bp.route("/update_passwrod", methods=["POST"])
def update_passwrod():
data = request.get_json()
email = data.get("email", "").strip().lower()
password = data.get('password')
token = data.get('token')
env_token = os.getenv('PASSWORD_TOKEN')
if not email and not password and not token:
entry = AuditModel(action=f"Update Password:", status='403 - Data not provided.')
audit.new_entry(entry)
return jsonify({"error": "Data not provided"}), 403
if env_token != env_token:
entry = AuditModel(action=f"Update Password:", status='401 - Invalid token.')
audit.new_entry(entry)
return jsonify({"error": "Data not provided"}), 401
users = Users()
users.update_password(email, password)
entry = AuditModel(action=f"Update Password:", status='200 - Password has been updated successfully.')
audit.new_entry(entry)
return jsonify({"message": "Password has been updated successfully."}), 200

417
server/routes/documents.py Normal file
View File

@@ -0,0 +1,417 @@
import os
import shutil
from flask import Blueprint, request, jsonify, send_from_directory
from flask_jwt_extended import jwt_required, get_jwt_identity
from models.documents.documents_category import DocumentsCategory, DocumentsCategoryModel
from models.documents.documents_standard import DocumentsStandard, DocumentsStandardModel
from models.documents.documents_custom import DocumentsCustom, DocumentsCustomModel, CustomDocumentRequests, CustomDocumentRequestModel
from models.audit import Audit, AuditModel
documents_bp = Blueprint("documents", __name__)
audit = Audit()
# Definirea căii către folderul principal de documente (la nivelul server/client)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
DOCUMENTS_ROOT = os.path.join(BASE_DIR, "documents")
os.makedirs(DOCUMENTS_ROOT, exist_ok=True)
# --- Document Categories ---
@documents_bp.route("/categories/add", methods=["POST"])
@jwt_required()
def add_category():
user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
access = data.get("access")
if not name:
entry = AuditModel(user_id=user_id, action="Attempt to create category", status="400 - Missing name")
audit.new_entry(entry)
return jsonify({"error": "Missing name"}), 400
docs_cat = DocumentsCategory()
# Filesafe: Verificăm dacă există deja o categorie cu acest nume (global)
# pentru a preveni duplicatele în DB și conflictele în sistemul de fișiere.
existing_categories = docs_cat.get_all_entries()
for cat in existing_categories:
if cat.name and cat.name.lower() == name.lower():
return jsonify({"message": "Category already exists", "id": cat.id}), 200
category = DocumentsCategoryModel(user_id=user_id, name=name, access=access)
result = docs_cat.new_entry(category)
if result:
# Creare folder fizic pentru categorie
category_path = os.path.join(DOCUMENTS_ROOT, name)
os.makedirs(category_path, exist_ok=True)
entry = AuditModel(user_id=user_id, action=f"Category created: {name}", status="201 - Created")
audit.new_entry(entry)
return jsonify({"message": "Category created successfully", "id": result}), 201
return jsonify({"error": "Failed to create category"}), 500
@documents_bp.route("/categories", methods=["GET"])
@jwt_required()
def get_categories():
docs_cat = DocumentsCategory()
categories = docs_cat.get_all_entries()
return jsonify([vars(c) for c in categories]), 200
@documents_bp.route("/categories/update/<int:id>", methods=["PUT"])
@jwt_required()
def update_category(id):
user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
access = data.get("access")
if not name:
return jsonify({"error": "Missing name"}), 400
docs_cat = DocumentsCategory()
old_category = docs_cat.get_entry_by_id(id)
if not old_category:
return jsonify({"error": "Category not found"}), 404
updated_count = docs_cat.update_entry(id, name, access)
if updated_count:
# Redenumire folder dacă numele s-a schimbat
if old_category.name != name:
old_path = os.path.join(DOCUMENTS_ROOT, old_category.name)
new_path = os.path.join(DOCUMENTS_ROOT, name)
if os.path.exists(old_path):
os.rename(old_path, new_path)
else:
os.makedirs(new_path, exist_ok=True)
entry = AuditModel(user_id=user_id, action=f"Category updated: {name} (ID: {id})", status="200 - OK")
audit.new_entry(entry)
return jsonify({"message": "Category updated successfully"}), 200
return jsonify({"error": "Category not found"}), 404
@documents_bp.route("/categories/delete/<int:id>", methods=["DELETE"])
@jwt_required()
def delete_category(id):
user_id = get_jwt_identity()
docs_cat = DocumentsCategory()
# Preluăm categoria înainte de ștergere pentru a cunoaște numele folderului
category = docs_cat.get_entry_by_id(id)
if not category:
return jsonify({"error": "Category not found"}), 404
deleted_count = docs_cat.delete_entry(id)
if deleted_count:
# Ștergere folder fizic și tot conținutul său (documente încărcate)
category_path = os.path.join(DOCUMENTS_ROOT, category.name)
if os.path.exists(category_path):
shutil.rmtree(category_path)
entry = AuditModel(user_id=user_id, action=f"Deleted Category: {category.name} (ID: {id})", status="200 - OK")
audit.new_entry(entry)
return jsonify({"message": "Category deleted successfully"}), 200
return jsonify({"error": "Category not found"}), 404
@documents_bp.route("/download", methods=["GET"])
@jwt_required()
def download_file():
# Path-ul vine ca parametru (ex: NumeCategorie/fisier.pdf)
file_path = request.args.get("path")
if not file_path:
return jsonify({"error": "Missing path"}), 400
# Securitate: prevenim ieșirea din DOCUMENTS_ROOT
abs_path = os.path.abspath(os.path.join(DOCUMENTS_ROOT, file_path))
if not abs_path.startswith(os.path.abspath(DOCUMENTS_ROOT)):
return jsonify({"error": "Unauthorized access"}), 403
directory = os.path.dirname(abs_path)
filename = os.path.basename(abs_path)
return send_from_directory(directory, filename, as_attachment=True)
@documents_bp.route("/categories/refresh/<int:category_id>", methods=["POST"])
@jwt_required()
def refresh_category_files(category_id):
user_id = get_jwt_identity()
docs_cat = DocumentsCategory()
docs_std = DocumentsStandard()
category = docs_cat.get_entry_by_id(category_id)
if not category:
return jsonify({"error": "Category not found"}), 404
category_path = os.path.join(DOCUMENTS_ROOT, category.name)
if not os.path.exists(category_path):
return jsonify({"error": "Category folder does not exist on disk"}), 404
# Obținem lista fișierelor de pe disc
files_on_disk = [f for f in os.listdir(category_path) if os.path.isfile(os.path.join(category_path, f))]
# Obținem lista documentelor din DB pentru această categorie
db_entries = docs_std.get_entries_by_category(category_id)
db_file_paths = {entry.path for entry in db_entries}
added_count = 0
for filename in files_on_disk:
# Reconstruim path-ul așa cum este stocat în DB: "NumeCategorie/fisier.ext"
relative_path = f"{category.name}/{filename}"
if relative_path not in db_file_paths:
new_doc = DocumentsStandardModel(
category_id=category_id,
user_id=0, # User ID 0 pentru fișiere adăugate extern
name=filename,
path=relative_path,
access=category.access
)
if docs_std.new_entry(new_doc):
added_count += 1
if added_count > 0:
audit.new_entry(AuditModel(user_id=user_id, action=f"Refreshed category {category.name}: added {added_count} files", status="200 - OK"))
return jsonify({"message": f"Refresh complete. Added {added_count} new documents.", "added": added_count}), 200
# --- Document Standards Category ---
@documents_bp.route("/standards/category/<int:category_id>", methods=["GET"])
@jwt_required()
def get_standards_by_category(category_id):
docs_std = DocumentsStandard()
items = docs_std.get_entries_by_category(category_id)
return jsonify([vars(i) for i in items]), 200
# --- Document Standards ---
@documents_bp.route("/standards/add", methods=["POST"])
@jwt_required()
def add_standard():
user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
path = data.get("path")
access = data.get("access")
category_id = data.get("category_id")
if not name or not path or not category_id:
entry = AuditModel(user_id=user_id, action="Attempt to create standard", status="400 - Missing fields")
audit.new_entry(entry)
return jsonify({"error": "Missing name, path or category_id"}), 400
# Mutare fizică a fișierului din folderul de upload în folderul categoriei
# Sursa este folderul unde Flet salvează implicit upload-urile
source_path = os.path.join(BASE_DIR, "client", "assets", "uploads", os.path.basename(path))
dest_path = os.path.join(DOCUMENTS_ROOT, path)
if os.path.exists(source_path):
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.move(source_path, dest_path)
docs_std = DocumentsStandard()
standard = DocumentsStandardModel(user_id=user_id, category_id=category_id, name=name, path=path, access=access)
result = docs_std.new_entry(standard)
if result:
entry = AuditModel(user_id=user_id, action=f"Standard created: {name}", status="201 - Created")
audit.new_entry(entry)
return jsonify({"message": "Standard created successfully", "id": result}), 201
return jsonify({"error": "Failed to create standard"}), 500
@documents_bp.route("/standards", methods=["GET"])
@jwt_required()
def get_standards():
user_id = get_jwt_identity()
docs_std = DocumentsStandard()
standards = docs_std.get_all_entries()
return jsonify([vars(s) for s in standards]), 200
@documents_bp.route("/standards/delete/<int:id>", methods=["DELETE"])
@jwt_required()
def delete_standard(id):
user_id = get_jwt_identity()
docs_std = DocumentsStandard()
# Preluăm documentul înainte de ștergere pentru a cunoaște calea fișierului
standard = docs_std.get_entry_by_id(id)
if not standard:
return jsonify({"error": "Standard document not found"}), 404
deleted_count = docs_std.delete_entry(id)
if deleted_count:
# Ștergere fișier fizic de pe disc
file_path = os.path.join(DOCUMENTS_ROOT, standard.path)
if os.path.exists(file_path):
try:
os.remove(file_path)
except Exception as e:
print(f"Error deleting physical file: {e}")
entry = AuditModel(user_id=user_id, action=f"Deleted Standard Document: {standard.name} (ID: {id})", status="200 - OK")
audit.new_entry(entry)
return jsonify({"message": "Standard document deleted successfully"}), 200
return jsonify({"error": "Failed to delete standard document"}), 500
# --- Document Custom ---
@documents_bp.route("/customs/add", methods=["POST"])
@jwt_required()
def add_custom():
user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
path = data.get("path")
access = data.get("access")
if not name or not path:
entry = AuditModel(user_id=user_id, action="Attempt to create custom document", status="400 - Missing fields")
audit.new_entry(entry)
return jsonify({"error": "Missing name or path"}), 400
# Physical file move from upload folder to documents root
source_path = os.path.join(BASE_DIR, "client", "assets", "uploads", os.path.basename(path))
dest_path = os.path.join(DOCUMENTS_ROOT, path)
if os.path.exists(source_path):
# Ensure the destination directory exists (for custom docs we usually put them in root or a 'custom' folder)
os.makedirs(os.path.dirname(dest_path), exist_ok=True)
shutil.move(source_path, dest_path)
docs_custom = DocumentsCustom()
custom = DocumentsCustomModel(user_id=user_id, name=name, path=path, access=access)
result = docs_custom.new_entry(custom)
if result:
entry = AuditModel(user_id=user_id, action=f"Custom document created: {name}", status="201 - Created")
audit.new_entry(entry)
return jsonify({"message": "Custom document created successfully", "id": result}), 201
return jsonify({"error": "Failed to create custom document"}), 500
@documents_bp.route("/customs", methods=["GET"])
@jwt_required()
def get_customs():
user_id = get_jwt_identity()
docs_custom = DocumentsCustom()
customs = docs_custom.get_entries_by_user_id(user_id)
return jsonify([vars(c) for c in customs]), 200
@documents_bp.route("/customs/<int:id>", methods=["GET"])
@jwt_required()
def get_custom_by_id(id):
docs_custom = DocumentsCustom()
document = docs_custom.get_entry_by_id(id)
if not document:
return jsonify({"error": "Custom document not found"}), 404
return jsonify(vars(document)), 200
@documents_bp.route("/customs/delete/<int:id>", methods=["DELETE"])
@jwt_required()
def delete_custom(id):
user_id = get_jwt_identity()
docs_custom = DocumentsCustom()
deleted_count = docs_custom.delete_entry(id)
if deleted_count:
entry = AuditModel(user_id=user_id, action=f"Deleted Custom Document ID: {id}", status="200 - OK")
audit.new_entry(entry)
return jsonify({"message": "Custom document deleted successfully"}), 200
return jsonify({"error": "Custom document not found"}), 404
# --- Custom Document Requests ---
@documents_bp.route("/customs/requests/add", methods=["POST"])
@jwt_required()
def add_custom_request():
user_id = get_jwt_identity()
data = request.get_json()
request_text = data.get("request_text")
if not request_text:
return jsonify({"error": "Missing request text"}), 400
repo = CustomDocumentRequests()
new_request = CustomDocumentRequestModel(
client_id=user_id,
request_text=request_text,
status="new"
)
result = repo.new_entry(new_request)
if result:
audit.new_entry(AuditModel(user_id=user_id, action=f"Custom request created: {result}", status="201 - Created"))
return jsonify({"message": "Request submitted successfully", "id": result}), 201
return jsonify({"error": "Failed to submit request"}), 500
@documents_bp.route("/customs/requests", methods=["GET"])
@jwt_required()
def get_all_custom_requests():
repo = CustomDocumentRequests()
requests_list = repo.get_all_entries()
return jsonify([vars(r) for r in requests_list]), 200
@documents_bp.route("/customs/requests/<int:id>", methods=["GET"])
@jwt_required()
def get_custom_request_by_id(id):
repo = CustomDocumentRequests()
req = repo.get_entry_by_id(id)
if not req:
return jsonify({"error": "Request not found"}), 404
return jsonify(vars(req)), 200
@documents_bp.route("/customs/requests/client", methods=["GET"])
@jwt_required()
def get_client_custom_requests():
user_id = get_jwt_identity()
repo = CustomDocumentRequests()
requests_list = repo.get_entries_by_client_id(user_id)
return jsonify([vars(r) for r in requests_list]), 200
@documents_bp.route("/customs/requests/expert", methods=["GET"])
@jwt_required()
def get_expert_custom_requests():
user_id = get_jwt_identity()
repo = CustomDocumentRequests()
requests_list = repo.get_entries_by_expert_id(user_id)
return jsonify([vars(r) for r in requests_list]), 200
@documents_bp.route("/customs/requests/update/<int:id>", methods=["PUT"])
@jwt_required()
def update_custom_request(id):
user_id = get_jwt_identity()
data = request.get_json()
repo = CustomDocumentRequests()
success = repo.update_entry(
id=id,
request_text=data.get("request_text"),
status=data.get("status"),
price=data.get("price"),
expert_id=data.get("expert_id"),
document_id=data.get("document_id")
)
if success:
audit.new_entry(AuditModel(user_id=user_id, action=f"Updated custom request ID: {id}", status="200 - OK"))
return jsonify({"message": "Request updated successfully"}), 200
return jsonify({"error": "Request not found or no updates provided"}), 404
@documents_bp.route("/customs/requests/delete/<int:id>", methods=["DELETE"])
@jwt_required()
def delete_custom_request(id):
user_id = get_jwt_identity()
repo = CustomDocumentRequests()
if repo.delete_entry(id):
audit.new_entry(AuditModel(user_id=user_id, action=f"Deleted custom request ID: {id}", status="200 - OK"))
return jsonify({"message": "Request deleted successfully"}), 200
return jsonify({"error": "Request not found"}), 404

85
server/routes/payments.py Normal file
View File

@@ -0,0 +1,85 @@
import os
import shutil
from flask import Blueprint, request, jsonify, send_from_directory
from flask_jwt_extended import jwt_required, get_jwt_identity
from models.payments.payments import Payments, PaymentsModel
from models.audit import Audit, AuditModel
payments_bp = Blueprint("payments", __name__)
audit = Audit()
@payments_bp.route("/add", methods=["POST"])
@jwt_required()
def add_payment():
current_user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
amount = data.get("amount")
payment_type = data.get("type")
if not name or amount is None or not payment_type:
return jsonify({"error": "Missing required fields (name, amount, type)"}), 400
payment_repo = Payments()
new_payment = PaymentsModel(
user_id=current_user_id,
name=name,
amount=float(amount),
type=payment_type
)
payment_id = payment_repo.add_payment(new_payment)
if payment_id:
audit.new_entry(AuditModel(user_id=current_user_id, action=f"Added payment: {name}", status="201 - Created"))
return jsonify({"message": "Payment added successfully", "id": payment_id}), 201
return jsonify({"error": "Failed to add payment"}), 500
@payments_bp.route("/", methods=["GET"])
@jwt_required()
def get_payments():
payment_repo = Payments()
payments = payment_repo.get_all_payments()
return jsonify([vars(p) for p in payments]), 200
@payments_bp.route("/<int:payment_id>", methods=["GET"])
@jwt_required()
def get_payment(payment_id):
payment_repo = Payments()
payment = payment_repo.get_payment(payment_id)
if not payment:
return jsonify({"error": "Payment not found"}), 404
return jsonify(vars(payment)), 200
@payments_bp.route("/update/<int:payment_id>", methods=["PUT"])
@jwt_required()
def update_payment(payment_id):
current_user_id = get_jwt_identity()
data = request.get_json()
payment_repo = Payments()
success = payment_repo.update_payment(
payment_id,
name=data.get("name"),
amount=float(data.get("amount")) if data.get("amount") is not None else None,
type=data.get("type")
)
if success:
audit.new_entry(AuditModel(user_id=current_user_id, action=f"Updated payment ID: {payment_id}", status="200 - OK"))
return jsonify({"message": "Payment updated successfully"}), 200
return jsonify({"error": "Payment not found or no valid fields to update"}), 404
@payments_bp.route("/delete/<int:payment_id>", methods=["DELETE"])
@jwt_required()
def delete_payment(payment_id):
current_user_id = get_jwt_identity()
payment_repo = Payments()
if payment_repo.delete_payment(payment_id):
audit.new_entry(AuditModel(user_id=current_user_id, action=f"Deleted payment ID: {payment_id}", status="200 - OK"))
return jsonify({"message": "Payment deleted successfully"}), 200
return jsonify({"error": "Payment not found"}), 404

21
server/routes/settings.py Normal file
View File

@@ -0,0 +1,21 @@
from flask import Blueprint, request, jsonify
#from werkzeug.security import generate_password_hash, check_password_hash
from utils.email import send_email, send_gmail
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from models.audit import Audit, AuditModel
import os
auth_bp = Blueprint("settings", __name__)
audit = Audit()
@auth_bp.route("/documente_juridice/add", methods=["POST"])
@jwt_required()
def documente_juridice_add():
data = request.get_json()
user_id = get_jwt_identity()
if 'category' not in data:
entry = AuditModel(user_id=user_id, action=f"Attempt to create a new category", status='400 - Missing category')
audit.new_entry(entry)

View File

@@ -0,0 +1,71 @@
from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from models.payments.subscriptions import Subscriptions, SubscriptionsModel
from models.audit import Audit, AuditModel
subscriptions_bp = Blueprint("subscriptions", __name__)
audit = Audit()
@subscriptions_bp.route("/add", methods=["POST"])
@jwt_required()
def add_subscription():
current_user_id = get_jwt_identity()
data = request.get_json()
name = data.get("name")
pay_and_subs_id = data.get("pay_and_subs_id")
mounts = data.get("mounts")
if not name or not pay_and_subs_id:
return jsonify({"error": "Missing required fields (name, pay_and_subs_id)"}), 400
subs_repo = Subscriptions()
new_sub = SubscriptionsModel(
user_id=current_user_id,
name=name,
pay_and_subs_id=pay_and_subs_id,
mounts=mounts
)
sub_id = subs_repo.add_subscription(new_sub)
if sub_id:
audit.new_entry(AuditModel(user_id=current_user_id, action=f"Added subscription: {name}", status="201 - Created"))
return jsonify({"message": "Subscription added successfully", "id": sub_id}), 201
return jsonify({"error": "Failed to add subscription"}), 500
@subscriptions_bp.route("/", methods=["GET"])
@jwt_required()
def get_all_subscriptions():
subs_repo = Subscriptions()
subscriptions = subs_repo.get_all_subscriptions()
return jsonify([vars(s) for s in subscriptions]), 200
@subscriptions_bp.route("/<int:subscription_id>", methods=["GET"])
@jwt_required()
def get_subscription(subscription_id):
subs_repo = Subscriptions()
subscription = subs_repo.get_subscription(subscription_id)
if not subscription:
return jsonify({"error": "Subscription not found"}), 404
return jsonify(vars(subscription)), 200
@subscriptions_bp.route("/update/<int:subscription_id>", methods=["PUT"])
@jwt_required()
def update_subscription(subscription_id):
current_user_id = get_jwt_identity()
data = request.get_json()
subs_repo = Subscriptions()
success = subs_repo.update_subscription(
subscription_id,
name=data.get("name"),
pay_and_subs_id=data.get("pay_and_subs_id"),
mounts=data.get("mounts")
)
if success:
audit.new_entry(AuditModel(user_id=current_user_id, action=f"Updated subscription ID: {subscription_id}", status="200 - OK"))
return jsonify({"message": "Subscription updated successfully"}), 200
return jsonify({"error": "Subscription not found or no valid fields to update"}), 404

108
server/routes/users.py Normal file
View File

@@ -0,0 +1,108 @@
from flask import Blueprint, request, jsonify
from flask_jwt_extended import jwt_required, get_jwt_identity
from models.users import Users, UserModel
from models.audit import Audit, AuditModel
users_bp = Blueprint("users", __name__)
audit = Audit()
@users_bp.route("/add", methods=["POST"])
@jwt_required()
def add_user():
current_admin_id = get_jwt_identity()
data = request.get_json()
email = data.get("email")
workspace_id = data.get("workspace_id")
if not email or not workspace_id:
return jsonify({"error": "Missing required fields (email, workspace_id)"}), 400
user_repo = Users()
if user_repo.get_user_by_email(email):
return jsonify({"error": "User already exists"}), 409
new_user = UserModel(
workspace_id=workspace_id,
first_name=data.get("first_name"),
last_name=data.get("last_name"),
email=email,
password=user_repo.hash_password(data.get("password")) if data.get("password") else None,
address=data.get("address"),
profession=data.get("profession"),
role=data.get("role", "user"),
status=data.get("status", "active"),
profile_pic=data.get("profile_pic"),
active=1
)
user_id = user_repo.add_user(new_user)
if user_id:
audit.new_entry(AuditModel(user_id=current_admin_id, action=f"Added user: {email}", status="201 - Created"))
return jsonify({"message": "User added successfully", "id": user_id}), 201
return jsonify({"error": "Failed to add user"}), 500
@users_bp.route("/<int:user_id>", methods=["GET"])
@jwt_required()
def get_user(user_id):
user_repo = Users()
user = user_repo.get_user(user_id)
if not user:
return jsonify({"error": "User not found"}), 404
# Convertim obiectul dataclass în dicționar pentru JSON
return jsonify(vars(user)), 200
@users_bp.route("/", methods=["GET"])
@jwt_required()
def get_all_users():
user_repo = Users()
users = user_repo.get_all_users()
# Mapăm lista de obiecte UserModel la o listă de dicționare
return jsonify([vars(u) for u in users]), 200
@users_bp.route("/update/<int:user_id>", methods=["PUT"])
@jwt_required()
def update_user(user_id):
current_admin_id = get_jwt_identity()
data = request.get_json()
user_repo = Users()
# Dacă se dorește actualizarea parolei, o hash-uim înainte de salvare
password = data.get("password")
hashed_password = user_repo.hash_password(password) if password else None
success = user_repo.update_user(
user_id,
first_name=data.get("first_name"),
last_name=data.get("last_name"),
email=data.get("email"),
password=hashed_password,
address=data.get("address"),
profession=data.get("profession"),
role=data.get("role"),
status=data.get("status"),
profile_pic=data.get("profile_pic"),
active=data.get("active")
)
if success:
audit.new_entry(AuditModel(user_id=current_admin_id, action=f"Updated user ID: {user_id}", status="200 - OK"))
return jsonify({"message": "User updated successfully"}), 200
return jsonify({"error": "User not found or no valid fields to update"}), 404
@users_bp.route("/delete/<int:user_id>", methods=["DELETE"])
@jwt_required()
def delete_user(user_id):
current_admin_id = get_jwt_identity()
user_repo = Users()
if user_repo.delete_user(user_id):
audit.new_entry(AuditModel(user_id=current_admin_id, action=f"Deleted user ID: {user_id}", status="200 - OK"))
return jsonify({"message": "User deleted successfully"}), 200
return jsonify({"error": "User not found"}), 404