main
ts 72 lines 3.46 KB
Raw
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
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 { downloadFileWithContent, focusSelector, isCtrlKey, KeepInScreen, prefix } from './misc'
7 import { Btn, Flex, IconBtn, reloadBtn } from './mui';
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 { adminApis } from '../../src/adminApis'
13
14 export default function ConfigFilePage() {
15 state.title = "Config file"
16 const { data, reload, element } = useApiEx<typeof adminApis.get_config_text>('get_config_text', {})
17 const [text, setText] = useState<string | undefined>()
18 const [saved, setSaved] = useState<string | undefined>()
19 const [edit, setEdit] = useState(false)
20 useEffect(() => { setSaved(data?.text) }, [data])
21 useEffect(() => { saved !== undefined && setText(saved || '') }, [saved])
22 return element || h(Fragment, {},
23 h(Flex, { flexWrap: 'wrap', justifyContent: 'space-between' },
24 h(Btn, { icon: Download, onClick: exportConfig, disabled: !data }, "Export without passwords"),
25 edit ? h(Fragment, {},
26 reloadBtn(reload),
27 h(IconBtn, {
28 icon: Save,
29 title: "Save\n(ctrl+enter)",
30 modified: text !== saved,
31 onClick: save,
32 }),
33 h(Alert, { severity: 'warning', sx: { minWidth: '10em' } }, "Be careful, you can easily break things here"),
34 ) : h(Btn, {
35 icon: Edit,
36 variant: 'outlined',
37 onClick() {
38 setEdit(true)
39 setTimeout(() => focusSelector('main textarea'), 500)
40 }
41 }, "Edit"),
42 h(Box, { sx: { flex: 1, minWidth: 'fit-content' } }, h(DisplayField, { label: "File path", value: data?.fullPath, size: 'small' }))
43 ),
44 element || text !== undefined && // avoids bad undo behavior on start
45 h(Box, { sx: { '& pre,& textarea': { wordBreak: 'break-all !important' } } }, // fixes long lines not wrapping at the right point when the side menu is visible
46 h(KeepInScreen, { margin: 10 }, h(TextEditor, {
47 value: text,
48 disabled: !edit,
49 style: { background: '#8881' },
50 onValueChange: setText,
51 onKeyDown(ev) {
52 if (['s','Enter'].includes(isCtrlKey(ev) as any)) {
53 void save()
54 ev.preventDefault()
55 }
56 },
57 }))),
58 )
59
60 function save() {
61 return apiCall('set_config_text', { text }).then(() => setSaved(text))
62 }
63
64 function exportConfig() {
65 const s = (text || '')
66 .replace(/^(\s*(\w*password|_\w*|token|srp|dynamic_dns_url):\s*).+\n/gm, '$1removed\n')
67 .replace(/(:\/\/)[^/@\s]+@/g, '$1removed@') // url credentials
68 + prefix('custom_html: | # this is currently ignored by hfs, just here for reference\n', data!.customHtml?.replace(/^/gm, ' '))
69 if (!s) return
70 downloadFileWithContent('config_no_passwords.yaml', s)
71 }
72 }