Petite page web pour présenter une action de ramassage des déchets et permettre aux visiteurs d'envoyer un cœur de soutien.
- Frontend Vite + TypeScript, sans framework.
- Backend Rust Axum + SQLx + SQLite embarqué.
- Déploiement en 2 containers :
frontendpublic etbackendprivé. - SQLite persisté dans
backend/data/hearts.dbsur l'hôte et monté dans/datacôté conteneur. - Les cœurs acceptés sont stockés comme des événements datés.
La page publique est prévue pour https://lovebin.uggla.fr.
docker compose up --buildOu selon l'environnement :
podman compose up --build
podman-compose up --buildLa page est ensuite disponible sur :
http://localhost:8080
Le backend n'est pas publié sur l'hôte. Le frontend sert les fichiers statiques et proxifie /api/* vers le service backend sur le réseau interne Compose.
La base SQLite est créée sur le filesystem local dans :
backend/data/hearts.db
En production, ce chemin correspond à /home/uggla/lovebin/backend/data/hearts.db.
Backend :
cd backend
DATABASE_URL=sqlite:data/hearts.db COOKIE_SECURE=false cargo runFrontend :
cd frontend
npm install
npm run devLe serveur Vite proxifie /api vers http://127.0.0.1:3000.
Variables d'environnement disponibles :
DATABASE_URL=sqlite:/data/hearts.db
BIND_ADDR=0.0.0.0:3000
COOKIE_NAME=cleanup_heart_vote
COOKIE_SECURE=true
COOKIE_SAME_SITE=Lax
VOTE_WINDOW_SECONDS=172800
En local HTTP, COOKIE_SECURE=false est nécessaire pour que le navigateur conserve le cookie. En production HTTPS, utiliser COOKIE_SECURE=true.
Le compteur est calculé depuis l'historique des cœurs. Les anciennes données du compteur initial ne sont pas conservées par la migration vers ce modèle événementiel.
GET /api/hearts{
"count": 123,
"already_voted": false
}POST /api/heartsSuccès :
{
"count": 124,
"voted": true
}Déjà voté :
{
"count": 124,
"voted": false,
"reason": "already_voted",
"retry_after_seconds": 172800
}GET /api/hearts/historyRetourne les timestamps publics des cœurs acceptés, triés du plus ancien au plus récent :
{
"events": [
"2026-05-26T12:34:56.789Z",
"2026-05-26T13:10:22.123Z"
]
}Le frontend contient déjà la section :
<section id="photos">
<h2>Les photos de la journée</h2>
</section>Elle peut recevoir plus tard une galerie responsive ou un carrousel simple avec des images statiques.
Les images actuelles sont servies depuis frontend/public/photos/ et sont embarquées dans l'image frontend au build.
Ce projet est distribué sous licence Apache 2.0. Voir le fichier LICENSE pour le texte complet.