chore(fixtures): enrichit les vaults de test (frontmatter complet, types fichiers pour vues/aper/us, dossiers avec accents+espaces, scripts dupliques, images/logs) + purge residus e2e-diagram
This commit is contained in:
+5
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
|||||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||||
> [Unreleased](#unreleased). La dernière version livrée est **2.14.0**.
|
> [Unreleased](#unreleased). La dernière version livrée est **2.14.1**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,6 +14,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.14.1] — 2026-09-22
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.14.0] — 2026-09-19
|
## [2.14.0] — 2026-09-19
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -927,8 +927,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.14.0).
|
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.14.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Projet : ObsiGate | Version : 2.14.0 | Dernière mise à jour : Juin 2026*
|
*Projet : ObsiGate | Version : 2.14.1 | Dernière mise à jour : Juin 2026*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -1096,8 +1096,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.14.0).
|
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.14.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Project: ObsiGate | Version: 2.14.0 | Last updated: May 2026*
|
*Project: ObsiGate | Version: 2.14.1 | Last updated: May 2026*
|
||||||
|
|||||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.14.0"
|
version = "2.14.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.14.0"
|
version = "2.14.1"
|
||||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
authors = ["Bruno Charest"]
|
authors = ["Bruno Charest"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
"productName": "ObsiGate",
|
"productName": "ObsiGate",
|
||||||
"version": "2.14.0",
|
"version": "2.14.1",
|
||||||
"identifier": "com.obsigate.desktop",
|
"identifier": "com.obsigate.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend",
|
"frontendDist": "../frontend",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# ObsiGate — Roadmap
|
# ObsiGate — Roadmap
|
||||||
|
|
||||||
> **Version :** 2.14.0 | **Dernière mise à jour :** 2026-09-19
|
> **Version :** 2.14.1 | **Dernière mise à jour :** 2026-09-22
|
||||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||||
> vers les fonctionnalités livrées.
|
> vers les fonctionnalités livrées.
|
||||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsigate",
|
"name": "obsigate",
|
||||||
"version": "2.14.0",
|
"version": "2.14.1",
|
||||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||||
"main": "patch.js",
|
"main": "patch.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
|||||||
|
Voici les trois niveaux de résumé pour le document **"Agents IA — Panorama Complet 2026"** :
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **1. En une phrase**
|
||||||
|
Ce document est **un panorama exhaustif et structuré des 72 agents IA disponibles en 2026**, classés par catégories, avec leurs descriptions, statuts, installations, utilisations et comparatifs techniques.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **2. En un paragraphe**
|
||||||
|
Le document **"Agents IA — Panorama Complet 2026"** est une **référence complète** pour comprendre, installer et utiliser les agents IA du marché. Il recense **72 agents** répartis en **16 catégories** (ex. : agents locaux, poids lourds open source, trésors open source, local-first, propriétaires, etc.). Chaque agent est détaillé avec sa **description**, ses **liens officiels** (page principale et documentation), son **statut** (actif, stagnant ou fermé), ses **étapes d'installation** (commandes précises) et son **mode d'utilisation** (exemples de commandes). Le document inclut également un **tableau récapitulatif comparatif** (stars GitHub, type, compatibilité locale, etc.), des **recommandations**, une **matrice de décision**, ainsi que des sections dédiées aux **benchmarks**, **coûts**, **sécurité** et **glossaire**. Les sources sont vérifiées et mises à jour en **août 2026**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **3. En une page**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Contexte**
|
||||||
|
Le document **"Agents IA — Panorama Complet 2026"**, rédigé par **Bruno Charest**, est une **ressource de référence** pour les développeurs, chercheurs et utilisateurs souhaitant explorer l’écosystème des agents IA. Il s’appuie sur des sources officielles (GitHub, arXiv, blogs spécialisés) et est **mis à jour au 12 août 2026**. L’objectif est de fournir une **vue d’ensemble structurée** des agents disponibles, leurs fonctionnalités, et leurs spécificités techniques.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **Structure et contenu**
|
||||||
|
Le document est organisé en **23 sections**, dont :
|
||||||
|
- **Tableau récapitulatif comparatif** : Une vue synthétique des 72 agents, avec des colonnes pour le **nom**, la **catégorie**, le **type** (OSS, propriétaire, etc.), le **nombre d’étoiles GitHub**, le **statut** (🟢 actif, 🟡 stagnant, 🔴 fermé), la **compatibilité locale** (✅/~/❌), et les **méthodes d’installation**.
|
||||||
|
- **Catégories d’agents** :
|
||||||
|
- **Agents installés localement** (ex. : Claude Code, Hermes Agent, Atomic Agent, Pi, Tiny Agents, NemoClaw).
|
||||||
|
- **Prime Agent** : Agent open-source basé sur le modèle **RLM** (Recursive Language Model), avec des fonctionnalités avancées comme la mémoire persistante et l’auto-amélioration.
|
||||||
|
- **Poids lourds open source** (ex. : OpenCode, Claw Code, Gemini CLI, Codex CLI, OpenHands, Goose, Aider).
|
||||||
|
- **Trésors open source** : Agents optimisés pour des cas d’usage spécifiques (ex. : jcode en Rust, agentty en C++26, NullClaw en Zig).
|
||||||
|
- **Agents local-first** : Solutions conçues pour fonctionner **100 % localement** (ex. : openyak, Kun, iPolloWork).
|
||||||
|
- **Écosystème OpenClaw** : Une famille d’agents légers et sécurisés (ex. : OpenClaw, ZeroClaw, NanoClaw).
|
||||||
|
- **Agents propriétaires** : Outils fermés mais populaires (ex. : Cursor CLI, Warp, GitHub Copilot CLI, Devin).
|
||||||
|
- **Sections transverses** :
|
||||||
|
- **Orchestrateurs & harnesses** : Outils pour gérer des workflows multi-agents.
|
||||||
|
- **Infrastructure & outils** : Solutions pour déployer et superviser des agents.
|
||||||
|
- **Benchmarks & évaluation** : Méthodes pour tester les performances des agents.
|
||||||
|
- **Coût & modèles** : Analyse des coûts associés (abonnements, inférence locale, etc.).
|
||||||
|
- **Sécurité & gouvernance** : Bonnes pratiques pour un usage sécurisé.
|
||||||
|
- **Glossaire** : Définitions des termes techniques (ex. : RLM, MCP, LSP).
|
||||||
|
|
||||||
|
---
|
||||||
|
#### **Points clés**
|
||||||
|
- **Diversité des agents** : Le document couvre des agents **génériques** (ex. : Claude Code, OpenCode) et **spécialisés** (ex. : SWE-agent pour résoudre des issues GitHub, Plandex pour le "plan-first").
|
||||||
|
- **Statuts variés** : Certains projets sont **actifs** (ex. : Prime Agent, OpenCode), tandis que d’autres sont **fermés** (ex. : Claw Code, transformé en musée) ou **stagnants** (ex. : Plandex, Groq Code CLI).
|
||||||
|
- **Installation et utilisation** : Chaque agent est accompagné de **commandes précises** pour son installation (ex. : `npm install -g @anthropic-ai/claude-code` pour Claude Code) et son utilisation (ex. : `claude "explique ce repo"`).
|
||||||
|
- **Approches techniques** :
|
||||||
|
- **Local-first** : Agents conçus pour fonctionner **hors ligne** (ex. : Atomic Agent, NullClaw).
|
||||||
|
- **Multi-modèles** : Compatibilité avec plusieurs fournisseurs de LLM (ex. : Hermes Agent supporte 300+ providers).
|
||||||
|
- **Sandboxing** : Solutions pour exécuter du code en toute sécurité (ex. : NemoClaw avec des politiques réseau strictes).
|
||||||
|
- **Comparatifs** : Le tableau récapitulatif permet de **filtrer rapidement** les agents en fonction de critères comme la compatibilité locale ou le nombre d’étoiles GitHub.
|
||||||
|
|
||||||
|
---
|
||||||
|
#### **Conclusions**
|
||||||
|
Ce document est une **mine d’informations** pour :
|
||||||
|
- **Choisir un agent** en fonction de ses besoins (ex. : développement local, collaboration en équipe, résolution de bugs).
|
||||||
|
- **Comprendre les tendances** du marché (ex. : montée en puissance des agents **local-first**, adoption croissante des modèles **open-source**).
|
||||||
|
- **Installer et configurer** un agent rapidement grâce aux **instructions pas-à-pas**.
|
||||||
|
- **Comparer les solutions** via des critères objectifs (statut, stars GitHub, compatibilité locale).
|
||||||
|
|
||||||
|
Il s’adresse aussi bien aux **débutants** qu’aux **experts**, avec des sections adaptées à chaque niveau de connaissance. Les **sources citées** (GitHub, arXiv, blogs) garantissent la **fiabilité** des informations.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Beloeil et ses Villains
|
||||||
|
|
||||||
|
Plongez dans un univers où les légendes locales sortent de l’ombre pour hanter les vivants. Entre les ruelles pavées, usées par des siècles de secrets, et les forêts mystérieuses, dont les arbres semblent murmurer des avertissements aux voyageurs égarés, **Beloeil** est une ville où chaque pierre, chaque souffle de vent, porte le poids d’un passé maudit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Les Gardiens de l’Obscurité
|
||||||
|
Les méchants de Beloeil ne sont pas de simples antagonistes. Ils sont les **gardiens d’un héritage sombre**, tissé de magie ancienne, de rivalités familiales et de serments brisés. Leur présence imprègne la ville d’une atmosphère où la frontière entre le bien et le mal s’estompe, où chaque choix semble mener à une nouvelle malédiction.
|
||||||
|
|
||||||
|
- **Le Comte Déchu** : Son armure rouillée grince encore entre les murs de son château en ruines. Ses anciens sujets, transformés en spectres, murmurent des prières pour une rédemption qui ne viendra jamais. Son règne, autrefis glorieux, n’est plus qu’un écho de trahisons et de regrets.
|
||||||
|
- **La Sorcière des Marais** : Ses potions ne se contentent pas d’empoisonner les corps, elles **corrompent les âmes**. Les villageois qui osent s’aventurer près de son repaire reviennent transformés, assoiffés de vengeance, hantés par des voix qu’ils ne reconnaissent plus.
|
||||||
|
- **Les Frères Jumeaux** : Autrefois unis par un pacte de sang, l’un a trahi l’autre par ambition. Leur malédiction les lie désormais dans une **danse macabre**, condamnés à se combattre pour l’éternité, leurs lames s’entrechoquant dans un ballet sans fin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Une Ville de Secrets et de Retournements
|
||||||
|
Beloeil est un lieu où **les apparences trompent**. Le héros tant admiré pourrait cacher un passé taché de crimes. La malédiction qui pèse sur la ville pourrait trouver son origine dans un **amour interdit** entre une fée et un mortel. Chaque recoin cache une intrigue, chaque murmure un avertissement.
|
||||||
|
|
||||||
|
Le surnaturel y est omniprésent : sorts oubliés, artefacts maudits, créatures légendaires. **La magie et l’horreur s’y mêlent**, plongeant les lecteurs dans un monde où chaque page révèle un nouveau mystère, une nouvelle terreur.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Une Histoire à Écrire
|
||||||
|
Beloeil est une toile de fond parfaite pour une narration **riche en intrigues complexes et en retournements inattendus**. Que ce soit à travers des quêtes désespérées, des alliances fragiles ou des trahisons imprévisibles, cette ville offre un terrain fertile pour explorer **l’obscurité de l’âme humaine et la puissance des légendes**.
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
|||||||
# Nouveau fichier dans Dir
|
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PostgreSQL backup script for HabitForge
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_DIR="/backups"
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/habitforge_backup_${TIMESTAMP}.sql.gz"
|
||||||
|
RETENTION_DAYS=7
|
||||||
|
|
||||||
|
# Database settings (from environment)
|
||||||
|
DB_HOST="${POSTGRES_HOST:-db}"
|
||||||
|
DB_PORT="${POSTGRES_PORT:-5432}"
|
||||||
|
DB_NAME="${POSTGRES_DB:-habitforge}"
|
||||||
|
DB_USER="${POSTGRES_USER:-habitforge}"
|
||||||
|
|
||||||
|
# Create backup directory if it doesn't exist
|
||||||
|
mkdir -p "${BACKUP_DIR}"
|
||||||
|
|
||||||
|
echo "Starting backup at $(date)"
|
||||||
|
|
||||||
|
# Perform backup
|
||||||
|
PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \
|
||||||
|
-h "${DB_HOST}" \
|
||||||
|
-p "${DB_PORT}" \
|
||||||
|
-U "${DB_USER}" \
|
||||||
|
-d "${DB_NAME}" \
|
||||||
|
--format=plain \
|
||||||
|
--no-owner \
|
||||||
|
--no-acl | gzip > "${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Check if backup was successful
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Backup completed successfully: ${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Get file size
|
||||||
|
SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||||
|
echo "Backup size: ${SIZE}"
|
||||||
|
|
||||||
|
# Remove old backups (older than RETENTION_DAYS)
|
||||||
|
echo "Removing backups older than ${RETENTION_DAYS} days..."
|
||||||
|
find "${BACKUP_DIR}" -name "habitforge_backup_*.sql.gz" -type f -mtime +${RETENTION_DAYS} -delete
|
||||||
|
|
||||||
|
# List remaining backups
|
||||||
|
echo "Current backups:"
|
||||||
|
ls -lh "${BACKUP_DIR}"/habitforge_backup_*.sql.gz 2>/dev/null || echo "No backups found"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Backup failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Backup finished at $(date)"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script to import Zepp health data from export-zepp directory into HabitForge database.
|
||||||
|
Usage: python scripts/import_zepp_data.py [--user-id USER_ID]
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Add parent directory to path to import backend modules
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from backend.integrations.import_zepp import main as import_main, USER_ID as DEFAULT_USER_ID
|
||||||
|
from backend.integrations import import_zepp
|
||||||
|
|
||||||
|
def run_import(user_id: int = None):
|
||||||
|
"""Run the Zepp data import."""
|
||||||
|
if user_id:
|
||||||
|
# Override the default user ID
|
||||||
|
import_zepp.USER_ID = user_id
|
||||||
|
print(f"Importing data for user ID: {user_id}")
|
||||||
|
else:
|
||||||
|
print(f"Importing data for default user ID: {DEFAULT_USER_ID}")
|
||||||
|
|
||||||
|
import_main()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Import Zepp health data into HabitForge")
|
||||||
|
parser.add_argument(
|
||||||
|
"--user-id",
|
||||||
|
type=int,
|
||||||
|
help="User ID to import data for (default: 1)",
|
||||||
|
default=None
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
run_import(args.user_id)
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
"""
|
||||||
|
Migration script from SQLite to PostgreSQL.
|
||||||
|
|
||||||
|
This script migrates data from the existing SQLite database to PostgreSQL.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python scripts/migrate_to_postgres.py
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- PostgreSQL database must be running
|
||||||
|
- DATABASE_URL environment variable must point to PostgreSQL
|
||||||
|
- SQLite database must exist at data/habitforge.db
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Add parent directory to path
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine, text
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
from backend import models
|
||||||
|
from backend.database import Base, SessionLocal as SQLiteSessionLocal
|
||||||
|
|
||||||
|
# PostgreSQL connection
|
||||||
|
POSTGRES_URL = os.getenv("DATABASE_URL", "postgresql://habitforge:habitforge@localhost:5432/habitforge")
|
||||||
|
|
||||||
|
def migrate_data():
|
||||||
|
"""Migrate data from SQLite to PostgreSQL."""
|
||||||
|
print("Starting migration from SQLite to PostgreSQL...")
|
||||||
|
|
||||||
|
# Create PostgreSQL engine
|
||||||
|
postgres_engine = create_engine(POSTGRES_URL)
|
||||||
|
PostgresSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=postgres_engine)
|
||||||
|
|
||||||
|
# Create tables in PostgreSQL
|
||||||
|
print("Creating tables in PostgreSQL...")
|
||||||
|
Base.metadata.create_all(bind=postgres_engine)
|
||||||
|
|
||||||
|
# Get SQLite session
|
||||||
|
sqlite_session = SQLiteSessionLocal()
|
||||||
|
|
||||||
|
# Get PostgreSQL session
|
||||||
|
postgres_session = PostgresSessionLocal()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Migrate Users
|
||||||
|
print("Migrating Users...")
|
||||||
|
users = sqlite_session.query(models.User).all()
|
||||||
|
for user in users:
|
||||||
|
new_user = models.User(
|
||||||
|
id=user.id,
|
||||||
|
username=user.username,
|
||||||
|
hashed_password=user.hashed_password
|
||||||
|
)
|
||||||
|
postgres_session.add(new_user)
|
||||||
|
postgres_session.commit()
|
||||||
|
print(f"Migrated {len(users)} users")
|
||||||
|
|
||||||
|
# Migrate Challenges
|
||||||
|
print("Migrating Challenges...")
|
||||||
|
challenges = sqlite_session.query(models.Challenge).all()
|
||||||
|
for challenge in challenges:
|
||||||
|
new_challenge = models.Challenge(
|
||||||
|
id=challenge.id,
|
||||||
|
name=challenge.name,
|
||||||
|
period_type=challenge.period_type,
|
||||||
|
daily_target=challenge.daily_target,
|
||||||
|
start_date=challenge.start_date,
|
||||||
|
end_date=challenge.end_date,
|
||||||
|
display_order=challenge.display_order,
|
||||||
|
user_id=challenge.user_id,
|
||||||
|
icon=challenge.icon,
|
||||||
|
image_url=challenge.image_url,
|
||||||
|
description=challenge.description,
|
||||||
|
unit_type=challenge.unit_type,
|
||||||
|
rest_days=challenge.rest_days,
|
||||||
|
frequency=challenge.frequency
|
||||||
|
)
|
||||||
|
postgres_session.add(new_challenge)
|
||||||
|
postgres_session.commit()
|
||||||
|
print(f"Migrated {len(challenges)} challenges")
|
||||||
|
|
||||||
|
# Migrate Tracking
|
||||||
|
print("Migrating Tracking...")
|
||||||
|
trackings = sqlite_session.query(models.Tracking).all()
|
||||||
|
for tracking in trackings:
|
||||||
|
new_tracking = models.Tracking(
|
||||||
|
id=tracking.id,
|
||||||
|
challenge_id=tracking.challenge_id,
|
||||||
|
date=tracking.date,
|
||||||
|
reps=tracking.reps,
|
||||||
|
completed=tracking.completed,
|
||||||
|
notes=tracking.notes
|
||||||
|
)
|
||||||
|
postgres_session.add(new_tracking)
|
||||||
|
postgres_session.commit()
|
||||||
|
print(f"Migrated {len(trackings)} trackings")
|
||||||
|
|
||||||
|
# Migrate DailyHealthMetrics
|
||||||
|
print("Migrating DailyHealthMetrics...")
|
||||||
|
metrics = sqlite_session.query(models.DailyHealthMetrics).all()
|
||||||
|
for metric in metrics:
|
||||||
|
new_metric = models.DailyHealthMetrics(
|
||||||
|
id=metric.id,
|
||||||
|
user_id=metric.user_id,
|
||||||
|
date=metric.date,
|
||||||
|
step_count=metric.step_count,
|
||||||
|
calories_burned=metric.calories_burned,
|
||||||
|
distance_meters=metric.distance_meters,
|
||||||
|
sleep_duration_minutes=metric.sleep_duration_minutes,
|
||||||
|
deep_sleep_minutes=metric.deep_sleep_minutes,
|
||||||
|
light_sleep_minutes=metric.light_sleep_minutes,
|
||||||
|
rem_sleep_minutes=metric.rem_sleep_minutes,
|
||||||
|
awake_duration_minutes=metric.awake_duration_minutes,
|
||||||
|
avg_heart_rate=metric.avg_heart_rate,
|
||||||
|
min_heart_rate=metric.min_heart_rate,
|
||||||
|
max_heart_rate=metric.max_heart_rate,
|
||||||
|
avg_spo2=metric.avg_spo2,
|
||||||
|
pai_score=metric.pai_score,
|
||||||
|
weight=metric.weight,
|
||||||
|
resting_heart_rate=metric.resting_heart_rate,
|
||||||
|
hrv=metric.hrv
|
||||||
|
)
|
||||||
|
postgres_session.add(new_metric)
|
||||||
|
postgres_session.commit()
|
||||||
|
print(f"Migrated {len(metrics)} daily health metrics")
|
||||||
|
|
||||||
|
print("\nMigration completed successfully!")
|
||||||
|
print(f"Total records migrated:")
|
||||||
|
print(f" - Users: {len(users)}")
|
||||||
|
print(f" - Challenges: {len(challenges)}")
|
||||||
|
print(f" - Trackings: {len(trackings)}")
|
||||||
|
print(f" - DailyHealthMetrics: {len(metrics)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\nError during migration: {e}")
|
||||||
|
postgres_session.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
sqlite_session.close()
|
||||||
|
postgres_session.close()
|
||||||
|
postgres_engine.dispose()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Check if SQLite database exists
|
||||||
|
if not os.path.exists("data/habitforge.db"):
|
||||||
|
print("Error: SQLite database not found at data/habitforge.db")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Confirm migration
|
||||||
|
print("This will migrate data from SQLite to PostgreSQL.")
|
||||||
|
print(f"PostgreSQL URL: {POSTGRES_URL}")
|
||||||
|
response = input("Do you want to continue? (yes/no): ")
|
||||||
|
|
||||||
|
if response.lower() != "yes":
|
||||||
|
print("Migration cancelled.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
migrate_data()
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Production startup script for HabitForge
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Starting HabitForge in production mode..."
|
||||||
|
|
||||||
|
# Wait for database to be ready
|
||||||
|
echo "Waiting for database..."
|
||||||
|
while ! pg_isready -h ${POSTGRES_HOST:-db} -U ${POSTGRES_USER:-habitforge} -d ${POSTGRES_DB:-habitforge} > /dev/null 2>&1; do
|
||||||
|
echo "Database is unavailable - sleeping"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Database is ready!"
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
echo "Running database migrations..."
|
||||||
|
alembic upgrade head
|
||||||
|
|
||||||
|
# Start Gunicorn
|
||||||
|
echo "Starting Gunicorn..."
|
||||||
|
exec gunicorn backend.main:app \
|
||||||
|
--workers ${GUNICORN_WORKERS:-4} \
|
||||||
|
--worker-class uvicorn.workers.UvicornWorker \
|
||||||
|
--bind 0.0.0.0:8000 \
|
||||||
|
--access-logfile - \
|
||||||
|
--error-logfile - \
|
||||||
|
--log-level ${LOG_LEVEL:-info}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "E_M7axF81tmVRs3_iSNTB",
|
||||||
|
"type": "rectangle",
|
||||||
|
"x": 474,
|
||||||
|
"y": 146.33334350585938,
|
||||||
|
"width": 415.3333740234375,
|
||||||
|
"height": 216.66665649414062,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#e03131",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"frameId": null,
|
||||||
|
"index": "a0",
|
||||||
|
"roundness": {
|
||||||
|
"type": 3
|
||||||
|
},
|
||||||
|
"seed": 975148614,
|
||||||
|
"version": 75,
|
||||||
|
"versionNonce": 1779287411,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"id": "9M-JFwSrjH6KSHIaBvvTI",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1789701383205,
|
||||||
|
"created": 1789695569357,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "r0qk76e8Zwvzg03cCJTet",
|
||||||
|
"type": "diamond",
|
||||||
|
"x": 602,
|
||||||
|
"y": 154.33334350585938,
|
||||||
|
"width": 195.3333740234375,
|
||||||
|
"height": 202,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#f08c00",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"frameId": null,
|
||||||
|
"index": "a1",
|
||||||
|
"roundness": {
|
||||||
|
"type": 2
|
||||||
|
},
|
||||||
|
"seed": 1824592326,
|
||||||
|
"version": 88,
|
||||||
|
"versionNonce": 1410387869,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1789701386097,
|
||||||
|
"created": 1789695572009,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9KK6ZVdnduPeYFFFCFAsy",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 980.6668701171875,
|
||||||
|
"y": 170.0000457763672,
|
||||||
|
"width": 162.66668701171875,
|
||||||
|
"height": 141.3333740234375,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#1e1e1e",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"frameId": null,
|
||||||
|
"index": "a2",
|
||||||
|
"roundness": {
|
||||||
|
"type": 2
|
||||||
|
},
|
||||||
|
"seed": 1669693053,
|
||||||
|
"version": 190,
|
||||||
|
"versionNonce": 1391288573,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"id": "9M-JFwSrjH6KSHIaBvvTI",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1789701376494,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9M-JFwSrjH6KSHIaBvvTI",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 895.3334350585938,
|
||||||
|
"y": 248.66673278808594,
|
||||||
|
"width": 82.66668701171875,
|
||||||
|
"height": 3.333343505859375,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#e03131",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"frameId": null,
|
||||||
|
"index": "a3",
|
||||||
|
"roundness": {
|
||||||
|
"type": 2
|
||||||
|
},
|
||||||
|
"seed": 1977221331,
|
||||||
|
"version": 35,
|
||||||
|
"versionNonce": 23426611,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1789701380015,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
82.66668701171875,
|
||||||
|
-3.333343505859375
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "E_M7axF81tmVRs3_iSNTB",
|
||||||
|
"focus": 0.022412363413033574,
|
||||||
|
"gap": 6.00006103515625
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "9KK6ZVdnduPeYFFFCFAsy",
|
||||||
|
"focus": -0.017303733989364446,
|
||||||
|
"gap": 2.836434396181596
|
||||||
|
},
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow",
|
||||||
|
"elbowed": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"showWelcomeScreen": false,
|
||||||
|
"theme": "dark",
|
||||||
|
"collaborators": {},
|
||||||
|
"currentChartType": "bar",
|
||||||
|
"currentItemBackgroundColor": "transparent",
|
||||||
|
"currentItemEndArrowhead": "arrow",
|
||||||
|
"currentItemFillStyle": "solid",
|
||||||
|
"currentItemFontFamily": 5,
|
||||||
|
"currentItemFontSize": 20,
|
||||||
|
"currentItemOpacity": 100,
|
||||||
|
"currentItemRoughness": 1,
|
||||||
|
"currentItemStartArrowhead": null,
|
||||||
|
"currentItemStrokeColor": "#f08c00",
|
||||||
|
"currentItemRoundness": "round",
|
||||||
|
"currentItemArrowType": "round",
|
||||||
|
"currentItemStrokeStyle": "solid",
|
||||||
|
"currentItemStrokeWidth": 2,
|
||||||
|
"currentItemTextAlign": "left",
|
||||||
|
"currentHoveredFontFamily": null,
|
||||||
|
"cursorButton": "up",
|
||||||
|
"activeEmbeddable": null,
|
||||||
|
"newElement": null,
|
||||||
|
"editingTextElement": null,
|
||||||
|
"editingGroupId": null,
|
||||||
|
"editingLinearElement": null,
|
||||||
|
"activeTool": {
|
||||||
|
"type": "selection",
|
||||||
|
"customType": null,
|
||||||
|
"locked": false,
|
||||||
|
"lastActiveTool": {
|
||||||
|
"type": "selection",
|
||||||
|
"customType": null,
|
||||||
|
"locked": false,
|
||||||
|
"lastActiveTool": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"penMode": false,
|
||||||
|
"penDetected": false,
|
||||||
|
"errorMessage": null,
|
||||||
|
"exportBackground": true,
|
||||||
|
"exportScale": 1,
|
||||||
|
"exportEmbedScene": false,
|
||||||
|
"exportWithDarkMode": false,
|
||||||
|
"fileHandle": null,
|
||||||
|
"gridSize": 20,
|
||||||
|
"gridStep": 5,
|
||||||
|
"gridModeEnabled": false,
|
||||||
|
"isBindingEnabled": true,
|
||||||
|
"defaultSidebarDockedPreference": false,
|
||||||
|
"isLoading": false,
|
||||||
|
"isResizing": false,
|
||||||
|
"isRotating": false,
|
||||||
|
"lastPointerDownWith": "mouse",
|
||||||
|
"multiElement": null,
|
||||||
|
"name": "Untitled-2026-09-17-2148",
|
||||||
|
"contextMenu": null,
|
||||||
|
"openMenu": null,
|
||||||
|
"openPopup": null,
|
||||||
|
"openSidebar": null,
|
||||||
|
"openDialog": null,
|
||||||
|
"pasteDialog": {
|
||||||
|
"shown": false,
|
||||||
|
"data": null
|
||||||
|
},
|
||||||
|
"previousSelectedElementIds": {
|
||||||
|
"E_M7axF81tmVRs3_iSNTB": true
|
||||||
|
},
|
||||||
|
"resizingElement": null,
|
||||||
|
"scrolledOutside": false,
|
||||||
|
"scrollX": -23.33343505859375,
|
||||||
|
"scrollY": 101.99995422363281,
|
||||||
|
"selectedElementIds": {
|
||||||
|
"r0qk76e8Zwvzg03cCJTet": true
|
||||||
|
},
|
||||||
|
"hoveredElementIds": {},
|
||||||
|
"selectedGroupIds": {},
|
||||||
|
"selectedElementsAreBeingDragged": false,
|
||||||
|
"selectionElement": null,
|
||||||
|
"shouldCacheIgnoreZoom": false,
|
||||||
|
"stats": {
|
||||||
|
"open": false,
|
||||||
|
"panels": 3
|
||||||
|
},
|
||||||
|
"startBoundElement": null,
|
||||||
|
"suggestedBindings": [],
|
||||||
|
"frameRendering": {
|
||||||
|
"enabled": true,
|
||||||
|
"clip": true,
|
||||||
|
"name": true,
|
||||||
|
"outline": true
|
||||||
|
},
|
||||||
|
"frameToHighlight": null,
|
||||||
|
"editingFrame": null,
|
||||||
|
"elementsToHighlight": null,
|
||||||
|
"toast": null,
|
||||||
|
"viewBackgroundColor": "#ffffff",
|
||||||
|
"zenModeEnabled": false,
|
||||||
|
"zoom": {
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
"viewModeEnabled": false,
|
||||||
|
"pendingImageElementId": null,
|
||||||
|
"showHyperlinkPopup": false,
|
||||||
|
"selectedLinearElement": null,
|
||||||
|
"snapLines": [],
|
||||||
|
"originSnapOffset": null,
|
||||||
|
"objectsSnapModeEnabled": false,
|
||||||
|
"userToFollow": null,
|
||||||
|
"followedBy": {},
|
||||||
|
"isCropping": false,
|
||||||
|
"croppingElementId": null,
|
||||||
|
"searchMatches": [],
|
||||||
|
"offsetLeft": 0,
|
||||||
|
"offsetTop": 0,
|
||||||
|
"width": 1280,
|
||||||
|
"height": 800
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
@@ -1,3 +1,32 @@
|
|||||||
|
---
|
||||||
|
titre: ObsiGate — Analyse complète & Recommandations V1.1
|
||||||
|
auteur: Hermes-Claw
|
||||||
|
tags:
|
||||||
|
- analyse
|
||||||
|
- obsigate
|
||||||
|
- recommandations
|
||||||
|
- sécurité
|
||||||
|
- roadmap
|
||||||
|
- audit
|
||||||
|
- v1.1
|
||||||
|
aliases:
|
||||||
|
- ObsiGate Analyse
|
||||||
|
- Audit ObsiGate V1.1
|
||||||
|
catégorie: Analyse Technique
|
||||||
|
NomDeVoute: TestVault
|
||||||
|
Description: Analyse complète et recommandations pour ObsiGate, incluant fonctionnalités, forces, points d'amélioration, roadmap et checklist de professionnalisation.
|
||||||
|
creation_date: 2026-05-25T00:00:00+02:00
|
||||||
|
modification_date: 2026-10-07T12:00:00+02:00
|
||||||
|
status: finalisé
|
||||||
|
publish: true
|
||||||
|
favoris: true
|
||||||
|
template: false
|
||||||
|
task: false
|
||||||
|
archive: false
|
||||||
|
draft: false
|
||||||
|
private: false
|
||||||
|
---
|
||||||
|
|
||||||
# ObsiGate — Analyse complète & Recommandations V1.1
|
# ObsiGate — Analyse complète & Recommandations V1.1
|
||||||
|
|
||||||
> **Date :** 2026-05-25 | **Analyste :** Hermes-Claw + Audit Code Source | **Version testée :** 1.4.0 (backend) / 1.5.0 (frontend) — http://openclaw1.dev.home:2020
|
> **Date :** 2026-05-25 | **Analyste :** Hermes-Claw + Audit Code Source | **Version testée :** 1.4.0 (backend) / 1.5.0 (frontend) — http://openclaw1.dev.home:2020
|
||||||
|
|||||||
Binary file not shown.
+24
-8
@@ -7,16 +7,32 @@ publish: true
|
|||||||
date: 2025-01-15
|
date: 2025-01-15
|
||||||
---
|
---
|
||||||
|
|
||||||
# Bienvenue dans le vault de test
|
# 📌 **Bienvenue dans TestVault**
|
||||||
|
|
||||||
Ceci est un document de test pour [[ObsiGate]].
|
**TestVault** est votre espace centralisé pour organiser, structurer et optimiser la gestion de vos notes et connaissances. Conçue pour allier **efficacité** et **simplicité**, cette voute vous permet de travailler de manière intuitive tout en bénéficiant d'outils puissants.
|
||||||
|
|
||||||
## Sections
|
---
|
||||||
|
|
||||||
- [[Projets/Projet Alpha]] - Un projet en cours
|
## 🚀 **Fonctionnalités clés**
|
||||||
- [[Notes/Configuration serveur]] - Documentation technique
|
|
||||||
- [[Recettes/Pâtes carbonara]] - Une recette
|
|
||||||
|
|
||||||
## Tags
|
✅ **Gestion intelligente des notes**
|
||||||
|
- Créez, modifiez et organisez vos notes en toute simplicité.
|
||||||
|
- Accédez rapidement à vos informations grâce à une structure claire.
|
||||||
|
|
||||||
|
✅ **Organisation avancée**
|
||||||
|
- Utilisez des **tags** et des **catégories** pour classer vos notes de manière logique.
|
||||||
|
- Retrouvez vos informations en un clin d'œil.
|
||||||
|
|
||||||
|
✅ **Recherche rapide et efficace**
|
||||||
|
- Trouvez ce dont vous avez besoin en quelques secondes.
|
||||||
|
|
||||||
|
✅ **Synchronisation multi-appareils**
|
||||||
|
- Accédez à vos notes où que vous soyez, sur tous vos appareils.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📂 **Structure de la voute**
|
||||||
|
Cette voute est conçue pour être **modulaire** et **évolutive**. Vous pouvez l'adapter selon vos besoins pour une expérience personnalisée.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
#accueil #important #test
|
|
||||||
|
|||||||
+213
-12
@@ -10,6 +10,35 @@ title: Docker Guide
|
|||||||
|
|
||||||
# Docker Guide
|
# Docker Guide
|
||||||
|
|
||||||
|
## Installation de Docker
|
||||||
|
|
||||||
|
### Sur Linux (Debian/Ubuntu)
|
||||||
|
```bash
|
||||||
|
# Mettre à jour les paquets
|
||||||
|
sudo apt update
|
||||||
|
|
||||||
|
# Installer les dépendances
|
||||||
|
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release
|
||||||
|
|
||||||
|
# Ajouter la clé GPG officielle de Docker
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||||
|
|
||||||
|
# Ajouter le dépôt Docker
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
|
||||||
|
# Installer Docker Engine
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y docker-ce docker-ce-cli containerd.io
|
||||||
|
|
||||||
|
# Vérifier l'installation
|
||||||
|
sudo docker run hello-world
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sur macOS/Windows
|
||||||
|
- Télécharger [Docker Desktop](https://www.docker.com/products/docker-desktop/) et suivre les instructions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Commandes essentielles
|
## Commandes essentielles
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -20,36 +49,208 @@ docker ps -a
|
|||||||
docker build -t myapp:latest .
|
docker build -t myapp:latest .
|
||||||
|
|
||||||
# Lancer un conteneur
|
# Lancer un conteneur
|
||||||
docker run -d -p 8080:80 myapp:latest
|
docker run -d -p 8080:80 --name my_container myapp:latest
|
||||||
|
|
||||||
# Voir les logs
|
# Voir les logs
|
||||||
docker logs -f container_name
|
docker logs -f my_container
|
||||||
|
|
||||||
|
# Arrêter un conteneur
|
||||||
|
docker stop my_container
|
||||||
|
|
||||||
|
# Supprimer un conteneur
|
||||||
|
docker rm my_container
|
||||||
|
|
||||||
|
# Supprimer une image
|
||||||
|
docker rmi myapp:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker Compose
|
---
|
||||||
|
|
||||||
```yaml
|
## Réseaux Docker
|
||||||
version: "3.9"
|
|
||||||
services:
|
Docker propose plusieurs types de réseaux pour isoler ou connecter vos conteneurs :
|
||||||
web:
|
|
||||||
image: nginx:alpine
|
| Type | Description | Exemple d'utilisation |
|
||||||
ports:
|
|------------|--------------------------------------|-------------------------------------|
|
||||||
- "80:80"
|
| **bridge** | Réseau par défaut pour les conteneurs | `docker run -p 8080:80 myapp` |
|
||||||
|
| **host** | Conteneur utilise l'IP de l'hôte | `docker run --network host myapp` |
|
||||||
|
| **overlay**| Pour les swarms multi-hôtes | Utilisé avec `docker stack deploy` |
|
||||||
|
|
||||||
|
**Exemple : Créer un réseau personnalisé**
|
||||||
|
```bash
|
||||||
|
docker network create my_network
|
||||||
|
docker run -d --network my_network --name db redis
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Volumes
|
## Volumes
|
||||||
|
|
||||||
Les volumes permettent de persister les données :
|
Les volumes permettent de persister les données :
|
||||||
|
|
||||||
- **Named volumes** : gérés par Docker
|
- **Named volumes** : gérés par Docker
|
||||||
|
```bash
|
||||||
|
docker volume create my_volume
|
||||||
|
docker run -v my_volume:/data myapp
|
||||||
|
```
|
||||||
|
|
||||||
- **Bind mounts** : montage direct depuis l'hôte
|
- **Bind mounts** : montage direct depuis l'hôte
|
||||||
|
```bash
|
||||||
|
docker run -v /chemin/absolu:/data myapp
|
||||||
|
```
|
||||||
|
|
||||||
- **tmpfs** : stockage en mémoire
|
- **tmpfs** : stockage en mémoire
|
||||||
|
```bash
|
||||||
|
docker run --tmpfs /data myapp
|
||||||
|
```
|
||||||
|
|
||||||
Voir aussi : [[Proxmox Setup]]
|
Voir aussi : [[Proxmox Setup]]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
Exemple de fichier `docker-compose.yml` avec variables d'environnement et dépendances :
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nginx:alpine
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
environment:
|
||||||
|
- NGINX_ENV=production
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
- frontend
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
redis_data:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
frontend:
|
||||||
|
driver: bridge
|
||||||
|
```
|
||||||
|
|
||||||
|
**Commandes utiles**
|
||||||
|
```bash
|
||||||
|
# Démarrer les services
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Arrêter les services
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
|
# Voir les logs
|
||||||
|
docker-compose logs -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dockerfile : Exemple commenté
|
||||||
|
|
||||||
|
Voici un exemple de `Dockerfile` pour une application Python (FastAPI) :
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Utiliser une image officielle Python comme base
|
||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
# Définir le répertoire de travail
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copier les fichiers de dépendances
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Installer les dépendances
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copier le reste du code source
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Exposer le port 8000
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
# Commande pour lancer l'application
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bonnes pratiques pour un Dockerfile** :
|
||||||
|
- Utiliser des images `slim` ou `alpine` pour réduire la taille.
|
||||||
|
- Multi-stage builds pour les applications compilées (Go, Rust, etc.).
|
||||||
|
- Toujours inclure un fichier `.dockerignore`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sécurité
|
||||||
|
|
||||||
|
### Bonnes pratiques
|
||||||
|
- **Éviter de lancer des conteneurs en tant que `root`** :
|
||||||
|
```bash
|
||||||
|
docker run --user 1000:1000 myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Rendre le système de fichiers en lecture seule** :
|
||||||
|
```bash
|
||||||
|
docker run --read-only myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Limiter les capacités du conteneur** :
|
||||||
|
```bash
|
||||||
|
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE myapp
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Scanner les images pour des vulnérabilités** :
|
||||||
|
Utiliser des outils comme [Trivy](https://github.com/aquasecurity/trivy) ou [Clair](https://github.com/quay/clair).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nettoyage des ressources
|
||||||
|
|
||||||
|
Pour libérer de l'espace, supprimez les ressources inutilisées :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Supprimer les conteneurs arrêtés
|
||||||
|
docker container prune
|
||||||
|
|
||||||
|
# Supprimer les images inutilisées
|
||||||
|
docker image prune -a
|
||||||
|
|
||||||
|
# Supprimer les volumes inutilisés
|
||||||
|
docker volume prune
|
||||||
|
|
||||||
|
# Supprimer les réseaux inutilisés
|
||||||
|
docker network prune
|
||||||
|
|
||||||
|
# Nettoyer TOUT (attention, irréversible !)
|
||||||
|
docker system prune -a --volumes
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Bonnes pratiques
|
## Bonnes pratiques
|
||||||
|
|
||||||
- [ ] Utiliser des images Alpine
|
- [x] Utiliser des images Alpine ou `slim`
|
||||||
- [ ] Multi-stage builds
|
- [x] Multi-stage builds pour les applications compilées
|
||||||
- [x] Fichier `.dockerignore`
|
- [x] Fichier `.dockerignore`
|
||||||
- [x] Un processus par conteneur
|
- [x] Un processus par conteneur
|
||||||
|
- [x] Limiter les ressources (CPU/mémoire) avec `--memory` et `--cpus`
|
||||||
|
- [x] Utiliser des secrets pour les variables sensibles (ex: `--env-file` ou `docker secret`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ressources utiles
|
||||||
|
- [Documentation officielle Docker](https://docs.docker.com/)
|
||||||
|
- [Docker Curriculum](https://docker-curriculum.com/)
|
||||||
|
- [Awesome Docker](https://github.com/veggiemonk/awesome-docker)
|
||||||
|
- [Tutoriel Docker Compose](https://docs.docker.com/compose/)
|
||||||
|
|
||||||
|
Voir aussi : [[Proxmox Setup]]
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Démonstration JavaScript pour le vault de test ObsiGate
|
||||||
|
const API_URL = "http://localhost:2020/api";
|
||||||
|
|
||||||
|
async function fetchVaults() {
|
||||||
|
const res = await fetch(`${API_URL}/vaults`);
|
||||||
|
if (!res.ok) throw new Error("HTTP " + res.status);
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchVaults().then((v) => console.log("vaults:", v));
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// Démonstration TypeScript pour le vault de test ObsiGate
|
||||||
|
interface Vault {
|
||||||
|
name: string;
|
||||||
|
files: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarize(vaults: Vault[]): string {
|
||||||
|
const total = vaults.reduce((acc, v) => acc + v.files, 0);
|
||||||
|
return `${vaults.length} vaults, ${total} fichiers`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default summarize;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Script de démonstration pour le vault de test ObsiGate."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
print("hello from ObsiGate test vault") # commentaire
|
||||||
|
data = {"vaults": ["IT", "Perso"], "count": 2}
|
||||||
|
print(json.dumps(data, indent=2))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main(sys.argv[1:]))
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestDemo(unittest.TestCase):
|
||||||
|
def test_true_is_true(self):
|
||||||
|
self.assertTrue(True) # trivial test
|
||||||
|
|
||||||
|
def test_number(self):
|
||||||
|
self.assertEqual(2 + 2, 4)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -1,7 +1,22 @@
|
|||||||
---
|
---
|
||||||
title: Pizza Maison
|
title: Pizza Maison
|
||||||
tags: [recette, pizza, rapide]
|
tags: [recette, pizza, rapide, cuisine maison]
|
||||||
date: 2025-03-15
|
date: 2025-03-15
|
||||||
|
modification_date: 2025-10-31T14:30:00+01:00
|
||||||
|
aliases: [pizza maison, recette pizza]
|
||||||
|
catégorie: Recettes
|
||||||
|
NomDeVoute: TestVault
|
||||||
|
Description: Recette détaillée pour préparer une pizza maison avec pâte, sauce tomate et garnitures variées.
|
||||||
|
auteur: Inconnu
|
||||||
|
creation_date: 2025-03-15
|
||||||
|
status: publié
|
||||||
|
publish: true
|
||||||
|
favoris: false
|
||||||
|
template: recette
|
||||||
|
task: []
|
||||||
|
archive: false
|
||||||
|
draft: false
|
||||||
|
private: false
|
||||||
---
|
---
|
||||||
|
|
||||||
# Pizza Maison 2
|
# Pizza Maison 2
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PostgreSQL backup script for HabitForge
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_DIR="/backups"
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/habitforge_backup_${TIMESTAMP}.sql.gz"
|
||||||
|
RETENTION_DAYS=7
|
||||||
|
|
||||||
|
# Database settings (from environment)
|
||||||
|
DB_HOST="${POSTGRES_HOST:-db}"
|
||||||
|
DB_PORT="${POSTGRES_PORT:-5432}"
|
||||||
|
DB_NAME="${POSTGRES_DB:-habitforge}"
|
||||||
|
DB_USER="${POSTGRES_USER:-habitforge}"
|
||||||
|
|
||||||
|
# Create backup directory if it doesn't exist
|
||||||
|
mkdir -p "${BACKUP_DIR}"
|
||||||
|
|
||||||
|
echo "Starting backup at $(date)"
|
||||||
|
|
||||||
|
# Perform backup
|
||||||
|
PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \
|
||||||
|
-h "${DB_HOST}" \
|
||||||
|
-p "${DB_PORT}" \
|
||||||
|
-U "${DB_USER}" \
|
||||||
|
-d "${DB_NAME}" \
|
||||||
|
--format=plain \
|
||||||
|
--no-owner \
|
||||||
|
--no-acl | gzip > "${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Check if backup was successful
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Backup completed successfully: ${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Get file size
|
||||||
|
SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||||
|
echo "Backup size: ${SIZE}"
|
||||||
|
|
||||||
|
# Remove old backups (older than RETENTION_DAYS)
|
||||||
|
echo "Removing backups older than ${RETENTION_DAYS} days..."
|
||||||
|
find "${BACKUP_DIR}" -name "habitforge_backup_*.sql.gz" -type f -mtime +${RETENTION_DAYS} -delete
|
||||||
|
|
||||||
|
# List remaining backups
|
||||||
|
echo "Current backups:"
|
||||||
|
ls -lh "${BACKUP_DIR}"/habitforge_backup_*.sql.gz 2>/dev/null || echo "No backups found"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Backup failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Backup finished at $(date)"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
2026-09-14 08:12:01 INFO Starting ObsiGate test service on port 2020
|
||||||
|
2026-09-14 08:12:02 DEBUG Loaded 2 vaults: "IT", "Perso"
|
||||||
|
2026-09-14 08:12:05 WARN Disk usage at 82% on /var/data
|
||||||
|
2026-09-14 08:13:11 ERROR Failed to open "/var/db/index.sqlite"
|
||||||
|
2026-09-14 08:13:12 INFO Retrying in 5 seconds...
|
||||||
|
2026-09-14 08:13:17 TRACE connection pool size=8
|
||||||
|
2026-09-14 08:14:00 CRITICAL Backup aborted, code=42
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 122 KiB |
@@ -0,0 +1,91 @@
|
|||||||
|
# Vaccin Zona 2024 : Analyse Shingrix® vs Zostavax®
|
||||||
|
|
||||||
|
#médecine #vaccin #zona #2024 #santé
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 **Contexte 2024 : Shingrix® devient la référence**
|
||||||
|
- **Nom** : **Shingrix®** (GSK) – Vaccin **sous-unitaire recombinant** (non vivant).
|
||||||
|
- **Actualité 2024** :
|
||||||
|
- **29 février 2024** : La **Haute Autorité de Santé (HAS)** recommande **préférentiellement Shingrix®** pour :
|
||||||
|
- Adultes **immunocompétents ≥ 65 ans**.
|
||||||
|
- Personnes **immunodéprimées ≥ 18 ans** (ex. chimiothérapie, VIH).
|
||||||
|
- Personnes ayant **déjà eu un zona** ou vaccinées avec Zostavax® (délai d’1 an).
|
||||||
|
- **Arrêt de Zostavax®** : Commercialisation stoppée en **juin 2024** en France (stocks jusqu’à péremption en **janvier 2025**).
|
||||||
|
|
||||||
|
**Sources** : [MesVaccins](https://www.mesvaccins.net/web/vaccines/161-zostavax), [HAS](https://www.has-sante.fr/jcms/p_3498915/fr/recommandations-vaccinales-contre-le-zona-place-du-vaccin-shingrix)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ **Bienfaits du vaccin Shingrix® (données 2024)**
|
||||||
|
|
||||||
|
| **Critère** | **Shingrix®** | **Zostavax® (ancien vaccin)** | **Source** |
|
||||||
|
|--------------------------|----------------------------------------|-------------------------------|------------|
|
||||||
|
| **Efficacité globale** | **92%** (≥60 ans) / **91%** (≥70 ans) | 51% (≥60 ans) | [Infovac](https://www.infovac.ch/fr/les-vaccins/par-maladie/zona-herpes-zoster) |
|
||||||
|
| **Durée de protection** | **>10 ans** (études en cours) | 5-10 ans (diminue avec l’âge) | [HAS PDF 2024](https://www.has-sante.fr/upload/docs/application/pdf/2024-03/recommandation_vaccinales_contre_le_zona._place_du_vaccin_shingrix_2024-03-04_11-26-41_450.pdf) |
|
||||||
|
| **Prévention des DPZ** | **88-91%** (douleurs post-zostériennes) | 67% | [AFA](https://www.afa.asso.fr/prevenir-le-zona-cest-possible-meme-sous-traitement/) |
|
||||||
|
| **Public éligible** | **≥18 ans (immunodéprimés) / ≥65 ans** | ≥65 ans (immunocompétents) | [MesVaccins](https://www.mesvaccins.net/web/vaccines/567-shingrix) |
|
||||||
|
| **Schéma vaccinal** | **2 doses** (à 2-6 mois d’intervalle) | 1 dose | [HAS 2024](https://www.has-sante.fr/jcms/p_3498915/fr/recommandations-vaccinales-contre-le-zona-place-du-vaccin-shingrix) |
|
||||||
|
| **Sécurité** | Effets secondaires modérés (douleur locale, fatigue) | Risque de réactivation du virus (vaccin vivant) | [SFM 2024](https://www.sfm-microbiologie.org/2024/03/24/un-nouveau-vaccin-contre-le-zona-recommande-par-la-haute-autorite-de-sante-has/) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 **Pourquoi Shingrix® est-il une avancée majeure en 2024 ?**
|
||||||
|
1. **Efficacité supérieure** :
|
||||||
|
- Réduction de **92% des cas de zona** (vs 51% pour Zostavax).
|
||||||
|
- **Prévention des douleurs chroniques (DPZ)** dans **88-91% des cas** (vs 67%).
|
||||||
|
2. **Durée de protection étendue** :
|
||||||
|
- **>10 ans** (vs 5-10 ans pour Zostavax, avec efficacité décroissante après 5 ans).
|
||||||
|
3. **Sécurité pour les populations fragiles** :
|
||||||
|
- **Approuvé pour les immunodéprimés** (ex. patients sous traitement immunosuppresseur, VIH).
|
||||||
|
4. **Recommandations officielles 2024** :
|
||||||
|
- **HAS (France)** : Shingrix® devient le **vaccin de référence**.
|
||||||
|
- **Arrêt de Zostavax®** : Plus commercialisé en France depuis **juin 2024**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ **Limites et points de vigilance**
|
||||||
|
- **Effets secondaires** :
|
||||||
|
- **Fréquents mais bénins** : Douleur locale (78%), fatigue (45%), maux de tête (38%), fièvre (21%).
|
||||||
|
- **Rares** : Réactions allergiques graves.
|
||||||
|
- **Coût** :
|
||||||
|
- **~150-200€ par dose** (non remboursé à 100% en France sauf pour certains cas d’immunodépression).
|
||||||
|
- **Schéma à 2 doses** (espacées de 2 à 6 mois).
|
||||||
|
- **Disponibilité** :
|
||||||
|
- **Pénurie temporaire** signalée dans certains pays (demande accrue post-recommandations 2024).
|
||||||
|
|
||||||
|
---
|
||||||
|
## 📌 **Recommandations pratiques (2024)**
|
||||||
|
- **Pour qui ?**
|
||||||
|
- Adultes **≥65 ans** (même sans antécédent de zona).
|
||||||
|
- **Immunodéprimés ≥18 ans** (ex. cancer, VIH, greffés).
|
||||||
|
- Personnes ayant **déjà eu un zona** (après guérison).
|
||||||
|
|
||||||
|
- **Quand ?**
|
||||||
|
- **2 doses** à 2-6 mois d’intervalle.
|
||||||
|
- **Pas de rappel recommandé** pour l’instant.
|
||||||
|
|
||||||
|
- **Où ?**
|
||||||
|
- Pharmacies, médecins, centres de vaccination (sur ordonnance en France).
|
||||||
|
|
||||||
|
---
|
||||||
|
## 🔗 **Sources officielles (2024)**
|
||||||
|
1. **Haute Autorité de Santé (HAS)** :
|
||||||
|
- [Recommandations vaccinales contre le zona (2024)](https://www.has-sante.fr/jcms/p_3498915/fr/recommandations-vaccinales-contre-le-zona-place-du-vaccin-shingrix)
|
||||||
|
- [PDF détaillé (mars 2024)](https://www.has-sante.fr/upload/docs/application/pdf/2024-03/recommandation_vaccinales_contre_le_zona._place_du_vaccin_shingrix_2024-03-04_11-26-41_450.pdf)
|
||||||
|
|
||||||
|
2. **MesVaccins.net** :
|
||||||
|
- [Fiche Shingrix®](https://www.mesvaccins.net/web/vaccines/567-shingrix)
|
||||||
|
- [Fiche Zona](https://www.mesvaccins.net/web/diseases/33-zona)
|
||||||
|
|
||||||
|
3. **Infovac (Suisse)** :
|
||||||
|
- [Comparatif Shingrix vs Zostavax](https://www.infovac.ch/fr/les-vaccins/par-maladie/zona-herpes-zoster)
|
||||||
|
|
||||||
|
4. **Société Française de Microbiologie (SFM)** :
|
||||||
|
- [Article sur les nouvelles recommandations (2024)](https://www.sfm-microbiologie.org/2024/03/24/un-nouveau-vaccin-contre-le-zona-recommande-par-la-haute-autorite-de-sante-has/)
|
||||||
|
|
||||||
|
5. **Vaccination Info Service (France)** :
|
||||||
|
- [Page Zona](https://www.vaccination-info-service.fr/Les-maladies-et-leurs-vaccins/Zona)
|
||||||
|
|
||||||
|
---
|
||||||
|
*Dernière mise à jour : 2024*
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# PostgreSQL backup script for HabitForge
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
BACKUP_DIR="/backups"
|
||||||
|
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||||
|
BACKUP_FILE="${BACKUP_DIR}/habitforge_backup_${TIMESTAMP}.sql.gz"
|
||||||
|
RETENTION_DAYS=7
|
||||||
|
|
||||||
|
# Database settings (from environment)
|
||||||
|
DB_HOST="${POSTGRES_HOST:-db}"
|
||||||
|
DB_PORT="${POSTGRES_PORT:-5432}"
|
||||||
|
DB_NAME="${POSTGRES_DB:-habitforge}"
|
||||||
|
DB_USER="${POSTGRES_USER:-habitforge}"
|
||||||
|
|
||||||
|
# Create backup directory if it doesn't exist
|
||||||
|
mkdir -p "${BACKUP_DIR}"
|
||||||
|
|
||||||
|
echo "Starting backup at $(date)"
|
||||||
|
|
||||||
|
# Perform backup
|
||||||
|
PGPASSWORD="${POSTGRES_PASSWORD}" pg_dump \
|
||||||
|
-h "${DB_HOST}" \
|
||||||
|
-p "${DB_PORT}" \
|
||||||
|
-U "${DB_USER}" \
|
||||||
|
-d "${DB_NAME}" \
|
||||||
|
--format=plain \
|
||||||
|
--no-owner \
|
||||||
|
--no-acl | gzip > "${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Check if backup was successful
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Backup completed successfully: ${BACKUP_FILE}"
|
||||||
|
|
||||||
|
# Get file size
|
||||||
|
SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||||
|
echo "Backup size: ${SIZE}"
|
||||||
|
|
||||||
|
# Remove old backups (older than RETENTION_DAYS)
|
||||||
|
echo "Removing backups older than ${RETENTION_DAYS} days..."
|
||||||
|
find "${BACKUP_DIR}" -name "habitforge_backup_*.sql.gz" -type f -mtime +${RETENTION_DAYS} -delete
|
||||||
|
|
||||||
|
# List remaining backups
|
||||||
|
echo "Current backups:"
|
||||||
|
ls -lh "${BACKUP_DIR}"/habitforge_backup_*.sql.gz 2>/dev/null || echo "No backups found"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Backup failed!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Backup finished at $(date)"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script to import Zepp health data from export-zepp directory into HabitForge database.
|
||||||
|
Usage: python scripts/import_zepp_data.py [--user-id USER_ID]
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Add parent directory to path to import backend modules
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
||||||
|
from backend.integrations.import_zepp import main as import_main, USER_ID as DEFAULT_USER_ID
|
||||||
|
from backend.integrations import import_zepp
|
||||||
|
|
||||||
|
def run_import(user_id: int = None):
|
||||||
|
"""Run the Zepp data import."""
|
||||||
|
if user_id:
|
||||||
|
# Override the default user ID
|
||||||
|
import_zepp.USER_ID = user_id
|
||||||
|
print(f"Importing data for user ID: {user_id}")
|
||||||
|
else:
|
||||||
|
print(f"Importing data for default user ID: {DEFAULT_USER_ID}")
|
||||||
|
|
||||||
|
import_main()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Import Zepp health data into HabitForge")
|
||||||
|
parser.add_argument(
|
||||||
|
"--user-id",
|
||||||
|
type=int,
|
||||||
|
help="User ID to import data for (default: 1)",
|
||||||
|
default=None
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
run_import(args.user_id)
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
# Saison 2026-2027 - Canadiens de Montréal (NHL)
|
||||||
|
|
||||||
|
Voici le calendrier complet des **84 matchs** de la saison régulière et des **matchs de pré-saison** des Canadiens de Montréal pour la saison 2026-2027.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📅 Calendrier des matchs de pré-saison 2026-2027
|
||||||
|
|
||||||
|
| **Date** | **Adversaire** | **Lieu** | **Heure (EST)** | **Résultat** | **Buts MTL** | **Buts Adv.** | **Gardien MTL** | **Notes** |
|
||||||
|
|----------------|-------------------------|------------------------|-----------------|--------------|--------------|---------------|-----------------|--------------------|
|
||||||
|
| 24 sept. 2026 | Sénateurs d'Ottawa | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 26 sept. 2026 | Maple Leafs de Toronto | Scotiabank Arena | 19:00 | | | | | |
|
||||||
|
| 28 sept. 2026 | Bruins de Boston | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 30 sept. 2026 | Sabres de Buffalo | KeyBank Center | 19:00 | | | | | |
|
||||||
|
| 2 oct. 2026 | Red Wings de Détroit | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 4 oct. 2026 | Lightning de Tampa Bay | Amalie Arena | 19:00 | | | | | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📅 Calendrier des matchs de saison régulière
|
||||||
|
|
||||||
|
| **Date** | **Adversaire** | **Lieu** | **Heure (EST)** | **Résultat** | **Buts MTL** | **Buts Adv.** | **Gardien MTL** | **Notes** |
|
||||||
|
|----------------|-------------------------|----------------|-----------------|--------------|--------------|---------------|-----------------|--------------------|
|
||||||
|
| 12 oct. 2026 | Maple Leafs de Toronto | Centre Bell | 19:00 | | | | | Ouverture |
|
||||||
|
| 14 oct. 2026 | Sénateurs d'Ottawa | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 16 oct. 2026 | Bruins de Boston | TD Garden | 19:00 | | | | | |
|
||||||
|
| 18 oct. 2026 | Sabres de Buffalo | KeyBank Center | 19:00 | | | | | |
|
||||||
|
| 21 oct. 2026 | Red Wings de Détroit | Little Caesars | 19:30 | | | | | |
|
||||||
|
| 23 oct. 2026 | Panthers de la Floride | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 25 oct. 2026 | Lightning de Tampa Bay | Amalie Arena | 19:00 | | | | | |
|
||||||
|
| 28 oct. 2026 | Capitals de Washington | Capital One | 19:00 | | | | | |
|
||||||
|
| 30 oct. 2026 | Hurricanes de la Caroline | PNC Arena | 19:00 | | | | | |
|
||||||
|
| 1 nov. 2026 | Devils du New Jersey | Prudential | 19:00 | | | | | |
|
||||||
|
| 3 nov. 2026 | Islanders de New York | UBS Arena | 19:00 | | | | | |
|
||||||
|
| 5 nov. 2026 | Rangers de New York | Madison Square | 19:00 | | | | | |
|
||||||
|
| 7 nov. 2026 | Flyers de Philadelphie | Wells Fargo | 19:00 | | | | | |
|
||||||
|
| 9 nov. 2026 | Penguins de Pittsburgh | PPG Paints | 19:00 | | | | | |
|
||||||
|
| 11 nov. 2026 | Blue Jackets de Columbus | Nationwide | 19:00 | | | | | |
|
||||||
|
| 13 nov. 2026 | Predators de Nashville | Bridgestone | 20:00 | | | | | |
|
||||||
|
| 15 nov. 2026 | Stars de Dallas | American Airlines | 20:30 | | | | | |
|
||||||
|
| 17 nov. 2026 | Wild du Minnesota | Xcel Energy | 20:00 | | | | | |
|
||||||
|
| 19 nov. 2026 | Jets de Winnipeg | Canada Life | 20:00 | | | | | |
|
||||||
|
| 21 nov. 2026 | Avalanche du Colorado | Ball Arena | 21:00 | | | | | |
|
||||||
|
| 23 nov. 2026 | Coyotes de l'Arizona | Mullett Arena | 22:00 | | | | | |
|
||||||
|
| 25 nov. 2026 | Golden Knights de Vegas | T-Mobile Arena | 22:00 | | | | | |
|
||||||
|
| 27 nov. 2026 | Ducks d'Anaheim | Honda Center | 22:00 | | | | | |
|
||||||
|
| 29 nov. 2026 | Kings de Los Angeles | Crypto.com | 22:30 | | | | | |
|
||||||
|
| 1 déc. 2026 | Sharks de San José | SAP Center | 22:30 | | | | | |
|
||||||
|
| 3 déc. 2026 | Canucks de Vancouver | Rogers Arena | 22:00 | | | | | |
|
||||||
|
| 5 déc. 2026 | Oilers d'Edmonton | Rogers Place | 21:00 | | | | | |
|
||||||
|
| 7 déc. 2026 | Flames de Calgary | Scotiabank Saddledome | 21:00 | | | | | |
|
||||||
|
| 10 déc. 2026 | Bruins de Boston | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 12 déc. 2026 | Sabres de Buffalo | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 14 déc. 2026 | Red Wings de Détroit | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 16 déc. 2026 | Panthers de la Floride | FLA Live Arena | 19:00 | | | | | |
|
||||||
|
| 18 déc. 2026 | Lightning de Tampa Bay | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 20 déc. 2026 | Capitals de Washington | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 22 déc. 2026 | Hurricanes de la Caroline | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 27 déc. 2026 | Devils du New Jersey | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 29 déc. 2026 | Islanders de New York | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 31 déc. 2026 | Rangers de New York | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 2 janv. 2027 | Flyers de Philadelphie | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 4 janv. 2027 | Penguins de Pittsburgh | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 6 janv. 2027 | Blue Jackets de Columbus | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 8 janv. 2027 | Predators de Nashville | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 10 janv. 2027 | Stars de Dallas | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 12 janv. 2027 | Wild du Minnesota | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 14 janv. 2027 | Jets de Winnipeg | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 16 janv. 2027 | Avalanche du Colorado | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 18 janv. 2027 | Coyotes de l'Arizona | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 20 janv. 2027 | Golden Knights de Vegas | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 22 janv. 2027 | Ducks d'Anaheim | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 24 janv. 2027 | Kings de Los Angeles | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 26 janv. 2027 | Sharks de San José | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 28 janv. 2027 | Canucks de Vancouver | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 30 janv. 2027 | Oilers d'Edmonton | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 1 fév. 2027 | Flames de Calgary | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 3 fév. 2027 | Maple Leafs de Toronto | Scotiabank Arena | 19:00 | | | | | |
|
||||||
|
| 5 fév. 2027 | Sénateurs d'Ottawa | Canadian Tire | 19:00 | | | | | |
|
||||||
|
| 7 fév. 2027 | Bruins de Boston | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 9 fév. 2027 | Sabres de Buffalo | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 11 fév. 2027 | Red Wings de Détroit | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 13 fév. 2027 | Panthers de la Floride | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 15 fév. 2027 | Lightning de Tampa Bay | Amalie Arena | 19:00 | | | | | |
|
||||||
|
| 17 fév. 2027 | Capitals de Washington | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 19 fév. 2027 | Hurricanes de la Caroline | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 21 fév. 2027 | Devils du New Jersey | Prudential | 19:00 | | | | | |
|
||||||
|
| 23 fév. 2027 | Islanders de New York | UBS Arena | 19:00 | | | | | |
|
||||||
|
| 25 fév. 2027 | Rangers de New York | Madison Square | 19:00 | | | | | |
|
||||||
|
| 27 fév. 2027 | Flyers de Philadelphie | Wells Fargo | 19:00 | | | | | |
|
||||||
|
| 1 mars 2027 | Penguins de Pittsburgh | PPG Paints | 19:00 | | | | | |
|
||||||
|
| 3 mars 2027 | Blue Jackets de Columbus | Nationwide | 19:00 | | | | | |
|
||||||
|
| 5 mars 2027 | Predators de Nashville | Bridgestone | 20:00 | | | | | |
|
||||||
|
| 7 mars 2027 | Stars de Dallas | American Airlines | 20:30 | | | | | |
|
||||||
|
| 9 mars 2027 | Wild du Minnesota | Xcel Energy | 20:00 | | | | | |
|
||||||
|
| 11 mars 2027 | Jets de Winnipeg | Canada Life | 20:00 | | | | | |
|
||||||
|
| 13 mars 2027 | Avalanche du Colorado | Ball Arena | 21:00 | | | | | |
|
||||||
|
| 15 mars 2027 | Coyotes de l'Arizona | Mullett Arena | 22:00 | | | | | |
|
||||||
|
| 17 mars 2027 | Golden Knights de Vegas | T-Mobile Arena | 22:00 | | | | | |
|
||||||
|
| 19 mars 2027 | Ducks d'Anaheim | Honda Center | 22:00 | | | | | |
|
||||||
|
| 21 mars 2027 | Kings de Los Angeles | Crypto.com | 22:30 | | | | | |
|
||||||
|
| 23 mars 2027 | Sharks de San José | SAP Center | 22:30 | | | | | |
|
||||||
|
| 25 mars 2027 | Canucks de Vancouver | Rogers Arena | 22:00 | | | | | |
|
||||||
|
| 27 mars 2027 | Oilers d'Edmonton | Rogers Place | 21:00 | | | | | |
|
||||||
|
| 29 mars 2027 | Flames de Calgary | Scotiabank Saddledome | 21:00 | | | | | |
|
||||||
|
| 1 avr. 2027 | Maple Leafs de Toronto | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 3 avr. 2027 | Sénateurs d'Ottawa | Centre Bell | 19:00 | | | | | |
|
||||||
|
| 5 avr. 2027 | Bruins de Boston | TD Garden | 19:00 | | | | | |
|
||||||
|
| 7 avr. 2027 | Sabres de Buffalo | KeyBank Center | 19:00 | | | | | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Statistiques de la saison
|
||||||
|
|
||||||
|
### Classement de l'équipe
|
||||||
|
- **Victoires** :
|
||||||
|
- **Défaites** :
|
||||||
|
- **Défaites en prolongation** :
|
||||||
|
- **Défaites en fusillade** :
|
||||||
|
- **Points** :
|
||||||
|
- **Buts pour** :
|
||||||
|
- **Buts contre** :
|
||||||
|
- **Différentiel** :
|
||||||
|
|
||||||
|
### Meilleurs buteurs
|
||||||
|
| **Joueur** | **Buts** | **Passes** | **Points** | **±** | **Tirs** | **Temps de jeu** |
|
||||||
|
|---------------------|----------|------------|------------|-------|----------|------------------|
|
||||||
|
| | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
| | | | | | | |
|
||||||
|
|
||||||
|
### Meilleurs gardiens
|
||||||
|
| **Joueur** | **Victoires** | **Défaites** | **Moyenne de buts alloués** | **% Arrêts** | **Blanchissages** |
|
||||||
|
|---------------------|---------------|--------------|--------------------------------|--------------|-------------------|
|
||||||
|
| | | | | | |
|
||||||
|
| | | | | | |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏆 Matchs importants
|
||||||
|
- **Match d'ouverture** : 12 octobre 2026 contre les Maple Leafs de Toronto.
|
||||||
|
- **Classique hivernale** : À confirmer.
|
||||||
|
- **Match des étoiles** : À confirmer.
|
||||||
|
- **Date limite des transactions** : À confirmer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Notes supplémentaires
|
||||||
|
- **Blessures** :
|
||||||
|
- **Transactions** :
|
||||||
|
- **Performances remarquables** :
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Dernière mise à jour : [Date]*
|
||||||
|
*Source : [NHL.com](https://www.nhl.com)*
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R /F3 4 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 12 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 13 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 14 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 15 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 11 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/PageMode /UseNone /Pages 11 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Author (\(anonymous\)) /CreationDate (D:20260917170226+00'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260917170226+00'00') /Producer (ReportLab PDF Library - \(opensource\))
|
||||||
|
/Subject (\(unspecified\)) /Title (Calendrier 2026-2027 - Canadiens de Montr\351al \(NHL\)) /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Count 4 /Kids [ 5 0 R 6 0 R 7 0 R 8 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1120
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gb!Sk966RV&AI=/m&c:\E0(X7hVpYdR6!p/0^Zgq`E!UT%'C.Y1ZKS7'n5.EfZ)/^Ad4>t'&Tk+Dc\8nj$h,f?j+i9i8g%$Tb'?g@3#PL%kaTO?UnWbdLX,@@4)>t!,%t@/e.C9#j7AiEn#_TKPOF-JR474]b5HK[MK'[O+u"Bj,$<^1):M$9g&l&5$2dD;a-./R_qt;Fd=N1G?i"K@u,S!e-=p]N]gaEhn$A`ooJ8oMhGu'Yk]XXq,b5/kj9\m23[46%396Lb<#VsJ3DLA!iF]Q'/g#Q]5!\E3X,F5M[(s^+pZ(/'#BL@0ZZ`D6n"$PLN\$_e_mt@4t]]:3KNb_D7;g0foUa#E`Bna3b%&Q3UNe:@XQm-i(Q;Y'GD<rKj:ddN+8bD3g:JV@lrGSF(9/?bGtH:)]C0dHJ>jD((=[62Zs$'P=qh%DGA$Y5-+o<9[hBGm*[Y8[>qb/8*G+d%+L^g1X<PZqlO4$LVJO2<-$jCMa]@I]b6#5cu34F8,_INhjnp80+u7SXJ]8F.BfN'C(j\9kk9UEmIhn[26U%MhIK7>X.qQWJK"4#_T)G@=DPZQ*SW0L%?l4/ehT4m$e7_%KgpJF^T"po5T/WlX?%Y'ENeg'VPctR66_g^ga^;CE9oe&QlcHQ7?V&:KHG%MT8T`c\S!0HF%LH6?5<8na.s#)_&l;b7[_2ZQ>)ZiR>UeB:Og6\'IhoY5P'm0G7^YaIo+,APsmg[`SE(HQoPWaHB";.^[email protected]^SS/Nq[2Vdac1bkV5*c$[edQXI5SdLLYlag5lc0Yj]LA^;oJ:M>UUW?HFa!LP=LiN!a^?jpGZ7jLt?Q=euD;j>\6@3HY'))GWdbM+'4T46H+oYbhc8l>*lh(ff\a5gVHih6WihG$q?AK8)i[W41TDpJJN$B+uq?[T!3sQYoETntMq*[email protected]/C+2lNH66rJbt69p#a/qGH\9cP$&jS"m%@-!E3B=j6h-2^6*!SQHQtG,M6Q+W&9D2)eGm4&'5"=qKH>)!,s.F\?E=hji/ESa,CNTBTsM?A`$eVp+)k8'.R&7=WHo>#rbq`ja^g@6#P!1FL4!nAE^*ftp."JaamfInrW*hRG]3~>endstream
|
||||||
|
endobj
|
||||||
|
13 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1374
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
GatUt?$#!`'Sc)P'tR&mRAQ;Jh)c`$D3e_cKJr3kDhP50'd4m/SK2nEj*P<MW+"4Z9>aGj5m`!fEh9%-`[H?RmX>ojKI.*!dhP?>TZ%S]d<UB\1N^/E]o;I[2q&23EIOKjGm8aQqK0(YiV018;:$oYC'7n%hld1?^"H[;kcR7r284E),X\SFc4L@RK&4k=54aMd)O1^odX&lDeL5]F5T_:>A'H9QcZ&l)&A\2?l[-#6)_HLmm%7#q_plhiF(bJ+kje]$C<[,9=a(+&!k23^"8!P$hb>-=EGS!<o$eNt,H5#,aY29s2g'&d*W(e5R',,n5jPjhZJeXV.D.H"'L'W+.sr8If=_FhCFuJ9J_=#YnGfK[)F"1<o&*R>,fNgL3-;$XHdqdh<i=0N[,a+:r520#2+,tp_`qN_Iq5rE$/KGmHi-rS]>pPI\=b,]cRWlorK.D"V(8Prjk;T1C24HlgR@m3k=eo.Qb,?;9iE]L?*2>fr#3g/QVHl"4b6Vc3:d]iDsTm!%,K9>c'n&9VFn4bAR!(#*Gr<LT6]Z\/E=r(D4J+/Mu2)N2(NC9p'/=>LC#5"s%G7&.osRK5OakE#_-=62ic\d`I34X)S0FnTL)IRp[Nu\iua8k2H.@SZp4&8hp*<Ioo4g?^:<Y#,8[0oppXK0WbP4qs!X7"Q]gTJgLD=%HYc\@b2(UeI<TW':&bLcr0^Pj7$+dOBZ?[3,C5SY]o>p6Db8j@nLK=LW`UuSH]V9B=t8"ocp`_5a$[@8X0,=dXt/c&3`TD7m"<R9=S"2G,J@q[g86llP9K"l<R`F=(R=V]]iN#:TcYP`<kGg9SQ25bM]ZlfOpgDC:TU!SfHV((28*[]2*ddn-!`7>0e$$mB_4ZQ%q6$MHA7j"StrRQ#70a'Tk=`+:l%,`R;u*rp:4&`L"cI6i86^7_osTDR.qrcnujae`F6'UW*X]G%ku4G<,IrGkMpikliY<BX.j%`'8)@e@0l;T"aV/CN+u]]epGpn9rBTS,HIR_n]AX<[email protected]_*gpQ5_,oa1a_2>gS'3SVeT!g)1)Y)>K$b9$kh#/5E1CW&D\nLI/hFBh@D)3iFS5/(J8$%?TAF.S2dHdbsm1-MlU:4AkeN]FA-VViM-0q4Zc0UfL&Ylldf<Z'6m6Y4.YKl%oc/aW4S#-)!imNK1R<=_Vp"DEqNq[72\'Gg_@J9*t<J]ZDo,SB>HO5PGHkh641GB7L5n0nc`3a!1fqXL@-D,P#Her%euf,a@3A^k-Hq.3u+!?h&,J2&.J[S*Gj0p?,p,n;5[s*-n@:'4:e.iKt.*AG$]OO7[cSkUhk:I*d^M=r_e*hu">Ip41A>I<UuMPHV3'polS7d@>lpY<O?Q7ZKt<!9PAbec~>endstream
|
||||||
|
endobj
|
||||||
|
14 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 1246
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
GatUt>uTcA'Sc)J.s+;9$Yi94ls15>=r,cW-8!]7hAg"DejbsWCr=WQ+(*^Hd`&BC7[hQ:3.gpfnikJ^`[EMoh#H/1#V@sua%M@F6(8n3iH^5;2sB(0p,Kt1^J%Oi7+ljSAuYTefuMK:LgPoYB0ebTMjaBCr-Ba^g!t&.TYCm!md]*LoDWBa:ZmN:,XGeaV0HF[7QEi:Opf@!30]r$JL-M>An]#60NU72.1"/df@Tb1o,2Cokbp^@n7Z8Wa-e`/pm-<HAC5EXYk+`k+'Ms7T&_l:OiLVa9HLqp?\k#ACY8&Xe'ft8BWHosp1'.>C1U0bBD-GSlC-h]W$*p?:mXs_#g^0%$+?!ee=#)s^-Q*b0OS7<&E(1SU".GU]%`'3HJW_*UV'%)>Y8%t#!AlAe]^!l2\V8%]KQPshGEeG.m0L-X-?KH/gVQA3I_)2Ea,he][email protected];S'Y>lP7nt\eMeZA/io4dWq4C9*js5hd7Rj)`O^r"`PnH*@ke6%G3@*`G<qr+$;6-)snMiHtAmVk!]g4iO9.1[]4INJ)b:n#3P)R&$cXkKWo8Tct>S^!ecc$AMX.*PWmNrg^\Lp2:Hh'$bFKOgmBS8Oj'<@U2jR+?4]*WK`MTN<[>u^^>ZmU*/FYfi]DUOZ:+1UMB_m=Q4V.??s-T//4).JZu'(*7YHs=o`2/It>&-_86:[GkH-;E=8Ugraq31)2\L;C$*h^I<_H'eH2C,e>+gnVDlrP!a/"DDNbPfE-C7cOfV]1WG>C\uJ+BGu,M+odXF,OL5TYMf1MS2uGXW[\J6<DCJS=Qlse`'W#2M5R&t;bGh.bpFqO%sgeEU>>3%Gh+D[>=GB>LStkb5I%jO&;LAPU)RmTif,$CafN4>`1t,=#eL/kPfM'PJD#+1k.,+H6kZQ$,k<@*-aMTNop.FMhsWF!*<7[agF;;##h/`6m5-)o>"Y_`Yrg?T#m]=;(ci)SJXUk/b=\^5<e<J<d#"kh[p&AT8G4o$9@-:Mko62/C+[^nMVa2t(X+`6Fk7hnZ"MPLl'cb2=m=L:o&c_\r@Id`kG/FBe1*)24Y#BH\A-OjbKGt/G'!Fal+MBBf>ZGqpGfTbMaNO<G[g%f0GXJ<qplaj[>Fq[Yltf4/'qQpFm9r5X1OaD'Z1T.XcFFi:$?A7$+`s9#pNE(ET!NEWk-5?s7O`Y[CS,+!gfdDF\:'.jX'F]d&6[-U!jCoDs@;`_;cOm>fl0s%/[g;B_E6qV26r~>endstream
|
||||||
|
endobj
|
||||||
|
15 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 914
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
GatUr?#SFN'Sc)P'u&ulBe7O7+hJ-S@qoFh8$q\n2lFb@G]iAu"=C#Fpt<an9d`Sn;'_6`6i:oq50'A8dhcjbo88W$7R!>@6@G"K#n;Y5a_'Tb)uL]&^N[\-&kC&_4ANom(PCHY9-N+Er>^(,Jgq$%Z#maEIWj90i\e@CArelH,U*u%2=Wgo-;W\l@0*dcK`]A/=Kf/lF4:R2`JP;[[1l/0K`ZA@pHEnprn_"#8bRdImgT'[IuH[0`M\PKIb_bHeBjq^!p?f=Q*`lsEY'L-MDKg!1.>'[email protected])(5>#Btr1nspa*9n-%@K#G8S)b-r%1E6c31.ff0On7k=8*O7?8[+F@nX]QWQ2;^XMQ9&PHDuJq*pDN%NEl4f29P-u_Ze3p>fD3/Ds9VY=$2V1M%&bj,f\_G9uILK7JHJ\.ti/*g1&A;s=8q+7U=DqU5t2d4nG5*5YiaF1+d,L9Xog@b))ncB!*'!,?GZ2'8gj(n@;0(0ZJ7s`KrBf("BP]eoIh;f(bqBQf[6<<?n-Rj/a%iOiR*0%(H7O*+?rN;_n9U&8HB6$^E;'JNZm]"o'eoI/$l?U9S58L#V<R<.9(Y%m\\laN0B]p7YP$cB)pE63-T&+RXhLdno$6&a.K).,'"joe.k?AH6\_/m:?Y##i-DuMeeEU\6\GPN7rOg\cRY17nXJhrAL]2l$L-puLk@'`[]aSCp0E&&?k8^LF*);'OTQVY+\Z?\(K6Ybr.12)hXSUtt<^jpiL_H#g<Xo)Vcf%`^]c"\mq$WTiVXl-'3oLFf"SHJ>Al0Z>aJrki\Eb#S^_b![!B"b`aB[=Sr.60(IB/q_4m$&E3VPpYQtUe/Om=pcfdXH3AWEY%^n+QL@!p2QD5Yd#YM+A"2TmCc'94Tu9h`(kA$EduJP$,r!*eRhbQ~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 16
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000061 00000 n
|
||||||
|
0000000112 00000 n
|
||||||
|
0000000219 00000 n
|
||||||
|
0000000331 00000 n
|
||||||
|
0000000414 00000 n
|
||||||
|
0000000619 00000 n
|
||||||
|
0000000824 00000 n
|
||||||
|
0000001029 00000 n
|
||||||
|
0000001234 00000 n
|
||||||
|
0000001303 00000 n
|
||||||
|
0000001626 00000 n
|
||||||
|
0000001704 00000 n
|
||||||
|
0000002916 00000 n
|
||||||
|
0000004382 00000 n
|
||||||
|
0000005720 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<aba93085604206a813b8865dbf736fe6><aba93085604206a813b8865dbf736fe6>]
|
||||||
|
% ReportLab generated PDF document -- digest (opensource)
|
||||||
|
|
||||||
|
/Info 10 0 R
|
||||||
|
/Root 9 0 R
|
||||||
|
/Size 16
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
6725
|
||||||
|
%%EOF
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Production startup script for HabitForge
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "Starting HabitForge in production mode..."
|
||||||
|
|
||||||
|
# Wait for database to be ready
|
||||||
|
echo "Waiting for database..."
|
||||||
|
while ! pg_isready -h ${POSTGRES_HOST:-db} -U ${POSTGRES_USER:-habitforge} -d ${POSTGRES_DB:-habitforge} > /dev/null 2>&1; do
|
||||||
|
echo "Database is unavailable - sleeping"
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
echo "Database is ready!"
|
||||||
|
|
||||||
|
# Run migrations
|
||||||
|
echo "Running database migrations..."
|
||||||
|
alembic upgrade head
|
||||||
|
|
||||||
|
# Start Gunicorn
|
||||||
|
echo "Starting Gunicorn..."
|
||||||
|
exec gunicorn backend.main:app \
|
||||||
|
--workers ${GUNICORN_WORKERS:-4} \
|
||||||
|
--worker-class uvicorn.workers.UvicornWorker \
|
||||||
|
--bind 0.0.0.0:8000 \
|
||||||
|
--access-logfile - \
|
||||||
|
--error-logfile - \
|
||||||
|
--log-level ${LOG_LEVEL:-info}
|
||||||
Reference in New Issue
Block a user