From 357fce09893b93605b80ce3c50cb285a2e4aeb4e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 13:39:54 -0800 Subject: [PATCH 01/21] comment out ngrok --- Server/index.js | 65 ++++++++++++++++++-------------- Server/service/networkService.js | 1 - 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Server/index.js b/Server/index.js index b1f3f914b..edc4f67f9 100644 --- a/Server/index.js +++ b/Server/index.js @@ -49,7 +49,7 @@ import ping from "ping"; import http from "http"; import Docker from "dockerode"; import net from "net"; -import ngrok from "ngrok"; +// import ngrok from "ngrok"; // Email service and dependencies import EmailService from "./service/emailService.js"; import nodemailer from "nodemailer"; @@ -74,9 +74,9 @@ import MongoDB from "./db/mongo/MongoDB.js"; import IORedis from "ioredis"; -import TranslationService from './service/translationService.js'; -import languageMiddleware from './middleware/languageMiddleware.js'; -import StringService from './service/stringService.js'; +import TranslationService from "./service/translationService.js"; +import languageMiddleware from "./middleware/languageMiddleware.js"; +import StringService from "./service/stringService.js"; const SERVICE_NAME = "Server"; const SHUTDOWN_TIMEOUT = 1000; @@ -136,29 +136,29 @@ const shutdown = async () => { // Need to wrap server setup in a function to handle async nature of JobQueue const startApp = async () => { const app = express(); - if (process.env.NODE_ENV === "development") { - try { - ngrokUrl = await ngrok.connect({ - proto: "http", - addr: PORT, - authtoken: process.env.NGROK_AUTH_TOKEN, - api_addr: false, - }); - process.env.NGROK_URL = ngrokUrl; - logger.info({ - message: `ngrok url: ${ngrokUrl}`, - service: SERVICE_NAME, - method: "startApp", - }); - } catch (error) { - logger.error({ - message: `Error connecting to ngrok`, - service: SERVICE_NAME, - method: "startApp", - stack: error.stack, - }); - } - } + // if (process.env.NODE_ENV === "development") { + // try { + // ngrokUrl = await ngrok.connect({ + // proto: "http", + // addr: PORT, + // authtoken: process.env.NGROK_AUTH_TOKEN, + // api_addr: false, + // }); + // process.env.NGROK_URL = ngrokUrl; + // logger.info({ + // message: `ngrok url: ${ngrokUrl}`, + // service: SERVICE_NAME, + // method: "startApp", + // }); + // } catch (error) { + // logger.error({ + // message: `Error connecting to ngrok`, + // service: SERVICE_NAME, + // method: "startApp", + // stack: error.stack, + // }); + // } + // } // Create and Register Primary services const translationService = new TranslationService(logger); @@ -170,7 +170,15 @@ const startApp = async () => { await db.connect(); // Create services - const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService); + const networkService = new NetworkService( + axios, + ping, + logger, + http, + Docker, + net, + stringService + ); const settingsService = new SettingsService(AppSettings); await settingsService.loadSettings(); const emailService = new EmailService( @@ -185,7 +193,6 @@ const startApp = async () => { const statusService = new StatusService(db, logger); const notificationService = new NotificationService(emailService, db, logger); - const jobQueue = new JobQueue( db, statusService, diff --git a/Server/service/networkService.js b/Server/service/networkService.js index a8599d1f5..fd3bb3748 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -396,7 +396,6 @@ class NetworkService { const CALLBACK_URL = process.env.NGROK_URL ? process.env.NGROK_URL : process.env.CALLBACK_URL; - const response = await this.axios.post( UPROCK_ENDPOINT, { From 9916fba694e07c68ca3a5b8a7420b763d7adb766 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:08:00 -0800 Subject: [PATCH 02/21] restructure users and teams, add teamMember model --- Server/db/models/Team.js | 33 +++++++++++++++++++++++++++++++-- Server/db/models/TeamMember.js | 28 ++++++++++++++++++++++++++++ Server/db/models/User.js | 18 ------------------ 3 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 Server/db/models/TeamMember.js diff --git a/Server/db/models/Team.js b/Server/db/models/Team.js index 95337c201..705274876 100644 --- a/Server/db/models/Team.js +++ b/Server/db/models/Team.js @@ -1,10 +1,39 @@ import mongoose from "mongoose"; + +const SubscriptionSchema = mongoose.Schema( + { + plan: { + type: String, + required: true, + enum: ["free", "basic", "pro", "enterprise"], + default: "free", + }, + maxMembers: { + type: Number, + required: true, + default: 5, + }, + }, + { + timestamps: true, + } +); + const TeamSchema = mongoose.Schema( { - email: { + name: { type: String, required: true, - unique: true, + default: "My Team", + }, + owner: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true, + }, + subscription: { + type: SubscriptionSchema, + required: true, }, }, { diff --git a/Server/db/models/TeamMember.js b/Server/db/models/TeamMember.js new file mode 100644 index 000000000..fb745c2e3 --- /dev/null +++ b/Server/db/models/TeamMember.js @@ -0,0 +1,28 @@ +import mongoose from "mongoose"; + +const TeamMemberSchema = mongoose.Schema( + { + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + required: true, + }, + owner: { + type: mongoose.Schema.Types.ObjectId, + ref: "User", + required: true, + }, + role: { + type: [String], + enum: { + values: ["owner", "admin", "member"], + message: "{VALUE} is not a valid role", + }, + default: ["member"], + }, + }, + { + timestamps: true, + } +); +export default mongoose.model("TeamMember", TeamMemberSchema); diff --git a/Server/db/models/User.js b/Server/db/models/User.js index 3ddc5cee0..b04d918d9 100644 --- a/Server/db/models/User.js +++ b/Server/db/models/User.js @@ -28,24 +28,6 @@ const UserSchema = mongoose.Schema( data: Buffer, contentType: String, }, - isActive: { - type: Boolean, - default: true, - }, - isVerified: { - type: Boolean, - default: false, - }, - role: { - type: [String], - default: "user", - enum: ["user", "admin", "superadmin", "demo"], - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - }, checkTTL: { type: Number, }, From 475faa6f4f7eb599c91391728cae9639fb2a4b9f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:08:15 -0800 Subject: [PATCH 03/21] update validation --- Server/validation/joi.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Server/validation/joi.js b/Server/validation/joi.js index b60022fc5..aead25c4e 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -58,13 +58,13 @@ const registrationBodyValidation = joi.object({ }), password: joi.string().min(8).required().pattern(passwordPattern), profileImage: joi.any(), + teamName: joi.string(), role: joi .array() - .items(joi.string().valid("superadmin", "admin", "user", "demo")) + .items(joi.string().valid("owner", "admin", "user", "demo")) .min(1) .required(), - teamId: joi.string().allow("").required(), - inviteToken: joi.string().allow("").required(), + plan: joi.string().valid("free", "basic", "pro", "enterprise").required(), }); const editUserParamValidation = joi.object({ From 78344f3ff77cd615e53b3cd9c278f709055b074a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:08:36 -0800 Subject: [PATCH 04/21] use registerNewUser --- Server/routes/authRoute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index cebe9a211..fe472c75c 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -18,7 +18,7 @@ class AuthRoutes { this.router.post( "/register", upload.single("profileImage"), - this.authController.registerUser + this.authController.registerNewUser ); this.router.post("/login", this.authController.loginUser); this.router.post("/refresh", this.authController.refreshAuthToken); From fe296906bc8088560decfc86dddd27887b3ca042 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:08:48 -0800 Subject: [PATCH 05/21] Add a team module --- Server/db/mongo/modules/teamModule.js | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Server/db/mongo/modules/teamModule.js diff --git a/Server/db/mongo/modules/teamModule.js b/Server/db/mongo/modules/teamModule.js new file mode 100644 index 000000000..86850bcaf --- /dev/null +++ b/Server/db/mongo/modules/teamModule.js @@ -0,0 +1,28 @@ +import Team from "../../models/Team.js"; +import TeamMember from "../../models/TeamMember.js"; +const SERVICE_NAME = "TeamModule"; + +const insertTeam = async (teamData) => { + try { + console.log(teamData); + const team = await Team.create(teamData); + return team; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "insertTeam"; + throw error; + } +}; + +const insertTeamMember = async (teamMemberData) => { + try { + const teamMember = await TeamMember.create(teamMemberData); + return teamMember; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "insertTeamMember"; + throw error; + } +}; + +export { insertTeam, insertTeamMember }; From 80182da2192232931e98846dc66a5a5183ea77dd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:09:01 -0800 Subject: [PATCH 06/21] add dbUserExists --- Server/service/stringService.js | 696 ++++++++++++++++---------------- 1 file changed, 352 insertions(+), 344 deletions(-) diff --git a/Server/service/stringService.js b/Server/service/stringService.js index 1ef208d6c..a3b4d79eb 100644 --- a/Server/service/stringService.js +++ b/Server/service/stringService.js @@ -1,358 +1,366 @@ class StringService { - static SERVICE_NAME = "StringService"; + static SERVICE_NAME = "StringService"; + + constructor(translationService) { + if (StringService.instance) { + return StringService.instance; + } - constructor(translationService) { - if (StringService.instance) { - return StringService.instance; - } + this.translationService = translationService; + this._language = "en"; // default language + StringService.instance = this; + } + + setLanguage(language) { + this._language = language; + } + + get language() { + return this._language; + } + + //************************************ + // Auth Messages + //************************************ + get dbUserExists() { + return this.translationService.getTranslation("dbUserExists"); + } - this.translationService = translationService; - this._language = 'en'; // default language - StringService.instance = this; - } - - setLanguage(language) { - this._language = language; - } - - get language() { - return this._language; - } - - // Auth Messages - get dontHaveAccount() { - return this.translationService.getTranslation('dontHaveAccount'); - } + get dontHaveAccount() { + return this.translationService.getTranslation("dontHaveAccount"); + } - get email() { - return this.translationService.getTranslation('email'); - } + get email() { + return this.translationService.getTranslation("email"); + } - get forgotPassword() { - return this.translationService.getTranslation('forgotPassword'); - } + get forgotPassword() { + return this.translationService.getTranslation("forgotPassword"); + } - get password() { - return this.translationService.getTranslation('password'); - } + get password() { + return this.translationService.getTranslation("password"); + } - get signUp() { - return this.translationService.getTranslation('signUp'); - } + get signUp() { + return this.translationService.getTranslation("signUp"); + } - get submit() { - return this.translationService.getTranslation('submit'); - } + get submit() { + return this.translationService.getTranslation("submit"); + } - get title() { - return this.translationService.getTranslation('title'); - } - - get continue() { - return this.translationService.getTranslation('continue'); - } - - get enterEmail() { - return this.translationService.getTranslation('enterEmail'); - } - - get authLoginTitle() { - return this.translationService.getTranslation('authLoginTitle'); - } - - get authLoginEnterPassword() { - return this.translationService.getTranslation('authLoginEnterPassword'); - } - - get commonPassword() { - return this.translationService.getTranslation('commonPassword'); - } - - get commonBack() { - return this.translationService.getTranslation('commonBack'); - } - - get authForgotPasswordTitle() { - return this.translationService.getTranslation('authForgotPasswordTitle'); - } - - get authForgotPasswordResetPassword() { - return this.translationService.getTranslation('authForgotPasswordResetPassword'); - } - - get createPassword() { - return this.translationService.getTranslation('createPassword'); - } - - get createAPassword() { - return this.translationService.getTranslation('createAPassword'); - } - - get authRegisterAlreadyHaveAccount() { - return this.translationService.getTranslation('authRegisterAlreadyHaveAccount'); - } - - get commonAppName() { - return this.translationService.getTranslation('commonAppName'); - } - - get authLoginEnterEmail() { - return this.translationService.getTranslation('authLoginEnterEmail'); - } - - get authRegisterTitle() { - return this.translationService.getTranslation('authRegisterTitle'); - } - - get monitorGetAll() { - return this.translationService.getTranslation('monitorGetAll'); - } - - get monitorGetById() { - return this.translationService.getTranslation('monitorGetById'); - } - - get monitorCreate() { - return this.translationService.getTranslation('monitorCreate'); - } - - get monitorEdit() { - return this.translationService.getTranslation('monitorEdit'); - } - - get monitorDelete() { - return this.translationService.getTranslation('monitorDelete'); - } + get title() { + return this.translationService.getTranslation("title"); + } + + get continue() { + return this.translationService.getTranslation("continue"); + } + + get enterEmail() { + return this.translationService.getTranslation("enterEmail"); + } + + get authLoginTitle() { + return this.translationService.getTranslation("authLoginTitle"); + } + + get authLoginEnterPassword() { + return this.translationService.getTranslation("authLoginEnterPassword"); + } + + get commonPassword() { + return this.translationService.getTranslation("commonPassword"); + } + + get commonBack() { + return this.translationService.getTranslation("commonBack"); + } + + get authForgotPasswordTitle() { + return this.translationService.getTranslation("authForgotPasswordTitle"); + } + + get authForgotPasswordResetPassword() { + return this.translationService.getTranslation("authForgotPasswordResetPassword"); + } + + get createPassword() { + return this.translationService.getTranslation("createPassword"); + } + + get createAPassword() { + return this.translationService.getTranslation("createAPassword"); + } + + get authRegisterAlreadyHaveAccount() { + return this.translationService.getTranslation("authRegisterAlreadyHaveAccount"); + } + + get commonAppName() { + return this.translationService.getTranslation("commonAppName"); + } + + get authLoginEnterEmail() { + return this.translationService.getTranslation("authLoginEnterEmail"); + } + + get authRegisterTitle() { + return this.translationService.getTranslation("authRegisterTitle"); + } + + get monitorGetAll() { + return this.translationService.getTranslation("monitorGetAll"); + } + + get monitorGetById() { + return this.translationService.getTranslation("monitorGetById"); + } + + get monitorCreate() { + return this.translationService.getTranslation("monitorCreate"); + } + + get monitorEdit() { + return this.translationService.getTranslation("monitorEdit"); + } - get monitorPause() { - return this.translationService.getTranslation('monitorPause'); - } + get monitorDelete() { + return this.translationService.getTranslation("monitorDelete"); + } - get monitorResume() { - return this.translationService.getTranslation('monitorResume'); - } + get monitorPause() { + return this.translationService.getTranslation("monitorPause"); + } - get monitorDemoAdded() { - return this.translationService.getTranslation('monitorDemoAdded'); - } - - get monitorStatsById() { - return this.translationService.getTranslation('monitorStatsById'); - } - - get monitorCertificate() { - return this.translationService.getTranslation('monitorCertificate'); - } - - // Maintenance Window Messages - get maintenanceWindowCreate() { - return this.translationService.getTranslation('maintenanceWindowCreate'); - } - - get maintenanceWindowGetById() { - return this.translationService.getTranslation('maintenanceWindowGetById'); - } - - get maintenanceWindowGetByTeam() { - return this.translationService.getTranslation('maintenanceWindowGetByTeam'); - } - - get maintenanceWindowDelete() { - return this.translationService.getTranslation('maintenanceWindowDelete'); - } - - get maintenanceWindowEdit() { - return this.translationService.getTranslation('maintenanceWindowEdit'); - } - - // Error Messages - get unknownError() { - return this.translationService.getTranslation('unknownError'); - } - - get friendlyError() { - return this.translationService.getTranslation('friendlyError'); - } - - get authIncorrectPassword() { - return this.translationService.getTranslation('authIncorrectPassword'); - } - - get unauthorized() { - return this.translationService.getTranslation('unauthorized'); - } - - get authAdminExists() { - return this.translationService.getTranslation('authAdminExists'); - } - - get authInviteNotFound() { - return this.translationService.getTranslation('authInviteNotFound'); - } - - get unknownService() { - return this.translationService.getTranslation('unknownService'); - } - - get noAuthToken() { - return this.translationService.getTranslation('noAuthToken'); - } - - get invalidAuthToken() { - return this.translationService.getTranslation('invalidAuthToken'); - } - - get expiredAuthToken() { - return this.translationService.getTranslation('expiredAuthToken'); - } - - // Queue Messages - get queueGetMetrics() { - return this.translationService.getTranslation('queueGetMetrics'); - } - - get queueAddJob() { - return this.translationService.getTranslation('queueAddJob'); - } - - get queueObliterate() { - return this.translationService.getTranslation('queueObliterate'); - } - - // Job Queue Messages - get jobQueueDeleteJobSuccess() { - return this.translationService.getTranslation('jobQueueDeleteJobSuccess'); - } - - get jobQueuePauseJob() { - return this.translationService.getTranslation('jobQueuePauseJob'); - } - - get jobQueueResumeJob() { - return this.translationService.getTranslation('jobQueueResumeJob'); - } - - // Status Page Messages - get statusPageByUrl() { - return this.translationService.getTranslation('statusPageByUrl'); - } - - get statusPageCreate() { - return this.translationService.getTranslation('statusPageCreate'); - } - - get statusPageDelete() { - return this.translationService.getTranslation('statusPageDelete'); - } - - get statusPageUpdate() { - return this.translationService.getTranslation('statusPageUpdate'); - } - - get statusPageNotFound() { - return this.translationService.getTranslation('statusPageNotFound'); - } - - get statusPageByTeamId() { - return this.translationService.getTranslation('statusPageByTeamId'); - } - - get statusPageUrlNotUnique() { - return this.translationService.getTranslation('statusPageUrlNotUnique'); - } - - // Docker Messages - get dockerFail() { - return this.translationService.getTranslation('dockerFail'); - } - - get dockerNotFound() { - return this.translationService.getTranslation('dockerNotFound'); - } - - get dockerSuccess() { - return this.translationService.getTranslation('dockerSuccess'); - } - - // Port Messages - get portFail() { - return this.translationService.getTranslation('portFail'); - } - - get portSuccess() { - return this.translationService.getTranslation('portSuccess'); - } - - // Alert Messages - get alertCreate() { - return this.translationService.getTranslation('alertCreate'); - } - - get alertGetByUser() { - return this.translationService.getTranslation('alertGetByUser'); - } - - get alertGetByMonitor() { - return this.translationService.getTranslation('alertGetByMonitor'); - } - - get alertGetById() { - return this.translationService.getTranslation('alertGetById'); - } - - get alertEdit() { - return this.translationService.getTranslation('alertEdit'); - } - - get alertDelete() { - return this.translationService.getTranslation('alertDelete'); - } - - getDeletedCount(count) { - return this.translationService.getTranslation('deletedCount') - .replace('{count}', count); - } - - get pingSuccess() { - return this.translationService.getTranslation('pingSuccess'); - } - - get getAppSettings() { - return this.translationService.getTranslation('getAppSettings'); - } - - get httpNetworkError() { - return this.translationService.getTranslation('httpNetworkError'); - } - - get httpNotJson() { - return this.translationService.getTranslation('httpNotJson'); - } - - get httpJsonPathError() { - return this.translationService.getTranslation('httpJsonPathError'); - } - - get httpEmptyResult() { - return this.translationService.getTranslation('httpEmptyResult'); - } - - get httpMatchSuccess() { - return this.translationService.getTranslation('httpMatchSuccess'); - } - - get httpMatchFail() { - return this.translationService.getTranslation('httpMatchFail'); - } - - get updateAppSettings() { - return this.translationService.getTranslation('updateAppSettings'); - } - - getDbFindMonitorById(monitorId) { - return this.translationService.getTranslation('dbFindMonitorById') - .replace('${monitorId}', monitorId); - } + get monitorResume() { + return this.translationService.getTranslation("monitorResume"); + } + + get monitorDemoAdded() { + return this.translationService.getTranslation("monitorDemoAdded"); + } + + get monitorStatsById() { + return this.translationService.getTranslation("monitorStatsById"); + } + + get monitorCertificate() { + return this.translationService.getTranslation("monitorCertificate"); + } + + // Maintenance Window Messages + get maintenanceWindowCreate() { + return this.translationService.getTranslation("maintenanceWindowCreate"); + } + + get maintenanceWindowGetById() { + return this.translationService.getTranslation("maintenanceWindowGetById"); + } + + get maintenanceWindowGetByTeam() { + return this.translationService.getTranslation("maintenanceWindowGetByTeam"); + } + + get maintenanceWindowDelete() { + return this.translationService.getTranslation("maintenanceWindowDelete"); + } + + get maintenanceWindowEdit() { + return this.translationService.getTranslation("maintenanceWindowEdit"); + } + + // Error Messages + get unknownError() { + return this.translationService.getTranslation("unknownError"); + } + + get friendlyError() { + return this.translationService.getTranslation("friendlyError"); + } + + get authIncorrectPassword() { + return this.translationService.getTranslation("authIncorrectPassword"); + } + + get unauthorized() { + return this.translationService.getTranslation("unauthorized"); + } + + get authAdminExists() { + return this.translationService.getTranslation("authAdminExists"); + } + + get authInviteNotFound() { + return this.translationService.getTranslation("authInviteNotFound"); + } + + get unknownService() { + return this.translationService.getTranslation("unknownService"); + } + + get noAuthToken() { + return this.translationService.getTranslation("noAuthToken"); + } + + get invalidAuthToken() { + return this.translationService.getTranslation("invalidAuthToken"); + } + + get expiredAuthToken() { + return this.translationService.getTranslation("expiredAuthToken"); + } + + // Queue Messages + get queueGetMetrics() { + return this.translationService.getTranslation("queueGetMetrics"); + } + + get queueAddJob() { + return this.translationService.getTranslation("queueAddJob"); + } + + get queueObliterate() { + return this.translationService.getTranslation("queueObliterate"); + } + + // Job Queue Messages + get jobQueueDeleteJobSuccess() { + return this.translationService.getTranslation("jobQueueDeleteJobSuccess"); + } + + get jobQueuePauseJob() { + return this.translationService.getTranslation("jobQueuePauseJob"); + } + + get jobQueueResumeJob() { + return this.translationService.getTranslation("jobQueueResumeJob"); + } + + // Status Page Messages + get statusPageByUrl() { + return this.translationService.getTranslation("statusPageByUrl"); + } + + get statusPageCreate() { + return this.translationService.getTranslation("statusPageCreate"); + } + + get statusPageDelete() { + return this.translationService.getTranslation("statusPageDelete"); + } + + get statusPageUpdate() { + return this.translationService.getTranslation("statusPageUpdate"); + } + + get statusPageNotFound() { + return this.translationService.getTranslation("statusPageNotFound"); + } + + get statusPageByTeamId() { + return this.translationService.getTranslation("statusPageByTeamId"); + } + + get statusPageUrlNotUnique() { + return this.translationService.getTranslation("statusPageUrlNotUnique"); + } + + // Docker Messages + get dockerFail() { + return this.translationService.getTranslation("dockerFail"); + } + + get dockerNotFound() { + return this.translationService.getTranslation("dockerNotFound"); + } + + get dockerSuccess() { + return this.translationService.getTranslation("dockerSuccess"); + } + + // Port Messages + get portFail() { + return this.translationService.getTranslation("portFail"); + } + + get portSuccess() { + return this.translationService.getTranslation("portSuccess"); + } + + // Alert Messages + get alertCreate() { + return this.translationService.getTranslation("alertCreate"); + } + + get alertGetByUser() { + return this.translationService.getTranslation("alertGetByUser"); + } + + get alertGetByMonitor() { + return this.translationService.getTranslation("alertGetByMonitor"); + } + + get alertGetById() { + return this.translationService.getTranslation("alertGetById"); + } + + get alertEdit() { + return this.translationService.getTranslation("alertEdit"); + } + + get alertDelete() { + return this.translationService.getTranslation("alertDelete"); + } + + getDeletedCount(count) { + return this.translationService + .getTranslation("deletedCount") + .replace("{count}", count); + } + + get pingSuccess() { + return this.translationService.getTranslation("pingSuccess"); + } + + get getAppSettings() { + return this.translationService.getTranslation("getAppSettings"); + } + + get httpNetworkError() { + return this.translationService.getTranslation("httpNetworkError"); + } + + get httpNotJson() { + return this.translationService.getTranslation("httpNotJson"); + } + + get httpJsonPathError() { + return this.translationService.getTranslation("httpJsonPathError"); + } + + get httpEmptyResult() { + return this.translationService.getTranslation("httpEmptyResult"); + } + + get httpMatchSuccess() { + return this.translationService.getTranslation("httpMatchSuccess"); + } + + get httpMatchFail() { + return this.translationService.getTranslation("httpMatchFail"); + } + + get updateAppSettings() { + return this.translationService.getTranslation("updateAppSettings"); + } + + getDbFindMonitorById(monitorId) { + return this.translationService + .getTranslation("dbFindMonitorById") + .replace("${monitorId}", monitorId); + } } -export default StringService; \ No newline at end of file +export default StringService; From 4cbf59abccec0ddb3a0d32d34b6936c6ef441157 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:10:12 -0800 Subject: [PATCH 07/21] update user module --- Server/db/mongo/modules/userModule.js | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js index 4069be8c6..5e94f4729 100644 --- a/Server/db/mongo/modules/userModule.js +++ b/Server/db/mongo/modules/userModule.js @@ -23,7 +23,7 @@ const insertUser = async ( ) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - if (imageFile) { + if (typeof imageFile !== "undefined") { // 1. Save the full size image userData.profileImage = { data: imageFile.buffer, @@ -35,23 +35,18 @@ const insertUser = async ( userData.avatarImage = avatar; } - // Handle creating team if superadmin - if (userData.role.includes("superadmin")) { - const team = new TeamModel({ - email: userData.email, - }); - userData.teamId = team._id; - userData.checkTTL = 60 * 60 * 24 * 30; - await team.save(); - } + const user = await UserModel.create({ + firstName: userData.firstName, + lastName: userData.lastName, + email: userData.email, + password: userData.password, + }); - const newUser = new UserModel(userData); - await newUser.save(); - return await UserModel.findOne({ _id: newUser._id }) - .select("-password") - .select("-profileImage"); // .select() doesn't work with create, need to save then find + user.password = undefined; + user.profileImage = undefined; + return user; } catch (error) { - if (error.code === DUPLICATE_KEY_CODE) { + if (error?.errorResponse?.code === DUPLICATE_KEY_CODE) { error.message = stringService.dbUserExists; } error.service = SERVICE_NAME; From 7a11f20e411111af75d53ffed9aa9e643d39a492 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:10:23 -0800 Subject: [PATCH 08/21] use teammember module --- Server/db/mongo/MongoDB.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js index 8f035c2dc..6c4fae8f9 100644 --- a/Server/db/mongo/MongoDB.js +++ b/Server/db/mongo/MongoDB.js @@ -9,6 +9,12 @@ import logger from "../../utils/logger.js"; import * as userModule from "./modules/userModule.js"; +//**************************************** +// User Operations +//**************************************** + +import * as teamModule from "./modules/teamModule.js"; + //**************************************** // Invite Token Operations //**************************************** @@ -73,6 +79,7 @@ class MongoDB { constructor() { Object.assign(this, userModule); + Object.assign(this, teamModule); Object.assign(this, inviteModule); Object.assign(this, recoveryModule); Object.assign(this, monitorModule); From cb32edd8f508a32899f3510b8ded78d4b9f0a8fa Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 15:11:00 -0800 Subject: [PATCH 09/21] create new registerNewUser method, remove refresh token --- Server/controllers/authController.js | 90 +++++++++++++++++++++------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 94952e3c7..41110449e 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -1,3 +1,7 @@ +import User from "../db/models/User.js"; +import Team from "../db/models/Team.js"; +import TeamMember from "../db/models/TeamMember.js"; + import { registrationBodyValidation, loginValidation, @@ -27,29 +31,76 @@ class AuthController { * Creates and returns JWT token with an arbitrary payload * @function * @param {Object} payload - * @param {tokenType} typeOfToken - Whether to generate refresh token with long TTL or access token with short TTL. * @param {Object} appSettings * @returns {String} * @throws {Error} */ - issueToken = (payload, typeOfToken, appSettings) => { + issueToken = (payload, appSettings) => { try { - const tokenTTL = - typeOfToken === tokenType.REFRESH_TOKEN - ? (appSettings?.refreshTokenTTL ?? "7d") - : (appSettings?.jwtTTL ?? "2h"); - const tokenSecret = - typeOfToken === tokenType.REFRESH_TOKEN - ? appSettings?.refreshTokenSecret - : appSettings?.jwtSecret; - const payloadData = typeOfToken === tokenType.REFRESH_TOKEN ? {} : payload; - - return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL }); + const tokenTTL = appSettings?.jwtTTL ?? "2h"; + const tokenSecret = appSettings?.jwtSecret; + + return jwt.sign(payload, tokenSecret, { expiresIn: tokenTTL }); } catch (error) { throw handleError(error, SERVICE_NAME, "issueToken"); } }; + registerNewUser = async (req, res, next) => { + // !!!! TEMPORARY !!!! + await User.deleteMany({}); + await Team.deleteMany({}); + await TeamMember.deleteMany({}); + + // This method is for a new signup for an Owner + try { + await registrationBodyValidation.validateAsync(req.body); + } catch (error) { + const validationError = handleValidationError(error, SERVICE_NAME); + next(validationError); + return; + } + + try { + const { subscription, teamName, ...user } = req.body; + + // 1. Create a user + const newUser = await this.db.insertUser(user); + + // 2. Create a team + const team = await this.db.insertTeam({ + name: teamName, + owner: newUser._id, + subscription: { + plan: subscription, + maxMembers: 5, + }, + }); + + // 3. Create a team member + const teamMember = await this.db.insertTeamMember({ + teamId: team._id, + owner: newUser._id, + role: ["owner", "admin"], + }); + + const userForToken = { ...newUser._doc }; + delete userForToken.profileImage; + delete userForToken.avatarImage; + + const appSettings = await this.settingsService.getSettings(); + + const token = this.issueToken(userForToken, appSettings); + + res.success({ + msg: this.stringService.authCreateUser, + data: { user: newUser, token: token }, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "registerNewUser")); + } + }; + /** * Registers a new user. If the user is the first account, a JWT secret is created. If not, an invite token is required. * @async @@ -164,14 +215,10 @@ class AuthController { delete userWithoutPassword.password; delete userWithoutPassword.avatarImage; - // Happy path, return token + // We need settings to issue tokens const appSettings = await this.settingsService.getSettings(); - const token = this.issueToken( - userWithoutPassword, - tokenType.ACCESS_TOKEN, - appSettings - ); - const refreshToken = this.issueToken({}, tokenType.REFRESH_TOKEN, appSettings); + + const token = this.issueToken(userWithoutPassword, appSettings); // reset avatar image userWithoutPassword.avatarImage = user.avatarImage; @@ -180,7 +227,6 @@ class AuthController { data: { user: userWithoutPassword, token: token, - refreshToken: refreshToken, }, }); } catch (error) { @@ -200,7 +246,6 @@ class AuthController { * @throws {Error} If there is an error during the process such as any of the token is not received */ refreshAuthToken = async (req, res, next) => { - try { // check for refreshToken const refreshToken = req.headers["x-refresh-token"]; @@ -266,7 +311,6 @@ class AuthController { * @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403). */ editUser = async (req, res, next) => { - try { await editUserParamValidation.validateAsync(req.params); await editUserBodyValidation.validateAsync(req.body); From df7f9048aa2b6cf9f5a602467518e0a1a996696b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 16:37:00 -0800 Subject: [PATCH 10/21] remove required --- Client/src/Components/Inputs/TextInput/index.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Client/src/Components/Inputs/TextInput/index.jsx b/Client/src/Components/Inputs/TextInput/index.jsx index 38a6b6161..3af05e1a2 100644 --- a/Client/src/Components/Inputs/TextInput/index.jsx +++ b/Client/src/Components/Inputs/TextInput/index.jsx @@ -7,6 +7,7 @@ const getSx = (theme, type, maxWidth) => { const sx = { maxWidth: maxWidth, "& .MuiOutlinedInput-root ": { + backgroundColor: theme.palette.background.paper, "&:hover .MuiOutlinedInput-notchedOutline": { borderColor: theme.palette.primary.contrastText, // Adjust hover border color }, @@ -152,7 +153,7 @@ TextInput.displayName = "TextInput"; TextInput.propTypes = { type: PropTypes.string, - id: PropTypes.string.isRequired, + id: PropTypes.string, name: PropTypes.string, value: PropTypes.string, placeholder: PropTypes.string, From 8a4e32745195a7751b3199a10b4c96673f764c38 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 16:37:15 -0800 Subject: [PATCH 11/21] add missing email string --- Client/src/locales/gb.json | 117 +++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/Client/src/locales/gb.json b/Client/src/locales/gb.json index 03dd02c35..0e503d101 100644 --- a/Client/src/locales/gb.json +++ b/Client/src/locales/gb.json @@ -1,59 +1,60 @@ { - "dontHaveAccount": "Don't have account", - "email": "E-mail", - "forgotPassword": "Forgot Password", - "password": "password", - "signUp": "Sign up", - "submit": "Submit", - "title": "Title", - "continue": "Continue", - "enterEmail": "Enter your email", - "authLoginTitle": "Log In", - "authLoginEnterPassword": "Enter your password", - "commonPassword": "Password", - "createPassword": "Create your password", - "createAPassword": "Create a password", - "commonBack": "Back", - "authForgotPasswordTitle": "Forgot password?", - "authForgotPasswordResetPassword": "Reset password", - "authRegisterAlreadyHaveAccount": "Already have an account?", - "commonAppName": "Checkmate", - "authLoginEnterEmail": "Enter your email", - "authRegisterTitle": "Create an account", - "authRegisterStepOneTitle": "Create your account", - "authRegisterStepOneDescription": "Enter your details to get started", - "authRegisterStepTwoTitle": "Set up your profile", - "authRegisterStepTwoDescription": "Tell us more about yourself", - "authRegisterStepThreeTitle": "Almost done!", - "authRegisterStepThreeDescription": "Review your information", - "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", - "authForgotPasswordSendInstructions": "Send instructions", - "authForgotPasswordBackTo": "Back to", - "authCheckEmailTitle": "Check your email", - "authCheckEmailDescription": "We sent a password reset link to {{email}}", - "authCheckEmailResendEmail": "Resend email", - "authCheckEmailBackTo": "Back to", - "goBackTo": "Go back to", - "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", - "authCheckEmailClickToResend": "Click to resend", - "authSetNewPasswordTitle": "Set new password", - "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", - "authSetNewPasswordNewPassword": "New password", - "authSetNewPasswordConfirmPassword": "Confirm password", - "confirmPassword": "Confirm your password", - "authSetNewPasswordResetPassword": "Reset password", - "authSetNewPasswordBackTo": "Back to", - "authPasswordMustBeAtLeast": "Must be at least", - "authPasswordCharactersLong": "8 characters long", - "authPasswordMustContainAtLeast": "Must contain at least", - "authPasswordSpecialCharacter": "one special character", - "authPasswordOneNumber": "one number", - "authPasswordUpperCharacter": "one upper character", - "authPasswordLowerCharacter": "one lower character", - "authPasswordConfirmAndPassword": "Confirm password and password", - "authPasswordMustMatch": "must match", - "authRegisterCreateAccount": "Create your account to get started", - "authRegisterCreateSuperAdminAccount": "Create your Super admin account to get started", - "authRegisterSignUpWithEmail": "Sign up with Email", - "authRegisterBySigningUp": "By signing up, you agree to our" -} \ No newline at end of file + "dontHaveAccount": "Don't have account", + "email": "E-mail", + "forgotPassword": "Forgot Password", + "password": "password", + "signUp": "Sign up", + "submit": "Submit", + "title": "Title", + "continue": "Continue", + "enterEmail": "Enter your email", + "authLoginTitle": "Log In", + "authLoginEnterPassword": "Enter your password", + "commonPassword": "Password", + "createPassword": "Create your password", + "createAPassword": "Create a password", + "commonBack": "Back", + "commonEmail": "Email", + "authForgotPasswordTitle": "Forgot password?", + "authForgotPasswordResetPassword": "Reset password", + "authRegisterAlreadyHaveAccount": "Already have an account?", + "commonAppName": "Checkmate", + "authLoginEnterEmail": "Enter your email", + "authRegisterTitle": "Create an account", + "authRegisterStepOneTitle": "Create your account", + "authRegisterStepOneDescription": "Enter your details to get started", + "authRegisterStepTwoTitle": "Set up your profile", + "authRegisterStepTwoDescription": "Tell us more about yourself", + "authRegisterStepThreeTitle": "Almost done!", + "authRegisterStepThreeDescription": "Review your information", + "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", + "authForgotPasswordSendInstructions": "Send instructions", + "authForgotPasswordBackTo": "Back to", + "authCheckEmailTitle": "Check your email", + "authCheckEmailDescription": "We sent a password reset link to {{email}}", + "authCheckEmailResendEmail": "Resend email", + "authCheckEmailBackTo": "Back to", + "goBackTo": "Go back to", + "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", + "authCheckEmailClickToResend": "Click to resend", + "authSetNewPasswordTitle": "Set new password", + "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", + "authSetNewPasswordNewPassword": "New password", + "authSetNewPasswordConfirmPassword": "Confirm password", + "confirmPassword": "Confirm your password", + "authSetNewPasswordResetPassword": "Reset password", + "authSetNewPasswordBackTo": "Back to", + "authPasswordMustBeAtLeast": "Must be at least", + "authPasswordCharactersLong": "8 characters long", + "authPasswordMustContainAtLeast": "Must contain at least", + "authPasswordSpecialCharacter": "one special character", + "authPasswordOneNumber": "one number", + "authPasswordUpperCharacter": "one upper character", + "authPasswordLowerCharacter": "one lower character", + "authPasswordConfirmAndPassword": "Confirm password and password", + "authPasswordMustMatch": "must match", + "authRegisterCreateAccount": "Create your account to get started", + "authRegisterCreateSuperAdminAccount": "Create your Super admin account to get started", + "authRegisterSignUpWithEmail": "Sign up with Email", + "authRegisterBySigningUp": "By signing up, you agree to our" +} From 85a5a20f79df8e36b3419c05397bcfcd3f704928 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 16:37:30 -0800 Subject: [PATCH 12/21] move old registration page --- Client/src/Routes/index.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Client/src/Routes/index.jsx b/Client/src/Routes/index.jsx index bf88c92f0..b767a8875 100644 --- a/Client/src/Routes/index.jsx +++ b/Client/src/Routes/index.jsx @@ -4,7 +4,8 @@ import NotFound from "../Pages/NotFound"; // Auth import AuthLogin from "../Pages/Auth/Login/Login"; -import AuthRegister from "../Pages/Auth/Register/Register"; +import OldAuthRegister from "../Pages/Auth/Register/Register"; +import AuthRegister from "../Pages/Auth/Register"; import AuthForgotPassword from "../Pages/Auth/ForgotPassword"; import AuthCheckEmail from "../Pages/Auth/CheckEmail"; import AuthSetNewPassword from "../Pages/Auth/SetNewPassword"; @@ -243,6 +244,10 @@ const Routes = () => { path="/register" element={} /> + } + /> Date: Fri, 14 Feb 2025 16:38:01 -0800 Subject: [PATCH 13/21] change return type to array for flexible destructuring --- Client/src/Pages/Auth/hooks/useValidatePassword.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Pages/Auth/hooks/useValidatePassword.jsx b/Client/src/Pages/Auth/hooks/useValidatePassword.jsx index b49d0944b..9339f3882 100644 --- a/Client/src/Pages/Auth/hooks/useValidatePassword.jsx +++ b/Client/src/Pages/Auth/hooks/useValidatePassword.jsx @@ -65,6 +65,6 @@ function useValidatePassword() { [form, errors] ); - return { handleChange, feedbacks, form, errors }; + return [handleChange, feedbacks, form, errors]; } export { useValidatePassword }; From 57584bb9b9e4754f19bc5882fe87e5a531d476cc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 14 Feb 2025 16:38:37 -0800 Subject: [PATCH 14/21] use array destructuring --- .../Pages/Auth/Register/StepThree/index.jsx | 2 +- Client/src/Pages/Auth/Register/index.jsx | 220 ++++++++++++++++++ 2 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 Client/src/Pages/Auth/Register/index.jsx diff --git a/Client/src/Pages/Auth/Register/StepThree/index.jsx b/Client/src/Pages/Auth/Register/StepThree/index.jsx index eb05f843a..99d2e705b 100644 --- a/Client/src/Pages/Auth/Register/StepThree/index.jsx +++ b/Client/src/Pages/Auth/Register/StepThree/index.jsx @@ -31,7 +31,7 @@ function StepThree({ onSubmit, onBack }) { } }, []); - const { handleChange, feedbacks, form, errors } = useValidatePassword(); + const [handleChange, feedbacks, form, errors] = useValidatePassword(); return ( <> { + const theme = useTheme(); + const { t } = useTranslation(); + const dispatch = useDispatch(); + const [handlePasswordChange, feedbacks, passwordForm, passwordErrors] = + useValidatePassword(); + + // Local state + const [form, setForm] = useState({ + firstName: "", + lastName: "", + email: "", + password: "", + confirm: "", + }); + + const [errors, setErrors] = useState({}); + console.log(passwordErrors); + const handleFormChange = (e) => { + const { name, value } = e.target; + // Handle password + if (name === "password" || name === "confirm") { + handlePasswordChange(e); + } + setForm((prev) => ({ ...prev, [name]: value })); + const { error } = credentials.validate({ [name]: value }, { abortEarly: false }); + setErrors((prev) => ({ + ...prev, + ...(error ? { [name]: error.details[0].message } : { [name]: undefined }), + })); + }; + + const handleSubmit = async () => { + const { error } = credentials.validate(form, { + abortEarly: false, + context: { password: form.password }, // Why do we have to do this? + }); + if (error) { + const newErrors = {}; + error.details.forEach((err) => { + newErrors[err.path[0]] = err.message; + }); + setErrors(newErrors); + return; + } + + const registrationForm = { + firstName: form.firstName, + lastName: form.lastName, + email: form.email, + password: form.password, + plan: "free", + role: ["owner", "admin", "user"], + }; + const action = await dispatch(register(registrationForm)); + if (action.payload.success) { + const authToken = action.payload.data; + localStorage.setItem("token", authToken); + createToast({ + body: "Welcome! Your account was created successfully.", + }); + } else { + if (action.payload) { + createToast({ + body: action.payload.msg, + }); + } else { + createToast({ + body: "Unknown error.", + }); + } + } + }; + + return ( + + + Checkmate + + Create your account to get started + + + + + + + + + + + + + + + + + + + ); +}; + +export default Register; From 16b732a56d23e0e28bc53bebbd67b0078cf8a6af Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 08:35:28 -0800 Subject: [PATCH 15/21] remove local storage, add redirect --- Client/src/Pages/Auth/Register/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/src/Pages/Auth/Register/index.jsx b/Client/src/Pages/Auth/Register/index.jsx index 48e547584..56fcc8e0a 100644 --- a/Client/src/Pages/Auth/Register/index.jsx +++ b/Client/src/Pages/Auth/Register/index.jsx @@ -13,9 +13,10 @@ import { useValidatePassword } from "../hooks/useValidatePassword"; import { credentials } from "../../../Validation/validation"; import { useDispatch } from "react-redux"; import { register } from "../../../Features/Auth/authSlice"; - +import { useNavigate } from "react-router-dom"; const Register = () => { const theme = useTheme(); + const navigate = useNavigate(); const { t } = useTranslation(); const dispatch = useDispatch(); const [handlePasswordChange, feedbacks, passwordForm, passwordErrors] = @@ -70,8 +71,7 @@ const Register = () => { }; const action = await dispatch(register(registrationForm)); if (action.payload.success) { - const authToken = action.payload.data; - localStorage.setItem("token", authToken); + navigate("/uptime"); createToast({ body: "Welcome! Your account was created successfully.", }); From 07fdfa7cb0c790774034111e99e79e15ebf1093d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 08:35:38 -0800 Subject: [PATCH 16/21] remove console log --- Server/db/mongo/modules/teamModule.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/db/mongo/modules/teamModule.js b/Server/db/mongo/modules/teamModule.js index 86850bcaf..51870486c 100644 --- a/Server/db/mongo/modules/teamModule.js +++ b/Server/db/mongo/modules/teamModule.js @@ -4,7 +4,6 @@ const SERVICE_NAME = "TeamModule"; const insertTeam = async (teamData) => { try { - console.log(teamData); const team = await Team.create(teamData); return team; } catch (error) { From 9d0472742e690bb8e7ec4127e3c588dbf8c46e19 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 09:11:34 -0800 Subject: [PATCH 17/21] add teams to auth state --- Client/src/Features/Auth/authSlice.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Client/src/Features/Auth/authSlice.js b/Client/src/Features/Auth/authSlice.js index 31c8cb89f..87c444b0b 100644 --- a/Client/src/Features/Auth/authSlice.js +++ b/Client/src/Features/Auth/authSlice.js @@ -7,6 +7,7 @@ const initialState = { isLoading: false, authToken: "", user: "", + teams: [], success: null, msg: null, }; @@ -148,6 +149,7 @@ const handleAuthFulfilled = (state, action) => { state.msg = action.payload.msg; state.authToken = action.payload.data.token; state.user = action.payload.data.user; + state.teams = action.payload.data.teams; }; const handleAuthRejected = (state, action) => { state.isLoading = false; From f2db6058b481d4be777808c1e5e6fe77c0e8b160 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 09:11:54 -0800 Subject: [PATCH 18/21] remove superadmin check --- Client/src/Pages/Auth/Login/Login.jsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Client/src/Pages/Auth/Login/Login.jsx b/Client/src/Pages/Auth/Login/Login.jsx index 60aaed6ba..c500210a4 100644 --- a/Client/src/Pages/Auth/Login/Login.jsx +++ b/Client/src/Pages/Auth/Login/Login.jsx @@ -48,16 +48,6 @@ const Login = () => { navigate("/uptime"); return; } - networkService - .doesSuperAdminExist() - .then((response) => { - if (response.data.data === false) { - navigate("/register"); - } - }) - .catch((error) => { - logger.error(error); - }); }, [authToken, navigate]); const handleChange = (event) => { From 2fc27b9d851fd131b0688a192d66bfeadf70fd8a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 09:12:19 -0800 Subject: [PATCH 19/21] change owner to userId --- Server/db/models/TeamMember.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/db/models/TeamMember.js b/Server/db/models/TeamMember.js index fb745c2e3..eed2e79fc 100644 --- a/Server/db/models/TeamMember.js +++ b/Server/db/models/TeamMember.js @@ -7,7 +7,7 @@ const TeamMemberSchema = mongoose.Schema( ref: "Team", required: true, }, - owner: { + userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true, From 122763bf37a31c0300137f6504dabbc4c66c4681 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 09:12:57 -0800 Subject: [PATCH 20/21] add teams to user --- Server/controllers/authController.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 41110449e..95eb77786 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -80,7 +80,7 @@ class AuthController { // 3. Create a team member const teamMember = await this.db.insertTeamMember({ teamId: team._id, - owner: newUser._id, + userId: newUser._id, role: ["owner", "admin"], }); @@ -94,7 +94,7 @@ class AuthController { res.success({ msg: this.stringService.authCreateUser, - data: { user: newUser, token: token }, + data: { user: newUser, teams: [team._id], token: token }, }); } catch (error) { next(handleError(error, SERVICE_NAME, "registerNewUser")); @@ -222,10 +222,14 @@ class AuthController { // reset avatar image userWithoutPassword.avatarImage = user.avatarImage; + // Get teams + const teams = await this.db.getTeamsByUserId(user._id); + return res.success({ msg: this.stringService.authLoginUser, data: { user: userWithoutPassword, + teams: teams, token: token, }, }); From 6273ce11eb504b9383df0f6e8a489185fdcba91a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sat, 15 Feb 2025 09:13:23 -0800 Subject: [PATCH 21/21] add getTeamsByUserId --- Server/db/mongo/modules/teamModule.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Server/db/mongo/modules/teamModule.js b/Server/db/mongo/modules/teamModule.js index 51870486c..22a39aa99 100644 --- a/Server/db/mongo/modules/teamModule.js +++ b/Server/db/mongo/modules/teamModule.js @@ -24,4 +24,16 @@ const insertTeamMember = async (teamMemberData) => { } }; -export { insertTeam, insertTeamMember }; +const getTeamsByUserId = async (userId) => { + try { + console.log(userId); + const teams = await TeamMember.find({ userId: userId }).select("teamId").lean(); + return teams.map((team) => team.teamId); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getTeamsByUserId"; + throw error; + } +}; + +export { insertTeam, insertTeamMember, getTeamsByUserId };