Skip to content

Commit 82bc389

Browse files
Feat/manage env vars (#107)
* ui to manage env variables added * ui to manage env variables updated * ui to manage env variables updated * ui to manage env variables updated * ui to manage env variables updated * ui updates * ui updates * ui updates * ui updates * env vars input field updated * env vars input field updated Co-authored-by: Anik Ghosh <[email protected]>
1 parent 8df8010 commit 82bc389

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+40201
-35
lines changed

dashboard/package-lock.json

Lines changed: 2304 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"esbuild": "^0.14.9",
2121
"framer-motion": "^5.5.5",
2222
"graphql": "^16.2.0",
23+
"lodash": "^4.17.21",
2324
"react": "^17.0.2",
2425
"react-dom": "^17.0.2",
2526
"react-icons": "^4.3.1",
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
import React from 'react';
2+
import {
3+
Box,
4+
Flex,
5+
Input,
6+
Center,
7+
InputGroup,
8+
InputRightElement,
9+
Tag,
10+
TagLabel,
11+
TagRightIcon,
12+
Select,
13+
Textarea,
14+
Switch,
15+
Code,
16+
} from '@chakra-ui/react';
17+
import {
18+
FaRegClone,
19+
FaRegEye,
20+
FaRegEyeSlash,
21+
FaPlus,
22+
FaTimes,
23+
} from 'react-icons/fa';
24+
import {
25+
ArrayInputOperations,
26+
ArrayInputType,
27+
SelectInputType,
28+
HiddenInputType,
29+
TextInputType,
30+
TextAreaInputType,
31+
SwitchInputType,
32+
} from '../constants';
33+
import { copyTextToClipboard } from '../utils';
34+
35+
const InputField = ({
36+
inputType,
37+
envVariables,
38+
setEnvVariables,
39+
fieldVisibility,
40+
setFieldVisibility,
41+
...downshiftProps
42+
}: any) => {
43+
const props = {
44+
size: 'sm',
45+
...downshiftProps,
46+
};
47+
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
48+
Record<string, boolean>
49+
>({
50+
ROLES: false,
51+
DEFAULT_ROLES: false,
52+
PROTECTED_ROLES: false,
53+
ALLOWED_ORIGINS: false,
54+
});
55+
const [inputData, setInputData] = React.useState<Record<string, string>>({
56+
ROLES: '',
57+
DEFAULT_ROLES: '',
58+
PROTECTED_ROLES: '',
59+
ALLOWED_ORIGINS: '',
60+
});
61+
const updateInputHandler = (
62+
type: string,
63+
operation: any,
64+
role: string = ''
65+
) => {
66+
if (operation === ArrayInputOperations.APPEND) {
67+
if (inputData[type] !== '') {
68+
setEnvVariables({
69+
...envVariables,
70+
[type]: [...envVariables[type], inputData[type]],
71+
});
72+
setInputData({ ...inputData, [type]: '' });
73+
}
74+
setInputFieldVisibility({ ...inputFieldVisibility, [type]: false });
75+
}
76+
if (operation === ArrayInputOperations.REMOVE) {
77+
let updatedEnvVars = envVariables[type].filter(
78+
(item: string) => item !== role
79+
);
80+
setEnvVariables({
81+
...envVariables,
82+
[type]: updatedEnvVars,
83+
});
84+
}
85+
};
86+
if (Object.values(TextInputType).includes(inputType)) {
87+
return (
88+
<InputGroup size="sm">
89+
<Input
90+
{...props}
91+
value={envVariables[inputType]}
92+
onChange={(
93+
event: Event & {
94+
target: HTMLInputElement;
95+
}
96+
) =>
97+
setEnvVariables({
98+
...envVariables,
99+
[inputType]: event.target.value,
100+
})
101+
}
102+
/>
103+
<InputRightElement
104+
children={<FaRegClone color="#bfbfbf" />}
105+
cursor="pointer"
106+
onClick={() => copyTextToClipboard(envVariables[inputType])}
107+
/>
108+
</InputGroup>
109+
);
110+
}
111+
if (Object.values(HiddenInputType).includes(inputType)) {
112+
return (
113+
<InputGroup size="sm">
114+
<Input
115+
{...props}
116+
value={envVariables[inputType]}
117+
onChange={(
118+
event: Event & {
119+
target: HTMLInputElement;
120+
}
121+
) =>
122+
setEnvVariables({
123+
...envVariables,
124+
[inputType]: event.target.value,
125+
})
126+
}
127+
type={!fieldVisibility[inputType] ? 'password' : 'text'}
128+
/>
129+
<InputRightElement
130+
right="15px"
131+
children={
132+
<Flex>
133+
{fieldVisibility[inputType] ? (
134+
<Center
135+
w="25px"
136+
margin="0 1.5%"
137+
cursor="pointer"
138+
onClick={() =>
139+
setFieldVisibility({
140+
...fieldVisibility,
141+
[inputType]: false,
142+
})
143+
}
144+
>
145+
<FaRegEyeSlash color="#bfbfbf" />
146+
</Center>
147+
) : (
148+
<Center
149+
w="25px"
150+
margin="0 1.5%"
151+
cursor="pointer"
152+
onClick={() =>
153+
setFieldVisibility({
154+
...fieldVisibility,
155+
[inputType]: true,
156+
})
157+
}
158+
>
159+
<FaRegEye color="#bfbfbf" />
160+
</Center>
161+
)}
162+
<Center
163+
w="25px"
164+
margin="0 1.5%"
165+
cursor="pointer"
166+
onClick={() => copyTextToClipboard(envVariables[inputType])}
167+
>
168+
<FaRegClone color="#bfbfbf" />
169+
</Center>
170+
</Flex>
171+
}
172+
/>
173+
</InputGroup>
174+
);
175+
}
176+
if (Object.values(ArrayInputType).includes(inputType)) {
177+
return (
178+
<Flex
179+
border="1px solid #e2e8f0"
180+
w="100%"
181+
h="45px"
182+
wrap="wrap"
183+
overflow="scroll"
184+
padding="1%"
185+
>
186+
{envVariables[inputType].map((role: string, index: number) => (
187+
<Box key={index} margin="1" role="group">
188+
<Tag
189+
size="sm"
190+
variant="outline"
191+
colorScheme="gray"
192+
minW="fit-content"
193+
>
194+
<TagLabel cursor="default">{role}</TagLabel>
195+
<TagRightIcon
196+
boxSize="12px"
197+
as={FaTimes}
198+
display="none"
199+
cursor="pointer"
200+
_groupHover={{ display: 'block' }}
201+
onClick={() =>
202+
updateInputHandler(
203+
inputType,
204+
ArrayInputOperations.REMOVE,
205+
role
206+
)
207+
}
208+
/>
209+
</Tag>
210+
</Box>
211+
))}
212+
{inputFieldVisibility[inputType] ? (
213+
<Box ml="1.15%">
214+
<Input
215+
type="text"
216+
size="xs"
217+
placeholder="add a new value"
218+
value={inputData[inputType]}
219+
onChange={(e: any) => {
220+
setInputData({ ...inputData, [inputType]: e.target.value });
221+
}}
222+
onBlur={() =>
223+
updateInputHandler(inputType, ArrayInputOperations.APPEND)
224+
}
225+
onKeyPress={(event) => {
226+
if (event.key === 'Enter') {
227+
updateInputHandler(inputType, ArrayInputOperations.APPEND);
228+
}
229+
}}
230+
/>
231+
</Box>
232+
) : (
233+
<Box
234+
margin="1"
235+
cursor="pointer"
236+
onClick={() =>
237+
setInputFieldVisibility({
238+
...inputFieldVisibility,
239+
[inputType]: true,
240+
})
241+
}
242+
>
243+
<Tag
244+
size="sm"
245+
variant="outline"
246+
colorScheme="gray"
247+
minW="fit-content"
248+
>
249+
<FaPlus />
250+
</Tag>
251+
</Box>
252+
)}
253+
</Flex>
254+
);
255+
}
256+
if (Object.values(SelectInputType).includes(inputType)) {
257+
return (
258+
<Select size="sm" {...props}>
259+
{[envVariables[inputType]].map((value: string) => (
260+
<option value="value" key={value}>
261+
{value}
262+
</option>
263+
))}
264+
</Select>
265+
);
266+
}
267+
if (Object.values(TextAreaInputType).includes(inputType)) {
268+
return (
269+
<Textarea
270+
{...props}
271+
size="lg"
272+
value={inputData[inputType]}
273+
onChange={(e: any) => {
274+
setInputData({ ...inputData, [inputType]: e.target.value });
275+
}}
276+
/>
277+
);
278+
}
279+
if (Object.values(SwitchInputType).includes(inputType)) {
280+
return (
281+
<Flex w="25%" justifyContent="space-between">
282+
<Code h="75%">Off</Code>
283+
<Switch
284+
size="md"
285+
isChecked={!envVariables[inputType]}
286+
onChange={() => {
287+
setEnvVariables({
288+
...envVariables,
289+
[inputType]: !envVariables[inputType],
290+
});
291+
}}
292+
/>
293+
<Code h="75%">On</Code>
294+
</Flex>
295+
);
296+
}
297+
return null;
298+
};
299+
300+
export default InputField;

dashboard/src/components/Menu.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ export const MobileNav = ({ onOpen, ...rest }: MobileProps) => {
161161
borderBottomWidth="1px"
162162
borderBottomColor={useColorModeValue('gray.200', 'gray.700')}
163163
justifyContent={{ base: 'space-between', md: 'flex-end' }}
164+
zIndex={99}
164165
{...rest}
165166
>
166167
<IconButton

dashboard/src/constants.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,56 @@
1-
export const LOGO_URL = "https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png"
1+
export const LOGO_URL =
2+
'https://user-images.githubusercontent.com/6964334/147834043-fc384cab-e7ca-40f8-9663-38fc25fd5f3a.png';
3+
4+
export const TextInputType = {
5+
GOOGLE_CLIENT_ID: 'GOOGLE_CLIENT_ID',
6+
GITHUB_CLIENT_ID: 'GITHUB_CLIENT_ID',
7+
FACEBOOK_CLIENT_ID: 'FACEBOOK_CLIENT_ID',
8+
JWT_ROLE_CLAIM: 'JWT_ROLE_CLAIM',
9+
REDIS_URL: 'REDIS_URL',
10+
SMTP_HOST: 'SMTP_HOST',
11+
SMTP_PORT: 'SMTP_PORT',
12+
SMTP_USERNAME: 'SMTP_USERNAME',
13+
SENDER_EMAIL: 'SENDER_EMAIL',
14+
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
15+
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
16+
DATABASE_NAME: 'DATABASE_NAME',
17+
DATABASE_TYPE: 'DATABASE_TYPE',
18+
DATABASE_URL: 'DATABASE_URL',
19+
};
20+
21+
export const HiddenInputType = {
22+
GOOGLE_CLIENT_SECRET: 'GOOGLE_CLIENT_SECRET',
23+
GITHUB_CLIENT_SECRET: 'GITHUB_CLIENT_SECRET',
24+
FACEBOOK_CLIENT_SECRET: 'FACEBOOK_CLIENT_SECRET',
25+
JWT_SECRET: 'JWT_SECRET',
26+
SMTP_PASSWORD: 'SMTP_PASSWORD',
27+
ADMIN_SECRET: 'ADMIN_SECRET',
28+
OLD_ADMIN_SECRET: 'OLD_ADMIN_SECRET',
29+
};
30+
31+
export const ArrayInputType = {
32+
ROLES: 'ROLES',
33+
DEFAULT_ROLES: 'DEFAULT_ROLES',
34+
PROTECTED_ROLES: 'PROTECTED_ROLES',
35+
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
36+
};
37+
38+
export const SelectInputType = {
39+
JWT_TYPE: 'JWT_TYPE',
40+
};
41+
42+
export const TextAreaInputType = {
43+
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
44+
};
45+
46+
export const SwitchInputType = {
47+
DISABLE_LOGIN_PAGE: 'DISABLE_LOGIN_PAGE',
48+
DISABLE_MAGIC_LINK_LOGIN: 'DISABLE_MAGIC_LINK_LOGIN',
49+
DISABLE_EMAIL_VERIFICATION: 'DISABLE_EMAIL_VERIFICATION',
50+
DISABLE_BASIC_AUTHENTICATION: 'DISABLE_BASIC_AUTHENTICATION',
51+
};
52+
53+
export const ArrayInputOperations = {
54+
APPEND: 'APPEND',
55+
REMOVE: 'REMOVE',
56+
};

dashboard/src/contexts/AuthContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const AuthContextProvider = ({ children }: { children: any }) => {
3232

3333
if (fetching) {
3434
return (
35-
<Center>
35+
<Center h="100%">
3636
<Spinner />
3737
</Center>
3838
);

dashboard/src/graphql/mutation/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,13 @@ export const AdminLogout = `
2121
}
2222
}
2323
`;
24+
25+
export const UpdateEnvVariables = `
26+
mutation updateEnvVariables(
27+
$params: UpdateEnvInput!
28+
) {
29+
_update_env(params: $params) {
30+
message
31+
}
32+
}
33+
`;

0 commit comments

Comments
 (0)