forked from pterodactyl/panel
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStartupContainer.tsx
More file actions
137 lines (126 loc) · 5.88 KB
/
StartupContainer.tsx
File metadata and controls
137 lines (126 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import React, { useCallback, useEffect, useState } from 'react';
import TitledGreyBox from '@/components/elements/TitledGreyBox';
import tw from 'twin.macro';
import VariableBox from '@/components/server/startup/VariableBox';
import ServerContentBlock from '@/components/elements/ServerContentBlock';
import getServerStartup from '@/api/swr/getServerStartup';
import Spinner from '@/components/elements/Spinner';
import { ServerError } from '@/components/elements/ScreenBlock';
import { httpErrorToHuman } from '@/api/http';
import { ServerContext } from '@/state/server';
import { useDeepCompareEffect } from '@/plugins/useDeepCompareEffect';
import Select from '@/components/elements/Select';
import isEqual from 'react-fast-compare';
import Input from '@/components/elements/Input';
import setSelectedDockerImage from '@/api/server/setSelectedDockerImage';
import InputSpinner from '@/components/elements/InputSpinner';
import useFlash from '@/plugins/useFlash';
const StartupContainer = () => {
const [loading, setLoading] = useState(false);
const { clearFlashes, clearAndAddHttpError } = useFlash();
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
const variables = ServerContext.useStoreState(
({ server }) => ({
variables: server.data!.variables,
invocation: server.data!.invocation,
dockerImage: server.data!.dockerImage,
}),
isEqual
);
const { data, error, isValidating, mutate } = getServerStartup(uuid, {
...variables,
dockerImages: { [variables.dockerImage]: variables.dockerImage },
});
const setServerFromState = ServerContext.useStoreActions((actions) => actions.server.setServerFromState);
const isCustomImage =
data &&
!Object.values(data.dockerImages)
.map((v) => v.toLowerCase())
.includes(variables.dockerImage.toLowerCase());
useEffect(() => {
// Since we're passing in initial data this will not trigger on mount automatically. We
// want to always fetch fresh information from the API however when we're loading the startup
// information.
mutate();
}, []);
useDeepCompareEffect(() => {
if (!data) return;
setServerFromState((s) => ({
...s,
invocation: data.invocation,
variables: data.variables,
}));
}, [data]);
const updateSelectedDockerImage = useCallback(
(v: React.ChangeEvent<HTMLSelectElement>) => {
setLoading(true);
clearFlashes('startup:image');
const image = v.currentTarget.value;
setSelectedDockerImage(uuid, image)
.then(() => setServerFromState((s) => ({ ...s, dockerImage: image })))
.catch((error) => {
console.error(error);
clearAndAddHttpError({ key: 'startup:image', error });
})
.then(() => setLoading(false));
},
[uuid]
);
return !data ? (
!error || (error && isValidating) ? (
<Spinner centered size={Spinner.Size.LARGE} />
) : (
<ServerError title={'Oops!'} message={httpErrorToHuman(error)} onRetry={() => mutate()} />
)
) : (
<ServerContentBlock title={'Startup Settings'} showFlashKey={'startup:image'}>
<div css={tw`md:flex`}>
<TitledGreyBox title={'Startup Command'} css={tw`flex-1`}>
<div css={tw`px-1 py-2`}>
<p css={tw`font-mono bg-neutral-900 rounded py-2 px-4`}>{data.invocation}</p>
</div>
</TitledGreyBox>
<TitledGreyBox title={'Docker Image'} css={tw`flex-1 lg:flex-none lg:w-1/3 mt-8 md:mt-0 md:ml-10`}>
{Object.keys(data.dockerImages).length > 1 && !isCustomImage ? (
<>
<InputSpinner visible={loading}>
<Select
disabled={Object.keys(data.dockerImages).length < 2}
onChange={updateSelectedDockerImage}
defaultValue={variables.dockerImage}
>
{Object.keys(data.dockerImages).map((key) => (
<option key={data.dockerImages[key]} value={data.dockerImages[key]}>
{key}
</option>
))}
</Select>
</InputSpinner>
<p css={tw`text-xs text-neutral-300 mt-2`}>
This is an advanced feature allowing you to select a Docker image to use when running
this server instance.
</p>
</>
) : (
<>
<Input disabled readOnly value={variables.dockerImage} />
{isCustomImage && (
<p css={tw`text-xs text-neutral-300 mt-2`}>
This {"server's"} Docker image has been manually set by an administrator and cannot
be changed through this UI.
</p>
)}
</>
)}
</TitledGreyBox>
</div>
<h3 css={tw`mt-8 mb-2 text-2xl`}>Variables</h3>
<div css={tw`grid gap-8 md:grid-cols-2`}>
{data.variables.map((variable) => (
<VariableBox key={variable.envVariable} variable={variable} />
))}
</div>
</ServerContentBlock>
);
};
export default StartupContainer;