From 3f4f1658dd7c2a92fbdfa0ec297e7883c85a026b Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 5 Jun 2024 13:50:42 +0100 Subject: [PATCH 01/11] Fixes issue with certificates not being updated --- docs/internals/changelog.md | 8 ++++++++ package.json | 2 +- server/cache/certificates.js | 7 ++----- server/cache/index.js | 15 +++------------ server/tools/checkCertificate.js | 13 ++++--------- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/docs/internals/changelog.md b/docs/internals/changelog.md index f562bdd..6885738 100644 --- a/docs/internals/changelog.md +++ b/docs/internals/changelog.md @@ -2,6 +2,14 @@ ## v0.5.1 +### Fixes issue with certificates not being updated + +### Summary + +There was an issue where certificates were not being saved to the database due to the value not being parsed as a string. This has been fixed now. + +## v0.5.1 + ### Replaces Sonner with React-Toastify ### Summary diff --git a/package.json b/package.json index ea22420..2138304 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lunalytics", - "version": "0.5.0", + "version": "0.5.1", "description": "Open source Node.js server/website monitoring tool", "private": true, "author": "KSJaay ", diff --git a/server/cache/certificates.js b/server/cache/certificates.js index 149c8fc..743409d 100644 --- a/server/cache/certificates.js +++ b/server/cache/certificates.js @@ -28,15 +28,12 @@ class Certificates { delete certificate.lastCheck; delete certificate.nextCheck; - await updateCertificate(monitorId, { - ...certificate, - issuer: JSON.stringify(certificate.issuer), - }); + await updateCertificate(monitorId, certificate); certificate.lastCheck = Date.now(); certificate.nextCheck = certificate.lastCheck + 86400000; - this.certificates.set(monitorId, certificate); + this.certificates.set(monitorId, cleanCertificate(certificate)); } async delete(monitorId) { diff --git a/server/cache/index.js b/server/cache/index.js index 53bc632..0a65368 100644 --- a/server/cache/index.js +++ b/server/cache/index.js @@ -2,7 +2,7 @@ import Certificates from './certificates.js'; import Heartbeats from './heartbeats.js'; import Monitor from './monitors.js'; -import getCertInfo, { parseCert } from '../tools/checkCertificate.js'; +import getCertInfo from '../tools/checkCertificate.js'; import httpStatusCheck from '../tools/httpStatus.js'; import tcpStatusCheck from '../tools/tcpPing.js'; @@ -70,21 +70,12 @@ class Master { const heartbeat = await httpStatusCheck(monitor); await this.heartbeats.addHeartbeat(heartbeat); - if (monitor.url?.startsWith('https://')) { + if (monitor.url?.toLowerCase().startsWith('https')) { const certificate = await this.certificates.get(monitorId); if (!certificate?.nextCheck || certificate.nextCheck <= Date.now()) { const cert = await getCertInfo(monitor.url); - - if (cert) { - await this.certificates.update( - monitorId, - parseCert({ - ...certificate, - ...cert, - }) - ); - } + await this.certificates.update(monitorId, cert); } } diff --git a/server/tools/checkCertificate.js b/server/tools/checkCertificate.js index 02efb79..99b4074 100644 --- a/server/tools/checkCertificate.js +++ b/server/tools/checkCertificate.js @@ -17,16 +17,14 @@ const getCertInfo = async (url) => { } catch (error) { logger.error('getCertInfo', error); - return { - isValid: false, - }; + return { isValid: false }; } }; const checkCertificate = (res) => { if (!res.request.socket) { logger.error('checkCertificate', 'Socket not found'); - throw new Error('Socket not found'); + return { isValid: false }; } const info = res.request.socket.getPeerCertificate(true); @@ -34,10 +32,7 @@ const checkCertificate = (res) => { const parsedInfo = parseCert(info); - return { - isValid: valid, - ...parsedInfo, - }; + return { isValid: valid, ...parsedInfo }; }; const getDaysBetween = (validFrom, validTo) => @@ -51,7 +46,7 @@ const getDaysRemaining = (validFrom, validTo) => { return daysRemaining; }; -export const parseCert = (cert) => { +const parseCert = (cert) => { const validOn = cert.subjectaltname ?.replace(/DNS:|IP Address:/g, '') .split(', '); From 213b4fb54d9f593ab15cca6a04e1fec99e9faf17 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 01:49:10 +0100 Subject: [PATCH 02/11] Removes utils folder from server and app directories --- app/utils/cookies.js | 11 --- app/utils/ms.js | 38 --------- app/utils/propTypes.js | 85 -------------------- app/utils/validators/auth.js | 46 ----------- app/utils/validators/index.js | 4 - app/utils/validators/monitor.js | 102 ------------------------ app/utils/validators/user.js | 41 ---------- server/utils/cookies.js | 42 ---------- server/utils/cron.js | 94 ---------------------- server/utils/errors.js | 55 ------------- server/utils/hashPassword.js | 23 ------ server/utils/jwt.js | 43 ---------- server/utils/logger.js | 40 ---------- server/utils/ms.js | 13 ---- server/utils/randomId.js | 7 -- server/utils/url.js | 26 ------- server/utils/validators/auth.js | 49 ------------ server/utils/validators/index.js | 7 -- server/utils/validators/monitor.js | 121 ----------------------------- server/utils/validators/user.js | 41 ---------- 20 files changed, 888 deletions(-) delete mode 100644 app/utils/cookies.js delete mode 100644 app/utils/ms.js delete mode 100644 app/utils/propTypes.js delete mode 100644 app/utils/validators/auth.js delete mode 100644 app/utils/validators/index.js delete mode 100644 app/utils/validators/monitor.js delete mode 100644 app/utils/validators/user.js delete mode 100644 server/utils/cookies.js delete mode 100644 server/utils/cron.js delete mode 100644 server/utils/errors.js delete mode 100644 server/utils/hashPassword.js delete mode 100644 server/utils/jwt.js delete mode 100644 server/utils/logger.js delete mode 100644 server/utils/ms.js delete mode 100644 server/utils/randomId.js delete mode 100644 server/utils/url.js delete mode 100644 server/utils/validators/auth.js delete mode 100644 server/utils/validators/index.js delete mode 100644 server/utils/validators/monitor.js delete mode 100644 server/utils/validators/user.js diff --git a/app/utils/cookies.js b/app/utils/cookies.js deleted file mode 100644 index 7639931..0000000 --- a/app/utils/cookies.js +++ /dev/null @@ -1,11 +0,0 @@ -import cookie from 'cookie'; - -const parseUserCookie = (cookies) => { - const parsedCookies = cookie.parse(cookies); - const { user } = parsedCookies; - - if (!user) return {}; - return JSON.parse(user); -}; - -export { parseUserCookie }; diff --git a/app/utils/ms.js b/app/utils/ms.js deleted file mode 100644 index 97a5e2a..0000000 --- a/app/utils/ms.js +++ /dev/null @@ -1,38 +0,0 @@ -const msToTime = (duration) => { - let seconds = Math.floor((duration / 1000) % 60); - let minutes = Math.floor((duration / (1000 * 60)) % 60); - let hours = Math.floor((duration / (1000 * 60 * 60)) % 24); - let days = Math.floor((duration / (1000 * 60 * 60 * 24)) % 30); - let months = Math.floor((duration / (1000 * 60 * 60 * 24 * 30)) % 12); - let years = Math.floor(duration / (1000 * 60 * 60 * 24 * 30 * 12)); - - // Only return the highest non-zero unit - - if (years > 0) { - return years === 1 ? `${years} year` : `${years} years`; - } - - if (months > 0) { - return months === 1 ? `${months} month` : `${months} months`; - } - - if (days > 0) { - return days === 1 ? `${days} day` : `${days} days`; - } - - if (hours > 0) { - return hours === 1 ? `${hours} hour` : `${hours} hours`; - } - - if (minutes > 0) { - return minutes === 1 ? `${minutes} minute` : `${minutes} minutes`; - } - - if (seconds > 0) { - return seconds === 1 ? `${seconds} second` : `${seconds} seconds`; - } - - return 'Unknown'; -}; - -export { msToTime }; diff --git a/app/utils/propTypes.js b/app/utils/propTypes.js deleted file mode 100644 index 4b29332..0000000 --- a/app/utils/propTypes.js +++ /dev/null @@ -1,85 +0,0 @@ -// import dependencies -import PropTypes from 'prop-types'; - -const userPropType = PropTypes.shape({ - email: PropTypes.string.isRequired, - displayName: PropTypes.string.isRequired, - avatar: PropTypes.string, - isVerified: PropTypes.number.isRequired, - permission: PropTypes.number.isRequired, - createdAt: PropTypes.string.isRequired, - canEdit: PropTypes.bool, - canManage: PropTypes.bool, -}); - -const monitorPropType = PropTypes.shape({ - monitorId: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - interval: PropTypes.number.isRequired, - retryInterval: PropTypes.number.isRequired, - requestTimeout: PropTypes.number.isRequired, - method: PropTypes.string.isRequired, - headers: PropTypes.string, - body: PropTypes.string, - valid_status_codes: PropTypes.array.isRequired, - email: PropTypes.string.isRequired, - nextCheck: PropTypes.number.isRequired, -}); - -const heartbeatPropType = PropTypes.shape({ - id: PropTypes.number.isRequired, - status: PropTypes.number.isRequired, - latency: PropTypes.number.isRequired, - date: PropTypes.number.isRequired, - isDown: PropTypes.number.isRequired, - message: PropTypes.string.isRequired, -}); - -const certPropType = PropTypes.shape({ - id: PropTypes.number.isRequired, - monitorId: PropTypes.string.isRequired, - isValid: PropTypes.bool.isRequired, - issuer: PropTypes.string.isRequired, - validFrom: PropTypes.string.isRequired, - validTill: PropTypes.string.isRequired, - validOn: PropTypes.string.isRequired, - daysRemaining: PropTypes.number.isRequired, -}); - -const fullMonitorPropType = PropTypes.shape({ - monitorId: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - interval: PropTypes.number.isRequired, - retryInterval: PropTypes.number.isRequired, - requestTimeout: PropTypes.number.isRequired, - method: PropTypes.string.isRequired, - headers: PropTypes.string, - body: PropTypes.string, - valid_status_codes: PropTypes.array.isRequired, - email: PropTypes.string.isRequired, - cert: certPropType, - heartbeats: PropTypes.arrayOf(heartbeatPropType), -}); - -const colorPropType = PropTypes.oneOf([ - 'blue', - 'cyan', - 'gray', - 'green', - 'pink', - 'primary', - 'purple', - 'red', - 'yellow', -]); - -export { - userPropType, - monitorPropType, - fullMonitorPropType, - heartbeatPropType, - certPropType, - colorPropType, -}; diff --git a/app/utils/validators/auth.js b/app/utils/validators/auth.js deleted file mode 100644 index 1381008..0000000 --- a/app/utils/validators/auth.js +++ /dev/null @@ -1,46 +0,0 @@ -// regex to check user has only letters, numbers, underscore, dash, spaces and should be 3-24 characters long -const usernameRegex = /^[a-zA-Z0-9_\- ]{3,32}$/; -// regex to check if email is valid -const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,254}$/; -// regex to check if one letter, one number or special character, atleast 8 characters long and max of 64 characters long -const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9!@#$%^&*~_\-+=]).{8,64}$/; - -const email = (email = '') => { - if (email.length < 3 || email.length > 254) { - return 'Email must be between 3 and 254 characters.'; - } - - if (!emailRegex.test(email)) { - return 'Email is not valid'; - } - - return false; -}; - -const username = (username = '') => { - if (username.length < 3 || username.length > 32) { - return 'Username must be between 3 and 32 characters.'; - } - - if (!usernameRegex.test(username)) { - return 'Username can only contain letters, numbers, underscores, and dashes.'; - } - - return false; -}; - -const password = (password = '') => { - if (password.length < 8 || password.length > 64) { - return 'Password must be between 8 and 64 characters.'; - } - - if (!passwordRegex.test(password)) { - return 'Password is not valid'; - } - - return false; -}; - -const auth = { email, username, password }; - -export default auth; diff --git a/app/utils/validators/index.js b/app/utils/validators/index.js deleted file mode 100644 index 81bb711..0000000 --- a/app/utils/validators/index.js +++ /dev/null @@ -1,4 +0,0 @@ -import auth from './auth'; -import monitor from './monitor'; - -export { auth, monitor }; diff --git a/app/utils/validators/monitor.js b/app/utils/validators/monitor.js deleted file mode 100644 index e4e1a63..0000000 --- a/app/utils/validators/monitor.js +++ /dev/null @@ -1,102 +0,0 @@ -const validMethods = [ - 'DELETE', - 'GET', - 'HEAD', - 'OPTIONS', - 'PATCH', - 'POST', - 'PUT', -]; - -const validTypes = ['http', 'tcp']; - -export const type = (type) => { - if (!type || !validTypes.includes(type)) { - return 'Please select a valid monitor type.'; - } -}; - -export const name = (name) => { - if (!name || !/^[a-zA-Z0-9-]+$/.test(name)) { - return 'Please enter a valid name. Only letters, numbers and - are allowed.'; - } -}; - -export const httpUrl = (url) => { - const urlRegex = /^(http|https):\/\/[^ "]+$/; - - if (!url || !urlRegex.test(url)) { - return 'Please enter a valid URL.'; - } -}; - -export const httpMethod = (method) => { - if (!method || !validMethods.includes(method)) { - return 'Please select a valid method.'; - } -}; - -export const httpStatusCodes = (codes) => { - if (!codes || !codes.length) { - return 'Please select at least one status code.'; - } -}; - -export const tcpHost = (host) => { - const isIpv4 = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/; - - if (!host || !isIpv4.test(host)) { - return 'Please enter a valid host (Only IPv4 is valid).'; - } -}; - -export const tcpPort = (port) => { - if (!port || port < 1 || port > 65535) { - return 'Please enter a valid port.'; - } -}; - -export const interval = (interval) => { - if (!interval) { - return 'Please enter a valid interval.'; - } - - if (interval < 20 || interval > 600) { - return 'Please enter a valid interval. Interval should be between 20 and 600 seconds.'; - } -}; - -export const retryInterval = (retryInterval) => { - if (!retryInterval) { - return 'Please enter a valid retry interval.'; - } - - if (retryInterval < 20 || retryInterval > 600) { - return 'Please enter a valid retry interval. Retry interval should be between 20 and 600 seconds.'; - } -}; - -export const requestTimeout = (requestTimeout) => { - if (!requestTimeout) { - return 'Please enter a valid request timeout.'; - } - - if (requestTimeout < 20 || requestTimeout > 600) { - return 'Please enter a valid request timeout. Request timeout should be between 20 and 600 seconds.'; - } -}; - -const monitorValidators = { - type, - name, - httpUrl, - httpMethod, - httpStatusCodes, - tcpHost, - tcpPort, - interval, - retryInterval, - requestTimeout, -}; - -export default monitorValidators; diff --git a/app/utils/validators/user.js b/app/utils/validators/user.js deleted file mode 100644 index fcf8881..0000000 --- a/app/utils/validators/user.js +++ /dev/null @@ -1,41 +0,0 @@ -const defaultAvatars = [ - 'Ape', - 'Bear', - 'Cat', - 'Dog', - 'Doggo', - 'Duck', - 'Eagle', - 'Fox', - 'Gerbil', - 'Hamster', - 'Hedgehog', - 'Koala', - 'Ostrich', - 'Panda', - 'Rabbit', - 'Rocket', - 'Tiger', -]; - -const isImageUrl = (url) => { - if (typeof url !== 'string') { - return false; - } - - return url.match(/^https?:\/\/.+\.(jpg|jpeg|png|gif)$/gim); -}; - -const isAvatar = (avatar) => { - if (avatar === null) { - return false; - } - - if (!defaultAvatars.includes(avatar) && !isImageUrl(avatar)) { - return 'Avatar must be a valid Imgur URL or one of the default avatars.'; - } - - return false; -}; - -export { isAvatar }; diff --git a/server/utils/cookies.js b/server/utils/cookies.js deleted file mode 100644 index b1dc7eb..0000000 --- a/server/utils/cookies.js +++ /dev/null @@ -1,42 +0,0 @@ -const thirtyDaysInMilliseconds = 2592000000; - -const setClientSideCookie = (res, name, value) => { - return res.cookie(name, value, { - expires: new Date(Date.now() + thirtyDaysInMilliseconds), - maxAge: thirtyDaysInMilliseconds, - secure: process.env.NODE_ENV === 'production', - sameSite: 'strict', - }); -}; - -const setServerSideCookie = (res, name, value) => { - return res.cookie(name, value, { - expires: new Date(Date.now() + thirtyDaysInMilliseconds), - maxAge: thirtyDaysInMilliseconds, - secure: process.env.NODE_ENV === 'production', - httpOnly: true, - sameSite: 'strict', - }); -}; - -const setDemoCookie = (res, name, value) => { - return res.cookie(name, value, { - secure: process.env.NODE_ENV === 'production', - httpOnly: true, - sameSite: 'strict', - }); -}; - -const deleteCookie = (res, name) => { - return res.cookie(name, '', { - expires: -1, - maxAge: -1, - }); -}; - -export { - setClientSideCookie, - setServerSideCookie, - setDemoCookie, - deleteCookie, -}; diff --git a/server/utils/cron.js b/server/utils/cron.js deleted file mode 100644 index 040a11a..0000000 --- a/server/utils/cron.js +++ /dev/null @@ -1,94 +0,0 @@ -// import dependencies -import { CronJob } from 'cron'; - -// import local files -import cache from '../cache/index.js'; -import logger from './logger.js'; -import { - fetchHeartbeatsByDate, - fetchLastDailyHeartbeat, -} from '../database/queries/heartbeat.js'; - -// fetch all monitors -// fetch only heartbeats that are up for each monitor -// get the average response time for each monitor -// create a new record in the database - -async function initialiseCronJobs() { - logger.info('Cron', 'Initialising cron jobs'); - - new CronJob( - '0 * * * *', - async function () { - logger.info('Cron', 'Running hourly cron job for creating heartbeat'); - - const monitors = await cache.monitors.getKeys(); - - if (monitors.length === 0) { - return; - } - - // the last hour on the hour - const date = Date.now(); - const lastHour = date - 3600000 - (date % 3600000); - - for (const monitorId of monitors) { - const query = await fetchHeartbeatsByDate(monitorId, lastHour); - - if (query.length === 0) { - continue; - } - - const [lastMonitor] = query; - - const upHeartbeats = query.filter((h) => !h.isDown); - - if (upHeartbeats.length === 0) { - continue; - } - - const averageLatency = Math.round( - upHeartbeats.reduce((acc, curr) => acc + curr.latency, 0) / - upHeartbeats.length - ); - - const data = { - monitorId, - date: lastHour, - status: lastMonitor.status, - latency: averageLatency, - }; - - await cache.heartbeats.addHourlyHeartbeat(monitorId, data); - } - }, - null, - true, - 'Europe/London' - ); - - // every 5 minutes - new CronJob( - '*/5 * * * *', - async function () { - logger.info('Cron', 'Running 5 minute cron job for fetching heartbeats'); - - const monitors = await cache.monitors.getKeys(); - - if (monitors.length === 0) { - return; - } - - for (const monitorId of monitors) { - const query = await fetchLastDailyHeartbeat(monitorId); - - cache.heartbeats.addFifthMinuteHeartbeat(monitorId, query); - } - }, - null, - true, - 'Europe/London' - ); -} - -export default initialiseCronJobs; diff --git a/server/utils/errors.js b/server/utils/errors.js deleted file mode 100644 index a8d6e62..0000000 --- a/server/utils/errors.js +++ /dev/null @@ -1,55 +0,0 @@ -import logger from './logger.js'; - -class AuthorizationError extends Error { - constructor(error) { - super(); - this.name = 'AuthorizationError'; - this.error = error; - } -} - -class UnprocessableError extends Error { - constructor(error) { - super(); - this.name = 'UnprocessableError'; - this.error = error; - } -} - -class ConflictError extends Error { - constructor(error) { - super(); - this.name = 'ConflictError'; - this.error = error; - } -} - -const handleError = (error, response) => { - logger.error('Error handler', error.message + error.stack); - - if (!response.headersSent) { - if (error instanceof AuthorizationError) { - return response.status(401).send({ - message: error.error, - }); - } - - if (error instanceof ConflictError) { - return response.status(409).send({ - message: error.error, - }); - } - - if (error instanceof UnprocessableError) { - return response.status(422).send({ - message: error.error, - }); - } - - return response.status(500).send({ - message: 'Something went wrong', - }); - } -}; - -export { AuthorizationError, ConflictError, UnprocessableError, handleError }; diff --git a/server/utils/hashPassword.js b/server/utils/hashPassword.js deleted file mode 100644 index b60e9cb..0000000 --- a/server/utils/hashPassword.js +++ /dev/null @@ -1,23 +0,0 @@ -import { hashSync, compareSync } from 'bcrypt'; -const saltRounds = 10; - -/** - * Hash a password - * @param {string} password - * @returns {string} - */ - -export const generateHash = (password) => { - return hashSync(password, saltRounds); -}; - -/** - * Check if the user password matches the user hash password - * @param {string} password - * @param {string} hashPassword - * @returns {boolean} Returns boolean if password matches - */ - -export const verifyPassword = (password, hashPassword) => { - return compareSync(password, hashPassword); -}; diff --git a/server/utils/jwt.js b/server/utils/jwt.js deleted file mode 100644 index 5fd5f4e..0000000 --- a/server/utils/jwt.js +++ /dev/null @@ -1,43 +0,0 @@ -// import dependencies -import jwt from 'jsonwebtoken'; - -// import local files -import logger from './logger.js'; - -const verifyCookie = (value) => { - try { - const jwtSecret = - process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease'; - - let token = jwt.verify(value, jwtSecret, { - algorithms: ['HS256'], - }); - return token; - } catch (error) { - logger.error('JWT Verify', { - message: error.message, - stack: error.stack, - }); - throw error; - } -}; - -const signCookie = (value) => { - try { - const jwtSecret = - process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease'; - - let token = jwt.sign(value, jwtSecret, { - expiresIn: 2592000, - }); - return token; - } catch (error) { - logger.error('JWT Sign', { - message: error.message, - stack: error.stack, - }); - throw error; - } -}; - -export { verifyCookie, signCookie }; diff --git a/server/utils/logger.js b/server/utils/logger.js deleted file mode 100644 index 69b0e2c..0000000 --- a/server/utils/logger.js +++ /dev/null @@ -1,40 +0,0 @@ -class LogMethods { - log(section, msg, level, withDate = true) { - let importance = { - INFO: '\x1b[96m[INFO]\x1b[39m', - WARN: '\x1b[33m[WARN]\x1b[39m', - ERROR: '\x1b[91m[ERROR]\x1b[39m', - }; - const message = `${ - typeof msg === 'string' - ? `${withDate ? `${new Date().toISOString()} ` : ''}[${section}] ${ - importance[level] - }: ${msg}` - : msg - }`; - if (level === 'INFO') { - console.info(message); - } else if (level === 'WARN') { - console.warn(message); - } else if (level === 'ERROR') { - console.error(message); - } else { - console.log(message); - } - } - info(section, msg) { - this.log(section, msg, 'INFO'); - } - - warn(section, msg) { - this.log(section, msg, 'WARN'); - } - - error(section, msg) { - this.log(section, msg, 'ERROR'); - } -} - -const logger = new LogMethods(); - -export default logger; diff --git a/server/utils/ms.js b/server/utils/ms.js deleted file mode 100644 index 21e2403..0000000 --- a/server/utils/ms.js +++ /dev/null @@ -1,13 +0,0 @@ -const timeToMs = (duration, type = 'hours') => { - const types = { - months: 2592000000, - days: 86400000, - hours: 3600000, - minutes: 60000, - seconds: 1000, - }; - - return duration * types[type]; -}; - -export { timeToMs }; diff --git a/server/utils/randomId.js b/server/utils/randomId.js deleted file mode 100644 index 954ee44..0000000 --- a/server/utils/randomId.js +++ /dev/null @@ -1,7 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; - -const randomId = () => { - return uuidv4(); -}; - -export default randomId; diff --git a/server/utils/url.js b/server/utils/url.js deleted file mode 100644 index 39cfbfd..0000000 --- a/server/utils/url.js +++ /dev/null @@ -1,26 +0,0 @@ -const createURL = (path = '', params) => { - if (!path || !params) return path; - - if (path.includes('?')) { - const [pathWithoutQuery, queries] = path.split('?'); - const queriesAsObject = {}; - - queries.split('&').forEach((query) => { - const [key, value] = query.split('='); - queriesAsObject[key] = value; - }); - - const searchParams = new URLSearchParams({ - ...queriesAsObject, - ...params, - }).toString(); - - return `${pathWithoutQuery}?${searchParams}`; - } - - const searchParams = new URLSearchParams(params).toString(); - - return `${path}?${searchParams}`; -}; - -export { createURL }; diff --git a/server/utils/validators/auth.js b/server/utils/validators/auth.js deleted file mode 100644 index b9d306e..0000000 --- a/server/utils/validators/auth.js +++ /dev/null @@ -1,49 +0,0 @@ -// regex to check user has only letters, numbers, underscore, dash, spaces and should be 3-24 characters long -const usernameRegex = /^[a-zA-Z0-9_\- ]{3,32}$/; -// regex to check if email is valid -const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,254}$/; -// regex to check if one letter, one number or special character, atleast 8 characters long and max of 64 characters long -const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9!@#$%^&*~_\-+=]).{8,64}$/; - -const email = (email = '') => { - if (email.length < 3 || email.length > 254) { - return { email: 'Email must be between 3 and 254 characters.' }; - } - - if (!emailRegex.test(email)) { - return { email: 'Email is not valid' }; - } - - return false; -}; - -const username = (username = '') => { - if (username.length < 3 || username.length > 32) { - return { username: 'Username must be between 3 and 32 characters.' }; - } - - if (!usernameRegex.test(username)) { - return { - username: - 'Username can only contain letters, numbers, underscores, and dashes.', - }; - } - - return false; -}; - -const password = (password = '') => { - if (password.length < 8 || password.length > 64) { - return { password: 'Password must be between 8 and 64 characters.' }; - } - - if (!passwordRegex.test(password)) { - return { password: 'Password is not valid' }; - } - - return false; -}; - -const auth = { email, username, password }; - -export default auth; diff --git a/server/utils/validators/index.js b/server/utils/validators/index.js deleted file mode 100644 index 1af23e5..0000000 --- a/server/utils/validators/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import auth from './auth.js'; -import * as monitor from './monitor.js'; -import * as user from './user.js'; - -const validators = { auth, monitor, user }; - -export default validators; diff --git a/server/utils/validators/monitor.js b/server/utils/validators/monitor.js deleted file mode 100644 index f1c7b21..0000000 --- a/server/utils/validators/monitor.js +++ /dev/null @@ -1,121 +0,0 @@ -const validMethods = [ - 'DELETE', - 'GET', - 'HEAD', - 'OPTIONS', - 'PATCH', - 'POST', - 'PUT', -]; - -const validTypes = ['http', 'tcp']; - -const http = ({ - name, - type, - url, - method, - valid_status_codes, - interval, - retryInterval, - requestTimeout, -}) => { - if (!type || !validTypes.includes(type)) { - return 'Please select a valid monitor type.'; - } - - if (!name || !/^[a-zA-Z0-9-]+$/.test(name)) { - return 'Please enter a valid name. Only letters, numbers and - are allowed.'; - } - - const urlRegex = /^(http|https):\/\/[^ "]+$/; - - if (!url || !urlRegex.test(url)) { - return 'Please enter a valid URL.'; - } - - if (!method || !validMethods.includes(method)) { - return 'Please select a valid method.'; - } - if (!valid_status_codes || !valid_status_codes.length) { - return 'Please select at least one status code.'; - } - - if (!interval) { - return 'Please enter a valid interval.'; - } - - if (interval < 20 || interval > 600) { - return 'Please enter a valid interval. Interval should be between 20 and 600 seconds.'; - } - - if (!retryInterval) { - return 'Please enter a valid retry interval.'; - } - - if (retryInterval < 20 || retryInterval > 600) { - return 'Please enter a valid retry interval. Retry interval should be between 20 and 600 seconds.'; - } - - if (!requestTimeout) { - return 'Please enter a valid request timeout.'; - } - - if (requestTimeout < 20 || requestTimeout > 600) { - return 'Please enter a valid request timeout. Request timeout should be between 20 and 600 seconds.'; - } -}; - -const tcp = ({ - type, - name, - url, - port, - interval, - retryInterval, - requestTimeout, -}) => { - if (!type || !validTypes.includes(type)) { - return 'Please select a valid monitor type.'; - } - - if (!name || !/^[a-zA-Z0-9-]+$/.test(name)) { - return 'Please enter a valid name. Only letters, numbers and - are allowed.'; - } - - const isIpv4 = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/; - - if (!url || !isIpv4.test(url)) { - return 'Please enter a valid host (Only IPv4 is valid).'; - } - - if (!port || port < 1 || port > 65535) { - return 'Please enter a valid port.'; - } - - if (!interval) { - return 'Please enter a valid interval.'; - } - - if (interval < 20 || interval > 600) { - return 'Please enter a valid interval. Interval should be between 20 and 600 seconds.'; - } - - if (!retryInterval) { - return 'Please enter a valid retry interval.'; - } - - if (retryInterval < 20 || retryInterval > 600) { - return 'Please enter a valid retry interval. Retry interval should be between 20 and 600 seconds.'; - } - - if (!requestTimeout) { - return 'Please enter a valid request timeout.'; - } - - if (requestTimeout < 20 || requestTimeout > 600) { - return 'Please enter a valid request timeout. Request timeout should be between 20 and 600 seconds.'; - } -}; - -export { http, tcp }; diff --git a/server/utils/validators/user.js b/server/utils/validators/user.js deleted file mode 100644 index fcf8881..0000000 --- a/server/utils/validators/user.js +++ /dev/null @@ -1,41 +0,0 @@ -const defaultAvatars = [ - 'Ape', - 'Bear', - 'Cat', - 'Dog', - 'Doggo', - 'Duck', - 'Eagle', - 'Fox', - 'Gerbil', - 'Hamster', - 'Hedgehog', - 'Koala', - 'Ostrich', - 'Panda', - 'Rabbit', - 'Rocket', - 'Tiger', -]; - -const isImageUrl = (url) => { - if (typeof url !== 'string') { - return false; - } - - return url.match(/^https?:\/\/.+\.(jpg|jpeg|png|gif)$/gim); -}; - -const isAvatar = (avatar) => { - if (avatar === null) { - return false; - } - - if (!defaultAvatars.includes(avatar) && !isImageUrl(avatar)) { - return 'Avatar must be a valid Imgur URL or one of the default avatars.'; - } - - return false; -}; - -export { isAvatar }; From b0494ef28f7d396aaf45c59977feed4a57020efa Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 01:49:40 +0100 Subject: [PATCH 03/11] Moves utils to shared folder --- shared/utils/cookies.js | 42 +++++++++ shared/utils/cron.js | 94 +++++++++++++++++++++ shared/utils/errors.js | 55 ++++++++++++ shared/utils/hashPassword.js | 23 +++++ shared/utils/jwt.js | 43 ++++++++++ shared/utils/logger.js | 40 +++++++++ shared/utils/ms.js | 50 +++++++++++ shared/utils/propTypes.js | 85 +++++++++++++++++++ shared/utils/randomId.js | 7 ++ shared/utils/url.js | 26 ++++++ shared/validators/auth.js | 49 +++++++++++ shared/validators/index.js | 7 ++ shared/validators/monitor.js | 159 +++++++++++++++++++++++++++++++++++ shared/validators/user.js | 41 +++++++++ 14 files changed, 721 insertions(+) create mode 100644 shared/utils/cookies.js create mode 100644 shared/utils/cron.js create mode 100644 shared/utils/errors.js create mode 100644 shared/utils/hashPassword.js create mode 100644 shared/utils/jwt.js create mode 100644 shared/utils/logger.js create mode 100644 shared/utils/ms.js create mode 100644 shared/utils/propTypes.js create mode 100644 shared/utils/randomId.js create mode 100644 shared/utils/url.js create mode 100644 shared/validators/auth.js create mode 100644 shared/validators/index.js create mode 100644 shared/validators/monitor.js create mode 100644 shared/validators/user.js diff --git a/shared/utils/cookies.js b/shared/utils/cookies.js new file mode 100644 index 0000000..b1dc7eb --- /dev/null +++ b/shared/utils/cookies.js @@ -0,0 +1,42 @@ +const thirtyDaysInMilliseconds = 2592000000; + +const setClientSideCookie = (res, name, value) => { + return res.cookie(name, value, { + expires: new Date(Date.now() + thirtyDaysInMilliseconds), + maxAge: thirtyDaysInMilliseconds, + secure: process.env.NODE_ENV === 'production', + sameSite: 'strict', + }); +}; + +const setServerSideCookie = (res, name, value) => { + return res.cookie(name, value, { + expires: new Date(Date.now() + thirtyDaysInMilliseconds), + maxAge: thirtyDaysInMilliseconds, + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + sameSite: 'strict', + }); +}; + +const setDemoCookie = (res, name, value) => { + return res.cookie(name, value, { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + sameSite: 'strict', + }); +}; + +const deleteCookie = (res, name) => { + return res.cookie(name, '', { + expires: -1, + maxAge: -1, + }); +}; + +export { + setClientSideCookie, + setServerSideCookie, + setDemoCookie, + deleteCookie, +}; diff --git a/shared/utils/cron.js b/shared/utils/cron.js new file mode 100644 index 0000000..16f4137 --- /dev/null +++ b/shared/utils/cron.js @@ -0,0 +1,94 @@ +// import dependencies +import { CronJob } from 'cron'; + +// import local files +import cache from '../../server/cache/index.js'; +import logger from './logger.js'; +import { + fetchHeartbeatsByDate, + fetchLastDailyHeartbeat, +} from '../../server/database/queries/heartbeat.js'; + +// fetch all monitors +// fetch only heartbeats that are up for each monitor +// get the average response time for each monitor +// create a new record in the database + +async function initialiseCronJobs() { + logger.info('Cron', 'Initialising cron jobs'); + + new CronJob( + '0 * * * *', + async function () { + logger.info('Cron', 'Running hourly cron job for creating heartbeat'); + + const monitors = await cache.monitors.getKeys(); + + if (monitors.length === 0) { + return; + } + + // the last hour on the hour + const date = Date.now(); + const lastHour = date - 3600000 - (date % 3600000); + + for (const monitorId of monitors) { + const query = await fetchHeartbeatsByDate(monitorId, lastHour); + + if (query.length === 0) { + continue; + } + + const [lastMonitor] = query; + + const upHeartbeats = query.filter((h) => !h.isDown); + + if (upHeartbeats.length === 0) { + continue; + } + + const averageLatency = Math.round( + upHeartbeats.reduce((acc, curr) => acc + curr.latency, 0) / + upHeartbeats.length + ); + + const data = { + monitorId, + date: lastHour, + status: lastMonitor.status, + latency: averageLatency, + }; + + await cache.heartbeats.addHourlyHeartbeat(monitorId, data); + } + }, + null, + true, + 'Europe/London' + ); + + // every 5 minutes + new CronJob( + '*/5 * * * *', + async function () { + logger.info('Cron', 'Running 5 minute cron job for fetching heartbeats'); + + const monitors = await cache.monitors.getKeys(); + + if (monitors.length === 0) { + return; + } + + for (const monitorId of monitors) { + const query = await fetchLastDailyHeartbeat(monitorId); + + cache.heartbeats.addFifthMinuteHeartbeat(monitorId, query); + } + }, + null, + true, + 'Europe/London' + ); +} + +export default initialiseCronJobs; diff --git a/shared/utils/errors.js b/shared/utils/errors.js new file mode 100644 index 0000000..a8d6e62 --- /dev/null +++ b/shared/utils/errors.js @@ -0,0 +1,55 @@ +import logger from './logger.js'; + +class AuthorizationError extends Error { + constructor(error) { + super(); + this.name = 'AuthorizationError'; + this.error = error; + } +} + +class UnprocessableError extends Error { + constructor(error) { + super(); + this.name = 'UnprocessableError'; + this.error = error; + } +} + +class ConflictError extends Error { + constructor(error) { + super(); + this.name = 'ConflictError'; + this.error = error; + } +} + +const handleError = (error, response) => { + logger.error('Error handler', error.message + error.stack); + + if (!response.headersSent) { + if (error instanceof AuthorizationError) { + return response.status(401).send({ + message: error.error, + }); + } + + if (error instanceof ConflictError) { + return response.status(409).send({ + message: error.error, + }); + } + + if (error instanceof UnprocessableError) { + return response.status(422).send({ + message: error.error, + }); + } + + return response.status(500).send({ + message: 'Something went wrong', + }); + } +}; + +export { AuthorizationError, ConflictError, UnprocessableError, handleError }; diff --git a/shared/utils/hashPassword.js b/shared/utils/hashPassword.js new file mode 100644 index 0000000..b60e9cb --- /dev/null +++ b/shared/utils/hashPassword.js @@ -0,0 +1,23 @@ +import { hashSync, compareSync } from 'bcrypt'; +const saltRounds = 10; + +/** + * Hash a password + * @param {string} password + * @returns {string} + */ + +export const generateHash = (password) => { + return hashSync(password, saltRounds); +}; + +/** + * Check if the user password matches the user hash password + * @param {string} password + * @param {string} hashPassword + * @returns {boolean} Returns boolean if password matches + */ + +export const verifyPassword = (password, hashPassword) => { + return compareSync(password, hashPassword); +}; diff --git a/shared/utils/jwt.js b/shared/utils/jwt.js new file mode 100644 index 0000000..5fd5f4e --- /dev/null +++ b/shared/utils/jwt.js @@ -0,0 +1,43 @@ +// import dependencies +import jwt from 'jsonwebtoken'; + +// import local files +import logger from './logger.js'; + +const verifyCookie = (value) => { + try { + const jwtSecret = + process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease'; + + let token = jwt.verify(value, jwtSecret, { + algorithms: ['HS256'], + }); + return token; + } catch (error) { + logger.error('JWT Verify', { + message: error.message, + stack: error.stack, + }); + throw error; + } +}; + +const signCookie = (value) => { + try { + const jwtSecret = + process.env.JWT_SECRET || 'lunalyticsJwtSecretKeyHerePlease'; + + let token = jwt.sign(value, jwtSecret, { + expiresIn: 2592000, + }); + return token; + } catch (error) { + logger.error('JWT Sign', { + message: error.message, + stack: error.stack, + }); + throw error; + } +}; + +export { verifyCookie, signCookie }; diff --git a/shared/utils/logger.js b/shared/utils/logger.js new file mode 100644 index 0000000..69b0e2c --- /dev/null +++ b/shared/utils/logger.js @@ -0,0 +1,40 @@ +class LogMethods { + log(section, msg, level, withDate = true) { + let importance = { + INFO: '\x1b[96m[INFO]\x1b[39m', + WARN: '\x1b[33m[WARN]\x1b[39m', + ERROR: '\x1b[91m[ERROR]\x1b[39m', + }; + const message = `${ + typeof msg === 'string' + ? `${withDate ? `${new Date().toISOString()} ` : ''}[${section}] ${ + importance[level] + }: ${msg}` + : msg + }`; + if (level === 'INFO') { + console.info(message); + } else if (level === 'WARN') { + console.warn(message); + } else if (level === 'ERROR') { + console.error(message); + } else { + console.log(message); + } + } + info(section, msg) { + this.log(section, msg, 'INFO'); + } + + warn(section, msg) { + this.log(section, msg, 'WARN'); + } + + error(section, msg) { + this.log(section, msg, 'ERROR'); + } +} + +const logger = new LogMethods(); + +export default logger; diff --git a/shared/utils/ms.js b/shared/utils/ms.js new file mode 100644 index 0000000..e953de0 --- /dev/null +++ b/shared/utils/ms.js @@ -0,0 +1,50 @@ +const timeToMs = (duration, type = 'hours') => { + const types = { + months: 2592000000, + days: 86400000, + hours: 3600000, + minutes: 60000, + seconds: 1000, + }; + + return duration * types[type]; +}; + +const msToTime = (duration) => { + let seconds = Math.floor((duration / 1000) % 60); + let minutes = Math.floor((duration / (1000 * 60)) % 60); + let hours = Math.floor((duration / (1000 * 60 * 60)) % 24); + let days = Math.floor((duration / (1000 * 60 * 60 * 24)) % 30); + let months = Math.floor((duration / (1000 * 60 * 60 * 24 * 30)) % 12); + let years = Math.floor(duration / (1000 * 60 * 60 * 24 * 30 * 12)); + + // Only return the highest non-zero unit + + if (years > 0) { + return years === 1 ? `${years} year` : `${years} years`; + } + + if (months > 0) { + return months === 1 ? `${months} month` : `${months} months`; + } + + if (days > 0) { + return days === 1 ? `${days} day` : `${days} days`; + } + + if (hours > 0) { + return hours === 1 ? `${hours} hour` : `${hours} hours`; + } + + if (minutes > 0) { + return minutes === 1 ? `${minutes} minute` : `${minutes} minutes`; + } + + if (seconds > 0) { + return seconds === 1 ? `${seconds} second` : `${seconds} seconds`; + } + + return 'Unknown'; +}; + +export { msToTime, timeToMs }; diff --git a/shared/utils/propTypes.js b/shared/utils/propTypes.js new file mode 100644 index 0000000..4b29332 --- /dev/null +++ b/shared/utils/propTypes.js @@ -0,0 +1,85 @@ +// import dependencies +import PropTypes from 'prop-types'; + +const userPropType = PropTypes.shape({ + email: PropTypes.string.isRequired, + displayName: PropTypes.string.isRequired, + avatar: PropTypes.string, + isVerified: PropTypes.number.isRequired, + permission: PropTypes.number.isRequired, + createdAt: PropTypes.string.isRequired, + canEdit: PropTypes.bool, + canManage: PropTypes.bool, +}); + +const monitorPropType = PropTypes.shape({ + monitorId: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + interval: PropTypes.number.isRequired, + retryInterval: PropTypes.number.isRequired, + requestTimeout: PropTypes.number.isRequired, + method: PropTypes.string.isRequired, + headers: PropTypes.string, + body: PropTypes.string, + valid_status_codes: PropTypes.array.isRequired, + email: PropTypes.string.isRequired, + nextCheck: PropTypes.number.isRequired, +}); + +const heartbeatPropType = PropTypes.shape({ + id: PropTypes.number.isRequired, + status: PropTypes.number.isRequired, + latency: PropTypes.number.isRequired, + date: PropTypes.number.isRequired, + isDown: PropTypes.number.isRequired, + message: PropTypes.string.isRequired, +}); + +const certPropType = PropTypes.shape({ + id: PropTypes.number.isRequired, + monitorId: PropTypes.string.isRequired, + isValid: PropTypes.bool.isRequired, + issuer: PropTypes.string.isRequired, + validFrom: PropTypes.string.isRequired, + validTill: PropTypes.string.isRequired, + validOn: PropTypes.string.isRequired, + daysRemaining: PropTypes.number.isRequired, +}); + +const fullMonitorPropType = PropTypes.shape({ + monitorId: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + url: PropTypes.string.isRequired, + interval: PropTypes.number.isRequired, + retryInterval: PropTypes.number.isRequired, + requestTimeout: PropTypes.number.isRequired, + method: PropTypes.string.isRequired, + headers: PropTypes.string, + body: PropTypes.string, + valid_status_codes: PropTypes.array.isRequired, + email: PropTypes.string.isRequired, + cert: certPropType, + heartbeats: PropTypes.arrayOf(heartbeatPropType), +}); + +const colorPropType = PropTypes.oneOf([ + 'blue', + 'cyan', + 'gray', + 'green', + 'pink', + 'primary', + 'purple', + 'red', + 'yellow', +]); + +export { + userPropType, + monitorPropType, + fullMonitorPropType, + heartbeatPropType, + certPropType, + colorPropType, +}; diff --git a/shared/utils/randomId.js b/shared/utils/randomId.js new file mode 100644 index 0000000..954ee44 --- /dev/null +++ b/shared/utils/randomId.js @@ -0,0 +1,7 @@ +import { v4 as uuidv4 } from 'uuid'; + +const randomId = () => { + return uuidv4(); +}; + +export default randomId; diff --git a/shared/utils/url.js b/shared/utils/url.js new file mode 100644 index 0000000..39cfbfd --- /dev/null +++ b/shared/utils/url.js @@ -0,0 +1,26 @@ +const createURL = (path = '', params) => { + if (!path || !params) return path; + + if (path.includes('?')) { + const [pathWithoutQuery, queries] = path.split('?'); + const queriesAsObject = {}; + + queries.split('&').forEach((query) => { + const [key, value] = query.split('='); + queriesAsObject[key] = value; + }); + + const searchParams = new URLSearchParams({ + ...queriesAsObject, + ...params, + }).toString(); + + return `${pathWithoutQuery}?${searchParams}`; + } + + const searchParams = new URLSearchParams(params).toString(); + + return `${path}?${searchParams}`; +}; + +export { createURL }; diff --git a/shared/validators/auth.js b/shared/validators/auth.js new file mode 100644 index 0000000..b9d306e --- /dev/null +++ b/shared/validators/auth.js @@ -0,0 +1,49 @@ +// regex to check user has only letters, numbers, underscore, dash, spaces and should be 3-24 characters long +const usernameRegex = /^[a-zA-Z0-9_\- ]{3,32}$/; +// regex to check if email is valid +const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,254}$/; +// regex to check if one letter, one number or special character, atleast 8 characters long and max of 64 characters long +const passwordRegex = /^(?=.*[a-zA-Z])(?=.*[0-9!@#$%^&*~_\-+=]).{8,64}$/; + +const email = (email = '') => { + if (email.length < 3 || email.length > 254) { + return { email: 'Email must be between 3 and 254 characters.' }; + } + + if (!emailRegex.test(email)) { + return { email: 'Email is not valid' }; + } + + return false; +}; + +const username = (username = '') => { + if (username.length < 3 || username.length > 32) { + return { username: 'Username must be between 3 and 32 characters.' }; + } + + if (!usernameRegex.test(username)) { + return { + username: + 'Username can only contain letters, numbers, underscores, and dashes.', + }; + } + + return false; +}; + +const password = (password = '') => { + if (password.length < 8 || password.length > 64) { + return { password: 'Password must be between 8 and 64 characters.' }; + } + + if (!passwordRegex.test(password)) { + return { password: 'Password is not valid' }; + } + + return false; +}; + +const auth = { email, username, password }; + +export default auth; diff --git a/shared/validators/index.js b/shared/validators/index.js new file mode 100644 index 0000000..1af23e5 --- /dev/null +++ b/shared/validators/index.js @@ -0,0 +1,7 @@ +import auth from './auth.js'; +import * as monitor from './monitor.js'; +import * as user from './user.js'; + +const validators = { auth, monitor, user }; + +export default validators; diff --git a/shared/validators/monitor.js b/shared/validators/monitor.js new file mode 100644 index 0000000..b8fd664 --- /dev/null +++ b/shared/validators/monitor.js @@ -0,0 +1,159 @@ +const validMethods = [ + 'DELETE', + 'GET', + 'HEAD', + 'OPTIONS', + 'PATCH', + 'POST', + 'PUT', +]; + +const validTypes = ['http', 'tcp']; + +export const type = (type) => { + if (!type || !validTypes.includes(type)) { + return 'Please select a valid monitor type.'; + } +}; + +export const name = (name) => { + if (!name || !/^[a-zA-Z0-9-]+$/.test(name)) { + return 'Please enter a valid name. Only letters, numbers and - are allowed.'; + } +}; + +export const httpUrl = (url) => { + const urlRegex = /^(http|https):\/\/[^ "]+$/; + + if (!url || !urlRegex.test(url)) { + return 'Please enter a valid URL.'; + } +}; + +export const httpMethod = (method) => { + if (!method || !validMethods.includes(method)) { + return 'Please select a valid method.'; + } +}; + +export const httpStatusCodes = (codes) => { + if (!codes || !codes.length) { + return 'Please select at least one status code.'; + } +}; + +export const tcpHost = (host) => { + const isIpv4 = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/; + + if (!host || !isIpv4.test(host)) { + return 'Please enter a valid host (Only IPv4 is valid).'; + } +}; + +export const tcpPort = (port) => { + if (!port || port < 1 || port > 65535) { + return 'Please enter a valid port.'; + } +}; + +export const interval = (interval) => { + if (!interval) { + return 'Please enter a valid interval.'; + } + + if (interval < 20 || interval > 600) { + return 'Please enter a valid interval. Interval should be between 20 and 600 seconds.'; + } +}; + +export const retryInterval = (retryInterval) => { + if (!retryInterval) { + return 'Please enter a valid retry interval.'; + } + + if (retryInterval < 20 || retryInterval > 600) { + return 'Please enter a valid retry interval. Retry interval should be between 20 and 600 seconds.'; + } +}; + +export const requestTimeout = (requestTimeout) => { + if (!requestTimeout) { + return 'Please enter a valid request timeout.'; + } + + if (requestTimeout < 20 || requestTimeout > 600) { + return 'Please enter a valid request timeout. Request timeout should be between 20 and 600 seconds.'; + } +}; + +const validators = { + type, + name, + httpUrl, + httpMethod, + httpStatusCodes, + tcpHost, + tcpPort, + interval, + retryInterval, + requestTimeout, +}; + +const httpValidators = [ + ['name', 'name'], + ['type', 'type'], + ['url', 'httpUrl'], + ['method', 'httpMethod'], + ['valid_status_codes', 'httpStatusCodes'], + ['interval', 'interval'], + ['retryInterval', 'retryInterval'], + ['requestTimeout', 'requestTimeout'], +]; + +const tcpValidators = [ + ['name', 'name'], + ['type', 'type'], + ['url', 'tcpHost'], + ['port', 'tcpPort'], + ['interval', 'interval'], + ['retryInterval', 'retryInterval'], + ['requestTimeout', 'requestTimeout'], +]; + +const http = (data) => { + const errors = httpValidators + .map((validator) => { + const error = validators[validator[1]](data[validator[0]]); + if (error) { + return error; + } + }) + .filter(Boolean); + + if (errors.length) { + return errors[0]; + } + + return false; +}; + +const tcp = (data) => { + const errors = tcpValidators + .map((validator) => { + const error = validators[validator[1]](data[validator[0]]); + if (error) { + return error; + } + }) + .filter(Boolean); + + console.log(errors); + + if (errors.length) { + return errors[0]; + } + + return false; +}; + +export default { ...validators, http, tcp }; diff --git a/shared/validators/user.js b/shared/validators/user.js new file mode 100644 index 0000000..538e4e6 --- /dev/null +++ b/shared/validators/user.js @@ -0,0 +1,41 @@ +const defaultAvatars = [ + 'Ape', + 'Bear', + 'Cat', + 'Dog', + 'Doggo', + 'Duck', + 'Eagle', + 'Fox', + 'Gerbil', + 'Hamster', + 'Hedgehog', + 'Koala', + 'Ostrich', + 'Panda', + 'Rabbit', + 'Rocket', + 'Tiger', +]; + +const isImageUrl = (url) => { + if (typeof url !== 'string') { + return false; + } + + return url.match(/^https?:\/\/.+\.(jpg|jpeg|png|gif)$/gim); +}; + +const isAvatar = (avatar) => { + if (avatar === null) { + return false; + } + + if (!defaultAvatars.includes(avatar) && !isImageUrl(avatar)) { + return 'Avatar must be a valid image URL or one of the default avatars.'; + } + + return false; +}; + +export { isAvatar }; From 321c2bb39d45a4e3d0f730c95fb1bcfe6ee87dce Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:38:29 +0100 Subject: [PATCH 04/11] Bumps cypress version --- package-lock.json | 13 ++++++------- package.json | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6946c64..b7c257d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "lunalytics", - "version": "0.5.0", + "version": "0.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "lunalytics", - "version": "0.5.0", + "version": "0.5.1", "license": "SEE LICENSE IN LICENSE", "dependencies": { "axios": "^1.6.2", @@ -40,7 +40,7 @@ "@vitest/coverage-v8": "^1.6.0", "concurrently": "^8.2.2", "cross-env": "^7.0.3", - "cypress": "^13.8.1", + "cypress": "^13.12.0", "eslint": "^8.53.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", @@ -3439,12 +3439,11 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.9.0.tgz", - "integrity": "sha512-atNjmYfHsvTuCaxTxLZr9xGoHz53LLui3266WWxXJHY7+N6OdwJdg/feEa3T+buez9dmUXHT1izCOklqG82uCQ==", + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.12.0.tgz", + "integrity": "sha512-udzS2JilmI9ApO/UuqurEwOvThclin5ntz7K0BtnHBs+tg2Bl9QShLISXpSEMDv/u8b6mqdoAdyKeZiSqKWL8g==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "@cypress/request": "^3.0.0", "@cypress/xvfb": "^1.2.4", diff --git a/package.json b/package.json index 2138304..88d6c78 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@vitest/coverage-v8": "^1.6.0", "concurrently": "^8.2.2", "cross-env": "^7.0.3", - "cypress": "^13.8.1", + "cypress": "^13.12.0", "eslint": "^8.53.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", From 84e1de288676af1b4dfc91a00da87f15abdc9264 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:40:06 +0100 Subject: [PATCH 05/11] Changes path in app components to shared folder instead of utils --- app/components/home/monitor/layout/card.jsx | 2 +- app/components/home/monitor/layout/compact.jsx | 2 +- app/components/home/monitor/layout/list.jsx | 2 +- app/components/modal/settings/account/password.jsx | 2 +- app/components/modal/settings/account/username.jsx | 7 +------ app/components/modal/settings/avatar.jsx | 2 +- app/components/modal/settings/manage/approve.jsx | 2 +- app/components/modal/settings/manage/decline.jsx | 2 +- app/components/modal/settings/manage/delete.jsx | 2 +- app/components/modal/settings/manage/permissions.jsx | 2 +- app/components/monitor/graph/index.jsx | 2 +- app/components/monitor/status.jsx | 2 +- app/components/monitor/updateInfo.jsx | 2 +- app/components/monitor/uptime.jsx | 2 +- app/components/settings/manage/member/actions.jsx | 2 +- app/components/settings/manage/member/index.jsx | 2 +- app/components/settings/manage/member/row.jsx | 2 +- app/components/ui/button.jsx | 2 +- app/components/ui/dropdown/item.jsx | 2 +- app/components/ui/modal/button.jsx | 2 +- app/components/ui/select/item.jsx | 2 +- app/components/ui/statusBar.jsx | 2 +- 22 files changed, 22 insertions(+), 27 deletions(-) diff --git a/app/components/home/monitor/layout/card.jsx b/app/components/home/monitor/layout/card.jsx index 5e4a911..7820bd4 100644 --- a/app/components/home/monitor/layout/card.jsx +++ b/app/components/home/monitor/layout/card.jsx @@ -6,7 +6,7 @@ import { useNavigate } from 'react-router-dom'; // import local files import StatusBar from '../../../ui/statusBar'; import MonitorOptions from '../options'; -import { monitorPropType } from '../../../../utils/propTypes'; +import { monitorPropType } from '../../../../../shared/utils/propTypes'; const MonitorCard = ({ monitor = {} }) => { const navigate = useNavigate(); diff --git a/app/components/home/monitor/layout/compact.jsx b/app/components/home/monitor/layout/compact.jsx index 8714274..878fcec 100644 --- a/app/components/home/monitor/layout/compact.jsx +++ b/app/components/home/monitor/layout/compact.jsx @@ -4,7 +4,7 @@ import './compact.scss'; import { useNavigate } from 'react-router-dom'; // import local files -import { monitorPropType } from '../../../../utils/propTypes'; +import { monitorPropType } from '../../../../../shared/utils/propTypes'; const MonitorList = ({ monitor = {} }) => { const navigate = useNavigate(); diff --git a/app/components/home/monitor/layout/list.jsx b/app/components/home/monitor/layout/list.jsx index cf32f8a..ef7d771 100644 --- a/app/components/home/monitor/layout/list.jsx +++ b/app/components/home/monitor/layout/list.jsx @@ -5,7 +5,7 @@ import { useNavigate } from 'react-router-dom'; // import local files import StatusBar from '../../../ui/statusBar'; -import { monitorPropType } from '../../../../utils/propTypes'; +import { monitorPropType } from '../../../../../shared/utils/propTypes'; const MonitorList = ({ monitor = {} }) => { const navigate = useNavigate(); diff --git a/app/components/modal/settings/account/password.jsx b/app/components/modal/settings/account/password.jsx index a037fc2..b4088f4 100644 --- a/app/components/modal/settings/account/password.jsx +++ b/app/components/modal/settings/account/password.jsx @@ -8,7 +8,7 @@ import RegisterChecklist from '../../../register/checklist'; import { useState } from 'react'; import { MdEye, MdEyeOff } from '../../../icons'; import handleChangePassword from '../../../../handlers/settings/account/password'; -import validators from '../../../../../server/utils/validators'; +import validators from '../../../../../shared/validators'; const SettingsAccountPasswordModal = ({ modalTitle, id, closeModal }) => { const [values, setValues] = useState({ diff --git a/app/components/modal/settings/account/username.jsx b/app/components/modal/settings/account/username.jsx index 9253b04..ebc6f2c 100644 --- a/app/components/modal/settings/account/username.jsx +++ b/app/components/modal/settings/account/username.jsx @@ -28,12 +28,7 @@ const SettingsAccountUsernameModal = ({ const submit = async () => { const value = document.getElementById(`settings-edit-${id}`).value; - const query = await handleChangeUsername( - value, - setError, - closeModal, - handleError - ); + const query = await handleChangeUsername(value, handleError, closeModal); if (query === true) { updateUsingKey('displayName', value); diff --git a/app/components/modal/settings/avatar.jsx b/app/components/modal/settings/avatar.jsx index 79de98b..b59c4e2 100644 --- a/app/components/modal/settings/avatar.jsx +++ b/app/components/modal/settings/avatar.jsx @@ -10,7 +10,7 @@ import { toast } from 'react-toastify'; import Modal from '../../ui/modal'; import useContextStore from '../../../context'; import { createPostRequest } from '../../../services/axios'; -import { userPropType } from '../../../utils/propTypes'; +import { userPropType } from '../../../../shared/utils/propTypes'; const avatars = [ 'Ape', diff --git a/app/components/modal/settings/manage/approve.jsx b/app/components/modal/settings/manage/approve.jsx index 2452a05..3fc1026 100644 --- a/app/components/modal/settings/manage/approve.jsx +++ b/app/components/modal/settings/manage/approve.jsx @@ -7,7 +7,7 @@ import { observer } from 'mobx-react-lite'; import { createPostRequest } from '../../../../services/axios'; import Modal from '../../../ui/modal'; import useTeamContext from '../../../../context/team'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MemberApproveModal = ({ member, onClose }) => { const { updateUserVerified } = useTeamContext(); diff --git a/app/components/modal/settings/manage/decline.jsx b/app/components/modal/settings/manage/decline.jsx index ca3b933..aa3f5cd 100644 --- a/app/components/modal/settings/manage/decline.jsx +++ b/app/components/modal/settings/manage/decline.jsx @@ -6,7 +6,7 @@ import { toast } from 'react-toastify'; import { createPostRequest } from '../../../../services/axios'; import Modal from '../../../ui/modal'; import useTeamContext from '../../../../context/team'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MemberDeclineModal = ({ member, onClose }) => { const { removeUser } = useTeamContext(); diff --git a/app/components/modal/settings/manage/delete.jsx b/app/components/modal/settings/manage/delete.jsx index 76f7dc1..82ff394 100644 --- a/app/components/modal/settings/manage/delete.jsx +++ b/app/components/modal/settings/manage/delete.jsx @@ -6,7 +6,7 @@ import { toast } from 'react-toastify'; import { createPostRequest } from '../../../../services/axios'; import Modal from '../../../ui/modal'; import useTeamContext from '../../../../context/team'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MemberDeleteModal = ({ member, onClose }) => { const { removeUser } = useTeamContext(); diff --git a/app/components/modal/settings/manage/permissions.jsx b/app/components/modal/settings/manage/permissions.jsx index d34a7db..950a92e 100644 --- a/app/components/modal/settings/manage/permissions.jsx +++ b/app/components/modal/settings/manage/permissions.jsx @@ -10,7 +10,7 @@ import classNames from 'classnames'; import { createPostRequest } from '../../../../services/axios'; import Modal from '../../../ui/modal'; import useTeamContext from '../../../../context/team'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MemeberPermission = ({ title, description, isActive, ...props }) => { const containerClasses = classNames('permissions-container', { diff --git a/app/components/monitor/graph/index.jsx b/app/components/monitor/graph/index.jsx index 3e114a7..21f82ca 100644 --- a/app/components/monitor/graph/index.jsx +++ b/app/components/monitor/graph/index.jsx @@ -22,7 +22,7 @@ import { Chart } from 'react-chartjs-2'; import useLocalStorageContext from '../../../hooks/useLocalstorage'; import GraphMenu from './menu'; import useGraphStatus from '../../../hooks/useGraphStatus'; -import { fullMonitorPropType } from '../../../utils/propTypes'; +import { fullMonitorPropType } from '../../../../shared/utils/propTypes'; ChartJs.register( LineController, diff --git a/app/components/monitor/status.jsx b/app/components/monitor/status.jsx index df57563..828ba79 100644 --- a/app/components/monitor/status.jsx +++ b/app/components/monitor/status.jsx @@ -1,7 +1,7 @@ import './status.scss'; // import local files -import { fullMonitorPropType } from '../../utils/propTypes'; +import { fullMonitorPropType } from '../../../shared/utils/propTypes'; const MonitorStatus = ({ monitor = [] }) => { const [lastHeartbeat = {}] = monitor.heartbeats; diff --git a/app/components/monitor/updateInfo.jsx b/app/components/monitor/updateInfo.jsx index 6c70740..2953a39 100644 --- a/app/components/monitor/updateInfo.jsx +++ b/app/components/monitor/updateInfo.jsx @@ -6,7 +6,7 @@ import moment from 'moment'; import classNames from 'classnames'; // import local files -import { heartbeatPropType } from '../../utils/propTypes'; +import { heartbeatPropType } from '../../../shared/utils/propTypes'; import useLocalStorageContext from '../../hooks/useLocalstorage'; const UptimeInfo = ({ heartbeat = {} }) => { diff --git a/app/components/monitor/uptime.jsx b/app/components/monitor/uptime.jsx index 9f2ef52..44ea044 100644 --- a/app/components/monitor/uptime.jsx +++ b/app/components/monitor/uptime.jsx @@ -5,7 +5,7 @@ import 'moment-timezone'; import PropTypes from 'prop-types'; // import local files -import { heartbeatPropType } from '../../utils/propTypes'; +import { heartbeatPropType } from '../../../shared/utils/propTypes'; import UptimeInfo from './updateInfo'; const MonitorUptime = ({ heartbeats = [] }) => { diff --git a/app/components/settings/manage/member/actions.jsx b/app/components/settings/manage/member/actions.jsx index 60f6a27..db80271 100644 --- a/app/components/settings/manage/member/actions.jsx +++ b/app/components/settings/manage/member/actions.jsx @@ -19,7 +19,7 @@ import MemberDeclineModal from '../../../modal/settings/manage/decline'; import MemberDeleteModal from '../../../modal/settings/manage/delete'; import MemberPermissionsModal from '../../../modal/settings/manage/permissions'; import useContextStore from '../../../../context'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MemberRowActions = ({ member = {}, canManage = false }) => { const classes = classNames({ diff --git a/app/components/settings/manage/member/index.jsx b/app/components/settings/manage/member/index.jsx index d8deef1..b7d43f9 100644 --- a/app/components/settings/manage/member/index.jsx +++ b/app/components/settings/manage/member/index.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; // import local files import MemberTableRow from './row'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const MembersTable = ({ members = [] }) => { const membersList = members.map((member, index) => ( diff --git a/app/components/settings/manage/member/row.jsx b/app/components/settings/manage/member/row.jsx index 9cd4c72..1aae89f 100644 --- a/app/components/settings/manage/member/row.jsx +++ b/app/components/settings/manage/member/row.jsx @@ -7,7 +7,7 @@ import { observer } from 'mobx-react-lite'; // import local files import MemberRowActions from './actions'; import useContextStore from '../../../../context'; -import { userPropType } from '../../../../utils/propTypes'; +import { userPropType } from '../../../../../shared/utils/propTypes'; const positions = { 1: 'Owner', 2: 'Admin', 3: 'Editor', 4: 'Guest' }; diff --git a/app/components/ui/button.jsx b/app/components/ui/button.jsx index 5089927..2db3f93 100644 --- a/app/components/ui/button.jsx +++ b/app/components/ui/button.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; // import local files -import { colorPropType } from '../../utils/propTypes'; +import { colorPropType } from '../../../shared/utils/propTypes'; const Button = ({ children, diff --git a/app/components/ui/dropdown/item.jsx b/app/components/ui/dropdown/item.jsx index 1da1870..3b3085d 100644 --- a/app/components/ui/dropdown/item.jsx +++ b/app/components/ui/dropdown/item.jsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; // import local files -import { colorPropType } from '../../../utils/propTypes'; +import { colorPropType } from '../../../../shared/utils/propTypes'; const Item = ({ dotColor, diff --git a/app/components/ui/modal/button.jsx b/app/components/ui/modal/button.jsx index 0ab21c6..67297f8 100644 --- a/app/components/ui/modal/button.jsx +++ b/app/components/ui/modal/button.jsx @@ -3,7 +3,7 @@ import './style.scss'; // import dependencies import PropTypes from 'prop-types'; import classNames from 'classnames'; -import { colorPropType } from '../../../utils/propTypes'; +import { colorPropType } from '../../../../shared/utils/propTypes'; const Button = ({ children, color, ...props }) => { const classes = classNames('modal-button', { diff --git a/app/components/ui/select/item.jsx b/app/components/ui/select/item.jsx index 87d7926..7f9cfaf 100644 --- a/app/components/ui/select/item.jsx +++ b/app/components/ui/select/item.jsx @@ -3,7 +3,7 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; // import local files -import { colorPropType } from '../../../utils/propTypes'; +import { colorPropType } from '../../../../shared/utils/propTypes'; const Item = ({ dotColor, diff --git a/app/components/ui/statusBar.jsx b/app/components/ui/statusBar.jsx index 7a00f3c..6b3257a 100644 --- a/app/components/ui/statusBar.jsx +++ b/app/components/ui/statusBar.jsx @@ -4,7 +4,7 @@ import './statusBar.scss'; import PropTypes from 'prop-types'; // import local files -import { heartbeatPropType } from '../../utils/propTypes'; +import { heartbeatPropType } from '../../../shared/utils/propTypes'; const StatusBar = ({ heartbeats = {}, maxHeartbeats = 12 }) => { const heartbeatList = []; From 3a82a06de263d0ce5a8a73c7bd07ce1eb4c462f4 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:40:45 +0100 Subject: [PATCH 06/11] Changes path for app handlers and hooks to shared folder instead of utils --- app/handlers/login.js | 2 +- app/handlers/register.js | 4 ++-- app/handlers/settings/account/password.js | 4 ++-- app/handlers/settings/account/username.js | 15 +++++---------- app/hooks/useLogin.jsx | 14 ++++++++++---- app/hooks/useMonitorForm.jsx | 2 +- app/hooks/useRegister.jsx | 8 ++++---- app/pages/login.jsx | 4 ++-- 8 files changed, 27 insertions(+), 26 deletions(-) diff --git a/app/handlers/login.js b/app/handlers/login.js index 3b16f4f..58bd44e 100644 --- a/app/handlers/login.js +++ b/app/handlers/login.js @@ -3,7 +3,7 @@ import { toast } from 'react-toastify'; // import local files import { createPostRequest } from '../services/axios'; -import * as validators from '../utils/validators'; +import validators from '../../shared/validators'; const handleLogin = async (inputs, setErrors, navigate) => { try { diff --git a/app/handlers/register.js b/app/handlers/register.js index ddf734b..8f2ef12 100644 --- a/app/handlers/register.js +++ b/app/handlers/register.js @@ -3,7 +3,7 @@ import { toast } from 'react-toastify'; // import local files import { createPostRequest } from '../services/axios'; -import * as validators from '../utils/validators'; +import validators from '../../shared/validators'; const handleRegister = async (inputs, setErrors, setPage, navigate) => { try { @@ -12,7 +12,7 @@ const handleRegister = async (inputs, setErrors, setPage, navigate) => { const isInvalidPassword = validators.auth.password(password); if (isInvalidPassword) { - return setErrors({ password: isInvalidPassword }); + return setErrors(isInvalidPassword); } if (password !== confirmPassword) { diff --git a/app/handlers/settings/account/password.js b/app/handlers/settings/account/password.js index 2e23718..fb97ca0 100644 --- a/app/handlers/settings/account/password.js +++ b/app/handlers/settings/account/password.js @@ -2,7 +2,7 @@ import { toast } from 'react-toastify'; // import local files -import * as validators from '../../../utils/validators'; +import validators from '../../../../shared/validators'; import { createPostRequest } from '../../../services/axios'; const handleChangePassword = async ({ @@ -20,7 +20,7 @@ const handleChangePassword = async ({ const isInvalid = validators.auth.password(newPassword); if (isInvalid) { - return handleErrors('new', isInvalid); + return handleErrors('new', isInvalid.password); } const query = await createPostRequest('/api/user/update/password', { diff --git a/app/handlers/settings/account/username.js b/app/handlers/settings/account/username.js index 6d9f096..e647c08 100644 --- a/app/handlers/settings/account/username.js +++ b/app/handlers/settings/account/username.js @@ -2,20 +2,15 @@ import { toast } from 'react-toastify'; // import local files -import * as validators from '../../../utils/validators'; +import validators from '../../../../shared/validators'; import { createPostRequest } from '../../../services/axios'; -const handleChangeUsername = async ( - displayName, - setError, - closeModal, - handleErrors -) => { +const handleChangeUsername = async (displayName, handleError, closeModal) => { try { const isInvalid = validators.auth.username(displayName); if (isInvalid) { - return setError(isInvalid); + return handleError(isInvalid.username); } const query = await createPostRequest('/api/user/update/username', { @@ -30,11 +25,11 @@ const handleChangeUsername = async ( return true; } catch (error) { if (error.response?.data?.current) { - return handleErrors('current', error.response?.data?.current); + return handleError('current', error.response?.data?.current); } if (error?.response?.status === 400) { - return handleErrors('new', error.response.data?.message); + return handleError('new', error.response.data?.message); } toast.error('Something went wrong, please try again later.'); diff --git a/app/hooks/useLogin.jsx b/app/hooks/useLogin.jsx index 4e0f7c3..1479fc3 100644 --- a/app/hooks/useLogin.jsx +++ b/app/hooks/useLogin.jsx @@ -2,7 +2,7 @@ import { useState } from 'react'; // import local files -import * as validators from '../utils/validators'; +import validators from '../../shared/validators'; const useLogin = () => { const [values, setValues] = useState({ @@ -18,12 +18,18 @@ const useLogin = () => { if (isInvalid) { return setValues((prev) => ({ - ...prev, - errors: { ...prev.errors, [id]: isInvalid }, + inputs: { + ...prev.inputs, + [id]: value, + }, + errors: { ...prev.errors, ...isInvalid }, })); } else { setValues((prev) => ({ - ...prev, + inputs: { + ...prev.inputs, + [id]: value, + }, errors: { ...prev.errors, [id]: null }, })); } diff --git a/app/hooks/useMonitorForm.jsx b/app/hooks/useMonitorForm.jsx index 208e65b..d5103bb 100644 --- a/app/hooks/useMonitorForm.jsx +++ b/app/hooks/useMonitorForm.jsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import handleMonitor from '../handlers/monitor'; -import monitorValidators from '../utils/validators/monitor'; +import monitorValidators from '../../shared/validators/monitor'; const initialPage = { page: 1, diff --git a/app/hooks/useRegister.jsx b/app/hooks/useRegister.jsx index a4b4ce8..95353c3 100644 --- a/app/hooks/useRegister.jsx +++ b/app/hooks/useRegister.jsx @@ -4,7 +4,7 @@ import { toast } from 'react-toastify'; // import local files import { createPostRequest } from '../services/axios'; -import * as validators from '../utils/validators'; +import validators from '../../shared/validators'; const useRegister = () => { const [values, setValues] = useState({ @@ -26,8 +26,8 @@ const useRegister = () => { ...prev, errors: { ...prev.errors, - email: isInvalidEmail, - username: isInvalidUsername, + ...isInvalidEmail, + ...isInvalidUsername, }, })); } @@ -79,7 +79,7 @@ const useRegister = () => { }, errors: { ...prev.errors, - [id]: isInvalid, + ...isInvalid, }, })); } else { diff --git a/app/pages/login.jsx b/app/pages/login.jsx index 088e0d7..7119645 100644 --- a/app/pages/login.jsx +++ b/app/pages/login.jsx @@ -16,9 +16,9 @@ const Login = () => { return (
-
Create your account
+
Signin to Lunalytics
- Please provide your name and email + Please provide your email and password
{errors['general'] && ( From ee6ec9a87f893d80f2a725fa0670454eb6252dc0 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:41:12 +0100 Subject: [PATCH 07/11] Changes path for scripts to shared folder instead of utils --- scripts/loadEnv.js | 2 +- scripts/migrate.js | 2 +- scripts/reset.js | 4 ++-- scripts/setup.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/loadEnv.js b/scripts/loadEnv.js index 1fcceea..3d95360 100644 --- a/scripts/loadEnv.js +++ b/scripts/loadEnv.js @@ -1,6 +1,6 @@ import { existsSync, readFileSync } from 'fs'; import path from 'path'; -import logger from '../server/utils/logger.js'; +import logger from '../shared/utils/logger.js'; const configPath = path.join(process.cwd(), 'config.json'); diff --git a/scripts/migrate.js b/scripts/migrate.js index be67dc1..fb91941 100644 --- a/scripts/migrate.js +++ b/scripts/migrate.js @@ -3,7 +3,7 @@ import fs from 'fs'; import path from 'path'; // import local files -import logger from '../server/utils/logger.js'; +import logger from '../shared/utils/logger.js'; import migrationList from '../server/migrations/index.js'; import { loadJSON } from '../shared/parseJson.js'; diff --git a/scripts/reset.js b/scripts/reset.js index 5987cc7..c93930b 100644 --- a/scripts/reset.js +++ b/scripts/reset.js @@ -4,9 +4,9 @@ import '../scripts/loadEnv.js'; import inquirer from 'inquirer'; // import local files -import logger from '../server/utils/logger.js'; +import logger from '../shared/utils/logger.js'; import SQLite from '../server/database/sqlite/setup.js'; -import { generateHash } from '../server/utils/hashPassword.js'; +import { generateHash } from '../shared/utils/hashPassword.js'; const questions = [ { type: 'input', name: 'email', message: 'Enter email added:' }, diff --git a/scripts/setup.js b/scripts/setup.js index 9a1228e..aba7015 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -5,7 +5,7 @@ import inquirer from 'inquirer'; import { v4 as uuidv4 } from 'uuid'; // import local files -import logger from '../server/utils/logger.js'; +import logger from '../shared/utils/logger.js'; import { loadJSON } from '../shared/parseJson.js'; const packageJson = loadJSON('../package.json'); From 72860ae39fef7d60c2f81e96002b1cc3692808b9 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:41:31 +0100 Subject: [PATCH 08/11] Changes path for server files to shared folder instead of utils --- server/database/queries/monitor.js | 6 +++--- server/database/queries/user.js | 12 +++++++++--- server/database/sqlite/setup.js | 2 +- server/index.js | 4 ++-- server/middleware/auth/login.js | 9 ++++++--- server/middleware/auth/logout.js | 6 +++--- server/middleware/auth/register.js | 9 ++++++--- server/middleware/authorization.js | 2 +- server/middleware/demo.js | 8 ++++++-- server/middleware/monitor/add.js | 7 +++++-- server/middleware/monitor/delete.js | 5 ++++- server/middleware/monitor/edit.js | 7 +++++-- server/middleware/monitor/id.js | 5 ++++- server/middleware/monitor/status.js | 5 ++++- server/middleware/user/access/approveUser.js | 2 +- server/middleware/user/access/declineUser.js | 2 +- server/middleware/user/access/removeUser.js | 2 +- server/middleware/user/deleteAccount.js | 2 +- server/middleware/user/hasAdmin.js | 2 +- server/middleware/user/hasEditor.js | 2 +- server/middleware/user/permission/update.js | 2 +- server/middleware/user/team/members.js | 2 +- server/middleware/user/transferOwnership.js | 2 +- server/middleware/user/update/avatar.js | 4 ++-- server/middleware/user/update/password.js | 6 +++--- server/middleware/user/update/username.js | 4 ++-- server/migrations/tcpUpdate-0-4-0.js | 2 +- server/tools/checkCertificate.js | 2 +- server/tools/httpStatus.js | 2 +- server/tools/tcpPing.js | 2 +- 30 files changed, 79 insertions(+), 48 deletions(-) diff --git a/server/database/queries/monitor.js b/server/database/queries/monitor.js index 93b5f3f..c5dd4f4 100644 --- a/server/database/queries/monitor.js +++ b/server/database/queries/monitor.js @@ -1,7 +1,7 @@ import SQLite from '../sqlite/setup.js'; -import randomId from '../../utils/randomId.js'; -import { timeToMs } from '../../utils/ms.js'; -import { UnprocessableError } from '../../utils/errors.js'; +import randomId from '../../../shared/utils/randomId.js'; +import { timeToMs } from '../../../shared/utils/ms.js'; +import { UnprocessableError } from '../../../shared/utils/errors.js'; const monitorExists = async (monitorId) => { return SQLite.client('monitor').where({ id: monitorId }).first(); diff --git a/server/database/queries/user.js b/server/database/queries/user.js index a65f368..a0674bb 100644 --- a/server/database/queries/user.js +++ b/server/database/queries/user.js @@ -1,7 +1,13 @@ import SQLite from '../sqlite/setup.js'; -import { generateHash, verifyPassword } from '../../utils/hashPassword.js'; -import { signCookie, verifyCookie } from '../../utils/jwt.js'; -import { AuthorizationError, ConflictError } from '../../utils/errors.js'; +import { + generateHash, + verifyPassword, +} from '../../../shared/utils/hashPassword.js'; +import { signCookie, verifyCookie } from '../../../shared/utils/jwt.js'; +import { + AuthorizationError, + ConflictError, +} from '../../../shared/utils/errors.js'; const passwordMatches = (user, password) => { const passwordMatches = verifyPassword(password, user.password); diff --git a/server/database/sqlite/setup.js b/server/database/sqlite/setup.js index 4a4b91d..e56c955 100644 --- a/server/database/sqlite/setup.js +++ b/server/database/sqlite/setup.js @@ -1,7 +1,7 @@ import { existsSync, closeSync, openSync } from 'fs'; import knex from 'knex'; -import logger from '../../utils/logger.js'; +import logger from '../../../shared/utils/logger.js'; export class SQLite { constructor() { diff --git a/server/index.js b/server/index.js index af4d8c5..c6c7949 100644 --- a/server/index.js +++ b/server/index.js @@ -8,10 +8,10 @@ import cookieParser from 'cookie-parser'; // import local files import cache from './cache/index.js'; -import logger from './utils/logger.js'; +import logger from '../shared/utils/logger.js'; import initialiseRoutes from './routes/index.js'; import SQLite from './database/sqlite/setup.js'; -import initialiseCronJobs from './utils/cron.js'; +import initialiseCronJobs from './../shared/utils/cron.js'; import authorization from './middleware/authorization.js'; import migrateDatabase from '../scripts/migrate.js'; import isDemo from './middleware/demo.js'; diff --git a/server/middleware/auth/login.js b/server/middleware/auth/login.js index 90c20f5..9f7f4ce 100644 --- a/server/middleware/auth/login.js +++ b/server/middleware/auth/login.js @@ -1,8 +1,11 @@ // import local files import { signInUser } from '../../database/queries/user.js'; -import { setServerSideCookie } from '../../utils/cookies.js'; -import { handleError, UnprocessableError } from '../../utils/errors.js'; -import validators from '../../utils/validators/index.js'; +import { setServerSideCookie } from '../../../shared/utils/cookies.js'; +import { + handleError, + UnprocessableError, +} from '../../../shared/utils/errors.js'; +import validators from '../../../shared/validators/index.js'; const login = async (request, response) => { try { diff --git a/server/middleware/auth/logout.js b/server/middleware/auth/logout.js index 8923832..4fdd248 100644 --- a/server/middleware/auth/logout.js +++ b/server/middleware/auth/logout.js @@ -1,6 +1,6 @@ -import { deleteCookie } from '../../utils/cookies.js'; -import { handleError } from '../../utils/errors.js'; -import { createURL } from '../../utils/url.js'; +import { deleteCookie } from '../../../shared/utils/cookies.js'; +import { handleError } from '../../../shared/utils/errors.js'; +import { createURL } from '../../../shared/utils/url.js'; const logout = (request, response) => { try { diff --git a/server/middleware/auth/register.js b/server/middleware/auth/register.js index 683bd9b..63844fe 100644 --- a/server/middleware/auth/register.js +++ b/server/middleware/auth/register.js @@ -1,8 +1,11 @@ // import local files import { registerUser, fetchMembers } from '../../database/queries/user.js'; -import { setServerSideCookie } from '../../utils/cookies.js'; -import { handleError, UnprocessableError } from '../../utils/errors.js'; -import validators from '../../utils/validators/index.js'; +import { setServerSideCookie } from '../../../shared/utils/cookies.js'; +import { + handleError, + UnprocessableError, +} from '../../../shared/utils/errors.js'; +import validators from '../../../shared/validators/index.js'; const register = async (request, response) => { try { diff --git a/server/middleware/authorization.js b/server/middleware/authorization.js index 4c7b27e..9d20489 100644 --- a/server/middleware/authorization.js +++ b/server/middleware/authorization.js @@ -1,5 +1,5 @@ import { userExists } from '../database/queries/user.js'; -import { deleteCookie } from '../utils/cookies.js'; +import { deleteCookie } from '../../shared/utils/cookies.js'; const authorization = async (request, response, next) => { const { access_token } = request.cookies; diff --git a/server/middleware/demo.js b/server/middleware/demo.js index ed6c467..8fb20c3 100644 --- a/server/middleware/demo.js +++ b/server/middleware/demo.js @@ -1,10 +1,14 @@ import { getDemoUser } from '../database/queries/user.js'; -import { setDemoCookie } from '../utils/cookies.js'; +import { setDemoCookie } from '../../shared/utils/cookies.js'; const isDemo = async (request, response, next) => { const { access_token } = request.cookies; - if (process.env.IS_DEMO === 'enabled' && !access_token) { + if ( + process.env.NODE_ENV === 'production' && + process.env.IS_DEMO === 'enabled' && + !access_token + ) { if ( !request.url.startsWith('/register') && !request.url.startsWith('/login') diff --git a/server/middleware/monitor/add.js b/server/middleware/monitor/add.js index 0e3cdf4..89dd974 100644 --- a/server/middleware/monitor/add.js +++ b/server/middleware/monitor/add.js @@ -1,6 +1,9 @@ // import local files -import { handleError, UnprocessableError } from '../../utils/errors.js'; -import * as validators from '../../utils/validators/monitor.js'; +import { + handleError, + UnprocessableError, +} from '../../../shared/utils/errors.js'; +import validators from '../../../shared/validators/monitor.js'; import cache from '../../cache/index.js'; import { userExists } from '../../database/queries/user.js'; import { cleanMonitor } from '../../class/monitor.js'; diff --git a/server/middleware/monitor/delete.js b/server/middleware/monitor/delete.js index 1d45498..76ea8f7 100644 --- a/server/middleware/monitor/delete.js +++ b/server/middleware/monitor/delete.js @@ -1,6 +1,9 @@ // import local files import cache from '../../cache/index.js'; -import { UnprocessableError, handleError } from '../../utils/errors.js'; +import { + UnprocessableError, + handleError, +} from '../../../shared/utils/errors.js'; const monitorDelete = async (request, response) => { try { diff --git a/server/middleware/monitor/edit.js b/server/middleware/monitor/edit.js index 73ec1c9..444befa 100644 --- a/server/middleware/monitor/edit.js +++ b/server/middleware/monitor/edit.js @@ -1,6 +1,9 @@ // import local files -import { handleError, UnprocessableError } from '../../utils/errors.js'; -import * as validators from '../../utils/validators/monitor.js'; +import { + handleError, + UnprocessableError, +} from '../../../shared/utils/errors.js'; +import validators from '../../../shared/validators/monitor.js'; import cache from '../../cache/index.js'; import { userExists } from '../../database/queries/user.js'; import { cleanMonitor } from '../../class/monitor.js'; diff --git a/server/middleware/monitor/id.js b/server/middleware/monitor/id.js index 93368e5..9fe1acb 100644 --- a/server/middleware/monitor/id.js +++ b/server/middleware/monitor/id.js @@ -1,6 +1,9 @@ import cache from '../../cache/index.js'; import { cleanMonitor } from '../../class/monitor.js'; -import { UnprocessableError, handleError } from '../../utils/errors.js'; +import { + UnprocessableError, + handleError, +} from '../../../shared/utils/errors.js'; const fetchMonitorUsingId = async (request, response) => { try { diff --git a/server/middleware/monitor/status.js b/server/middleware/monitor/status.js index cc63518..8211217 100644 --- a/server/middleware/monitor/status.js +++ b/server/middleware/monitor/status.js @@ -1,5 +1,8 @@ import cache from '../../cache/index.js'; -import { UnprocessableError, handleError } from '../../utils/errors.js'; +import { + UnprocessableError, + handleError, +} from '../../../shared/utils/errors.js'; const validTypes = ['latest', 'day', 'week', 'month']; const fetchMonitorStatus = async (request, response) => { diff --git a/server/middleware/user/access/approveUser.js b/server/middleware/user/access/approveUser.js index 837e0cc..3f22439 100644 --- a/server/middleware/user/access/approveUser.js +++ b/server/middleware/user/access/approveUser.js @@ -1,5 +1,5 @@ import { approveAccess } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; +import { handleError } from '../../../../shared/utils/errors.js'; const accessApproveMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/access/declineUser.js b/server/middleware/user/access/declineUser.js index ec52398..a8d1a23 100644 --- a/server/middleware/user/access/declineUser.js +++ b/server/middleware/user/access/declineUser.js @@ -1,5 +1,5 @@ import { declineAccess } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; +import { handleError } from '../../../../shared/utils/errors.js'; const accessDeclineMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/access/removeUser.js b/server/middleware/user/access/removeUser.js index 014ee5a..6316757 100644 --- a/server/middleware/user/access/removeUser.js +++ b/server/middleware/user/access/removeUser.js @@ -1,5 +1,5 @@ import { declineAccess } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; +import { handleError } from '../../../../shared/utils/errors.js'; const accessRemoveMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/deleteAccount.js b/server/middleware/user/deleteAccount.js index 975c0cb..d215ca4 100644 --- a/server/middleware/user/deleteAccount.js +++ b/server/middleware/user/deleteAccount.js @@ -1,5 +1,5 @@ import { userExists, declineAccess } from '../../database/queries/user.js'; -import { handleError } from '../../utils/errors.js'; +import { handleError } from '../../../shared/utils/errors.js'; const deleteAccountMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/hasAdmin.js b/server/middleware/user/hasAdmin.js index 7213bf6..8a9e4ac 100644 --- a/server/middleware/user/hasAdmin.js +++ b/server/middleware/user/hasAdmin.js @@ -1,5 +1,5 @@ import { userExists } from '../../database/queries/user.js'; -import { handleError } from '../../utils/errors.js'; +import { handleError } from '../../../shared/utils/errors.js'; const hasAdminPermissions = async (request, response, next) => { try { diff --git a/server/middleware/user/hasEditor.js b/server/middleware/user/hasEditor.js index 78c4d87..0de5e55 100644 --- a/server/middleware/user/hasEditor.js +++ b/server/middleware/user/hasEditor.js @@ -1,5 +1,5 @@ import { userExists } from '../../database/queries/user.js'; -import { handleError } from '../../utils/errors.js'; +import { handleError } from '../../../shared/utils/errors.js'; const hasEditorPermissions = async (request, response, next) => { try { diff --git a/server/middleware/user/permission/update.js b/server/middleware/user/permission/update.js index 17b01bb..8199b15 100644 --- a/server/middleware/user/permission/update.js +++ b/server/middleware/user/permission/update.js @@ -2,7 +2,7 @@ import { userExists, updateUserPermission, } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; +import { handleError } from '../../../../shared/utils/errors.js'; const permissionUpdateMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/team/members.js b/server/middleware/user/team/members.js index 1c1929b..fedc1a4 100644 --- a/server/middleware/user/team/members.js +++ b/server/middleware/user/team/members.js @@ -1,5 +1,5 @@ import { fetchMembers } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; +import { handleError } from '../../../../shared/utils/errors.js'; const teamMembersListMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/transferOwnership.js b/server/middleware/user/transferOwnership.js index afabd1d..1afd4e5 100644 --- a/server/middleware/user/transferOwnership.js +++ b/server/middleware/user/transferOwnership.js @@ -3,7 +3,7 @@ import { transferOwnership, userExists, } from '../../database/queries/user.js'; -import { handleError } from '../../utils/errors.js'; +import { handleError } from '../../../shared/utils/errors.js'; const transferOwnershipMiddleware = async (request, response) => { try { diff --git a/server/middleware/user/update/avatar.js b/server/middleware/user/update/avatar.js index 8eb8fa5..3e5c1b3 100644 --- a/server/middleware/user/update/avatar.js +++ b/server/middleware/user/update/avatar.js @@ -2,8 +2,8 @@ import { userExists, updateUserAvatar, } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; -import validators from '../../../utils/validators/index.js'; +import { handleError } from '../../../../shared/utils/errors.js'; +import validators from '../../../../shared/validators/index.js'; const userUpdateAvatar = async (request, response) => { try { diff --git a/server/middleware/user/update/password.js b/server/middleware/user/update/password.js index 3dc79b1..3281d20 100644 --- a/server/middleware/user/update/password.js +++ b/server/middleware/user/update/password.js @@ -2,9 +2,9 @@ import { updateUserPassword, userExists, } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; -import { verifyPassword } from '../../../utils/hashPassword.js'; -import validators from '../../../utils/validators/index.js'; +import { handleError } from '../../../../shared/utils/errors.js'; +import { verifyPassword } from '../../../../shared/utils/hashPassword.js'; +import validators from '../../../../shared/validators/index.js'; const userUpdatePassword = async (request, response) => { try { diff --git a/server/middleware/user/update/username.js b/server/middleware/user/update/username.js index 8b4946a..7e15f80 100644 --- a/server/middleware/user/update/username.js +++ b/server/middleware/user/update/username.js @@ -2,8 +2,8 @@ import { updateUserDisplayname, userExists, } from '../../../database/queries/user.js'; -import { handleError } from '../../../utils/errors.js'; -import validators from '../../../utils/validators/index.js'; +import { handleError } from '../../../../shared/utils/errors.js'; +import validators from '../../../../shared/validators/index.js'; const userUpdateUsername = async (request, response) => { try { diff --git a/server/migrations/tcpUpdate-0-4-0.js b/server/migrations/tcpUpdate-0-4-0.js index 74bb0d5..02fda38 100644 --- a/server/migrations/tcpUpdate-0-4-0.js +++ b/server/migrations/tcpUpdate-0-4-0.js @@ -1,6 +1,6 @@ // import local files import SQLite from '../database/sqlite/setup.js'; -import logger from '../utils/logger.js'; +import logger from '../../shared/utils/logger.js'; const infomation = { title: 'Support for TCP pings', diff --git a/server/tools/checkCertificate.js b/server/tools/checkCertificate.js index 99b4074..3ebcf02 100644 --- a/server/tools/checkCertificate.js +++ b/server/tools/checkCertificate.js @@ -1,6 +1,6 @@ import https from 'https'; import axios from 'axios'; -import logger from '../utils/logger.js'; +import logger from '../../shared/utils/logger.js'; const getCertInfo = async (url) => { try { diff --git a/server/tools/httpStatus.js b/server/tools/httpStatus.js index f99f2f6..335d933 100644 --- a/server/tools/httpStatus.js +++ b/server/tools/httpStatus.js @@ -2,7 +2,7 @@ import axios from 'axios'; // import local files -import logger from '../utils/logger.js'; +import logger from '../../shared/utils/logger.js'; const httpStatusCheck = async (monitor) => { const options = { diff --git a/server/tools/tcpPing.js b/server/tools/tcpPing.js index 936ad81..7935b68 100644 --- a/server/tools/tcpPing.js +++ b/server/tools/tcpPing.js @@ -2,7 +2,7 @@ import net from 'net'; // import local files -import logger from '../utils/logger.js'; +import logger from '../../shared/utils/logger.js'; const tcpStatusCheck = async (monitor, callback) => { const socket = new net.Socket(); From 2916b39d6913ac28ca86d7b88a3f028c9cff1467 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:41:46 +0100 Subject: [PATCH 09/11] Changes path for tests to shared folder instead of utils --- test/server/middleware/auth/login.test.js | 4 ++-- test/server/middleware/auth/logout.test.js | 6 +++--- test/server/middleware/auth/register.test.js | 4 ++-- test/server/middleware/authorization.test.js | 4 ++-- test/server/middleware/user/access/approve.test.js | 2 +- test/server/middleware/user/access/decline.test.js | 2 +- test/server/middleware/user/access/removeUser.test.js | 2 +- test/server/middleware/user/permission/update.test.js | 2 +- test/server/middleware/user/update/avatar.test.js | 4 ++-- test/server/middleware/user/update/password.test.js | 4 ++-- test/server/middleware/user/update/username.test.js | 4 ++-- test/shared/setupDatabase.js | 4 ++-- 12 files changed, 21 insertions(+), 21 deletions(-) diff --git a/test/server/middleware/auth/login.test.js b/test/server/middleware/auth/login.test.js index 723c40b..ae88592 100644 --- a/test/server/middleware/auth/login.test.js +++ b/test/server/middleware/auth/login.test.js @@ -2,12 +2,12 @@ import { beforeEach, describe, it, vi } from 'vitest'; import { createRequest, createResponse } from 'node-mocks-http'; import SQLite from '../../../../server/database/sqlite/setup'; import login from '../../../../server/middleware/auth/login'; -import { setServerSideCookie } from '../../../../server/utils/cookies'; +import { setServerSideCookie } from '../../../../shared/utils/cookies'; import { signInUser } from '../../../../server/database/queries/user'; vi.mock('../../../../server/database/sqlite/setup'); vi.mock('../../../../server/database/queries/user'); -vi.mock('../../../../server/utils/cookies'); +vi.mock('../../../../shared/utils/cookies'); describe('Login - Middleware', () => { const user = { diff --git a/test/server/middleware/auth/logout.test.js b/test/server/middleware/auth/logout.test.js index c0cdcb5..94efb2e 100644 --- a/test/server/middleware/auth/logout.test.js +++ b/test/server/middleware/auth/logout.test.js @@ -1,9 +1,9 @@ import { createRequest, createResponse } from 'node-mocks-http'; -import { deleteCookie } from '../../../../server/utils/cookies'; +import { deleteCookie } from '../../../../shared/utils/cookies'; import logout from '../../../../server/middleware/auth/logout'; -import { createURL } from '../../../../server/utils/url'; +import { createURL } from '../../../../shared/utils/url'; -vi.mock('../../../../server/utils/cookies'); +vi.mock('../../../../shared/utils/cookies'); describe('Logout - Middleware', () => { let fakeRequest; diff --git a/test/server/middleware/auth/register.test.js b/test/server/middleware/auth/register.test.js index 5e86d90..343b16e 100644 --- a/test/server/middleware/auth/register.test.js +++ b/test/server/middleware/auth/register.test.js @@ -5,10 +5,10 @@ import { fetchMembers, registerUser, } from '../../../../server/database/queries/user'; -import { setServerSideCookie } from '../../../../server/utils/cookies'; +import { setServerSideCookie } from '../../../../shared/utils/cookies'; vi.mock('../../../../server/database/queries/user'); -vi.mock('../../../../server/utils/cookies'); +vi.mock('../../../../shared/utils/cookies'); describe('Register - Middleware', () => { let fakeRequest; diff --git a/test/server/middleware/authorization.test.js b/test/server/middleware/authorization.test.js index 39c23fc..12e57ab 100644 --- a/test/server/middleware/authorization.test.js +++ b/test/server/middleware/authorization.test.js @@ -2,10 +2,10 @@ import { createRequest, createResponse } from 'node-mocks-http'; import { afterEach, beforeEach, describe, it, vi } from 'vitest'; import { userExists } from '../../../server/database/queries/user'; import authorization from '../../../server/middleware/authorization'; -import { deleteCookie } from '../../../server/utils/cookies'; +import { deleteCookie } from '../../../shared/utils/cookies'; vi.mock('../../../server/database/queries/user'); -vi.mock('../../../server/utils/cookies'); +vi.mock('../../../shared/utils/cookies'); describe('Authorization - Middleware', () => { let fakeRequest; diff --git a/test/server/middleware/user/access/approve.test.js b/test/server/middleware/user/access/approve.test.js index c048f46..d382d6a 100644 --- a/test/server/middleware/user/access/approve.test.js +++ b/test/server/middleware/user/access/approve.test.js @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createRequest, createResponse } from 'node-mocks-http'; import { approveAccess } from '../../../../../server/database/queries/user'; import accessApproveMiddleware from '../../../../../server/middleware/user/access/approveUser'; -import { AuthorizationError } from '../../../../../server/utils/errors'; +import { AuthorizationError } from '../../../../../shared/utils/errors'; vi.mock('../../../../../server/database/queries/user'); diff --git a/test/server/middleware/user/access/decline.test.js b/test/server/middleware/user/access/decline.test.js index 3aef60d..8876cea 100644 --- a/test/server/middleware/user/access/decline.test.js +++ b/test/server/middleware/user/access/decline.test.js @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createRequest, createResponse } from 'node-mocks-http'; import { declineAccess } from '../../../../../server/database/queries/user'; -import { AuthorizationError } from '../../../../../server/utils/errors'; +import { AuthorizationError } from '../../../../../shared/utils/errors'; import accessDeclineMiddleware from '../../../../../server/middleware/user/access/declineUser'; vi.mock('../../../../../server/database/queries/user'); diff --git a/test/server/middleware/user/access/removeUser.test.js b/test/server/middleware/user/access/removeUser.test.js index 2b9f7cb..0f9392a 100644 --- a/test/server/middleware/user/access/removeUser.test.js +++ b/test/server/middleware/user/access/removeUser.test.js @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { createRequest, createResponse } from 'node-mocks-http'; import { declineAccess } from '../../../../../server/database/queries/user'; -import { AuthorizationError } from '../../../../../server/utils/errors'; +import { AuthorizationError } from '../../../../../shared/utils/errors'; import accessRemoveMiddleware from '../../../../../server/middleware/user/access/removeUser'; vi.mock('../../../../../server/database/queries/user'); diff --git a/test/server/middleware/user/permission/update.test.js b/test/server/middleware/user/permission/update.test.js index 7420872..b6999e9 100644 --- a/test/server/middleware/user/permission/update.test.js +++ b/test/server/middleware/user/permission/update.test.js @@ -4,7 +4,7 @@ import { userExists, } from '../../../../../server/database/queries/user'; import permissionUpdateMiddleware from '../../../../../server/middleware/user/permission/update'; -import { AuthorizationError } from '../../../../../server/utils/errors'; +import { AuthorizationError } from '../../../../../shared/utils/errors'; vi.mock('../../../../../server/database/queries/user'); diff --git a/test/server/middleware/user/update/avatar.test.js b/test/server/middleware/user/update/avatar.test.js index cfa77bb..37f1910 100644 --- a/test/server/middleware/user/update/avatar.test.js +++ b/test/server/middleware/user/update/avatar.test.js @@ -4,10 +4,10 @@ import { userExists, } from '../../../../../server/database/queries/user'; import userUpdateAvatar from '../../../../../server/middleware/user/update/avatar'; -import validators from '../../../../../server/utils/validators'; +import validators from '../../../../../shared/validators'; vi.mock('../../../../../server/database/queries/user'); -vi.mock('../../../../../server/utils/validators'); +vi.mock('../../../../../shared/validators'); describe('userUpdateAvatar - Middleware', () => { const user = { diff --git a/test/server/middleware/user/update/password.test.js b/test/server/middleware/user/update/password.test.js index 85ca3c7..3cad730 100644 --- a/test/server/middleware/user/update/password.test.js +++ b/test/server/middleware/user/update/password.test.js @@ -4,10 +4,10 @@ import { userExists, } from '../../../../../server/database/queries/user'; import userUpdatePassword from '../../../../../server/middleware/user/update/password'; -import { verifyPassword } from '../../../../../server/utils/hashPassword'; +import { verifyPassword } from '../../../../../shared/utils/hashPassword'; vi.mock('../../../../../server/database/queries/user'); -vi.mock('../../../../../server/utils/hashPassword'); +vi.mock('../../../../../shared/utils/hashPassword'); describe('userUpdatePassword - Middleware', () => { const user = { diff --git a/test/server/middleware/user/update/username.test.js b/test/server/middleware/user/update/username.test.js index 3096ea4..32c50b7 100644 --- a/test/server/middleware/user/update/username.test.js +++ b/test/server/middleware/user/update/username.test.js @@ -4,10 +4,10 @@ import { userExists, } from '../../../../../server/database/queries/user'; import userUpdateUsername from '../../../../../server/middleware/user/update/username'; -import validators from '../../../../../server/utils/validators'; +import validators from '../../../../../shared/validators'; vi.mock('../../../../../server/database/queries/user'); -vi.mock('../../../../../server/utils/validators'); +vi.mock('../../../../../shared/validators'); describe('userUpdateUsername - Middleware', () => { const user = { diff --git a/test/shared/setupDatabase.js b/test/shared/setupDatabase.js index 29ea54e..72b07bb 100644 --- a/test/shared/setupDatabase.js +++ b/test/shared/setupDatabase.js @@ -3,8 +3,8 @@ import fs from 'fs'; // import local files import { SQLite } from '../../server/database/sqlite/setup.js'; -import { generateHash } from '../../server/utils/hashPassword.js'; -import logger from '../../server/utils/logger.js'; +import { generateHash } from '../../shared/utils/hashPassword.js'; +import logger from '../../shared/utils/logger.js'; import { loadJSON } from '../../shared/parseJson.js'; const loginDetails = loadJSON('../test/e2e/setup/fixtures/login.json'); From 7b4a27723929950e539e2ca8805bc50f0e77d478 Mon Sep 17 00:00:00 2001 From: ksjaay Date: Wed, 19 Jun 2024 21:54:57 +0100 Subject: [PATCH 10/11] Updates changelog --- docs/internals/changelog.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/internals/changelog.md b/docs/internals/changelog.md index 6885738..dcda582 100644 --- a/docs/internals/changelog.md +++ b/docs/internals/changelog.md @@ -2,13 +2,24 @@ ## v0.5.1 -### Fixes issue with certificates not being updated +### Fixes issue with certificates and moves utils to shared folder + +### Summary + +There was a lot of duplicate code for utils in the server and app folders. I've decided to create a shared folder which now has all of the code. While moving the files, some of the validators also needed to be reworked so that they can now be used in both the front and backend code. Also, had to fix some issues with certificates not being updated properly. + +### Updates + +- Moves all utils from app and server to shared folder +- Changes file paths from utils to shared folder +- Fixes issues with certificates not being updated properly +- Updates handlers to support updated validator responses ### Summary There was an issue where certificates were not being saved to the database due to the value not being parsed as a string. This has been fixed now. -## v0.5.1 +## v0.5.05 ### Replaces Sonner with React-Toastify From 6110e54fbea65a58147b23000886c8f383ca8b1d Mon Sep 17 00:00:00 2001 From: ksjaay Date: Thu, 4 Jul 2024 22:20:48 +0100 Subject: [PATCH 11/11] Updates change username handler --- app/handlers/settings/account/username.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/handlers/settings/account/username.js b/app/handlers/settings/account/username.js index e647c08..d12d8b6 100644 --- a/app/handlers/settings/account/username.js +++ b/app/handlers/settings/account/username.js @@ -24,12 +24,8 @@ const handleChangeUsername = async (displayName, handleError, closeModal) => { return true; } catch (error) { - if (error.response?.data?.current) { - return handleError('current', error.response?.data?.current); - } - - if (error?.response?.status === 400) { - return handleError('new', error.response.data?.message); + if (error.response?.data?.username) { + return handleError(error.response?.data?.username); } toast.error('Something went wrong, please try again later.');