diff --git a/README.md b/README.md
index 05a0376be07d028f5beb35ecb34f9d46d354fefc..b19e00577c220b0e486297968d3ee3dec6468183 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,41 @@
-# be-sgbd-library
+# Requirements
 
+The only requirement is Docker.
+To install docker, search for your platform on [the docker documentation](https://docs.docker.com/desktop/) and follow the instructions.
 
+# Installation
 
-## Getting started
+First, build the docker images :
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
-
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
-
-## Add your files
-
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
-
-```
-cd existing_repo
-git remote add origin https://gitlab.ec-lyon.fr/holivier/be-sgbd-library.git
-git branch -M main
-git push -uf origin main
+```bash
+docker compose build
 ```
 
-## Integrate with your tools
-
-- [ ] [Set up project integrations](https://gitlab.ec-lyon.fr/holivier/be-sgbd-library/-/settings/integrations)
-
-## Collaborate with your team
-
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
-
-## Test and Deploy
-
-Use the built-in continuous integration in GitLab.
-
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+This will pull multiple images for mariadb, python and node.
+It can take some time.
 
-***
+Then initialize the database with :
 
-# Editing this README
-
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
-
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
-
-## Name
-Choose a self-explaining name for your project.
-
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+```bash
+docker compose run --rm initdb
+```
 
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+This will launch the mariadb container, load the `BE_SGBD_LIBRARY.ddl` file to create the tables, and then fill the database with test values.
 
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
+Once this command ends, you can run the following command to launch the application.
 
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
+```bash
+docker compose up react-app
+```
 
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+It will start the backend server (port 8910) and the frontend server (port 3000).
 
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+Finally, you can go to [http://localhost:3000/](http://localhost:3000/) to see the app.
 
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
+To stop every container, run :
 
-## License
-For open source projects, say how it is licensed.
+```bash
+docker compose down
+```
 
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
+Note that this will remove the database data, and running `docker compose run --rm initdb` again will be necessary to restart.
diff --git a/back/Dockerfile b/back/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..033dabba9703e358ff6cb3cffa168868718916e6
--- /dev/null
+++ b/back/Dockerfile
@@ -0,0 +1,22 @@
+FROM python:3.12-bullseye
+
+WORKDIR /app
+
+RUN wget https://r.mariadb.com/downloads/mariadb_repo_setup
+
+RUN chmod +x mariadb_repo_setup
+
+RUN ./mariadb_repo_setup
+
+RUN apt install -y libmariadb3 libmariadb-dev
+
+RUN pip install mariadb
+
+COPY ./server/requirements.txt ./
+
+RUN --mount=type=cache,target=/root/.cache/pip \
+    pip install --no-cache-dir --upgrade -r requirements.txt
+
+COPY ./server ./app
+
+CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "8000"]
\ No newline at end of file
diff --git a/back/server/main.py b/back/server/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3f1eefbd4534b404c641c199f4fc1a74635eafb
--- /dev/null
+++ b/back/server/main.py
@@ -0,0 +1,124 @@
+import logging
+import json
+import sys
+import mariadb
+from fastapi import FastAPI, Query
+from fastapi.middleware.cors import CORSMiddleware
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.DEBUG)
+
+app = FastAPI()
+app.add_middleware(
+    CORSMiddleware,
+    allow_origins=["*"],
+    allow_credentials=True,
+    allow_methods=["*"],
+    allow_headers=["*"],
+)
+
+# Connect to MariaDB Platform
+try:
+    conn = mariadb.connect(
+        user="root",
+        password="",
+        host="mariadb",
+        port=3306,
+        database="library"
+    )
+except mariadb.Error as e:
+    print(f"Error connecting to MariaDB Platform: {e}")
+    sys.exit(1)
+
+# Get Cursor
+cur = conn.cursor()
+
+
+@app.get("/app")
+async def read_main():
+    return {"message": "Hello World from main app"}
+
+@app.get("/search")
+async def search(search="",
+        docType: int = None,
+        categories: list[str] = Query([]),
+        auteur: str = "",
+        annee: int = None,
+        editeur: str = "",
+    ):
+    reponse = []
+    if docType is None or docType == 0:
+        regex = '|'.join(categories) if categories else None
+        cur.execute(f"""
+                    SELECT P.ID_PUB, P.type, titre, LIVRE.ISBN, editeur, edition, annee_de_publication,
+                    categorie1, categorie2, categorie3, categorie4,
+                    GROUP_CONCAT(A.nom ORDER BY A.nom ASC SEPARATOR ', '),
+                    COUNT(CASE WHEN Ex.statut = 0 THEN 1 END),
+                    COUNT(CASE WHEN Ex.statut = 1 THEN 1 END)
+                    FROM LIVRE
+                    JOIN library.ecrit_par ep on LIVRE.ISBN = ep.ISBN
+                    JOIN library.Auteur A on A.nom = ep.nom
+                    JOIN library.PUBLICATION P on LIVRE.ISBN = P.ISBN
+                    JOIN library.EXEMPLAIRE Ex on Ex.ID_PUB = P.ID_PUB
+                    WHERE titre LIKE %(txt)s
+                    AND editeur LIKE %(editeur)s
+                    {'AND annee_de_publication = %(annee)s' if annee else ''}
+                    {'''AND (
+                        CONCAT_WS('|', categorie1, categorie2, categorie3, categorie4)
+                        REGEXP %(regex)s
+                    )''' if categories else ""}
+                    AND A.nom LIKE %(auteur)s
+                    GROUP BY P.ID_PUB
+                    LIMIT 100;
+                    """ , {"regex":regex, "txt": f"%{search}%","auteur":f"%{auteur}%", "annee":annee, "editeur":f"%{editeur}%"})
+        print(cur.statement)
+        print({"regex":regex, "txt": f"%{search}%","auteur":f"%{auteur}%", "annee":annee, "editeur":f"%{editeur}%"})
+        print(categories)
+        reponse.extend(cur.fetchall())
+    if docType is None or docType == 1:
+        cur.execute(f"""SELECT P.ID_PUB, P.type, titre, annee_de_publication,
+                    GROUP_CONCAT(A.nom ORDER BY A.nom ASC SEPARATOR ', '),
+                    COUNT(CASE WHEN Ex.statut = 0 THEN 1 END),
+                    COUNT(CASE WHEN Ex.statut = 1 THEN 1 END)
+                    FROM RAPPORT
+                    JOIN library.redige_par rp on RAPPORT.ID_RAP = rp.ID_RAP
+                    JOIN library.Auteur A on A.nom = rp.nom
+                    JOIN library.PUBLICATION P on RAPPORT.ID_RAP = P.ID_RAP
+                    JOIN library.EXEMPLAIRE Ex on Ex.ID_PUB = P.ID_PUB
+                    WHERE titre LIKE %(txt)s
+                    {'AND annee_de_publication = %(annee)s' if annee else ''}
+                    AND A.nom LIKE %(auteur)s
+                    GROUP BY P.ID_PUB
+                    LIMIT 100;
+                    """, {"txt": f"%{search}%","auteur":f"%{auteur}%", "annee":annee,})
+        print(cur.statement)
+        reponse.extend(cur.fetchall())
+    if docType is None or docType == 2:
+        cur.execute(f"""SELECT P.ID_PUB, P.type, numero, editeur, edition, annee_de_publication,
+                    COUNT(CASE WHEN Ex.statut = 0 THEN 1 END),
+                    COUNT(CASE WHEN Ex.statut = 1 THEN 1 END)
+                    FROM PERIODIQUE
+                    JOIN library.PUBLICATION P on PERIODIQUE.ID_PER = P.ID_PER
+                    JOIN library.EXEMPLAIRE Ex on Ex.ID_PUB = P.ID_PUB
+                    WHERE (editeur LIKE %(txt)s OR editeur LIKE %(editeur)s)
+                    {'AND annee_de_publication = %(annee)s' if annee else ''}
+                    GROUP BY P.ID_PUB
+                    LIMIT 100;
+                    """, {"txt": f"%{search}%","editeur":f"%{editeur}%", "annee":annee,})
+        print(cur.statement)
+        reponse.extend(cur.fetchall())
+    return reponse
+
+@app.get("/categories")
+async def get_categories(text=""):
+    cur.execute("SELECT DISTINCT mot FROM a WHERE mot LIKE ? LIMIT 100;", (f"{text}%",))
+    res = cur.fetchall()
+    return res
+
+@app.get("/auteurs")
+async def get_auteurs(text=""):
+    cur.execute("SELECT DISTINCT nom FROM Auteur WHERE nom LIKE ? LIMIT 100;", (f"%{text}%",))
+    res = cur.fetchall()
+    logger.info(str(res))
+    print(res)
+    return res
diff --git a/back/server/requirements.txt b/back/server/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..658b8424574d98e0cd801313a2d1183d27a2d647
--- /dev/null
+++ b/back/server/requirements.txt
@@ -0,0 +1 @@
+fastapi[all]
\ No newline at end of file
diff --git a/bdd/Dockerfile b/bdd/Dockerfile
index 8d84c0af772b7443015df70c5c87bbf404fe21de..2521d6a3d3a031a35b39fe130d20e971ec6aa845 100644
--- a/bdd/Dockerfile
+++ b/bdd/Dockerfile
@@ -12,4 +12,6 @@ RUN apt install -y libmariadb3 libmariadb-dev
 
 RUN pip install mariadb
 
-CMD ["python"]
\ No newline at end of file
+COPY ./init .
+
+CMD ["python", "init.py"]
\ No newline at end of file
diff --git a/bdd/init.py b/bdd/init.py
deleted file mode 100644
index edd0577c6c838cdf04be8db57ab792c4d04b02c5..0000000000000000000000000000000000000000
--- a/bdd/init.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Module Imports
-import mariadb
-import sys
-
-# Connect to MariaDB Platform
-try:
-    conn = mariadb.connect(
-        user="root",
-        password="",
-        host="mariadb",
-        port=3306,
-        database="library"
-
-    )
-except mariadb.Error as e:
-    print(f"Error connecting to MariaDB Platform: {e}")
-    sys.exit(1)
-
-# Get Cursor
-cur = conn.cursor()
\ No newline at end of file
diff --git a/BE_SGBD_LIBRARY.ddl b/bdd/init/BE_SGBD_LIBRARY.ddl
similarity index 70%
rename from BE_SGBD_LIBRARY.ddl
rename to bdd/init/BE_SGBD_LIBRARY.ddl
index adbbf64a9baa33a81b4daa80abd4121a3a533161..ca35f6f8e3180b249ec1962d5c08a96a20b03305 100644
--- a/BE_SGBD_LIBRARY.ddl
+++ b/bdd/init/BE_SGBD_LIBRARY.ddl
@@ -12,9 +12,9 @@
 -- Database Section
 -- ________________ 
 
-DROP DATABASE IF EXISTS MAIN;
-create database MAIN;
-USE MAIN;
+DROP DATABASE IF EXISTS library;
+create database library;
+USE library;
 
 
 -- DBSpace Section
@@ -24,12 +24,12 @@ USE MAIN;
 -- Tables Section
 -- _____________ 
 
-create table a (
+create table if not exists a (
      ID_PUB bigint not null,
      mot varchar(100) not null,
      constraint ID_a_ID primary key (ID_PUB, mot));
 
-create table ACHAT (
+create table if not exists ACHAT (
      ID_EXE bigint not null,
      date timestamp not null,
      prix numeric(10,2) not null,
@@ -37,22 +37,22 @@ create table ACHAT (
      code char(3) not null,
      constraint FKprovient_d_un_ID primary key (ID_EXE));
 
-create table Auteur (
+create table if not exists Auteur (
      nom varchar(300) not null,
      constraint ID_Auteur_ID primary key (nom));
 
-create table autorise_l_acces (
+create table if not exists autorise_l_acces (
      nom varchar(300) not null,
      email varchar(300) not null,
      constraint ID_autorise_l_acces_ID primary key (email, nom));
 
-create table DEVISE (
+create table if not exists DEVISE (
      code char(3) not null,
      taux numeric(30,10) not null,
      symbole char(3) not null,
      constraint ID_DEVISE_ID primary key (code));
 
-create table EXEMPLAIRE (
+create table if not exists EXEMPLAIRE (
      ID_EXE bigint not null auto_increment,
      statut smallint not null,
      nom varchar(300) not null,
@@ -60,16 +60,16 @@ create table EXEMPLAIRE (
      ID_PUB bigint not null,
      constraint ID_ID primary key (ID_EXE));
 
-create table interesse_par (
+create table if not exists interesse_par (
      mot varchar(100) not null,
      email varchar(300) not null,
      constraint ID_interesse_par_ID primary key (mot, email));
 
-create table LABORATOIRE (
+create table if not exists LABORATOIRE (
      nom varchar(300) not null,
      constraint ID_LABORATOIRE_ID primary key (nom));
 
-create table LIVRE (
+create table if not exists LIVRE (
      titre varchar(100) not null,
      ISBN char(13) not null,
      editeur varchar(300) not null,
@@ -81,11 +81,11 @@ create table LIVRE (
      categorie4 varchar(100),
      constraint ID_LIVRE_ID primary key (ISBN));
 
-create table MOT_CLE (
+create table if not exists MOT_CLE (
      mot varchar(100) not null,
      constraint ID_MOT_CLE_ID primary key (mot));
 
-create table PERIODIQUE (
+create table if not exists PERIODIQUE (
      ID_PER bigint not null auto_increment,
      numero char(5) not null,
      editeur varchar(300) not null,
@@ -93,39 +93,39 @@ create table PERIODIQUE (
      annee_de_publication smallint not null,
      constraint ID_PERIODIQUE_ID primary key (ID_PER));
 
-create table propose (
+create table if not exists propose (
      ID_PUB bigint not null,
      date datetime not null,
      email varchar(300),
      constraint FKpro_PUB_ID primary key (ID_PUB));
 
-create table PUBLICATION (
+create table if not exists PUBLICATION (
      ID_PUB bigint not null auto_increment,
      ID_PER bigint,
      ID_RAP bigint,
-     ISBN char(13),
+     ISBN char(13) default null,
      type smallint not null,
      constraint ID_ID primary key (ID_PUB),
      constraint FKou_est_un_ID unique (ID_PER),
      constraint FKou_encore_est_un_ID unique (ID_RAP),
      constraint FKest_un_ID unique (ISBN));
 
-create table RAPPORT (
+create table if not exists RAPPORT (
      titre varchar(300) not null,
      ID_RAP bigint not null auto_increment,
      annee_de_publication char(4) not null,
      constraint ID_RAPPORT_ID primary key (ID_RAP));
 
-create table redige_par (
+create table if not exists redige_par (
      nom varchar(300) not null,
      ID_RAP bigint not null,
      constraint ID_redige_par_ID primary key (ID_RAP, nom));
 
-create table UTILISATEUR (
+create table if not exists UTILISATEUR (
      email varchar(300) not null,
      constraint ID_UTILISATEUR_ID primary key (email));
 
-create table ecrit_par (
+create table if not exists ecrit_par (
      nom varchar(300) not null,
      ISBN char(13) not null,
      constraint ID_ecrit_par_ID primary key (nom, ISBN));
@@ -228,93 +228,5 @@ alter table ecrit_par add constraint FKecr_Aut
 -- Index Section
 -- _____________ 
 
-create unique index ID_a_IND
-     on a (ID_PUB, mot);
 
-create index FKa_MOT_IND
-     on a (mot);
-
-create index FKprix_en_IND
-     on ACHAT (code);
-
-create unique index FKprovient_d_un_IND
-     on ACHAT (ID_EXE);
-
-create unique index ID_Auteur_IND
-     on Auteur (nom);
-
-create unique index ID_autorise_l_acces_IND
-     on autorise_l_acces (email, nom);
-
-create index FKaut_LAB_IND
-     on autorise_l_acces (nom);
-
-create unique index ID_DEVISE_IND
-     on DEVISE (code);
-
-create unique index ID_IND
-     on EXEMPLAIRE (ID_EXE);
-
-create index FKpossede_IND
-     on EXEMPLAIRE (nom);
-
-create index FKemprunte_IND
-     on EXEMPLAIRE (email);
-
-create index FKcomporte_IND
-     on EXEMPLAIRE (ID_PUB);
-
-create unique index ID_interesse_par_IND
-     on interesse_par (mot, email);
-
-create index FKint_UTI_IND
-     on interesse_par (email);
-
-create unique index ID_LABORATOIRE_IND
-     on LABORATOIRE (nom);
-
-create unique index ID_LIVRE_IND
-     on LIVRE (ISBN);
-
-create unique index ID_MOT_CLE_IND
-     on MOT_CLE (mot);
-
-create unique index ID_PERIODIQUE_IND
-     on PERIODIQUE (ID_PER);
-
-create index FKpro_UTI_IND
-     on propose (email);
-
-create unique index FKpro_PUB_IND
-     on propose (ID_PUB);
-
-create unique index ID_IND
-     on PUBLICATION (ID_PUB);
-
-create unique index FKou_est_un_IND
-     on PUBLICATION (ID_PER);
-
-create unique index FKou_encore_est_un_IND
-     on PUBLICATION (ID_RAP);
-
-create unique index FKest_un_IND
-     on PUBLICATION (ISBN);
-
-create unique index ID_RAPPORT_IND
-     on RAPPORT (ID_RAP);
-
-create unique index ID_redige_par_IND
-     on redige_par (ID_RAP, nom);
-
-create index FKred_Aut_IND
-     on redige_par (nom);
-
-create unique index ID_UTILISATEUR_IND
-     on UTILISATEUR (email);
-
-create unique index ID_ecrit_par_IND
-     on ecrit_par (nom, ISBN);
-
-create index FKecr_LIV_IND
-     on ecrit_par (ISBN);
 
diff --git a/bdd/init/init.py b/bdd/init/init.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd5596c226eb882c802934237eec10daeec5b390
--- /dev/null
+++ b/bdd/init/init.py
@@ -0,0 +1,102 @@
+# Module Imports
+import mariadb
+import sys
+from util import *
+import random
+from time import sleep
+
+
+# Connect to MariaDB Platform
+try:
+    conn = mariadb.connect(
+        user="root",
+        password="",
+        host="mariadb",
+        port=3306,
+        database="library"
+    )
+except mariadb.Error as e:
+    print(f"Error connecting to MariaDB Platform: {e}")
+    sys.exit(1)
+
+# Get Cursor
+cur = conn.cursor()
+
+## load ddl file
+def exec_sql_file(cursor, sql_file):
+    statement = ""
+    for line in open(sql_file):
+        if line.startswith('--'):
+            continue
+        if not line.strip().endswith(';'):
+            statement = statement + line
+        else:  # when you get a line ending in ';' then exec statement and reset for next statement
+            statement = statement + line
+            try:
+                cursor.execute(statement)
+            except (mariadb.OperationalError, mariadb.ProgrammingError) as e:
+                print("\n[WARN] MySQLError during execute statement \n\tArgs: '%s'" % (str(e.args)))
+            statement = ""
+
+exec_sql_file(cur, "BE_SGBD_LIBRARY.ddl")
+
+## database fill functions
+def nom_livre_random():
+    pre = ["L'histroire des", "Tout savoir sur les", "Encyclopédie des", "Le guide des", "Les secrets des", "Le manuel des", "Le livre des", "Le dictionnaire des", "Le guide pratique des"]
+    sujet = ["animaux", "plantes", "champignons", "insectes", "oiseaux", "poissons", "mammifères", "reptiles", "amphibiens", "ordinateurs", "micro-controleurs", "micro-ordinateurs", "micro-ondes", "microscopes", "micro-organismes", "micro-organisations", "macro-états", "dictatures", "démocraties", "monarchies", "républiques", "oligarchies", "aristocraties", "autocraties", "théocraties", "anarchies", "communautés", "communistes", "capitalistes", "socialistes", "libéraux"]
+    post = ["pour les nuls", "", "pour les enfants", "pour les adultes", "pour les vieux", "pour les jeunes", "pour les riches", "pour les pauvres", "pour les moyens", "pour les grands", "pour les petits", "pour les grands et les petits"]
+    return pre[random.randint(0, len(pre)-1)] + " " + sujet[random.randint(0, len(sujet)-1)] + " " + post[random.randint(0, len(post)- 1)]
+
+def nom_auteur_random():
+    prenom = ["Patrick", "Jean", "Alexandre", "Marie", "Jeanne", "Eudes", "Eudette", "Valentine", "Valentin", "Albert", "Alfred", "Marcel", "Claude", "Claudette", "Gérard""Martin", "Bernard", "Thomas", "Robert", "Richard", "Mathieu", "Clement", "Lucas", "Jean", "Marie", "Pierre", "Arnaud", "Rolland", "Philippe", "Olivier", "Hubert", "Louis", "Charles", "Guillaume", "Benoit", "Antoine", "Géraldine"]
+    nom = ["Dupont", "Dupond", "Durand", "Duchemin", "Duchesse", "Duchesne", "Petit", "Leroy", "Moreau", "Lefebvre", "Garcia", "David", "Bertrand", "Roux", "Balkani", "Fournier", "Lefevre", "Mercier", "Dupuy", "Lambert", "Bonnet", "Martinez", "Legrand", "Garnier", "Faure", "Rousseau", "Blanc", "Guerin", "Muller", "Roussel", "Perrin", "Morin", "Gauthier", "Dumont", "Lopez", "Fontaine", "Chevalier", "Robin", "Masson", "Sanchez", "Nguyen", "Boyer", "Denis", "Lemaire", "Duval", "Joly", "Roger", "Roche", "Roy", "Meyer", "Meunier", "Perez", "Marchand", "Dufour", "Blanchard", "Barbier", "Brun", "Dumas", "Brunet", "Schmitt", "Leroux", "Colin", "Fernandez", "Renard", "Caron", "Aubert", "Giraud", "Leclerc", "Vidal", "Bourgeois", "Renaud", "Lemoine", "Picard", "Gaillard", "Leclercq", "Lacroix", "Fabre", "Dupuis", "Rodriguez", "Da silva", "Riviere", "Le gall", "Guillot", "Royer", "Huet", "Dupre" "Maillard", "Bailly", "Janvier", "Renault", "Charpentier"]
+    return prenom[random.randint(0, len(prenom)-1)] + " " + nom[random.randint(0, len(nom)-1)]
+
+def nom_periodique_random():
+    nom = ["Nature", "Science", "Science et vie", "Science et avenir", "Science et fiction", "Science et technologie", "Science et nature", "Science et société", "Science et politique", "Science et religion", "Science et philosophie", "Science et histoire", "Science et géographie", "Science et économie", "Science et finance", "Science et culture", "Science et éducation", "Science et éthique", "Science et morale", "Science et art", "Science et littérature", "Science et poésie", "Science et musique", "Science et cinéma", "Science et théatre", "Science et architecture", "Science et peinture", "Science et sculpture", "Science et photographie", "Science et dessin", "Science et design", "Science et mode", "Science et sport", "Science et santé", "Science et médecine", "Science et psychologie", "Science et psychiatrie", "Science et psychanalyse", "Scie"]
+    return nom[random.randint(0, len(nom)-1)]
+
+def nom_rapport_random():
+    rtype = ["Rapport de PE", "Rapport de PAi", "Rapport de PAr", "These"]
+    about = ["sur", "concernant", "à propos de", "relatif à", "portant sur", "traitant de", "au sujet de"]
+    sub = ["l'impact", "l'effet", "l'influence", "l'incidence", "l'effet", "la reproduction", "la propagation"]
+    sub2 = ["des escargots", "des limaces", "des fourmis", "de l'intelligence", "de la bêtise", "des aérosols", "des gaz", "des liquides", "du soleil", "de la lune"]
+    about2 = ["sur", "dans", "dans le", "dans la", "dans les", "dans l'"]
+    end = ["espace", "univers", "système solaire", "galaxie", "voie lactée", "planète", "terre", "atmosphère", "air", "eau", "mer", "océan", "continent", "pays", "région", "département", "ville", "village", "commune", "rue", "maison", "chambre", "salon", "cuisine", "salle de bain", "toilettes", "chiotte", "cave", "grenier", "garage", "jardin", "parc", "forêt", "montagne", "colline", "plaine", "rivière", "lac", "étang", "mer", "océan", "continent", "pays", "région", "département", "ville", "village", "commune", "rue", "maison", "chambre", "salon", "cuisine", "salle de bain", "toilettes", "chiotte", "cave", "grenier", "garage", "jardin", "parc", "forêt", "montagne", "colline", "plaine", "rivière", "lac", "étang"]
+    return rtype[random.randint(0, len(rtype)-1)] + " " + about[random.randint(0, len(about)-1)] + " " + sub[random.randint(0, len(sub)-1)] + " " + sub2[random.randint(0, len(sub2)-1)] + " " + about2[random.randint(0, len(about2)-1)] + " " + end[random.randint(0, len(end)-1)]
+
+def ISBN_random():
+    return str(random.randint(1000000000000, 9999999999999))
+
+for i in range(1000):
+    if i%50 == 0:
+        print("Added/updated ", i, " rows", end="\r")
+    if i % 10 == 0:
+        add_lab(conn, cur, "Labo " + str(i/10))
+    add_user(conn, cur, "user" + str(i) + "@gmail.com")
+add_lab(conn, cur, "Labo solo")
+add_user(conn, cur, "user@gmail.com")
+print()
+
+for _ in range(1000):
+    if _%50 == 0:
+        print("Added ", 3*_, " rows", end="\r")
+    rid = add_livre(conn, cur, nom_livre_random(), "Hachette", random.randint(1,5), random.randint(1900, 2020), ["Science", "Nature"], ISBN_random(), nom_auteur_random())
+    for k in range(random.randint(0,3)):
+        add_exemplaire(conn,cur,random.randint(0,1),"Labo solo","user@gmail.com",rid)
+    n = random.randint(1, 100)
+    rid = add_periodique(conn, cur, n, nom_periodique_random(), random.randint(1,5), random.randint(1900 + n//10, 2000+n//10))
+    for k in range(random.randint(0,3)):
+        add_exemplaire(conn,cur,random.randint(0,1),"Labo solo","user@gmail.com",rid)
+    rid = add_rapport(conn, cur, nom_rapport_random(), random.randint(1900, 2020), nom_auteur_random())
+    for k in range(random.randint(0,3)):
+        add_exemplaire(conn,cur,random.randint(0,1),"Labo solo","user@gmail.com",rid)
+print()
+
+
+
+for _ in range(5000):
+    if _%100 == 0:
+        print("Added/updated ", _, " rows", end="\r")
+    add_mot_cle_publi(conn, cur, random.randint(1, 125), ["Science", "Nature", "Espace", "Animaux", "Philosophie", "Société"][random.randint(0, 3)])
+print()
diff --git a/bdd/init/util.py b/bdd/init/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa58f73b44ddda13dcadad769b4e40a08398f7bb
--- /dev/null
+++ b/bdd/init/util.py
@@ -0,0 +1,146 @@
+import mariadb
+
+def with_transaction(func):
+    def wrapper(conn, *args, **kwargs):
+        try:
+            conn.begin()
+            result = func(conn, *args, **kwargs)
+            conn.commit()
+            return result
+        except mariadb.Error as e:
+            conn.rollback()
+            print(f"Transaction failed: {e}")
+    return wrapper
+
+@with_transaction
+def add_livre(conn, cur, titre, editeur, edition, annee, categories, ISBN, auteur):
+    assert len(categories) <= 4
+    if (len(categories) < 4):
+        categories += [None] * (4 - len(categories))
+        # some padding
+    conn.begin()
+    cur.execute("INSERT INTO LIVRE (titre, ISBN, editeur, edition, annee_de_publication, categorie1, categorie2, categorie3, categorie4) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (titre, ISBN, editeur, edition, annee, categories[0], categories[1], categories[2], categories[3]))
+    cur.execute("INSERT INTO Auteur (nom) VALUES (?) ON DUPLICATE KEY UPDATE nom = ?", (auteur, auteur))
+    cur.execute("INSERT INTO ecrit_par (ISBN, nom) VALUES (?, ?)", (ISBN, auteur))
+    cur.execute("INSERT INTO PUBLICATION (ISBN, type) VALUES (?, 0)", (ISBN,))
+    rowid = cur.lastrowid
+    conn.commit()
+    return rowid
+
+@with_transaction
+def add_rapport(conn, cur, titre, annee, auteur):
+    conn.begin()
+    cur.execute("INSERT INTO RAPPORT (titre, annee_de_publication) VALUES (?, ?)", (titre, annee))
+    id_rap = cur.lastrowid
+    cur.execute("INSERT INTO Auteur (nom) VALUES (?) ON DUPLICATE KEY UPDATE nom = ?", (auteur, auteur))
+    cur.execute("INSERT INTO redige_par (ID_RAP, nom) VALUES (?, ?)", (id_rap, auteur))
+    cur.execute("INSERT INTO PUBLICATION (ID_RAP, type) VALUES (?, 1)", (id_rap,))
+    rowid = cur.lastrowid
+    conn.commit()
+    return rowid
+
+@with_transaction
+def add_periodique(conn, cur, numero, editeur, edition, annee):
+    conn.begin()
+    cur.execute("INSERT INTO PERIODIQUE (numero, editeur, edition, annee_de_publication) VALUES (?, ?, ?, ?)", (numero, editeur, edition, annee))
+    cur.execute("INSERT INTO PUBLICATION (ID_PER, type) VALUES (?, 2)", (cur.lastrowid,))
+    rowid = cur.lastrowid
+    conn.commit()
+    return rowid
+
+@with_transaction
+def get_livre(conn, cur, ISBN):
+    cur.execute("SELECT titre, LIVRE.ISBN, editeur, edition, annee_de_publication, categorie1, categorie2, categorie3, categorie4, A.nom FROM LIVRE JOIN library.ecrit_par ep on LIVRE.ISBN = ep.ISBN JOIN library.Auteur A on A.nom = ep.nom JOIN library.PUBLICATION P on LIVRE.ISBN = P.ISBN WHERE ISBN = ?", (ISBN,))
+    return cur.fetchone()
+
+@with_transaction
+def get_rapport(conn, cur, ID_RAP):
+    cur.execute("SELECT titre, annee_de_publication, A.nom FROM RAPPORT JOIN library.redige_par rp on RAPPORT.ID_RAP = rp.ID_RAP JOIN library.Auteur A on A.nom = rp.nom JOIN library.PUBLICATION P on RAPPORT.ID_RAP = P.ID_RAP WHERE RAPPORT.ID_RAP = ?", (ID_RAP,))
+    return cur.fetchone()
+
+@with_transaction
+def get_periodique(conn, cur, ID_PER):
+    cur.execute("SELECT numero, editeur, edition, annee_de_publication FROM PERIODIQUE JOIN library.PUBLICATION P on PERIODIQUE.ID_PER = P.ID_PER WHERE PERIODIQUE.ID_PER = ?", (ID_PER,))
+    return cur.fetchone()
+
+@with_transaction
+def add_mot_cle(conn, cur, mot_cle):
+    cur.execute("SELECT * FROM MOT_CLE WHERE mot = ?", (mot_cle,))
+    if cur.fetchone() is None:
+        cur.execute("INSERT INTO MOT_CLE (mot) VALUES (?)", (mot_cle,))
+        conn.commit()
+    return mot_cle
+
+@with_transaction
+def add_mot_cle_publi(conn, cur, ID_PUB, mot_cle):
+    cur.execute("INSERT INTO a (ID_PUB, mot) VALUES (?, ?) ON DUPLICATE KEY UPDATE mot = ?", (ID_PUB, add_mot_cle(conn, cur, mot_cle), mot_cle))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_mot_cle_livre(conn, cur, ISBN, mot_cle):
+    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ISBN = ?", (ISBN,))
+    ID_PUB = cur.fetchone()[0]
+    return add_mot_cle_publi(conn, cur, ID_PUB, mot_cle)
+
+@with_transaction
+def add_mot_cle_rapport(conn, cur, ID_RAP, mot_cle):
+    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ID_RAP = ?", (ID_RAP,))
+    ID_PUB = cur.fetchone()[0]
+    return add_mot_cle_publi(conn, cur, ID_PUB, mot_cle)
+
+@with_transaction
+def add_mot_cle_periodique(conn, cur, ID_PER, mot_cle):
+    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ID_PER = ?", (ID_PER,))
+    ID_PUB = cur.fetchone()[0]
+    return add_mot_cle_publi(conn, cur, ID_PUB, mot_cle)
+
+@with_transaction
+def add_lab(conn, cur, nom):
+    cur.execute("INSERT INTO LABORATOIRE (nom) VALUES (?)", (nom,))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_user(conn, cur, email):
+    cur.execute("INSERT INTO UTILISATEUR (email) VALUES (?)", (email,))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_access(conn, cur, lab, user):
+    cur.execute("INSERT INTO autorise_l_acces (nom, email) VALUES (?, ?)", (lab, user))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_exemplaire(conn, cur, statut, lab, user, ID_PUB):
+    cur.execute("INSERT INTO EXEMPLAIRE (statut, nom, email, ID_PUB) VALUES (?, ?, ?, ?)", (statut, lab, user, ID_PUB))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_devise(conn, cur, code, taux, symbole):
+    cur.execute("INSERT INTO DEVISE (code, taux, symbole) VALUES (?, ?, ?)", (code, taux, symbole))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_achat(conn, cur, ID_EXE, prix, lieu, date, devise):
+    cur.execute("INSERT INTO ACHAT (ID_EXE, prix, lieu, date, code) VALUES (?, ?, ?, ?, ?)", (ID_EXE, prix, lieu, date, devise))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
+
+@with_transaction
+def add_proposition(conn, cur, date, email, ID_PUB):
+    cur.execute("INSERT INTO PROPOSITION (date, email, ID_PUB) VALUES (?, ?, ?)", (date, email, ID_PUB))
+    conn.commit()
+    rowid = cur.lastrowid
+    return rowid
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
index 76896f79bf8bddd3627fdde38fa02e6711c3129d..752f7e389a0d00db28b6692e2b6de08dc25fdba8 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -7,17 +7,20 @@ services:
       dockerfile: Dockerfile
     ports:
       - 3000:3000
-    networks:
-      - library-network
+    depends_on:
+      - api
+
+  api:
+    build:
+      context: ./back
+      dockerfile: Dockerfile
+    ports:
+      - 8910:8000
     depends_on:
       - mariadb
 
   mariadb:
     image: mariadb:latest
-    volumes:
-      - ./mariadb_data:/var/lib/mysql
-    networks:
-      - library-network
     environment:
       MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1
       MARIADB_DATABASE: library
@@ -26,11 +29,6 @@ services:
     build:
       context: ./bdd
       dockerfile: Dockerfile
-    networks:
-      - library-network
+    command: sh -c "echo 'waiting mariadb server...' && sleep 60s && python init.py"
     depends_on:
       - mariadb
-
-networks:
-  library-network:
-    driver: bridge
diff --git a/fill.ipynb b/fill.ipynb
index 6593dc0ed719ccaf0b7a1e662e70f23042be4797..4071bbe0e2e0f21c5bb62faa245fe61dbc8239b3 100644
--- a/fill.ipynb
+++ b/fill.ipynb
@@ -17,19 +17,9 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Requirement already satisfied: mariadb in ./.venv/lib/python3.10/site-packages (1.1.8)\n",
-      "Requirement already satisfied: packaging in ./.venv/lib/python3.10/site-packages (from mariadb) (23.2)\n",
-      "Note: you may need to restart the kernel to use updated packages.\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "%pip install mariadb\n",
     "import mariadb\n",
@@ -133,85 +123,62 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Now we create a copious amount of Publications:"
+    "Let's create some users:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 5,
+   "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
-    "for _ in range(1000):\n",
-    "    add_livre(mariaDB, nom_livre_random(), \"Hachette\", random.randint(1,5), random.randint(1900, 2020), [\"Science\", \"Nature\"], ISBN_random(), nom_auteur_random())\n",
-    "    n = random.randint(1, 100)\n",
-    "    add_periodique(mariaDB, n, nom_periodique_random(), random.randint(1,5), random.randint(1900 + n//10, 2000+n//10))\n",
-    "    add_rapport(mariaDB, nom_rapport_random(), random.randint(1900, 2020), nom_auteur_random())"
+    "for i in range(1000):\n",
+    "    if i % 10 == 0:\n",
+    "        add_lab(conn, cur, \"Labo \" + str(i/10))\n",
+    "    add_user(conn, cur, \"user\" + str(i) + \"@gmail.com\")"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Let's create some metadata:"
+    "Now we create a copious amount of Publications:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 12,
+   "execution_count": 5,
    "metadata": {},
    "outputs": [],
    "source": [
-    "for _ in range(5000):\n",
-    "    add_mot_cle_publi(mariaDB, random.randint(1, 3000), [\"Science\", \"Nature\", \"Espace\", \"Animaux\", \"Philosophie\", \"Société\"][random.randint(0, 3)])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Some users:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 13,
-   "metadata": {},
-   "outputs": [
-    {
-     "ename": "NameError",
-     "evalue": "name 'add_lab' is not defined",
-     "output_type": "error",
-     "traceback": [
-      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
-      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
-      "Cell \u001b[0;32mIn[13], line 3\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[38;5;241m1000\u001b[39m):\n\u001b[1;32m      2\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m i \u001b[38;5;241m%\u001b[39m \u001b[38;5;241m10\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m0\u001b[39m:\n\u001b[0;32m----> 3\u001b[0m         \u001b[43madd_lab\u001b[49m(mariaDB, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mLabo \u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m \u001b[38;5;28mstr\u001b[39m(i))\n\u001b[1;32m      4\u001b[0m     add_user(mariaDB, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124muser\u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;241m+\u001b[39m \u001b[38;5;28mstr\u001b[39m(i) \u001b[38;5;241m+\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m@gmail.com\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
-      "\u001b[0;31mNameError\u001b[0m: name 'add_lab' is not defined"
-     ]
-    }
-   ],
-   "source": [
-    "for i in range(1000):\n",
-    "    if i % 10 == 0:\n",
-    "        add_lab(mariaDB, \"Labo \" + str(i/10))\n",
-    "    add_user(mariaDB, \"user\" + str(i) + \"@gmail.com\")"
+    "for _ in range(1000):\n",
+    "    rid = add_livre(conn, cur, nom_livre_random(), \"Hachette\", random.randint(1,5), random.randint(1900, 2020), [\"Science\", \"Nature\"], ISBN_random(), nom_auteur_random())\n",
+    "    for k in range(random.randint(0,3)):\n",
+    "        add_exemplaire(conn,cur,random.randint(0,1),\"Labo solo\",\"user@gmail.com\",rid)\n",
+    "    n = random.randint(1, 100)\n",
+    "    rid = add_periodique(conn, cur, n, nom_periodique_random(), random.randint(1,5), random.randint(1900 + n//10, 2000+n//10))\n",
+    "    for k in range(random.randint(0,3)):\n",
+    "        add_exemplaire(conn,cur,random.randint(0,1),\"Labo solo\",\"user@gmail.com\",rid)\n",
+    "    rid = add_rapport(conn, cur, nom_rapport_random(), random.randint(1900, 2020), nom_auteur_random())\n",
+    "    for k in range(random.randint(0,3)):\n",
+    "        add_exemplaire(conn,cur,random.randint(0,1),\"Labo solo\",\"user@gmail.com\",rid)"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "And finally, some ownership:"
+    "Let's create some metadata:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 12,
    "metadata": {},
    "outputs": [],
    "source": [
-    "for i in range(1000):\n",
-    "    add_achat(mariaDB, "
+    "for _ in range(5000):\n",
+    "    add_mot_cle_publi(conn, cur, random.randint(1, 125), [\"Science\", \"Nature\", \"Espace\", \"Animaux\", \"Philosophie\", \"Société\"][random.randint(0, 3)])\n"
    ]
   }
  ],
diff --git a/front/.dockerignore b/front/.dockerignore
new file mode 100644
index 0000000000000000000000000000000000000000..600e365ec83f8384cb5c98d0379367a7909cd329
--- /dev/null
+++ b/front/.dockerignore
@@ -0,0 +1 @@
+**/node_modules
\ No newline at end of file
diff --git a/front/Dockerfile b/front/Dockerfile
index d567258cfbe4960cb4656afbde852630d8d0b70f..62a39163bab5bf406e9363d08114d379aa786e57 100644
--- a/front/Dockerfile
+++ b/front/Dockerfile
@@ -5,14 +5,17 @@ FROM node:20-alpine
 WORKDIR /usr/src/app
 
 # Copy the entire local project to the working directory
-COPY ./app .
+COPY ./app/package.json .
+COPY ./app/package-lock.json .
 
 # Install project dependencies
 RUN npm ci
 RUN npm install -g serve
 
+COPY ./app .
+
 # Build the React app
 RUN npm run build
 
 # Command to run the application
-CMD ["serve", "-s", "build"]
+CMD ["serve", "-s", "dist"]
diff --git a/front/app/src/App.tsx b/front/app/src/App.tsx
index 5eb1479ad4c4398c640023bad49f9ad6585cff5b..a059e76e77f12f19489a325650300a730bb77c4f 100644
--- a/front/app/src/App.tsx
+++ b/front/app/src/App.tsx
@@ -1,48 +1,60 @@
 /** @jsxImportSource @emotion/react */
-import { css } from '@emotion/react';
-
-import './App.css';
-import '@fontsource/roboto/300.css';
-import '@fontsource/roboto/400.css';
-import '@fontsource/roboto/500.css';
-import '@fontsource/roboto/700.css';
-import TopBar from "./Components/TopBar"
-import { Search } from './Components/Search';
-import {
-  createBrowserRouter,
-  Outlet,
-  RouterProvider,
-} from "react-router-dom";
-import { Login } from './Components/Login';
+import { css } from "@emotion/react";
 
+import "./App.css";
+import "@fontsource/roboto/300.css";
+import "@fontsource/roboto/400.css";
+import "@fontsource/roboto/500.css";
+import "@fontsource/roboto/700.css";
+import TopBar from "./Components/TopBar";
+import { Search } from "./Components/Search";
+import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
+import { Login } from "./Components/Login";
+import { Admin } from "./Components/Admin";
+import { MePage } from "./Components/MePage";
 
 const router = createBrowserRouter([
   {
     path: "/",
-    element: <>
-      <TopBar></TopBar>
-      <Outlet></Outlet>
-    </>,
+    element: (
+      <>
+        <TopBar></TopBar>
+        <Outlet></Outlet>
+      </>
+    ),
     children: [
       {
-        path:"",
-        element:
-        <div css={css`padding: 32px;`}>
-          <Search></Search>
-        </div>
+        path: "",
+        element: (
+          <div
+            css={css`
+              padding: 32px;
+            `}
+          >
+            <Search></Search>
+          </div>
+        ),
+      },
+      {
+        path: "login",
+        element: <Login />,
       },
       {
-        path:"login",
-        element: <Login/>
-      }
-    ]
+        path: "admin",
+        element: <Admin />,
+      },
+      {
+        path: "me",
+        element: <MePage />,
+      },
+    ],
   },
 ]);
 
 function App() {
   return (
     <div className="App">
-    <RouterProvider router={router} />
+      <RouterProvider router={router} />
     </div>
   );
 }
diff --git a/front/app/src/Components/Admin.tsx b/front/app/src/Components/Admin.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9b5de6d8a55289054dd10349292d6a3b8bc551e0
--- /dev/null
+++ b/front/app/src/Components/Admin.tsx
@@ -0,0 +1,16 @@
+/** @jsxImportSource @emotion/react */
+
+import { useEffect } from "react";
+import { useAuth } from "../Services/auth";
+import { useNavigate } from "react-router-dom";
+
+export function Admin() {
+  const authed = useAuth("admin");
+  const navigate = useNavigate();
+  useEffect(() => {
+    if (!authed) {
+      navigate("/login");
+    }
+  }, []);
+  return <div>Hello</div>;
+}
diff --git a/front/app/src/Components/DocTable.tsx b/front/app/src/Components/DocTable.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..7f94b294fab34bbe5e01f583c477a6454dcd1713
--- /dev/null
+++ b/front/app/src/Components/DocTable.tsx
@@ -0,0 +1,176 @@
+/** @jsxImportSource @emotion/react */
+
+import { css } from "@emotion/react";
+import {
+  AnyDoc,
+  docTypeList,
+  isLivre,
+  isPeriodique,
+  isRapport,
+} from "../models";
+
+export function DocTable({ docs }: { docs: AnyDoc[] }) {
+  const getDisplay = (doc: AnyDoc) => {
+    if (isLivre(doc)) {
+      return (
+        <div
+          css={css`
+            display: flex;
+            flex-direction: column;
+            gap: 2px;
+            border: 1px solid gray;
+            border-radius: 8px;
+            padding: 4px;
+          `}
+        >
+          <span>{doc.auteur}</span>
+          <span>{doc.editeur}</span>
+          <span>Edition {doc.edition}</span>
+          <span>{doc.isbn}</span>
+          <span>
+            {[1, 2, 3, 4]
+              .map((el) => (doc as any)["categorie" + el] ?? "")
+              .join(", ")}
+          </span>
+        </div>
+      );
+    }
+    if (isRapport(doc)) {
+      return (
+        <div
+          css={css`
+            display: flex;
+            flex-direction: column;
+            gap: 2px;
+            border: 1px solid gray;
+            border-radius: 8px;
+            padding: 4px;
+          `}
+        >
+          <span>{doc.titre}</span>
+          <span>{doc.auteur}</span>
+        </div>
+      );
+    }
+    if (isPeriodique(doc)) {
+      return (
+        <div
+          css={css`
+            display: flex;
+            flex-direction: column;
+            gap: 2px;
+            border: 1px solid gray;
+            border-radius: 8px;
+            padding: 4px;
+          `}
+        >
+          <span>{doc.editeur}</span>
+          <span>Edition {doc.edition}</span>
+        </div>
+      );
+    }
+  };
+
+  const build = () => {
+    const display = [];
+    for (const doc of docs) {
+      display.push(
+        <div
+          css={css`
+            display: flex;
+            flex-direction: row;
+            gap: 12px;
+            flex-wrap: wrap;
+            border: 1px solid lightgray;
+            border-radius: 4px;
+            padding: 4px;
+            justify-content: space-between;
+            > * {
+              flex-grow: 0;
+              flex-shrink: 0;
+              flex-basis: calc(33% - 20px);
+            }
+          `}
+          key={doc.id}
+        >
+          {/* main data */}
+          <div
+            css={css`
+              display: flex;
+              flex-direction: column;
+              gap: 4px;
+              border: 1px solid gray;
+              border-radius: 8px;
+              padding: 4px;
+            `}
+          >
+            <span>
+              {isPeriodique(doc) ? `${doc.editeur} #${doc.numero}` : doc.titre}
+            </span>
+            <span>
+              {docTypeList[doc.type]} - {doc.annee}
+            </span>
+          </div>
+          {/* specific data */}
+          {getDisplay(doc)}
+          {/* user data */}
+          <div
+            css={css`
+              border: 1px solid gray;
+              border-radius: 8px;
+              display: flex;
+              flex-direction: row;
+              justify-content: center;
+              align-items: center;
+              padding: 4px;
+            `}
+          >
+            <span
+              css={css`
+                color: green;
+              `}
+            >
+              {doc.free}
+            </span>{" "}
+            +{" "}
+            <span
+              css={css`
+                color: red;
+              `}
+            >
+              {doc.taken}
+            </span>
+          </div>
+        </div>
+      );
+    }
+    return display;
+  };
+  return (
+    <div
+      css={css`
+        display: flex;
+        flex-direction: column;
+        gap: 16px;
+      `}
+    >
+      <div
+        css={css`
+          display: flex;
+          flex-direction: row;
+          justify-content: space-between;
+          gap: 4px;
+          > * {
+            flex-basis: calc(33% - 20px);
+            text-align: center;
+          }
+        `}
+      >
+        <span>Titre</span>
+        <span>Infos</span>
+        <span>Disponible + Empruntés</span>
+      </div>
+      {build()}
+    </div>
+  );
+}
diff --git a/front/app/src/Components/Login.tsx b/front/app/src/Components/Login.tsx
index fc9e903a6ae27b99f3bacdc06756d9f2bf95364b..f0d102600da5706cf4606daabe8006590200be70 100644
--- a/front/app/src/Components/Login.tsx
+++ b/front/app/src/Components/Login.tsx
@@ -2,14 +2,37 @@
 
 import Button from "@mui/material/Button";
 import TextField from "@mui/material/TextField";
-import { css } from '@emotion/react';
+import { css } from "@emotion/react";
 import Box from "@mui/material/Box";
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
 
+export function Login() {
+  const nextUrl = localStorage.getItem("afterLoginUrl");
+  const [message, setMessage] = useState("");
+  const [username, setUsername] = useState("");
+  const [password, setPassword] = useState("");
+  const navigate = useNavigate();
 
+  const login = () => {
+    if (username === "admin" && password === "admin") {
+      localStorage.setItem("token", "admin");
+    } else if (username === "user" && password === "user") {
+      localStorage.setItem("token", "user");
+    } else {
+      setMessage("Invalid username or password");
+      return;
+    }
+    localStorage.setItem("user", username);
+    if (nextUrl) {
+      navigate(nextUrl);
+    } else {
+      navigate("/");
+    }
+  };
 
-
-export function Login() {
-    return <Box
+  return (
+    <Box
       component="form"
       noValidate
       autoComplete="off"
@@ -21,9 +44,27 @@ export function Login() {
         margin: 32px;
       `}
     >
-        <TextField required label="Username"/>
-        <TextField required label="Password" type="password"/>
-        <Button variant='outlined'>Se connecter</Button>
+      <span>{message}</span>
+      <TextField
+        required
+        value={username}
+        onChange={(event) => {
+          setUsername(event.target.value);
+        }}
+        label="Username"
+      />
+      <TextField
+        required
+        label="Password"
+        value={password}
+        onChange={(event) => {
+          setPassword(event.target.value);
+        }}
+        type="password"
+      />
+      <Button variant="outlined" onClick={login}>
+        Se connecter
+      </Button>
     </Box>
-
-}
\ No newline at end of file
+  );
+}
diff --git a/front/app/src/Components/MePage.tsx b/front/app/src/Components/MePage.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..f4baae3640804a7f3edfc4bccb182ef53536ab86
--- /dev/null
+++ b/front/app/src/Components/MePage.tsx
@@ -0,0 +1,52 @@
+/** @jsxImportSource @emotion/react */
+
+import { FormControl, InputLabel, Select, MenuItem } from "@mui/material";
+import { useEffect, useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { useAuth } from "../Services/auth";
+
+export function MePage() {
+  const authed = useAuth("user");
+  const navigate = useNavigate();
+  useEffect(() => {
+    if (!authed) {
+      navigate("/login");
+    }
+  }, []);
+
+  return <></>;
+
+  // const [selectedValue, setSelectedValue] = useState("");
+
+  // const handleSelectChange = (event: any) => {
+  //   setSelectedValue(event.target.value);
+  // };
+
+  // const changeSelectValueDynamically = () => {
+  //   const newValue = "option1";
+
+  //   setSelectedValue(newValue);
+  // };
+
+  // return (
+  //   <div>
+  //     <button onClick={changeSelectValueDynamically}>
+  //       Change Select Value Dynamically
+  //     </button>
+
+  //     <FormControl>
+  //       <InputLabel id="select-label">Select</InputLabel>
+  //       <Select
+  //         labelId="select-label"
+  //         id="select"
+  //         value={selectedValue}
+  //         onChange={handleSelectChange}
+  //       >
+  //         <MenuItem value="option1">Option 1</MenuItem>
+  //         <MenuItem value="option2">Option 2</MenuItem>
+  //         <MenuItem value="option3">Option 3</MenuItem>
+  //       </Select>
+  //     </FormControl>
+  //   </div>
+  // );
+}
diff --git a/front/app/src/Components/Search.tsx b/front/app/src/Components/Search.tsx
index 2f7d477fffbd9218cb02a5f5483430a1cdf10f29..959469449d81e75fd168bd0f4dd1c9427562ba4b 100644
--- a/front/app/src/Components/Search.tsx
+++ b/front/app/src/Components/Search.tsx
@@ -2,26 +2,335 @@
 
 import Autocomplete from "@mui/material/Autocomplete";
 import TextField from "@mui/material/TextField";
-import { css } from '@emotion/react';
+import { css } from "@emotion/react";
+import { useSearchParams } from "react-router-dom";
+import { useEffect, useState } from "react";
+import {
+  AnyDoc,
+  DocTypes,
+  Livre,
+  Periodique,
+  Rapport,
+  docTypeList,
+} from "../models";
+import Select from "@mui/material/Select";
+import MenuItem from "@mui/material/MenuItem";
+import * as api from "../Services/api";
+import Button from "@mui/material/Button";
+import { DocTable } from "./DocTable";
+import InputLabel from "@mui/material/InputLabel";
+import FormControl from "@mui/material/FormControl";
 
+export function Search() {
+  const [searchParams, setSearchParams] = useSearchParams();
 
+  const [search, setSearch] = useState("");
+  const [docType, setDocType] = useState("");
+  const [categories, setCategories] = useState<string[]>([]);
+  const [auteur, setAuteur] = useState<string>("");
+  const [annee, setAnnee] = useState<number | undefined>();
+  const [editeur, setEditeur] = useState<string>("");
+  const [optAuteur, setOptAuteur] = useState<string[]>([]);
+  const [optCategories, setOptCategories] = useState<string[]>([]);
+  const [categoriesInput, setCategoriesInput] = useState("");
+  const [auteurInput, setAuteurInput] = useState("");
+  const [results, setResults] = useState<AnyDoc[]>([]);
 
+  useEffect(() => {
+    setSearch(searchParams.get("search") || "");
+    const dt = searchParams.get("docType");
+    setDocType(dt || "");
+    setCategories(searchParams.getAll("category") || "");
+    setAuteur(searchParams.get("auteur") || "");
+    const a = searchParams.get("annee");
+    setAnnee(a != null ? +a : undefined);
+    setEditeur(searchParams.get("editeur") || "");
+    api
+      .get<string[][]>("/auteurs")
+      .then((auteurs) =>
+        setOptAuteur(auteurs.reduce((acc, el) => acc.concat(el)))
+      );
+    api
+      .get<string[][]>("/categories")
+      .then((categories) =>
+        setOptCategories(categories.reduce((acc, el) => acc.concat(el)))
+      );
+  }, []);
 
-export function Search() {
-    return <Autocomplete
-    freeSolo
-    id="free-solo-2-demo"
-    disableClearable
-    options={[]}
-    renderInput={(params) => (
-      <TextField
-        {...params}
-        label="Trouver une publication"
-        InputProps={{
-          ...params.InputProps,
-          type: 'search',
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (search) params.set("search", search);
+      else params.delete("search");
+      return params;
+    });
+  }, [search]);
+
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (docType) params.set("docType", docType);
+      else params.delete("docType");
+      return params;
+    });
+  }, [docType]);
+
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (categories.length) {
+        params.delete("category");
+        for (const cat of categories) {
+          params.append("category", cat);
+        }
+      } else {
+        params.delete("category");
+      }
+      return params;
+    });
+  }, [categories]);
+
+  useEffect(() => {
+    if (categoriesInput) {
+      api
+        .get<string[][]>(`/categories?text=${categoriesInput}`)
+        .then((categories) =>
+          setOptCategories(categories.reduce((acc, el) => acc.concat(el), []))
+        );
+    }
+  }, [categoriesInput]);
+
+  useEffect(() => {
+    if (auteurInput) {
+      api
+        .get<string[][]>(`/auteurs?text=${auteurInput}`)
+        .then((auteurs) =>
+          setOptAuteur(auteurs.reduce((acc, el) => acc.concat(el), []))
+        );
+    }
+  }, [auteurInput]);
+
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (auteur) params.set("auteur", auteur);
+      else params.delete("auteur");
+      return params;
+    });
+  }, [auteur]);
+
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (annee || annee === 0) params.set("annee", String(annee));
+      else params.delete("annee");
+      return params;
+    });
+  }, [annee]);
+
+  useEffect(() => {
+    setSearchParams((params) => {
+      if (editeur) params.set("editeur", editeur);
+      else params.delete("editeur");
+      return params;
+    });
+  }, [editeur]);
+
+  const runSearch = () => {
+    const params = new URLSearchParams();
+    if (search) params.set("search", search);
+    categories.forEach((c) => {
+      params.append("categories", c);
+    });
+    if (docType) params.set("docType", docType);
+    if (auteur) params.set("auteur", auteur);
+    if (annee || annee === 0) params.set("annee", String(annee));
+    if (editeur) params.set("editeur", editeur);
+
+    const paramString = params.toString();
+    api
+      .get<string[][]>("/search" + (paramString ? "?" : "") + paramString)
+      .then((res) => {
+        const docs = res.map((doc) => {
+          const id = +doc[0];
+          const type = +doc[1];
+          if (type === DocTypes.Livre) {
+            const d: Partial<Livre> = { id, type };
+            d.titre = doc[2];
+            d.isbn = doc[3];
+            d.editeur = doc[4];
+            d.edition = doc[5];
+            d.annee = +doc[6];
+            d.categorie1 = doc[7];
+            d.categorie2 = doc[8];
+            d.categorie3 = doc[9];
+            d.categorie4 = doc[10];
+            d.auteur = doc[11];
+            d.free = +doc[12];
+            d.taken = +doc[13];
+            return d;
+          }
+          if (type === DocTypes.Rapport) {
+            const d: Partial<Rapport> = { id, type };
+            d.titre = doc[2];
+            d.annee = +doc[3];
+            d.auteur = doc[4];
+            d.free = +doc[5];
+            d.taken = +doc[6];
+            return d;
+          }
+          if (type === DocTypes.Periodique) {
+            const d: Partial<Periodique> = { id, type };
+            d.numero = doc[2];
+            d.editeur = doc[3];
+            d.edition = doc[4];
+            d.annee = +doc[5];
+            d.free = +doc[6];
+            d.taken = +doc[7];
+            return d;
+          }
+        }) as AnyDoc[];
+        console.log(docs);
+        setResults(docs);
+      });
+  };
+
+  return (
+    <div>
+      <div
+        css={css`
+          display: flex;
+          flex-direction: row;
+          gap: 8px;
+          flex-wrap: wrap;
+          margin-bottom: 8px;
+        `}
+      >
+        <FormControl>
+          <InputLabel id="select-label">Type</InputLabel>
+          <Select
+            css={css`
+              width: 150px;
+            `}
+            value={docType}
+            labelId="select-label"
+            label="Type"
+            onChange={(event: any) => {
+              setDocType(event.target.value);
+            }}
+          >
+            <MenuItem value={""}>Aucun</MenuItem>
+            <MenuItem value={"0"}>{docTypeList[0]}</MenuItem>
+            <MenuItem value={"1"}>{docTypeList[1]}</MenuItem>
+            <MenuItem value={"2"}>{docTypeList[2]}</MenuItem>
+          </Select>
+        </FormControl>
+
+        <Autocomplete
+          css={css`
+            width: 300px;
+          `}
+          multiple
+          options={optCategories}
+          value={categories}
+          inputValue={categoriesInput}
+          onChange={(event: unknown, newValue) => {
+            setCategories(newValue);
+          }}
+          onInputChange={(event: any, value) => setCategoriesInput(value)}
+          renderInput={(params) => (
+            <TextField
+              {...params}
+              label="Catégories"
+              InputProps={{
+                ...params.InputProps,
+                type: "search",
+              }}
+            />
+          )}
+        />
+        <Autocomplete
+          css={css`
+            width: 200px;
+          `}
+          options={optAuteur}
+          value={auteur}
+          inputValue={auteurInput}
+          onChange={(event: unknown, newValue) => {
+            setAuteur(newValue || "");
+          }}
+          onInputChange={(event: any, value) => setAuteurInput(value)}
+          renderInput={(params) => (
+            <TextField
+              {...params}
+              label="Auteur"
+              InputProps={{
+                ...params.InputProps,
+                type: "search",
+              }}
+            />
+          )}
+        />
+        <TextField
+          label="Année"
+          value={annee}
+          onChange={(event: any) => {
+            setAnnee(event.target.value);
+          }}
+          InputProps={{
+            type: "number",
+          }}
+        />
+        <Autocomplete
+          freeSolo
+          disableClearable
+          css={css`
+            width: 200px;
+          `}
+          options={[]}
+          value={editeur}
+          onChange={(event: unknown, newValue) => {
+            setEditeur(newValue || "");
+          }}
+          renderInput={(params) => (
+            <TextField
+              {...params}
+              label="Editeur"
+              InputProps={{
+                ...params.InputProps,
+                type: "search",
+              }}
+            />
+          )}
+        />
+      </div>
+
+      <Autocomplete
+        freeSolo
+        disableClearable
+        options={[]}
+        value={search}
+        onChange={(event: unknown, newValue) => {
+          setSearch(newValue);
         }}
+        renderInput={(params) => (
+          <TextField
+            {...params}
+            label="Trouver une publication"
+            InputProps={{
+              ...params.InputProps,
+              type: "search",
+            }}
+          />
+        )}
       />
-    )}
-  />
-}
\ No newline at end of file
+
+      <Button
+        variant="outlined"
+        onClick={runSearch}
+        css={css`
+          margin: 16px;
+        `}
+      >
+        Chercher
+      </Button>
+
+      <DocTable docs={results}></DocTable>
+    </div>
+  );
+}
diff --git a/front/app/src/Components/TopBar.tsx b/front/app/src/Components/TopBar.tsx
index 281a5daa3f8c823f9f2fc976072cae7be35de25c..df55dd61856803f4b8898a5b55be00ef01586f30 100644
--- a/front/app/src/Components/TopBar.tsx
+++ b/front/app/src/Components/TopBar.tsx
@@ -1,28 +1,47 @@
 /** @jsxImportSource @emotion/react */
 
-import Button from '@mui/material/Button';
-import { css } from '@emotion/react';
-import { useLocation, useNavigate } from 'react-router-dom';
-
+// import Button from "@mui/material/Button";
+import { css } from "@emotion/react";
+// import { useLocation, useNavigate } from "react-router-dom";
+// // import Chip from "@mui/material/Chip";
 
 export default function TopBar() {
-    const navigate = useNavigate();
-    const location = useLocation();
+  // const navigate = useNavigate();
+  // const location = useLocation();
+  // const username = localStorage.getItem("user");
 
-    return <div css={css`
+  return (
+    <div
+      css={css`
         padding: 16px;
         display: flex;
         align-items: center;
         height: min(5%, 60px);
         gap: 8px;
         border-bottom: outset lightskyblue 1px;
-    `}>
-        <h1>Librarie</h1>
-        {location.pathname === "/login" ? null : 
+      `}
+    >
+      <h1>Librarie</h1>
+      {/* {location.pathname === "/login" ? null : username ? (
+        <Chip
+          label={username}
+          css={css`
+            margin-left: auto;
+            height: 40px;
+          `}
+        ></Chip>
+      ) : (
         <Button
-            variant='outlined'
-            css={css`margin-left: auto; height: 40px;`}
-            onClick={() => navigate("/login")}
-        >Se connecter</Button>}
+          variant="outlined"
+          css={css`
+            margin-left: auto;
+            height: 40px;
+          `}
+          onClick={() => navigate("/login")}
+        >
+          Se connecter
+        </Button>
+      )} */}
     </div>
-}
\ No newline at end of file
+  );
+}
diff --git a/front/app/src/Services/api.tsx b/front/app/src/Services/api.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6894054f504ab290b390db46a13167128f40ffa5
--- /dev/null
+++ b/front/app/src/Services/api.tsx
@@ -0,0 +1,41 @@
+import { useState } from "react";
+
+const prefix = "http://localhost:8910";
+
+export function get<T = unknown>(url: string) {
+  return fetch(prefix + url, {
+    method: "GET",
+    headers: { "Content-Type": "application/json" },
+  }).then((response) => response.json() as T);
+}
+
+export function post<R, T = unknown>(url: string, data: R) {
+  return fetch(prefix + url, {
+    method: "POST",
+    headers: { "Content-Type": "application/json" },
+    body: JSON.stringify(data),
+  }).then((response) => response.json() as T);
+}
+
+export function put<T>(url: string, data: T) {
+  return fetch(prefix + url, {
+    method: "PUT",
+    headers: { "Content-Type": "application/json" },
+    body: JSON.stringify(data),
+  }).then((response) => response.json() as T);
+}
+
+export function patch<T = unknown>(url: string, data: Partial<T>) {
+  return fetch(prefix + url, {
+    method: "PATCH",
+    headers: { "Content-Type": "application/json" },
+    body: JSON.stringify(data),
+  }).then((response) => response.json() as T);
+}
+
+export function del(url: string) {
+  return fetch(prefix + url, {
+    method: "DELETE",
+    headers: { "Content-Type": "application/json" },
+  }).then((response) => response.json());
+}
diff --git a/front/app/src/Services/auth.tsx b/front/app/src/Services/auth.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fe9df6c75aa2fc576222449771542fceec2c6147
--- /dev/null
+++ b/front/app/src/Services/auth.tsx
@@ -0,0 +1,27 @@
+import { useLocation, useNavigate } from "react-router-dom";
+
+export function useAuth(level: string) {
+  // fake authentication
+  const token = localStorage.getItem("token");
+  const location = useLocation();
+
+  if (level === "admin") {
+    if (token !== "admin") {
+      localStorage.setItem(
+        "afterLoginUrl",
+        location.pathname + location.search + location.hash
+      );
+      return false;
+    }
+  }
+  if (level === "user") {
+    if (token !== "admin" && token !== "user") {
+      localStorage.setItem(
+        "afterLoginUrl",
+        location.pathname + location.search + location.hash
+      );
+      return false;
+    }
+  }
+  return true;
+}
diff --git a/front/app/src/models.tsx b/front/app/src/models.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..baa423eab2eb6537433b1d29abe9c77885a92a33
--- /dev/null
+++ b/front/app/src/models.tsx
@@ -0,0 +1,51 @@
+export enum DocTypes {
+  Livre = 0,
+  Rapport,
+  Periodique,
+}
+export const docTypeList = ["Livre", "Rapport", "Periodique"];
+
+export interface Doc {
+  id: number;
+  type: number;
+  free: number;
+  taken: number;
+}
+
+export interface Livre extends Doc {
+  titre: string;
+  isbn: string;
+  editeur: string;
+  edition: string;
+  annee: number;
+  categorie1?: string;
+  categorie2?: string;
+  categorie3?: string;
+  categorie4?: string;
+  auteur: string;
+}
+
+export interface Rapport extends Doc {
+  titre: string;
+  annee: number;
+  auteur: string;
+}
+
+export interface Periodique extends Doc {
+  numero: string;
+  editeur: string;
+  edition: string;
+  annee: number;
+}
+
+export type AnyDoc = Livre | Rapport | Periodique;
+
+export function isLivre(doc: AnyDoc): doc is Livre {
+  return doc.type === 0;
+}
+export function isRapport(doc: AnyDoc): doc is Rapport {
+  return doc.type === 1;
+}
+export function isPeriodique(doc: AnyDoc): doc is Periodique {
+  return doc.type === 2;
+}
diff --git a/util.py b/util.py
deleted file mode 100644
index 71f747d632361dd5c622dc61603e8e8669b2b2d3..0000000000000000000000000000000000000000
--- a/util.py
+++ /dev/null
@@ -1,121 +0,0 @@
-def add_livre(mariaDB, titre, editeur, edition, annee, categories, ISBN, auteur):
-    assert len(categories) <= 4
-    if (len(categories) < 4):
-        categories += [None] * (4 - len(categories))
-        # some padding
-    mariaDB.begin()
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO LIVRE (titre, ISBN, editeur, edition, annee_de_publication, categorie1, categorie2, categorie3, categorie4) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (titre, ISBN, editeur, edition, annee, categories[0], categories[1], categories[2], categories[3]))
-    cur.execute("INSERT INTO PUBLICATION (ISBN, type) VALUES (?, 0)", (ISBN,))
-    cur.execute("INSERT INTO Auteur (nom) VALUES (?) ON DUPLICATE KEY UPDATE nom = ?", (auteur, auteur))
-    cur.execute("INSERT INTO ecrit_par (ISBN, nom) VALUES (?, ?)", (ISBN, auteur))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_rapport(mariaDB, titre, annee, auteur):
-    mariaDB.begin()
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO RAPPORT (titre, annee_de_publication) VALUES (?, ?)", (titre, annee))
-    id_rap = cur.lastrowid
-    cur.execute("INSERT INTO PUBLICATION (ID_RAP, type) VALUES (?, 1)", (id_rap,))
-    cur.execute("INSERT INTO Auteur (nom) VALUES (?) ON DUPLICATE KEY UPDATE nom = ?", (auteur, auteur))
-    cur.execute("INSERT INTO redige_par (ID_RAP, nom) VALUES (?, ?)", (id_rap, auteur))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_periodique(mariaDB, numero, editeur, edition, annee):
-    mariaDB.begin()
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO PERIODIQUE (numero, editeur, edition, annee_de_publication) VALUES (?, ?, ?, ?)", (numero, editeur, edition, annee))
-    cur.execute("INSERT INTO PUBLICATION (ID_PER, type) VALUES (?, 2)", (cur.lastrowid,))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def get_livre(mariaDB, ISBN):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT titre, LIVRE.ISBN, editeur, edition, annee_de_publication, categorie1, categorie2, categorie3, categorie4, A.nom FROM LIVRE JOIN MAIN.ecrit_par ep on LIVRE.ISBN = ep.ISBN JOIN MAIN.Auteur A on A.nom = ep.nom JOIN MAIN.PUBLICATION P on LIVRE.ISBN = P.ISBN WHERE ISBN = ?", (ISBN,))
-    return cur.fetchone()
-
-def get_rapport(mariaDB, ID_RAP):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT titre, annee_de_publication, A.nom FROM RAPPORT JOIN MAIN.redige_par rp on RAPPORT.ID_RAP = rp.ID_RAP JOIN MAIN.Auteur A on A.nom = rp.nom JOIN MAIN.PUBLICATION P on RAPPORT.ID_RAP = P.ID_RAP WHERE RAPPORT.ID_RAP = ?", (ID_RAP,))
-    return cur.fetchone()
-
-def get_periodique(mariaDB, ID_PER):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT numero, editeur, edition, annee_de_publication FROM PERIODIQUE JOIN MAIN.PUBLICATION P on PERIODIQUE.ID_PER = P.ID_PER WHERE PERIODIQUE.ID_PER = ?", (ID_PER,))
-    return cur.fetchone()
-
-def add_mot_cle(mariaDB, mot_cle):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT * FROM MOT_CLE WHERE mot = ?", (mot_cle,))
-    if cur.fetchone() is None:
-        cur.execute("INSERT INTO MOT_CLE (mot) VALUES (?)", (mot_cle,))
-        mariaDB.commit()
-    return mot_cle
-
-def add_mot_cle_publi(mariaDB, ID_PUB, mot_cle):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO a (ID_PUB, mot) VALUES (?, ?) ON DUPLICATE KEY UPDATE mot = ?", (ID_PUB, add_mot_cle(mariaDB, mot_cle), mot_cle))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_mot_cle_livre(mariaDB, ISBN, mot_cle):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ISBN = ?", (ISBN,))
-    ID_PUB = cur.fetchone()[0]
-    return add_mot_cle_publi(mariaDB, ID_PUB, mot_cle)
-
-def add_mot_cle_rapport(mariaDB, ID_RAP, mot_cle):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ID_RAP = ?", (ID_RAP,))
-    ID_PUB = cur.fetchone()[0]
-    return add_mot_cle_publi(mariaDB, ID_PUB, mot_cle)
-
-def add_mot_cle_periodique(mariaDB, ID_PER, mot_cle):
-    cur = mariaDB.cursor()
-    cur.execute("SELECT ID_PUB FROM PUBLICATION WHERE ID_PER = ?", (ID_PER,))
-    ID_PUB = cur.fetchone()[0]
-    return add_mot_cle_publi(mariaDB, ID_PUB, mot_cle)
-
-def add_lab(mariaDB, nom):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO LABORATOIRE (nom) VALUES (?)", (nom,))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_user(mariaDB, email):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO UTILISATEUR (email) VALUES (?)", (email,))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_access(mariaDB, lab, user):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO autorise_l_acces (nom, email) VALUES (?, ?)", (lab, user))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_exemplaire(mariaDB, lab, user, ID_PUB):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO EXEMPLAIRE (status, nom, email, ID_PUB) VALUES (0, ?, ?, ?)", (lab, user, ID_PUB))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_devise(mariaDB, code, taux, symbole):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO DEVISE (code, taux, symbole) VALUES (?, ?, ?)", (code, taux, symbole))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_achat(mariaDB, ID_EXE, prix, lieu, date, devise):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO ACHAT (ID_EXE, prix, lieu, date, code) VALUES (?, ?, ?, ?, ?)", (ID_EXE, prix, lieu, date, devise))
-    mariaDB.commit()
-    return cur.lastrowid
-
-def add_proposition(mariaDB, date, email, ID_PUB):
-    cur = mariaDB.cursor()
-    cur.execute("INSERT INTO PROPOSITION (date, email, ID_PUB) VALUES (?, ?, ?)", (date, email, ID_PUB))
-    mariaDB.commit()
-    return cur.lastrowid
\ No newline at end of file