throw error;
}
}
+
+ /**
+ * Crée ou met à jour la stratégie d'un utilisateur pour une paire donnée.
+ * Appelée par le Frontend / App Mobile (POST /api/strategy/select)
+ */
+ async upsertUserStrategy(userId, pairId, mode, params) {
+ const now = Date.now();
+ const strategyKey = `${mode}_${pairId}`; // Génération d'une clé simple
+
+ // On vérifie si l'utilisateur a déjà une configuration pour cette paire
+ const checkSql = `SELECT user_strategy_id FROM user_strategies WHERE user_id = ? AND pair_id = ?`;
+ const [rows] = await db.query(checkSql, [userId, pairId]); // ⚠️ Remplace 'db' par le nom de ta variable de connexion (souvent 'pool' ou 'db')
+
+ if (rows.length > 0) {
+ // MISE À JOUR : La stratégie existe, on l'écrase avec le nouveau mode
+ const updateSql = `
+ UPDATE user_strategies
+ SET mode = ?, params = ?, strategy_key = ?, is_active = 1, updated_at_ms = ?
+ WHERE user_id = ? AND pair_id = ?
+ `;
+ await db.query(updateSql, [mode, params, strategyKey, now, userId, pairId]);
+ return { action: 'updated', user_id: userId, pair_id: pairId, mode };
+ } else {
+ // CRÉATION : C'est la première fois qu'il configure cette paire
+ const insertSql = `
+ INSERT INTO user_strategies
+ (user_strategy_id, user_id, pair_id, strategy_key, mode, params, is_active, created_at_ms, updated_at_ms)
+ VALUES (UUID(), ?, ?, ?, ?, ?, 1, ?, ?)
+ `;
+ await db.query(insertSql, [userId, pairId, strategyKey, mode, params, now, now]);
+ return { action: 'created', user_id: userId, pair_id: pairId, mode };
+ }
+ }
}
const strategyRepo = new StrategyRepo();
// =========================================================
-// STRATEGY SERVICE - Serveur standalone (stub)
-// =========================================================
-// Ce fichier est volontairement minimal.
-// Son rôle : avoir un process indépendant sur le port 3002
-// pour que le gateway ne retourne pas 502 sur /api/strategy/*
-// et pour que le health check soit vert au démarrage.
-//
-// USAGE : node modules/strategy/server.js (depuis Wallette/server/)
-// PORT : process.env.STRATEGY_PORT || 3002
-//
-// QUAND Sacha aura finalisé son module, ce fichier sera
-// remplacé par un vrai serveur avec les routes strategy.
+// STRATEGY SERVICE - Serveur
// =========================================================
import cors from 'cors';
import path from 'path';
import { fileURLToPath } from 'url';
+// IMPORT DES REPOSITORIES
+import strategyRepo from './repositories/StrategyRepo.js';
+import signalRepo from './repositories/SignalRepo.js';
+
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.resolve(__dirname, '../../.env') });
app.use(express.json());
// =========================================================
-// HEALTH CHECK (requis par le gateway)
+// HEALTH CHECk
// =========================================================
app.get('/health', (req, res) => {
res.json({
ok: true,
service: 'strategy-service',
port: PORT,
- status: 'stub — module Sacha en cours d\'intégration',
+ status: 'ONLINE — Module opérationnel',
});
});
// =========================================================
-// STUB ROUTES /api/strategy/*
+// ROUTE 1 : SÉLECTIONNER UNE STRATÉGIE (App Mobile / Web)
+// POST /api/strategy/select
+// =========================================================
+app.post('/api/strategy/select', async (req, res) => {
+ try {
+ const { userId, pairId, mode, params } = req.body;
+
+ if (!userId || !pairId || !mode) {
+ return res.status(400).json({ ok: false, message: "Données incomplètes (userId, pairId, mode requis)." });
+ }
+
+ // Appel au Repository pour insérer/mettre à jour la stratégie
+ const result = await strategyRepo.upsertUserStrategy(userId, pairId, mode, params || '{}');
+
+ return res.status(200).json({
+ ok: true,
+ message: "Stratégie configurée avec succès",
+ data: result
+ });
+ } catch (error) {
+ console.error("Erreur POST /api/strategy/select :", error);
+ return res.status(500).json({ ok: false, message: "Erreur interne du serveur." });
+ }
+});
+
+// =========================================================
+// ROUTE 2 : RÉCUPÉRER LE DERNIER SIGNAL (Dashboard Web)
+// GET /api/signal/current?userId=...&pair=...
+// =========================================================
+app.get('/api/signal/current', async (req, res) => {
+ try {
+ const { userId, pair } = req.query;
+
+ if (!userId || !pair) {
+ return res.status(400).json({ ok: false, message: "Les paramètres userId et pair sont requis." });
+ }
+
+ // Appel au Repository pour chercher le dernier signal
+ const latestSignal = await signalRepo.getLatestSignal(userId, pair);
+
+ if (!latestSignal) {
+ return res.status(404).json({ ok: true, data: null, message: "Aucun signal récent trouvé." });
+ }
+
+ return res.status(200).json({ ok: true, data: latestSignal });
+ } catch (error) {
+ console.error("Erreur GET /api/signal/current :", error);
+ return res.status(500).json({ ok: false, message: "Erreur interne du serveur." });
+ }
+});
+
+// =========================================================
+// FALLBACK POUR LES AUTRES ROUTES STRATEGY (Toujours en 501)
// =========================================================
-// Répond 501 (Not Implemented) pour toutes les routes
-// au lieu de laisser le gateway retourner 502.
app.use('/api/strategy', (req, res) => {
res.status(501).json({
- ok: false,
- error: {
- code: 'NOT_IMPLEMENTED',
- message: 'strategy-service non encore disponible',
- },
+ ok: false,
+ error: { code: 'NOT_IMPLEMENTED', message: 'Cette fonctionnalité arrive bientôt.' },
});
});
// =========================================================
-// 404
+// 404 GLOBAL
// =========================================================
app.use((req, res) => {
res.status(404).json({
- ok: false,
+ ok: false,
error: { code: 'NOT_FOUND', message: 'Route non trouvée' },
});
});
app.listen(PORT, () => {
console.log(`
╔══════════════════════════════════════════╗
-║ STRATEGY SERVICE (STUB) ║
+║ STRATEGY SERVICE (ONLINE) ║
║ HTTP : http://localhost:${PORT} ║
-║ → /health = OK ║
-║ → /api/strategy/* = 501 ║
+║ → /health = OK ║
+║ → POST /api/strategy/select = OK ║
+║ → GET /api/signal/current = OK ║
╚══════════════════════════════════════════╝`);
-});
+});
\ No newline at end of file