Des tonnes de modifications notamment VIM / Couleurs / typos
This commit is contained in:
436
internal/api/daily_notes.go
Normal file
436
internal/api/daily_notes.go
Normal file
@ -0,0 +1,436 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DailyNoteInfo contient les métadonnées d'une daily note
|
||||
type DailyNoteInfo struct {
|
||||
Date time.Time
|
||||
Path string
|
||||
Exists bool
|
||||
Title string
|
||||
DayOfWeek string
|
||||
DayOfMonth int
|
||||
}
|
||||
|
||||
// CalendarDay représente un jour dans le calendrier
|
||||
type CalendarDay struct {
|
||||
Day int
|
||||
Date time.Time
|
||||
HasNote bool
|
||||
IsToday bool
|
||||
NotePath string
|
||||
InMonth bool // Indique si le jour appartient au mois affiché
|
||||
}
|
||||
|
||||
// CalendarData contient les données pour le template du calendrier
|
||||
type CalendarData struct {
|
||||
Year int
|
||||
Month time.Month
|
||||
MonthName string
|
||||
Weeks [][7]CalendarDay
|
||||
PrevMonth string // Format: YYYY-MM
|
||||
NextMonth string // Format: YYYY-MM
|
||||
CurrentMonth string // Format: YYYY-MM
|
||||
}
|
||||
|
||||
// getDailyNotePath retourne le chemin d'une daily note pour une date donnée
|
||||
// Format: notes/daily/2025/01/11.md
|
||||
func (h *Handler) getDailyNotePath(date time.Time) string {
|
||||
year := date.Format("2006")
|
||||
month := date.Format("01")
|
||||
day := date.Format("02")
|
||||
|
||||
relativePath := filepath.Join("daily", year, month, fmt.Sprintf("%s.md", day))
|
||||
return relativePath
|
||||
}
|
||||
|
||||
// getDailyNoteAbsolutePath retourne le chemin absolu d'une daily note
|
||||
func (h *Handler) getDailyNoteAbsolutePath(date time.Time) string {
|
||||
relativePath := h.getDailyNotePath(date)
|
||||
return filepath.Join(h.notesDir, relativePath)
|
||||
}
|
||||
|
||||
// dailyNoteExists vérifie si une daily note existe pour une date donnée
|
||||
func (h *Handler) dailyNoteExists(date time.Time) bool {
|
||||
absPath := h.getDailyNoteAbsolutePath(date)
|
||||
_, err := os.Stat(absPath)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// createDailyNote crée une daily note avec un template par défaut
|
||||
func (h *Handler) createDailyNote(date time.Time) error {
|
||||
absPath := h.getDailyNoteAbsolutePath(date)
|
||||
|
||||
// Créer les dossiers parents si nécessaire
|
||||
dir := filepath.Dir(absPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("impossible de créer les dossiers: %w", err)
|
||||
}
|
||||
|
||||
// Vérifier si le fichier existe déjà
|
||||
if _, err := os.Stat(absPath); err == nil {
|
||||
return nil // Fichier existe déjà, ne pas écraser
|
||||
}
|
||||
|
||||
// Formatter les dates
|
||||
dateStr := date.Format("02-01-2006")
|
||||
dateTimeStr := date.Format("02-01-2006:15:04")
|
||||
|
||||
// Noms des jours en français
|
||||
dayNames := map[time.Weekday]string{
|
||||
time.Monday: "Lundi",
|
||||
time.Tuesday: "Mardi",
|
||||
time.Wednesday: "Mercredi",
|
||||
time.Thursday: "Jeudi",
|
||||
time.Friday: "Vendredi",
|
||||
time.Saturday: "Samedi",
|
||||
time.Sunday: "Dimanche",
|
||||
}
|
||||
|
||||
// Noms des mois en français
|
||||
monthNames := map[time.Month]string{
|
||||
time.January: "janvier",
|
||||
time.February: "février",
|
||||
time.March: "mars",
|
||||
time.April: "avril",
|
||||
time.May: "mai",
|
||||
time.June: "juin",
|
||||
time.July: "juillet",
|
||||
time.August: "août",
|
||||
time.September: "septembre",
|
||||
time.October: "octobre",
|
||||
time.November: "novembre",
|
||||
time.December: "décembre",
|
||||
}
|
||||
|
||||
dayName := dayNames[date.Weekday()]
|
||||
monthName := monthNames[date.Month()]
|
||||
|
||||
// Template de la daily note
|
||||
template := fmt.Sprintf(`---
|
||||
title: "Daily Note - %s"
|
||||
date: "%s"
|
||||
last_modified: "%s"
|
||||
tags: [daily]
|
||||
---
|
||||
|
||||
# 📅 %s %d %s %d
|
||||
|
||||
## 🎯 Objectifs du jour
|
||||
-
|
||||
|
||||
## 📝 Notes
|
||||
-
|
||||
|
||||
## ✅ Accompli
|
||||
-
|
||||
|
||||
## 💭 Réflexions
|
||||
-
|
||||
|
||||
## 🔗 Liens
|
||||
-
|
||||
`, date.Format("2006-01-02"), dateStr, dateTimeStr, dayName, date.Day(), monthName, date.Year())
|
||||
|
||||
// Écrire le fichier
|
||||
if err := os.WriteFile(absPath, []byte(template), 0644); err != nil {
|
||||
return fmt.Errorf("impossible d'écrire le fichier: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleDailyToday gère GET /api/daily/today - Ouvre ou crée la note du jour
|
||||
func (h *Handler) handleDailyToday(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Méthode non autorisée", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
today := time.Now()
|
||||
|
||||
// Créer la note si elle n'existe pas
|
||||
if !h.dailyNoteExists(today) {
|
||||
if err := h.createDailyNote(today); err != nil {
|
||||
h.logger.Printf("Erreur création daily note: %v", err)
|
||||
http.Error(w, "Erreur lors de la création de la note", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Déclencher la ré-indexation
|
||||
go h.idx.Load(h.notesDir)
|
||||
}
|
||||
|
||||
// Rediriger vers l'endpoint normal de note
|
||||
notePath := h.getDailyNotePath(today)
|
||||
http.Redirect(w, r, "/api/notes/"+notePath, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// handleDailyDate gère GET /api/daily/{YYYY-MM-DD} - Ouvre ou crée la note d'une date spécifique
|
||||
func (h *Handler) handleDailyDate(w http.ResponseWriter, r *http.Request, dateStr string) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Méthode non autorisée", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Parser la date (format YYYY-MM-DD)
|
||||
date, err := time.Parse("2006-01-02", dateStr)
|
||||
if err != nil {
|
||||
http.Error(w, "Format de date invalide (attendu: YYYY-MM-DD)", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Créer la note si elle n'existe pas
|
||||
if !h.dailyNoteExists(date) {
|
||||
if err := h.createDailyNote(date); err != nil {
|
||||
h.logger.Printf("Erreur création daily note: %v", err)
|
||||
http.Error(w, "Erreur lors de la création de la note", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Déclencher la ré-indexation
|
||||
go h.idx.Load(h.notesDir)
|
||||
}
|
||||
|
||||
// Rediriger vers l'endpoint normal de note
|
||||
notePath := h.getDailyNotePath(date)
|
||||
http.Redirect(w, r, "/api/notes/"+notePath, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// handleDailyCalendar gère GET /api/daily/calendar/{YYYY}/{MM} - Retourne le HTML du calendrier
|
||||
func (h *Handler) handleDailyCalendar(w http.ResponseWriter, r *http.Request, yearStr, monthStr string) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Méthode non autorisée", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Parser année et mois
|
||||
year, err := strconv.Atoi(yearStr)
|
||||
if err != nil || year < 1900 || year > 2100 {
|
||||
http.Error(w, "Année invalide", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
month, err := strconv.Atoi(monthStr)
|
||||
if err != nil || month < 1 || month > 12 {
|
||||
http.Error(w, "Mois invalide", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Créer les données du calendrier
|
||||
calendarData := h.buildCalendarData(year, time.Month(month))
|
||||
|
||||
// Rendre le template
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := h.templates.ExecuteTemplate(w, "daily-calendar.html", calendarData); err != nil {
|
||||
h.logger.Printf("Erreur template calendrier: %v", err)
|
||||
http.Error(w, "Erreur de rendu", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// buildCalendarData construit les données du calendrier pour un mois donné
|
||||
func (h *Handler) buildCalendarData(year int, month time.Month) *CalendarData {
|
||||
// Premier jour du mois
|
||||
firstDay := time.Date(year, month, 1, 0, 0, 0, 0, time.Local)
|
||||
|
||||
// Dernier jour du mois
|
||||
lastDay := firstDay.AddDate(0, 1, -1)
|
||||
|
||||
// Date d'aujourd'hui
|
||||
today := time.Now()
|
||||
|
||||
// Noms des mois en français
|
||||
monthNames := map[time.Month]string{
|
||||
time.January: "Janvier",
|
||||
time.February: "Février",
|
||||
time.March: "Mars",
|
||||
time.April: "Avril",
|
||||
time.May: "Mai",
|
||||
time.June: "Juin",
|
||||
time.July: "Juillet",
|
||||
time.August: "Août",
|
||||
time.September: "Septembre",
|
||||
time.October: "Octobre",
|
||||
time.November: "Novembre",
|
||||
time.December: "Décembre",
|
||||
}
|
||||
|
||||
data := &CalendarData{
|
||||
Year: year,
|
||||
Month: month,
|
||||
MonthName: monthNames[month],
|
||||
Weeks: make([][7]CalendarDay, 0),
|
||||
}
|
||||
|
||||
// Calculer mois précédent et suivant
|
||||
prevMonth := firstDay.AddDate(0, -1, 0)
|
||||
nextMonth := firstDay.AddDate(0, 1, 0)
|
||||
data.PrevMonth = fmt.Sprintf("%d-%02d", prevMonth.Year(), prevMonth.Month())
|
||||
data.NextMonth = fmt.Sprintf("%d-%02d", nextMonth.Year(), nextMonth.Month())
|
||||
data.CurrentMonth = fmt.Sprintf("%d-%02d", year, month)
|
||||
|
||||
// Construire les semaines
|
||||
// Lundi = 0, Dimanche = 6
|
||||
var week [7]CalendarDay
|
||||
weekDay := 0
|
||||
|
||||
// Jour de la semaine du premier jour (convertir : Dimanche=0 → Lundi=0)
|
||||
firstWeekday := int(firstDay.Weekday())
|
||||
if firstWeekday == 0 {
|
||||
firstWeekday = 7 // Dimanche devient 7
|
||||
}
|
||||
firstWeekday-- // Maintenant Lundi=0
|
||||
|
||||
// Remplir les jours avant le premier du mois (mois précédent)
|
||||
prevMonthLastDay := firstDay.AddDate(0, 0, -1)
|
||||
for i := 0; i < firstWeekday; i++ {
|
||||
daysBack := firstWeekday - i
|
||||
date := prevMonthLastDay.AddDate(0, 0, -daysBack+1)
|
||||
week[i] = CalendarDay{
|
||||
Day: date.Day(),
|
||||
Date: date,
|
||||
HasNote: h.dailyNoteExists(date),
|
||||
IsToday: isSameDay(date, today),
|
||||
InMonth: false,
|
||||
}
|
||||
}
|
||||
weekDay = firstWeekday
|
||||
|
||||
// Remplir les jours du mois
|
||||
for day := 1; day <= lastDay.Day(); day++ {
|
||||
date := time.Date(year, month, day, 0, 0, 0, 0, time.Local)
|
||||
|
||||
week[weekDay] = CalendarDay{
|
||||
Day: day,
|
||||
Date: date,
|
||||
HasNote: h.dailyNoteExists(date),
|
||||
IsToday: isSameDay(date, today),
|
||||
NotePath: h.getDailyNotePath(date),
|
||||
InMonth: true,
|
||||
}
|
||||
|
||||
weekDay++
|
||||
if weekDay == 7 {
|
||||
data.Weeks = append(data.Weeks, week)
|
||||
week = [7]CalendarDay{}
|
||||
weekDay = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Remplir les jours après le dernier du mois (mois suivant)
|
||||
if weekDay > 0 {
|
||||
nextMonthDay := 1
|
||||
for weekDay < 7 {
|
||||
date := time.Date(year, month+1, nextMonthDay, 0, 0, 0, 0, time.Local)
|
||||
week[weekDay] = CalendarDay{
|
||||
Day: nextMonthDay,
|
||||
Date: date,
|
||||
HasNote: h.dailyNoteExists(date),
|
||||
IsToday: isSameDay(date, today),
|
||||
InMonth: false,
|
||||
}
|
||||
weekDay++
|
||||
nextMonthDay++
|
||||
}
|
||||
data.Weeks = append(data.Weeks, week)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// handleDailyRecent gère GET /api/daily/recent - Retourne les 7 dernières daily notes
|
||||
func (h *Handler) handleDailyRecent(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Méthode non autorisée", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// Chercher les daily notes des 14 derniers jours (au cas où certaines manquent)
|
||||
recentNotes := make([]*DailyNoteInfo, 0, 7)
|
||||
|
||||
today := time.Now()
|
||||
for i := 0; i < 14 && len(recentNotes) < 7; i++ {
|
||||
date := today.AddDate(0, 0, -i)
|
||||
|
||||
if h.dailyNoteExists(date) {
|
||||
dayNames := map[time.Weekday]string{
|
||||
time.Monday: "Lun",
|
||||
time.Tuesday: "Mar",
|
||||
time.Wednesday: "Mer",
|
||||
time.Thursday: "Jeu",
|
||||
time.Friday: "Ven",
|
||||
time.Saturday: "Sam",
|
||||
time.Sunday: "Dim",
|
||||
}
|
||||
|
||||
info := &DailyNoteInfo{
|
||||
Date: date,
|
||||
Path: h.getDailyNotePath(date),
|
||||
Exists: true,
|
||||
Title: date.Format("02/01/2006"),
|
||||
DayOfWeek: dayNames[date.Weekday()],
|
||||
DayOfMonth: date.Day(),
|
||||
}
|
||||
recentNotes = append(recentNotes, info)
|
||||
}
|
||||
}
|
||||
|
||||
// Rendre le template
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
if err := h.templates.ExecuteTemplate(w, "daily-recent.html", map[string]interface{}{
|
||||
"Notes": recentNotes,
|
||||
}); err != nil {
|
||||
h.logger.Printf("Erreur template notes récentes: %v", err)
|
||||
http.Error(w, "Erreur de rendu", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// isSameDay vérifie si deux dates sont le même jour
|
||||
func isSameDay(d1, d2 time.Time) bool {
|
||||
y1, m1, day1 := d1.Date()
|
||||
y2, m2, day2 := d2.Date()
|
||||
return y1 == y2 && m1 == m2 && day1 == day2
|
||||
}
|
||||
|
||||
// handleDaily route les requêtes /api/daily/*
|
||||
func (h *Handler) handleDaily(w http.ResponseWriter, r *http.Request) {
|
||||
path := strings.TrimPrefix(r.URL.Path, "/api/daily")
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
|
||||
// /api/daily/today
|
||||
if path == "today" || path == "" {
|
||||
h.handleDailyToday(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// /api/daily/recent
|
||||
if path == "recent" {
|
||||
h.handleDailyRecent(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// /api/daily/calendar/{YYYY}/{MM}
|
||||
if strings.HasPrefix(path, "calendar/") {
|
||||
parts := strings.Split(strings.TrimPrefix(path, "calendar/"), "/")
|
||||
if len(parts) == 2 {
|
||||
h.handleDailyCalendar(w, r, parts[0], parts[1])
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// /api/daily/{YYYY-MM-DD}
|
||||
if len(path) == 10 && path[4] == '-' && path[7] == '-' {
|
||||
h.handleDailyDate(w, r, path)
|
||||
return
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
Reference in New Issue
Block a user