Apply new eslint rules; default to prettier for styling

This commit is contained in:
DaneEveritt 2022-06-26 15:13:52 -04:00
parent f22cce8881
commit dc84af9937
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
218 changed files with 3876 additions and 3564 deletions

View file

@ -11,17 +11,9 @@ interface ChartBlockProps {
export default ({ title, legend, children }: ChartBlockProps) => (
<div className={classNames(styles.chart_container, 'group')}>
<div className={'flex items-center justify-between px-4 py-2'}>
<h3 className={'font-header transition-colors duration-100 group-hover:text-gray-50'}>
{title}
</h3>
{legend &&
<p className={'text-sm flex items-center'}>
{legend}
</p>
}
</div>
<div className={'z-10 ml-2'}>
{children}
<h3 className={'font-header transition-colors duration-100 group-hover:text-gray-50'}>{title}</h3>
{legend && <p className={'text-sm flex items-center'}>{legend}</p>}
</div>
<div className={'z-10 ml-2'}>{children}</div>
</div>
);

View file

@ -59,16 +59,15 @@ export default () => {
const searchBar = new SearchBarAddon({ searchAddon });
const webLinksAddon = new WebLinksAddon();
const scrollDownHelperAddon = new ScrollDownHelperAddon();
const { connected, instance } = ServerContext.useStoreState(state => state.socket);
const [ canSendCommands ] = usePermissions([ 'control.console' ]);
const serverId = ServerContext.useStoreState(state => state.server.data!.id);
const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring);
const [ history, setHistory ] = usePersistedState<string[]>(`${serverId}:command_history`, []);
const [ historyIndex, setHistoryIndex ] = useState(-1);
const { connected, instance } = ServerContext.useStoreState((state) => state.socket);
const [canSendCommands] = usePermissions(['control.console']);
const serverId = ServerContext.useStoreState((state) => state.server.data!.id);
const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring);
const [history, setHistory] = usePersistedState<string[]>(`${serverId}:command_history`, []);
const [historyIndex, setHistoryIndex] = useState(-1);
const handleConsoleOutput = (line: string, prelude = false) => terminal.writeln(
(prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m',
);
const handleConsoleOutput = (line: string, prelude = false) =>
terminal.writeln((prelude ? TERMINAL_PRELUDE : '') + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m');
const handleTransferStatus = (status: string) => {
switch (status) {
@ -79,17 +78,20 @@ export default () => {
// Sent by the source node whenever the server was archived successfully.
case 'archive':
terminal.writeln(TERMINAL_PRELUDE + 'Server has been archived successfully, attempting connection to target node..\u001b[0m');
terminal.writeln(
TERMINAL_PRELUDE +
'Server has been archived successfully, attempting connection to target node..\u001b[0m'
);
}
};
const handleDaemonErrorOutput = (line: string) => terminal.writeln(
TERMINAL_PRELUDE + '\u001b[1m\u001b[41m' + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m',
);
const handleDaemonErrorOutput = (line: string) =>
terminal.writeln(
TERMINAL_PRELUDE + '\u001b[1m\u001b[41m' + line.replace(/(?:\r\n|\r|\n)$/im, '') + '\u001b[0m'
);
const handlePowerChangeEvent = (state: string) => terminal.writeln(
TERMINAL_PRELUDE + 'Server marked as ' + state + '...\u001b[0m',
);
const handlePowerChangeEvent = (state: string) =>
terminal.writeln(TERMINAL_PRELUDE + 'Server marked as ' + state + '...\u001b[0m');
const handleCommandKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'ArrowUp') {
@ -112,7 +114,7 @@ export default () => {
const command = e.currentTarget.value;
if (e.key === 'Enter' && command.length > 0) {
setHistory(prevHistory => [ command, ...prevHistory! ].slice(0, 32));
setHistory((prevHistory) => [command, ...prevHistory!].slice(0, 32));
setHistoryIndex(-1);
instance && instance.send('send command', command);
@ -146,13 +148,16 @@ export default () => {
return true;
});
}
}, [ terminal, connected ]);
}, [terminal, connected]);
useEventListener('resize', debounce(() => {
if (terminal.element) {
fitAddon.fit();
}
}, 100));
useEventListener(
'resize',
debounce(() => {
if (terminal.element) {
fitAddon.fit();
}
}, 100)
);
useEffect(() => {
const listeners: Record<string, (s: string) => void> = {
@ -161,7 +166,7 @@ export default () => {
[SocketEvent.INSTALL_OUTPUT]: handleConsoleOutput,
[SocketEvent.TRANSFER_LOGS]: handleConsoleOutput,
[SocketEvent.TRANSFER_STATUS]: handleTransferStatus,
[SocketEvent.DAEMON_MESSAGE]: line => handleConsoleOutput(line, true),
[SocketEvent.DAEMON_MESSAGE]: (line) => handleConsoleOutput(line, true),
[SocketEvent.DAEMON_ERROR]: handleDaemonErrorOutput,
};
@ -184,15 +189,17 @@ export default () => {
});
}
};
}, [ connected, instance ]);
}, [connected, instance]);
return (
<div className={styles.terminal}>
<SpinnerOverlay visible={!connected} size={'large'}/>
<div className={classNames(styles.container, styles.overflows_container, { 'rounded-b': !canSendCommands })}>
<div id={styles.terminal} ref={ref}/>
<SpinnerOverlay visible={!connected} size={'large'} />
<div
className={classNames(styles.container, styles.overflows_container, { 'rounded-b': !canSendCommands })}
>
<div id={styles.terminal} ref={ref} />
</div>
{canSendCommands &&
{canSendCommands && (
<div className={classNames('relative', styles.overflows_container)}>
<input
className={classNames('peer', styles.command_input)}
@ -204,11 +211,16 @@ export default () => {
autoCorrect={'off'}
autoCapitalize={'none'}
/>
<div className={classNames('text-gray-100 peer-focus:text-gray-50 peer-focus:animate-pulse', styles.command_icon)}>
<ChevronDoubleRightIcon className={'w-4 h-4'}/>
<div
className={classNames(
'text-gray-100 peer-focus:text-gray-50 peer-focus:animate-pulse',
styles.command_icon
)}
>
<ChevronDoubleRightIcon className={'w-4 h-4'} />
</div>
</div>
}
)}
</div>
);
};

View file

@ -10,12 +10,15 @@ interface PowerButtonProps {
}
export default ({ className }: PowerButtonProps) => {
const [ open, setOpen ] = useState(false);
const status = ServerContext.useStoreState(state => state.status.value);
const instance = ServerContext.useStoreState(state => state.socket.instance);
const [open, setOpen] = useState(false);
const status = ServerContext.useStoreState((state) => state.status.value);
const instance = ServerContext.useStoreState((state) => state.socket.instance);
const killable = status === 'stopping';
const onButtonClick = (action: PowerAction | 'kill-confirmed', e: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
const onButtonClick = (
action: PowerAction | 'kill-confirmed',
e: React.MouseEvent<HTMLButtonElement, MouseEvent>
): void => {
e.preventDefault();
if (action === 'kill') {
return setOpen(true);
@ -31,7 +34,7 @@ export default ({ className }: PowerButtonProps) => {
if (status === 'offline') {
setOpen(false);
}
}, [ status ]);
}, [status]);
return (
<div className={className}>

View file

@ -15,11 +15,11 @@ import ServerDetailsBlock from '@/components/server/console/ServerDetailsBlock';
export type PowerAction = 'start' | 'stop' | 'restart' | 'kill';
const ServerConsoleContainer = () => {
const name = ServerContext.useStoreState(state => state.server.data!.name);
const description = ServerContext.useStoreState(state => state.server.data!.description);
const isInstalling = ServerContext.useStoreState(state => state.server.data!.isInstalling);
const isTransferring = ServerContext.useStoreState(state => state.server.data!.isTransferring);
const eggFeatures = ServerContext.useStoreState(state => state.server.data!.eggFeatures, isEqual);
const name = ServerContext.useStoreState((state) => state.server.data!.name);
const description = ServerContext.useStoreState((state) => state.server.data!.description);
const isInstalling = ServerContext.useStoreState((state) => state.server.data!.isInstalling);
const isTransferring = ServerContext.useStoreState((state) => state.server.data!.isTransferring);
const eggFeatures = ServerContext.useStoreState((state) => state.server.data!.eggFeatures, isEqual);
return (
<ServerContentBlock title={'Console'} className={'flex flex-col gap-2 sm:gap-4'}>
@ -29,19 +29,19 @@ const ServerConsoleContainer = () => {
<p className={'text-sm line-clamp-2'}>{description}</p>
</div>
<div className={'flex-1'}>
<Can action={[ 'control.start', 'control.stop', 'control.restart' ]} matchAny>
<PowerButtons className={'flex sm:justify-end space-x-2'}/>
<Can action={['control.start', 'control.stop', 'control.restart']} matchAny>
<PowerButtons className={'flex sm:justify-end space-x-2'} />
</Can>
</div>
</div>
<div className={'grid grid-cols-4 gap-2 sm:gap-4'}>
<ServerDetailsBlock className={'col-span-4 lg:col-span-1 order-last lg:order-none'}/>
<ServerDetailsBlock className={'col-span-4 lg:col-span-1 order-last lg:order-none'} />
<div className={'col-span-4 lg:col-span-3'}>
<Spinner.Suspense>
<Console/>
<Console />
</Spinner.Suspense>
</div>
{isInstalling ?
{isInstalling ? (
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
<ContentContainer>
<p css={tw`text-sm text-yellow-900`}>
@ -50,26 +50,23 @@ const ServerConsoleContainer = () => {
</p>
</ContentContainer>
</div>
:
isTransferring ?
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
<ContentContainer>
<p css={tw`text-sm text-yellow-900`}>
This server is currently being transferred to another node and all actions
are unavailable.
</p>
</ContentContainer>
</div>
:
null
}
) : isTransferring ? (
<div css={tw`mt-4 rounded bg-yellow-500 p-3`}>
<ContentContainer>
<p css={tw`text-sm text-yellow-900`}>
This server is currently being transferred to another node and all actions are
unavailable.
</p>
</ContentContainer>
</div>
) : null}
</div>
<div className={'grid grid-cols-1 md:grid-cols-3 gap-2 sm:gap-4'}>
<Spinner.Suspense>
<StatGraphs/>
<StatGraphs />
</Spinner.Suspense>
</div>
<Features enabled={eggFeatures}/>
<Features enabled={eggFeatures} />
</ServerContentBlock>
);
};

View file

@ -19,7 +19,7 @@ import classNames from 'classnames';
type Stats = Record<'memory' | 'cpu' | 'disk' | 'uptime' | 'rx' | 'tx', number>;
const getBackgroundColor = (value: number, max: number | null): string | undefined => {
const delta = !max ? 0 : (value / max);
const delta = !max ? 0 : value / max;
if (delta > 0.8) {
if (delta > 0.9) {
@ -32,14 +32,14 @@ const getBackgroundColor = (value: number, max: number | null): string | undefin
};
const ServerDetailsBlock = ({ className }: { className?: string }) => {
const [ stats, setStats ] = useState<Stats>({ memory: 0, cpu: 0, disk: 0, uptime: 0, tx: 0, rx: 0 });
const [stats, setStats] = useState<Stats>({ memory: 0, cpu: 0, disk: 0, uptime: 0, tx: 0, rx: 0 });
const status = ServerContext.useStoreState(state => state.status.value);
const connected = ServerContext.useStoreState(state => state.socket.connected);
const instance = ServerContext.useStoreState(state => state.socket.instance);
const limits = ServerContext.useStoreState(state => state.server.data!.limits);
const allocation = ServerContext.useStoreState(state => {
const match = state.server.data!.allocations.find(allocation => allocation.isDefault);
const status = ServerContext.useStoreState((state) => state.status.value);
const connected = ServerContext.useStoreState((state) => state.socket.connected);
const instance = ServerContext.useStoreState((state) => state.socket.instance);
const limits = ServerContext.useStoreState((state) => state.server.data!.limits);
const allocation = ServerContext.useStoreState((state) => {
const match = state.server.data!.allocations.find((allocation) => allocation.isDefault);
return !match ? 'n/a' : `${match.alias || ip(match.ip)}:${match.port}`;
});
@ -50,7 +50,7 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
}
instance.send(SocketRequest.SEND_STATS);
}, [ instance, connected ]);
}, [instance, connected]);
useWebsocketEvent(SocketEvent.STATS, (data) => {
let stats: any = {};
@ -78,51 +78,42 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
<StatBlock
icon={faClock}
title={'Uptime'}
color={getBackgroundColor(status === 'running' ? 0 : (status !== 'offline' ? 9 : 10), 10)}
color={getBackgroundColor(status === 'running' ? 0 : status !== 'offline' ? 9 : 10, 10)}
>
{stats.uptime > 0 ?
<UptimeDuration uptime={stats.uptime / 1000}/>
:
'Offline'
}
{stats.uptime > 0 ? <UptimeDuration uptime={stats.uptime / 1000} /> : 'Offline'}
</StatBlock>
<StatBlock
icon={faMicrochip}
title={'CPU Load'}
color={getBackgroundColor(stats.cpu, limits.cpu)}
description={limits.cpu
? `This server is allowed to use up to ${limits.cpu}% of the host's available CPU resources.`
: 'No CPU limit has been configured for this server.'
description={
limits.cpu
? `This server is allowed to use up to ${limits.cpu}% of the host's available CPU resources.`
: 'No CPU limit has been configured for this server.'
}
>
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
`${stats.cpu.toFixed(2)}%`
}
{status === 'offline' ? <span className={'text-gray-400'}>Offline</span> : `${stats.cpu.toFixed(2)}%`}
</StatBlock>
<StatBlock
icon={faMemory}
title={'Memory'}
color={getBackgroundColor(stats.memory / 1024, limits.memory * 1024)}
description={limits.memory
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.memory))} of memory.`
: 'No memory limit has been configured for this server.'
description={
limits.memory
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.memory))} of memory.`
: 'No memory limit has been configured for this server.'
}
>
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToString(stats.memory)
}
{status === 'offline' ? <span className={'text-gray-400'}>Offline</span> : bytesToString(stats.memory)}
</StatBlock>
<StatBlock
icon={faHdd}
title={'Disk'}
color={getBackgroundColor(stats.disk / 1024, limits.disk * 1024)}
description={limits.disk
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.disk))} of disk space.`
: 'No disk space limit has been configured for this server.'
description={
limits.disk
? `This server is allowed to use up to ${bytesToString(mbToBytes(limits.disk))} of disk space.`
: 'No disk space limit has been configured for this server.'
}
>
{bytesToString(stats.disk)}
@ -132,22 +123,16 @@ const ServerDetailsBlock = ({ className }: { className?: string }) => {
title={'Network (Inbound)'}
description={'The total amount of network traffic that your server has recieved since it was started.'}
>
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToString(stats.tx)
}
{status === 'offline' ? <span className={'text-gray-400'}>Offline</span> : bytesToString(stats.tx)}
</StatBlock>
<StatBlock
icon={faCloudUploadAlt}
title={'Network (Outbound)'}
description={'The total amount of traffic your server has sent across the internet since it was started.'}
>
{status === 'offline' ?
<span className={'text-gray-400'}>Offline</span>
:
bytesToString(stats.rx)
description={
'The total amount of traffic your server has sent across the internet since it was started.'
}
>
{status === 'offline' ? <span className={'text-gray-400'}>Offline</span> : bytesToString(stats.rx)}
</StatBlock>
</div>
);

View file

@ -21,7 +21,7 @@ export default ({ title, icon, color, description, className, children }: StatBl
return (
<Tooltip arrow placement={'top'} disabled={!description} content={description || ''}>
<div className={classNames(styles.stat_block, 'bg-gray-600', className)}>
<div className={classNames(styles.status_bar, color || 'bg-gray-700')}/>
<div className={classNames(styles.status_bar, color || 'bg-gray-700')} />
<div className={classNames(styles.icon, color || 'bg-gray-700')}>
<Icon
icon={icon}

View file

@ -12,8 +12,8 @@ import ChartBlock from '@/components/server/console/ChartBlock';
import Tooltip from '@/components/elements/tooltip/Tooltip';
export default () => {
const status = ServerContext.useStoreState(state => state.status.value);
const limits = ServerContext.useStoreState(state => state.server.data!.limits);
const status = ServerContext.useStoreState((state) => state.status.value);
const limits = ServerContext.useStoreState((state) => state.server.data!.limits);
const previous = useRef<Record<'tx' | 'rx', number>>({ tx: -1, rx: -1 });
const cpu = useChartTickLabel('CPU', limits.cpu, '%');
@ -24,14 +24,14 @@ export default () => {
scales: {
y: {
ticks: {
callback (value) {
callback(value) {
return bytesToString(typeof value === 'string' ? parseInt(value, 10) : value);
},
},
},
},
},
callback (opts, index) {
callback(opts, index) {
return {
...opts,
label: !index ? 'Network In' : 'Network Out',
@ -47,7 +47,7 @@ export default () => {
memory.clear();
network.clear();
}
}, [ status ]);
}, [status]);
useWebsocketEvent(SocketEvent.STATS, (data: string) => {
let values: any = {};
@ -70,25 +70,25 @@ export default () => {
return (
<>
<ChartBlock title={'CPU Load'}>
<Line {...cpu.props}/>
<Line {...cpu.props} />
</ChartBlock>
<ChartBlock title={'Memory'}>
<Line {...memory.props}/>
<Line {...memory.props} />
</ChartBlock>
<ChartBlock
title={'Network'}
legend={
<>
<Tooltip arrow content={'Inbound'}>
<CloudDownloadIcon className={'mr-2 w-4 h-4 text-yellow-400'}/>
<CloudDownloadIcon className={'mr-2 w-4 h-4 text-yellow-400'} />
</Tooltip>
<Tooltip arrow content={'Outbound'}>
<CloudUploadIcon className={'w-4 h-4 text-cyan-400'}/>
<CloudUploadIcon className={'w-4 h-4 text-cyan-400'} />
</Tooltip>
</>
}
>
<Line {...network.props}/>
<Line {...network.props} />
</ChartBlock>
</>
);

View file

@ -70,24 +70,33 @@ const options: ChartOptions<'line'> = {
},
};
function getOptions (opts?: DeepPartial<ChartOptions<'line'>> | undefined): ChartOptions<'line'> {
function getOptions(opts?: DeepPartial<ChartOptions<'line'>> | undefined): ChartOptions<'line'> {
return deepmerge(options, opts || {});
}
type ChartDatasetCallback = (value: ChartDataset<'line'>, index: number) => ChartDataset<'line'>;
function getEmptyData (label: string, sets = 1, callback?: ChartDatasetCallback | undefined): ChartData<'line'> {
const next = callback || (value => value);
function getEmptyData(label: string, sets = 1, callback?: ChartDatasetCallback | undefined): ChartData<'line'> {
const next = callback || ((value) => value);
return {
labels: Array(20).fill(0).map((_, index) => index),
datasets: Array(sets).fill(0).map((_, index) => next({
fill: true,
label,
data: Array(20).fill(0),
borderColor: theme('colors.cyan.400'),
backgroundColor: hexToRgba(theme('colors.cyan.700'), 0.5),
}, index)),
labels: Array(20)
.fill(0)
.map((_, index) => index),
datasets: Array(sets)
.fill(0)
.map((_, index) =>
next(
{
fill: true,
label,
data: Array(20).fill(0),
borderColor: theme('colors.cyan.400'),
backgroundColor: hexToRgba(theme('colors.cyan.700'), 0.5),
},
index
)
),
};
}
@ -99,28 +108,36 @@ interface UseChartOptions {
callback?: ChartDatasetCallback | undefined;
}
function useChart (label: string, opts?: UseChartOptions) {
const options = getOptions(typeof opts?.options === 'number' ? { scales: { y: { min: 0, suggestedMax: opts.options } } } : opts?.options);
const [ data, setData ] = useState(getEmptyData(label, opts?.sets || 1, opts?.callback));
function useChart(label: string, opts?: UseChartOptions) {
const options = getOptions(
typeof opts?.options === 'number' ? { scales: { y: { min: 0, suggestedMax: opts.options } } } : opts?.options
);
const [data, setData] = useState(getEmptyData(label, opts?.sets || 1, opts?.callback));
const push = (items: number | null | ((number | null)[])) => setData(state => merge(state, {
datasets: (Array.isArray(items) ? items : [ items ]).map((item, index) => ({
...state.datasets[index],
data: state.datasets[index].data.slice(1).concat(item),
})),
}));
const push = (items: number | null | (number | null)[]) =>
setData((state) =>
merge(state, {
datasets: (Array.isArray(items) ? items : [items]).map((item, index) => ({
...state.datasets[index],
data: state.datasets[index].data.slice(1).concat(item),
})),
})
);
const clear = () => setData(state => merge(state, {
datasets: state.datasets.map(value => ({
...value,
data: Array(20).fill(0),
})),
}));
const clear = () =>
setData((state) =>
merge(state, {
datasets: state.datasets.map((value) => ({
...value,
data: Array(20).fill(0),
})),
})
);
return { props: { data, options }, push, clear };
}
function useChartTickLabel (label: string, max: number, tickLabel: string) {
function useChartTickLabel(label: string, max: number, tickLabel: string) {
return useChart(label, {
sets: 1,
options: {
@ -128,7 +145,7 @@ function useChartTickLabel (label: string, max: number, tickLabel: string) {
y: {
suggestedMax: max,
ticks: {
callback (value) {
callback(value) {
return `${value}${tickLabel}`;
},
},