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 ...@@ -30,10 +30,19 @@ pytest -q
Run the app: Run the app:
```bash ```bash
export MYSQL_USER=root flask --app app run
export MYSQL_PASSWORD=yourpass ```
export MYSQL_DB=car_rental
python app.py 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) Database migrations (Flask-Migrate / Alembic)
......
...@@ -7,201 +7,193 @@ from models import db, Car, Reservation, CarType ...@@ -7,201 +7,193 @@ from models import db, Car, Reservation, CarType
from flask_migrate import Migrate from flask_migrate import Migrate
def create_app(): app = Flask(__name__)
app = Flask(__name__) app.config.from_object(Config)
app.config.from_object(Config) db.init_app(app)
db.init_app(app) # Migrations
# Migrations Migrate(app, db)
Migrate(app, db)
@app.errorhandler(404)
@app.errorhandler(404) def not_found(e):
def not_found(e): return jsonify({'error': 'Not found'}), 404
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(400)
@app.errorhandler(400) def bad_request(e):
def bad_request(e): return jsonify({'error': 'Bad request', 'message': str(e)}), 400
return jsonify({'error': 'Bad request', 'message': str(e)}), 400
@app.route('/health', methods=['GET'])
@app.route('/health', methods=['GET']) def health():
def health(): return jsonify({'status': 'ok'})
return jsonify({'status': 'ok'})
@app.route('/cars', methods=['GET'])
@app.route('/cars', methods=['GET']) def list_cars():
def list_cars(): cars = Car.query.all()
cars = Car.query.all() return jsonify([c.to_dict() for c in cars])
return jsonify([c.to_dict() for c in cars])
@app.route('/cars/<int:car_id>', methods=['GET'])
@app.route('/cars/<int:car_id>', methods=['GET']) def get_car(car_id):
def get_car(car_id): car = Car.query.get_or_404(car_id)
car = Car.query.get_or_404(car_id) return jsonify(car.to_dict())
return jsonify(car.to_dict())
@app.route('/cars', methods=['POST'])
@app.route('/cars', methods=['POST']) def create_car():
def create_car(): data = request.get_json() or {}
data = request.get_json() or {} required = ['name', 'seats', 'ports', 'type', 'brand', 'license_plate']
required = ['name', 'seats', 'ports', 'type', 'brand', 'license_plate'] for f in required:
for f in required: if f not in data:
if f not in data: abort(400, f"Missing field: {f}")
abort(400, f"Missing field: {f}")
# Validate ports
# 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): if data['ports'] not in (3, 5):
abort(400, 'ports must be either 3 or 5') abort(400, 'ports must be either 3 or 5')
car.ports = int(data['ports'])
# Validate type if 'type' in data:
try: try:
car_type = CarType(data['type']) car.type = CarType(data['type'])
except ValueError: except ValueError:
abort(400, "type must be one of 'suv', 'small', 'van'") 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 try:
car = Car( db.session.commit()
name=data['name'], except Exception as e:
seats=int(data['seats']), db.session.rollback()
ports=int(data['ports']), abort(400, f'Could not update car: {e}')
type=car_type,
brand=data['brand'], return jsonify(car.to_dict())
license_plate=data['license_plate'],
) @app.route('/cars/<int:car_id>', methods=['DELETE'])
db.session.add(car) def delete_car(car_id):
try: car = Car.query.get_or_404(car_id)
db.session.commit() db.session.delete(car)
except Exception as e: db.session.commit()
db.session.rollback() return jsonify({'deleted': car_id})
abort(400, f'Could not create car: {e}')
# Reservations
return jsonify(car.to_dict()), 201 @app.route('/reservations', methods=['GET'])
def list_reservations():
@app.route('/cars/<int:car_id>', methods=['PUT']) res = Reservation.query.all()
def update_car(car_id): return jsonify([r.to_dict() for r in res])
car = Car.query.get_or_404(car_id)
data = request.get_json() or {} @app.route('/reservations/<int:res_id>', methods=['GET'])
if 'name' in data: def get_reservation(res_id):
car.name = data['name'] r = Reservation.query.get_or_404(res_id)
if 'seats' in data: return jsonify(r.to_dict())
car.seats = int(data['seats'])
if 'ports' in data: @app.route('/reservations', methods=['POST'])
if data['ports'] not in (3, 5): def create_reservation():
abort(400, 'ports must be either 3 or 5') data = request.get_json() or {}
car.ports = int(data['ports']) required = ['pickup_date', 'return_date', 'car_id', 'customer_fullname', 'drive_license']
if 'type' in data: for f in required:
try: if f not in data:
car.type = CarType(data['type']) abort(400, f"Missing field: {f}")
except ValueError:
abort(400, "type must be one of 'suv', 'small', 'van'") try:
if 'brand' in data: pickup = datetime.fromisoformat(data['pickup_date'])
car.brand = data['brand'] ret = datetime.fromisoformat(data['return_date'])
if 'license_plate' in data: except Exception:
car.license_plate = data['license_plate'] abort(400, 'pickup_date and return_date must be ISO datetime strings')
try: if ret <= pickup:
db.session.commit() abort(400, 'return_date must be after pickup_date')
except Exception as e:
db.session.rollback() car = Car.query.get(data['car_id'])
abort(400, f'Could not update car: {e}') if not car:
abort(400, 'car_id does not exist')
return jsonify(car.to_dict())
reservation = Reservation(
@app.route('/cars/<int:car_id>', methods=['DELETE']) pickup_date=pickup,
def delete_car(car_id): return_date=ret,
car = Car.query.get_or_404(car_id) car_id=int(data['car_id']),
db.session.delete(car) customer_fullname=data['customer_fullname'],
drive_license=data['drive_license'],
)
db.session.add(reservation)
try:
db.session.commit() db.session.commit()
return jsonify({'deleted': car_id}) except Exception as e:
db.session.rollback()
# Reservations abort(400, f'Could not create reservation: {e}')
@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}")
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: try:
pickup = datetime.fromisoformat(data['pickup_date']) r.pickup_date = datetime.fromisoformat(data['pickup_date'])
ret = datetime.fromisoformat(data['return_date'])
except Exception: except Exception:
abort(400, 'pickup_date and return_date must be ISO datetime strings') abort(400, 'pickup_date must be ISO datetime string')
if 'return_date' in data:
if ret <= pickup: try:
abort(400, 'return_date must be after pickup_date') 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']) car = Car.query.get(data['car_id'])
if not car: if not car:
abort(400, 'car_id does not exist') 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( if r.return_date <= r.pickup_date:
pickup_date=pickup, abort(400, 'return_date must be after pickup_date')
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())
@app.route('/reservations/<int:res_id>', methods=['DELETE']) try:
def delete_reservation(res_id):
r = Reservation.query.get_or_404(res_id)
db.session.delete(r)
db.session.commit() db.session.commit()
return jsonify({'deleted': res_id}) except Exception as e:
db.session.rollback()
return app abort(400, f'Could not update reservation: {e}')
return jsonify(r.to_dict())
if __name__ == '__main__':
app = create_app() @app.route('/reservations/<int:res_id>', methods=['DELETE'])
app.run(host=os.getenv('FLASK_RUN_HOST', '0.0.0.0'), port=int(os.getenv('FLASK_RUN_PORT', 5000)), debug=True) 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 ...@@ -4,5 +4,4 @@ PyMySQL>=1.0
cryptography==46.0.3 cryptography==46.0.3
Flask-Migrate>=4.0 Flask-Migrate>=4.0
pytest>=7.0 pytest>=7.0
pytest-flask>=1.2 pytest-flask>=1.2
gunicorn>=20.0 \ No newline at end of file
\ 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