-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
382fa28
commit 032eb55
Showing
5 changed files
with
430 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,19 @@ | ||
# Utilisez l'image officielle de Python comme image de base | ||
FROM python:3.12-slim | ||
FROM python:3.12 | ||
|
||
# Définir le répertoire de travail dans le conteneur | ||
# Install dependencies | ||
WORKDIR /app | ||
COPY requirements.txt requirements.txt | ||
RUN pip install --no-cache-dir -r requirements.txt | ||
|
||
# Copier les fichiers de l'application dans le répertoire de travail | ||
COPY . /app | ||
# Copy application code | ||
COPY . . | ||
|
||
# Installer les dépendances | ||
RUN pip install --no-cache-dir -r requirements.txt | ||
# Set environment variables | ||
ENV FLASK_APP=app.py | ||
|
||
# Exposer le port sur lequel l'application va fonctionner | ||
# Expose port | ||
EXPOSE 5000 | ||
|
||
# Définir la commande par défaut pour exécuter l'application | ||
CMD ["gunicorn", "-w", "3", "-b", "0.0.0.0:5000", "app:app"] | ||
# Run the command | ||
CMD ["bash", "-c", "python initialize_db.py && gunicorn --bind 0.0.0.0:5000 app:app"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
# app.py | ||
|
||
from flask import Flask, render_template, request, redirect, url_for, flash, session, send_from_directory | ||
from flask_sqlalchemy import SQLAlchemy | ||
from werkzeug.security import generate_password_hash, check_password_hash | ||
from werkzeug.utils import secure_filename | ||
from datetime import datetime | ||
import os | ||
import re | ||
from models import db, User, Article | ||
from config import db_config, SECRET_KEY | ||
|
||
app = Flask(__name__) | ||
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql+mysqlconnector://{db_config['user']}:{db_config['password']}@{db_config['host']}/{db_config['database']}" | ||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False | ||
app.config['SECRET_KEY'] = SECRET_KEY | ||
|
||
# Configurer l'upload d'images | ||
UPLOAD_FOLDER = 'static/uploads' | ||
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} | ||
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER | ||
|
||
db.init_app(app) | ||
|
||
def allowed_file(filename): | ||
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS | ||
|
||
@app.route('/') | ||
def home(): | ||
category = request.args.get('category') | ||
if category: | ||
articles = Article.query.filter_by(category=category).order_by(Article.created_at.desc()).all() | ||
else: | ||
articles = Article.query.order_by(Article.created_at.desc()).all() | ||
return render_template('home.html', articles=articles) | ||
|
||
@app.route('/article/<int:article_id>') | ||
def article(article_id): | ||
article = Article.query.get_or_404(article_id) | ||
return render_template('article.html', article=article) | ||
|
||
@app.route('/article/delete/<int:article_id>', methods=['POST']) | ||
def delete_article(article_id): | ||
article = Article.query.get_or_404(article_id) | ||
if article.author_id != session.get('user_id'): | ||
flash('Vous ne pouvez supprimer que vos propres articles.') | ||
return redirect(url_for('home')) | ||
db.session.delete(article) | ||
db.session.commit() | ||
flash('Article supprimé avec succès.') | ||
return redirect(url_for('home')) | ||
|
||
@app.route('/article/edit/<int:article_id>', methods=['GET', 'POST']) | ||
def edit_article(article_id): | ||
article = Article.query.get_or_404(article_id) | ||
if article.author_id != session.get('user_id'): | ||
flash('Vous ne pouvez modifier que vos propres articles.') | ||
return redirect(url_for('home')) | ||
|
||
if request.method == 'POST': | ||
title = request.form['title'] | ||
category = request.form['category'] | ||
content = request.form['content'] | ||
file = request.files['image'] | ||
|
||
if not title or not category or not content: | ||
flash('Tous les champs sont obligatoires.') | ||
elif len(content.split()) > 1000: | ||
flash('Le contenu dépasse la limite maximale de 1000 mots.') | ||
elif file and allowed_file(file.filename): | ||
if file.mimetype not in ['image/jpeg', 'image/png', 'image/gif']: | ||
flash('Format d\'image non supporté.') | ||
elif len(file.read()) > 1 * 1024 * 1024: # 1 Mo | ||
flash('La taille de l\'image dépasse 1 Mo.') | ||
else: | ||
filename = secure_filename(file.filename) | ||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) | ||
file.seek(0) | ||
file.save(filepath) | ||
article.image_path = filepath | ||
article.title = title | ||
article.category = category | ||
article.content = content | ||
article.updated_at = datetime.utcnow() | ||
|
||
db.session.commit() | ||
flash('Article modifié avec succès !') | ||
return redirect(url_for('article', article_id=article.id)) | ||
|
||
return render_template('edit_article.html', article=article) | ||
|
||
@app.route('/register', methods=['GET', 'POST']) | ||
def register(): | ||
if request.method == 'POST': | ||
username = request.form['username'] | ||
email = request.form['email'] | ||
password = request.form['password'] | ||
if not re.match(r"[^@]+@[^@]+\.[^@]+", email): | ||
flash('Adresse email invalide.') | ||
return redirect(url_for('register')) | ||
hashed_password = generate_password_hash(password, method='pbkdf2:sha256') | ||
|
||
new_user = User(username=username, email=email, password=hashed_password) | ||
db.session.add(new_user) | ||
db.session.commit() | ||
flash('Inscription réussie ! Veuillez vous connecter.') | ||
return redirect(url_for('login')) | ||
return render_template('register.html') | ||
|
||
@app.route('/login', methods=['GET', 'POST']) | ||
def login(): | ||
if request.method == 'POST': | ||
email = request.form['email'] | ||
password = request.form['password'] | ||
user = User.query.filter_by(email=email).first() | ||
|
||
if user and check_password_hash(user.password, password): | ||
session['user_id'] = user.id | ||
session['username'] = user.username | ||
flash('Connexion réussie !') | ||
return redirect(url_for('home')) | ||
else: | ||
flash('Identifiants invalides. Veuillez réessayer.') | ||
return render_template('login.html') | ||
|
||
@app.route('/logout') | ||
def logout(): | ||
session.pop('user_id', None) | ||
session.pop('username', None) | ||
flash('Vous avez été déconnecté.') | ||
return redirect(url_for('home')) | ||
|
||
@app.route('/write', methods=['GET', 'POST']) | ||
def write(): | ||
if 'user_id' not in session: | ||
flash('Vous devez être connecté pour écrire un article.') | ||
return redirect(url_for('login')) | ||
|
||
if request.method == 'POST': | ||
title = request.form['title'] | ||
category = request.form['category'] | ||
content = request.form['content'] | ||
file = request.files['image'] | ||
|
||
if not title or not category or not content: | ||
flash('Tous les champs sont obligatoires.') | ||
elif len(content.split()) > 1000: | ||
flash('Le contenu dépasse la limite maximale de 1000 mots.') | ||
elif file and allowed_file(file.filename): | ||
if file.mimetype not in ['image/jpeg', 'image/png', 'image/gif']: | ||
flash('Format d\'image non supporté.') | ||
elif len(file.read()) > 1 * 1024 * 1024: # 1 Mo | ||
flash('La taille de l\'image dépasse 1 Mo.') | ||
else: | ||
filename = secure_filename(file.filename) | ||
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) | ||
file.seek(0) | ||
file.save(filepath) | ||
|
||
new_article = Article( | ||
title=title, | ||
category=category, | ||
content=content, | ||
author_id=session['user_id'], | ||
image_path=filepath | ||
) | ||
db.session.add(new_article) | ||
db.session.commit() | ||
flash('Article publié avec succès !') | ||
return redirect(url_for('home')) | ||
|
||
return render_template('write.html') | ||
|
||
@app.route('/uploads/<filename>') | ||
def uploaded_file(filename): | ||
return send_from_directory(app.config['UPLOAD_FOLDER'], filename) | ||
|
||
if __name__ == '__main__': | ||
app.run(debug=True, host='0.0.0.0') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
services: | ||
web: | ||
build: . | ||
ports: | ||
- "5000:5000" | ||
environment: | ||
- DB_HOST=${DB_HOST} | ||
- DB_USER=${DB_USER} | ||
- DB_PASSWORD=${DB_PASSWORD} | ||
- DB_NAME=${DB_NAME} | ||
- SECRET_KEY=${SECRET_KEY} | ||
depends_on: | ||
- db | ||
command: bash -c "python initialize_db.py && gunicorn --bind 0.0.0.0:5000 app:app" | ||
env_file: | ||
- .env | ||
|
||
db: | ||
image: mysql:8.0 | ||
environment: | ||
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} | ||
MYSQL_DATABASE: ${DB_NAME} | ||
MYSQL_USER: ${DB_USER} | ||
MYSQL_PASSWORD: ${DB_PASSWORD} | ||
ports: | ||
- "3306:3306" | ||
volumes: | ||
- db_data:/var/lib/mysql | ||
env_file: | ||
- .env | ||
volumes: | ||
db_data: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# initialize_db.py | ||
|
||
import time | ||
import mysql.connector | ||
from app import db, app | ||
|
||
def wait_for_db(): | ||
while True: | ||
try: | ||
conn = mysql.connector.connect( | ||
host=app.config['SQLALCHEMY_DATABASE_URI'].split('@')[1].split('/')[0].split(':')[0], | ||
user=app.config['SQLALCHEMY_DATABASE_URI'].split('//')[1].split(':')[0], | ||
password=app.config['SQLALCHEMY_DATABASE_URI'].split(':')[2].split('@')[0] | ||
) | ||
conn.close() | ||
break | ||
except mysql.connector.Error as err: | ||
print("Waiting for MySQL to be ready...") | ||
time.sleep(5) | ||
|
||
wait_for_db() | ||
|
||
with app.app_context(): | ||
db.create_all() | ||
|
Oops, something went wrong.