From faadb22e1193c8b29f42c132ef3c6a994d7a1543 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Thu, 4 Jan 2024 15:35:26 +0530 Subject: [PATCH 01/21] fix: made the RECAPTCHA_SITE_KEY mandatory if RECAPTCHA_SITE_KEY is provided and fixed the duplication of congratulations message --- setup.ts | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/setup.ts b/setup.ts index eb2edd46df..40e1a96adf 100644 --- a/setup.ts +++ b/setup.ts @@ -31,7 +31,7 @@ async function accessAndRefreshTokens( config.ACCESS_TOKEN_SECRET = accessTokenSecret; fs.writeFileSync(".env", ""); for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); + fs.appendFileSync(".env", `${key} = ${config[key]}\n`); } } @@ -247,15 +247,11 @@ async function recaptcha(): Promise { } } async function recaptchaSiteKey(): Promise { - console.log( - "\nPlease visit this URL to set up reCAPTCHA:\n\nhttps://www.google.com/recaptcha/admin/create" - ); - console.log( - '\nSelect reCAPTCHA v2 and the "I`m not a robot" checkbox option' - ); - console.log( - '\nAdd "localhost" in domains and accept the terms, then press submit' - ); + if (process.env.RECAPTCHA_SECRET_KEY) { + console.log( + ` \nreCAPTCHA secret key already exists with the value ${process.env.RECAPTCHA_SECRET_KEY}` + ); + } const { recaptchaSiteKey } = await inquirer.prompt([ { @@ -386,22 +382,17 @@ async function importData(): Promise { (error: { message: string }, stdout: string, stderr: string) => { if (error) { console.error(`Error: ${error.message}`); + console.log("Could not import sample data.\n"); abort(); } if (stderr) { console.error(`Error: ${stderr}`); + console.log("Could not import sample data.\n"); abort(); } console.log(`Output: ${stdout}`); - console.log( - "\nCongratulations! Talawa API has been successfully setup! 🥂🎉" - ); } ); - } else { - console.log( - "\nCongratulations! Talawa API has been successfully setup! 🥂🎉" - ); } } @@ -418,7 +409,7 @@ async function main(): Promise { refreshToken: string | null = ""; if (process.env.ACCESS_TOKEN_SECRET) { console.log( - `\nAccess token secret already exists with the value:\n${process.env.ACCESS_TOKEN_SECRET}` + `\nAccess token secret already exists with the value: \n${process.env.ACCESS_TOKEN_SECRET}` ); } const { shouldGenerateAccessToken } = await inquirer.prompt({ @@ -434,7 +425,7 @@ async function main(): Promise { if (process.env.REFRESH_TOKEN_SECRET) { console.log( - `\nRefresh token secret already exists with the value:\n${process.env.REFRESH_TOKEN_SECRET}` + `\nRefresh token secret already exists with the value: \n${process.env.REFRESH_TOKEN_SECRET}` ); } const { shouldGenerateRefreshToken } = await inquirer.prompt({ @@ -460,7 +451,7 @@ async function main(): Promise { // Redis configuration if (process.env.REDIS_URL) { console.log( - `\nRedis URL already exists with the value:\n${process.env.REDIS_URL}` + ` \nRedis URL already exists with the value: \n${process.env.REDIS_URL}` ); } const { shouldSetRedis } = await inquirer.prompt({ @@ -476,7 +467,7 @@ async function main(): Promise { // MongoDB configuration if (process.env.MONGO_DB_URL) { console.log( - `\nMongoDB URL already exists with the value:\n${process.env.MONGO_DB_URL}` + ` \nMongoDB URL already exists with the value: \n${process.env.MONGO_DB_URL}` ); } const { shouldSetMongoDb } = await inquirer.prompt({ @@ -504,16 +495,6 @@ async function main(): Promise { if (shouldSetRecaptcha) { await recaptcha(); - } - - const { shouldSetRecaptchaSiteKey } = await inquirer.prompt({ - type: "confirm", - name: "shouldSetRecaptchaSiteKey", - message: "Would you like to set up a reCAPTCHA site key?", - default: true, - }); - - if (shouldSetRecaptchaSiteKey) { await recaptchaSiteKey(); } @@ -585,7 +566,7 @@ async function main(): Promise { } console.log( - "\nCongratulations! Talawa API has been successfully setup! 🥂🎉" + "\nCongratulations! Talawa API has been successfully setup! 🥂🎉\n" ); } From 90a27417d4e4496d0f987479dc94a70b641d0c69 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Fri, 5 Jan 2024 01:06:59 +0530 Subject: [PATCH 02/21] fix: The script now verifies and displays if the recaptcha site key is already configured --- setup.ts | 4 ++-- videos/304lmNRim4video.mp4 | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 videos/304lmNRim4video.mp4 diff --git a/setup.ts b/setup.ts index 40e1a96adf..86a9494c69 100644 --- a/setup.ts +++ b/setup.ts @@ -247,9 +247,9 @@ async function recaptcha(): Promise { } } async function recaptchaSiteKey(): Promise { - if (process.env.RECAPTCHA_SECRET_KEY) { + if (process.env.RECAPTCHA_SITE_KEY) { console.log( - ` \nreCAPTCHA secret key already exists with the value ${process.env.RECAPTCHA_SECRET_KEY}` + ` \nreCAPTCHA site key already exists with the value ${process.env.RECAPTCHA_SITE_KEY}` ); } diff --git a/videos/304lmNRim4video.mp4 b/videos/304lmNRim4video.mp4 new file mode 100644 index 0000000000..3f8fe0f88e --- /dev/null +++ b/videos/304lmNRim4video.mp4 @@ -0,0 +1 @@ +T��;�@HN��0��D \ No newline at end of file From 7f8629e62e6601a735038c678db7b9ec50e7f17f Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Fri, 5 Jan 2024 01:09:05 +0530 Subject: [PATCH 03/21] fix: The script now verifies and displays if the recaptcha site key is already configured --- videos/304lmNRim4video.mp4 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 videos/304lmNRim4video.mp4 diff --git a/videos/304lmNRim4video.mp4 b/videos/304lmNRim4video.mp4 deleted file mode 100644 index 3f8fe0f88e..0000000000 --- a/videos/304lmNRim4video.mp4 +++ /dev/null @@ -1 +0,0 @@ -T��;�@HN��0��D \ No newline at end of file From ad78be7aa7d9d9f323d88a0b16476c09970f8f0c Mon Sep 17 00:00:00 2001 From: Neyati <116624667+Doraemon012@users.noreply.github.com> Date: Sat, 6 Jan 2024 13:39:17 +0530 Subject: [PATCH 04/21] fix: Update setup.ts --- setup.ts | 91 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/setup.ts b/setup.ts index 86a9494c69..a9d695f2f8 100644 --- a/setup.ts +++ b/setup.ts @@ -31,7 +31,7 @@ async function accessAndRefreshTokens( config.ACCESS_TOKEN_SECRET = accessTokenSecret; fs.writeFileSync(".env", ""); for (const key in config) { - fs.appendFileSync(".env", `${key} = ${config[key]}\n`); + fs.appendFileSync(".env", `${key}=${config[key]}\n`); } } @@ -214,18 +214,19 @@ async function mongoDB(): Promise { } } +// Function to ask if the user wants to keep the entered values +async function askToKeepValues(): Promise { + const { keepValues } = await inquirer.prompt({ + type: "confirm", + name: "keepValues", + message: `Would you like to keep the entered key? `, + default: true, + }); + return keepValues; +} + //Get recaptcha details async function recaptcha(): Promise { - console.log( - "\nPlease visit this URL to set up reCAPTCHA:\n\nhttps://www.google.com/recaptcha/admin/create" - ); - console.log( - '\nSelect reCAPTCHA v2 and the "I`m not a robot" checkbox option' - ); - console.log( - '\nAdd "localhost" in domains and accept the terms, then press submit' - ); - const { recaptchaSecretKey } = await inquirer.prompt([ { type: "input", @@ -239,24 +240,32 @@ async function recaptcha(): Promise { }, }, ]); - const config = dotenv.parse(fs.readFileSync(".env")); - config.RECAPTCHA_SECRET_KEY = recaptchaSecretKey; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); + + const shouldKeepDetails = await askToKeepValues(); + + if (shouldKeepDetails) { + const config = dotenv.parse(fs.readFileSync(".env")); + config.RECAPTCHA_SECRET_KEY = recaptchaSecretKey; + fs.writeFileSync(".env", ""); + for (const key in config) { + fs.appendFileSync(".env", `${key}=${config[key]}\n`); + } + } else { + await recaptcha(); } } + async function recaptchaSiteKey(): Promise { if (process.env.RECAPTCHA_SITE_KEY) { console.log( - ` \nreCAPTCHA site key already exists with the value ${process.env.RECAPTCHA_SITE_KEY}` + `\nreCAPTCHA site key already exists with the value ${process.env.RECAPTCHA_SITE_KEY}` ); } - const { recaptchaSiteKey } = await inquirer.prompt([ + const { recaptchaSiteKeyInp } = await inquirer.prompt([ { type: "input", - name: "recaptchaSiteKey", + name: "recaptchaSiteKeyInp", message: "Enter your reCAPTCHA site key:", validate: async (input: string): Promise => { if (validateRecaptcha(input)) { @@ -266,11 +275,18 @@ async function recaptchaSiteKey(): Promise { }, }, ]); - const config = dotenv.parse(fs.readFileSync(".env")); - config.RECAPTCHA_SITE_KEY = recaptchaSiteKey; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); + + const shouldKeepDetails = await askToKeepValues(); + + if (shouldKeepDetails) { + const config = dotenv.parse(fs.readFileSync(".env")); + config.RECAPTCHA_SITE_KEY = recaptchaSiteKeyInp; + fs.writeFileSync(".env", ""); + for (const key in config) { + fs.appendFileSync(".env", `${key}=${config[key]}\n`); + } + } else { + await recaptchaSiteKey(); } } @@ -382,18 +398,16 @@ async function importData(): Promise { (error: { message: string }, stdout: string, stderr: string) => { if (error) { console.error(`Error: ${error.message}`); - console.log("Could not import sample data.\n"); abort(); } if (stderr) { console.error(`Error: ${stderr}`); - console.log("Could not import sample data.\n"); abort(); } console.log(`Output: ${stdout}`); } ); - } + } } async function main(): Promise { @@ -409,14 +423,14 @@ async function main(): Promise { refreshToken: string | null = ""; if (process.env.ACCESS_TOKEN_SECRET) { console.log( - `\nAccess token secret already exists with the value: \n${process.env.ACCESS_TOKEN_SECRET}` + `\nAccess token secret already exists with the value:\n${process.env.ACCESS_TOKEN_SECRET}` ); } const { shouldGenerateAccessToken } = await inquirer.prompt({ type: "confirm", name: "shouldGenerateAccessToken", message: "Would you like to generate a new access token secret?", - default: true, + default: process.env.ACCESS_TOKEN_SECRET ? false : true, }); if (shouldGenerateAccessToken) { @@ -425,14 +439,14 @@ async function main(): Promise { if (process.env.REFRESH_TOKEN_SECRET) { console.log( - `\nRefresh token secret already exists with the value: \n${process.env.REFRESH_TOKEN_SECRET}` + `\nRefresh token secret already exists with the value:\n${process.env.REFRESH_TOKEN_SECRET}` ); } const { shouldGenerateRefreshToken } = await inquirer.prompt({ type: "confirm", name: "shouldGenerateRefreshToken", message: "Would you like to generate a new refresh token secret?", - default: true, + default: process.env.REFRESH_TOKEN_SECRET ? false : true, }); if (shouldGenerateRefreshToken) { @@ -451,14 +465,14 @@ async function main(): Promise { // Redis configuration if (process.env.REDIS_URL) { console.log( - ` \nRedis URL already exists with the value: \n${process.env.REDIS_URL}` + `\nRedis URL already exists with the value:\n${process.env.REDIS_URL}` ); } const { shouldSetRedis } = await inquirer.prompt({ type: "confirm", name: "shouldSetRedis", message: "Would you like to set up a Redis URL?", - default: true, + default: process.env.REDIS_URL ? false : true, }); if (shouldSetRedis) { await redisConfiguration(); @@ -467,14 +481,14 @@ async function main(): Promise { // MongoDB configuration if (process.env.MONGO_DB_URL) { console.log( - ` \nMongoDB URL already exists with the value: \n${process.env.MONGO_DB_URL}` + `\nMongoDB URL already exists with the value:\n${process.env.MONGO_DB_URL}` ); } const { shouldSetMongoDb } = await inquirer.prompt({ type: "confirm", name: "shouldSetMongoDb", message: "Would you like to set up a MongoDB URL?", - default: true, + default: process.env.MONGO_DB_URL ? false : true, }); if (shouldSetMongoDb) { @@ -490,7 +504,7 @@ async function main(): Promise { type: "confirm", name: "shouldSetRecaptcha", message: "Would you like to set up a reCAPTCHA secret key?", - default: true, + default: process.env.RECAPTCHA_SECRET_KEY ? false : true, }); if (shouldSetRecaptcha) { @@ -508,6 +522,7 @@ async function main(): Promise { type: "confirm", name: "shouldSetMail", message: "Would you like to setup the mail username and password?", + default: process.env.MAIL_USERNAME ? false : true, }, ]); if (shouldSetMail) { @@ -525,7 +540,7 @@ async function main(): Promise { type: "confirm", name: "shouldSetSuperUserEmail", message: "Would you like to setup a Super Admin email of last resort?", - default: true, + default: process.env.LAST_RESORT_SUPERADMIN_EMAIL ? false : true, }, ]); if (shouldSetSuperUserEmail) { @@ -566,7 +581,7 @@ async function main(): Promise { } console.log( - "\nCongratulations! Talawa API has been successfully setup! 🥂🎉\n" + "\nCongratulations! Talawa API has been successfully setup! 🥂🎉" ); } From d076e4098c4115be26eaa17d3fee09ab7250b46d Mon Sep 17 00:00:00 2001 From: Neyati <116624667+Doraemon012@users.noreply.github.com> Date: Sat, 6 Jan 2024 23:08:39 +0530 Subject: [PATCH 05/21] fix: Updated setup to handel comments and deleted parameters in .env --- setup.ts | 73 +++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 41 deletions(-) diff --git a/setup.ts b/setup.ts index a9d695f2f8..38bf45b64c 100644 --- a/setup.ts +++ b/setup.ts @@ -14,10 +14,28 @@ function checkEnvFile(): void { const envSample = dotenv.parse(fs.readFileSync(".env.sample")); const misplaced = Object.keys(envSample).filter((key) => !(key in env)); if (misplaced.length > 0) { - console.log("Please copy the contents of .env.sample to .env file"); - abort(); + // copy the missing fields from .env.sample to .env + for (const key of misplaced) { + fs.appendFileSync(".env", `${key}=${envSample[key]}\n`); + } + } } +// Update the value of an environment variable in .env file +function updateEnvVariable(config: { [key: string]: string | number }): void { + + const existingContent: string = fs. + readFileSync(".env", 'utf8'); + + let updatedContent: string = existingContent; + for (const key in config) { + const regex: RegExp = new RegExp(`^${key}=.*`, 'gm'); + updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + } + + fs.writeFileSync(".env", updatedContent, 'utf8'); +} + // Generate and update the access and refresh token secrets in .env async function accessAndRefreshTokens( @@ -29,19 +47,13 @@ async function accessAndRefreshTokens( if (accessTokenSecret === null) { accessTokenSecret = cryptolib.randomBytes(32).toString("hex"); config.ACCESS_TOKEN_SECRET = accessTokenSecret; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } if (refreshTokenSecret === null) { refreshTokenSecret = cryptolib.randomBytes(32).toString("hex"); config.REFRESH_TOKEN_SECRET = refreshTokenSecret; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } } @@ -108,10 +120,7 @@ async function redisConfiguration(): Promise { const config = dotenv.parse(fs.readFileSync(".env")); config.REDIS_URL = url; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } catch (err) { console.error(err); abort(); @@ -138,14 +147,9 @@ async function askForSuperAdminEmail(): Promise { async function superAdmin(): Promise { try { const email = await askForSuperAdminEmail(); - const config = dotenv.parse(fs.readFileSync(".env")); - config.LAST_RESORT_SUPERADMIN_EMAIL = email; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } catch (err) { console.log(err); abort(); @@ -204,10 +208,7 @@ async function mongoDB(): Promise { config.MONGO_DB_URL = DB_URL; // Modifying the environment variable to be able to access the url in importData function. process.env.MONGO_DB_URL = DB_URL; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } catch (err) { console.error(err); abort(); @@ -246,10 +247,8 @@ async function recaptcha(): Promise { if (shouldKeepDetails) { const config = dotenv.parse(fs.readFileSync(".env")); config.RECAPTCHA_SECRET_KEY = recaptchaSecretKey; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); + } else { await recaptcha(); } @@ -281,10 +280,7 @@ async function recaptchaSiteKey(): Promise { if (shouldKeepDetails) { const config = dotenv.parse(fs.readFileSync(".env")); config.RECAPTCHA_SITE_KEY = recaptchaSiteKeyInp; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } else { await recaptchaSiteKey(); } @@ -338,10 +334,7 @@ async function twoFactorAuth(): Promise { config.MAIL_USERNAME = email; config.MAIL_PASSWORD = password; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); } //Checks if the data exists and ask for deletion @@ -407,7 +400,7 @@ async function importData(): Promise { console.log(`Output: ${stdout}`); } ); - } + } } async function main(): Promise { @@ -559,10 +552,8 @@ async function main(): Promise { } const config = dotenv.parse(fs.readFileSync(".env")); config.LAST_RESORT_SUPERADMIN_EMAIL = config.MAIL_USERNAME; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); + } if (!isDockerInstallation) { From e3ac11da304dd170185b289ae5468d29643fd207 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Wed, 10 Jan 2024 17:41:23 +0530 Subject: [PATCH 06/21] fix: Made MongoDB and Redis mandatory --- setup.ts | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/setup.ts b/setup.ts index 38bf45b64c..304e3df8ad 100644 --- a/setup.ts +++ b/setup.ts @@ -18,25 +18,21 @@ function checkEnvFile(): void { for (const key of misplaced) { fs.appendFileSync(".env", `${key}=${envSample[key]}\n`); } - } } // Update the value of an environment variable in .env file function updateEnvVariable(config: { [key: string]: string | number }): void { - - const existingContent: string = fs. - readFileSync(".env", 'utf8'); + const existingContent: string = fs.readFileSync(".env", "utf8"); let updatedContent: string = existingContent; for (const key in config) { - const regex: RegExp = new RegExp(`^${key}=.*`, 'gm'); + const regex = new RegExp(`^${key}=.*`, "gm"); updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); } - fs.writeFileSync(".env", updatedContent, 'utf8'); + fs.writeFileSync(".env", updatedContent, "utf8"); } - // Generate and update the access and refresh token secrets in .env async function accessAndRefreshTokens( accessTokenSecret: string | null, @@ -248,7 +244,6 @@ async function recaptcha(): Promise { const config = dotenv.parse(fs.readFileSync(".env")); config.RECAPTCHA_SECRET_KEY = recaptchaSecretKey; updateEnvVariable(config); - } else { await recaptcha(); } @@ -461,15 +456,8 @@ async function main(): Promise { `\nRedis URL already exists with the value:\n${process.env.REDIS_URL}` ); } - const { shouldSetRedis } = await inquirer.prompt({ - type: "confirm", - name: "shouldSetRedis", - message: "Would you like to set up a Redis URL?", - default: process.env.REDIS_URL ? false : true, - }); - if (shouldSetRedis) { - await redisConfiguration(); - } + + await redisConfiguration(); // MongoDB configuration if (process.env.MONGO_DB_URL) { @@ -477,16 +465,8 @@ async function main(): Promise { `\nMongoDB URL already exists with the value:\n${process.env.MONGO_DB_URL}` ); } - const { shouldSetMongoDb } = await inquirer.prompt({ - type: "confirm", - name: "shouldSetMongoDb", - message: "Would you like to set up a MongoDB URL?", - default: process.env.MONGO_DB_URL ? false : true, - }); - if (shouldSetMongoDb) { - await mongoDB(); - } + await mongoDB(); } if (process.env.RECAPTCHA_SECRET_KEY) { console.log( @@ -553,7 +533,6 @@ async function main(): Promise { const config = dotenv.parse(fs.readFileSync(".env")); config.LAST_RESORT_SUPERADMIN_EMAIL = config.MAIL_USERNAME; updateEnvVariable(config); - } if (!isDockerInstallation) { From 919cbe135a82b0c7d79db4071913b9c1bcd3d1e1 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Wed, 10 Jan 2024 18:02:33 +0530 Subject: [PATCH 07/21] fix: Fixed the redis url check functionality --- setup.ts | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/setup.ts b/setup.ts index 304e3df8ad..bcf22d5933 100644 --- a/setup.ts +++ b/setup.ts @@ -56,7 +56,7 @@ async function accessAndRefreshTokens( // Check connection to Redis with the specified URL. async function checkRedisConnection(url: string): Promise { let response = false; - const client = redis.createClient(url); + const client = redis.createClient({ url }); console.log("\nChecking Redis connection...."); @@ -66,8 +66,9 @@ async function checkRedisConnection(url: string): Promise { response = true; } catch (error) { console.log(`\nConnection to Redis failed. Please try again.\n`); + } finally { + client.quit(); } - client.quit(); return response; } @@ -108,15 +109,34 @@ async function redisConfiguration(): Promise { try { let isConnected = false, url = ""; + let host!: string; + let port!: number; + let password!: string; + while (!isConnected) { - const { host, port, password } = await askForRedisUrl(); + const result = await askForRedisUrl(); + host = result.host; + port = result.port; + password = result.password; + url = `redis://${password ? password + "@" : ""}${host}:${port}`; isConnected = await checkRedisConnection(url); } + // Set the Redis parameters in process.env + process.env.REDIS_URL = url; + process.env.REDIS_HOST = host; + process.env.REDIS_PORT = port.toString(); + process.env.REDIS_PASSWORD = password; + + // Update the .env file const config = dotenv.parse(fs.readFileSync(".env")); config.REDIS_URL = url; + config.REDIS_HOST = host; + config.REDIS_PORT = port; + config.REDIS_PASSWORD = password; updateEnvVariable(config); + console.log("\nRedis configuration updated successfully!"); } catch (err) { console.error(err); abort(); @@ -451,10 +471,11 @@ async function main(): Promise { }); if (!isDockerInstallation) { // Redis configuration - if (process.env.REDIS_URL) { - console.log( - `\nRedis URL already exists with the value:\n${process.env.REDIS_URL}` - ); + if (process.env.REDIS_HOST && process.env.REDIS_PORT) { + const url = `redis://${ + process.env.REDIS_PASSWORD ? process.env.REDIS_PASSWORD + "@" : "" + }${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`; + console.log(`\nRedis URL already exists with the value:\n${url}`); } await redisConfiguration(); From 56d76246751418444297b01569be5cfba1531c7c Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Wed, 10 Jan 2024 18:10:51 +0530 Subject: [PATCH 08/21] feat: Added a prompt for node environment --- setup.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/setup.ts b/setup.ts index bcf22d5933..624839b9b8 100644 --- a/setup.ts +++ b/setup.ts @@ -33,6 +33,35 @@ function updateEnvVariable(config: { [key: string]: string | number }): void { fs.writeFileSync(".env", updatedContent, "utf8"); } +// Get the node environment +async function getNodeEnvironment(): Promise { + const { nodeEnv } = await inquirer.prompt([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); + + return nodeEnv; +} + +async function setNodeEnvironment(): Promise { + try { + const nodeEnv = await getNodeEnvironment(); + process.env.NODE_ENV = nodeEnv; + + const config = dotenv.parse(fs.readFileSync(".env")); + config.NODE_ENV = nodeEnv; + updateEnvVariable(config); + } catch (err) { + console.error(err); + abort(); + } +} + // Generate and update the access and refresh token secrets in .env async function accessAndRefreshTokens( accessTokenSecret: string | null, @@ -427,6 +456,11 @@ async function main(): Promise { checkEnvFile(); } + if (process.env.NODE_ENV) { + console.log(`\nNode environment is already set to ${process.env.NODE_ENV}`); + } + await setNodeEnvironment(); + let accessToken: string | null = "", refreshToken: string | null = ""; if (process.env.ACCESS_TOKEN_SECRET) { From bd33c8914418a80e851ca3f4ee8f259937b44af8 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Wed, 10 Jan 2024 18:20:03 +0530 Subject: [PATCH 09/21] feat: Added a warning for LAST_RESORT_SUPERADMIN_EMAIL --- setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.ts b/setup.ts index 624839b9b8..0173c10332 100644 --- a/setup.ts +++ b/setup.ts @@ -179,7 +179,7 @@ async function askForSuperAdminEmail(): Promise { type: "input", name: "email", message: - "Enter the email which you wish to assign as the Super Admin of last resort:", + "Please make sure to register with this email before logging in.\n Enter the email which you wish to assign as the Super Admin of last resort :", validate: (input: string) => isValidEmail(input) || "Invalid email. Please try again.", }, From 6dccc28c2a759bf4d77c118cdd5161d476f8977e Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Thu, 11 Jan 2024 16:20:45 +0530 Subject: [PATCH 10/21] fix: Removed the Redis password being shown on the screen --- setup.ts | 5 +---- videos/CxKJv0SOsUvideo.mp4 | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 videos/CxKJv0SOsUvideo.mp4 diff --git a/setup.ts b/setup.ts index 0173c10332..386697a0bd 100644 --- a/setup.ts +++ b/setup.ts @@ -506,10 +506,7 @@ async function main(): Promise { if (!isDockerInstallation) { // Redis configuration if (process.env.REDIS_HOST && process.env.REDIS_PORT) { - const url = `redis://${ - process.env.REDIS_PASSWORD ? process.env.REDIS_PASSWORD + "@" : "" - }${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`; - console.log(`\nRedis URL already exists with the value:\n${url}`); + console.log(`\nRedis URL already exists`); } await redisConfiguration(); diff --git a/videos/CxKJv0SOsUvideo.mp4 b/videos/CxKJv0SOsUvideo.mp4 new file mode 100644 index 0000000000..3f8fe0f88e --- /dev/null +++ b/videos/CxKJv0SOsUvideo.mp4 @@ -0,0 +1 @@ +T��;�@HN��0��D \ No newline at end of file From c5f0074e097866684a128d82d95e01a7740166fe Mon Sep 17 00:00:00 2001 From: Neyati <116624667+Doraemon012@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:25:42 +0530 Subject: [PATCH 11/21] Delete additional video --- videos/CxKJv0SOsUvideo.mp4 | 1 - 1 file changed, 1 deletion(-) delete mode 100644 videos/CxKJv0SOsUvideo.mp4 diff --git a/videos/CxKJv0SOsUvideo.mp4 b/videos/CxKJv0SOsUvideo.mp4 deleted file mode 100644 index 3f8fe0f88e..0000000000 --- a/videos/CxKJv0SOsUvideo.mp4 +++ /dev/null @@ -1 +0,0 @@ -T��;�@HN��0��D \ No newline at end of file From 72659ba4c41a99d96872b4b2e255c51573f250a2 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Sat, 13 Jan 2024 14:14:08 +0530 Subject: [PATCH 12/21] feat: Added confirmation prompts for Mongo DB and Redis --- setup.ts | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/setup.ts b/setup.ts index 386697a0bd..e5a2b710c4 100644 --- a/setup.ts +++ b/setup.ts @@ -507,18 +507,40 @@ async function main(): Promise { // Redis configuration if (process.env.REDIS_HOST && process.env.REDIS_PORT) { console.log(`\nRedis URL already exists`); - } - await redisConfiguration(); + const { shouldSetupRedis } = await inquirer.prompt({ + type: "confirm", + name: "shouldSetupRedis", + message: "Would you like to change the existing Redis URL?", + default: false, + }); + + if (shouldSetupRedis) { + await redisConfiguration(); + } + } else { + await redisConfiguration(); + } // MongoDB configuration if (process.env.MONGO_DB_URL) { console.log( `\nMongoDB URL already exists with the value:\n${process.env.MONGO_DB_URL}` ); - } - await mongoDB(); + const { shouldSetupMongo } = await inquirer.prompt({ + type: "confirm", + name: "shouldSetupMongo", + message: "Would you like to change the existing Mongo DB URL?", + default: false, + }); + + if (shouldSetupMongo) { + await mongoDB(); + } + } else { + await mongoDB(); + } } if (process.env.RECAPTCHA_SECRET_KEY) { console.log( From a0d003d18d16095bb0f04ca7a3de3cbe183a8c41 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Sat, 13 Jan 2024 14:40:38 +0530 Subject: [PATCH 13/21] feat: Added comments for TsDoc --- setup.ts | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/setup.ts b/setup.ts index e5a2b710c4..d5a912b40b 100644 --- a/setup.ts +++ b/setup.ts @@ -9,6 +9,10 @@ const { exec } = require("child_process"); dotenv.config(); // Check if all the fields in .env.sample are present in .env +/** + * The function `checkEnvFile` checks if any fields are missing in the .env file compared to the .env.sample file, and + * if so, it copies the missing fields from .env.sample to .env. + */ function checkEnvFile(): void { const env = dotenv.parse(fs.readFileSync(".env")); const envSample = dotenv.parse(fs.readFileSync(".env.sample")); @@ -20,7 +24,15 @@ function checkEnvFile(): void { } } } + // Update the value of an environment variable in .env file +/** + * The function `updateEnvVariable` updates the values of environment variables in a .env file based on the provided + * configuration object. + * @param config - An object that contains key-value pairs where the keys are strings and the values + * can be either strings or numbers. These key-value pairs represent the environment variables that + * need to be updated. + */ function updateEnvVariable(config: { [key: string]: string | number }): void { const existingContent: string = fs.readFileSync(".env", "utf8"); @@ -34,6 +46,11 @@ function updateEnvVariable(config: { [key: string]: string | number }): void { } // Get the node environment +/** + * The function `getNodeEnvironment` is an asynchronous function that prompts the user to select a Node + * environment (either "development" or "production") and returns the selected environment as a string. + * @returns a Promise that resolves to a string representing the selected Node environment. + */ async function getNodeEnvironment(): Promise { const { nodeEnv } = await inquirer.prompt([ { @@ -48,6 +65,10 @@ async function getNodeEnvironment(): Promise { return nodeEnv; } +/** + * The function `setNodeEnvironment` sets the Node environment by reading the value from a file, updating the process + * environment variable, and updating a configuration file. + */ async function setNodeEnvironment(): Promise { try { const nodeEnv = await getNodeEnvironment(); @@ -63,6 +84,16 @@ async function setNodeEnvironment(): Promise { } // Generate and update the access and refresh token secrets in .env +/** + * The function `accessAndRefreshTokens` generates and updates access and refresh tokens if they are + * null. + * @param {string | null} accessTokenSecret - A string representing the access token secret. It is + * initially set to `null` and will be generated if it is `null`. + * @param {string | null} refreshTokenSecret - The `refreshTokenSecret` parameter is a string that + * represents the secret key used to generate and verify refresh tokens. Refresh tokens are typically + * used in authentication systems to obtain new access tokens without requiring the user to + * re-authenticate. + */ async function accessAndRefreshTokens( accessTokenSecret: string | null, refreshTokenSecret: string | null @@ -83,6 +114,13 @@ async function accessAndRefreshTokens( } // Check connection to Redis with the specified URL. +/** + * The function `checkRedisConnection` checks if a connection to Redis can be established using the + * provided URL. + * @param {string} url - The `url` parameter is a string that represents the URL of the Redis server. + * It is used to establish a connection to the Redis server. + * @returns a Promise that resolves to a boolean value. + */ async function checkRedisConnection(url: string): Promise { let response = false; const client = redis.createClient({ url }); @@ -102,6 +140,12 @@ async function checkRedisConnection(url: string): Promise { } // Redis url prompt +/** + * The function `askForRedisUrl` prompts the user to enter the Redis hostname, port, and password, and + * returns an object with these values. + * @returns The function `askForRedisUrl` returns a promise that resolves to an object with the + * properties `host`, `port`, and `password`. + */ async function askForRedisUrl(): Promise<{ host: string; port: number; @@ -132,6 +176,11 @@ async function askForRedisUrl(): Promise<{ } // get the redis url +/** + * The `redisConfiguration` function updates the Redis configuration by prompting the user for the + * Redis URL, checking the connection, and updating the environment variables and .env file + * accordingly. + */ async function redisConfiguration(): Promise { const REDIS_URL = process.env.REDIS_URL; @@ -173,6 +222,10 @@ async function redisConfiguration(): Promise { } //LAST_RESORT_SUPERADMIN_EMAIL prompt +/** + * The function `askForSuperAdminEmail` asks the user to enter an email address and returns it as a promise. + * @returns The email entered by the user is being returned. + */ async function askForSuperAdminEmail(): Promise { const { email } = await inquirer.prompt([ { @@ -189,6 +242,10 @@ async function askForSuperAdminEmail(): Promise { } // Get the super admin email +/** + * The function `superAdmin` prompts the user for a super admin email, updates a configuration file + * with the email, and handles any errors that occur. + */ async function superAdmin(): Promise { try { const email = await askForSuperAdminEmail(); @@ -202,6 +259,16 @@ async function superAdmin(): Promise { } // Check the connection to MongoDB with the specified URL. +/** + * The function `checkConnection` is an asynchronous function that checks the connection to a MongoDB + * database using the provided URL and returns a boolean value indicating whether the connection was + * successful or not. + * @param {string} url - The `url` parameter is a string that represents the connection URL for the + * MongoDB server. It typically includes the protocol (e.g., `mongodb://`), the host and port + * information, and any authentication credentials if required. + * @returns a Promise that resolves to a boolean value. The boolean value indicates whether the + * connection to the MongoDB server was successful (true) or not (false). + */ async function checkConnection(url: string): Promise { let response = false; const client = new mongodb.MongoClient(url, { @@ -224,6 +291,11 @@ async function checkConnection(url: string): Promise { } //Mongodb url prompt +/** + * The function `askForMongoDBUrl` prompts the user to enter a MongoDB URL and returns the entered URL + * as a string. + * @returns a Promise that resolves to a string. + */ async function askForMongoDBUrl(): Promise { const { url } = await inquirer.prompt([ { @@ -237,6 +309,10 @@ async function askForMongoDBUrl(): Promise { } // Get the mongodb url +/** + * The `mongoDB` function connects to a MongoDB database by asking for a URL, checking the connection, + * and updating the environment variable with the URL. + */ async function mongoDB(): Promise { let DB_URL = process.env.MONGO_DB_URL; @@ -261,6 +337,11 @@ async function mongoDB(): Promise { } // Function to ask if the user wants to keep the entered values +/** + * The function `askToKeepValues` prompts the user with a confirmation message and returns a boolean + * indicating whether the user wants to keep the entered key. + * @returns a boolean value, either true or false. + */ async function askToKeepValues(): Promise { const { keepValues } = await inquirer.prompt({ type: "confirm", @@ -272,6 +353,10 @@ async function askToKeepValues(): Promise { } //Get recaptcha details +/** + * The function `recaptcha` prompts the user to enter a reCAPTCHA secret key, validates the input, and + * allows the user to choose whether to keep the entered value or try again. + */ async function recaptcha(): Promise { const { recaptchaSecretKey } = await inquirer.prompt([ { @@ -298,6 +383,10 @@ async function recaptcha(): Promise { } } +/** + * The function `recaptchaSiteKey` prompts the user to enter a reCAPTCHA site key, validates the input, + * and updates the environment variable if the user chooses to keep the entered value. + */ async function recaptchaSiteKey(): Promise { if (process.env.RECAPTCHA_SITE_KEY) { console.log( @@ -330,23 +419,42 @@ async function recaptchaSiteKey(): Promise { } } +/** + * The function `isValidEmail` checks if a given email address is valid according to a specific pattern. + * @param {string} email - The `email` parameter is a string that represents an email address. + * @returns a boolean value. It returns true if the email passed as an argument matches the specified + * pattern, and false otherwise. + */ function isValidEmail(email: string): boolean { const pattern = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/; const match = email.match(pattern); return match !== null && match[0] === email; } +/** + * The function validates whether a given string matches the pattern of a reCAPTCHA token. + * @param {string} string - The `string` parameter represents the input string that needs to be + * validated. In this case, it is expected to be a string containing a Recaptcha response token. + * @returns a boolean value. + */ function validateRecaptcha(string: string): boolean { const pattern = /^[a-zA-Z0-9_-]{40}$/; return pattern.test(string); } +/** + * The `abort` function logs a message and exits the process. + */ function abort(): void { console.log("\nSetup process aborted. 🫠"); process.exit(1); } //Get mail username and password +/** + * The function `twoFactorAuth` prompts the user to set up Two-Factor Authentication Google Account and + * then collects their email and generated password to update environment variables. + */ async function twoFactorAuth(): Promise { console.log("\nIMPORTANT"); console.log( @@ -382,6 +490,14 @@ async function twoFactorAuth(): Promise { } //Checks if the data exists and ask for deletion +/** + * The function `shouldWipeExistingData` checks if there is existing data in a MongoDB database and prompts the user to delete + * it before importing new data. + * @param {string} url - The `url` parameter is a string that represents the connection URL for the + * MongoDB database. It is used to establish a connection to the database using the `MongoClient` class + * from the `mongodb` package. + * @returns The function returns a Promise. + */ async function shouldWipeExistingData(url: string): Promise { let shouldImport = false; const client = new mongodb.MongoClient(url, { @@ -421,6 +537,11 @@ async function shouldWipeExistingData(url: string): Promise { } //Import sample data +/** + * The function `importData` imports sample data into a MongoDB database if the database URL is provided and if it + * is determined that existing data should be wiped. + * @returns The function returns a Promise that resolves to `void`. + */ async function importData(): Promise { if (!process.env.MONGO_DB_URL) { console.log("Couldn't find mongodb url"); @@ -447,6 +568,10 @@ async function importData(): Promise { } } +/** + * The main function sets up the Talawa API by prompting the user to configure various environment + * variables and import sample data if desired. + */ async function main(): Promise { console.log("Welcome to the Talawa API setup! 🚀"); From 29f59db6d5a3856f2e5bd6c1f039b2d1ffec2c9b Mon Sep 17 00:00:00 2001 From: Neyati <116624667+Doraemon012@users.noreply.github.com> Date: Sat, 13 Jan 2024 17:30:02 +0530 Subject: [PATCH 14/21] fix: added remaining comments --- setup.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.ts b/setup.ts index 18f1d48941..6260a7fe76 100644 --- a/setup.ts +++ b/setup.ts @@ -260,6 +260,9 @@ async function superAdmin(): Promise { } // Function to check if Existing MongoDB instance is running +/* The function `checkExistingMongoDB` checks for an existing MongoDB connection. It first +creates an array `existingMongoDbUrls` which contains two elements: `process.env.MONGO_DB_URL` and +`"mongodb://localhost:27017"`. */ async function checkExistingMongoDB(): Promise { const existingMongoDbUrls = [ process.env.MONGO_DB_URL, From 15011ceec576ebb4496bd17159223db579b420d6 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Sat, 13 Jan 2024 23:36:29 +0530 Subject: [PATCH 15/21] fix: Fixed merge confilcts and fixed a bug --- setup.ts | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/setup.ts b/setup.ts index 6260a7fe76..75604dae7a 100644 --- a/setup.ts +++ b/setup.ts @@ -215,7 +215,6 @@ async function redisConfiguration(): Promise { config.REDIS_PORT = port; config.REDIS_PASSWORD = password; updateEnvVariable(config); - console.log("\nRedis configuration updated successfully!"); } catch (err) { console.error(err); abort(); @@ -260,9 +259,11 @@ async function superAdmin(): Promise { } // Function to check if Existing MongoDB instance is running -/* The function `checkExistingMongoDB` checks for an existing MongoDB connection. It first -creates an array `existingMongoDbUrls` which contains two elements: `process.env.MONGO_DB_URL` and -`"mongodb://localhost:27017"`. */ +/** + * The function `checkExistingMongoDB` checks for an existing MongoDB connection by iterating through a + * list of URLs and testing the connection using the `checkConnection` function. + * @returns The function `checkExistingMongoDB` returns a promise that resolves to a string or null. + */ async function checkExistingMongoDB(): Promise { const existingMongoDbUrls = [ process.env.MONGO_DB_URL, @@ -338,6 +339,7 @@ async function askForMongoDBUrl(): Promise { */ async function mongoDB(): Promise { let DB_URL = process.env.MONGO_DB_URL; + try { let url = await checkExistingMongoDB(); @@ -352,7 +354,7 @@ async function mongoDB(): Promise { isConnected = await checkConnection(url); } - DB_URL = `${url}/talawa-api`; + DB_URL = `${url?.endsWith("/talawa-api") ? url : `${url}/talawa-api`}`; const config = dotenv.parse(fs.readFileSync(".env")); config.MONGO_DB_URL = DB_URL; // Modifying the environment variable to be able to access the url in importData function. @@ -360,6 +362,7 @@ async function mongoDB(): Promise { updateEnvVariable(config); } catch (err) { console.error(err); + abort(); } } @@ -711,10 +714,7 @@ async function configureSmtp(): Promise { const config = dotenv.parse(fs.readFileSync(".env")); config.IS_SMTP = "true"; Object.assign(config, smtpConfig); - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); console.log("SMTP configuration saved successfully."); } @@ -798,10 +798,7 @@ async function main(): Promise { process.env.REDIS_PORT = REDIS_PORT; process.env.REDIS_PASSWORD = REDIS_PASSWORD; - fs.writeFileSync(".env", ""); - for (const key in config) { - fs.appendFileSync(".env", `${key}=${config[key]}\n`); - } + updateEnvVariable(config); console.log(`Your MongoDB URL is:\n${process.env.MONGO_DB_URL}`); console.log(`Your Redis host is:\n${process.env.REDIS_HOST}`); console.log(`Your Redis port is:\n${process.env.REDIS_PORT}`); @@ -816,7 +813,8 @@ async function main(): Promise { type: "confirm", name: "shouldSetupRedis", message: "Would you like to change the existing Redis URL?", - default: false, + default: + process.env.REDIS_HOST && process.env.REDIS_PORT ? false : true, }); if (shouldSetupRedis) { @@ -832,13 +830,6 @@ async function main(): Promise { `\nMongoDB URL already exists with the value:\n${process.env.MONGO_DB_URL}` ); - const { shouldSetMongoDb } = await inquirer.prompt({ - type: "confirm", - name: "shouldSetMongoDb", - message: "Would you like to set up a MongoDB URL?", - default: true, - }); - const { shouldSetupMongo } = await inquirer.prompt({ type: "confirm", name: "shouldSetupMongo", From 94f0fe2804d2fe709c3673062b465b0e51b49d48 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Mon, 15 Jan 2024 22:30:57 +0530 Subject: [PATCH 16/21] fix: Fixed prompts --- setup.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.ts b/setup.ts index 75604dae7a..0ef5d72780 100644 --- a/setup.ts +++ b/setup.ts @@ -227,12 +227,15 @@ async function redisConfiguration(): Promise { * @returns The email entered by the user is being returned. */ async function askForSuperAdminEmail(): Promise { + console.log( + "\nPlease make sure to register with this email before logging in.\n" + ); const { email } = await inquirer.prompt([ { type: "input", name: "email", message: - "Please make sure to register with this email before logging in.\n Enter the email which you wish to assign as the Super Admin of last resort :", + "Enter the email which you wish to assign as the Super Admin of last resort :", validate: (input: string) => isValidEmail(input) || "Invalid email. Please try again.", }, @@ -645,7 +648,7 @@ async function verifySmtpConnection( */ async function configureSmtp(): Promise { console.log( - "SMTP Configuration is necessary for sending Emails through Talawa" + "\nSMTP Configuration is necessary for sending Emails through Talawa\n" ); const { shouldConfigureSmtp } = await inquirer.prompt({ type: "confirm", From 9d47698439239766b61138654574fbdb314c5f09 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Mon, 15 Jan 2024 22:48:10 +0530 Subject: [PATCH 17/21] fix: Removed incorrect prompt for MongoDB --- images/TFvWB_Ky2image.png | Bin 0 -> 132 bytes setup.ts | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 images/TFvWB_Ky2image.png diff --git a/images/TFvWB_Ky2image.png b/images/TFvWB_Ky2image.png new file mode 100644 index 0000000000000000000000000000000000000000..e22d4f1a3c5333c68bc4e05fbdad02cfffe317dc GIT binary patch literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5P}0-IF+?If*{1l}nR+&bgvw7(IGLrTrI{Jr XQ`poMzO&r|Dq`?-^>bP0l+XkKyptXN literal 0 HcmV?d00001 diff --git a/setup.ts b/setup.ts index 0ef5d72780..3ab371ee23 100644 --- a/setup.ts +++ b/setup.ts @@ -307,7 +307,6 @@ async function checkConnection(url: string): Promise { useUnifiedTopology: true, serverSelectionTimeoutMS: 1000, }); - console.log("\nConnection to MongoDB successful! 🎉"); await connection.close(); return true; } catch (error) { @@ -345,11 +344,22 @@ async function mongoDB(): Promise { try { let url = await checkExistingMongoDB(); - let isConnected = url !== null; if (isConnected) { console.log("MongoDB URL detected: " + url); + const { keepValues } = await inquirer.prompt({ + type: "confirm", + name: "keepValues", + message: `Do you want to connect to the detected MongoDB URL?`, + default: true, + }); + + if (keepValues) { + console.log("Keeping existing MongoDB URL: " + url); + } else { + isConnected = false; + } } while (!isConnected) { @@ -357,10 +367,13 @@ async function mongoDB(): Promise { isConnected = await checkConnection(url); } + if (isConnected) { + console.log("\nConnection to MongoDB successful! 🎉"); + } DB_URL = `${url?.endsWith("/talawa-api") ? url : `${url}/talawa-api`}`; const config = dotenv.parse(fs.readFileSync(".env")); config.MONGO_DB_URL = DB_URL; - // Modifying the environment variable to be able to access the url in importData function. + // Modifying the environment variable to be able to access the URL in importData function. process.env.MONGO_DB_URL = DB_URL; updateEnvVariable(config); } catch (err) { From badb669fe857a1af50dac87d8b8b25c7aba638a9 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Mon, 15 Jan 2024 23:03:30 +0530 Subject: [PATCH 18/21] feat: Automated Redis setup --- setup.ts | 65 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/setup.ts b/setup.ts index 3ab371ee23..a9da186ac0 100644 --- a/setup.ts +++ b/setup.ts @@ -130,7 +130,6 @@ async function checkRedisConnection(url: string): Promise { try { await client.connect(); - console.log("\nConnection to Redis successful! 🎉"); response = true; } catch (error) { console.log(`\nConnection to Redis failed. Please try again.\n`); @@ -176,6 +175,29 @@ async function askForRedisUrl(): Promise<{ return { host, port, password }; } +//check existing redis url +/** + * The function `checkExistingRedis` checks if there is an existing Redis connection by iterating + * through a list of Redis URLs and testing the connection. + * @returns The function `checkExistingRedis` returns a Promise that resolves to a string or null. + */ +async function checkExistingRedis(): Promise { + const existingRedisURL = ["redis://localhost:6379"]; + + for (const url of existingRedisURL) { + if (!url) { + continue; + } + + const isConnected = await checkRedisConnection(url); + if (isConnected) { + return url; + } + } + + return null; +} + // get the redis url /** * The `redisConfiguration` function updates the Redis configuration by prompting the user for the @@ -186,31 +208,54 @@ async function redisConfiguration(): Promise { const REDIS_URL = process.env.REDIS_URL; try { - let isConnected = false, - url = ""; let host!: string; let port!: number; let password!: string; + let url = await checkExistingRedis(); + let isConnected = url !== null; - while (!isConnected) { - const result = await askForRedisUrl(); - host = result.host; - port = result.port; - password = result.password; + if (isConnected) { + console.log("Redis URL detected: " + url); + const { keepValues } = await inquirer.prompt({ + type: "confirm", + name: "keepValues", + message: `Do you want to connect to the detected Redis URL?`, + default: true, + }); + if (keepValues) { + console.log("Keeping existing Redis URL: " + url); + host = "localhost"; + port = 6379; + password = ""; + } else { + isConnected = false; + } + } + + url = ""; + + while (!isConnected) { + if (!url) { + const result = await askForRedisUrl(); + host = result.host; + port = result.port; + password = result.password; + } url = `redis://${password ? password + "@" : ""}${host}:${port}`; isConnected = await checkRedisConnection(url); } + if (isConnected) { + console.log("\nConnection to Redis successful! 🎉"); + } // Set the Redis parameters in process.env - process.env.REDIS_URL = url; process.env.REDIS_HOST = host; process.env.REDIS_PORT = port.toString(); process.env.REDIS_PASSWORD = password; // Update the .env file const config = dotenv.parse(fs.readFileSync(".env")); - config.REDIS_URL = url; config.REDIS_HOST = host; config.REDIS_PORT = port; config.REDIS_PASSWORD = password; From 8c001686fed7136aaa017278df6aef804e5e3c51 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Mon, 15 Jan 2024 23:41:29 +0530 Subject: [PATCH 19/21] fix: Fixed SMTP and Mail configuration --- setup.ts | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/setup.ts b/setup.ts index a9da186ac0..91dbad34db 100644 --- a/setup.ts +++ b/setup.ts @@ -705,21 +705,6 @@ async function verifySmtpConnection( * @returns a Promise that resolves to void. */ async function configureSmtp(): Promise { - console.log( - "\nSMTP Configuration is necessary for sending Emails through Talawa\n" - ); - const { shouldConfigureSmtp } = await inquirer.prompt({ - type: "confirm", - name: "shouldConfigureSmtp", - message: "Would you like to configure SMTP for Talawa to send emails?", - default: true, - }); - - if (!shouldConfigureSmtp) { - console.log("SMTP configuration skipped."); - return; - } - const smtpConfig = await inquirer.prompt([ { type: "input", @@ -922,12 +907,16 @@ async function main(): Promise { await recaptchaSiteKey(); } + console.log( + "\n You can configure either SMTP or Mail for sending emails through Talawa.\n" + ); + if (process.env.MAIL_USERNAME) { console.log( - `\nMail username already exists with the value ${process.env.MAIL_USERNAME}` + `Mail username already exists with the value ${process.env.MAIL_USERNAME}` ); } - await configureSmtp(); + const { shouldSetMail } = await inquirer.prompt([ { type: "confirm", @@ -938,6 +927,20 @@ async function main(): Promise { ]); if (shouldSetMail) { await twoFactorAuth(); + } else { + console.log("Mail configuration skipped.\n"); + const { shouldConfigureSmtp } = await inquirer.prompt({ + type: "confirm", + name: "shouldConfigureSmtp", + message: "Would you like to configure SMTP for Talawa to send emails?", + default: true, + }); + + if (shouldConfigureSmtp) { + await configureSmtp(); + } else { + console.log("SMTP configuration skipped.\n"); + } } if (process.env.LAST_RESORT_SUPERADMIN_EMAIL) { From 8b534778685cbb6d18b4cd3d643d9f56ba97e41e Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Wed, 17 Jan 2024 00:35:44 +0530 Subject: [PATCH 20/21] fix: Fixed Redis URL being displayed on screen --- setup.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/setup.ts b/setup.ts index 91dbad34db..e45d6eaa9a 100644 --- a/setup.ts +++ b/setup.ts @@ -200,13 +200,10 @@ async function checkExistingRedis(): Promise { // get the redis url /** - * The `redisConfiguration` function updates the Redis configuration by prompting the user for the - * Redis URL, checking the connection, and updating the environment variables and .env file - * accordingly. + * The `redisConfiguration` function connects to a Redis server and sets the Redis parameters in the + * environment variables and updates the .env file. */ async function redisConfiguration(): Promise { - const REDIS_URL = process.env.REDIS_URL; - try { let host!: string; let port!: number; @@ -232,19 +229,17 @@ async function redisConfiguration(): Promise { isConnected = false; } } - url = ""; while (!isConnected) { - if (!url) { - const result = await askForRedisUrl(); - host = result.host; - port = result.port; - password = result.password; - } + const result = await askForRedisUrl(); + host = result.host; + port = result.port; + password = result.password; url = `redis://${password ? password + "@" : ""}${host}:${port}`; isConnected = await checkRedisConnection(url); } + if (isConnected) { console.log("\nConnection to Redis successful! 🎉"); } @@ -853,7 +848,15 @@ async function main(): Promise { if (!isDockerInstallation) { // Redis configuration if (process.env.REDIS_HOST && process.env.REDIS_PORT) { - console.log(`\nRedis URL already exists`); + const redis_password_str = process.env.REDIS_PASSWORD + ? "X".repeat(process.env.REDIS_PASSWORD.length) + : ""; + + const url = `redis://${ + process.env.REDIS_PASSWORD ? redis_password_str + "@" : "" + }${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`; + + console.log(`\nRedis URL already exists with the value:\n${url}`); const { shouldSetupRedis } = await inquirer.prompt({ type: "confirm", @@ -929,6 +932,7 @@ async function main(): Promise { await twoFactorAuth(); } else { console.log("Mail configuration skipped.\n"); + const { shouldConfigureSmtp } = await inquirer.prompt({ type: "confirm", name: "shouldConfigureSmtp", From be5e175f8017419ff82a0f82f81a8ddcc33c0a63 Mon Sep 17 00:00:00 2001 From: Doraemon012 Date: Thu, 18 Jan 2024 16:08:26 +0530 Subject: [PATCH 21/21] fix: set the default for import sample data to no and fixed some tsdoc comments --- images/TFvWB_Ky2image.png | Bin 132 -> 0 bytes setup.ts | 27 ++++++++++++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 images/TFvWB_Ky2image.png diff --git a/images/TFvWB_Ky2image.png b/images/TFvWB_Ky2image.png deleted file mode 100644 index e22d4f1a3c5333c68bc4e05fbdad02cfffe317dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132 zcmeAS@N?(olHy`uVBq!ia0vp^Od!m`1|*BN@u~nR#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DinK$vl=HlH+5P}0-IF+?If*{1l}nR+&bgvw7(IGLrTrI{Jr XQ`poMzO&r|Dq`?-^>bP0l+XkKyptXN diff --git a/setup.ts b/setup.ts index e45d6eaa9a..6f33804502 100644 --- a/setup.ts +++ b/setup.ts @@ -12,7 +12,7 @@ dotenv.config(); // Check if all the fields in .env.sample are present in .env /** * The function `checkEnvFile` checks if any fields are missing in the .env file compared to the .env.sample file, and - * if so, it copies the missing fields from .env.sample to .env. + * if so, it copies the missing fields from .env.sampale to .env. */ function checkEnvFile(): void { const env = dotenv.parse(fs.readFileSync(".env")); @@ -88,9 +88,9 @@ async function setNodeEnvironment(): Promise { /** * The function `accessAndRefreshTokens` generates and updates access and refresh tokens if they are * null. - * @param {string | null} accessTokenSecret - A string representing the access token secret. It is + * @param accessTokenSecret - A string representing the access token secret. It is * initially set to `null` and will be generated if it is `null`. - * @param {string | null} refreshTokenSecret - The `refreshTokenSecret` parameter is a string that + * @param refreshTokenSecret - The `refreshTokenSecret` parameter is a string that * represents the secret key used to generate and verify refresh tokens. Refresh tokens are typically * used in authentication systems to obtain new access tokens without requiring the user to * re-authenticate. @@ -118,7 +118,7 @@ async function accessAndRefreshTokens( /** * The function `checkRedisConnection` checks if a connection to Redis can be established using the * provided URL. - * @param {string} url - The `url` parameter is a string that represents the URL of the Redis server. + * @param url - The `url` parameter is a string that represents the URL of the Redis server. * It is used to establish a connection to the Redis server. * @returns a Promise that resolves to a boolean value. */ @@ -200,8 +200,9 @@ async function checkExistingRedis(): Promise { // get the redis url /** - * The `redisConfiguration` function connects to a Redis server and sets the Redis parameters in the - * environment variables and updates the .env file. + * The `redisConfiguration` function updates the Redis configuration by prompting the user for the + * Redis URL, checking the connection, and updating the environment variables and .env file + * accordingly. */ async function redisConfiguration(): Promise { try { @@ -332,7 +333,7 @@ async function checkExistingMongoDB(): Promise { * The function `checkConnection` is an asynchronous function that checks the connection to a MongoDB * database using the provided URL and returns a boolean value indicating whether the connection was * successful or not. - * @param {string} url - The `url` parameter is a string that represents the connection URL for the + * @param url - The `url` parameter is a string that represents the connection URL for the * MongoDB server. It typically includes the protocol (e.g., `mongodb://`), the host and port * information, and any authentication credentials if required. * @returns a Promise that resolves to a boolean value. The boolean value indicates whether the @@ -507,7 +508,7 @@ async function recaptchaSiteKey(): Promise { /** * The function `isValidEmail` checks if a given email address is valid according to a specific pattern. - * @param {string} email - The `email` parameter is a string that represents an email address. + * @param email - The `email` parameter is a string that represents an email address. * @returns a boolean value. It returns true if the email passed as an argument matches the specified * pattern, and false otherwise. */ @@ -519,7 +520,7 @@ function isValidEmail(email: string): boolean { /** * The function validates whether a given string matches the pattern of a reCAPTCHA token. - * @param {string} string - The `string` parameter represents the input string that needs to be + * @param string - The `string` parameter represents the input string that needs to be * validated. In this case, it is expected to be a string containing a Recaptcha response token. * @returns a boolean value. */ @@ -579,7 +580,7 @@ async function twoFactorAuth(): Promise { /** * The function `shouldWipeExistingData` checks if there is existing data in a MongoDB database and prompts the user to delete * it before importing new data. - * @param {string} url - The `url` parameter is a string that represents the connection URL for the + * @param url - The `url` parameter is a string that represents the connection URL for the * MongoDB database. It is used to establish a connection to the database using the `MongoClient` class * from the `mongodb` package. * @returns The function returns a Promise. @@ -848,12 +849,12 @@ async function main(): Promise { if (!isDockerInstallation) { // Redis configuration if (process.env.REDIS_HOST && process.env.REDIS_PORT) { - const redis_password_str = process.env.REDIS_PASSWORD + const redisPasswordStr = process.env.REDIS_PASSWORD ? "X".repeat(process.env.REDIS_PASSWORD.length) : ""; const url = `redis://${ - process.env.REDIS_PASSWORD ? redis_password_str + "@" : "" + process.env.REDIS_PASSWORD ? redisPasswordStr + "@" : "" }${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`; console.log(`\nRedis URL already exists with the value:\n${url}`); @@ -986,7 +987,7 @@ async function main(): Promise { type: "confirm", name: "shouldRunDataImport", message: "Do you want to import sample data?", - default: true, + default: false, }, ]);