Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions src/lib/electron-app/factories/ipcs/register-system-bandwidth-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import { ipcMain } from 'electron'
import * as fs from 'fs'
import * as os from 'os'
import * as path from 'path'

export function registerSystemInfo() {
console.log('Enregistrement des handlers système...')

// Handler pour l'espace libre
ipcMain.handle('get-free-space', async (event, folderPath: string) => {
console.log('Demande espace libre pour:', folderPath)

try {
if (!fs.existsSync(folderPath)) {
throw new Error('Le dossier n\'existe pas')
}

const stats = fs.statSync(folderPath)
if (!stats.isDirectory()) {
throw new Error('Ce n\'est pas un dossier')
}

const { spawn } = require('child_process')

return new Promise((resolve) => {
let command: string
let args: string[]

// Commandes spécifiques par OS
if (process.platform === 'win32') {
command = 'wmic'
args = ['logicaldisk', 'where', `"DeviceID='${path.parse(folderPath).root.replace('\\', '').replace(':', '')}':"`, 'get', 'FreeSpace', '/format:csv']
} else if (process.platform === 'darwin') {
command = 'df'
args = ['-k', folderPath] // -k pour avoir en KB sur Mac
} else {
command = 'df'
args = ['-B1', folderPath] // -B1 pour avoir en bytes sur Linux
}

const child = spawn(command, args, { shell: true })
let output = ''

child.stdout.on('data', (data: any) => {
output += data.toString()
})

child.on('close', (code: any) => {
console.log('Sortie commande espace disque:', output)

try {
if (process.platform === 'win32') {
// Parse WMIC output
const lines = output.split('\n').filter(line => line.trim())
for (const line of lines) {
const match = line.match(/(\d+)/)
if (match && parseInt(match[1]) > 1000000) { // Éviter les headers
const freeBytes = parseInt(match[1])
console.log(`Espace libre Windows: ${Math.round(freeBytes / (1024**3))} GB`)
resolve(freeBytes)
return
}
}
} else {
// Parse df output (Linux/Mac)
const lines = output.split('\n')
for (const line of lines) {
if (line.includes('/') && !line.startsWith('Filesystem')) {
const parts = line.trim().split(/\s+/)
if (parts.length >= 4) {
let freeBytes: number

if (process.platform === 'darwin') {
// Mac : df -k retourne en KB
freeBytes = parseInt(parts[3]) * 1024
} else {
// Linux : df -B1 retourne en bytes
freeBytes = parseInt(parts[3])
}

if (freeBytes > 0) {
console.log(`Espace libre ${process.platform}: ${Math.round(freeBytes / (1024**3))} GB`)
resolve(freeBytes)
return
}
}
}
}
}
} catch (parseError) {
console.error('Erreur parsing:', parseError)
}

console.log('Parsing échoué, utilisation valeur par défaut')
resolve(50 * 1024 * 1024 * 1024)
})

child.on('error', (err: any) => {
console.error('Erreur commande espace disque:', err)
resolve(50 * 1024 * 1024 * 1024)
})

setTimeout(() => {
child.kill()
resolve(50 * 1024 * 1024 * 1024)
}, 10000)
})

} catch (error) {
console.error('Erreur get-free-space:', error)
return 50 * 1024 * 1024 * 1024
}
})

// Handler pour la bande passante
ipcMain.handle('detect-bandwidth', async () => {
console.log('Détection de la bande passante...')

return new Promise((resolve) => {
const https = require('https')

const startTime = Date.now()
let downloadedBytes = 0

const options = {
hostname: 'speed.cloudflare.com',
path: '/__down?bytes=1048576', // 1MB
method: 'GET',
timeout: 10000,
headers: {
'User-Agent': 'ShelterApp-BandwidthTest/1.0'
}
}

const req = https.request(options, (res: any) => {
res.on('data', (chunk: any) => {
downloadedBytes += chunk.length
})

res.on('end', () => {
const duration = Math.max((Date.now() - startTime) / 1000, 0.1)
const speedBps = downloadedBytes / duration
const speedMbps = (speedBps * 8) / 1000000
const speedGbps = speedMbps / 1000

console.log(`Test vitesse: ${downloadedBytes} bytes en ${duration}s = ${speedMbps.toFixed(1)} Mbps`)

const result = {
download: Math.max(speedGbps, 0.001),
upload: Math.max(speedGbps * 0.1, 0.0005)
}

resolve(result)
})
})

req.on('error', (err: any) => {
console.log('Erreur test réseau:', err.message)
resolve({
download: 0.0,
upload: 0.00
})
})

req.on('timeout', () => {
console.log('Timeout test réseau')
req.destroy()
resolve({
download: 0.1,
upload: 0.01
})
})

req.end()
})
})

// Handlers pour les préférences
ipcMain.handle('set-storage-allocation', async (event, percentage: number) => {
try {
const configPath = path.join(os.homedir(), '.shelter-config.json')
let config: any = {}

if (fs.existsSync(configPath)) {
try {
config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
} catch (e) {
config = {}
}
}

config.storageAllocation = percentage
fs.writeFileSync(configPath, JSON.stringify(config))
console.log(`Allocation stockage sauvegardée: ${percentage}%`)
return true
} catch (error) {
console.error('Erreur sauvegarde allocation stockage:', error)
return false
}
})

ipcMain.handle('set-bandwidth-allocation', async (event, percentage: number) => {
try {
const configPath = path.join(os.homedir(), '.shelter-config.json')
let config: any = {}

if (fs.existsSync(configPath)) {
try {
config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
} catch (e) {
config = {}
}
}

config.bandwidthAllocation = percentage
fs.writeFileSync(configPath, JSON.stringify(config))
console.log(`Allocation bande passante sauvegardée: ${percentage}%`)
return true
} catch (error) {
console.error('Erreur sauvegarde allocation bande passante:', error)
return false
}
})

ipcMain.handle('get-storage-allocation', async () => {
try {
const configPath = path.join(os.homedir(), '.shelter-config.json')
if (fs.existsSync(configPath)) {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
return config.storageAllocation || 50
}
} catch (error) {
console.error('Erreur lecture allocation stockage:', error)
}
return 50
})

ipcMain.handle('get-bandwidth-allocation', async () => {
try {
const configPath = path.join(os.homedir(), '.shelter-config.json')
if (fs.existsSync(configPath)) {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
return config.bandwidthAllocation || 10
}
} catch (error) {
console.error('Erreur lecture allocation bande passante:', error)
}
return 10
})

console.log('Handlers système enregistrés ✓')
}
4 changes: 3 additions & 1 deletion src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { registerFolderPicker } from "lib/electron-app/factories/ipcs/register-f
import { registerDownloadDataset } from "lib/electron-app/factories/ipcs/register-download-dataset";
import { registerManageDownloadPath } from "lib/electron-app/factories/ipcs/register-manage-download-path";
import { registerSeedingManagement } from "lib/electron-app/factories/ipcs/register-seeding-management";
import { registerSystemInfo } from "lib/electron-app/factories/ipcs/register-system-bandwidth-info";

makeAppWithSingleInstanceLock(async () => {
await app.whenReady();
Expand All @@ -16,6 +17,7 @@ makeAppWithSingleInstanceLock(async () => {
registerManageDownloadPath();
registerDownloadDataset();
registerSeedingManagement();
registerSystemInfo();

await makeAppSetup(MainWindow);
});
});
20 changes: 18 additions & 2 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,23 @@ const API = {
removeSeedingInfo: (filePath: string) =>
ipcRenderer.invoke('remove-seeding-info', filePath),
scanDirectoryForSeeding: (directoryPath: string) =>
ipcRenderer.invoke('scan-directory-for-seeding', directoryPath)
ipcRenderer.invoke('scan-directory-for-seeding', directoryPath),

// Obtenir l'espace disque disponible (libre) pour un chemin donné
getFreeSpace: (path: string) => ipcRenderer.invoke('get-free-space', path),

// Détecter la bande passante réseau actuelle
detectBandwidth: () => ipcRenderer.invoke('detect-bandwidth'),

// Sauvegarder les préférences d'allocation
setStorageAllocation: (percentage: number) =>
ipcRenderer.invoke('set-storage-allocation', percentage),
setBandwidthAllocation: (percentage: number) =>
ipcRenderer.invoke('set-bandwidth-allocation', percentage),

// Récupérer les préférences sauvegardées
getStorageAllocation: () => ipcRenderer.invoke('get-storage-allocation'),
getBandwidthAllocation: () => ipcRenderer.invoke('get-bandwidth-allocation')
}

contextBridge.exposeInMainWorld('App', API)
contextBridge.exposeInMainWorld('App', API)
95 changes: 95 additions & 0 deletions src/renderer/components/bandwidth-monitor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { useEffect, useState } from 'react'

interface BandwidthMonitorProps {
onBandwidthAllocationChange?: (percentage: number) => void
}

export function BandwidthMonitor({ onBandwidthAllocationChange }: BandwidthMonitorProps) {
const [downloadSpeed, setDownloadSpeed] = useState<number>(0)
const [uploadSpeed, setUploadSpeed] = useState<number>(0)
const [selectedPercentage, setSelectedPercentage] = useState<number>(10)
const [isDetecting, setIsDetecting] = useState<boolean>(true)

useEffect(() => {
// Détection de la bande passante réseau réelle
const detectBandwidth = async () => {
try {
setIsDetecting(true)
const bandwidth = await window.App.detectBandwidth()
if (bandwidth) {
setDownloadSpeed(bandwidth.download)
setUploadSpeed(bandwidth.upload)
}
} catch (error) {
console.warn('Impossible de détecter la bande passante:', error)
} finally {
setIsDetecting(false)
}
}

detectBandwidth()
}, [])

const handlePercentageSelect = (percentage: number) => {
setSelectedPercentage(percentage)
onBandwidthAllocationChange?.(percentage)
}

const formatSpeed = (gbps: number): string => {
if (gbps >= 1) {
return `${gbps.toFixed(1)}Gbps`
}
return `${(gbps * 1000).toFixed(0)}Mbps`
}

return (
<div className="mb-6">
<h2 className="text-sm font-semibold mb-3 uppercase tracking-wide">
Max Bandwidth Allowed
</h2>

<div className="bg-white/10 backdrop-blur-sm rounded-lg p-4">
{isDetecting ? (
<div className="text-center py-4">
<div className="animate-pulse text-sm text-white/80">
Detecting network speed...
</div>
</div>
) : (
<>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-2">
<div className="text-lg">↓</div>
<span className="text-sm">{formatSpeed(downloadSpeed)}</span>
</div>
<div className="flex items-center space-x-2">
<div className="text-lg">↑</div>
<span className="text-sm">{formatSpeed(uploadSpeed)}</span>
</div>
</div>

<div className="flex space-x-2">
{[10, 25, 50, 75, 100].map((percentage) => (
<button
key={percentage}
onClick={() => handlePercentageSelect(percentage)}
className={`px-3 py-1 rounded text-sm font-medium transition-colors duration-200 ${
selectedPercentage === percentage
? 'bg-white text-blue-600'
: 'bg-white/20 text-white hover:bg-white/30'
}`}
>
{percentage}%
</button>
))}
</div>

<div className="mt-2 text-xs text-white/80">
Download limit: {formatSpeed(downloadSpeed * (selectedPercentage / 100))}
</div>
</>
)}
</div>
</div>
)
}
Loading