main
Grégory Lebreton 7 months ago
commit b8fde88b58

@ -0,0 +1,12 @@
FROM python:3.11-slim-bookworm
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY app/. .
CMD ["gunicorn", "-w", "4", "wsgi:app", "--bind", "0.0.0.0:8000"]

@ -0,0 +1,96 @@
# BRANCH API
Base for create an API for french quotes in flask with SQL Alchemy for backend
## CONFIGURATION :wrench:
- In app/app.py configure admin_api_key and user_api_key
## UTILISATION :checkered_flag:
- créer un evironnement virtuel:
```bash
sudo apt install python3-venv
python3 -m venv venv
```
- Installation des dépendances
```bash
pip install -r requirements.txt
cd app
flask run
```
> Récupérer l'api_key admin qui s'affiche dans le terminal
- Ajouter les deux citations test:
```bash
curl -X POST -H "Content-Type: application/json; charset=utf-8" -H "API-Key: <admin_api_key>" -d '{"auteur": "François de La Rochefoucauld", "citation": "Il est plus aisé detre sage pour les autres que de letre pour soi-même."}' http://localhost:5000/api/citations/add
curl -X POST -H "Content-Type: application/json; charset=utf-8" -H "API-Key: <admin_api_key>" -d '{"auteur": "Pierre De Montesquieu", "citation": "On ne justifie pas une injustice par une autre."}' http://localhost:5000/api/citations/add
```
- Ajouter une liste de citations:
```bash
curl -X POST -H "Content-Type: application/json; charset=utf-8" -H "API-Key: <admin_api_key>" -d '{
"citations": [
{"auteur": "Auteur1", "citation": "Citation1"},
{"auteur": "Auteur2", "citation": "Citation2"},
{"auteur": "Auteur3", "citation": "Citation3"}
]
}' http://localhost:5000/api/citations/add_list
```
- API admin:
```bash
curl -H "API-Key: <admin_api_key>" http://localhost:5000/api/citations/all | jq
curl -H "API-Key: <admin_api_key>" http://localhost:5000/api/citations/random | jq
```
- Ajouter un utilisateur non admin:
```bash
curl -X POST -H "Content-Type: application/json; charset=utf-8" -H "API-Key: <admin_api_key>" -d '{"username": "nouvel_utilisateur", "is_admin": false}' http://localhost:5000/api/users/add
```
- Supprimer un utilisateur:
```bash
curl -X DELETE -H "Content-Type: application/json; charset=utf-8" -H "Api-Key: <admin_api_key>" -d '{"username": "nom_utilisateur_a_supprimer"}' http://localhost:5000/api/users/delete
```
> Récupérer l'api_key qui s'affiche pour le nouvel utilisateur
- API user:
```bash
curl -H "API-Key: <nouvel_utilisateur_api_key>" http://localhost:5000/api/citations/random | jq
```
:bulb: Utiliser le script ajout_citations.sh pour ajouter plusieurs citations en une fois
## DOCKER :whale:
- Construire l'image Docker et démarrer un conteneur:
```bash
docker build -t api:1.0 .
docker run -d -p 8000:8000 api:1.0
```
## DOCKER COMPOSE
- Démarrer l'API:
```bash
docker-compose up -d
```
## TO DO :bookmark_tabs:
- [X] fonction check si citation existe deja
- [X] fonction add user
- [X] test added user
- [X] fonction ajout_citations(list)
- [X] tester Docker
- [X] gérer l'UTF8
- [X] endpoint del user

@ -0,0 +1,32 @@
#!/bin/bash
# ADMIN KEY
API_KEY="A RECUPERER DES LOGS DOCKER"
########################## LIST CITATIONS ##########################
API_ENDPOINT="http://localhost:8000/api/citations/add_list"
CITATIONS='{
"citations": [
{"auteur": "Maya Angelo", "citation": "La vie n´est pas mesurée par le nombre de respirations que nous prenons, mais par les moments qui nous coupent le souffle."},
{"auteur": "Leonardo da Vinci", "citation": "La simplicité est la sophistication ultime."},
{"auteur": "Albert Einstein", "citation": "La folie, c´est de faire la même chose et de s´attendre à un résultat différent."},
{"auteur": "Mark Twain", "citation": "La gentillesse est la langue que les sourds peuvent entendre et les aveugles peuvent voir."},
{"auteur": "Henry Ford", "citation": "L´échec est simplement l´opportunité de recommencer, cette fois d´une manière plus intelligente."},
{"auteur": "Oscar Wilde", "citation": "La vie est trop importante pour être prise au sérieux."},
{"auteur": "Neale Donald Walsch", "citation": "La vie commence à la fin de votre zone de confort."},
{"auteur": "Mahatma Gandhi", "citation": "Soyez le changement que vous voulez voir dans le monde."},
{"auteur": "Muhammad Ali", "citation": "Ne comptez pas les jours, faites que les jours comptent."},
{"auteur": "Sénèque", "citation": "La vie n´est pas d´attendre que l´orage passe, mais d´apprendre à danser sous la pluie."},
{"auteur": "Steve Jobs", "citation": "Votre temps est limité, ne le gaspillez pas à vivre la vie de quelqu´un d´autre."},
{"auteur": "Ralph Marston", "citation": "Le bonheur est un choix, pas une résultante. Rien ne vous rend heureux à moins que vous ne décidiez de l´être."},
{"auteur": "Vidal Sassoon", "citation": "Le seul endroit où le succès vient avant le travail, c´est dans le dictionnaire."},
{"auteur": "Dalaï Lama", "citation": "Il n´y a personne qui soit né sous une mauvaise étoile, il n´y a que des gens qui ne savent pas lire le ciel."}
]
}'
# AJOUT CITATION
curl -X POST -H "Content-Type: application/json; charset=utf-8" -H "API-Key: $API_KEY" -d "$CITATIONS" "$API_ENDPOINT"
# VERIF ALL CITATIONS
curl -H "API-Key: $API_KEY" http://localhost:8000/api/citations/all | jq

@ -0,0 +1,203 @@
from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql.expression import func
import secrets
################################ CONFIG ################################
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///citations.db'
db = SQLAlchemy(app)
################################ CLASS ################################
# CLASS CITATION
class Citation(db.Model):
id = db.Column(db.Integer, primary_key=True)
auteur = db.Column(db.String(50), nullable=False)
citation = db.Column(db.String(500), nullable=False)
# CLASS USER
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
api_key = db.Column(db.String(64), unique=True, nullable=False)
is_admin = db.Column(db.Boolean, default=False)
# CREATE ADMIN FIRST TIME
def initialize_admin_api_key():
# Verif si l'admin existe déjà
admin_user = User.query.filter_by(username='admin').first()
if not admin_user:
# Si l'admin n'existe pas, générer une nouvelle clé API et l'ajouter à la bdd
admin_api_key = secrets.token_urlsafe(32)
admin_user = User(username='admin', api_key=admin_api_key, is_admin=True)
db.session.add(admin_user)
db.session.commit()
print(f"Clé API de l'administrateur : {admin_api_key}")
# REQUIRE API KEY WRAPPER
def require_api_key(*api_types):
def decorator(func):
def wrapper(*args, **kwargs):
provided_key = request.headers.get('API-Key')
user = User.query.filter_by(api_key=provided_key).first()
if user:
if 'admin' in api_types and user.is_admin:
return func(*args, **kwargs)
elif 'user' in api_types:
return func(*args, **kwargs)
return jsonify({"error": "Invalid API key or insufficient permissions"}), 401
return wrapper
return decorator
# CREATE DB
with app.app_context():
db.create_all()
initialize_admin_api_key()
################################ ROUTES ################################
## RANDOM CITATION
@app.route('/api/citations/random', methods=['GET'], endpoint='random')
@require_api_key('user', 'admin')
def get_citation():
citation = Citation.query.order_by(func.random()).first()
citation = {"auteur": citation.auteur, "citation": citation.citation}
response = jsonify({"citations": citation})
response.headers['Content-Type'] = 'application/json; charset=utf-8'
return response
## GET ALL CITATION
@app.route('/api/citations/all', methods=['GET'], endpoint='all')
@require_api_key('admin')
def get_all_citations():
citations = Citation.query.all()
citations_list = [{"auteur": citation.auteur, "citation": citation.citation} for citation in citations]
response = jsonify({"citations": citations_list})
response.headers['Content-Type'] = 'application/json; charset=utf-8'
return response
## AJOUT CITATION
@app.route('/api/citations/add', methods=['POST'], endpoint='ajout')
@require_api_key('admin')
def add_citation():
try:
data = request.get_json()
new_auteur = data['auteur']
new_citation_text = data['citation']
existing_citation = Citation.query.filter_by(auteur=new_auteur, citation=new_citation_text).first()
if existing_citation:
return jsonify({"error": "Cette citation existe deja dans la base de donnees"}), 400
new_citation = Citation(auteur=data['auteur'], citation=data['citation'])
db.session.add(new_citation)
db.session.commit()
except Exception as e:
return jsonify({"error": str(e)}), 500
response = jsonify({"message": "Citation ajoutee avec succes!"})
response.headers['Content-Type'] = 'application/json; charset=utf-8'
return response
## AJOUT LISTE DE CITATIONS
@app.route('/api/citations/add_list', methods=['POST'], endpoint='ajout_liste')
@require_api_key('admin')
def add_citations_from_list():
try:
data = request.get_json()
citations_to_add = data.get('citations')
if not citations_to_add or not isinstance(citations_to_add, list):
response = jsonify({"error": "Veuillez fournir une liste valide de citations"}), 400
return response
citations = []
for citation_data in citations_to_add:
new_auteur = citation_data.get('auteur')
new_citation_text = citation_data.get('citation')
if not new_auteur or not new_citation_text:
response = jsonify({"error": "Chaque element de la liste doit avoir des cles 'auteur' et 'citation'"}), 400
return response
existing_citation = Citation.query.filter_by(auteur=new_auteur, citation=new_citation_text).first()
if existing_citation:
response = jsonify({"error": f"La citation de {new_auteur} existe deja dans la base de donnees"}), 400
return response
new_citation = Citation(auteur=new_auteur, citation=new_citation_text)
citations.append(new_citation)
db.session.add_all(citations)
db.session.commit()
except Exception as e:
return jsonify({"error": str(e)}), 500
response = jsonify({"message": "Citations ajoutees avec succes depuis la liste!"})
response.headers['Content-Type'] = 'application/json; charset=utf-8'
return response
################################ AJOUT USER ################################
@app.route('/api/users/add', methods=['POST'], endpoint='add_user')
@require_api_key('admin')
def add_user():
try:
data = request.get_json()
new_username = data['username']
is_admin = data.get('is_admin', False)
existing_user = User.query.filter_by(username=new_username).first()
if existing_user:
return jsonify({"error": "Cet utilisateur existe déjà"}), 400
new_api_key = secrets.token_urlsafe(32)
new_user = User(username=new_username, api_key=new_api_key, is_admin=is_admin)
db.session.add(new_user)
db.session.commit()
return jsonify({"username": new_username, "api_key": new_api_key, "is_admin": is_admin})
except Exception as e:
return jsonify({"error": str(e)}), 500
################################ DEL USER ################################
@app.route('/api/users/delete', methods=['DELETE'], endpoint='delete_user')
@require_api_key('admin')
def delete_user():
try:
data = request.get_json()
username_to_delete = data.get('username')
if not username_to_delete:
return jsonify({"error": "Le nom d'utilisateur est requis"}), 400
user_to_delete = User.query.filter_by(username=username_to_delete).first()
if not user_to_delete:
return jsonify({"error": "Cet utilisateur n'existe pas"}), 404
db.session.delete(user_to_delete)
db.session.commit()
return jsonify({"message": "Utilisateur " + username_to_delete + " supprimé avec succès"})
except Exception as e:
return jsonify({"error": str(e)}), 500
################################ MAIN ################################
if __name__ == '__main__':
app.run(debug=True)

@ -0,0 +1,4 @@
from app import app
if __name__ == '__main__':
app.run(debug=True)

@ -0,0 +1,11 @@
version: '3.8'
services:
api:
build: .
container_name: ostro
restart: unless-stopped
ports:
- 8000:8000
volumes:
- ./app:/app/instance

@ -0,0 +1,6 @@
Flask==2.2.5
Flask-SQLalchemy==3.1.1
requests==2.28.1
gunicorn==21.2.0
Werkzeug==2.3.7

@ -0,0 +1,4 @@
from app import app
if __name__ == '__main__':
app.run(debug=True)
Loading…
Cancel
Save