diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6d020bd..3c153bc 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -18,7 +18,9 @@ "Bash(fish -c \"which node\")", "Read(//opt/**)", "Bash(/home/anthony/.config/JetBrains/WebStorm2026.1/node/versions/24.14.1/bin/node node_modules/.bin/tsc --noEmit)", - "Bash(chmod +x /home/anthony/Documents/Projects/Tradarr/build-push.sh)" + "Bash(chmod +x /home/anthony/Documents/Projects/Tradarr/build-push.sh)", + "Bash(fish -c \"npm install react-markdown\")", + "Bash(fish -c \"which npm; which pnpm; which bun\")" ] } } diff --git a/backend/internal/ai/pipeline.go b/backend/internal/ai/pipeline.go index fe0b000..15bcb5b 100644 --- a/backend/internal/ai/pipeline.go +++ b/backend/internal/ai/pipeline.go @@ -116,10 +116,12 @@ func (p *Pipeline) GenerateForUser(ctx context.Context, userID string) (*models. systemPrompt = DefaultSystemPrompt } + tz, _ := p.repo.GetSetting("timezone") + // Passe 2 : résumé complet fmt.Printf("[pipeline] Passe 2 — résumé : génération sur %d articles…\n", len(articles)) t2 := time.Now() - prompt := buildPrompt(systemPrompt, symbols, articles) + prompt := buildPrompt(systemPrompt, symbols, articles, tz) summary, err := provider.Summarize(ctx, prompt) if err != nil { return nil, fmt.Errorf("AI summarize: %w", err) @@ -266,7 +268,7 @@ func (p *Pipeline) callProviderForReport(ctx context.Context, excerpt, question return provider.Summarize(ctx, prompt) } -func buildPrompt(systemPrompt string, symbols []string, articles []models.Article) string { +func buildPrompt(systemPrompt string, symbols []string, articles []models.Article, tz string) string { var sb strings.Builder sb.WriteString(systemPrompt) sb.WriteString("\n\n") @@ -275,7 +277,11 @@ func buildPrompt(systemPrompt string, symbols []string, articles []models.Articl sb.WriteString(strings.Join(symbols, ", ")) sb.WriteString(".\n\n") } - sb.WriteString(fmt.Sprintf("Date d'analyse : %s\n\n", time.Now().Format("02/01/2006 15:04"))) + loc, err := time.LoadLocation(tz) + if err != nil || tz == "" { + loc = time.UTC + } + sb.WriteString(fmt.Sprintf("Date d'analyse : %s\n\n", time.Now().In(loc).Format("02/01/2006 15:04"))) sb.WriteString("/think\n\n") sb.WriteString("## Actualités\n\n") diff --git a/backend/internal/database/migrations/000007_timezone.down.sql b/backend/internal/database/migrations/000007_timezone.down.sql new file mode 100644 index 0000000..2de0254 --- /dev/null +++ b/backend/internal/database/migrations/000007_timezone.down.sql @@ -0,0 +1 @@ +DELETE FROM settings WHERE key = 'timezone'; diff --git a/backend/internal/database/migrations/000007_timezone.up.sql b/backend/internal/database/migrations/000007_timezone.up.sql new file mode 100644 index 0000000..8c5347e --- /dev/null +++ b/backend/internal/database/migrations/000007_timezone.up.sql @@ -0,0 +1,2 @@ +INSERT INTO settings (key, value) VALUES ('timezone', 'Europe/Paris') +ON CONFLICT (key) DO NOTHING; diff --git a/backend/internal/scheduler/scheduler.go b/backend/internal/scheduler/scheduler.go index eee6077..50b3adf 100644 --- a/backend/internal/scheduler/scheduler.go +++ b/backend/internal/scheduler/scheduler.go @@ -57,9 +57,14 @@ func (s *Scheduler) loadSchedule() error { return nil } + tz, _ := s.repo.GetSetting("timezone") + if tz == "" { + tz = "UTC" + } + for _, slot := range slots { - // Format cron: "minute hour * * day_of_week" - spec := fmt.Sprintf("%d %d * * %d", slot.Minute, slot.Hour, slot.DayOfWeek) + // TZ= prefix permet à robfig/cron d'interpréter les heures dans le fuseau configuré + spec := fmt.Sprintf("TZ=%s %d %d * * %d", tz, slot.Minute, slot.Hour, slot.DayOfWeek) id, err := s.cron.AddFunc(spec, s.run) if err != nil { fmt.Printf("scheduler: invalid cron spec %q: %v\n", spec, err) @@ -68,7 +73,7 @@ func (s *Scheduler) loadSchedule() error { s.entryIDs = append(s.entryIDs, id) } - fmt.Printf("scheduler: %d time slots loaded\n", len(s.entryIDs)) + fmt.Printf("scheduler: %d time slots loaded (timezone: %s)\n", len(s.entryIDs), tz) return nil } diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx index f612114..7185b80 100644 --- a/frontend/src/pages/Dashboard.tsx +++ b/frontend/src/pages/Dashboard.tsx @@ -126,17 +126,28 @@ function ContextPanel({ // ── Summary content renderer ──────────────────────────────────────────────── +function renderInline(text: string): React.ReactNode { + const parts = text.split(/(\*\*[^*]+\*\*)/g) + return parts.map((part, i) => + part.startsWith('**') && part.endsWith('**') + ? {part.slice(2, -2)} + : part + ) +} + function SummaryContent({ content }: { content: string }) { const lines = content.split('\n') return ( -
{line.slice(2, -2)}
- if (line.trim() === '') return - return{line}
+ if (line.startsWith('##### ')) return{renderInline(line)}
})}Configuration globale du service
+ Utilisé pour le planning de scraping et l'horodatage des résumés +
+