diff --git a/Containers/UserSettings/EmailSettings/EmailSettings.tsx b/Containers/UserSettings/EmailSettings/EmailSettings.tsx index e960a0b..86f6e87 100644 --- a/Containers/UserSettings/EmailSettings/EmailSettings.tsx +++ b/Containers/UserSettings/EmailSettings/EmailSettings.tsx @@ -10,16 +10,9 @@ import { SpinnerDotted } from 'spinners-react'; import Error from '~/Components/UI/Error/Error'; import Info from '~/Components/UI/Info/Info'; import { useFormStatus } from '~/hooks/useFormStatus'; +import { EmailSettingDTO } from '~/types/api/settings.types'; -type EmailDataForm = { - email: string; -}; - -type EmailSettingsProps = { - email?: string; -}; - -export default function EmailSettings(props: EmailSettingsProps) { +export default function EmailSettings(props: EmailSettingDTO) { //Var const toastOptions: ToastOptions = { position: 'top-right', @@ -36,7 +29,7 @@ export default function EmailSettings(props: EmailSettingsProps) { handleSubmit, reset, formState: { errors, isSubmitting, isValid }, - } = useForm({ mode: 'onChange' }); + } = useForm({ mode: 'onChange' }); const { isLoading, error, setIsLoading, handleError, clearError } = useFormStatus(); @@ -44,7 +37,7 @@ export default function EmailSettings(props: EmailSettingsProps) { const [info, setInfo] = useState(false); ////Functions - const formSubmitHandler = async (data: EmailDataForm) => { + const formSubmitHandler = async (data: EmailSettingDTO) => { clearError(); setIsLoading(true); diff --git a/Containers/UserSettings/PasswordSettings/PasswordSettings.tsx b/Containers/UserSettings/PasswordSettings/PasswordSettings.tsx index a05cf8e..980ea47 100644 --- a/Containers/UserSettings/PasswordSettings/PasswordSettings.tsx +++ b/Containers/UserSettings/PasswordSettings/PasswordSettings.tsx @@ -8,11 +8,7 @@ import { SpinnerDotted } from 'spinners-react'; //Components import Error from '~/Components/UI/Error/Error'; import { useFormStatus } from '~/hooks/useFormStatus'; - -type PasswordDataForm = { - oldPassword: string; - newPassword: string; -}; +import { PasswordSettingDTO } from '~/types/api/settings.types'; export default function PasswordSettings() { //Var @@ -31,12 +27,12 @@ export default function PasswordSettings() { handleSubmit, reset, formState: { errors, isSubmitting, isValid }, - } = useForm({ mode: 'onChange' }); + } = useForm({ mode: 'onChange' }); const { isLoading, error, setIsLoading, handleError, clearError } = useFormStatus(); ////Functions - const formSubmitHandler = async (data: PasswordDataForm) => { + const formSubmitHandler = async (data: PasswordSettingDTO) => { clearError(); setIsLoading(true); diff --git a/Containers/UserSettings/UsernameSettings/UsernameSettings.tsx b/Containers/UserSettings/UsernameSettings/UsernameSettings.tsx index 9766a8a..3f82539 100644 --- a/Containers/UserSettings/UsernameSettings/UsernameSettings.tsx +++ b/Containers/UserSettings/UsernameSettings/UsernameSettings.tsx @@ -10,16 +10,9 @@ import { SpinnerDotted } from 'spinners-react'; import Error from '~/Components/UI/Error/Error'; import Info from '~/Components/UI/Info/Info'; import { useFormStatus } from '~/hooks/useFormStatus'; +import { UsernameSettingDTO } from '~/types/api/settings.types'; -type UsernameDataForm = { - username: string; -}; - -type UsernameSettingsProps = { - username?: string; -}; - -export default function UsernameSettings(props: UsernameSettingsProps) { +export default function UsernameSettings(props: UsernameSettingDTO) { //Var const toastOptions: ToastOptions = { position: 'top-right', @@ -36,7 +29,7 @@ export default function UsernameSettings(props: UsernameSettingsProps) { handleSubmit, reset, formState: { errors, isSubmitting, isValid }, - } = useForm({ mode: 'onChange' }); + } = useForm({ mode: 'onChange' }); const { isLoading, error, setIsLoading, handleError, clearError } = useFormStatus(); @@ -44,7 +37,7 @@ export default function UsernameSettings(props: UsernameSettingsProps) { const [info, setInfo] = useState(false); ////Functions - const formSubmitHandler = async (data: UsernameDataForm) => { + const formSubmitHandler = async (data: UsernameSettingDTO) => { clearError(); setIsLoading(true); diff --git a/helpers/functions/index.ts b/helpers/functions/index.ts new file mode 100644 index 0000000..94dff3a --- /dev/null +++ b/helpers/functions/index.ts @@ -0,0 +1,8 @@ +export * from './auth'; +export * from './fileHelpers'; +export * from './isSshPubKeyDuplicate'; +export * from './lanCommandOption'; +export * from './nodemailerSMTP'; +export * from './repoHistory'; +export * from './timestampConverter'; +export * from './tokenController'; diff --git a/pages/api/account/updateAppriseMode.ts b/pages/api/account/updateAppriseMode.ts index 6633aba..2e6b15b 100644 --- a/pages/api/account/updateAppriseMode.ts +++ b/pages/api/account/updateAppriseMode.ts @@ -1,4 +1,3 @@ -// Imports import { getUsersList, updateUsersList } from '~/helpers/functions/fileHelpers'; import { authOptions } from '../auth/[...nextauth]'; import { getServerSession } from 'next-auth/next'; diff --git a/pages/api/account/updateEmail.js b/pages/api/account/updateEmail.js deleted file mode 100644 index 29a4bc5..0000000 --- a/pages/api/account/updateEmail.js +++ /dev/null @@ -1,84 +0,0 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; - -export default async function handler(req, res) { - if (req.method == 'PUT') { - //Verify that the user is logged in. - const session = await getServerSession(req, res, authOptions); - if (!session) { - res.status(401).json({ message: 'You must be logged in.' }); - return; - } - - //The data we expect to receive - let { email } = req.body; - - //Read the users file - //Find the absolute path of the json directory - const jsonDirectory = path.join(process.cwd(), '/config'); - let usersList = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - //Parse the usersList - usersList = JSON.parse(usersList); - - //1 : We check that we receive data. - if (!email) { - //If a variable is empty. - res.status(400).json({ message: 'A field is missing.' }); - return; - } - - //2 : control the data - const emailRegex = new RegExp( - /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - ); - if (!emailRegex.test(email)) { - res.status(400).json({ message: 'Your email is not valid' }); - return; - } - - //3 : Verify that the user of the session exists - const userIndex = usersList.map((user) => user.username).indexOf(session.user.name); - if (userIndex === -1) { - res.status(400).json({ - message: 'User is incorrect. Please, logout to update your session.', - }); - return; - } - - //4 : Change the email - try { - //Modify the email for the user - let newUsersList = usersList.map((user) => - user.username == session.user.name ? { ...user, email: email } : user - ); - //Stringify the new users list - newUsersList = JSON.stringify(newUsersList); - //Write the new JSON - await fs.writeFile(jsonDirectory + '/users.json', newUsersList, (err) => { - if (err) console.log(err); - }); - res.status(200).json({ message: 'Successful API send' }); - } catch (error) { - //Log for backend - console.log(error); - //Log for frontend - if (error.code == 'ENOENT') { - res.status(500).json({ - status: 500, - message: 'No such file or directory', - }); - } else { - res.status(500).json({ - status: 500, - message: 'API error, contact the administrator', - }); - } - return; - } - } else { - res.status(405).json({ message: 'Bad request on API' }); - } -} diff --git a/pages/api/account/updateEmail.ts b/pages/api/account/updateEmail.ts new file mode 100644 index 0000000..5614437 --- /dev/null +++ b/pages/api/account/updateEmail.ts @@ -0,0 +1,57 @@ +import { getUsersList, updateUsersList } from '~/helpers/functions/fileHelpers'; +import { authOptions } from '../auth/[...nextauth]'; +import { getServerSession } from 'next-auth/next'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { EmailSettingDTO } from '~/types/api/settings.types'; +import { ErrorResponse } from '~/types/api/error.types'; + +export default async function handler( + req: NextApiRequest & { body: EmailSettingDTO }, + res: NextApiResponse +) { + if (req.method !== 'PUT') { + return res.status(405); + } + + const session = await getServerSession(req, res, authOptions); + if (!session) { + return res.status(401); + } + + const { email } = req.body; + + if (!email) { + return res.status(422).json({ message: 'Unexpected data' }); + } + + try { + const usersList = await getUsersList(); + const userIndex = usersList.findIndex((user) => user.username === session.user?.name); + + if (userIndex === -1) { + return res.status(400).json({ + message: 'User is incorrect. Please, logout to update your session.', + }); + } + + if (usersList.some((user) => user.email === email)) { + return res.status(400).json({ message: 'Email already exists' }); + } + + const updatedUsersList = usersList.map((user, index) => + index === userIndex ? { ...user, email } : user + ); + + await updateUsersList(updatedUsersList); + return res.status(200).json({ message: 'Successful API send' }); + } catch (error: any) { + console.error(error); + return res.status(500).json({ + status: 500, + message: + error.code === 'ENOENT' + ? 'No such file or directory' + : 'API error, contact the administrator', + }); + } +} diff --git a/pages/api/account/updateEmailAlert.js b/pages/api/account/updateEmailAlert.js deleted file mode 100644 index 9a97c99..0000000 --- a/pages/api/account/updateEmailAlert.js +++ /dev/null @@ -1,74 +0,0 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; - -export default async function handler(req, res) { - if (req.method == 'PUT') { - //Verify that the user is logged in. - const session = await getServerSession(req, res, authOptions); - if (!session) { - res.status(401).json({ message: 'You must be logged in.' }); - return; - } - - //The data we expect to receive - let { emailAlert } = req.body; - - //Read the users file - //Find the absolute path of the json directory - const jsonDirectory = path.join(process.cwd(), '/config'); - let usersList = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - //Parse the usersList - usersList = JSON.parse(usersList); - - //1 : control the data - if (typeof emailAlert != 'boolean') { - res.status(422).json({ message: 'Unexpected data' }); - return; - } - - //2 : Verify that the user of the session exists - const userIndex = usersList.map((user) => user.username).indexOf(session.user.name); - if (userIndex === -1) { - res.status(400).json({ - message: 'User is incorrect. Please, logout to update your session.', - }); - return; - } - - //3 : Change the emailAlert settings - try { - //Modify the email for the user - let newUsersList = usersList.map((user) => - user.username == session.user.name ? { ...user, emailAlert: emailAlert } : user - ); - //Stringify the new users list - newUsersList = JSON.stringify(newUsersList); - //Write the new JSON - await fs.writeFile(jsonDirectory + '/users.json', newUsersList, (err) => { - if (err) console.log(err); - }); - res.status(200).json({ message: 'Successful API send' }); - } catch (error) { - //Log for backend - console.log(error); - //Log for frontend - if (error.code == 'ENOENT') { - res.status(500).json({ - status: 500, - message: 'No such file or directory', - }); - } else { - res.status(500).json({ - status: 500, - message: 'API error, contact the administrator', - }); - } - return; - } - } else { - res.status(405).json({ message: 'Bad request on API' }); - } -} diff --git a/pages/api/account/updateEmailAlert.ts b/pages/api/account/updateEmailAlert.ts new file mode 100644 index 0000000..116ee46 --- /dev/null +++ b/pages/api/account/updateEmailAlert.ts @@ -0,0 +1,53 @@ +import { getUsersList, updateUsersList } from '~/helpers/functions/fileHelpers'; +import { authOptions } from '../auth/[...nextauth]'; +import { getServerSession } from 'next-auth/next'; +import { EmailAlertDTO } from '~/types/api/notifications.types'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { ErrorResponse } from '~/types/api/error.types'; + +export default async function handler( + req: NextApiRequest & { body: EmailAlertDTO }, + res: NextApiResponse +) { + if (req.method !== 'PUT') { + return res.status(405); + } + + const session = await getServerSession(req, res, authOptions); + if (!session) { + return res.status(401); + } + + const { emailAlert } = req.body; + + if (typeof emailAlert !== 'boolean') { + return res.status(422).json({ message: 'Unexpected data' }); + } + + try { + const usersList = await getUsersList(); + const userIndex = usersList.findIndex((u) => u.username === session.user?.name); + + if (userIndex === -1) { + return res + .status(400) + .json({ message: 'User is incorrect. Please, logout to update your session.' }); + } + + const updatedUsersList = usersList.map((user, index) => + index === userIndex ? { ...user, emailAlert } : user + ); + + await updateUsersList(updatedUsersList); + return res.status(200).json({ message: 'Successful API send' }); + } catch (error: any) { + console.error(error); + return res.status(500).json({ + status: 500, + message: + error.code === 'ENOENT' + ? 'No such file or directory' + : 'API error, contact the administrator', + }); + } +} diff --git a/pages/api/account/updatePassword.js b/pages/api/account/updatePassword.js deleted file mode 100644 index 26582f1..0000000 --- a/pages/api/account/updatePassword.js +++ /dev/null @@ -1,84 +0,0 @@ -//Lib -import { hashPassword, verifyPassword } from '../../../helpers/functions/auth'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; - -export default async function handler(req, res) { - if (req.method == 'PUT') { - //Verify that the user is logged in. - const session = await getServerSession(req, res, authOptions); - if (!session) { - res.status(401).json({ message: 'You must be logged in.' }); - return; - } - - //The data we expect to receive - let { oldPassword, newPassword } = req.body; - - //Read the users file - //Find the absolute path of the json directory - const jsonDirectory = path.join(process.cwd(), '/config'); - let usersList = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - //Parse the usersList - usersList = JSON.parse(usersList); - - //1 : We check that we receive data for each variable. - if (!oldPassword || !newPassword) { - //If a variable is empty. - res.status(400).json({ message: 'A field is missing.' }); - return; - } - //Hash the new password - newPassword = await hashPassword(newPassword); - - //2 : Verify that the user of the session exists - const userIndex = usersList.map((user) => user.username).indexOf(session.user.name); - if (userIndex === -1) { - res.status(400).json({ message: 'User is incorrect.' }); - return; - } - const user = usersList[userIndex]; - - //3 : Check that the old password is correct - const isValid = await verifyPassword(oldPassword, user.password); - if (!isValid) { - res.status(400).json({ message: 'Old password is incorrect.' }); - return; - } - - //4 : Change the password - try { - //Modify the password for the user - let newUsersList = usersList.map((user) => - user.username == session.user.name ? { ...user, password: newPassword } : user - ); - //Stringify the new users list - newUsersList = JSON.stringify(newUsersList); - //Write the new JSON - await fs.writeFile(jsonDirectory + '/users.json', newUsersList, (err) => { - if (err) console.log(err); - }); - res.status(200).json({ message: 'Successful API send' }); - } catch (error) { - //Log for backend - console.log(error); - //Log for frontend - if (error.code == 'ENOENT') { - res.status(500).json({ - status: 500, - message: 'No such file or directory', - }); - } else { - res.status(500).json({ - status: 500, - message: 'API error, contact the administrator', - }); - } - return; - } - } else { - res.status(405).json({ message: 'Bad request on API' }); - } -} diff --git a/pages/api/account/updatePassword.ts b/pages/api/account/updatePassword.ts new file mode 100644 index 0000000..d0dac57 --- /dev/null +++ b/pages/api/account/updatePassword.ts @@ -0,0 +1,61 @@ +import { getUsersList, updateUsersList, hashPassword, verifyPassword } from '~/helpers/functions'; +import { authOptions } from '../auth/[...nextauth]'; +import { getServerSession } from 'next-auth/next'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { ErrorResponse } from '~/types/api/error.types'; +import { PasswordSettingDTO } from '~/types/api/settings.types'; + +export default async function handler( + req: NextApiRequest & { body: PasswordSettingDTO }, + res: NextApiResponse +) { + if (req.method !== 'PUT') { + return res.status(405); + } + + const session = await getServerSession(req, res, authOptions); + if (!session) { + return res.status(401); + } + + const { oldPassword, newPassword } = req.body; + + if (typeof oldPassword !== 'string' || typeof newPassword !== 'string') { + return res.status(422).json({ message: 'Unexpected data' }); + } + + try { + const usersList = await getUsersList(); + const userIndex = usersList.findIndex((user) => user.username === session.user?.name); + const user = usersList[userIndex]; + + if (userIndex === -1 || !user) { + return res.status(400).json({ + message: 'User is incorrect. Please, logout to update your session.', + }); + } + + const isValidPassword = await verifyPassword(oldPassword, user.password); + if (!isValidPassword) { + return res.status(400).json({ message: 'Old password is incorrect.' }); + } + + const newPasswordHash = await hashPassword(newPassword); + const updatedUsersList = usersList.map((user, index) => + index === userIndex ? { ...user, password: newPasswordHash } : user + ); + + await updateUsersList(updatedUsersList); + + return res.status(200).json({ message: 'Successful API send' }); + } catch (error: any) { + console.error(error); + return res.status(500).json({ + status: 500, + message: + error.code === 'ENOENT' + ? 'No such file or directory' + : 'API error, contact the administrator', + }); + } +} diff --git a/pages/api/account/updateUsername.js b/pages/api/account/updateUsername.js deleted file mode 100644 index 18077c6..0000000 --- a/pages/api/account/updateUsername.js +++ /dev/null @@ -1,84 +0,0 @@ -//Lib -import { promises as fs } from 'fs'; -import path from 'path'; -import { authOptions } from '../auth/[...nextauth]'; -import { getServerSession } from 'next-auth/next'; - -export default async function handler(req, res) { - if (req.method == 'PUT') { - //Verify that the user is logged in. - const session = await getServerSession(req, res, authOptions); - if (!session) { - res.status(401).json({ message: 'You must be logged in.' }); - return; - } - - //The data we expect to receive - let { username } = req.body; - - //Read the users file - //Find the absolute path of the json directory - const jsonDirectory = path.join(process.cwd(), '/config'); - let usersList = await fs.readFile(jsonDirectory + '/users.json', 'utf8'); - //Parse the usersList - usersList = JSON.parse(usersList); - - //1 : We check that we receive data. - if (!username) { - //If a variable is empty. - res.status(400).json({ message: 'A field is missing.' }); - return; - } - - //2 : control the data - const usernameRegex = new RegExp(/^[a-z]{5,15}$/); - if (!usernameRegex.test(username)) { - res.status(400).json({ - message: 'Only a-z characters are allowed (5 to 15 char.)', - }); - return; - } - - //3 : Verify that the user of the session exists - const userIndex = usersList.map((user) => user.username).indexOf(session.user.name); - if (userIndex === -1) { - res.status(400).json({ - message: 'User is incorrect. Please, logout to update your session.', - }); - return; - } - - //4 : Change the username - try { - //Modify the username for the user - let newUsersList = usersList.map((user) => - user.username == session.user.name ? { ...user, username: username } : user - ); - //Stringify the new users list - newUsersList = JSON.stringify(newUsersList); - //Write the new JSON - await fs.writeFile(jsonDirectory + '/users.json', newUsersList, (err) => { - if (err) console.log(err); - }); - res.status(200).json({ message: 'Successful API send' }); - } catch (error) { - //Log for backend - console.log(error); - //Log for frontend - if (error.code == 'ENOENT') { - res.status(500).json({ - status: 500, - message: 'No such file or directory', - }); - } else { - res.status(500).json({ - status: 500, - message: 'API error, contact the administrator', - }); - } - return; - } - } else { - res.status(405).json({ message: 'Bad request on API' }); - } -} diff --git a/pages/api/account/updateUsername.ts b/pages/api/account/updateUsername.ts new file mode 100644 index 0000000..c4e04b9 --- /dev/null +++ b/pages/api/account/updateUsername.ts @@ -0,0 +1,65 @@ +import { getUsersList, updateUsersList } from '~/helpers/functions'; +import { authOptions } from '../auth/[...nextauth]'; +import { getServerSession } from 'next-auth/next'; +import { UsernameSettingDTO } from '~/types/api/settings.types'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { ErrorResponse } from '~/types/api/error.types'; + +export default async function handler( + req: NextApiRequest & { body: UsernameSettingDTO }, + res: NextApiResponse +) { + if (req.method !== 'PUT') { + return res.status(405); + } + + const session = await getServerSession(req, res, authOptions); + if (!session) { + return res.status(401); + } + + //The data we expect to receive + let { username } = req.body; + + if (typeof username !== 'string') { + return res.status(422).json({ message: 'Unexpected data' }); + } + const usernameRegex = new RegExp(/^[a-z]{5,15}$/); + if (!usernameRegex.test(username)) { + res.status(422).json({ + message: 'Only a-z characters are allowed (5 to 15 char.)', + }); + return; + } + + try { + const usersList = await getUsersList(); + const userIndex = usersList.findIndex((user) => user.username === session.user?.name); + + if (userIndex === -1) { + return res.status(400).json({ + message: 'User is incorrect. Please, logout to update your session.', + }); + } + + if (usersList.some((user) => user.username === username)) { + return res.status(400).json({ message: 'Username already exists' }); + } + + const updatedUsersList = usersList.map((user, index) => + index === userIndex ? { ...user, username } : user + ); + await updateUsersList(updatedUsersList); + + return res.status(200).json({ message: 'Successful API send' }); + } catch (error: any) { + console.error(error); + return res.status(500).json({ + status: 500, + message: + error.code === 'ENOENT' + ? 'No such file or directory' + : 'API error, contact the administrator', + }); + } +} diff --git a/types/api/settings.types.ts b/types/api/settings.types.ts new file mode 100644 index 0000000..febfda8 --- /dev/null +++ b/types/api/settings.types.ts @@ -0,0 +1,12 @@ +export type EmailSettingDTO = { + email: string; +}; + +export type UserSettingDTO = { + username: string; +}; + +export type PasswordSettingDTO = { + oldPassword: string; + newPassword: string; +};