Commit b0e07009 authored by Michele Del Giudice's avatar Michele Del Giudice
Browse files

Fixed simple dockerfile

parent 1a6781bc
FROM python:3.11-slim
WORKDIR /app
# Install build deps needed for some packages (cryptography, etc.) then remove them
RUN apt-get update && \
apt-get install -y --no-install-recommends build-essential libssl-dev libffi-dev curl ca-certificates && \
rm -rf /var/lib/apt/lists/*
COPY requirements.txt ./
RUN pip install --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . /app
#ENV FLASK_APP=app:create_app
EXPOSE 5000
# Healthcheck: call the Flask /health endpoint
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \
CMD curl -fS http://127.0.0.1:5000/health || exit 1
CMD ["flask", "--app" , "app", "run", "--host=0.0.0.0"]
......@@ -30,10 +30,19 @@ pytest -q
Run the app:
```bash
export MYSQL_USER=root
export MYSQL_PASSWORD=yourpass
export MYSQL_DB=car_rental
python app.py
flask --app app run
```
Run the app (on all interfaces):
```bash
flask --app app run --host=0.0.0.0
```
Run the app (on different port):
```bash
flask --app app run --port=12345
```
Database migrations (Flask-Migrate / Alembic)
......
......@@ -7,201 +7,193 @@ from models import db, Car, Reservation, CarType
from flask_migrate import Migrate
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
# Migrations
Migrate(app, db)
@app.errorhandler(404)
def not_found(e):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(400)
def bad_request(e):
return jsonify({'error': 'Bad request', 'message': str(e)}), 400
@app.route('/health', methods=['GET'])
def health():
return jsonify({'status': 'ok'})
@app.route('/cars', methods=['GET'])
def list_cars():
cars = Car.query.all()
return jsonify([c.to_dict() for c in cars])
@app.route('/cars/<int:car_id>', methods=['GET'])
def get_car(car_id):
car = Car.query.get_or_404(car_id)
return jsonify(car.to_dict())
@app.route('/cars', methods=['POST'])
def create_car():
data = request.get_json() or {}
required = ['name', 'seats', 'ports', 'type', 'brand', 'license_plate']
for f in required:
if f not in data:
abort(400, f"Missing field: {f}")
# Validate ports
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
# Migrations
Migrate(app, db)
@app.errorhandler(404)
def not_found(e):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(400)
def bad_request(e):
return jsonify({'error': 'Bad request', 'message': str(e)}), 400
@app.route('/health', methods=['GET'])
def health():
return jsonify({'status': 'ok'})
@app.route('/cars', methods=['GET'])
def list_cars():
cars = Car.query.all()
return jsonify([c.to_dict() for c in cars])
@app.route('/cars/<int:car_id>', methods=['GET'])
def get_car(car_id):
car = Car.query.get_or_404(car_id)
return jsonify(car.to_dict())
@app.route('/cars', methods=['POST'])
def create_car():
data = request.get_json() or {}
required = ['name', 'seats', 'ports', 'type', 'brand', 'license_plate']
for f in required:
if f not in data:
abort(400, f"Missing field: {f}")
# Validate ports
if data['ports'] not in (3, 5):
abort(400, 'ports must be either 3 or 5')
# Validate type
try:
car_type = CarType(data['type'])
except ValueError:
abort(400, "type must be one of 'suv', 'small', 'van'")
# Create
car = Car(
name=data['name'],
seats=int(data['seats']),
ports=int(data['ports']),
type=car_type,
brand=data['brand'],
license_plate=data['license_plate'],
)
db.session.add(car)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not create car: {e}')
return jsonify(car.to_dict()), 201
@app.route('/cars/<int:car_id>', methods=['PUT'])
def update_car(car_id):
car = Car.query.get_or_404(car_id)
data = request.get_json() or {}
if 'name' in data:
car.name = data['name']
if 'seats' in data:
car.seats = int(data['seats'])
if 'ports' in data:
if data['ports'] not in (3, 5):
abort(400, 'ports must be either 3 or 5')
# Validate type
car.ports = int(data['ports'])
if 'type' in data:
try:
car_type = CarType(data['type'])
car.type = CarType(data['type'])
except ValueError:
abort(400, "type must be one of 'suv', 'small', 'van'")
if 'brand' in data:
car.brand = data['brand']
if 'license_plate' in data:
car.license_plate = data['license_plate']
# Create
car = Car(
name=data['name'],
seats=int(data['seats']),
ports=int(data['ports']),
type=car_type,
brand=data['brand'],
license_plate=data['license_plate'],
)
db.session.add(car)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not create car: {e}')
return jsonify(car.to_dict()), 201
@app.route('/cars/<int:car_id>', methods=['PUT'])
def update_car(car_id):
car = Car.query.get_or_404(car_id)
data = request.get_json() or {}
if 'name' in data:
car.name = data['name']
if 'seats' in data:
car.seats = int(data['seats'])
if 'ports' in data:
if data['ports'] not in (3, 5):
abort(400, 'ports must be either 3 or 5')
car.ports = int(data['ports'])
if 'type' in data:
try:
car.type = CarType(data['type'])
except ValueError:
abort(400, "type must be one of 'suv', 'small', 'van'")
if 'brand' in data:
car.brand = data['brand']
if 'license_plate' in data:
car.license_plate = data['license_plate']
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not update car: {e}')
return jsonify(car.to_dict())
@app.route('/cars/<int:car_id>', methods=['DELETE'])
def delete_car(car_id):
car = Car.query.get_or_404(car_id)
db.session.delete(car)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not update car: {e}')
return jsonify(car.to_dict())
@app.route('/cars/<int:car_id>', methods=['DELETE'])
def delete_car(car_id):
car = Car.query.get_or_404(car_id)
db.session.delete(car)
db.session.commit()
return jsonify({'deleted': car_id})
# Reservations
@app.route('/reservations', methods=['GET'])
def list_reservations():
res = Reservation.query.all()
return jsonify([r.to_dict() for r in res])
@app.route('/reservations/<int:res_id>', methods=['GET'])
def get_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
return jsonify(r.to_dict())
@app.route('/reservations', methods=['POST'])
def create_reservation():
data = request.get_json() or {}
required = ['pickup_date', 'return_date', 'car_id', 'customer_fullname', 'drive_license']
for f in required:
if f not in data:
abort(400, f"Missing field: {f}")
try:
pickup = datetime.fromisoformat(data['pickup_date'])
ret = datetime.fromisoformat(data['return_date'])
except Exception:
abort(400, 'pickup_date and return_date must be ISO datetime strings')
if ret <= pickup:
abort(400, 'return_date must be after pickup_date')
car = Car.query.get(data['car_id'])
if not car:
abort(400, 'car_id does not exist')
reservation = Reservation(
pickup_date=pickup,
return_date=ret,
car_id=int(data['car_id']),
customer_fullname=data['customer_fullname'],
drive_license=data['drive_license'],
)
db.session.add(reservation)
try:
db.session.commit()
return jsonify({'deleted': car_id})
# Reservations
@app.route('/reservations', methods=['GET'])
def list_reservations():
res = Reservation.query.all()
return jsonify([r.to_dict() for r in res])
@app.route('/reservations/<int:res_id>', methods=['GET'])
def get_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
return jsonify(r.to_dict())
@app.route('/reservations', methods=['POST'])
def create_reservation():
data = request.get_json() or {}
required = ['pickup_date', 'return_date', 'car_id', 'customer_fullname', 'drive_license']
for f in required:
if f not in data:
abort(400, f"Missing field: {f}")
except Exception as e:
db.session.rollback()
abort(400, f'Could not create reservation: {e}')
return jsonify(reservation.to_dict()), 201
@app.route('/reservations/<int:res_id>', methods=['PUT'])
def update_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
data = request.get_json() or {}
if 'pickup_date' in data:
try:
pickup = datetime.fromisoformat(data['pickup_date'])
ret = datetime.fromisoformat(data['return_date'])
r.pickup_date = datetime.fromisoformat(data['pickup_date'])
except Exception:
abort(400, 'pickup_date and return_date must be ISO datetime strings')
if ret <= pickup:
abort(400, 'return_date must be after pickup_date')
abort(400, 'pickup_date must be ISO datetime string')
if 'return_date' in data:
try:
r.return_date = datetime.fromisoformat(data['return_date'])
except Exception:
abort(400, 'return_date must be ISO datetime string')
if 'car_id' in data:
car = Car.query.get(data['car_id'])
if not car:
abort(400, 'car_id does not exist')
r.car_id = int(data['car_id'])
if 'customer_fullname' in data:
r.customer_fullname = data['customer_fullname']
if 'drive_license' in data:
r.drive_license = data['drive_license']
reservation = Reservation(
pickup_date=pickup,
return_date=ret,
car_id=int(data['car_id']),
customer_fullname=data['customer_fullname'],
drive_license=data['drive_license'],
)
db.session.add(reservation)
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not create reservation: {e}')
return jsonify(reservation.to_dict()), 201
@app.route('/reservations/<int:res_id>', methods=['PUT'])
def update_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
data = request.get_json() or {}
if 'pickup_date' in data:
try:
r.pickup_date = datetime.fromisoformat(data['pickup_date'])
except Exception:
abort(400, 'pickup_date must be ISO datetime string')
if 'return_date' in data:
try:
r.return_date = datetime.fromisoformat(data['return_date'])
except Exception:
abort(400, 'return_date must be ISO datetime string')
if 'car_id' in data:
car = Car.query.get(data['car_id'])
if not car:
abort(400, 'car_id does not exist')
r.car_id = int(data['car_id'])
if 'customer_fullname' in data:
r.customer_fullname = data['customer_fullname']
if 'drive_license' in data:
r.drive_license = data['drive_license']
if r.return_date <= r.pickup_date:
abort(400, 'return_date must be after pickup_date')
try:
db.session.commit()
except Exception as e:
db.session.rollback()
abort(400, f'Could not update reservation: {e}')
return jsonify(r.to_dict())
if r.return_date <= r.pickup_date:
abort(400, 'return_date must be after pickup_date')
@app.route('/reservations/<int:res_id>', methods=['DELETE'])
def delete_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
db.session.delete(r)
try:
db.session.commit()
return jsonify({'deleted': res_id})
return app
if __name__ == '__main__':
app = create_app()
app.run(host=os.getenv('FLASK_RUN_HOST', '0.0.0.0'), port=int(os.getenv('FLASK_RUN_PORT', 5000)), debug=True)
except Exception as e:
db.session.rollback()
abort(400, f'Could not update reservation: {e}')
return jsonify(r.to_dict())
@app.route('/reservations/<int:res_id>', methods=['DELETE'])
def delete_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
db.session.delete(r)
db.session.commit()
return jsonify({'deleted': res_id})
......@@ -4,5 +4,4 @@ PyMySQL>=1.0
cryptography==46.0.3
Flask-Migrate>=4.0
pytest>=7.0
pytest-flask>=1.2
gunicorn>=20.0
\ No newline at end of file
pytest-flask>=1.2
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment