Working log in/route guard

This commit is contained in:
2025-09-04 08:05:01 -04:00
parent d250e136c3
commit 20f9a4485e
9 changed files with 199 additions and 355 deletions

View File

@@ -1,37 +1,23 @@
FROM python:3.13.3-bullseye
ENV PYTHONFAULTHANDLER=1
# Use an official Python runtime as a parent image
FROM python:3.11-slim-bullseye
# Set environment variables
ENV PYTHONUNBUFFERED=1
ENV APP_HOME=/app
WORKDIR $APP_HOME
ENV TZ=America/New_York
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn
ENV MODE="PRODUCTION"
RUN mkdir -p /app
COPY requirements.txt /app
WORKDIR /app
RUN pip3 install -r requirements.txt
RUN pip3 install gunicorn
# Install Nginx
RUN apt-get update && apt-get install -y nginx && rm -rf /var/lib/apt/lists/*
COPY . /app
# Copy Nginx configuration
COPY nginx.conf /etc/nginx/sites-available/default
# Enable the Nginx site
RUN ln -sf /etc/nginx/sites-available/default /etc/nginx/sites-enabled/
# Copy start script
COPY start.sh /app/start.sh
RUN chmod +x /app/start.sh
# Copy the rest of the application code
COPY . .
# Tell Docker that the container listens on port 80
EXPOSE 80
CMD ["/app/start.sh"]
# Run the application using Gunicorn
# This command runs the Flask app. 'app:app' means "in the file named app.py, run the variable named app".
# Adjust if your main file or Flask app variable is named differently.
CMD ["gunicorn", "--bind", "0.0.0.0:80", "app:app"]

View File

@@ -11,7 +11,7 @@ from sqlalchemy.orm import sessionmaker
from werkzeug.routing import BaseConverter
from flask_mail import Mail
from config import load_config
import re
ApplicationConfig = load_config()
@@ -70,29 +70,28 @@ login_manager.anonymous_user = "Guest"
@login_manager.request_loader
def load_user_from_request(request):
from app.classes.auth import Auth_User
# first, try to log in using the api_key url arg
api_key = request.args.get('api_key')
if api_key:
user = db.session\
.query(Auth_User)\
.filter_by(api_key=api_key)\
.first()
if user:
return user
# next, try to log in using Basic Auth
api_key_auth = request.headers.get('Authorization')
if api_key_auth:
api_key = api_key_auth.replace('bearer ', '', 1)
if api_key.startswith('"') and api_key.endswith('"'):
api_key = api_key[1:-1]
user = db.session\
.query(Auth_User)\
.filter_by(api_key=api_key)\
.first()
if user:
return user
return None
# Check for Authorization header first, as it's the standard
auth_header = request.headers.get('Authorization')
if auth_header:
# --- THIS IS THE FIX ---
# Use a case-insensitive regular expression to strip "bearer "
api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
if api_key:
user = db.session.query(Auth_User).filter_by(api_key=api_key).first()
if user:
return user
# As a fallback, check for api_key in URL args (less secure, but keeps existing logic)
api_key_arg = request.args.get('api_key')
if api_key_arg:
user = db.session.query(Auth_User).filter_by(api_key=api_key_arg).first()
if user:
return user
# If no valid key is found in header or args, return None
return None
# api_main = {
# "origins": [ApplicationConfig.ORIGIN_URL],

View File

@@ -6,33 +6,28 @@ from datetime import datetime
from uuid import uuid4
from app.classes.auth import Auth_User
from app.classes.employee import Employee_Employee
import re
@auth.route("/whoami", methods=["GET"])
def check_session():
"""
Checks auth token and returns user and associated employee data.
"""
api_key = request.headers.get('Authorization')
if not api_key:
auth_header = request.headers.get('Authorization')
if not auth_header:
return jsonify({"ok": False, "error": "Authorization header missing"}), 401
# Clean up the token
api_key = api_key.replace('bearer ', '', 1).strip('"')
# --- THIS IS THE FIX ---
# Use a case-insensitive regular expression to remove "bearer "
# This handles "Bearer ", "bearer ", "BEARER ", etc.
api_key = re.sub(r'^bearer\s+', '', auth_header, flags=re.IGNORECASE).strip('"')
user = db.session.query(Auth_User).filter(Auth_User.api_key == api_key).first()
if not user:
print("no user found with that api key")
return jsonify({"ok": False, "error": "Invalid token"}), 401
# --- THIS IS THE CRITICAL FIX ---
# Now that we have the user, find the corresponding employee record.
# This assumes your Employee model has a 'user_id' field linking to the Auth_User 'id'.
employee = db.session.query(Employee_Employee).filter(Employee_Employee.user_id == user.id).first()
# It's possible a user exists without an employee record, so we handle that case.
if not employee:
return jsonify({"ok": False, "error": "User found, but no associated employee record"}), 404
# Now, build the complete response with both user and employee data.
return jsonify({
"ok": True,
@@ -44,13 +39,6 @@ def check_session():
'token': user.api_key,
'confirmed': user.confirmed
},
# ADD THE EMPLOYEE OBJECT TO THE RESPONSE
'employee': {
'id': employee.id,
'employee_first_name': employee.employee_first_name,
'employee_last_name': employee.employee_last_name,
# Add any other employee fields you might need on the frontend
}
}), 200
@@ -86,38 +74,27 @@ def logout():
@auth.route("/login", methods=["POST"])
def login():
"""
Main post function to a user
"""
username = request.json["username"]
password = request.json["password"]
user = db.session\
.query(Auth_User)\
.filter_by(username=username)\
.first() is not None
user = db.session.query(Auth_User).filter_by(username=username).first()
# Important checks!
if not user:
return jsonify({"error": True}), 200
user = db.session\
.query(Auth_User)\
.filter_by(username=username)\
.first()
return jsonify({"error": "User not found"}), 401 # Use a more descriptive error and status code
if not bcrypt.check_password_hash(user.password_hash, password):
return jsonify({"error": True}), 200
db.session.add(user)
db.session.commit()
return jsonify({"error": "Invalid password"}), 401 # Use a more descriptive error and status code
# If login is successful, return the correct structure
return jsonify({
"ok": True,
'user': {'user_id': user.uuid,
'user_id': user.id,
'user_email': user.email,
'admin_role': user.admin_role,
'token': user.api_key
},
'user': {
'user_name': user.username,
'user_id': user.id,
'user_email': user.email,
'admin_role': user.admin_role,
},
'token': user.api_key
}), 200

View File

@@ -32,7 +32,7 @@ def generate_random_number_string(length):
@customer.route("/all", methods=["GET"])
@login_required
def all_customers_around():
customer_list = db.session \
.query(Customer_Customer) \
@@ -42,7 +42,7 @@ def all_customers_around():
@customer.route("/all/<int:page>", methods=["GET"])
@login_required
def all_customers(page):
"""
pagination all customers
@@ -141,7 +141,7 @@ def get_a_customer_tank(customer_id):
@customer.route("/create", methods=["POST"])
@login_required
def create_customer():
"""
"""

View File

@@ -6,169 +6,47 @@ from app import db
from app.classes.delivery import (Delivery_Delivery,
Delivery_Delivery_schema,
)
from app.classes.service import Service_Service
from app.classes.auto import Auto_Delivery
from datetime import date, timedelta, datetime
@deliverystatus.route("/delivered", methods=["GET"])
def delivered_delivery():
# --- NEW EFFICIENT ENDPOINT ---
@deliverystatus.route("/stats/sidebar-counts", methods=["GET"])
def get_sidebar_counts():
"""
Get deliveries that have been delivered
Efficiently gets all counts needed for the navigation sidebar in a single request.
This combines logic from all the individual /count/* endpoints.
"""
try:
now = datetime.now()
today_date = date.today()
delivery_ticket = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 10)
.all())
# Replicate the logic from each of your /count/* endpoints
today_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 2).count()
tomorrow_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 3).count()
waiting_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 0).count()
pending_count = db.session.query(Delivery_Delivery).filter(Delivery_Delivery.delivery_status == 9).count()
automatic_count = db.session.query(Auto_Delivery).filter(Auto_Delivery.estimated_gallons_left <= 80).count()
upcoming_service_count = db.session.query(Service_Service).filter(Service_Service.scheduled_date >= now).count()
return jsonify({
"ok": True,
"counts": {
"today": today_count,
"tomorrow": tomorrow_count,
"waiting": waiting_count,
"pending": pending_count,
"automatic": automatic_count,
"upcoming_service": upcoming_service_count,
}
}), 200
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(delivery_ticket))
@deliverystatus.route("/count/delivered", methods=["GET"])
def delivered_delivery_count():
delivery_ticket = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 10)
.count())
return jsonify({
"ok": True,
'count':delivery_ticket,
}), 200
@deliverystatus.route("/today/driver/<int:user_id>", methods=["GET"])
def get_deliveries_driver_today(user_id):
"""
Get deliveries for driver that day
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.driver_employee_id == user_id)
.filter(Delivery_Delivery.expected_delivery_date == date.today())
.all())
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_delivery))
@deliverystatus.route("/count/today", methods=["GET"])
def get_deliveries_today_count():
"""
Get deliveries for driver that day
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 2)
.count())
return jsonify({
"ok": True,
'count':get_delivery,
}), 200
@deliverystatus.route("/tommorrow/driver/<int:user_id>", methods=["GET"])
def get_deliveries_driver_tommorrow(user_id):
"""
Get deliveries for driver tommrrow
"""
tomm = date.today() + timedelta(days=1)
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.driver_employee_id == user_id)
.filter(Delivery_Delivery.delivery_status == 3)
.all())
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_delivery))
@deliverystatus.route("/count/tommorrow", methods=["GET"])
def get_deliveries_driver_tommorrow_count():
"""
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 3)
.count())
return jsonify({
"ok": True,
'count':get_delivery,
}), 200
@deliverystatus.route("/waiting/driver/<int:user_id>", methods=["GET"])
def get_deliveries_driver_waiting(user_id):
"""
waiting deliveries scheduled out
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.driver_employee_id == user_id)
.filter(Delivery_Delivery.delivery_status == 0)
.all())
delivery_schema = Delivery_Delivery_schema(many=True)
return jsonify(delivery_schema.dump(get_delivery))
@deliverystatus.route("/count/automatic", methods=["GET"])
def get_deliveries_automatic_count():
"""
"""
autos = (db.session
.query(Auto_Delivery)
.filter(Auto_Delivery.estimated_gallons_left <= 80)
.count())
return jsonify({
"ok": True,
'count':autos,
}), 200
@deliverystatus.route("/count/waiting", methods=["GET"])
def get_deliveries_waiting_count():
"""
waiting deliveries scheduled out
"""
tomm = date.today() + timedelta(days=1)
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 0)
.count())
return jsonify({
"ok": True,
'count':get_delivery,
}), 200
@deliverystatus.route("/count/pending", methods=["GET"])
def get_deliveries_pending_count():
"""
"""
get_delivery = (db.session
.query(Delivery_Delivery)
.filter(Delivery_Delivery.delivery_status == 9)
.count())
return jsonify({
"ok": True,
'count':get_delivery,
}), 200
except Exception as e:
# Basic error handling
return jsonify({"ok": False, "error": str(e)}), 500

View File

@@ -4,8 +4,8 @@ from app.info import info
from app import db
from app.classes.pricing import Pricing_Oil_Oil, Pricing_Oil_Oil_schema
from app.classes.admin import Admin_Company
from app.classes.delivery import Delivery_Delivery
from app.classes.service import Service_Service
@info.route("/price/oil/tiers", methods=["GET"])

View File

@@ -94,53 +94,6 @@ def get_user_specific_card(card_id):
return jsonify(card_schema.dump(get_user_card))
@payment.route("/card/create/<int:user_id>", methods=["POST"])
def create_user_card(user_id):
"""
adds a card of a user
"""
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == user_id)
.first())
# --- FIX: Use .get() for safety and get the correct key 'name_on_card' ---
data = request.get_json()
name_on_card = data.get("name_on_card") # <-- CORRECT KEY
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
card_number = data.get("card_number")
# --- FIX: Correctly slice the last four digits ---
last_four = card_number[-4:] if card_number else ""
create_new_card = Card_Card(
user_id=get_customer.id,
card_number=card_number,
last_four_digits=last_four,
name_on_card=name_on_card,
expiration_month=expiration_month,
expiration_year=expiration_year,
type_of_card=type_of_card,
security_number=security_number,
accepted_or_declined=None,
main_card=main_card,
zip_code=zip_code
)
db.session.add(create_new_card)
db.session.flush()
if main_card:
set_card_main(user_id=get_customer.id, card_id=create_new_card.id)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/main/<int:card_id>/<int:user_id>", methods=["PUT"])
def set_main_card(user_id, card_id):
@@ -171,50 +124,6 @@ def set_main_card(user_id, card_id):
return jsonify({"ok": True}), 200
@payment.route("/card/edit/<int:card_id>", methods=["PUT"])
def update_user_card(card_id):
"""
edits a card
"""
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_id)
.first())
if not get_card:
return jsonify({"ok": False, "error": "Card not found"}), 404
# --- FIX: Use .get() for safety and get the correct key 'name_on_card' ---
data = request.get_json()
name_on_card = data.get("name_on_card") # <-- CORRECT KEY
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
card_number = data.get("card_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
get_card.card_number = card_number
get_card.name_on_card = name_on_card
get_card.expiration_month = expiration_month
get_card.expiration_year = expiration_year
get_card.type_of_card = type_of_card
get_card.security_number = security_number
get_card.main_card = main_card
get_card.zip_code = zip_code
# --- FIX: Correctly slice the last four digits on edit ---
if card_number:
get_card.last_four_digits = card_number[-4:]
if main_card:
set_card_main(user_id=get_card.user_id, card_id=get_card.id)
db.session.add(get_card)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/remove/<int:card_id>", methods=["DELETE"])
@@ -232,3 +141,96 @@ def remove_user_card(card_id):
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/create/<int:user_id>", methods=["POST"])
def create_user_card(user_id):
"""
adds a card of a user
"""
get_customer = (db.session
.query(Customer_Customer)
.filter(Customer_Customer.id == user_id)
.first())
data = request.get_json()
# FIX: Use .get() for safety and get the correct key 'name_on_card'
name_on_card = data.get("name_on_card") # <-- This now matches the frontend
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
card_number = data.get("card_number")
# FIX: Correctly slice the last four digits
last_four = card_number[-4:] if card_number else ""
create_new_card = Card_Card(
user_id=get_customer.id,
card_number=card_number,
last_four_digits=last_four, # <-- Use the correctly sliced value
name_on_card=name_on_card,
expiration_month=expiration_month,
expiration_year=expiration_year,
type_of_card=type_of_card,
security_number=security_number,
accepted_or_declined=None,
main_card=main_card,
zip_code=zip_code
)
db.session.add(create_new_card)
db.session.flush()
if main_card:
set_card_main(user_id=get_customer.id, card_id=create_new_card.id)
db.session.commit()
return jsonify({"ok": True}), 200
@payment.route("/card/edit/<int:card_id>", methods=["PUT"])
def update_user_card(card_id):
"""
edits a card
"""
get_card = (db.session
.query(Card_Card)
.filter(Card_Card.id == card_id)
.first())
if not get_card:
return jsonify({"ok": False, "error": "Card not found"}), 404
data = request.get_json()
# FIX: Use .get() for safety and get the correct key 'name_on_card'
name_on_card = data.get("name_on_card") # <-- This now matches the frontend
expiration_month = data.get("expiration_month")
expiration_year = data.get("expiration_year")
type_of_card = data.get("type_of_card")
security_number = data.get("security_number")
card_number = data.get("card_number")
main_card = data.get("main_card", False)
zip_code = data.get("zip_code")
get_card.card_number = card_number
get_card.name_on_card = name_on_card
get_card.expiration_month = expiration_month
get_card.expiration_year = expiration_year
get_card.type_of_card = type_of_card
get_card.security_number = security_number
get_card.main_card = main_card
get_card.zip_code = zip_code
# FIX: Correctly slice the last four digits on edit
if card_number:
get_card.last_four_digits = card_number[-4:]
if main_card:
set_card_main(user_id=get_card.user_id, card_id=get_card.id)
db.session.add(get_card)
db.session.commit()
return jsonify({"ok": True}), 200

View File

@@ -16,7 +16,8 @@ def load_config(mode=os.environ.get('MODE')):
from settings_local import ApplicationConfig
return ApplicationConfig
else:
pass
from settings_prod import ApplicationConfig
return ApplicationConfig
except ImportError:
from settings_local import ApplicationConfig

View File

@@ -9,6 +9,7 @@ class ApplicationConfig:
POSTGRES_USERNAME = 'postgres'
POSTGRES_PW = 'password'
POSTGRES_SERVER = '192.168.1.204:5432'
POSTGRES_DBNAME00 = 'auburnoil'
SQLALCHEMY_DATABASE_URI = "postgresql+psycopg2://{}:{}@{}/{}".format(POSTGRES_USERNAME,
POSTGRES_PW,