admin/options/config: replaced "copy" button with "export"
Massimo Melina committed
Apr 9, 2025 at 23:59 UTC
51e6131212f213d62ecfca670ba09090c5ea78ed
2 files changed
+17
-8
admin/src/ConfigFilePage.ts
+5
-7
@@ -3,13 +3,12 @@
3
import { createElement as h, Fragment, useEffect, useState } from 'react';
4
import { apiCall, useApiEx } from './api'
5
import { Alert, Box } from '@mui/material'
6
-import { copyTextToClipboard, focusSelector, isCtrlKey, KeepInScreen, md } from './misc'
6
+import { downloadFileWithContent, focusSelector, isCtrlKey, KeepInScreen } from './misc'
7
import { Btn, Flex, IconBtn, reloadBtn } from './mui';
8
-import { Save, ContentCopy, Edit } from '@mui/icons-material'
8
+import { Save, Edit, Download } from '@mui/icons-material'
9
import { TextEditor } from './TextEditor';
10
import { state } from './state';
11
import { DisplayField } from '@hfs/mui-grid-form'
12
-import { toast } from './dialog';
12
13
export default function ConfigFilePage() {
14
state.title = "Config file"
@@ -21,7 +20,7 @@ export default function ConfigFilePage() {
20
useEffect(() => { saved !== undefined && setText(saved || '') }, [saved])
21
return h(Fragment, {},
22
h(Flex, { flexWrap: 'wrap', justifyContent: 'space-between' },
24
- h(Btn, { icon: ContentCopy, onClick: copy }, "Copy without passwords"),
23
+ h(Btn, { icon: Download, onClick: exportConfig }, "Export without passwords"),
24
edit ? h(Fragment, {},
25
reloadBtn(reload),
26
h(IconBtn, {
@@ -60,13 +59,12 @@ export default function ConfigFilePage() {
59
return apiCall('set_config_text', { text }).then(() => setSaved(text))
60
}
61
63
- function copy() {
62
+ function exportConfig() {
63
const s = (text || '')
64
.replace(/^(\s*(\w*password(?!_change)\w*|srp):\s*).+\n/gm, '$1removed\n')
65
.replace(/(:\/\/)[^/@\s]+@/g, '$1removed@')
66
+ 'custom_html: | # this is currently ignored by hfs, just here for reference\n' + data.customHtml.replace(/^/gm, ' ')
67
if (!s) return
69
- copyTextToClipboard(s)
70
- toast(md`Copied!\ncustom.html included`)
68
+ downloadFileWithContent('config_no_passwords.yaml', s)
69
}
70
}
shared/index.ts
+12
-1
@@ -190,7 +190,18 @@ export function copyTextToClipboard(text: string) {
190
d.body.removeChild(ta)
191
}
192
193
+export function downloadFileWithContent(name: string, content: Blob | string) {
194
+ const blob = content instanceof Blob ? content : new Blob([content], {type: 'text/plain'})
195
+ const a = document.createElement('a')
196
+ a.href = URL.createObjectURL(blob)
197
+ a.download = name
198
+ a.style.display = 'none'
199
+ document.body.append(a)
200
+ a.click()
201
+ setTimeout(() => a.remove(), 100) // Chrome needs this timeout
202
+}
203
+
204
Element.prototype.replaceChildren ||= function(this:Element, addNodes) { // polyfill
194
- while (this.lastChild) this.removeChild(this.lastChild);
205
+ while (this.lastChild) this.removeChild(this.lastChild)
206
if (addNodes !== undefined) this.append(addNodes);
207
}