Compare commits
20 Commits
1.1.21
...
feat/plain
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ecefe12355 | ||
![]() |
5c8f9406f6 | ||
![]() |
ae84213e34 | ||
![]() |
87bf1c3045 | ||
![]() |
b467e7002d | ||
![]() |
4afd544c41 | ||
![]() |
307c6f7d15 | ||
![]() |
bbc6394cf3 | ||
![]() |
63c8e2e55f | ||
![]() |
b224892a39 | ||
![]() |
13edf1965c | ||
![]() |
1f220a5205 | ||
![]() |
32fb954a1c | ||
![]() |
65eadb66fa | ||
![]() |
9ce53eb8e8 | ||
![]() |
3b196f074b | ||
![]() |
f2fe584793 | ||
![]() |
287b952dad | ||
![]() |
e690066652 | ||
![]() |
0f67d74657 |
@@ -60,7 +60,12 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
|
|||||||
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
{view === VIEW_TYPES.FORGOT_PASSWORD && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
<h1 style={{ textAlign: 'center' }}>Forgot Password</h1>
|
||||||
<AuthorizerForgotPassword urlProps={urlProps} />
|
<AuthorizerForgotPassword
|
||||||
|
urlProps={{
|
||||||
|
...urlProps,
|
||||||
|
redirect_uri: `${window.location.origin}/app/reset-password`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Footer>
|
<Footer>
|
||||||
<Link
|
<Link
|
||||||
to="#"
|
to="#"
|
||||||
|
1079
dashboard/package-lock.json
generated
1079
dashboard/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Center,
|
Center,
|
||||||
@@ -20,13 +20,14 @@ import { useClient } from 'urql';
|
|||||||
import { FaSave } from 'react-icons/fa';
|
import { FaSave } from 'react-icons/fa';
|
||||||
import InputField from './InputField';
|
import InputField from './InputField';
|
||||||
import {
|
import {
|
||||||
ArrayInputType,
|
|
||||||
DateInputType,
|
DateInputType,
|
||||||
|
MultiSelectInputType,
|
||||||
SelectInputType,
|
SelectInputType,
|
||||||
TextInputType,
|
TextInputType,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { getObjectDiff } from '../utils';
|
import { getObjectDiff } from '../utils';
|
||||||
import { UpdateUser } from '../graphql/mutation';
|
import { UpdateUser } from '../graphql/mutation';
|
||||||
|
import { GetAvailableRolesQuery } from '../graphql/queries';
|
||||||
|
|
||||||
const GenderTypes = {
|
const GenderTypes = {
|
||||||
Undisclosed: null,
|
Undisclosed: null,
|
||||||
@@ -57,8 +58,9 @@ const EditUserModal = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const client = useClient();
|
const client = useClient();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
|
const [availableRoles, setAvailableRoles] = useState<string[]>([]);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [userData, setUserData] = React.useState<userDataTypes>({
|
const [userData, setUserData] = useState<userDataTypes>({
|
||||||
id: '',
|
id: '',
|
||||||
email: '',
|
email: '',
|
||||||
given_name: '',
|
given_name: '',
|
||||||
@@ -73,7 +75,17 @@ const EditUserModal = ({
|
|||||||
});
|
});
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setUserData(user);
|
setUserData(user);
|
||||||
|
fetchAvailableRoles();
|
||||||
}, []);
|
}, []);
|
||||||
|
const fetchAvailableRoles = async () => {
|
||||||
|
const res = await client.query(GetAvailableRolesQuery).toPromise();
|
||||||
|
if (res.data?._env?.ROLES && res.data?._env?.PROTECTED_ROLES) {
|
||||||
|
setAvailableRoles([
|
||||||
|
...res.data._env.ROLES,
|
||||||
|
...res.data._env.PROTECTED_ROLES,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
const saveHandler = async () => {
|
const saveHandler = async () => {
|
||||||
const diff = getObjectDiff(user, userData);
|
const diff = getObjectDiff(user, userData);
|
||||||
const updatedUserData = diff.reduce(
|
const updatedUserData = diff.reduce(
|
||||||
@@ -221,7 +233,8 @@ const EditUserModal = ({
|
|||||||
<InputField
|
<InputField
|
||||||
variables={userData}
|
variables={userData}
|
||||||
setVariables={setUserData}
|
setVariables={setUserData}
|
||||||
inputType={ArrayInputType.USER_ROLES}
|
availableRoles={availableRoles}
|
||||||
|
inputType={MultiSelectInputType.USER_ROLES}
|
||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
@@ -48,6 +48,26 @@ const EmailConfigurations = ({
|
|||||||
/>
|
/>
|
||||||
</Center>
|
</Center>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
|
<Flex
|
||||||
|
w={isNotSmallerScreen ? '30%' : '40%'}
|
||||||
|
justifyContent="start"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Text fontSize="sm">SMTP Local Name:</Text>
|
||||||
|
</Flex>
|
||||||
|
<Center
|
||||||
|
w={isNotSmallerScreen ? '70%' : '100%'}
|
||||||
|
mt={isNotSmallerScreen ? '0' : '3'}
|
||||||
|
>
|
||||||
|
<InputField
|
||||||
|
borderRadius={5}
|
||||||
|
variables={variables}
|
||||||
|
setVariables={setVariables}
|
||||||
|
inputType={TextInputType.SMTP_LOCAL_NAME}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
</Flex>
|
||||||
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
<Flex direction={isNotSmallerScreen ? 'row' : 'column'}>
|
||||||
<Flex
|
<Flex
|
||||||
w={isNotSmallerScreen ? '30%' : '40%'}
|
w={isNotSmallerScreen ? '30%' : '40%'}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@@ -13,6 +13,12 @@ import {
|
|||||||
Textarea,
|
Textarea,
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
|
MenuButton,
|
||||||
|
MenuList,
|
||||||
|
MenuItemOption,
|
||||||
|
MenuOptionGroup,
|
||||||
|
Button,
|
||||||
|
Menu,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
FaRegClone,
|
FaRegClone,
|
||||||
@@ -20,6 +26,7 @@ import {
|
|||||||
FaRegEyeSlash,
|
FaRegEyeSlash,
|
||||||
FaPlus,
|
FaPlus,
|
||||||
FaTimes,
|
FaTimes,
|
||||||
|
FaAngleDown,
|
||||||
} from 'react-icons/fa';
|
} from 'react-icons/fa';
|
||||||
import {
|
import {
|
||||||
ArrayInputOperations,
|
ArrayInputOperations,
|
||||||
@@ -30,6 +37,7 @@ import {
|
|||||||
TextAreaInputType,
|
TextAreaInputType,
|
||||||
SwitchInputType,
|
SwitchInputType,
|
||||||
DateInputType,
|
DateInputType,
|
||||||
|
MultiSelectInputType,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { copyTextToClipboard } from '../utils';
|
import { copyTextToClipboard } from '../utils';
|
||||||
|
|
||||||
@@ -39,13 +47,16 @@ const InputField = ({
|
|||||||
setVariables,
|
setVariables,
|
||||||
fieldVisibility,
|
fieldVisibility,
|
||||||
setFieldVisibility,
|
setFieldVisibility,
|
||||||
|
availableRoles,
|
||||||
...downshiftProps
|
...downshiftProps
|
||||||
}: any) => {
|
}: any) => {
|
||||||
const props = {
|
const props = {
|
||||||
size: 'sm',
|
size: 'sm',
|
||||||
...downshiftProps,
|
...downshiftProps,
|
||||||
};
|
};
|
||||||
const [inputFieldVisibility, setInputFieldVisibility] = React.useState<
|
const [availableUserRoles, setAvailableUserRoles] =
|
||||||
|
useState<string[]>(availableRoles);
|
||||||
|
const [inputFieldVisibility, setInputFieldVisibility] = useState<
|
||||||
Record<string, boolean>
|
Record<string, boolean>
|
||||||
>({
|
>({
|
||||||
ROLES: false,
|
ROLES: false,
|
||||||
@@ -54,7 +65,7 @@ const InputField = ({
|
|||||||
ALLOWED_ORIGINS: false,
|
ALLOWED_ORIGINS: false,
|
||||||
roles: false,
|
roles: false,
|
||||||
});
|
});
|
||||||
const [inputData, setInputData] = React.useState<Record<string, string>>({
|
const [inputData, setInputData] = useState<Record<string, string>>({
|
||||||
ROLES: '',
|
ROLES: '',
|
||||||
DEFAULT_ROLES: '',
|
DEFAULT_ROLES: '',
|
||||||
PROTECTED_ROLES: '',
|
PROTECTED_ROLES: '',
|
||||||
@@ -116,7 +127,7 @@ const InputField = ({
|
|||||||
<InputGroup size="sm">
|
<InputGroup size="sm">
|
||||||
<Input
|
<Input
|
||||||
{...props}
|
{...props}
|
||||||
value={variables[inputType] ?? ''}
|
value={variables[inputType] || ''}
|
||||||
onChange={(
|
onChange={(
|
||||||
event: Event & {
|
event: Event & {
|
||||||
target: HTMLInputElement;
|
target: HTMLInputElement;
|
||||||
@@ -221,7 +232,7 @@ const InputField = ({
|
|||||||
size="xs"
|
size="xs"
|
||||||
minW="150px"
|
minW="150px"
|
||||||
placeholder="add a new value"
|
placeholder="add a new value"
|
||||||
value={inputData[inputType] ?? ''}
|
value={inputData[inputType] || ''}
|
||||||
onChange={(e: any) => {
|
onChange={(e: any) => {
|
||||||
setInputData({ ...inputData, [inputType]: e.target.value });
|
setInputData({ ...inputData, [inputType]: e.target.value });
|
||||||
}}
|
}}
|
||||||
@@ -278,6 +289,87 @@ const InputField = ({
|
|||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (Object.values(MultiSelectInputType).includes(inputType)) {
|
||||||
|
return (
|
||||||
|
<Flex w="100%" style={{ position: 'relative' }}>
|
||||||
|
<Flex
|
||||||
|
border="1px solid #e2e8f0"
|
||||||
|
w="100%"
|
||||||
|
borderRadius="var(--chakra-radii-sm)"
|
||||||
|
p="1% 0 0 2.5%"
|
||||||
|
overflowX={variables[inputType].length > 3 ? 'scroll' : 'hidden'}
|
||||||
|
overflowY="hidden"
|
||||||
|
justifyContent="space-between"
|
||||||
|
alignItems="center"
|
||||||
|
>
|
||||||
|
<Flex justifyContent="start" alignItems="center" w="100%" wrap="wrap">
|
||||||
|
{variables[inputType].map((role: string, index: number) => (
|
||||||
|
<Box key={index} margin="0.5%" role="group">
|
||||||
|
<Tag
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
colorScheme="gray"
|
||||||
|
minW="fit-content"
|
||||||
|
>
|
||||||
|
<TagLabel cursor="default">{role}</TagLabel>
|
||||||
|
<TagRightIcon
|
||||||
|
boxSize="12px"
|
||||||
|
as={FaTimes}
|
||||||
|
display="none"
|
||||||
|
cursor="pointer"
|
||||||
|
_groupHover={{ display: 'block' }}
|
||||||
|
onClick={() =>
|
||||||
|
updateInputHandler(
|
||||||
|
inputType,
|
||||||
|
ArrayInputOperations.REMOVE,
|
||||||
|
role,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tag>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Flex>
|
||||||
|
<Menu matchWidth={true}>
|
||||||
|
<MenuButton px="10px" py="7.5px">
|
||||||
|
<FaAngleDown />
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList
|
||||||
|
position="absolute"
|
||||||
|
top="0"
|
||||||
|
right="0"
|
||||||
|
zIndex="10"
|
||||||
|
maxH="150"
|
||||||
|
overflowX="scroll"
|
||||||
|
>
|
||||||
|
<MenuOptionGroup
|
||||||
|
title={undefined}
|
||||||
|
value={variables[inputType]}
|
||||||
|
type="checkbox"
|
||||||
|
onChange={(values: string[] | string) => {
|
||||||
|
setVariables({
|
||||||
|
...variables,
|
||||||
|
[inputType]: values,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{availableUserRoles.map((role) => {
|
||||||
|
return (
|
||||||
|
<MenuItemOption
|
||||||
|
key={`multiselect-menu-${role}`}
|
||||||
|
value={role}
|
||||||
|
>
|
||||||
|
{role}
|
||||||
|
</MenuItemOption>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MenuOptionGroup>
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (Object.values(TextAreaInputType).includes(inputType)) {
|
if (Object.values(TextAreaInputType).includes(inputType)) {
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
|
@@ -29,6 +29,10 @@ import {
|
|||||||
Tbody,
|
Tbody,
|
||||||
Td,
|
Td,
|
||||||
Code,
|
Code,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
Stack,
|
||||||
|
Textarea,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
import { FaPlus, FaAngleDown, FaAngleUp } from 'react-icons/fa';
|
||||||
import { useClient } from 'urql';
|
import { useClient } from 'urql';
|
||||||
@@ -38,6 +42,7 @@ import {
|
|||||||
EmailTemplateInputDataFields,
|
EmailTemplateInputDataFields,
|
||||||
emailTemplateEventNames,
|
emailTemplateEventNames,
|
||||||
emailTemplateVariables,
|
emailTemplateVariables,
|
||||||
|
EmailTemplateEditors,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
import { capitalizeFirstLetter } from '../utils';
|
import { capitalizeFirstLetter } from '../utils';
|
||||||
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation';
|
||||||
@@ -66,6 +71,8 @@ interface templateVariableDataTypes {
|
|||||||
interface emailTemplateDataType {
|
interface emailTemplateDataType {
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
[EmailTemplateInputDataFields.EVENT_NAME]: string;
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: string;
|
[EmailTemplateInputDataFields.SUBJECT]: string;
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: string;
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface validatorDataType {
|
interface validatorDataType {
|
||||||
@@ -75,6 +82,8 @@ interface validatorDataType {
|
|||||||
const initTemplateData: emailTemplateDataType = {
|
const initTemplateData: emailTemplateDataType = {
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
[EmailTemplateInputDataFields.EVENT_NAME]: emailTemplateEventNames.Signup,
|
||||||
[EmailTemplateInputDataFields.SUBJECT]: '',
|
[EmailTemplateInputDataFields.SUBJECT]: '',
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
const initTemplateValidatorData: validatorDataType = {
|
const initTemplateValidatorData: validatorDataType = {
|
||||||
@@ -91,6 +100,9 @@ const UpdateEmailTemplate = ({
|
|||||||
const emailEditorRef = useRef(null);
|
const emailEditorRef = useRef(null);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [editor, setEditor] = useState<string>(
|
||||||
|
EmailTemplateEditors.PLAIN_HTML_EDITOR,
|
||||||
|
);
|
||||||
const [templateVariables, setTemplateVariables] = useState<
|
const [templateVariables, setTemplateVariables] = useState<
|
||||||
templateVariableDataTypes[]
|
templateVariableDataTypes[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -107,9 +119,11 @@ const UpdateEmailTemplate = ({
|
|||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
const { design } = selectedTemplate;
|
const { design } = selectedTemplate;
|
||||||
try {
|
try {
|
||||||
const designData = JSON.parse(design);
|
if (design) {
|
||||||
// @ts-ignore
|
const designData = JSON.parse(design);
|
||||||
emailEditorRef.current.editor.loadDesign(designData);
|
// @ts-ignore
|
||||||
|
emailEditorRef.current.editor.loadDesign(designData);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
onClose();
|
onClose();
|
||||||
@@ -136,70 +150,85 @@ const UpdateEmailTemplate = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateTemplate = async (params: emailTemplateDataType) => {
|
||||||
|
let res: any = {};
|
||||||
|
if (
|
||||||
|
view === UpdateModalViews.Edit &&
|
||||||
|
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
||||||
|
) {
|
||||||
|
res = await client
|
||||||
|
.mutation(EditEmailTemplate, {
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.toPromise();
|
||||||
|
} else {
|
||||||
|
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
if (res.error) {
|
||||||
|
toast({
|
||||||
|
title: capitalizeFirstLetter(res.error.message),
|
||||||
|
isClosable: true,
|
||||||
|
status: 'error',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
res.data?._add_email_template ||
|
||||||
|
res.data?._update_email_template
|
||||||
|
) {
|
||||||
|
toast({
|
||||||
|
title: capitalizeFirstLetter(
|
||||||
|
res.data?._add_email_template?.message ||
|
||||||
|
res.data?._update_email_template?.message,
|
||||||
|
),
|
||||||
|
isClosable: true,
|
||||||
|
status: 'success',
|
||||||
|
position: 'bottom-right',
|
||||||
|
});
|
||||||
|
setTemplateData({
|
||||||
|
...initTemplateData,
|
||||||
|
});
|
||||||
|
setValidator({ ...initTemplateValidatorData });
|
||||||
|
fetchEmailTemplatesData();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
if (!validateData()) return;
|
if (!validateData()) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
// @ts-ignore
|
let params: emailTemplateDataType = {
|
||||||
return await emailEditorRef.current.editor.exportHtml(async (data) => {
|
[EmailTemplateInputDataFields.EVENT_NAME]:
|
||||||
const { design, html } = data;
|
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
||||||
if (!html || !design) {
|
[EmailTemplateInputDataFields.SUBJECT]:
|
||||||
setLoading(false);
|
templateData[EmailTemplateInputDataFields.SUBJECT],
|
||||||
return;
|
[EmailTemplateInputDataFields.TEMPLATE]:
|
||||||
}
|
templateData[EmailTemplateInputDataFields.TEMPLATE],
|
||||||
const params = {
|
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||||
[EmailTemplateInputDataFields.EVENT_NAME]:
|
};
|
||||||
templateData[EmailTemplateInputDataFields.EVENT_NAME],
|
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||||
[EmailTemplateInputDataFields.SUBJECT]:
|
// @ts-ignore
|
||||||
templateData[EmailTemplateInputDataFields.SUBJECT],
|
await emailEditorRef.current.editor.exportHtml(async (data) => {
|
||||||
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
const { design, html } = data;
|
||||||
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
if (!html || !design) {
|
||||||
};
|
setLoading(false);
|
||||||
let res: any = {};
|
return;
|
||||||
if (
|
}
|
||||||
view === UpdateModalViews.Edit &&
|
params = {
|
||||||
selectedTemplate?.[EmailTemplateInputDataFields.ID]
|
...params,
|
||||||
) {
|
[EmailTemplateInputDataFields.TEMPLATE]: html.trim(),
|
||||||
res = await client
|
[EmailTemplateInputDataFields.DESIGN]: JSON.stringify(design),
|
||||||
.mutation(EditEmailTemplate, {
|
};
|
||||||
params: {
|
await updateTemplate(params);
|
||||||
...params,
|
});
|
||||||
id: selectedTemplate[EmailTemplateInputDataFields.ID],
|
} else {
|
||||||
},
|
await updateTemplate(params);
|
||||||
})
|
}
|
||||||
.toPromise();
|
view === UpdateModalViews.ADD && onClose();
|
||||||
} else {
|
|
||||||
res = await client.mutation(AddEmailTemplate, { params }).toPromise();
|
|
||||||
}
|
|
||||||
setLoading(false);
|
|
||||||
if (res.error) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(res.error.message),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'error',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
res.data?._add_email_template ||
|
|
||||||
res.data?._update_email_template
|
|
||||||
) {
|
|
||||||
toast({
|
|
||||||
title: capitalizeFirstLetter(
|
|
||||||
res.data?._add_email_template?.message ||
|
|
||||||
res.data?._update_email_template?.message,
|
|
||||||
),
|
|
||||||
isClosable: true,
|
|
||||||
status: 'success',
|
|
||||||
position: 'bottom-right',
|
|
||||||
});
|
|
||||||
setTemplateData({
|
|
||||||
...initTemplateData,
|
|
||||||
});
|
|
||||||
setValidator({ ...initTemplateValidatorData });
|
|
||||||
fetchEmailTemplatesData();
|
|
||||||
}
|
|
||||||
view === UpdateModalViews.ADD && onClose();
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetData = () => {
|
const resetData = () => {
|
||||||
if (selectedTemplate) {
|
if (selectedTemplate) {
|
||||||
setTemplateData(selectedTemplate);
|
setTemplateData(selectedTemplate);
|
||||||
@@ -207,6 +236,8 @@ const UpdateEmailTemplate = ({
|
|||||||
setTemplateData({ ...initTemplateData });
|
setTemplateData({ ...initTemplateData });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// set template data if edit modal is open
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
isOpen &&
|
isOpen &&
|
||||||
@@ -214,10 +245,12 @@ const UpdateEmailTemplate = ({
|
|||||||
selectedTemplate &&
|
selectedTemplate &&
|
||||||
Object.keys(selectedTemplate || {}).length
|
Object.keys(selectedTemplate || {}).length
|
||||||
) {
|
) {
|
||||||
const { id, created_at, template, design, ...rest } = selectedTemplate;
|
const { id, created_at, ...rest } = selectedTemplate;
|
||||||
setTemplateData(rest);
|
setTemplateData(rest);
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
|
// set template variables
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const updatedTemplateVariables = Object.entries(
|
const updatedTemplateVariables = Object.entries(
|
||||||
emailTemplateVariables,
|
emailTemplateVariables,
|
||||||
@@ -244,6 +277,51 @@ const UpdateEmailTemplate = ({
|
|||||||
setTemplateVariables(updatedTemplateVariables);
|
setTemplateVariables(updatedTemplateVariables);
|
||||||
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
}, [templateData[EmailTemplateInputDataFields.EVENT_NAME]]);
|
||||||
|
|
||||||
|
// change editor
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen && selectedTemplate) {
|
||||||
|
const { design } = selectedTemplate;
|
||||||
|
if (design) {
|
||||||
|
setEditor(EmailTemplateEditors.UNLAYER_EDITOR);
|
||||||
|
} else {
|
||||||
|
setEditor(EmailTemplateEditors.PLAIN_HTML_EDITOR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isOpen, selectedTemplate]);
|
||||||
|
|
||||||
|
// reset fields when editor is changed
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedTemplate?.design) {
|
||||||
|
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||||
|
setTemplateData({
|
||||||
|
...templateData,
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate.template,
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: selectedTemplate.design,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTemplateData({
|
||||||
|
...templateData,
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (selectedTemplate?.template) {
|
||||||
|
if (editor === EmailTemplateEditors.UNLAYER_EDITOR) {
|
||||||
|
setTemplateData({
|
||||||
|
...templateData,
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: '',
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTemplateData({
|
||||||
|
...templateData,
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: selectedTemplate?.template,
|
||||||
|
[EmailTemplateInputDataFields.DESIGN]: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{view === UpdateModalViews.ADD ? (
|
{view === UpdateModalViews.ADD ? (
|
||||||
@@ -414,7 +492,22 @@ const UpdateEmailTemplate = ({
|
|||||||
alignItems="center"
|
alignItems="center"
|
||||||
marginBottom="2%"
|
marginBottom="2%"
|
||||||
>
|
>
|
||||||
Template Body
|
<Flex flex="1">Template Body</Flex>
|
||||||
|
<Flex flex="3">
|
||||||
|
<RadioGroup
|
||||||
|
onChange={(value) => setEditor(value)}
|
||||||
|
value={editor}
|
||||||
|
>
|
||||||
|
<Stack direction="row" spacing="50px">
|
||||||
|
<Radio value={EmailTemplateEditors.PLAIN_HTML_EDITOR}>
|
||||||
|
Plain HTML
|
||||||
|
</Radio>
|
||||||
|
<Radio value={EmailTemplateEditors.UNLAYER_EDITOR}>
|
||||||
|
Unlayer Editor
|
||||||
|
</Radio>
|
||||||
|
</Stack>
|
||||||
|
</RadioGroup>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex
|
<Flex
|
||||||
width="100%"
|
width="100%"
|
||||||
@@ -423,7 +516,22 @@ const UpdateEmailTemplate = ({
|
|||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="gray.200"
|
borderColor="gray.200"
|
||||||
>
|
>
|
||||||
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
{editor === EmailTemplateEditors.UNLAYER_EDITOR ? (
|
||||||
|
<EmailEditor ref={emailEditorRef} onReady={onReady} />
|
||||||
|
) : (
|
||||||
|
<Textarea
|
||||||
|
value={templateData.template}
|
||||||
|
onChange={(e) => {
|
||||||
|
setTemplateData({
|
||||||
|
...templateData,
|
||||||
|
[EmailTemplateInputDataFields.TEMPLATE]: e.target.value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
placeholder="Template HTML"
|
||||||
|
border="0"
|
||||||
|
height="500px"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
@@ -15,6 +15,7 @@ export const TextInputType = {
|
|||||||
SMTP_HOST: 'SMTP_HOST',
|
SMTP_HOST: 'SMTP_HOST',
|
||||||
SMTP_PORT: 'SMTP_PORT',
|
SMTP_PORT: 'SMTP_PORT',
|
||||||
SMTP_USERNAME: 'SMTP_USERNAME',
|
SMTP_USERNAME: 'SMTP_USERNAME',
|
||||||
|
SMTP_LOCAL_NAME: 'SMTP_LOCAL_NAME',
|
||||||
SENDER_EMAIL: 'SENDER_EMAIL',
|
SENDER_EMAIL: 'SENDER_EMAIL',
|
||||||
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
ORGANIZATION_NAME: 'ORGANIZATION_NAME',
|
||||||
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
ORGANIZATION_LOGO: 'ORGANIZATION_LOGO',
|
||||||
@@ -48,7 +49,6 @@ export const ArrayInputType = {
|
|||||||
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
DEFAULT_ROLES: 'DEFAULT_ROLES',
|
||||||
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
PROTECTED_ROLES: 'PROTECTED_ROLES',
|
||||||
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
ALLOWED_ORIGINS: 'ALLOWED_ORIGINS',
|
||||||
USER_ROLES: 'roles',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectInputType = {
|
export const SelectInputType = {
|
||||||
@@ -56,6 +56,10 @@ export const SelectInputType = {
|
|||||||
GENDER: 'gender',
|
GENDER: 'gender',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MultiSelectInputType = {
|
||||||
|
USER_ROLES: 'roles',
|
||||||
|
};
|
||||||
|
|
||||||
export const TextAreaInputType = {
|
export const TextAreaInputType = {
|
||||||
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
|
CUSTOM_ACCESS_TOKEN_SCRIPT: 'CUSTOM_ACCESS_TOKEN_SCRIPT',
|
||||||
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
|
JWT_PRIVATE_KEY: 'JWT_PRIVATE_KEY',
|
||||||
@@ -129,6 +133,7 @@ export interface envVarTypes {
|
|||||||
SMTP_PORT: string;
|
SMTP_PORT: string;
|
||||||
SMTP_USERNAME: string;
|
SMTP_USERNAME: string;
|
||||||
SMTP_PASSWORD: string;
|
SMTP_PASSWORD: string;
|
||||||
|
SMTP_LOCAL_NAME: string;
|
||||||
SENDER_EMAIL: string;
|
SENDER_EMAIL: string;
|
||||||
ALLOWED_ORIGINS: [string] | [];
|
ALLOWED_ORIGINS: [string] | [];
|
||||||
ORGANIZATION_NAME: string;
|
ORGANIZATION_NAME: string;
|
||||||
@@ -332,3 +337,8 @@ export const webhookPayloadExample: string = `{
|
|||||||
},
|
},
|
||||||
"auth_recipe":"google"
|
"auth_recipe":"google"
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
export enum EmailTemplateEditors {
|
||||||
|
UNLAYER_EDITOR = 'unlayer_editor',
|
||||||
|
PLAIN_HTML_EDITOR = 'plain_html_editor',
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,7 @@ export const EnvVariablesQuery = `
|
|||||||
SMTP_PORT
|
SMTP_PORT
|
||||||
SMTP_USERNAME
|
SMTP_USERNAME
|
||||||
SMTP_PASSWORD
|
SMTP_PASSWORD
|
||||||
|
SMTP_LOCAL_NAME
|
||||||
SENDER_EMAIL
|
SENDER_EMAIL
|
||||||
ALLOWED_ORIGINS
|
ALLOWED_ORIGINS
|
||||||
ORGANIZATION_NAME
|
ORGANIZATION_NAME
|
||||||
@@ -169,3 +170,12 @@ export const WebhookLogsQuery = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GetAvailableRolesQuery = `
|
||||||
|
query {
|
||||||
|
_env {
|
||||||
|
ROLES
|
||||||
|
PROTECTED_ROLES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@@ -65,6 +65,7 @@ const Environment = () => {
|
|||||||
SMTP_PORT: '',
|
SMTP_PORT: '',
|
||||||
SMTP_USERNAME: '',
|
SMTP_USERNAME: '',
|
||||||
SMTP_PASSWORD: '',
|
SMTP_PASSWORD: '',
|
||||||
|
SMTP_LOCAL_NAME: '',
|
||||||
SENDER_EMAIL: '',
|
SENDER_EMAIL: '',
|
||||||
ALLOWED_ORIGINS: [],
|
ALLOWED_ORIGINS: [],
|
||||||
ORGANIZATION_NAME: '',
|
ORGANIZATION_NAME: '',
|
||||||
|
@@ -51,6 +51,8 @@ const (
|
|||||||
EnvKeySmtpUsername = "SMTP_USERNAME"
|
EnvKeySmtpUsername = "SMTP_USERNAME"
|
||||||
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD
|
// EnvKeySmtpPassword key for env variable SMTP_PASSWORD
|
||||||
EnvKeySmtpPassword = "SMTP_PASSWORD"
|
EnvKeySmtpPassword = "SMTP_PASSWORD"
|
||||||
|
// EnvKeySmtpLocalName key for env variable SMTP_LOCAL_NAME
|
||||||
|
EnvKeySmtpLocalName = "SMTP_LOCAL_NAME"
|
||||||
// EnvKeySenderEmail key for env variable SENDER_EMAIL
|
// EnvKeySenderEmail key for env variable SENDER_EMAIL
|
||||||
EnvKeySenderEmail = "SENDER_EMAIL"
|
EnvKeySenderEmail = "SENDER_EMAIL"
|
||||||
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
|
||||||
|
@@ -26,7 +26,6 @@ func NewProvider() (*provider, error) {
|
|||||||
config := aws.Config{
|
config := aws.Config{
|
||||||
MaxRetries: aws.Int(3),
|
MaxRetries: aws.Int(3),
|
||||||
CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs
|
CredentialsChainVerboseErrors: aws.Bool(true), // for full error logs
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if awsRegion != "" {
|
if awsRegion != "" {
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -126,6 +127,12 @@ func SendEmail(to []string, event string, data map[string]interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
smtpLocalName, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeySmtpLocalName)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Error while getting smtp localname from env variable: %v", err)
|
||||||
|
smtpLocalName = ""
|
||||||
|
}
|
||||||
|
|
||||||
isProd, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsProd)
|
isProd, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsProd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error while getting env variable: %v", err)
|
log.Errorf("Error while getting env variable: %v", err)
|
||||||
@@ -141,6 +148,11 @@ func SendEmail(to []string, event string, data map[string]interface{}) error {
|
|||||||
if !isProd {
|
if !isProd {
|
||||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(smtpLocalName) != "" {
|
||||||
|
d.LocalName = smtpLocalName
|
||||||
|
}
|
||||||
|
|
||||||
if err := d.DialAndSend(m); err != nil {
|
if err := d.DialAndSend(m); err != nil {
|
||||||
log.Debug("SMTP Failed: ", err)
|
log.Debug("SMTP Failed: ", err)
|
||||||
return err
|
return err
|
||||||
|
8
server/env/env.go
vendored
8
server/env/env.go
vendored
@@ -55,6 +55,7 @@ func InitAllEnv() error {
|
|||||||
osSmtpPort := os.Getenv(constants.EnvKeySmtpPort)
|
osSmtpPort := os.Getenv(constants.EnvKeySmtpPort)
|
||||||
osSmtpUsername := os.Getenv(constants.EnvKeySmtpUsername)
|
osSmtpUsername := os.Getenv(constants.EnvKeySmtpUsername)
|
||||||
osSmtpPassword := os.Getenv(constants.EnvKeySmtpPassword)
|
osSmtpPassword := os.Getenv(constants.EnvKeySmtpPassword)
|
||||||
|
osSmtpLocalName := os.Getenv(constants.EnvKeySmtpLocalName)
|
||||||
osSenderEmail := os.Getenv(constants.EnvKeySenderEmail)
|
osSenderEmail := os.Getenv(constants.EnvKeySenderEmail)
|
||||||
osJwtType := os.Getenv(constants.EnvKeyJwtType)
|
osJwtType := os.Getenv(constants.EnvKeyJwtType)
|
||||||
osJwtSecret := os.Getenv(constants.EnvKeyJwtSecret)
|
osJwtSecret := os.Getenv(constants.EnvKeyJwtSecret)
|
||||||
@@ -205,6 +206,13 @@ func InitAllEnv() error {
|
|||||||
envData[constants.EnvKeySmtpUsername] = osSmtpUsername
|
envData[constants.EnvKeySmtpUsername] = osSmtpUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val, ok := envData[constants.EnvKeySmtpLocalName]; !ok || val == "" {
|
||||||
|
envData[constants.EnvKeySmtpLocalName] = osSmtpLocalName
|
||||||
|
}
|
||||||
|
if osSmtpLocalName != "" && envData[constants.EnvKeySmtpLocalName] != osSmtpLocalName {
|
||||||
|
envData[constants.EnvKeySmtpLocalName] = osSmtpLocalName
|
||||||
|
}
|
||||||
|
|
||||||
if val, ok := envData[constants.EnvKeySmtpPassword]; !ok || val == "" {
|
if val, ok := envData[constants.EnvKeySmtpPassword]; !ok || val == "" {
|
||||||
envData[constants.EnvKeySmtpPassword] = osSmtpPassword
|
envData[constants.EnvKeySmtpPassword] = osSmtpPassword
|
||||||
}
|
}
|
||||||
|
@@ -123,7 +123,6 @@ github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
|||||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
|
github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE=
|
||||||
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8=
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
@@ -258,6 +257,7 @@ github.com/jinzhu/now v1.1.3 h1:PlHq1bSCSZL9K0wUhbm2pGLoTWs2GwVhsP6emvGV/ZI=
|
|||||||
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
@@ -492,12 +492,11 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
|
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas=
|
||||||
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||||
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -561,14 +560,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
|
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,10 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type AddEmailTemplateRequest struct {
|
type AddEmailTemplateRequest struct {
|
||||||
EventName string `json:"event_name"`
|
EventName string `json:"event_name"`
|
||||||
Subject string `json:"subject"`
|
Subject string `json:"subject"`
|
||||||
Template string `json:"template"`
|
Template string `json:"template"`
|
||||||
Design string `json:"design"`
|
Design *string `json:"design"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddWebhookRequest struct {
|
type AddWebhookRequest struct {
|
||||||
@@ -74,6 +74,7 @@ type Env struct {
|
|||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT"`
|
||||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||||
|
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
|
||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET"`
|
||||||
@@ -271,6 +272,7 @@ type UpdateEnvInput struct {
|
|||||||
SMTPPort *string `json:"SMTP_PORT"`
|
SMTPPort *string `json:"SMTP_PORT"`
|
||||||
SMTPUsername *string `json:"SMTP_USERNAME"`
|
SMTPUsername *string `json:"SMTP_USERNAME"`
|
||||||
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
SMTPPassword *string `json:"SMTP_PASSWORD"`
|
||||||
|
SMTPLocalName *string `json:"SMTP_LOCAL_NAME"`
|
||||||
SenderEmail *string `json:"SENDER_EMAIL"`
|
SenderEmail *string `json:"SENDER_EMAIL"`
|
||||||
JwtType *string `json:"JWT_TYPE"`
|
JwtType *string `json:"JWT_TYPE"`
|
||||||
JwtSecret *string `json:"JWT_SECRET"`
|
JwtSecret *string `json:"JWT_SECRET"`
|
||||||
@@ -384,7 +386,8 @@ type ValidateJWTTokenInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ValidateJWTTokenResponse struct {
|
type ValidateJWTTokenResponse struct {
|
||||||
IsValid bool `json:"is_valid"`
|
IsValid bool `json:"is_valid"`
|
||||||
|
Claims map[string]interface{} `json:"claims"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
|
@@ -110,6 +110,7 @@ type Env {
|
|||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
SMTP_USERNAME: String
|
SMTP_USERNAME: String
|
||||||
SMTP_PASSWORD: String
|
SMTP_PASSWORD: String
|
||||||
|
SMTP_LOCAL_NAME: String
|
||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
@@ -152,6 +153,7 @@ type Env {
|
|||||||
|
|
||||||
type ValidateJWTTokenResponse {
|
type ValidateJWTTokenResponse {
|
||||||
is_valid: Boolean!
|
is_valid: Boolean!
|
||||||
|
claims: Map
|
||||||
}
|
}
|
||||||
|
|
||||||
type GenerateJWTKeysResponse {
|
type GenerateJWTKeysResponse {
|
||||||
@@ -219,6 +221,7 @@ input UpdateEnvInput {
|
|||||||
SMTP_PORT: String
|
SMTP_PORT: String
|
||||||
SMTP_USERNAME: String
|
SMTP_USERNAME: String
|
||||||
SMTP_PASSWORD: String
|
SMTP_PASSWORD: String
|
||||||
|
SMTP_LOCAL_NAME: String
|
||||||
SENDER_EMAIL: String
|
SENDER_EMAIL: String
|
||||||
JWT_TYPE: String
|
JWT_TYPE: String
|
||||||
JWT_SECRET: String
|
JWT_SECRET: String
|
||||||
@@ -427,7 +430,7 @@ input AddEmailTemplateRequest {
|
|||||||
event_name: String!
|
event_name: String!
|
||||||
subject: String!
|
subject: String!
|
||||||
template: String!
|
template: String!
|
||||||
design: String!
|
design: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateEmailTemplateRequest {
|
input UpdateEmailTemplateRequest {
|
||||||
@@ -498,4 +501,4 @@ type Query {
|
|||||||
_webhooks(params: PaginatedInput): Webhooks!
|
_webhooks(params: PaginatedInput): Webhooks!
|
||||||
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
_webhook_logs(params: ListWebhookLogRequest): WebhookLogs!
|
||||||
_email_templates(params: PaginatedInput): EmailTemplates!
|
_email_templates(params: PaginatedInput): EmailTemplates!
|
||||||
}
|
}
|
@@ -154,6 +154,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
"error": "invalid_refresh_token",
|
"error": "invalid_refresh_token",
|
||||||
"error_description": "The refresh token is invalid",
|
"error_description": "The refresh token is invalid",
|
||||||
})
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||||
@@ -163,9 +164,10 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
"error": "unauthorized",
|
"error": "unauthorized",
|
||||||
"error_description": err.Error(),
|
"error_description": err.Error(),
|
||||||
})
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
userID = claims["sub"].(string)
|
userID = claims["sub"].(string)
|
||||||
loginMethod := claims["login_method"]
|
claimLoginMethod := claims["login_method"]
|
||||||
rolesInterface := claims["roles"].([]interface{})
|
rolesInterface := claims["roles"].([]interface{})
|
||||||
scopeInterface := claims["scope"].([]interface{})
|
scopeInterface := claims["scope"].([]interface{})
|
||||||
for _, v := range rolesInterface {
|
for _, v := range rolesInterface {
|
||||||
@@ -176,9 +178,11 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sessionKey = userID
|
sessionKey = userID
|
||||||
if loginMethod != nil && loginMethod != "" {
|
if claimLoginMethod != nil && claimLoginMethod != "" {
|
||||||
sessionKey = loginMethod.(string) + ":" + sessionKey
|
sessionKey = claimLoginMethod.(string) + ":" + sessionKey
|
||||||
|
loginMethod = claimLoginMethod.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove older refresh token and rotate it for security
|
// remove older refresh token and rotate it for security
|
||||||
go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
|
go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
|
||||||
}
|
}
|
||||||
@@ -211,6 +215,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/authorizerdev/authorizer/server/validators"
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
@@ -40,15 +41,17 @@ func AddEmailTemplateResolver(ctx context.Context, params model.AddEmailTemplate
|
|||||||
return nil, fmt.Errorf("empty template not allowed")
|
return nil, fmt.Errorf("empty template not allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.TrimSpace(params.Design) == "" {
|
var design string
|
||||||
return nil, fmt.Errorf("empty design not allowed")
|
|
||||||
|
if params.Design == nil || strings.TrimSpace(refs.StringValue(params.Design)) == "" {
|
||||||
|
design = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
_, err = db.Provider.AddEmailTemplate(ctx, models.EmailTemplate{
|
||||||
EventName: params.EventName,
|
EventName: params.EventName,
|
||||||
Template: params.Template,
|
Template: params.Template,
|
||||||
Subject: params.Subject,
|
Subject: params.Subject,
|
||||||
Design: params.Design,
|
Design: design,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add email template: ", err)
|
log.Debug("Failed to add email template: ", err)
|
||||||
|
@@ -89,6 +89,9 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
|
|||||||
if val, ok := store[constants.EnvKeySenderEmail]; ok {
|
if val, ok := store[constants.EnvKeySenderEmail]; ok {
|
||||||
res.SenderEmail = refs.NewStringRef(val.(string))
|
res.SenderEmail = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
|
if val, ok := store[constants.EnvKeySmtpLocalName]; ok {
|
||||||
|
res.SMTPLocalName = refs.NewStringRef(val.(string))
|
||||||
|
}
|
||||||
if val, ok := store[constants.EnvKeyJwtType]; ok {
|
if val, ok := store[constants.EnvKeyJwtType]; ok {
|
||||||
res.JwtType = refs.NewStringRef(val.(string))
|
res.JwtType = refs.NewStringRef(val.(string))
|
||||||
}
|
}
|
||||||
|
@@ -62,12 +62,21 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
log.Debug("Failed to generate nonce: ", err)
|
log.Debug("Failed to generate nonce: ", err)
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
redirectURL := parsers.GetAppURL(gc)
|
|
||||||
|
redirectURI := ""
|
||||||
|
// give higher preference to params redirect uri
|
||||||
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
|
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
|
||||||
redirectURL = refs.StringValue(params.RedirectURI)
|
redirectURI = refs.StringValue(params.RedirectURI)
|
||||||
|
} else {
|
||||||
|
redirectURI, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("ResetPasswordURL not found using default app url: ", err)
|
||||||
|
redirectURI = hostname + "/app/reset-password"
|
||||||
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURL)
|
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create verification token", err)
|
log.Debug("Failed to create verification token", err)
|
||||||
return res, err
|
return res, err
|
||||||
@@ -78,7 +87,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
Nonce: nonceHash,
|
Nonce: nonceHash,
|
||||||
RedirectURI: redirectURL,
|
RedirectURI: redirectURI,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to add verification request", err)
|
log.Debug("Failed to add verification request", err)
|
||||||
@@ -89,7 +98,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||||||
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
|
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
|
||||||
"user": user.ToMap(),
|
"user": user.ToMap(),
|
||||||
"organization": utils.GetOrganization(),
|
"organization": utils.GetOrganization(),
|
||||||
"verification_url": utils.GetForgotPasswordURL(verificationToken, hostname),
|
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
|
||||||
})
|
})
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
|
@@ -93,5 +93,6 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
|||||||
}
|
}
|
||||||
return &model.ValidateJWTTokenResponse{
|
return &model.ValidateJWTTokenResponse{
|
||||||
IsValid: true,
|
IsValid: true,
|
||||||
|
Claims: claims,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@@ -51,16 +51,30 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
assert.Nil(t, emailTemplate)
|
assert.Nil(t, emailTemplate)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should not add email template with empty design", func(t *testing.T) {
|
var design string
|
||||||
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
design = ""
|
||||||
EventName: s.TestInfo.TestEmailTemplateEventTypes[0],
|
|
||||||
Template: "test",
|
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
||||||
Subject: "test",
|
t.Run("should add email template with empty design for "+eventType, func(t *testing.T) {
|
||||||
Design: " ",
|
emailTemplate, err := resolvers.AddEmailTemplateResolver(ctx, model.AddEmailTemplateRequest{
|
||||||
|
EventName: eventType,
|
||||||
|
Template: "Test email",
|
||||||
|
Subject: "Test email",
|
||||||
|
Design: &design,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, emailTemplate)
|
||||||
|
assert.NotEmpty(t, emailTemplate.Message)
|
||||||
|
|
||||||
|
et, err := db.Provider.GetEmailTemplateByEventName(ctx, eventType)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, et.EventName, eventType)
|
||||||
|
assert.Equal(t, "Test email", et.Subject)
|
||||||
|
assert.Equal(t, "Test design", et.Design)
|
||||||
})
|
})
|
||||||
assert.Error(t, err)
|
}
|
||||||
assert.Nil(t, emailTemplate)
|
|
||||||
})
|
design = "Test design"
|
||||||
|
|
||||||
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
for _, eventType := range s.TestInfo.TestEmailTemplateEventTypes {
|
||||||
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
t.Run("should add email template for "+eventType, func(t *testing.T) {
|
||||||
@@ -68,7 +82,7 @@ func addEmailTemplateTest(t *testing.T, s TestSetup) {
|
|||||||
EventName: eventType,
|
EventName: eventType,
|
||||||
Template: "Test email",
|
Template: "Test email",
|
||||||
Subject: "Test email",
|
Subject: "Test email",
|
||||||
Design: "Test design",
|
Design: &design,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, emailTemplate)
|
assert.NotNil(t, emailTemplate)
|
||||||
|
@@ -2,7 +2,6 @@ package test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -63,24 +62,23 @@ func TestResolvers(t *testing.T) {
|
|||||||
|
|
||||||
err := db.InitDB()
|
err := db.InitDB()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error initializing database: %s", err.Error())
|
t.Logf("Error initializing database: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean the persisted config for test to use fresh config
|
// clean the persisted config for test to use fresh config
|
||||||
envData, err := db.Provider.GetEnv(ctx)
|
envData, err := db.Provider.GetEnv(ctx)
|
||||||
fmt.Println("envData", envData.ID, envData.EnvData)
|
|
||||||
if err == nil && envData.ID != "" {
|
if err == nil && envData.ID != "" {
|
||||||
envData.EnvData = ""
|
envData.EnvData = ""
|
||||||
_, err = db.Provider.UpdateEnv(ctx, envData)
|
_, err = db.Provider.UpdateEnv(ctx, envData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error updating env: %s", err.Error())
|
t.Logf("Error updating env: %s", err.Error())
|
||||||
}
|
}
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Errorf("Error getting env: %s", err.Error())
|
t.Logf("Error getting env: %s", err.Error())
|
||||||
}
|
}
|
||||||
err = env.PersistEnv()
|
err = env.PersistEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error persisting env: %s", err.Error())
|
t.Logf("Error persisting env: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test")
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEnv, "test")
|
||||||
|
@@ -93,5 +93,6 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
|||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, res.IsValid)
|
assert.True(t, res.IsValid)
|
||||||
|
assert.Equal(t, user.Email, res.Claims["email"])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -114,16 +114,17 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": hostname,
|
"iss": hostname,
|
||||||
"aud": clientID,
|
"aud": clientID,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
"scope": scopes,
|
"scope": scopes,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
"login_method": loginMethod,
|
"login_method": loginMethod,
|
||||||
|
"allowed_roles": strings.Split(user.Roles, ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := SignJWTToken(customClaims)
|
token, err := SignJWTToken(customClaims)
|
||||||
@@ -153,16 +154,17 @@ func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce
|
|||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": hostName,
|
"iss": hostName,
|
||||||
"aud": clientID,
|
"aud": clientID,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeAccessToken,
|
"token_type": constants.TokenTypeAccessToken,
|
||||||
"scope": scopes,
|
"scope": scopes,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
"login_method": loginMethod,
|
"login_method": loginMethod,
|
||||||
|
"allowed_roles": strings.Split(user.Roles, ","),
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := SignJWTToken(customClaims)
|
token, err := SignJWTToken(customClaims)
|
||||||
@@ -256,7 +258,6 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
|
|||||||
if loginMethod != nil && loginMethod != "" {
|
if loginMethod != nil && loginMethod != "" {
|
||||||
sessionKey = loginMethod.(string) + ":" + userID
|
sessionKey = loginMethod.(string) + ":" + userID
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
|
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
|
||||||
if nonce == "" || err != nil {
|
if nonce == "" || err != nil {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
@@ -81,17 +81,8 @@ func GetOrganization() map[string]interface{} {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetForgotPasswordURL to get url for given token and hostname
|
// GetForgotPasswordURL to get url for given token and hostname
|
||||||
func GetForgotPasswordURL(token, hostname string) string {
|
func GetForgotPasswordURL(token, redirectURI string) string {
|
||||||
resetPasswordUrl, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyResetPasswordURL)
|
verificationURL := redirectURI + "?token=" + token
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if resetPasswordUrl == "" {
|
|
||||||
if err := memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, hostname+"/app/reset-password"); err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
verificationURL := resetPasswordUrl + "?token=" + token
|
|
||||||
return verificationURL
|
return verificationURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user