Architecture complète, SQL, endpoints API, structure de fichiers et design system pour le dashboard SaaS — basé sur l'analyse de ton extension existante.
Fichiers core
24
composants + pages
Endpoints API
11
routes sécurisées
Tables SQL
7
avec RLS Supabase
Stack
Next.js
+ Supabase + Stripe
🔗 Compatibilité extension
Tout est construit pour être 100% compatible avec ton background.js existant. Le magic link /api/session-sync/generate est déjà présent dans ton code — il suffit d'adapter la destination vers le dashboard web.
01 — Structure des fichiers
Arborescence du projet
Architecture Next.js App Router propre, scalable, organisée par domaine métier.
7 tables. Toutes avec RLS activé. Compatible avec les données existantes de l'extension.
Tables existantes (à conserver)
Table
Description
Utilisée par
tracking_data
Temps par domaine, par jour, par user
Extension + Dashboard
user_subscriptions
Plan actif, status, dates
Extension + Dashboard
Nouvelles tables à créer
-- ══════════════════════════════════════════════════════════
-- FLOWLY V1 — SQL Migration
-- Compatible Supabase + RLS
-- ══════════════════════════════════════════════════════════-- 1. Streak utilisateur (remplace localStorage)CREATE TABLEuser_streaks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users ON DELETE CASCADE UNIQUE NOT NULL,
streak_current INT DEFAULT0,
streak_best INT DEFAULT0,
last_visit DATE,
updated_at TIMESTAMPTZ DEFAULT now()
);
ALTER TABLE user_streaks ENABLE ROW LEVEL SECURITY;
CREATE POLICY"Users own their streak"ON user_streaks
FOR ALL USING (auth.uid() = user_id);
-- 2. Apps bureau (Desktop Tracker)CREATE TABLEapp_tracking (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users ON DELETE CASCADE NOT NULL,
app_name TEXT NOT NULL,
duration INT NOT NULL, -- secondes
tracked_date DATE NOT NULL,
created_at TIMESTAMPTZ DEFAULT now()
);
ALTER TABLE app_tracking ENABLE ROW LEVEL SECURITY;
CREATE POLICY"Users own their app data"ON app_tracking
FOR ALL USING (auth.uid() = user_id);
-- 3. Record personnel (record de temps max)CREATE TABLEuser_stats (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users ON DELETE CASCADE UNIQUE NOT NULL,
record_time_sec INT DEFAULT0,
record_time_date DATE,
total_tracked_sec BIGINT DEFAULT0,
updated_at TIMESTAMPTZ DEFAULT now()
);
ALTER TABLE user_stats ENABLE ROW LEVEL SECURITY;
CREATE POLICY"Users own their stats"ON user_stats
FOR ALL USING (auth.uid() = user_id);
-- 4. Index de performanceCREATE INDEX ON tracking_data (user_id, tracked_date DESC);
CREATE INDEX ON app_tracking (user_id, tracked_date DESC);
-- 5. Vue agrégée (dashboard overview) — lecture rapideCREATE OR REPLACE VIEWv_daily_totalsASSELECT
user_id,
tracked_date,
SUM(duration) AS total_sec,
COUNT DISTINCT(domain) AS domain_count
FROM tracking_data
GROUP BY user_id, tracked_date;
-- Sécurité vueALTER VIEW v_daily_totals OWNER TO authenticated;
⚠️ Note sur tracking_data
Ton extension utilise déjà une table pour les données de tracking via /api/track. Vérifie que la structure de ta table actuelle correspond à : user_id, domain, duration (int, secondes), tracked_date (date). Si ta colonne de date s'appelle différemment, adapter les queries.
03 — Auth & Session
Connexion extension → dashboard
Le flow magic link est déjà implémenté dans ton background.js. Il suffit de créer la page de réception côté web.
Flow existant (background.js)
// Déjà dans ton background.js ✓if (msg.action === "OPEN_BILLING") {
const resp = awaitfetch(
`${FLOWLY_API_BASE}/api/session-sync/generate`,
{ method: "POST",
body: JSON.stringify({
access_token: session.access_token,
refresh_token: session.refresh_token
})
}
);
// → ouvre l'onglet avec redirect_urlawait chrome.tabs.create({
url: json.redirect_url
});
}
const today = newDate().toISOString().slice(0,10);
const yesterday = getYesterday();
// Même logique que dashboard.js de l'extensionif (last === today) { /* rien */ }
elif(last === yesterday){ count++; }
else { count = 1; }
best = Math.max(best, count);
// upsert dans user_streaks
08 — Plans Free / Pro
Architecture de monétisation
Compatible avec la structure Stripe existante. Protections à deux niveaux : frontend + backend obligatoirement.
Free
0€ / toujours
Dashboard aujourd'hui
Historique 3 jours
Focus Streak
Historique complet
Analytics semaine/mois
Export CSV
Desktop Tracker
Pro ✦
4.99€ / mois ou 29€ lifetime
Dashboard aujourd'hui
Historique illimité
Focus Streak + Records
Analytics semaine/mois
Tendances + comparaisons
Export CSV complet
Desktop Tracker
🔐 Règle absolue — toujours vérifier côté serveur
Ne jamais faire confiance au plan passé dans le frontend. Chaque endpoint API doit relire user_subscriptions en base. Le flou CSS et les composants masqués côté client sont UX, pas sécurité.
09 — Desktop Tracker
Tracker d'applications bureau
Solution Electron légère. Détecte la fenêtre active, flush vers Supabase toutes les 15s. Même pattern que l'extension.
Electron — choix recommandé
Multiplatform (Mac/Win/Linux), accès natif aux APIs OS, même JS que l'extension. Distribution via GitHub Releases, auto-update avec electron-updater.
Sécurité
Login via magic link (même flow que l'extension → dashboard). Token stocké dans electron-store encrypté. Requêtes signées avec access_token JWT.
Même architecture de tracking
Buffer en mémoire + flush 15s. Détection AFK à 60s. Envoie vers /api/track (même endpoint que l'extension). Les données arrivent dans app_tracking.
tracker.ts — Core
// flowly-desktop/src/tracker.tsimport activeWin from'active-win';
let buffer: Record<string, number> = {};
let currentApp: string | null = null;
let lastActivity = Date.now();
// Tick toutes les secondes (même logique que background.js)setInterval(async () => {
const win = await activeWin.getActiveWindow();
if (!win) return;
const app = win.owner.name; // "Google Chrome", "VS Code"...// Idle detectionif (Date.now() - lastActivity > 60000) return;
buffer[app] = (buffer[app] ?? 0) + 1;
}, 1000);
// Flush toutes les 15s → même endpoint que l'extensionsetInterval(async () => {
const session = awaitgetSession();
if (!session) return;
for (const [app, duration] ofObject.entries(buffer)) {
awaitfetch(`${API_BASE}/api/track`, {
method: 'POST',
headers: { Authorization: `Bearer ${session.access_token}` },
body: JSON.stringify({ app, duration, type: 'desktop' })
});
}
buffer = {};
}, 15000);
Dépendances minimales
electron ^28active-win ^8@supabase/supabase-js ^2electron-store ^8electron-updater ^6electron-builder (build)
10 — Design System
Tokens CSS & conventions
Design sobre et lisible inspiré Supabase/Linear. Pas de néons, pas d'effets superflus. Tailwind CSS recommandé.
Palette de couleurs
Background
#0a0c10
Surface
#111418
Accent (bleu)
#4F7FFF
Succès
#10B981
Streak (flamme)
#FF7A3A
Typographie
DISPLAY / HEADINGS
DM Sans 600
BODY
DM Sans 400 — texte de contenu, descriptions, labels secondaires.
Séquence en 4 semaines pour une V1 solide, en partant de ce qui existe déjà.
Semaine 1 — Fondations
Setup Next.js + Supabase
Auth, middleware, types générés auto depuis Supabase
Page callback magic link
Compatible avec OPEN_BILLING existant
Migration SQL
Créer les 3 nouvelles tables + indexes
lib/plans.ts + PLAN_LIMITS
Centraliser les limites dès le départ
Semaine 2 — Dashboard core
Sidebar + layout authentifié
Composant Sidebar.tsx, Header.tsx
Vue overview complète
Stats, top domaines, donut chart, streak widget
Page historique
Timeline + PaywallGate pour les 3j/illimité
Semaine 3 — Analytics Pro
BarChart semaine / mois
Charts.js ou Recharts, protégé PaywallGate
Export CSV côté API
Protégé plan.exportCsv
Page settings + billing
Portail Stripe, gestion abonnement
Semaine 4 — Desktop Tracker
Scaffolding Electron
main.ts, tracker.ts, sync.ts, auth.ts
Build + distribution
electron-builder + GitHub Releases
Intégration dashboard
Section "Apps bureau" dans overview, fusion des données
✅ Points clés à ne pas oublier
1. Ne jamais faire confiance au client pour les limites de plan — toujours vérifier en base côté API.
2. RLS Supabase activé sur toutes les tables — même si l'API vérifie, c'est une défense en profondeur.
3. Le magic link est déjà dans background.js — adapter uniquement l'URL de destination.
4. Le streak doit rester simple côté serveur — la même logique que dashboard.js de l'extension, juste portée en API.
5. Recharts ou Chart.js — ne pas réinventer les graphiques, utiliser une lib éprouvée.