diff --git a/.gitignore b/.gitignore index b5848e7..940489f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,3 @@ .venv/ .env .idea/ -.vscode/ -__pycache__/ -*.pyc -instance/ -db.sqlite3 - diff --git a/To-Do.md b/To-Do.md deleted file mode 100644 index 238ab43..0000000 --- a/To-Do.md +++ /dev/null @@ -1,17 +0,0 @@ -# Project To-Do and Feature Ideas - -This file tracks potential new features and improvements for the Interbend banking system. - -## Feature Suggestions - -1. **Automated Payroll System:** - * **Description:** Instead of requiring users to manually call the `/collect` endpoint, a scheduled script could run periodically (e.g., every 24 hours) to automatically distribute salaries to all eligible users. - * **Benefits:** Improves user experience, ensures consistent pay, and reduces repeated API calls to the server. - * **Important:** Due to the concept of this whole system it needs to be considered to only pay users who are attend. Bad Idea. - * **Alternative:** Implement system to make sure you can only collect if the host is online. Make admin route to open server (set global bool) - -2. **User Transaction History:** - * **Description:** Create a new API endpoint (e.g., `GET /transactions`) that allows an authenticated user to retrieve a paginated list of their own transaction history. - * **Benefits:** Provides users with transparency and a way to track their finances, which is a core feature of any banking application. - - * FINISHED: PLEASE CHECK IF WORKING! \ No newline at end of file diff --git a/config.py b/config.py index 62a2adf..9dfe8d4 100644 --- a/config.py +++ b/config.py @@ -18,4 +18,4 @@ class Config: # Admin ADMIN_KEY = os.getenv('ADMIN_KEY') - COLLECT_COOLDOWN = int(os.getenv('COLLECT_COOLDOWN', 24)) + COLLECT_COOLDOWN = os.getenv('COLLECT_COOLDOWN') diff --git a/interbend/routes/transaction_routes.py b/interbend/routes/transaction_routes.py index c8a2155..e206a06 100644 --- a/interbend/routes/transaction_routes.py +++ b/interbend/routes/transaction_routes.py @@ -20,81 +20,59 @@ def get_balance(): def collect(): bid = request.bid cooldown = Config.COLLECT_COOLDOWN - try: with db.cursor(dictionary=True) as cur: - # 1. Get user job cur.execute("SELECT * FROM user_jobs WHERE bid = %s", (bid,)) user_jt = cur.fetchone() - if not user_jt: - return jsonify({"error": "You dont have any Jobs"}), 404 - - # 2. Check cooldown - active_cooldown = user_jt.get("collected") - if active_cooldown and (active_cooldown + timedelta(hours=cooldown) > datetime.now(timezone.utc)): - remaining_time = (active_cooldown + timedelta(hours=cooldown)) - datetime.now(timezone.utc) - hours = int(remaining_time.total_seconds() // 3600) - minutes = int(remaining_time.total_seconds() % 3600 // 60) - return jsonify({"error": f"You can only collect your salary every {cooldown} hours. Please wait {hours}h {minutes}m."}), 429 - - # 3. Get job details - job_id = user_jt["job_id"] - cur.execute("SELECT * FROM jobs WHERE job_id = %s", (job_id,)) - job_data = cur.fetchone() - if not job_data: - return jsonify({"error": "Invalid Job", "message": "If you believe this is an error, contact an Administrator"}), 404 - - # 4. Get salary details - salary_class = job_data["salary_class"] - cur.execute("SELECT * FROM salary WHERE class = %s", (salary_class,)) - salary_data = cur.fetchone() - if not salary_data: - return jsonify({"error": "Invalid Salary Class"}), 500 - - amount = salary_data["money"] - - # 5. Perform transaction - db.start_transaction() - cur.execute("UPDATE users SET balance = balance + %s WHERE bid = %s", (amount, bid)) - cur.execute("UPDATE user_jobs SET collected = %s WHERE bid = %s", (datetime.now(timezone.utc), bid)) - cur.execute( - "INSERT INTO transactions (source, target, amount, type, timestamp, status) VALUES (%s, %s, %s, %s, %s, %s)", - ("SYSTEM", bid, amount, "salary", datetime.now(timezone.utc), "completed") - ) - cur.execute("SELECT balance FROM users WHERE bid = %s", (bid,)) - new_balance = cur.fetchone()["balance"] - db.commit() - - return jsonify({"message": "Salary Collected", "New Balance": new_balance}), 200 - + if not user_jt: + return jsonify({"error": "You dont have any Jobs"}), 404 + active_cooldown = user_jt["collected"] except mysql.connector.Error as err: - db.rollback() - current_app.logger.error(f"Database error in /collect: {err}") + current_app.logger.error(f"Database error in collect, salary: {err}") return jsonify({"error": "A database error occurred, please try again later."}), 500 - except Exception as e: - db.rollback() - current_app.logger.error(f"An unexpected error occurred in /collect: {e}") - return jsonify({"error": "An unexpected server error occurred."}), 500 - -@transactions_bp.route('/transactions', methods=['GET']) -@jwt_required -def get_transactions(): - user_bid = request.bid - limit = request.args.get('limit', default=10, type=int) + if active_cooldown + timedelta(hours=cooldown) > datetime.now(timezone.utc): + remaining_time = (active_cooldown + timedelta(hours=cooldown)) - datetime.now(timezone.utc) + hours = int(remaining_time.total_seconds() // 3600) + minutes = int(remaining_time.total_seconds() % 3600 // 60) + return jsonify({"error": f"You can only collect your salary every {cooldown} hours. Please wait {hours}h {minutes}m."}), 429 + job = user_jt["job_id"] try: with db.cursor(dictionary=True) as cur: - cur.execute("SELECT * FROM transactions WHERE source = %s OR target = %s ORDER BY timestamp DESC LIMIT %s", (user_bid, user_bid, limit)) - transactions = cur.fetchall() - return jsonify({"transactions": transactions}), 200 + cur.execute("SELECT * FROM jobs WHERE job_id = %i", (job,)) + job_data = cur.fetchone() except mysql.connector.Error as err: - current_app.logger.error(f"Database error in /transactions: {err}") + current_app.logger.error(f"Database error in collect, salary: {err}") return jsonify({"error": "A database error occurred, please try again later."}), 500 - except Exception as e: - current_app.logger.error(f"An unexpected error occurred in /transactions: {e}") - return jsonify({"error": "An unexpected server error occurred."}), 500 - + if not job_data: + return jsonify({"error": "Invalid Job","message":"If you believe this is an error, contact a " + "Administrator"}), 404 + salary_class = job_data["salary_class"] + try: + with db.cursor(dictionary=True) as cur: + cur.execute("SELECT * FROM salary WHERE class = %i", (salary_class,)) + salary_data = cur.fetchone() + except mysql.connector.Error as err: + current_app.logger.error(f"Database error in collect, salary: {err}") + return jsonify({"error": "A database error occurred, please try again later."}), 500 + if not salary_data: + return jsonify({"error": "Invalid Salary Class"}), 500 + amount = salary_data["money"] + try: + with db.cursor(dictionary=True) as cur: + cur.execute("UPDATE users SET balance = balance + %s WHERE bid = %s", (amount, bid,)) + cur.execute("UPDATE user_jobs SET collected = %s WHERE bid = %s", (datetime.now(timezone.utc), bid,)) + cur.execute("INSERT INTO transactions (source, target, amount, type, timestamp, status) VALUES (%s, %s, " + "%s, %s, %s, %s)", "NULL", bid, amount, "salary", datetime.now(timezone.utc), "completed",) + cur.execute("SELECT balance FROM users WHERE bid = %s", (bid,)) + new_bal2 = cur.fetchone() + new_bal = new_bal2["balance"] + db.commit() + except mysql.connector.Error as err: + db.rollback() + current_app.logger.error(f"Database error in collect, salary: {err}") + return jsonify({"error": "A database error occurred, please try again later."}), 500 + return jsonify({"message":"Salary Collected","New Balance":new_bal}), 200 -# this should be fine @transactions_bp.route('/transfer', methods=['POST']) @jwt_required def transfer():