()
| 170 | } |
| 171 | |
| 172 | function exportCredentialsCSV() { |
| 173 | if (!credentialsCache || credentialsCache.length === 0) { |
| 174 | addConsoleMessage('No credentials to export', 'warning'); |
| 175 | return; |
| 176 | } |
| 177 | const csvRows = [['IP', 'Service', 'Port', 'Username', 'Password']]; |
| 178 | credentialsCache.forEach(r => { |
| 179 | csvRows.push([r.ip || '', r.service || '', CRED_SERVICE_PORTS[r.service] || '', r.username || '', r.password || '']); |
| 180 | }); |
| 181 | const csv = csvRows.map(r => r.map(c => `"${String(c).replace(/"/g, '""')}"`).join(',')).join('\n'); |
| 182 | const blob = new Blob([csv], { type: 'text/csv' }); |
| 183 | const url = URL.createObjectURL(blob); |
| 184 | const a = document.createElement('a'); |
| 185 | a.href = url; |
| 186 | a.download = `ragnar_credentials_${new Date().toISOString().slice(0,10)}.csv`; |
| 187 | a.click(); |
| 188 | URL.revokeObjectURL(url); |
| 189 | } |
| 190 | |
| 191 | const configMetadata = { |
| 192 | manual_mode: { |
nothing calls this directly
no test coverage detected