Upload files to "/"

This commit is contained in:
Willy 2026-06-02 11:25:06 +02:00
parent 2591cdc655
commit 51e5ef9988
4 changed files with 218 additions and 0 deletions

24
Dockerfile Normal file
View file

@ -0,0 +1,24 @@
# Image de base minuscule : Python sur Alpine Linux (~50 Mo une fois construite)
FROM python:3.12-alpine
# Version de l'image, transmise au build via docker-compose.yml (source : .env).
# Valeur par défaut « dev » si le build est lancé sans la fournir.
ARG VERSION=dev
LABEL org.opencontainers.image.version="${VERSION}"
WORKDIR /app
# On installe les dépendances en premier pour profiter du cache de build
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Le code de l'application : le script Python et la page HTML (à la racine).
COPY app.py index.html ./
# Dossier où vivra la base SQLite. Il est monté depuis l'extérieur via le
# volume défini dans docker-compose.yml.
ENV DATA_DIR=/data
EXPOSE 5000
CMD ["python", "app.py"]

14
docker-compose.yml Normal file
View file

@ -0,0 +1,14 @@
services:
demo-docker:
build:
context: .
args:
VERSION: ${VERSION:-dev} # Transmet la version (depuis .env) au Dockerfile pour le LABEL.
image: demo-docker:${VERSION:-dev} # Nom et tag de l'image docker qui sera compilée
container_name: demo-docker-01 # Nom du conteneur
ports:
- "5000:5000" # Mappage du port hote/conteneur
volumes:
- ./data-dd:/data # Mappage du volume hote/conteneur
- /etc/localtime:/etc/localtime:ro # Synchronise le fuseau horaire avec l'hôte
restart: unless-stopped

179
index.html Normal file
View file

@ -0,0 +1,179 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>La page commentaire pour les nerds</title>
<!-- Polices libres (SIL OFL) : un clin d'œil à l'esprit open-source -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Atkinson+Hyperlegible:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
<style>
:root {
--papier: #fdf3e3;
--papier-fonce:#f6e7cf;
--encre: #3a2b22;
--encre-douce: #7a6655;
--corail: #e8743b;
--corail-fonce:#cf5a23;
--sauge: #2f9e8f;
--carte: #fffdf8;
--ombre: rgba(58, 43, 34, 0.13);
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
font-family: "Atkinson Hyperlegible", system-ui, sans-serif;
color: var(--encre);
background-color: var(--papier);
background-image:
radial-gradient(var(--papier-fonce) 1px, transparent 1px),
radial-gradient(var(--papier-fonce) 1px, transparent 1px);
background-size: 28px 28px;
background-position: 0 0, 14px 14px;
line-height: 1.6;
}
header { text-align: center; padding: 3rem 1.25rem 1.5rem; }
.minou { font-size: 3.5rem; line-height: 1; display: inline-block; }
h1 {
font-family: "Baloo 2", cursive; font-weight: 700;
font-size: clamp(2rem, 6vw, 3.2rem); margin: 0.4rem 0 0.3rem;
color: var(--corail-fonce);
}
header p {
max-width: 34rem; margin: 0 auto;
color: var(--encre-douce); font-size: 1.05rem;
}
main { max-width: 44rem; margin: 0 auto; padding: 0 1.25rem 4rem; }
.formulaire {
background: var(--carte); border: 2px dashed var(--papier-fonce);
border-radius: 22px; padding: 1.5rem; margin: 1.5rem 0 2.5rem;
box-shadow: 0 8px 22px var(--ombre);
}
.formulaire h2 {
font-family: "Baloo 2", cursive; margin: 0 0 1rem;
font-size: 1.35rem; color: var(--encre);
}
label {
display: block; font-weight: 700; font-size: 0.9rem;
margin-bottom: 0.35rem; color: var(--encre-douce);
}
input, textarea {
width: 100%; font-family: inherit; font-size: 1rem; color: var(--encre);
background: var(--papier); border: 2px solid var(--papier-fonce);
border-radius: 14px; padding: 0.7rem 0.9rem; margin-bottom: 1rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
input:focus, textarea:focus {
outline: none; border-color: var(--sauge);
box-shadow: 0 0 0 4px rgba(47, 158, 143, 0.18);
}
/* Cadre figé : ni redimensionnable, ni poignée dans l'angle */
textarea { height: 7rem; resize: none; }
.actions { text-align: right; }
button {
font-family: "Baloo 2", cursive; font-weight: 600; font-size: 1.05rem;
color: #fff; background: var(--corail); border: none; border-radius: 14px;
padding: 0.7rem 1.6rem; cursor: pointer; box-shadow: 0 4px 0 var(--corail-fonce);
transition: transform 0.1s, box-shadow 0.1s;
}
button:hover { transform: translateY(-1px); box-shadow: 0 5px 0 var(--corail-fonce); }
button:active { transform: translateY(3px); box-shadow: 0 1px 0 var(--corail-fonce); }
.compteur { text-align: center; color: var(--encre-douce); font-size: 0.95rem; margin-bottom: 1.5rem; }
.compteur b { color: var(--corail-fonce); }
.reset { text-align: center; margin-top: 2rem; }
.reset button {
font-family: inherit; font-size: 0.85rem; color: var(--encre-douce);
background: none; border: none; box-shadow: none; cursor: pointer;
text-decoration: underline; padding: 0.3rem 0.6rem;
}
.reset button:hover { color: var(--corail-fonce); transform: none; box-shadow: none; }
.commentaire {
background: var(--carte); border-radius: 18px; padding: 1.1rem 1.3rem;
margin-bottom: 1.1rem; box-shadow: 0 5px 16px var(--ombre);
animation: apparition 0.5s ease both;
}
@keyframes apparition {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.commentaire .meta {
display: flex; justify-content: space-between; align-items: baseline;
gap: 1rem; margin-bottom: 0.4rem; flex-wrap: wrap;
}
.commentaire .auteur { font-family: "Baloo 2", cursive; font-weight: 600; color: var(--encre); }
.commentaire .date { font-size: 0.8rem; color: var(--encre-douce); }
.commentaire .message { margin: 0; white-space: pre-wrap; word-wrap: break-word; }
footer { text-align: center; padding: 2rem 1.25rem 3rem; color: var(--encre-douce); font-size: 0.85rem; }
</style>
</head>
<body>
<header>
<span class="minou" aria-hidden="true">🐧</span>
<h1>Commentaires de nerds</h1>
<p>Laissez un petit mot à la communauté.<br>Pas besoin de s'inscrire&nbsp;: écrivez, c'est tout.<br>Les messages s'affichent les uns à la suite des autres, du plus récent au plus ancien.</p>
</header>
<main>
<section class="formulaire">
<h2>✍️ Votre message</h2>
<form action="{{ url_for('poster') }}" method="post">
<label for="auteur">Votre nom (facultatif)</label>
<input type="text" id="auteur" name="auteur" maxlength="60" placeholder="Anonyme, ou comme vous voulez…">
<label for="message">Votre commentaire</label>
<textarea id="message" name="message" maxlength="2000" required placeholder="Dites bonjour, partagez une idée, remerciez quelqu'un…"></textarea>
<div class="actions">
<button type="submit">Send</button>
</div>
</form>
</section>
{% if commentaires %}
<p class="compteur"><b>{{ commentaires|length }}</b> message{{ 's' if commentaires|length > 1 else '' }} sur le site 🚀</p>
{% endif %}
{% for c in commentaires %}
<article class="commentaire">
<div class="meta">
<span class="auteur">{{ c.auteur }}</span>
<span class="date">{{ c.date_creation }}</span>
</div>
<p class="message">{{ c.message }}</p>
</article>
{% endfor %}
{% if commentaires %}
<div class="reset">
<form action="{{ url_for('reset') }}" method="post">
<button type="submit">Réinitialiser les commentaires</button>
</form>
</div>
{% endif %}
</main>
<footer>
Demo-docker <a href="https://git.selfitdeploy.com/Willy/-/packages/container/demo-docker/1.0.0" style="text-decoration:none"><span>v1.0.0</span></a><br>
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/">
<img src="https://img.shields.io/badge/License-CC_BY--NC--SA_4.0-lightgrey.svg" alt="License CC BY-NC-SA 4.0"/>
</a>
</footer>
</body>
</html>

1
requirements.txt Normal file
View file

@ -0,0 +1 @@
Flask>=3.0,<4.0