diff --git a/electron/main.js b/electron/main.js index edb5131..7aadda2 100644 --- a/electron/main.js +++ b/electron/main.js @@ -10,8 +10,24 @@ const { spawn } = require('child_process'); const http = require('http'); const https = require('https'); const QRCode = require('qrcode'); +const yaml = require('yaml'); const setupHelper = require('./setup-helper'); +const CONFIG_PATH = path.join(__dirname, '..', 'server', 'config', 'config.yaml'); + +function readConfig() { + return yaml.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); +} + +function writeConfig(config) { + fs.writeFileSync(CONFIG_PATH, yaml.stringify(config), 'utf8'); +} + +function slugify(text) { + return text.toString().normalize('NFD').replace(/[̀-ͯ]/g, '') + .toLowerCase().trim().replace(/\s+/g, '-').replace(/[^\w-]+/g, '').replace(/--+/g, '-'); +} + // État de l'application let mainWindow = null; let tray = null; @@ -366,6 +382,60 @@ app.whenReady().then(async () => { return setupHelper.getNetworkIP(); }); + // ========== Groupes (lecture/écriture YAML directe, sans serveur) ========== + + ipcMain.handle('groups:list', () => { + try { + const config = readConfig(); + return { groups: config.groups || [] }; + } catch (error) { + return { groups: [], error: error.message }; + } + }); + + ipcMain.handle('groups:create', (event, { name, audioBitrate }) => { + try { + const config = readConfig(); + const id = slugify(name); + if ((config.groups || []).find(g => slugify(g.name) === id)) { + return { success: false, error: `Un groupe "${name}" existe déjà` }; + } + const group = { name, ...(audioBitrate ? { audioBitrate } : {}) }; + config.groups = [...(config.groups || []), group]; + writeConfig(config); + return { success: true, group }; + } catch (error) { + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('groups:update', (event, { id, name, audioBitrate }) => { + try { + const config = readConfig(); + const idx = (config.groups || []).findIndex(g => slugify(g.name) === id); + if (idx === -1) return { success: false, error: `Groupe ${id} introuvable` }; + if (name !== undefined) config.groups[idx].name = name; + if (audioBitrate !== undefined) config.groups[idx].audioBitrate = audioBitrate; + writeConfig(config); + return { success: true, group: config.groups[idx] }; + } catch (error) { + return { success: false, error: error.message }; + } + }); + + ipcMain.handle('groups:delete', (event, { id }) => { + try { + const config = readConfig(); + const idx = (config.groups || []).findIndex(g => slugify(g.name) === id); + if (idx === -1) return { success: false, error: `Groupe ${id} introuvable` }; + config.groups.splice(idx, 1); + writeConfig(config); + return { success: true }; + } catch (error) { + return { success: false, error: error.message }; + } + }); + ipcMain.handle('config:export', async () => { const configPath = path.join(__dirname, '..', 'server', 'config', 'config.yaml'); diff --git a/electron/package.json b/electron/package.json index bb84e4f..f990cb9 100644 --- a/electron/package.json +++ b/electron/package.json @@ -55,6 +55,7 @@ }, "dependencies": { "electron-store": "^8.1.0", - "qrcode": "^1.5.4" + "qrcode": "^1.5.4", + "yaml": "^2.9.0" } } diff --git a/electron/preload.js b/electron/preload.js index 2dd75d4..a6d2f2f 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -43,6 +43,14 @@ contextBridge.exposeInMainWorld('electronAPI', { import: () => ipcRenderer.invoke('config:import') }, + // Groupes : lecture/écriture YAML directe (fonctionne sans serveur) + groups: { + list: () => ipcRenderer.invoke('groups:list'), + create: (data) => ipcRenderer.invoke('groups:create', data), + update: (data) => ipcRenderer.invoke('groups:update', data), + delete: (data) => ipcRenderer.invoke('groups:delete', data) + }, + // Helpers platform: process.platform, version: process.env.npm_package_version || '0.3.0' diff --git a/electron/ui/app.js b/electron/ui/app.js index 2d24cc4..009fc3f 100644 --- a/electron/ui/app.js +++ b/electron/ui/app.js @@ -302,17 +302,19 @@ async function fetchDevices() { } async function fetchGroups() { - const data = await apiCall('/admin/groups'); - if (!data) return; - const container = document.getElementById('groups-list'); + // Lecture directe depuis config.yaml via IPC (fonctionne sans serveur) + const data = await window.electronAPI.groups.list(); + if (!data.groups || data.groups.length === 0) { container.innerHTML = '
Aucun groupe configuré
'; return; } - container.innerHTML = data.groups.map(group => { + const serverNote = serverRunning ? '' : 'Serveur arrêté — les modifications seront appliquées au prochain démarrage.
'; + + container.innerHTML = serverNote + data.groups.map(group => { const id = slugify(group.name); return `