From 147016cda5d00aec97343f47733cbef80eed37cc Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Thu, 23 Jan 2025 18:22:27 +0530 Subject: [PATCH 01/10] create setup --- package.json | 4 +- pnpm-lock.yaml | 298 +++++++++++++++++++++++ setup.ts | 408 ++++++++++++++++++++++++++++++++ src/setup/administratorEmail.ts | 22 ++ src/setup/getNodeEnvironment.ts | 22 ++ src/setup/updateEnvVariable.ts | 32 +++ vitest.config.ts | 4 +- 7 files changed, 788 insertions(+), 2 deletions(-) create mode 100644 setup.ts create mode 100644 src/setup/administratorEmail.ts create mode 100644 src/setup/getNodeEnvironment.ts create mode 100644 src/setup/updateEnvVariable.ts diff --git a/package.json b/package.json index 7cd7f3d786c..b300f850a35 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "graphql": "^16.10.0", "graphql-scalars": "^1.24.0", "graphql-upload-minimal": "^1.6.1", + "inquirer": "^12.3.2", "mercurius": "^16.0.1", "mercurius-upload": "^8.0.0", "minio": "^8.0.3", @@ -90,7 +91,8 @@ "start_development_server_with_debugger": "tsx watch --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./src/index.ts", "start_production_server": "pnpm build_production && node ./dist/index.js", "start_production_server_with_debugger": "pnpm build_production && node --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./dist/index.js", - "upgrade_drizzle_metadata": "drizzle-kit up" + "upgrade_drizzle_metadata": "drizzle-kit up", + "setup": "tsx setup.ts" }, "type": "module", "version": "1.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d0d0cf4390..075377fc33e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,6 +65,9 @@ importers: graphql-upload-minimal: specifier: ^1.6.1 version: 1.6.1(graphql@16.10.0) + inquirer: + specifier: ^12.3.2 + version: 12.3.2(@types/node@22.10.7) mercurius: specifier: ^16.0.1 version: 16.0.1(graphql@16.10.0) @@ -893,6 +896,86 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@inquirer/checkbox@4.0.6': + resolution: {integrity: sha512-PgP35JfmGjHU0LSXOyRew0zHuA9N6OJwOlos1fZ20b7j8ISeAdib3L+n0jIxBtX958UeEpte6xhG/gxJ5iUqMw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/confirm@5.1.3': + resolution: {integrity: sha512-fuF9laMmHoOgWapF9h9hv6opA5WvmGFHsTYGCmuFxcghIhEhb3dN0CdQR4BUMqa2H506NCj8cGX4jwMsE4t6dA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/core@10.1.4': + resolution: {integrity: sha512-5y4/PUJVnRb4bwWY67KLdebWOhOc7xj5IP2J80oWXa64mVag24rwQ1VAdnj7/eDY/odhguW0zQ1Mp1pj6fO/2w==} + engines: {node: '>=18'} + + '@inquirer/editor@4.2.3': + resolution: {integrity: sha512-S9KnIOJuTZpb9upeRSBBhoDZv7aSV3pG9TECrBj0f+ZsFwccz886hzKBrChGrXMJwd4NKY+pOA9Vy72uqnd6Eg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/expand@4.0.6': + resolution: {integrity: sha512-TRTfi1mv1GeIZGyi9PQmvAaH65ZlG4/FACq6wSzs7Vvf1z5dnNWsAAXBjWMHt76l+1hUY8teIqJFrWBk5N6gsg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/figures@1.0.9': + resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} + engines: {node: '>=18'} + + '@inquirer/input@4.1.3': + resolution: {integrity: sha512-zeo++6f7hxaEe7OjtMzdGZPHiawsfmCZxWB9X1NpmYgbeoyerIbWemvlBxxl+sQIlHC0WuSAG19ibMq3gbhaqQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/number@3.0.6': + resolution: {integrity: sha512-xO07lftUHk1rs1gR0KbqB+LJPhkUNkyzV/KhH+937hdkMazmAYHLm1OIrNKpPelppeV1FgWrgFDjdUD8mM+XUg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/password@4.0.6': + resolution: {integrity: sha512-QLF0HmMpHZPPMp10WGXh6F+ZPvzWE7LX6rNoccdktv/Rov0B+0f+eyXkAcgqy5cH9V+WSpbLxu2lo3ysEVK91w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/prompts@7.2.3': + resolution: {integrity: sha512-hzfnm3uOoDySDXfDNOm9usOuYIaQvTgKp/13l1uJoe6UNY+Zpcn2RYt0jXz3yA+yemGHvDOxVzqWl3S5sQq53Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/rawlist@4.0.6': + resolution: {integrity: sha512-QoE4s1SsIPx27FO4L1b1mUjVcoHm1pWE/oCmm4z/Hl+V1Aw5IXl8FYYzGmfXaBT0l/sWr49XmNSiq7kg3Kd/Lg==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/search@3.0.6': + resolution: {integrity: sha512-eFZ2hiAq0bZcFPuFFBmZEtXU1EarHLigE+ENCtpO+37NHCl4+Yokq1P/d09kUblObaikwfo97w+0FtG/EXl5Ng==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/select@4.0.6': + resolution: {integrity: sha512-yANzIiNZ8fhMm4NORm+a74+KFYHmf7BZphSOBovIzYPVLquseTGEkU5l2UTnBOf5k0VLmTgPighNDLE9QtbViQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + + '@inquirer/type@3.0.2': + resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1453,6 +1536,10 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1577,6 +1664,9 @@ packages: resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1585,6 +1675,10 @@ packages: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + close-with-grace@2.2.0: resolution: {integrity: sha512-OdcFxnxTm/AMLPHA4Aq3J1BLpkojXP7I4G5QBQLN5TT55ED/rk04rAoDbtfNnfZ988kGXPxh1bdRLeIU9bz/lA==} @@ -1878,6 +1972,10 @@ packages: resolution: {integrity: sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==} engines: {node: '>=4'} + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + fast-copy@3.0.2: resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} @@ -2110,6 +2208,10 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2120,6 +2222,12 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inquirer@12.3.2: + resolution: {integrity: sha512-YjQCIcDd3yyDuQrbII0FBtm/ZqNoWtvaC71yeCnd5Vbg4EgzsAGaemzfpzmqfvIZEp2roSwuZZKdM0C65hA43g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + inspect-with-kind@1.0.5: resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} @@ -2425,6 +2533,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2452,6 +2564,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -2605,9 +2721,16 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -2835,6 +2958,10 @@ packages: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2869,6 +2996,10 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + typescript@5.7.3: resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} @@ -2993,6 +3124,10 @@ packages: engines: {node: '>=8'} hasBin: true + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3032,6 +3167,10 @@ packages: resolution: {integrity: sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==} engines: {node: '>=12'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + zod@3.24.1: resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} @@ -3514,6 +3653,112 @@ snapshots: dependencies: graphql: 16.10.0 + '@inquirer/checkbox@4.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/confirm@5.1.3(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + + '@inquirer/core@10.1.4(@types/node@22.10.7)': + dependencies: + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.7) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + transitivePeerDependencies: + - '@types/node' + + '@inquirer/editor@4.2.3(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + external-editor: 3.1.0 + + '@inquirer/expand@4.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + yoctocolors-cjs: 2.1.2 + + '@inquirer/figures@1.0.9': {} + + '@inquirer/input@4.1.3(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + + '@inquirer/number@3.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + + '@inquirer/password@4.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + ansi-escapes: 4.3.2 + + '@inquirer/prompts@7.2.3(@types/node@22.10.7)': + dependencies: + '@inquirer/checkbox': 4.0.6(@types/node@22.10.7) + '@inquirer/confirm': 5.1.3(@types/node@22.10.7) + '@inquirer/editor': 4.2.3(@types/node@22.10.7) + '@inquirer/expand': 4.0.6(@types/node@22.10.7) + '@inquirer/input': 4.1.3(@types/node@22.10.7) + '@inquirer/number': 3.0.6(@types/node@22.10.7) + '@inquirer/password': 4.0.6(@types/node@22.10.7) + '@inquirer/rawlist': 4.0.6(@types/node@22.10.7) + '@inquirer/search': 3.0.6(@types/node@22.10.7) + '@inquirer/select': 4.0.6(@types/node@22.10.7) + '@types/node': 22.10.7 + + '@inquirer/rawlist@4.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + yoctocolors-cjs: 2.1.2 + + '@inquirer/search@3.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + yoctocolors-cjs: 2.1.2 + + '@inquirer/select@4.0.6(@types/node@22.10.7)': + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/figures': 1.0.9 + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + + '@inquirer/type@3.0.2(@types/node@22.10.7)': + dependencies: + '@types/node': 22.10.7 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -4002,6 +4247,10 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -4131,10 +4380,14 @@ snapshots: loupe: 3.1.2 pathval: 2.0.0 + chardet@0.7.0: {} + check-error@2.1.1: {} clean-stack@2.2.0: {} + cli-width@4.1.0: {} + close-with-grace@2.2.0: {} color-convert@2.0.1: @@ -4408,6 +4661,12 @@ snapshots: ext-list: 2.2.2 sort-keys-length: 1.0.1 + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + fast-copy@3.0.2: {} fast-decode-uri-component@1.0.1: {} @@ -4697,12 +4956,27 @@ snapshots: human-signals@2.1.0: {} + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} indent-string@4.0.0: {} inherits@2.0.4: {} + inquirer@12.3.2(@types/node@22.10.7): + dependencies: + '@inquirer/core': 10.1.4(@types/node@22.10.7) + '@inquirer/prompts': 7.2.3(@types/node@22.10.7) + '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@types/node': 22.10.7 + ansi-escapes: 4.3.2 + mute-stream: 2.0.0 + run-async: 3.0.0 + rxjs: 7.8.1 + inspect-with-kind@1.0.5: dependencies: kind-of: 6.0.3 @@ -4994,6 +5268,8 @@ snapshots: ms@2.1.3: {} + mute-stream@2.0.0: {} + nanoid@3.3.8: {} normalize-url@8.0.1: {} @@ -5014,6 +5290,8 @@ snapshots: dependencies: mimic-fn: 2.1.0 + os-tmpdir@1.0.2: {} + p-cancelable@3.0.0: {} p-map@4.0.0: @@ -5183,10 +5461,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.30.1 fsevents: 2.3.3 + run-async@3.0.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + rxjs@7.8.1: + dependencies: + tslib: 2.8.1 + safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -5395,6 +5679,10 @@ snapshots: tinyspy@3.0.2: {} + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -5421,6 +5709,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + type-fest@0.21.3: {} + typescript@5.7.3: {} uint8array-extras@1.4.0: {} @@ -5552,6 +5842,12 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -5582,4 +5878,6 @@ snapshots: buffer-crc32: 0.2.13 pend: 1.2.0 + yoctocolors-cjs@2.1.2: {} + zod@3.24.1: {} diff --git a/setup.ts b/setup.ts new file mode 100644 index 00000000000..4313b06381f --- /dev/null +++ b/setup.ts @@ -0,0 +1,408 @@ +import dotenv from "dotenv"; +import fs from "fs"; +import { updateEnvVariable } from './src/setup/updateEnvVariable'; +import { getNodeEnvironment } from "./src/setup/getNodeEnvironment"; +import { abort } from "process"; +import { askForAdministratorEmail } from "./src/setup/administratorEmail"; +import inquirer from "inquirer"; + +function checkEnvFile(): void { + const envDevcontainer = dotenv.parse(fs.readFileSync("./envFiles/.env.devcontainer")); + const envFileName = process.env.NODE_ENV === "test" ? ".env_test" : ".env"; + + const currentEnv = dotenv.parse(fs.readFileSync(envFileName)); + const missingKeys = Object.keys(envDevcontainer).filter((key) => !(key in currentEnv)); + if (missingKeys.length > 0) { + for (const key of missingKeys) { + fs.appendFileSync(envFileName, `${key}=${envDevcontainer[key]}\n`); + } + } + } + +async function setNodeEnvironment(): Promise { + if (process.env.NODE_ENV === "test") { + try { + const nodeEnv = await getNodeEnvironment(); + const config = dotenv.parse(fs.readFileSync(".env_test")); + config.NODE_ENV = nodeEnv; + updateEnvVariable(config); + } catch (err) { + console.error(err); + abort(); + } + } else { + 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(); + } + } + } + +// Get the administratorEmail email +/** + * The function `administratorEmail` prompts the user for a administratorEmail email, updates a configuration file + * with the email, and handles any errors that occur. + */ + +async function administratorEmail(): Promise { + try { + const email = await askForAdministratorEmail(); + if (process.env.NODE_ENV === "test") { + const config = dotenv.parse(fs.readFileSync(".env_test")); + config.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; + updateEnvVariable(config); + } else { + const config = dotenv.parse(fs.readFileSync(".env")); + config.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; + updateEnvVariable(config); + } + } catch (err) { + console.log(err); + abort(); + } + } + +async function apiSetup(): Promise { + const questions = [ + { + type: "input", + name: "API_BASE_URL", + message: "API base URL:", + default: "http://127.0.0.1:4000" + }, + { + type: "input", + name: "API_HOST", + message: "API host:", + default: "0.0.0.0" + }, + { + type: "input", + name: "API_PORT", + message: "API port:", + default: "4000" + }, + { + type: "list", + name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", + message: "Apply Drizzle migrations?", + choices: ["true", "false"], + default: "true" + }, + { + type: "list", + name: "API_IS_GRAPHIQL", + message: "Enable GraphQL?", + choices: ["true", "false"], + default: "true" + }, + { + type: "list", + name: "API_IS_PINO_PRETTY", + message: "Enable Pino Pretty logs?", + choices: ["true", "false"], + default: "true" + }, + { + type: "input", + name: "API_JWT_EXPIRES_IN", + message: "JWT expiration (ms):", + default: "2592000000" + }, + { + type: "input", + name: "API_JWT_SECRET", + message: "JWT secret:", + default: "b4896453be722d5ca94058a73f52b31c75980b485fa6d74d91f417a8059d8731" + }, + { + type: "input", + name: "API_MINIO_ACCESS_KEY", + message: "Minio access key:", + default: "talawa" + }, + { + type: "input", + name: "API_MINIO_END_POINT", + message: "Minio endpoint:", + default: "minio" + }, + { + type: "input", + name: "API_MINIO_PORT", + message: "Minio port:", + default: "9000" + }, + { + type: "input", + name: "API_MINIO_SECRET_KEY", + message: "Minio secret key:", + default: "password" + }, + { + type: "input", + name: "API_MINIO_TEST_END_POINT", + message: "Minio test endpoint:", + default: "minio-test" + }, + { + type: "list", + name: "API_MINIO_USE_SSL", + message: "Use Minio SSL?", + choices: ["true", "false"], + default: "false" + }, + { + type: "input", + name: "API_POSTGRES_DATABASE", + message: "Postgres database:", + default: "talawa" + }, + { + type: "input", + name: "API_POSTGRES_HOST", + message: "Postgres host:", + default: "postgres" + }, + { + type: "input", + name: "API_POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password" + }, + { + type: "input", + name: "API_POSTGRES_PORT", + message: "Postgres port:", + default: "5432" + }, + { + type: "list", + name: "API_POSTGRES_SSL_MODE", + message: "Use Postgres SSL?", + choices: ["true", "false"], + default: "false" + }, + { + type: "input", + name: "API_POSTGRES_TEST_HOST", + message: "Postgres test host:", + default: "postgres-test" + }, + { + type: "input", + name: "API_POSTGRES_USER", + message: "Postgres user:", + default: "talawa" + } + ]; + const answers = await inquirer.prompt(questions); + + updateEnvVariable(answers); + + console.log("Environment variables updated."); +} + +async function cloudbeaverSetup(): Promise { + const questions = [ + { + type: "input", + name: "CLOUDBEAVER_ADMIN_NAME", + message: "CloudBeaver admin name:", + default: "talawa" + }, + { + type: "input", + name: "CLOUDBEAVER_ADMIN_PASSWORD", + message: "CloudBeaver admin password:", + default: "password" + }, + { + type: "input", + name: "CLOUDBEAVER_MAPPED_HOST_IP", + message: "CloudBeaver mapped host IP:", + default: "127.0.0.1" + }, + { + type: "input", + name: "CLOUDBEAVER_MAPPED_PORT", + message: "CloudBeaver mapped port:", + default: "8978" + }, + { + type: "input", + name: "CLOUDBEAVER_SERVER_NAME", + message: "CloudBeaver server name:", + default: "Talawa CloudBeaver Server" + }, + { + type: "input", + name: "CLOUDBEAVER_SERVER_URL", + message: "CloudBeaver server URL:", + default: "http://127.0.0.1:8978" + } + ]; + + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); + console.log("CloudBeaver environment variables updated."); +} + +async function minioSetup(): Promise { + const questions = [ + { + type: "input", + name: "MINIO_BROWSER", + message: "Minio browser (on/off):", + default: "on" + }, + { + type: "input", + name: "MINIO_API_MAPPED_HOST_IP", + message: "Minio API mapped host IP:", + default: "127.0.0.1" + }, + { + type: "input", + name: "MINIO_API_MAPPED_PORT", + message: "Minio API mapped port:", + default: "9000" + }, + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_HOST_IP", + message: "Minio console mapped host IP:", + default: "127.0.0.1" + }, + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_PORT", + message: "Minio console mapped port:", + default: "9001" + }, + { + type: "input", + name: "MINIO_ROOT_PASSWORD", + message: "Minio root password:", + default: "password" + }, + { + type: "input", + name: "MINIO_ROOT_USER", + message: "Minio root user:", + default: "talawa" + } + ]; + + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); + console.log("Minio environment variables updated."); +} + +async function postgresSetup(): Promise { + const questions = [ + { + type: "input", + name: "POSTGRES_DB", + message: "Postgres database:", + default: "talawa" + }, + { + type: "input", + name: "POSTGRES_MAPPED_HOST_IP", + message: "Postgres mapped host IP:", + default: "127.0.0.1" + }, + { + type: "input", + name: "POSTGRES_MAPPED_PORT", + message: "Postgres mapped port:", + default: "5432" + }, + { + type: "input", + name: "POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password" + }, + { + type: "input", + name: "POSTGRES_USER", + message: "Postgres user:", + default: "talawa" + } + ]; + + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); + console.log("Postgres environment variables updated."); +} + +export async function main(): Promise { + checkEnvFile(); + const { useDefaultApi } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultApi", + message: "Do you want to use the recommended default API settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultApi) { + await apiSetup(); + } + + const { useDefaultMinio } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultMinio", + message: "Do you want to use the recommended default Minio settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultMinio) { + await minioSetup(); + } + + const { useDefaultCloudbeaver } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultCloudbeaver", + message: + "Do you want to use the recommended default CloudBeaver settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultCloudbeaver) { + await cloudbeaverSetup(); + } + + const { useDefaultPostgres } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultPostgres", + message: + "Do you want to use the recommended default Postgres settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultPostgres) { + await postgresSetup(); + } + await setNodeEnvironment(); + await administratorEmail(); + + console.log("Configuration complete."); +} + +// You can call main() to run this configuration setup: +main().catch((err) => { + console.error("Configuration error:", err); +}); diff --git a/src/setup/administratorEmail.ts b/src/setup/administratorEmail.ts new file mode 100644 index 00000000000..f6215f2ea6a --- /dev/null +++ b/src/setup/administratorEmail.ts @@ -0,0 +1,22 @@ +import inquirer from "inquirer"; + +/** + * ADMINISTRATOR_EMAIL prompt + * The function `askForAdministratorEmail` asks the user to enter an email address and returns it as a promise. + * @returns The email entered by the user is being returned. + */ +export async function askForAdministratorEmail(): 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: + "Enter email :", + }, + ]); + + return email; +} \ No newline at end of file diff --git a/src/setup/getNodeEnvironment.ts b/src/setup/getNodeEnvironment.ts new file mode 100644 index 00000000000..1f70412668b --- /dev/null +++ b/src/setup/getNodeEnvironment.ts @@ -0,0 +1,22 @@ +import inquirer from "inquirer"; + +/** + * 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. + */ + +export async function getNodeEnvironment(): Promise { + const { nodeEnv } = await inquirer.prompt([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); + + return nodeEnv; +} \ No newline at end of file diff --git a/src/setup/updateEnvVariable.ts b/src/setup/updateEnvVariable.ts new file mode 100644 index 00000000000..5e158dd3052 --- /dev/null +++ b/src/setup/updateEnvVariable.ts @@ -0,0 +1,32 @@ +import fs from "fs"; + +// 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. + */ + +export function updateEnvVariable(config: { + [key: string]: string | number; +}): void { + if (process.env.NODE_ENV === "test") { + const existingContent: string = fs.readFileSync(".env_test", "utf8"); + let updatedContent: string = existingContent; + for (const key in config) { + const regex = new RegExp(`^${key}=.*`, "gm"); + updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + } + fs.writeFileSync(".env_test", updatedContent, "utf8"); + } else { + const existingContent: string = fs.readFileSync(".env", "utf8"); + let updatedContent: string = existingContent; + for (const key in config) { + const regex = new RegExp(`^${key}=.*`, "gm"); + updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + } + fs.writeFileSync(".env", updatedContent, "utf8"); + } +} \ No newline at end of file diff --git a/vitest.config.ts b/vitest.config.ts index 15a59e249c2..2917ae789c8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,7 +3,9 @@ import tsconfigPaths from "vite-tsconfig-paths"; import { defineConfig } from "vitest/config"; export default defineConfig({ - plugins: [tsconfigPaths()], + plugins: [tsconfigPaths({ + ignoreConfigErrors: true, + })], test: { // // https://vitest.dev/config/#fileparallelism // fileParallelism: true, From 11d91cfea752c6d3aac1101a503708a7927e67df Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Sun, 26 Jan 2025 20:41:58 +0530 Subject: [PATCH 02/10] setups tests --- package.json | 1 + pnpm-lock.yaml | 3 + setup.ts | 136 ++++++++++++++++---------- src/setup/administratorEmail.ts | 22 ----- src/setup/getNodeEnvironment.ts | 22 ----- test/setup/administratorEmail.test.ts | 23 +++++ test/setup/apiSetup.test.ts | 47 +++++++++ test/setup/cloudbeaverSetup.test.ts | 36 +++++++ test/setup/minioSetup.test.ts | 38 +++++++ test/setup/postgresSetup.test.ts | 34 +++++++ test/setup/setNodeEnviorment.test.ts | 68 +++++++++++++ 11 files changed, 337 insertions(+), 93 deletions(-) delete mode 100644 src/setup/administratorEmail.ts delete mode 100644 src/setup/getNodeEnvironment.ts create mode 100644 test/setup/administratorEmail.test.ts create mode 100644 test/setup/apiSetup.test.ts create mode 100644 test/setup/cloudbeaverSetup.test.ts create mode 100644 test/setup/minioSetup.test.ts create mode 100644 test/setup/postgresSetup.test.ts create mode 100644 test/setup/setNodeEnviorment.test.ts diff --git a/package.json b/package.json index 5c5e6b9a14d..ad3b622d3f3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@sinclair/typebox": "^0.34.14", "ajv-formats": "^3.0.1", "close-with-grace": "^2.2.0", + "dotenv": "^16.4.7", "drizzle-orm": "^0.38.4", "drizzle-zod": "0.6.1", "env-schema": "^6.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c763ef8595..f23fb052cc8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: close-with-grace: specifier: ^2.2.0 version: 2.2.0 + dotenv: + specifier: ^16.4.7 + version: 16.4.7 drizzle-orm: specifier: ^0.38.4 version: 0.38.4(postgres@3.4.5) diff --git a/setup.ts b/setup.ts index 4313b06381f..b3a9470afc5 100644 --- a/setup.ts +++ b/setup.ts @@ -1,48 +1,63 @@ import dotenv from "dotenv"; import fs from "fs"; -import { updateEnvVariable } from './src/setup/updateEnvVariable'; -import { getNodeEnvironment } from "./src/setup/getNodeEnvironment"; +import { updateEnvVariable } from "./src/setup/updateEnvVariable"; import { abort } from "process"; -import { askForAdministratorEmail } from "./src/setup/administratorEmail"; import inquirer from "inquirer"; -function checkEnvFile(): void { - const envDevcontainer = dotenv.parse(fs.readFileSync("./envFiles/.env.devcontainer")); - const envFileName = process.env.NODE_ENV === "test" ? ".env_test" : ".env"; - const currentEnv = dotenv.parse(fs.readFileSync(envFileName)); - const missingKeys = Object.keys(envDevcontainer).filter((key) => !(key in currentEnv)); - if (missingKeys.length > 0) { - for (const key of missingKeys) { - fs.appendFileSync(envFileName, `${key}=${envDevcontainer[key]}\n`); - } - } +let originalEnvContent: string | null = null; +const envFileName = ".env"; + +function backupEnvFile(): void { + if (fs.existsSync(envFileName)) { + originalEnvContent = fs.readFileSync(envFileName, "utf-8"); + } else { + originalEnvContent = null; } +} -async function setNodeEnvironment(): Promise { - if (process.env.NODE_ENV === "test") { - try { - const nodeEnv = await getNodeEnvironment(); - const config = dotenv.parse(fs.readFileSync(".env_test")); - config.NODE_ENV = nodeEnv; - updateEnvVariable(config); - } catch (err) { - console.error(err); - abort(); - } - } else { +function restoreEnvFile(): void { + try { + if (originalEnvContent !== null) { + fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); + console.log("\nChanges undone. Restored the original environment file."); + } else if (fs.existsSync(envFileName)) { + fs.unlinkSync(envFileName); + console.log("\nChanges undone. Removed the environment file."); + } + } catch (err) { + console.error("Error restoring env file:", err); + } +} + +function checkEnvFile(): void { + const envFileName = ".env"; + + const envDevcontainer = dotenv.parse(fs.readFileSync("envFiles/.env.devcontainer")); + + dotenv.config({ path: envFileName }); + const content = Object.entries(envDevcontainer) + .map(([key, value]) => `${key}=${value}`) + .join("\n"); + fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); +} + +export async function setNodeEnvironment(): Promise { try { - const nodeEnv = await getNodeEnvironment(); + const { nodeEnv } = await inquirer.prompt([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); process.env.NODE_ENV = nodeEnv; - - const config = dotenv.parse(fs.readFileSync(".env")); - config.NODE_ENV = nodeEnv; - updateEnvVariable(config); } catch (err) { console.error(err); abort(); } - } } // Get the administratorEmail email @@ -51,25 +66,24 @@ async function setNodeEnvironment(): Promise { * with the email, and handles any errors that occur. */ -async function administratorEmail(): Promise { + export async function administratorEmail(): Promise { try { - const email = await askForAdministratorEmail(); - if (process.env.NODE_ENV === "test") { - const config = dotenv.parse(fs.readFileSync(".env_test")); - config.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; - updateEnvVariable(config); - } else { - const config = dotenv.parse(fs.readFileSync(".env")); - config.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; - updateEnvVariable(config); - } + const { email } = await inquirer.prompt([ + { + type: "input", + name: "email", + message: + "Enter email :", + }, + ]); + process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; } catch (err) { console.log(err); abort(); } } -async function apiSetup(): Promise { +export async function apiSetup(): Promise { const questions = [ { type: "input", @@ -207,10 +221,14 @@ async function apiSetup(): Promise { updateEnvVariable(answers); + Object.entries(answers).forEach(([key, value]) => { + process.env[key] = value; + }); + console.log("Environment variables updated."); } -async function cloudbeaverSetup(): Promise { +export async function cloudbeaverSetup(): Promise { const questions = [ { type: "input", @@ -252,10 +270,14 @@ async function cloudbeaverSetup(): Promise { const answers = await inquirer.prompt(questions); updateEnvVariable(answers); + + Object.entries(answers).forEach(([key, value]) => { + process.env[key] = value; + }); console.log("CloudBeaver environment variables updated."); } -async function minioSetup(): Promise { +export async function minioSetup(): Promise { const questions = [ { type: "input", @@ -303,10 +325,14 @@ async function minioSetup(): Promise { const answers = await inquirer.prompt(questions); updateEnvVariable(answers); + + Object.entries(answers).forEach(([key, value]) => { + process.env[key] = value; + }); console.log("Minio environment variables updated."); } -async function postgresSetup(): Promise { +export async function postgresSetup(): Promise { const questions = [ { type: "input", @@ -342,12 +368,25 @@ async function postgresSetup(): Promise { const answers = await inquirer.prompt(questions); updateEnvVariable(answers); + + Object.entries(answers).forEach(([key, value]) => { + process.env[key] = value; + }); console.log("Postgres environment variables updated."); } export async function main(): Promise { + dotenv.config({ path: envFileName }); checkEnvFile(); - const { useDefaultApi } = await inquirer.prompt([ + backupEnvFile(); + + process.on("SIGINT", () => { + console.log("\nProcess interrupted! Undoing changes..."); + restoreEnvFile(); + process.exit(1); + }); + + const {useDefaultApi} = await inquirer.prompt([ { type: "confirm", name: "useDefaultApi", @@ -402,7 +441,6 @@ export async function main(): Promise { console.log("Configuration complete."); } -// You can call main() to run this configuration setup: main().catch((err) => { - console.error("Configuration error:", err); + restoreEnvFile(); }); diff --git a/src/setup/administratorEmail.ts b/src/setup/administratorEmail.ts deleted file mode 100644 index f6215f2ea6a..00000000000 --- a/src/setup/administratorEmail.ts +++ /dev/null @@ -1,22 +0,0 @@ -import inquirer from "inquirer"; - -/** - * ADMINISTRATOR_EMAIL prompt - * The function `askForAdministratorEmail` asks the user to enter an email address and returns it as a promise. - * @returns The email entered by the user is being returned. - */ -export async function askForAdministratorEmail(): 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: - "Enter email :", - }, - ]); - - return email; -} \ No newline at end of file diff --git a/src/setup/getNodeEnvironment.ts b/src/setup/getNodeEnvironment.ts deleted file mode 100644 index 1f70412668b..00000000000 --- a/src/setup/getNodeEnvironment.ts +++ /dev/null @@ -1,22 +0,0 @@ -import inquirer from "inquirer"; - -/** - * 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. - */ - -export async function getNodeEnvironment(): Promise { - const { nodeEnv } = await inquirer.prompt([ - { - type: "list", - name: "nodeEnv", - message: "Select Node environment:", - choices: ["development", "production"], - default: "development", - }, - ]); - - return nodeEnv; -} \ No newline at end of file diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts new file mode 100644 index 00000000000..1295b375ee7 --- /dev/null +++ b/test/setup/administratorEmail.test.ts @@ -0,0 +1,23 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { administratorEmail } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> askForAdministratorEmail", () => { + const originalEmail = process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS; + + afterEach(() => { + process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = originalEmail; + }); + + it("should prompt the user for an email and update the email env", async () => { + const mockedEmail = "testuser@email.com"; + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ email: mockedEmail }); + + await administratorEmail(); + + expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); + }); +}); diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts new file mode 100644 index 00000000000..57413ea82f2 --- /dev/null +++ b/test/setup/apiSetup.test.ts @@ -0,0 +1,47 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { apiSetup } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> apiSetup", () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + it("should prompt the user for API configuration and update process.env", async () => { + const mockedAnswers = { + API_BASE_URL: "http://localhost:5000", + API_HOST: "127.0.0.1", + API_PORT: "5000", + API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", + API_IS_GRAPHIQL: "false", + API_IS_PINO_PRETTY: "true", + API_JWT_EXPIRES_IN: "3600000", + API_JWT_SECRET: "mocked-secret", + API_MINIO_ACCESS_KEY: "mocked-access-key", + API_MINIO_END_POINT: "mocked-endpoint", + API_MINIO_PORT: "9001", + API_MINIO_SECRET_KEY: "mocked-secret-key", + API_MINIO_TEST_END_POINT: "mocked-test-endpoint", + API_MINIO_USE_SSL: "true", + API_POSTGRES_DATABASE: "mocked-database", + API_POSTGRES_HOST: "mocked-host", + API_POSTGRES_PASSWORD: "mocked-password", + API_POSTGRES_PORT: "5433", + API_POSTGRES_SSL_MODE: "true", + API_POSTGRES_TEST_HOST: "mocked-test-host", + API_POSTGRES_USER: "mocked-user", + }; + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + + await apiSetup(); + expect(process.env.API_BASE_URL).toBe("http://localhost:5000") + expect(process.env.API_HOST).toBe("127.0.0.1") + + }); +}); diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts new file mode 100644 index 00000000000..40c2ee299d2 --- /dev/null +++ b/test/setup/cloudbeaverSetup.test.ts @@ -0,0 +1,36 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { cloudbeaverSetup } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> cloudbeaverSetup", () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + it("should prompt the user for CloudBeaver configuration and update process.env", async () => { + const mockedAnswers = { + CLOUDBEAVER_ADMIN_NAME: "mocked-admin", + CLOUDBEAVER_ADMIN_PASSWORD: "mocked-password", + CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", + CLOUDBEAVER_MAPPED_PORT: "8080", + CLOUDBEAVER_SERVER_NAME: "Mocked Server", + CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080", + }; + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + + await cloudbeaverSetup(); + + expect(process.env.CLOUDBEAVER_ADMIN_NAME).toBe("mocked-admin"); + expect(process.env.CLOUDBEAVER_ADMIN_PASSWORD).toBe("mocked-password"); + expect(process.env.CLOUDBEAVER_MAPPED_HOST_IP).toBe("127.0.0.1"); + expect(process.env.CLOUDBEAVER_MAPPED_PORT).toBe("8080"); + expect(process.env.CLOUDBEAVER_SERVER_NAME).toBe("Mocked Server"); + expect(process.env.CLOUDBEAVER_SERVER_URL).toBe("http://127.0.0.1:8080"); + }); +}); diff --git a/test/setup/minioSetup.test.ts b/test/setup/minioSetup.test.ts new file mode 100644 index 00000000000..07bf210cf37 --- /dev/null +++ b/test/setup/minioSetup.test.ts @@ -0,0 +1,38 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { minioSetup } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> minioSetup", () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + it("should prompt the user for Minio configuration and update process.env", async () => { + const mockedAnswers = { + MINIO_BROWSER: "off", + MINIO_API_MAPPED_HOST_IP: "192.168.1.10", + MINIO_API_MAPPED_PORT: "8000", + MINIO_CONSOLE_MAPPED_HOST_IP: "192.168.1.20", + MINIO_CONSOLE_MAPPED_PORT: "8001", + MINIO_ROOT_PASSWORD: "mocked-password", + MINIO_ROOT_USER: "mocked-user", + }; + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + + await minioSetup(); + + expect(process.env.MINIO_BROWSER).toBe("off"); + expect(process.env.MINIO_API_MAPPED_HOST_IP).toBe("192.168.1.10"); + expect(process.env.MINIO_API_MAPPED_PORT).toBe("8000"); + expect(process.env.MINIO_CONSOLE_MAPPED_HOST_IP).toBe("192.168.1.20"); + expect(process.env.MINIO_CONSOLE_MAPPED_PORT).toBe("8001"); + expect(process.env.MINIO_ROOT_PASSWORD).toBe("mocked-password"); + expect(process.env.MINIO_ROOT_USER).toBe("mocked-user"); + }); +}); diff --git a/test/setup/postgresSetup.test.ts b/test/setup/postgresSetup.test.ts new file mode 100644 index 00000000000..e765cc064a6 --- /dev/null +++ b/test/setup/postgresSetup.test.ts @@ -0,0 +1,34 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { postgresSetup } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> postgresSetup", () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + it("should prompt the user for Postgres configuration and update process.env", async () => { + const mockedAnswers = { + POSTGRES_DB: "mocked-db", + POSTGRES_MAPPED_HOST_IP: "192.168.1.100", + POSTGRES_MAPPED_PORT: "5434", + POSTGRES_PASSWORD: "mocked-password", + POSTGRES_USER: "mocked-user", + }; + + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + + await postgresSetup(); + + expect(process.env.POSTGRES_DB).toBe("mocked-db"); + expect(process.env.POSTGRES_MAPPED_HOST_IP).toBe("192.168.1.100"); + expect(process.env.POSTGRES_MAPPED_PORT).toBe("5434"); + expect(process.env.POSTGRES_PASSWORD).toBe("mocked-password"); + expect(process.env.POSTGRES_USER).toBe("mocked-user"); + }); +}); diff --git a/test/setup/setNodeEnviorment.test.ts b/test/setup/setNodeEnviorment.test.ts new file mode 100644 index 00000000000..5d53e7383c0 --- /dev/null +++ b/test/setup/setNodeEnviorment.test.ts @@ -0,0 +1,68 @@ +import { describe, it, vi, expect, afterEach } from "vitest"; +import { setNodeEnvironment } from "setup"; +import inquirer from "inquirer"; + +vi.mock("inquirer"); + +describe("Setup -> setNodeEnvironment", () => { + const originalNodeEnv = process.env.NODE_ENV; + + afterEach(() => { + process.env.NODE_ENV = originalNodeEnv; + vi.resetAllMocks(); + }); + + it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { + const mockedNodeEnv = "production"; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ nodeEnv: mockedNodeEnv }); + process.env.NODE_ENV = "test"; + + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe(mockedNodeEnv); + expect(inquirer.prompt).toHaveBeenCalledWith([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); + }); + + it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ nodeEnv: "development" }); // Mock inquirer prompt with default + + process.env.NODE_ENV = "test"; + + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe("development"); + expect(inquirer.prompt).toHaveBeenCalled(); + }); + + it("should handle errors thrown by inquirer.prompt when NODE_ENV is 'test'", async () => { + const mockedError = new Error("Prompt failed"); + vi.spyOn(inquirer, "prompt").mockRejectedValueOnce(mockedError); // Mock inquirer to throw an error + + process.env.NODE_ENV = "test"; + + const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + + await expect(setNodeEnvironment()).rejects.toThrow(); + + expect(consoleErrorSpy).toHaveBeenCalledWith(mockedError); + + consoleErrorSpy.mockRestore(); + }); + + it("should leave NODE_ENV unchanged when not in 'test' environment", async () => { + process.env.NODE_ENV = "development"; + + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe("development"); + expect(inquirer.prompt).not.toHaveBeenCalled(); + }); +}); From 922a84e3ce4203a1d429d771908c8297a554b2d0 Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Sun, 26 Jan 2025 20:48:06 +0530 Subject: [PATCH 03/10] biome fix --- setup.ts | 778 +++++++++++++------------- src/setup/updateEnvVariable.ts | 40 +- test/setup/administratorEmail.test.ts | 26 +- test/setup/apiSetup.test.ts | 73 ++- test/setup/cloudbeaverSetup.test.ts | 50 +- test/setup/minioSetup.test.ts | 54 +- test/setup/postgresSetup.test.ts | 46 +- test/setup/setNodeEnviorment.test.ts | 96 ++-- vitest.config.ts | 8 +- 9 files changed, 590 insertions(+), 581 deletions(-) diff --git a/setup.ts b/setup.ts index b3a9470afc5..dc1d8f56ea1 100644 --- a/setup.ts +++ b/setup.ts @@ -1,64 +1,65 @@ +import fs from "node:fs"; +import { abort } from "node:process"; import dotenv from "dotenv"; -import fs from "fs"; -import { updateEnvVariable } from "./src/setup/updateEnvVariable"; -import { abort } from "process"; import inquirer from "inquirer"; - +import { updateEnvVariable } from "./src/setup/updateEnvVariable"; let originalEnvContent: string | null = null; const envFileName = ".env"; function backupEnvFile(): void { - if (fs.existsSync(envFileName)) { - originalEnvContent = fs.readFileSync(envFileName, "utf-8"); - } else { - originalEnvContent = null; - } + if (fs.existsSync(envFileName)) { + originalEnvContent = fs.readFileSync(envFileName, "utf-8"); + } else { + originalEnvContent = null; + } } function restoreEnvFile(): void { - try { - if (originalEnvContent !== null) { - fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); - console.log("\nChanges undone. Restored the original environment file."); - } else if (fs.existsSync(envFileName)) { - fs.unlinkSync(envFileName); - console.log("\nChanges undone. Removed the environment file."); - } - } catch (err) { - console.error("Error restoring env file:", err); - } + try { + if (originalEnvContent !== null) { + fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); + console.log("\nChanges undone. Restored the original environment file."); + } else if (fs.existsSync(envFileName)) { + fs.unlinkSync(envFileName); + console.log("\nChanges undone. Removed the environment file."); + } + } catch (err) { + console.error("Error restoring env file:", err); + } } function checkEnvFile(): void { - const envFileName = ".env"; + const envFileName = ".env"; - const envDevcontainer = dotenv.parse(fs.readFileSync("envFiles/.env.devcontainer")); + const envDevcontainer = dotenv.parse( + fs.readFileSync("envFiles/.env.devcontainer"), + ); - dotenv.config({ path: envFileName }); - const content = Object.entries(envDevcontainer) - .map(([key, value]) => `${key}=${value}`) - .join("\n"); - fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); + dotenv.config({ path: envFileName }); + const content = Object.entries(envDevcontainer) + .map(([key, value]) => `${key}=${value}`) + .join("\n"); + fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); } export async function setNodeEnvironment(): Promise { - try { - const { nodeEnv } = await inquirer.prompt([ - { - type: "list", - name: "nodeEnv", - message: "Select Node environment:", - choices: ["development", "production"], - default: "development", - }, - ]); - process.env.NODE_ENV = nodeEnv; - } catch (err) { - console.error(err); - abort(); - } - } + try { + const { nodeEnv } = await inquirer.prompt([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); + process.env.NODE_ENV = nodeEnv; + } catch (err) { + console.error(err); + abort(); + } +} // Get the administratorEmail email /** @@ -66,381 +67,382 @@ export async function setNodeEnvironment(): Promise { * with the email, and handles any errors that occur. */ - export async function administratorEmail(): Promise { - try { - const { email } = await inquirer.prompt([ - { - type: "input", - name: "email", - message: - "Enter email :", - }, - ]); - process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; - } catch (err) { - console.log(err); - abort(); - } - } +export async function administratorEmail(): Promise { + try { + const { email } = await inquirer.prompt([ + { + type: "input", + name: "email", + message: "Enter email :", + }, + ]); + process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; + } catch (err) { + console.log(err); + abort(); + } +} export async function apiSetup(): Promise { - const questions = [ - { - type: "input", - name: "API_BASE_URL", - message: "API base URL:", - default: "http://127.0.0.1:4000" - }, - { - type: "input", - name: "API_HOST", - message: "API host:", - default: "0.0.0.0" - }, - { - type: "input", - name: "API_PORT", - message: "API port:", - default: "4000" - }, - { - type: "list", - name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", - message: "Apply Drizzle migrations?", - choices: ["true", "false"], - default: "true" - }, - { - type: "list", - name: "API_IS_GRAPHIQL", - message: "Enable GraphQL?", - choices: ["true", "false"], - default: "true" - }, - { - type: "list", - name: "API_IS_PINO_PRETTY", - message: "Enable Pino Pretty logs?", - choices: ["true", "false"], - default: "true" - }, - { - type: "input", - name: "API_JWT_EXPIRES_IN", - message: "JWT expiration (ms):", - default: "2592000000" - }, - { - type: "input", - name: "API_JWT_SECRET", - message: "JWT secret:", - default: "b4896453be722d5ca94058a73f52b31c75980b485fa6d74d91f417a8059d8731" - }, - { - type: "input", - name: "API_MINIO_ACCESS_KEY", - message: "Minio access key:", - default: "talawa" - }, - { - type: "input", - name: "API_MINIO_END_POINT", - message: "Minio endpoint:", - default: "minio" - }, - { - type: "input", - name: "API_MINIO_PORT", - message: "Minio port:", - default: "9000" - }, - { - type: "input", - name: "API_MINIO_SECRET_KEY", - message: "Minio secret key:", - default: "password" - }, - { - type: "input", - name: "API_MINIO_TEST_END_POINT", - message: "Minio test endpoint:", - default: "minio-test" - }, - { - type: "list", - name: "API_MINIO_USE_SSL", - message: "Use Minio SSL?", - choices: ["true", "false"], - default: "false" - }, - { - type: "input", - name: "API_POSTGRES_DATABASE", - message: "Postgres database:", - default: "talawa" - }, - { - type: "input", - name: "API_POSTGRES_HOST", - message: "Postgres host:", - default: "postgres" - }, - { - type: "input", - name: "API_POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password" - }, - { - type: "input", - name: "API_POSTGRES_PORT", - message: "Postgres port:", - default: "5432" - }, - { - type: "list", - name: "API_POSTGRES_SSL_MODE", - message: "Use Postgres SSL?", - choices: ["true", "false"], - default: "false" - }, - { - type: "input", - name: "API_POSTGRES_TEST_HOST", - message: "Postgres test host:", - default: "postgres-test" - }, - { - type: "input", - name: "API_POSTGRES_USER", - message: "Postgres user:", - default: "talawa" - } - ]; - const answers = await inquirer.prompt(questions); + const questions = [ + { + type: "input", + name: "API_BASE_URL", + message: "API base URL:", + default: "http://127.0.0.1:4000", + }, + { + type: "input", + name: "API_HOST", + message: "API host:", + default: "0.0.0.0", + }, + { + type: "input", + name: "API_PORT", + message: "API port:", + default: "4000", + }, + { + type: "list", + name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", + message: "Apply Drizzle migrations?", + choices: ["true", "false"], + default: "true", + }, + { + type: "list", + name: "API_IS_GRAPHIQL", + message: "Enable GraphQL?", + choices: ["true", "false"], + default: "true", + }, + { + type: "list", + name: "API_IS_PINO_PRETTY", + message: "Enable Pino Pretty logs?", + choices: ["true", "false"], + default: "true", + }, + { + type: "input", + name: "API_JWT_EXPIRES_IN", + message: "JWT expiration (ms):", + default: "2592000000", + }, + { + type: "input", + name: "API_JWT_SECRET", + message: "JWT secret:", + default: + "b4896453be722d5ca94058a73f52b31c75980b485fa6d74d91f417a8059d8731", + }, + { + type: "input", + name: "API_MINIO_ACCESS_KEY", + message: "Minio access key:", + default: "talawa", + }, + { + type: "input", + name: "API_MINIO_END_POINT", + message: "Minio endpoint:", + default: "minio", + }, + { + type: "input", + name: "API_MINIO_PORT", + message: "Minio port:", + default: "9000", + }, + { + type: "input", + name: "API_MINIO_SECRET_KEY", + message: "Minio secret key:", + default: "password", + }, + { + type: "input", + name: "API_MINIO_TEST_END_POINT", + message: "Minio test endpoint:", + default: "minio-test", + }, + { + type: "list", + name: "API_MINIO_USE_SSL", + message: "Use Minio SSL?", + choices: ["true", "false"], + default: "false", + }, + { + type: "input", + name: "API_POSTGRES_DATABASE", + message: "Postgres database:", + default: "talawa", + }, + { + type: "input", + name: "API_POSTGRES_HOST", + message: "Postgres host:", + default: "postgres", + }, + { + type: "input", + name: "API_POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password", + }, + { + type: "input", + name: "API_POSTGRES_PORT", + message: "Postgres port:", + default: "5432", + }, + { + type: "list", + name: "API_POSTGRES_SSL_MODE", + message: "Use Postgres SSL?", + choices: ["true", "false"], + default: "false", + }, + { + type: "input", + name: "API_POSTGRES_TEST_HOST", + message: "Postgres test host:", + default: "postgres-test", + }, + { + type: "input", + name: "API_POSTGRES_USER", + message: "Postgres user:", + default: "talawa", + }, + ]; + const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); + updateEnvVariable(answers); - Object.entries(answers).forEach(([key, value]) => { - process.env[key] = value; - }); + for (const [key, value] of Object.entries(answers)) { + process.env[key] = value; + } - console.log("Environment variables updated."); + console.log("Environment variables updated."); } export async function cloudbeaverSetup(): Promise { - const questions = [ - { - type: "input", - name: "CLOUDBEAVER_ADMIN_NAME", - message: "CloudBeaver admin name:", - default: "talawa" - }, - { - type: "input", - name: "CLOUDBEAVER_ADMIN_PASSWORD", - message: "CloudBeaver admin password:", - default: "password" - }, - { - type: "input", - name: "CLOUDBEAVER_MAPPED_HOST_IP", - message: "CloudBeaver mapped host IP:", - default: "127.0.0.1" - }, - { - type: "input", - name: "CLOUDBEAVER_MAPPED_PORT", - message: "CloudBeaver mapped port:", - default: "8978" - }, - { - type: "input", - name: "CLOUDBEAVER_SERVER_NAME", - message: "CloudBeaver server name:", - default: "Talawa CloudBeaver Server" - }, - { - type: "input", - name: "CLOUDBEAVER_SERVER_URL", - message: "CloudBeaver server URL:", - default: "http://127.0.0.1:8978" - } - ]; + const questions = [ + { + type: "input", + name: "CLOUDBEAVER_ADMIN_NAME", + message: "CloudBeaver admin name:", + default: "talawa", + }, + { + type: "input", + name: "CLOUDBEAVER_ADMIN_PASSWORD", + message: "CloudBeaver admin password:", + default: "password", + }, + { + type: "input", + name: "CLOUDBEAVER_MAPPED_HOST_IP", + message: "CloudBeaver mapped host IP:", + default: "127.0.0.1", + }, + { + type: "input", + name: "CLOUDBEAVER_MAPPED_PORT", + message: "CloudBeaver mapped port:", + default: "8978", + }, + { + type: "input", + name: "CLOUDBEAVER_SERVER_NAME", + message: "CloudBeaver server name:", + default: "Talawa CloudBeaver Server", + }, + { + type: "input", + name: "CLOUDBEAVER_SERVER_URL", + message: "CloudBeaver server URL:", + default: "http://127.0.0.1:8978", + }, + ]; - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); - Object.entries(answers).forEach(([key, value]) => { - process.env[key] = value; - }); - console.log("CloudBeaver environment variables updated."); + for (const [key, value] of Object.entries(answers)) { + process.env[key] = value; + } + console.log("CloudBeaver environment variables updated."); } export async function minioSetup(): Promise { - const questions = [ - { - type: "input", - name: "MINIO_BROWSER", - message: "Minio browser (on/off):", - default: "on" - }, - { - type: "input", - name: "MINIO_API_MAPPED_HOST_IP", - message: "Minio API mapped host IP:", - default: "127.0.0.1" - }, - { - type: "input", - name: "MINIO_API_MAPPED_PORT", - message: "Minio API mapped port:", - default: "9000" - }, - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_HOST_IP", - message: "Minio console mapped host IP:", - default: "127.0.0.1" - }, - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_PORT", - message: "Minio console mapped port:", - default: "9001" - }, - { - type: "input", - name: "MINIO_ROOT_PASSWORD", - message: "Minio root password:", - default: "password" - }, - { - type: "input", - name: "MINIO_ROOT_USER", - message: "Minio root user:", - default: "talawa" - } - ]; + const questions = [ + { + type: "input", + name: "MINIO_BROWSER", + message: "Minio browser (on/off):", + default: "on", + }, + { + type: "input", + name: "MINIO_API_MAPPED_HOST_IP", + message: "Minio API mapped host IP:", + default: "127.0.0.1", + }, + { + type: "input", + name: "MINIO_API_MAPPED_PORT", + message: "Minio API mapped port:", + default: "9000", + }, + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_HOST_IP", + message: "Minio console mapped host IP:", + default: "127.0.0.1", + }, + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_PORT", + message: "Minio console mapped port:", + default: "9001", + }, + { + type: "input", + name: "MINIO_ROOT_PASSWORD", + message: "Minio root password:", + default: "password", + }, + { + type: "input", + name: "MINIO_ROOT_USER", + message: "Minio root user:", + default: "talawa", + }, + ]; - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); - Object.entries(answers).forEach(([key, value]) => { - process.env[key] = value; - }); - console.log("Minio environment variables updated."); + for (const [key, value] of Object.entries(answers)) { + process.env[key] = value; + } + console.log("Minio environment variables updated."); } export async function postgresSetup(): Promise { - const questions = [ - { - type: "input", - name: "POSTGRES_DB", - message: "Postgres database:", - default: "talawa" - }, - { - type: "input", - name: "POSTGRES_MAPPED_HOST_IP", - message: "Postgres mapped host IP:", - default: "127.0.0.1" - }, - { - type: "input", - name: "POSTGRES_MAPPED_PORT", - message: "Postgres mapped port:", - default: "5432" - }, - { - type: "input", - name: "POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password" - }, - { - type: "input", - name: "POSTGRES_USER", - message: "Postgres user:", - default: "talawa" - } - ]; + const questions = [ + { + type: "input", + name: "POSTGRES_DB", + message: "Postgres database:", + default: "talawa", + }, + { + type: "input", + name: "POSTGRES_MAPPED_HOST_IP", + message: "Postgres mapped host IP:", + default: "127.0.0.1", + }, + { + type: "input", + name: "POSTGRES_MAPPED_PORT", + message: "Postgres mapped port:", + default: "5432", + }, + { + type: "input", + name: "POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password", + }, + { + type: "input", + name: "POSTGRES_USER", + message: "Postgres user:", + default: "talawa", + }, + ]; - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); + const answers = await inquirer.prompt(questions); + updateEnvVariable(answers); - Object.entries(answers).forEach(([key, value]) => { - process.env[key] = value; - }); - console.log("Postgres environment variables updated."); + for (const [key, value] of Object.entries(answers)) { + process.env[key] = value; + } + console.log("Postgres environment variables updated."); } export async function main(): Promise { - dotenv.config({ path: envFileName }); - checkEnvFile(); - backupEnvFile(); + dotenv.config({ path: envFileName }); + checkEnvFile(); + backupEnvFile(); - process.on("SIGINT", () => { - console.log("\nProcess interrupted! Undoing changes..."); - restoreEnvFile(); - process.exit(1); - }); + process.on("SIGINT", () => { + console.log("\nProcess interrupted! Undoing changes..."); + restoreEnvFile(); + process.exit(1); + }); - const {useDefaultApi} = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultApi", - message: "Do you want to use the recommended default API settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultApi) { - await apiSetup(); - } + const { useDefaultApi } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultApi", + message: "Do you want to use the recommended default API settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultApi) { + await apiSetup(); + } - const { useDefaultMinio } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultMinio", - message: "Do you want to use the recommended default Minio settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultMinio) { - await minioSetup(); - } + const { useDefaultMinio } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultMinio", + message: + "Do you want to use the recommended default Minio settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultMinio) { + await minioSetup(); + } - const { useDefaultCloudbeaver } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultCloudbeaver", - message: - "Do you want to use the recommended default CloudBeaver settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultCloudbeaver) { - await cloudbeaverSetup(); - } + const { useDefaultCloudbeaver } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultCloudbeaver", + message: + "Do you want to use the recommended default CloudBeaver settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultCloudbeaver) { + await cloudbeaverSetup(); + } - const { useDefaultPostgres } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultPostgres", - message: - "Do you want to use the recommended default Postgres settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultPostgres) { - await postgresSetup(); - } - await setNodeEnvironment(); - await administratorEmail(); + const { useDefaultPostgres } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultPostgres", + message: + "Do you want to use the recommended default Postgres settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultPostgres) { + await postgresSetup(); + } + await setNodeEnvironment(); + await administratorEmail(); - console.log("Configuration complete."); + console.log("Configuration complete."); } main().catch((err) => { - restoreEnvFile(); + restoreEnvFile(); }); diff --git a/src/setup/updateEnvVariable.ts b/src/setup/updateEnvVariable.ts index 5e158dd3052..c021099e046 100644 --- a/src/setup/updateEnvVariable.ts +++ b/src/setup/updateEnvVariable.ts @@ -1,4 +1,4 @@ -import fs from "fs"; +import fs from "node:fs"; // Update the value of an environment variable in .env file /** @@ -10,23 +10,23 @@ import fs from "fs"; */ export function updateEnvVariable(config: { - [key: string]: string | number; + [key: string]: string | number; }): void { - if (process.env.NODE_ENV === "test") { - const existingContent: string = fs.readFileSync(".env_test", "utf8"); - let updatedContent: string = existingContent; - for (const key in config) { - const regex = new RegExp(`^${key}=.*`, "gm"); - updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); - } - fs.writeFileSync(".env_test", updatedContent, "utf8"); - } else { - const existingContent: string = fs.readFileSync(".env", "utf8"); - let updatedContent: string = existingContent; - for (const key in config) { - const regex = new RegExp(`^${key}=.*`, "gm"); - updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); - } - fs.writeFileSync(".env", updatedContent, "utf8"); - } -} \ No newline at end of file + if (process.env.NODE_ENV === "test") { + const existingContent: string = fs.readFileSync(".env_test", "utf8"); + let updatedContent: string = existingContent; + for (const key in config) { + const regex = new RegExp(`^${key}=.*`, "gm"); + updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + } + fs.writeFileSync(".env_test", updatedContent, "utf8"); + } else { + const existingContent: string = fs.readFileSync(".env", "utf8"); + let updatedContent: string = existingContent; + for (const key in config) { + const regex = new RegExp(`^${key}=.*`, "gm"); + updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + } + fs.writeFileSync(".env", updatedContent, "utf8"); + } +} diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts index 1295b375ee7..f522358b914 100644 --- a/test/setup/administratorEmail.test.ts +++ b/test/setup/administratorEmail.test.ts @@ -1,23 +1,23 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { administratorEmail } from "setup"; import inquirer from "inquirer"; +import { administratorEmail } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> askForAdministratorEmail", () => { - const originalEmail = process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS; + const originalEmail = process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS; + + afterEach(() => { + process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = originalEmail; + }); - afterEach(() => { - process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = originalEmail; - }); + it("should prompt the user for an email and update the email env", async () => { + const mockedEmail = "testuser@email.com"; - it("should prompt the user for an email and update the email env", async () => { - const mockedEmail = "testuser@email.com"; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ email: mockedEmail }); - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ email: mockedEmail }); + await administratorEmail(); - await administratorEmail(); - - expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); - }); + expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); + }); }); diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts index 57413ea82f2..0f5fe00e9e3 100644 --- a/test/setup/apiSetup.test.ts +++ b/test/setup/apiSetup.test.ts @@ -1,47 +1,46 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { apiSetup } from "setup"; import inquirer from "inquirer"; +import { apiSetup } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> apiSetup", () => { - const originalEnv = { ...process.env }; - - afterEach(() => { - process.env = { ...originalEnv }; - vi.resetAllMocks(); - }); + const originalEnv = { ...process.env }; - it("should prompt the user for API configuration and update process.env", async () => { - const mockedAnswers = { - API_BASE_URL: "http://localhost:5000", - API_HOST: "127.0.0.1", - API_PORT: "5000", - API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", - API_IS_GRAPHIQL: "false", - API_IS_PINO_PRETTY: "true", - API_JWT_EXPIRES_IN: "3600000", - API_JWT_SECRET: "mocked-secret", - API_MINIO_ACCESS_KEY: "mocked-access-key", - API_MINIO_END_POINT: "mocked-endpoint", - API_MINIO_PORT: "9001", - API_MINIO_SECRET_KEY: "mocked-secret-key", - API_MINIO_TEST_END_POINT: "mocked-test-endpoint", - API_MINIO_USE_SSL: "true", - API_POSTGRES_DATABASE: "mocked-database", - API_POSTGRES_HOST: "mocked-host", - API_POSTGRES_PASSWORD: "mocked-password", - API_POSTGRES_PORT: "5433", - API_POSTGRES_SSL_MODE: "true", - API_POSTGRES_TEST_HOST: "mocked-test-host", - API_POSTGRES_USER: "mocked-user", - }; + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + it("should prompt the user for API configuration and update process.env", async () => { + const mockedAnswers = { + API_BASE_URL: "http://localhost:5000", + API_HOST: "127.0.0.1", + API_PORT: "5000", + API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", + API_IS_GRAPHIQL: "false", + API_IS_PINO_PRETTY: "true", + API_JWT_EXPIRES_IN: "3600000", + API_JWT_SECRET: "mocked-secret", + API_MINIO_ACCESS_KEY: "mocked-access-key", + API_MINIO_END_POINT: "mocked-endpoint", + API_MINIO_PORT: "9001", + API_MINIO_SECRET_KEY: "mocked-secret-key", + API_MINIO_TEST_END_POINT: "mocked-test-endpoint", + API_MINIO_USE_SSL: "true", + API_POSTGRES_DATABASE: "mocked-database", + API_POSTGRES_HOST: "mocked-host", + API_POSTGRES_PASSWORD: "mocked-password", + API_POSTGRES_PORT: "5433", + API_POSTGRES_SSL_MODE: "true", + API_POSTGRES_TEST_HOST: "mocked-test-host", + API_POSTGRES_USER: "mocked-user", + }; - await apiSetup(); - expect(process.env.API_BASE_URL).toBe("http://localhost:5000") - expect(process.env.API_HOST).toBe("127.0.0.1") + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - }); + await apiSetup(); + expect(process.env.API_BASE_URL).toBe("http://localhost:5000"); + expect(process.env.API_HOST).toBe("127.0.0.1"); + }); }); diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts index 40c2ee299d2..d98a8163ed3 100644 --- a/test/setup/cloudbeaverSetup.test.ts +++ b/test/setup/cloudbeaverSetup.test.ts @@ -1,36 +1,36 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { cloudbeaverSetup } from "setup"; import inquirer from "inquirer"; +import { cloudbeaverSetup } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> cloudbeaverSetup", () => { - const originalEnv = { ...process.env }; + const originalEnv = { ...process.env }; - afterEach(() => { - process.env = { ...originalEnv }; - vi.resetAllMocks(); - }); + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); - it("should prompt the user for CloudBeaver configuration and update process.env", async () => { - const mockedAnswers = { - CLOUDBEAVER_ADMIN_NAME: "mocked-admin", - CLOUDBEAVER_ADMIN_PASSWORD: "mocked-password", - CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", - CLOUDBEAVER_MAPPED_PORT: "8080", - CLOUDBEAVER_SERVER_NAME: "Mocked Server", - CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080", - }; + it("should prompt the user for CloudBeaver configuration and update process.env", async () => { + const mockedAnswers = { + CLOUDBEAVER_ADMIN_NAME: "mocked-admin", + CLOUDBEAVER_ADMIN_PASSWORD: "mocked-password", + CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", + CLOUDBEAVER_MAPPED_PORT: "8080", + CLOUDBEAVER_SERVER_NAME: "Mocked Server", + CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080", + }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - await cloudbeaverSetup(); + await cloudbeaverSetup(); - expect(process.env.CLOUDBEAVER_ADMIN_NAME).toBe("mocked-admin"); - expect(process.env.CLOUDBEAVER_ADMIN_PASSWORD).toBe("mocked-password"); - expect(process.env.CLOUDBEAVER_MAPPED_HOST_IP).toBe("127.0.0.1"); - expect(process.env.CLOUDBEAVER_MAPPED_PORT).toBe("8080"); - expect(process.env.CLOUDBEAVER_SERVER_NAME).toBe("Mocked Server"); - expect(process.env.CLOUDBEAVER_SERVER_URL).toBe("http://127.0.0.1:8080"); - }); + expect(process.env.CLOUDBEAVER_ADMIN_NAME).toBe("mocked-admin"); + expect(process.env.CLOUDBEAVER_ADMIN_PASSWORD).toBe("mocked-password"); + expect(process.env.CLOUDBEAVER_MAPPED_HOST_IP).toBe("127.0.0.1"); + expect(process.env.CLOUDBEAVER_MAPPED_PORT).toBe("8080"); + expect(process.env.CLOUDBEAVER_SERVER_NAME).toBe("Mocked Server"); + expect(process.env.CLOUDBEAVER_SERVER_URL).toBe("http://127.0.0.1:8080"); + }); }); diff --git a/test/setup/minioSetup.test.ts b/test/setup/minioSetup.test.ts index 07bf210cf37..ce0c6db192a 100644 --- a/test/setup/minioSetup.test.ts +++ b/test/setup/minioSetup.test.ts @@ -1,38 +1,38 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { minioSetup } from "setup"; import inquirer from "inquirer"; +import { minioSetup } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> minioSetup", () => { - const originalEnv = { ...process.env }; + const originalEnv = { ...process.env }; - afterEach(() => { - process.env = { ...originalEnv }; - vi.resetAllMocks(); - }); + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); - it("should prompt the user for Minio configuration and update process.env", async () => { - const mockedAnswers = { - MINIO_BROWSER: "off", - MINIO_API_MAPPED_HOST_IP: "192.168.1.10", - MINIO_API_MAPPED_PORT: "8000", - MINIO_CONSOLE_MAPPED_HOST_IP: "192.168.1.20", - MINIO_CONSOLE_MAPPED_PORT: "8001", - MINIO_ROOT_PASSWORD: "mocked-password", - MINIO_ROOT_USER: "mocked-user", - }; + it("should prompt the user for Minio configuration and update process.env", async () => { + const mockedAnswers = { + MINIO_BROWSER: "off", + MINIO_API_MAPPED_HOST_IP: "192.168.1.10", + MINIO_API_MAPPED_PORT: "8000", + MINIO_CONSOLE_MAPPED_HOST_IP: "192.168.1.20", + MINIO_CONSOLE_MAPPED_PORT: "8001", + MINIO_ROOT_PASSWORD: "mocked-password", + MINIO_ROOT_USER: "mocked-user", + }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - await minioSetup(); + await minioSetup(); - expect(process.env.MINIO_BROWSER).toBe("off"); - expect(process.env.MINIO_API_MAPPED_HOST_IP).toBe("192.168.1.10"); - expect(process.env.MINIO_API_MAPPED_PORT).toBe("8000"); - expect(process.env.MINIO_CONSOLE_MAPPED_HOST_IP).toBe("192.168.1.20"); - expect(process.env.MINIO_CONSOLE_MAPPED_PORT).toBe("8001"); - expect(process.env.MINIO_ROOT_PASSWORD).toBe("mocked-password"); - expect(process.env.MINIO_ROOT_USER).toBe("mocked-user"); - }); + expect(process.env.MINIO_BROWSER).toBe("off"); + expect(process.env.MINIO_API_MAPPED_HOST_IP).toBe("192.168.1.10"); + expect(process.env.MINIO_API_MAPPED_PORT).toBe("8000"); + expect(process.env.MINIO_CONSOLE_MAPPED_HOST_IP).toBe("192.168.1.20"); + expect(process.env.MINIO_CONSOLE_MAPPED_PORT).toBe("8001"); + expect(process.env.MINIO_ROOT_PASSWORD).toBe("mocked-password"); + expect(process.env.MINIO_ROOT_USER).toBe("mocked-user"); + }); }); diff --git a/test/setup/postgresSetup.test.ts b/test/setup/postgresSetup.test.ts index e765cc064a6..254304d5e99 100644 --- a/test/setup/postgresSetup.test.ts +++ b/test/setup/postgresSetup.test.ts @@ -1,34 +1,34 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { postgresSetup } from "setup"; import inquirer from "inquirer"; +import { postgresSetup } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> postgresSetup", () => { - const originalEnv = { ...process.env }; + const originalEnv = { ...process.env }; - afterEach(() => { - process.env = { ...originalEnv }; - vi.resetAllMocks(); - }); + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); - it("should prompt the user for Postgres configuration and update process.env", async () => { - const mockedAnswers = { - POSTGRES_DB: "mocked-db", - POSTGRES_MAPPED_HOST_IP: "192.168.1.100", - POSTGRES_MAPPED_PORT: "5434", - POSTGRES_PASSWORD: "mocked-password", - POSTGRES_USER: "mocked-user", - }; + it("should prompt the user for Postgres configuration and update process.env", async () => { + const mockedAnswers = { + POSTGRES_DB: "mocked-db", + POSTGRES_MAPPED_HOST_IP: "192.168.1.100", + POSTGRES_MAPPED_PORT: "5434", + POSTGRES_PASSWORD: "mocked-password", + POSTGRES_USER: "mocked-user", + }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - await postgresSetup(); + await postgresSetup(); - expect(process.env.POSTGRES_DB).toBe("mocked-db"); - expect(process.env.POSTGRES_MAPPED_HOST_IP).toBe("192.168.1.100"); - expect(process.env.POSTGRES_MAPPED_PORT).toBe("5434"); - expect(process.env.POSTGRES_PASSWORD).toBe("mocked-password"); - expect(process.env.POSTGRES_USER).toBe("mocked-user"); - }); + expect(process.env.POSTGRES_DB).toBe("mocked-db"); + expect(process.env.POSTGRES_MAPPED_HOST_IP).toBe("192.168.1.100"); + expect(process.env.POSTGRES_MAPPED_PORT).toBe("5434"); + expect(process.env.POSTGRES_PASSWORD).toBe("mocked-password"); + expect(process.env.POSTGRES_USER).toBe("mocked-user"); + }); }); diff --git a/test/setup/setNodeEnviorment.test.ts b/test/setup/setNodeEnviorment.test.ts index 5d53e7383c0..a18943036a4 100644 --- a/test/setup/setNodeEnviorment.test.ts +++ b/test/setup/setNodeEnviorment.test.ts @@ -1,68 +1,74 @@ -import { describe, it, vi, expect, afterEach } from "vitest"; -import { setNodeEnvironment } from "setup"; import inquirer from "inquirer"; +import { setNodeEnvironment } from "setup"; +import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("inquirer"); describe("Setup -> setNodeEnvironment", () => { - const originalNodeEnv = process.env.NODE_ENV; + const originalNodeEnv = process.env.NODE_ENV; - afterEach(() => { - process.env.NODE_ENV = originalNodeEnv; - vi.resetAllMocks(); - }); + afterEach(() => { + process.env.NODE_ENV = originalNodeEnv; + vi.resetAllMocks(); + }); - it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { - const mockedNodeEnv = "production"; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ nodeEnv: mockedNodeEnv }); - process.env.NODE_ENV = "test"; + it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { + const mockedNodeEnv = "production"; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + nodeEnv: mockedNodeEnv, + }); + process.env.NODE_ENV = "test"; - await setNodeEnvironment(); + await setNodeEnvironment(); - expect(process.env.NODE_ENV).toBe(mockedNodeEnv); - expect(inquirer.prompt).toHaveBeenCalledWith([ - { - type: "list", - name: "nodeEnv", - message: "Select Node environment:", - choices: ["development", "production"], - default: "development", - }, - ]); - }); + expect(process.env.NODE_ENV).toBe(mockedNodeEnv); + expect(inquirer.prompt).toHaveBeenCalledWith([ + { + type: "list", + name: "nodeEnv", + message: "Select Node environment:", + choices: ["development", "production"], + default: "development", + }, + ]); + }); - it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ nodeEnv: "development" }); // Mock inquirer prompt with default + it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + nodeEnv: "development", + }); // Mock inquirer prompt with default - process.env.NODE_ENV = "test"; + process.env.NODE_ENV = "test"; - await setNodeEnvironment(); + await setNodeEnvironment(); - expect(process.env.NODE_ENV).toBe("development"); - expect(inquirer.prompt).toHaveBeenCalled(); - }); + expect(process.env.NODE_ENV).toBe("development"); + expect(inquirer.prompt).toHaveBeenCalled(); + }); - it("should handle errors thrown by inquirer.prompt when NODE_ENV is 'test'", async () => { - const mockedError = new Error("Prompt failed"); - vi.spyOn(inquirer, "prompt").mockRejectedValueOnce(mockedError); // Mock inquirer to throw an error + it("should handle errors thrown by inquirer.prompt when NODE_ENV is 'test'", async () => { + const mockedError = new Error("Prompt failed"); + vi.spyOn(inquirer, "prompt").mockRejectedValueOnce(mockedError); // Mock inquirer to throw an error - process.env.NODE_ENV = "test"; + process.env.NODE_ENV = "test"; - const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); + const consoleErrorSpy = vi + .spyOn(console, "error") + .mockImplementation(() => {}); - await expect(setNodeEnvironment()).rejects.toThrow(); + await expect(setNodeEnvironment()).rejects.toThrow(); - expect(consoleErrorSpy).toHaveBeenCalledWith(mockedError); + expect(consoleErrorSpy).toHaveBeenCalledWith(mockedError); - consoleErrorSpy.mockRestore(); - }); + consoleErrorSpy.mockRestore(); + }); - it("should leave NODE_ENV unchanged when not in 'test' environment", async () => { - process.env.NODE_ENV = "development"; + it("should leave NODE_ENV unchanged when not in 'test' environment", async () => { + process.env.NODE_ENV = "development"; - await setNodeEnvironment(); + await setNodeEnvironment(); - expect(process.env.NODE_ENV).toBe("development"); - expect(inquirer.prompt).not.toHaveBeenCalled(); - }); + expect(process.env.NODE_ENV).toBe("development"); + expect(inquirer.prompt).not.toHaveBeenCalled(); + }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 58057c6bd44..4b3d42a8006 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,9 +2,11 @@ import tsconfigPaths from "vite-tsconfig-paths"; import { defineConfig } from "vitest/config"; export default defineConfig({ - plugins: [tsconfigPaths({ - ignoreConfigErrors: true, - })], + plugins: [ + tsconfigPaths({ + ignoreConfigErrors: true, + }), + ], test: { coverage: { provider: "v8", // or 'istanbul' if you prefer From 06de1a173a2c2fd4c0cf19eb8899def73b5ed58a Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Tue, 28 Jan 2025 22:13:24 +0530 Subject: [PATCH 04/10] setup env for testing config --- package.json | 1 + pnpm-lock.yaml | 18 + setup.ts | 450 +---------------- src/setup.ts | 674 ++++++++++++++++++++++++++ src/setup/updateEnvVariable.ts | 53 +- test/setup/administratorEmail.test.ts | 7 +- test/setup/apiSetup.test.ts | 50 +- test/setup/cloudbeaverSetup.test.ts | 34 +- test/setup/minioSetup.test.ts | 36 +- test/setup/postgresSetup.test.ts | 31 +- test/setup/setNodeEnviorment.test.ts | 74 --- test/setup/setNodeEnvironment.test.ts | 47 ++ vitest.config.ts | 6 +- 13 files changed, 882 insertions(+), 599 deletions(-) create mode 100644 src/setup.ts delete mode 100644 test/setup/setNodeEnviorment.test.ts create mode 100644 test/setup/setNodeEnvironment.test.ts diff --git a/package.json b/package.json index ad3b622d3f3..f2d7d835b82 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@faker-js/faker": "^9.4.0", "@swc/cli": "0.6.0", "@swc/core": "^1.10.9", + "@types/inquirer": "^9.0.7", "@types/node": "^22.10.7", "@vitest/coverage-v8": "^3.0.3", "drizzle-kit": "^0.30.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f23fb052cc8..b2134893f8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -105,6 +105,9 @@ importers: '@swc/core': specifier: ^1.10.9 version: 1.10.9 + '@types/inquirer': + specifier: ^9.0.7 + version: 9.0.7 '@types/node': specifier: ^22.10.7 version: 22.10.7 @@ -1433,9 +1436,15 @@ packages: '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@types/inquirer@9.0.7': + resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} + '@types/node@22.10.7': resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + '@vitest/coverage-v8@3.0.3': resolution: {integrity: sha512-uVbJ/xhImdNtzPnLyxCZJMTeTIYdgcC2nWtBBBpR1H6z0w8m7D+9/zrDIx2nNxgMg9r+X8+RY2qVpUDeW2b3nw==} peerDependencies: @@ -4095,10 +4104,19 @@ snapshots: '@types/http-cache-semantics@4.0.4': {} + '@types/inquirer@9.0.7': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.1 + '@types/node@22.10.7': dependencies: undici-types: 6.20.0 + '@types/through@0.0.33': + dependencies: + '@types/node': 22.10.7 + '@vitest/coverage-v8@3.0.3(vitest@3.0.3(@types/node@22.10.7)(tsx@4.19.2))': dependencies: '@ampproject/remapping': 2.3.0 diff --git a/setup.ts b/setup.ts index dc1d8f56ea1..ae710b97ac6 100644 --- a/setup.ts +++ b/setup.ts @@ -1,448 +1,8 @@ -import fs from "node:fs"; -import { abort } from "node:process"; -import dotenv from "dotenv"; -import inquirer from "inquirer"; -import { updateEnvVariable } from "./src/setup/updateEnvVariable"; +import { setup } from "./src/setup"; +import { restoreEnvFile } from "./src/setup"; -let originalEnvContent: string | null = null; -const envFileName = ".env"; - -function backupEnvFile(): void { - if (fs.existsSync(envFileName)) { - originalEnvContent = fs.readFileSync(envFileName, "utf-8"); - } else { - originalEnvContent = null; - } -} - -function restoreEnvFile(): void { - try { - if (originalEnvContent !== null) { - fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); - console.log("\nChanges undone. Restored the original environment file."); - } else if (fs.existsSync(envFileName)) { - fs.unlinkSync(envFileName); - console.log("\nChanges undone. Removed the environment file."); - } - } catch (err) { - console.error("Error restoring env file:", err); - } -} - -function checkEnvFile(): void { - const envFileName = ".env"; - - const envDevcontainer = dotenv.parse( - fs.readFileSync("envFiles/.env.devcontainer"), - ); - - dotenv.config({ path: envFileName }); - const content = Object.entries(envDevcontainer) - .map(([key, value]) => `${key}=${value}`) - .join("\n"); - fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); -} - -export async function setNodeEnvironment(): Promise { - try { - const { nodeEnv } = await inquirer.prompt([ - { - type: "list", - name: "nodeEnv", - message: "Select Node environment:", - choices: ["development", "production"], - default: "development", - }, - ]); - process.env.NODE_ENV = nodeEnv; - } catch (err) { - console.error(err); - abort(); - } -} - -// Get the administratorEmail email -/** - * The function `administratorEmail` prompts the user for a administratorEmail email, updates a configuration file - * with the email, and handles any errors that occur. - */ - -export async function administratorEmail(): Promise { - try { - const { email } = await inquirer.prompt([ - { - type: "input", - name: "email", - message: "Enter email :", - }, - ]); - process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = email; - } catch (err) { - console.log(err); - abort(); - } -} - -export async function apiSetup(): Promise { - const questions = [ - { - type: "input", - name: "API_BASE_URL", - message: "API base URL:", - default: "http://127.0.0.1:4000", - }, - { - type: "input", - name: "API_HOST", - message: "API host:", - default: "0.0.0.0", - }, - { - type: "input", - name: "API_PORT", - message: "API port:", - default: "4000", - }, - { - type: "list", - name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", - message: "Apply Drizzle migrations?", - choices: ["true", "false"], - default: "true", - }, - { - type: "list", - name: "API_IS_GRAPHIQL", - message: "Enable GraphQL?", - choices: ["true", "false"], - default: "true", - }, - { - type: "list", - name: "API_IS_PINO_PRETTY", - message: "Enable Pino Pretty logs?", - choices: ["true", "false"], - default: "true", - }, - { - type: "input", - name: "API_JWT_EXPIRES_IN", - message: "JWT expiration (ms):", - default: "2592000000", - }, - { - type: "input", - name: "API_JWT_SECRET", - message: "JWT secret:", - default: - "b4896453be722d5ca94058a73f52b31c75980b485fa6d74d91f417a8059d8731", - }, - { - type: "input", - name: "API_MINIO_ACCESS_KEY", - message: "Minio access key:", - default: "talawa", - }, - { - type: "input", - name: "API_MINIO_END_POINT", - message: "Minio endpoint:", - default: "minio", - }, - { - type: "input", - name: "API_MINIO_PORT", - message: "Minio port:", - default: "9000", - }, - { - type: "input", - name: "API_MINIO_SECRET_KEY", - message: "Minio secret key:", - default: "password", - }, - { - type: "input", - name: "API_MINIO_TEST_END_POINT", - message: "Minio test endpoint:", - default: "minio-test", - }, - { - type: "list", - name: "API_MINIO_USE_SSL", - message: "Use Minio SSL?", - choices: ["true", "false"], - default: "false", - }, - { - type: "input", - name: "API_POSTGRES_DATABASE", - message: "Postgres database:", - default: "talawa", - }, - { - type: "input", - name: "API_POSTGRES_HOST", - message: "Postgres host:", - default: "postgres", - }, - { - type: "input", - name: "API_POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password", - }, - { - type: "input", - name: "API_POSTGRES_PORT", - message: "Postgres port:", - default: "5432", - }, - { - type: "list", - name: "API_POSTGRES_SSL_MODE", - message: "Use Postgres SSL?", - choices: ["true", "false"], - default: "false", - }, - { - type: "input", - name: "API_POSTGRES_TEST_HOST", - message: "Postgres test host:", - default: "postgres-test", - }, - { - type: "input", - name: "API_POSTGRES_USER", - message: "Postgres user:", - default: "talawa", - }, - ]; - const answers = await inquirer.prompt(questions); - - updateEnvVariable(answers); - - for (const [key, value] of Object.entries(answers)) { - process.env[key] = value; - } - - console.log("Environment variables updated."); -} - -export async function cloudbeaverSetup(): Promise { - const questions = [ - { - type: "input", - name: "CLOUDBEAVER_ADMIN_NAME", - message: "CloudBeaver admin name:", - default: "talawa", - }, - { - type: "input", - name: "CLOUDBEAVER_ADMIN_PASSWORD", - message: "CloudBeaver admin password:", - default: "password", - }, - { - type: "input", - name: "CLOUDBEAVER_MAPPED_HOST_IP", - message: "CloudBeaver mapped host IP:", - default: "127.0.0.1", - }, - { - type: "input", - name: "CLOUDBEAVER_MAPPED_PORT", - message: "CloudBeaver mapped port:", - default: "8978", - }, - { - type: "input", - name: "CLOUDBEAVER_SERVER_NAME", - message: "CloudBeaver server name:", - default: "Talawa CloudBeaver Server", - }, - { - type: "input", - name: "CLOUDBEAVER_SERVER_URL", - message: "CloudBeaver server URL:", - default: "http://127.0.0.1:8978", - }, - ]; - - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); - - for (const [key, value] of Object.entries(answers)) { - process.env[key] = value; - } - console.log("CloudBeaver environment variables updated."); -} - -export async function minioSetup(): Promise { - const questions = [ - { - type: "input", - name: "MINIO_BROWSER", - message: "Minio browser (on/off):", - default: "on", - }, - { - type: "input", - name: "MINIO_API_MAPPED_HOST_IP", - message: "Minio API mapped host IP:", - default: "127.0.0.1", - }, - { - type: "input", - name: "MINIO_API_MAPPED_PORT", - message: "Minio API mapped port:", - default: "9000", - }, - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_HOST_IP", - message: "Minio console mapped host IP:", - default: "127.0.0.1", - }, - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_PORT", - message: "Minio console mapped port:", - default: "9001", - }, - { - type: "input", - name: "MINIO_ROOT_PASSWORD", - message: "Minio root password:", - default: "password", - }, - { - type: "input", - name: "MINIO_ROOT_USER", - message: "Minio root user:", - default: "talawa", - }, - ]; - - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); - - for (const [key, value] of Object.entries(answers)) { - process.env[key] = value; - } - console.log("Minio environment variables updated."); -} - -export async function postgresSetup(): Promise { - const questions = [ - { - type: "input", - name: "POSTGRES_DB", - message: "Postgres database:", - default: "talawa", - }, - { - type: "input", - name: "POSTGRES_MAPPED_HOST_IP", - message: "Postgres mapped host IP:", - default: "127.0.0.1", - }, - { - type: "input", - name: "POSTGRES_MAPPED_PORT", - message: "Postgres mapped port:", - default: "5432", - }, - { - type: "input", - name: "POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password", - }, - { - type: "input", - name: "POSTGRES_USER", - message: "Postgres user:", - default: "talawa", - }, - ]; - - const answers = await inquirer.prompt(questions); - updateEnvVariable(answers); - - for (const [key, value] of Object.entries(answers)) { - process.env[key] = value; - } - console.log("Postgres environment variables updated."); -} - -export async function main(): Promise { - dotenv.config({ path: envFileName }); - checkEnvFile(); - backupEnvFile(); - - process.on("SIGINT", () => { - console.log("\nProcess interrupted! Undoing changes..."); - restoreEnvFile(); - process.exit(1); - }); - - const { useDefaultApi } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultApi", - message: "Do you want to use the recommended default API settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultApi) { - await apiSetup(); - } - - const { useDefaultMinio } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultMinio", - message: - "Do you want to use the recommended default Minio settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultMinio) { - await minioSetup(); - } - - const { useDefaultCloudbeaver } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultCloudbeaver", - message: - "Do you want to use the recommended default CloudBeaver settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultCloudbeaver) { - await cloudbeaverSetup(); - } - - const { useDefaultPostgres } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultPostgres", - message: - "Do you want to use the recommended default Postgres settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultPostgres) { - await postgresSetup(); - } - await setNodeEnvironment(); - await administratorEmail(); - - console.log("Configuration complete."); -} - -main().catch((err) => { +setup().catch((err) => { restoreEnvFile(); + console.error("An error occurred during setup:", err); + process.exit(1); }); diff --git a/src/setup.ts b/src/setup.ts new file mode 100644 index 00000000000..3e449774ba8 --- /dev/null +++ b/src/setup.ts @@ -0,0 +1,674 @@ +import crypto from "node:crypto"; +import fs from "node:fs"; +import { abort } from "node:process"; +import dotenv from "dotenv"; +import inquirer from "inquirer"; +import { updateEnvVariable } from "./setup/updateEnvVariable"; + +export function generateJwtSecret(): string { + try { + return crypto.randomBytes(32).toString("hex"); + } catch (err) { + console.error("Failed to generate JWT secret:", err); + throw new Error("Failed to generate JWT secret"); + } +} + +let originalEnvContent: string | null = null; +const envFileName = ".env"; + +function backupEnvFile(): void { + if (fs.existsSync(envFileName)) { + originalEnvContent = fs.readFileSync(envFileName, "utf-8"); + } else { + originalEnvContent = null; + } +} + +export function restoreEnvFile(): void { + try { + if (originalEnvContent !== null) { + fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); + console.log("\nChanges undone. Restored the original environment file."); + } else if (fs.existsSync(envFileName)) { + fs.unlinkSync(envFileName); + console.log("\nChanges undone. Removed the environment file."); + } + } catch (err) { + console.error("Error restoring env file:", err); + } +} + +function initializeEnvFile(): void { + const envFileToUse = + process.env.CI === "true" + ? "envFiles/.env.ci" + : "envFiles/.env.devcontainer"; + + const envFileName = ".env"; + + const parsedEnv = dotenv.parse(fs.readFileSync(envFileToUse)); + + dotenv.config({ path: envFileName }); + const content = Object.entries(parsedEnv) + .map(([key, value]) => `${key}=${value}`) + .join("\n"); + fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); +} + +export async function setCI(): Promise { + try { + const { CI } = await inquirer.prompt([ + { + type: "list", + name: "CI", + message: "Set CI:", + choices: ["true", "false"], + default: "false", + }, + ]); + updateEnvVariable({ CI }); + } catch (err) { + console.error(err); + abort(); + } +} + +export async function setNodeEnvironment(): Promise { + try { + const { NODE_ENV } = await inquirer.prompt([ + { + type: "list", + name: "NODE_ENV", + message: "Select Node environment:", + choices: ["development", "production", "test"], + default: "production", + }, + ]); + updateEnvVariable({ NODE_ENV }); + } catch (err) { + console.error(err); + abort(); + } +} + +export async function administratorEmail(): Promise { + try { + const { API_ADMINISTRATOR_USER_EMAIL_ADDRESS } = await inquirer.prompt([ + { + type: "input", + name: "API_ADMINISTRATOR_USER_EMAIL_ADDRESS", + message: "Enter email :", + default: "administrator@email.com", + }, + ]); + updateEnvVariable({ API_ADMINISTRATOR_USER_EMAIL_ADDRESS }); + } catch (err) { + console.log(err); + abort(); + } +} + +export async function apiSetup(): Promise { + const { API_BASE_URL } = await inquirer.prompt([ + { + type: "input", + name: "API_BASE_URL", + message: "API base URL:", + default: "http://127.0.0.1:4000", + validate: (input: string) => { + try { + new URL(input); + return true; + } catch { + return "Please enter a valid URL."; + } + }, + }, + ]); + updateEnvVariable({ API_BASE_URL }); + + const { API_HOST } = await inquirer.prompt([ + { + type: "input", + name: "API_HOST", + message: "API host:", + default: "0.0.0.0", + }, + ]); + updateEnvVariable({ API_HOST }); + + const { API_PORT } = await inquirer.prompt([ + { + type: "input", + name: "API_PORT", + message: "API port:", + default: "4000", + validate: (input: string) => { + const portNumber = Number(input); + if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { + return "Please enter a valid port number (1-65535)."; + } + return true; + }, + }, + ]); + updateEnvVariable({ API_PORT }); + + const { API_IS_APPLY_DRIZZLE_MIGRATIONS } = await inquirer.prompt([ + { + type: "list", + name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", + message: "Apply Drizzle migrations?", + choices: ["true", "false"], + default: "true", + }, + ]); + updateEnvVariable({ API_IS_APPLY_DRIZZLE_MIGRATIONS }); + + const { API_IS_GRAPHIQL } = await inquirer.prompt([ + { + type: "list", + name: "API_IS_GRAPHIQL", + message: "Enable GraphQL?", + choices: ["true", "false"], + default: process.env.CI === "false" ? "false" : "true", + }, + ]); + updateEnvVariable({ API_IS_GRAPHIQL }); + + const { API_IS_PINO_PRETTY } = await inquirer.prompt([ + { + type: "list", + name: "API_IS_PINO_PRETTY", + message: "Enable Pino Pretty logs?", + choices: ["true", "false"], + default: process.env.CI === "false" ? "false" : "true", + }, + ]); + updateEnvVariable({ API_IS_PINO_PRETTY }); + + const { API_JWT_EXPIRES_IN } = await inquirer.prompt([ + { + type: "input", + name: "API_JWT_EXPIRES_IN", + message: "JWT expiration (ms):", + default: "2592000000", + }, + ]); + updateEnvVariable({ API_JWT_EXPIRES_IN }); + + const jwtSecret = generateJwtSecret(); + + const { API_JWT_SECRET } = await inquirer.prompt([ + { + type: "input", + name: "API_JWT_SECRET", + message: "JWT secret:", + default: jwtSecret, + validate: (input: string) => { + if (input.length < 64) { + return "JWT secret must be at least 64 characters long."; + } + return true; + }, + }, + ]); + updateEnvVariable({ API_JWT_SECRET }); + + const { API_LOG_LEVEL } = await inquirer.prompt([ + { + type: "input", + name: "API_LOG_LEVEL", + message: "LOG level:", + choices: ["info", "debug"], + default: process.env.CI === "true" ? "info" : "debug", + }, + ]); + updateEnvVariable({ API_LOG_LEVEL }); + + const { API_MINIO_ACCESS_KEY } = await inquirer.prompt([ + { + type: "input", + name: "API_MINIO_ACCESS_KEY", + message: "Minio access key:", + default: "talawa", + }, + ]); + updateEnvVariable({ API_MINIO_ACCESS_KEY }); + + const { API_MINIO_END_POINT } = await inquirer.prompt([ + { + type: "input", + name: "API_MINIO_END_POINT", + message: "Minio endpoint:", + default: "minio", + }, + ]); + updateEnvVariable({ API_MINIO_END_POINT }); + + const { API_MINIO_PORT } = await inquirer.prompt([ + { + type: "input", + name: "API_MINIO_PORT", + message: "Minio port:", + default: "9000", + }, + ]); + updateEnvVariable({ API_MINIO_PORT }); + + const { API_MINIO_SECRET_KEY } = await inquirer.prompt([ + { + type: "input", + name: "API_MINIO_SECRET_KEY", + message: "Minio secret key:", + default: "password", + }, + ]); + updateEnvVariable({ API_MINIO_SECRET_KEY }); + + const { API_MINIO_TEST_END_POINT } = await inquirer.prompt([ + { + type: "input", + name: "API_MINIO_TEST_END_POINT", + message: "Minio test endpoint:", + default: "minio-test", + }, + ]); + updateEnvVariable({ API_MINIO_TEST_END_POINT }); + + const { API_MINIO_USE_SSL } = await inquirer.prompt([ + { + type: "list", + name: "API_MINIO_USE_SSL", + message: "Use Minio SSL?", + choices: ["true", "false"], + default: "false", + }, + ]); + updateEnvVariable({ API_MINIO_USE_SSL }); + + const { API_POSTGRES_DATABASE } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_DATABASE", + message: "Postgres database:", + default: "talawa", + }, + ]); + updateEnvVariable({ API_POSTGRES_DATABASE }); + + const { API_POSTGRES_HOST } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_HOST", + message: "Postgres host:", + default: "postgres", + }, + ]); + updateEnvVariable({ API_POSTGRES_HOST }); + + const { API_POSTGRES_PASSWORD } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password", + }, + ]); + updateEnvVariable({ API_POSTGRES_PASSWORD }); + + const { API_POSTGRES_PORT } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_PORT", + message: "Postgres port:", + default: "5432", + }, + ]); + updateEnvVariable({ API_POSTGRES_PORT }); + + const { API_POSTGRES_SSL_MODE } = await inquirer.prompt([ + { + type: "list", + name: "API_POSTGRES_SSL_MODE", + message: "Use Postgres SSL?", + choices: ["true", "false"], + default: "false", + }, + ]); + updateEnvVariable({ API_POSTGRES_SSL_MODE }); + + const { API_POSTGRES_TEST_HOST } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_TEST_HOST", + message: "Postgres test host:", + default: "postgres-test", + }, + ]); + updateEnvVariable({ API_POSTGRES_TEST_HOST }); + + const { API_POSTGRES_USER } = await inquirer.prompt([ + { + type: "input", + name: "API_POSTGRES_USER", + message: "Postgres user:", + default: "talawa", + }, + ]); + updateEnvVariable({ API_POSTGRES_USER }); + console.log("Environment variables updated."); +} + +export async function cloudbeaverSetup(): Promise { + const { CLOUDBEAVER_ADMIN_NAME } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_ADMIN_NAME", + message: "CloudBeaver admin name:", + default: "talawa", + }, + ]); + updateEnvVariable({ CLOUDBEAVER_ADMIN_NAME }); + + const { CLOUDBEAVER_ADMIN_PASSWORD } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_ADMIN_PASSWORD", + message: "CloudBeaver admin password:", + default: "password", + }, + ]); + updateEnvVariable({ CLOUDBEAVER_ADMIN_PASSWORD }); + + const { CLOUDBEAVER_MAPPED_HOST_IP } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_MAPPED_HOST_IP", + message: "CloudBeaver mapped host IP:", + default: "127.0.0.1", + }, + ]); + updateEnvVariable({ CLOUDBEAVER_MAPPED_HOST_IP }); + + const { CLOUDBEAVER_MAPPED_PORT } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_MAPPED_PORT", + message: "CloudBeaver mapped port:", + default: "8978", + validate: (input: string) => { + const portNumber = Number(input); + if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { + return "Please enter a valid port number (1-65535)."; + } + return true; + }, + }, + ]); + updateEnvVariable({ CLOUDBEAVER_MAPPED_PORT }); + + const { CLOUDBEAVER_SERVER_NAME } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_SERVER_NAME", + message: "CloudBeaver server name:", + default: "Talawa CloudBeaver Server", + }, + ]); + updateEnvVariable({ CLOUDBEAVER_SERVER_NAME }); + + const { CLOUDBEAVER_SERVER_URL } = await inquirer.prompt([ + { + type: "input", + name: "CLOUDBEAVER_SERVER_URL", + message: "CloudBeaver server URL:", + default: "http://127.0.0.1:8978", + validate: (input: string) => { + try { + new URL(input); + return true; + } catch { + return "Please enter a valid URL."; + } + }, + }, + ]); + updateEnvVariable({ CLOUDBEAVER_SERVER_URL }); + console.log("CloudBeaver environment variables updated."); +} + +export async function minioSetup(): Promise { + const { MINIO_BROWSER } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_BROWSER", + message: "Minio browser (on/off):", + default: process.env.CI === "true" ? "off" : "on", + }, + ]); + updateEnvVariable({ MINIO_BROWSER }); + + if (process.env.CI === "false") { + const { MINIO_API_MAPPED_HOST_IP } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_API_MAPPED_HOST_IP", + message: "Minio API mapped host IP:", + default: "127.0.0.1", + }, + ]); + updateEnvVariable({ MINIO_API_MAPPED_HOST_IP }); + + const { MINIO_API_MAPPED_PORT } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_API_MAPPED_PORT", + message: "Minio API mapped port:", + default: "9000", + validate: (input: string) => { + const portNumber = Number(input); + if ( + Number.isNaN(portNumber) || + portNumber <= 0 || + portNumber > 65535 + ) { + return "Please enter a valid port number (1-65535)."; + } + return true; + }, + }, + ]); + updateEnvVariable({ MINIO_API_MAPPED_PORT }); + + const { MINIO_CONSOLE_MAPPED_HOST_IP } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_HOST_IP", + message: "Minio console mapped host IP:", + default: "127.0.0.1", + }, + ]); + updateEnvVariable({ MINIO_CONSOLE_MAPPED_HOST_IP }); + + const { MINIO_CONSOLE_MAPPED_PORT } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_CONSOLE_MAPPED_PORT", + message: "Minio console mapped port:", + default: "9001", + validate: (input: string) => { + const portNumber = Number(input); + if ( + Number.isNaN(portNumber) || + portNumber <= 0 || + portNumber > 65535 + ) { + return "Please enter a valid port number (1-65535)."; + } + return true; + }, + }, + ]); + updateEnvVariable({ MINIO_CONSOLE_MAPPED_PORT }); + } + + const { MINIO_ROOT_PASSWORD } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_ROOT_PASSWORD", + message: "Minio root password:", + default: "password", + }, + ]); + updateEnvVariable({ MINIO_ROOT_PASSWORD }); + + const { MINIO_ROOT_USER } = await inquirer.prompt([ + { + type: "input", + name: "MINIO_ROOT_USER", + message: "Minio root user:", + default: "talawa", + }, + ]); + updateEnvVariable({ MINIO_ROOT_USER }); + console.log("Minio environment variables updated."); +} + +export async function postgresSetup(): Promise { + console.log("\n--- Postgres Setup ---"); + + const { POSTGRES_DB } = await inquirer.prompt([ + { + type: "input", + name: "POSTGRES_DB", + message: "Postgres database:", + default: "talawa", + }, + ]); + updateEnvVariable({ POSTGRES_DB }); + + if (process.env.CI === "false") { + const { POSTGRES_MAPPED_HOST_IP } = await inquirer.prompt([ + { + type: "input", + name: "POSTGRES_MAPPED_HOST_IP", + message: "Postgres mapped host IP:", + default: "127.0.0.1", + }, + ]); + updateEnvVariable({ POSTGRES_MAPPED_HOST_IP }); + + const { POSTGRES_MAPPED_PORT } = await inquirer.prompt([ + { + type: "input", + name: "POSTGRES_MAPPED_PORT", + message: "Postgres mapped port:", + default: "5432", + validate: (input: string) => { + const portNumber = Number(input); + if ( + Number.isNaN(portNumber) || + portNumber <= 0 || + portNumber > 65535 + ) { + return "Please enter a valid port number (1-65535)."; + } + return true; + }, + }, + ]); + updateEnvVariable({ POSTGRES_MAPPED_PORT }); + } + + const { POSTGRES_PASSWORD } = await inquirer.prompt([ + { + type: "input", + name: "POSTGRES_PASSWORD", + message: "Postgres password:", + default: "password", + }, + ]); + updateEnvVariable({ POSTGRES_PASSWORD }); + + const { POSTGRES_USER } = await inquirer.prompt([ + { + type: "input", + name: "POSTGRES_USER", + message: "Postgres user:", + default: "talawa", + }, + ]); + updateEnvVariable({ POSTGRES_USER }); +} + +export async function setup(): Promise { + dotenv.config({ path: envFileName }); + backupEnvFile(); + await setCI(); + initializeEnvFile(); + + process.on("SIGINT", () => { + console.log("\nProcess interrupted! Undoing changes..."); + restoreEnvFile(); + process.exit(1); + }); + await setNodeEnvironment(); + + const { useDefaultApi } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultApi", + message: "Do you want to use the recommended default API settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultApi) { + await apiSetup(); + } + + const { useDefaultMinio } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultMinio", + message: + "Do you want to use the recommended default Minio settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultMinio) { + await minioSetup(); + } + + if (process.env.CI === "false") { + const { useDefaultCloudbeaver } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultCloudbeaver", + message: + "Do you want to use the recommended default CloudBeaver settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultCloudbeaver) { + await cloudbeaverSetup(); + } + } + + const { useDefaultPostgres } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultPostgres", + message: + "Do you want to use the recommended default Postgres settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultPostgres) { + await postgresSetup(); + } + await administratorEmail(); + + console.log("Configuration complete."); +} diff --git a/src/setup/updateEnvVariable.ts b/src/setup/updateEnvVariable.ts index c021099e046..0067254a8b8 100644 --- a/src/setup/updateEnvVariable.ts +++ b/src/setup/updateEnvVariable.ts @@ -1,32 +1,41 @@ import fs from "node:fs"; -// 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. + * Updates environment variables in the .env or .env_test file and synchronizes them with `process.env`. + * @param config - An object containing key-value pairs where the keys are the environment variable names and + * the values are the new values for those variables. */ - export function updateEnvVariable(config: { [key: string]: string | number; }): void { - if (process.env.NODE_ENV === "test") { - const existingContent: string = fs.readFileSync(".env_test", "utf8"); - let updatedContent: string = existingContent; - for (const key in config) { - const regex = new RegExp(`^${key}=.*`, "gm"); - updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); - } - fs.writeFileSync(".env_test", updatedContent, "utf8"); - } else { - const existingContent: string = fs.readFileSync(".env", "utf8"); - let updatedContent: string = existingContent; - for (const key in config) { - const regex = new RegExp(`^${key}=.*`, "gm"); - updatedContent = updatedContent.replace(regex, `${key}=${config[key]}`); + const envFileName = process.env.NODE_ENV === "test" ? ".env_test" : ".env"; + + // Read the existing content of the .env or .env_test file + const existingContent: string = fs.existsSync(envFileName) + ? fs.readFileSync(envFileName, "utf8") + : ""; + + let updatedContent: string = existingContent; + + // Update the .env file and process.env for each variable + for (const key in config) { + const value = config[key]; + const regex = new RegExp( + `^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=.*`, + "gm", + ); + + // Update or add the variable in the .env file + if (regex.test(updatedContent)) { + updatedContent = updatedContent.replace(regex, `${key}=${value}`); + } else { + updatedContent += `\n${key}=${value}`; } - fs.writeFileSync(".env", updatedContent, "utf8"); + + // Update the variable in process.env + process.env[key] = String(value); } + + // Write the updated content back to the .env or .env_test file + fs.writeFileSync(envFileName, updatedContent, "utf8"); } diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts index f522358b914..59fb5922e45 100644 --- a/test/setup/administratorEmail.test.ts +++ b/test/setup/administratorEmail.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; -import { administratorEmail } from "setup"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { administratorEmail } from "~/src/setup"; vi.mock("inquirer"); @@ -14,9 +14,12 @@ describe("Setup -> askForAdministratorEmail", () => { it("should prompt the user for an email and update the email env", async () => { const mockedEmail = "testuser@email.com"; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ email: mockedEmail }); + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + API_ADMINISTRATOR_USER_EMAIL_ADDRESS: mockedEmail, + }); await administratorEmail(); + console.log(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS); expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); }); diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts index 0f5fe00e9e3..5f4cede7437 100644 --- a/test/setup/apiSetup.test.ts +++ b/test/setup/apiSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; -import { apiSetup } from "setup"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { apiSetup } from "~/src/setup"; vi.mock("inquirer"); @@ -12,16 +12,50 @@ describe("Setup -> apiSetup", () => { vi.resetAllMocks(); }); - it("should prompt the user for API configuration and update process.env", async () => { - const mockedAnswers = { + it("should prompt the user for API configuration and update environment variables", async () => { + const mockResponses = [ + { API_BASE_URL: "http://localhost:5000" }, + { API_HOST: "127.0.0.1" }, + { API_PORT: "5000" }, + { API_IS_APPLY_DRIZZLE_MIGRATIONS: "true" }, + { API_IS_GRAPHIQL: "false" }, + { API_IS_PINO_PRETTY: "false" }, + { API_JWT_EXPIRES_IN: "3600000" }, + { API_JWT_SECRET: "mocked-secret" }, + { API_LOG_LEVEL: "info" }, + { API_MINIO_ACCESS_KEY: "mocked-access-key" }, + { API_MINIO_END_POINT: "mocked-endpoint" }, + { API_MINIO_PORT: "9001" }, + { API_MINIO_SECRET_KEY: "mocked-secret-key" }, + { API_MINIO_TEST_END_POINT: "mocked-test-endpoint" }, + { API_MINIO_USE_SSL: "true" }, + { API_POSTGRES_DATABASE: "mocked-database" }, + { API_POSTGRES_HOST: "mocked-host" }, + { API_POSTGRES_PASSWORD: "mocked-password" }, + { API_POSTGRES_PORT: "5433" }, + { API_POSTGRES_SSL_MODE: "true" }, + { API_POSTGRES_TEST_HOST: "mocked-test-host" }, + { API_POSTGRES_USER: "mocked-user" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await apiSetup(); + + const expectedEnv = { API_BASE_URL: "http://localhost:5000", API_HOST: "127.0.0.1", API_PORT: "5000", API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", API_IS_GRAPHIQL: "false", - API_IS_PINO_PRETTY: "true", + API_IS_PINO_PRETTY: "false", API_JWT_EXPIRES_IN: "3600000", API_JWT_SECRET: "mocked-secret", + API_LOG_LEVEL: "info", API_MINIO_ACCESS_KEY: "mocked-access-key", API_MINIO_END_POINT: "mocked-endpoint", API_MINIO_PORT: "9001", @@ -37,10 +71,8 @@ describe("Setup -> apiSetup", () => { API_POSTGRES_USER: "mocked-user", }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - - await apiSetup(); - expect(process.env.API_BASE_URL).toBe("http://localhost:5000"); - expect(process.env.API_HOST).toBe("127.0.0.1"); + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } }); }); diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts index d98a8163ed3..6475e1ad827 100644 --- a/test/setup/cloudbeaverSetup.test.ts +++ b/test/setup/cloudbeaverSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; -import { cloudbeaverSetup } from "setup"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { cloudbeaverSetup } from "~/src/setup"; vi.mock("inquirer"); @@ -13,7 +13,24 @@ describe("Setup -> cloudbeaverSetup", () => { }); it("should prompt the user for CloudBeaver configuration and update process.env", async () => { - const mockedAnswers = { + const mockResponses = [ + { CLOUDBEAVER_ADMIN_NAME: "mocked-admin" }, + { CLOUDBEAVER_ADMIN_PASSWORD: "mocked-password" }, + { CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1" }, + { CLOUDBEAVER_MAPPED_PORT: "8080" }, + { CLOUDBEAVER_SERVER_NAME: "Mocked Server" }, + { CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await cloudbeaverSetup(); + + const expectedEnv = { CLOUDBEAVER_ADMIN_NAME: "mocked-admin", CLOUDBEAVER_ADMIN_PASSWORD: "mocked-password", CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", @@ -22,15 +39,8 @@ describe("Setup -> cloudbeaverSetup", () => { CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080", }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - - await cloudbeaverSetup(); - - expect(process.env.CLOUDBEAVER_ADMIN_NAME).toBe("mocked-admin"); - expect(process.env.CLOUDBEAVER_ADMIN_PASSWORD).toBe("mocked-password"); - expect(process.env.CLOUDBEAVER_MAPPED_HOST_IP).toBe("127.0.0.1"); - expect(process.env.CLOUDBEAVER_MAPPED_PORT).toBe("8080"); - expect(process.env.CLOUDBEAVER_SERVER_NAME).toBe("Mocked Server"); - expect(process.env.CLOUDBEAVER_SERVER_URL).toBe("http://127.0.0.1:8080"); + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } }); }); diff --git a/test/setup/minioSetup.test.ts b/test/setup/minioSetup.test.ts index ce0c6db192a..cebf7d172ba 100644 --- a/test/setup/minioSetup.test.ts +++ b/test/setup/minioSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; -import { minioSetup } from "setup"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { minioSetup } from "~/src/setup"; vi.mock("inquirer"); @@ -13,26 +13,28 @@ describe("Setup -> minioSetup", () => { }); it("should prompt the user for Minio configuration and update process.env", async () => { - const mockedAnswers = { + const mockResponses = [ + { MINIO_BROWSER: "off" }, + { MINIO_ROOT_PASSWORD: "mocked-password" }, + { MINIO_ROOT_USER: "mocked-user" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await minioSetup(); + + const expectedEnv = { MINIO_BROWSER: "off", - MINIO_API_MAPPED_HOST_IP: "192.168.1.10", - MINIO_API_MAPPED_PORT: "8000", - MINIO_CONSOLE_MAPPED_HOST_IP: "192.168.1.20", - MINIO_CONSOLE_MAPPED_PORT: "8001", MINIO_ROOT_PASSWORD: "mocked-password", MINIO_ROOT_USER: "mocked-user", }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - - await minioSetup(); - - expect(process.env.MINIO_BROWSER).toBe("off"); - expect(process.env.MINIO_API_MAPPED_HOST_IP).toBe("192.168.1.10"); - expect(process.env.MINIO_API_MAPPED_PORT).toBe("8000"); - expect(process.env.MINIO_CONSOLE_MAPPED_HOST_IP).toBe("192.168.1.20"); - expect(process.env.MINIO_CONSOLE_MAPPED_PORT).toBe("8001"); - expect(process.env.MINIO_ROOT_PASSWORD).toBe("mocked-password"); - expect(process.env.MINIO_ROOT_USER).toBe("mocked-user"); + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } }); }); diff --git a/test/setup/postgresSetup.test.ts b/test/setup/postgresSetup.test.ts index 254304d5e99..063cdf80244 100644 --- a/test/setup/postgresSetup.test.ts +++ b/test/setup/postgresSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; -import { postgresSetup } from "setup"; import { afterEach, describe, expect, it, vi } from "vitest"; +import { postgresSetup } from "~/src/setup"; vi.mock("inquirer"); @@ -13,22 +13,27 @@ describe("Setup -> postgresSetup", () => { }); it("should prompt the user for Postgres configuration and update process.env", async () => { - const mockedAnswers = { + const mockResponses = [ + { POSTGRES_DB: "mocked-db" }, + { POSTGRES_PASSWORD: "mocked-password" }, + { POSTGRES_USER: "mocked-user" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await postgresSetup(); + + const expectedEnv = { POSTGRES_DB: "mocked-db", - POSTGRES_MAPPED_HOST_IP: "192.168.1.100", - POSTGRES_MAPPED_PORT: "5434", POSTGRES_PASSWORD: "mocked-password", POSTGRES_USER: "mocked-user", }; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce(mockedAnswers); - - await postgresSetup(); - - expect(process.env.POSTGRES_DB).toBe("mocked-db"); - expect(process.env.POSTGRES_MAPPED_HOST_IP).toBe("192.168.1.100"); - expect(process.env.POSTGRES_MAPPED_PORT).toBe("5434"); - expect(process.env.POSTGRES_PASSWORD).toBe("mocked-password"); - expect(process.env.POSTGRES_USER).toBe("mocked-user"); + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } }); }); diff --git a/test/setup/setNodeEnviorment.test.ts b/test/setup/setNodeEnviorment.test.ts deleted file mode 100644 index a18943036a4..00000000000 --- a/test/setup/setNodeEnviorment.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import inquirer from "inquirer"; -import { setNodeEnvironment } from "setup"; -import { afterEach, describe, expect, it, vi } from "vitest"; - -vi.mock("inquirer"); - -describe("Setup -> setNodeEnvironment", () => { - const originalNodeEnv = process.env.NODE_ENV; - - afterEach(() => { - process.env.NODE_ENV = originalNodeEnv; - vi.resetAllMocks(); - }); - - it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { - const mockedNodeEnv = "production"; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - nodeEnv: mockedNodeEnv, - }); - process.env.NODE_ENV = "test"; - - await setNodeEnvironment(); - - expect(process.env.NODE_ENV).toBe(mockedNodeEnv); - expect(inquirer.prompt).toHaveBeenCalledWith([ - { - type: "list", - name: "nodeEnv", - message: "Select Node environment:", - choices: ["development", "production"], - default: "development", - }, - ]); - }); - - it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - nodeEnv: "development", - }); // Mock inquirer prompt with default - - process.env.NODE_ENV = "test"; - - await setNodeEnvironment(); - - expect(process.env.NODE_ENV).toBe("development"); - expect(inquirer.prompt).toHaveBeenCalled(); - }); - - it("should handle errors thrown by inquirer.prompt when NODE_ENV is 'test'", async () => { - const mockedError = new Error("Prompt failed"); - vi.spyOn(inquirer, "prompt").mockRejectedValueOnce(mockedError); // Mock inquirer to throw an error - - process.env.NODE_ENV = "test"; - - const consoleErrorSpy = vi - .spyOn(console, "error") - .mockImplementation(() => {}); - - await expect(setNodeEnvironment()).rejects.toThrow(); - - expect(consoleErrorSpy).toHaveBeenCalledWith(mockedError); - - consoleErrorSpy.mockRestore(); - }); - - it("should leave NODE_ENV unchanged when not in 'test' environment", async () => { - process.env.NODE_ENV = "development"; - - await setNodeEnvironment(); - - expect(process.env.NODE_ENV).toBe("development"); - expect(inquirer.prompt).not.toHaveBeenCalled(); - }); -}); diff --git a/test/setup/setNodeEnvironment.test.ts b/test/setup/setNodeEnvironment.test.ts new file mode 100644 index 00000000000..783444562e4 --- /dev/null +++ b/test/setup/setNodeEnvironment.test.ts @@ -0,0 +1,47 @@ +import inquirer from "inquirer"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { setNodeEnvironment } from "~/src/setup"; + +vi.mock("inquirer"); + +describe("Setup -> setNodeEnvironment", () => { + const originalNodeEnv = process.env.NODE_ENV; + + afterEach(() => { + process.env.NODE_ENV = originalNodeEnv; + vi.resetAllMocks(); + }); + it("should update NODE_ENV when selection is made", async () => { + const mockedNodeEnv = "development"; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + NODE_ENV: mockedNodeEnv, + }); + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe(mockedNodeEnv); + }); + + it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { + const mockedNodeEnv = "production"; + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + NODE_ENV: mockedNodeEnv, + }); + process.env.NODE_ENV = "test"; + + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe(mockedNodeEnv); + }); + + it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { + vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ + NODE_ENV: "development", + }); + + process.env.NODE_ENV = "test"; + + await setNodeEnvironment(); + + expect(process.env.NODE_ENV).toBe("development"); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 4b3d42a8006..106064717a2 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -2,11 +2,7 @@ import tsconfigPaths from "vite-tsconfig-paths"; import { defineConfig } from "vitest/config"; export default defineConfig({ - plugins: [ - tsconfigPaths({ - ignoreConfigErrors: true, - }), - ], + plugins: [tsconfigPaths()], test: { coverage: { provider: "v8", // or 'istanbul' if you prefer From b29de0431735db39deeaeafdb803d37b17b3fd1a Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Wed, 29 Jan 2025 20:50:45 +0530 Subject: [PATCH 05/10] checkenv file --- .gitignore | 2 +- package.json | 4 +- src/setup.ts | 129 +++++++++++++------------- src/setup/updateEnvVariable.ts | 57 +++++++----- test/setup/administratorEmail.test.ts | 17 +++- test/setup/apiSetup.test.ts | 53 ++++++++++- 6 files changed, 167 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index fb0779cee9e..cd13444318d 100644 --- a/.gitignore +++ b/.gitignore @@ -293,7 +293,7 @@ web_modules/ .env.test.local .env.production.local .env.local - +.env.backup # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache diff --git a/package.json b/package.json index f2d7d835b82..818ff706384 100644 --- a/package.json +++ b/package.json @@ -93,8 +93,8 @@ "start_development_server_with_debugger": "tsx watch --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./src/index.ts", "start_production_server": "pnpm build_production && node ./dist/index.js", "start_production_server_with_debugger": "pnpm build_production && node --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./dist/index.js", - "upgrade_drizzle_metadata": "drizzle-kit up", - "setup": "tsx setup.ts" + "setup": "tsx setup.ts", + "upgrade_drizzle_metadata": "drizzle-kit up" }, "type": "module", "version": "1.0.0" diff --git a/src/setup.ts b/src/setup.ts index 3e449774ba8..8b447077fee 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -7,13 +7,54 @@ import { updateEnvVariable } from "./setup/updateEnvVariable"; export function generateJwtSecret(): string { try { - return crypto.randomBytes(32).toString("hex"); + return crypto.randomBytes(64).toString("hex"); } catch (err) { console.error("Failed to generate JWT secret:", err); throw new Error("Failed to generate JWT secret"); } } +export function validateURL(input: string): true | string { + try { + new URL(input); + return true; + } catch { + return "Please enter a valid URL."; + } +} + +export function validatePort(input: string): true | string { + const portNumber = Number(input); + if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { + return "Please enter a valid port number (1-65535)."; + } + return true; +} + +export function validateEmail(input: string): true | string { + if (!input.trim()) { + console.log("Email cannot be empty."); + return "Email cannot be empty."; + } + + if (input.length > 254) { + return "Email is too long."; + } + + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(input)) { + return "Invalid email format. Please enter a valid email address."; + } + return true; +} + +function checkEnvFile(): boolean { + if (fs.existsSync(envFileName)) { + return true; + } + return false; +} + let originalEnvContent: string | null = null; const envFileName = ".env"; @@ -39,7 +80,7 @@ export function restoreEnvFile(): void { } } -function initializeEnvFile(): void { +export function initializeEnvFile(): void { const envFileToUse = process.env.CI === "true" ? "envFiles/.env.ci" @@ -100,6 +141,7 @@ export async function administratorEmail(): Promise { name: "API_ADMINISTRATOR_USER_EMAIL_ADDRESS", message: "Enter email :", default: "administrator@email.com", + validate: validateEmail, }, ]); updateEnvVariable({ API_ADMINISTRATOR_USER_EMAIL_ADDRESS }); @@ -116,14 +158,7 @@ export async function apiSetup(): Promise { name: "API_BASE_URL", message: "API base URL:", default: "http://127.0.0.1:4000", - validate: (input: string) => { - try { - new URL(input); - return true; - } catch { - return "Please enter a valid URL."; - } - }, + validate: validateURL, }, ]); updateEnvVariable({ API_BASE_URL }); @@ -144,13 +179,7 @@ export async function apiSetup(): Promise { name: "API_PORT", message: "API port:", default: "4000", - validate: (input: string) => { - const portNumber = Number(input); - if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { - return "Please enter a valid port number (1-65535)."; - } - return true; - }, + validate: validatePort, }, ]); updateEnvVariable({ API_PORT }); @@ -398,13 +427,7 @@ export async function cloudbeaverSetup(): Promise { name: "CLOUDBEAVER_MAPPED_PORT", message: "CloudBeaver mapped port:", default: "8978", - validate: (input: string) => { - const portNumber = Number(input); - if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { - return "Please enter a valid port number (1-65535)."; - } - return true; - }, + validate: validatePort, }, ]); updateEnvVariable({ CLOUDBEAVER_MAPPED_PORT }); @@ -425,14 +448,7 @@ export async function cloudbeaverSetup(): Promise { name: "CLOUDBEAVER_SERVER_URL", message: "CloudBeaver server URL:", default: "http://127.0.0.1:8978", - validate: (input: string) => { - try { - new URL(input); - return true; - } catch { - return "Please enter a valid URL."; - } - }, + validate: validateURL, }, ]); updateEnvVariable({ CLOUDBEAVER_SERVER_URL }); @@ -467,17 +483,7 @@ export async function minioSetup(): Promise { name: "MINIO_API_MAPPED_PORT", message: "Minio API mapped port:", default: "9000", - validate: (input: string) => { - const portNumber = Number(input); - if ( - Number.isNaN(portNumber) || - portNumber <= 0 || - portNumber > 65535 - ) { - return "Please enter a valid port number (1-65535)."; - } - return true; - }, + validate: validatePort, }, ]); updateEnvVariable({ MINIO_API_MAPPED_PORT }); @@ -498,17 +504,7 @@ export async function minioSetup(): Promise { name: "MINIO_CONSOLE_MAPPED_PORT", message: "Minio console mapped port:", default: "9001", - validate: (input: string) => { - const portNumber = Number(input); - if ( - Number.isNaN(portNumber) || - portNumber <= 0 || - portNumber > 65535 - ) { - return "Please enter a valid port number (1-65535)."; - } - return true; - }, + validate: validatePort, }, ]); updateEnvVariable({ MINIO_CONSOLE_MAPPED_PORT }); @@ -566,17 +562,7 @@ export async function postgresSetup(): Promise { name: "POSTGRES_MAPPED_PORT", message: "Postgres mapped port:", default: "5432", - validate: (input: string) => { - const portNumber = Number(input); - if ( - Number.isNaN(portNumber) || - portNumber <= 0 || - portNumber > 65535 - ) { - return "Please enter a valid port number (1-65535)."; - } - return true; - }, + validate: validatePort, }, ]); updateEnvVariable({ POSTGRES_MAPPED_PORT }); @@ -604,6 +590,19 @@ export async function postgresSetup(): Promise { } export async function setup(): Promise { + if (checkEnvFile()) { + const { envExists } = await inquirer.prompt([ + { + type: "confirm", + name: "envExists", + message: "Env file found, Do you want to re-configure? (Y)/N", + default: true, + }, + ]); + if (!envExists) { + process.exit(1); + } + } dotenv.config({ path: envFileName }); backupEnvFile(); await setCI(); diff --git a/src/setup/updateEnvVariable.ts b/src/setup/updateEnvVariable.ts index 0067254a8b8..25f227737ab 100644 --- a/src/setup/updateEnvVariable.ts +++ b/src/setup/updateEnvVariable.ts @@ -10,32 +10,39 @@ export function updateEnvVariable(config: { }): void { const envFileName = process.env.NODE_ENV === "test" ? ".env_test" : ".env"; - // Read the existing content of the .env or .env_test file - const existingContent: string = fs.existsSync(envFileName) - ? fs.readFileSync(envFileName, "utf8") - : ""; - - let updatedContent: string = existingContent; - - // Update the .env file and process.env for each variable - for (const key in config) { - const value = config[key]; - const regex = new RegExp( - `^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=.*`, - "gm", - ); - - // Update or add the variable in the .env file - if (regex.test(updatedContent)) { - updatedContent = updatedContent.replace(regex, `${key}=${value}`); - } else { - updatedContent += `\n${key}=${value}`; + const backupFile = `${envFileName}.backup`; + if (fs.existsSync(envFileName)) { + fs.copyFileSync(envFileName, backupFile); + } + + try { + const existingContent: string = fs.existsSync(envFileName) + ? fs.readFileSync(envFileName, "utf8") + : ""; + + let updatedContent: string = existingContent; + + for (const key in config) { + const value = config[key]; + const regex = new RegExp( + `^${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}=.*`, + "gm", + ); + + if (regex.test(updatedContent)) { + updatedContent = updatedContent.replace(regex, `${key}=${value}`); + } else { + updatedContent += `\n${key}=${value}`; + } + + process.env[key] = String(value); } - // Update the variable in process.env - process.env[key] = String(value); + fs.writeFileSync(envFileName, updatedContent, "utf8"); + } catch (error) { + if (fs.existsSync(backupFile)) { + fs.copyFileSync(backupFile, envFileName); + } + throw error; } - - // Write the updated content back to the .env or .env_test file - fs.writeFileSync(envFileName, updatedContent, "utf8"); } diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts index 59fb5922e45..1c808649ad7 100644 --- a/test/setup/administratorEmail.test.ts +++ b/test/setup/administratorEmail.test.ts @@ -1,6 +1,7 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; import { administratorEmail } from "~/src/setup"; +import { validateEmail } from "~/src/setup"; vi.mock("inquirer"); @@ -19,8 +20,22 @@ describe("Setup -> askForAdministratorEmail", () => { }); await administratorEmail(); - console.log(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS); expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); }); + + it("should return true for valid email addresses", () => { + expect(validateEmail("user@example.com")).toBe(true); + expect(validateEmail("test.email@domain.io")).toBe(true); + }); + + it("should return an error message for invalid email addresses", () => { + expect(validateEmail("invalid-email")).toBe( + "Invalid email format. Please enter a valid email address.", + ); + expect(validateEmail(" ")).toBe("Email cannot be empty."); + expect(validateEmail(`${"a".repeat(255)}@example.com`)).toBe( + "Email is too long.", + ); + }); }); diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts index 5f4cede7437..6e99d1f2e58 100644 --- a/test/setup/apiSetup.test.ts +++ b/test/setup/apiSetup.test.ts @@ -1,6 +1,11 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { apiSetup } from "~/src/setup"; +import { + apiSetup, + generateJwtSecret, + validatePort, + validateURL, +} from "~/src/setup"; vi.mock("inquirer"); @@ -76,3 +81,49 @@ describe("Setup -> apiSetup", () => { } }); }); +describe("validateURL", () => { + it("should return true for a valid URL", () => { + expect(validateURL("https://example.com")).toBe(true); + expect(validateURL("http://localhost:3000")).toBe(true); + expect(validateURL("ftp://ftp.example.com")).toBe(true); + }); + + it("should return an error message for an invalid URL", () => { + expect(validateURL("invalid-url")).toBe("Please enter a valid URL."); + expect(validateURL(" ")).toBe("Please enter a valid URL."); + }); +}); + +describe("validatePort", () => { + it("should return true for valid port numbers", () => { + expect(validatePort("80")).toBe(true); + expect(validatePort("443")).toBe(true); + expect(validatePort("65535")).toBe(true); + expect(validatePort("1")).toBe(true); + }); + + it("should return an error message for invalid port numbers", () => { + expect(validatePort("0")).toBe( + "Please enter a valid port number (1-65535).", + ); + expect(validatePort("65536")).toBe( + "Please enter a valid port number (1-65535).", + ); + expect(validatePort("-1")).toBe( + "Please enter a valid port number (1-65535).", + ); + expect(validatePort("not-a-number")).toBe( + "Please enter a valid port number (1-65535).", + ); + expect(validatePort(" ")).toBe( + "Please enter a valid port number (1-65535).", + ); + }); +}); + +describe("generateJwtSecret", () => { + it("should generate a 64-byte hex string", () => { + const secret = generateJwtSecret(); + expect(secret).toMatch(/^[a-f0-9]{128}$/); + }); +}); From 67f62aacd901a59db1d9319e8489c53f618d15c0 Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Fri, 31 Jan 2025 18:33:21 +0530 Subject: [PATCH 06/10] update at last --- setup.ts | 2 - src/setup.ts | 173 +++++++++++--------------- test/setup/administratorEmail.test.ts | 8 +- test/setup/apiSetup.test.ts | 4 +- test/setup/cloudbeaverSetup.test.ts | 4 +- test/setup/minioSetup.test.ts | 4 +- test/setup/postgresSetup.test.ts | 4 +- test/setup/setNodeEnvironment.test.ts | 31 +---- 8 files changed, 91 insertions(+), 139 deletions(-) diff --git a/setup.ts b/setup.ts index ae710b97ac6..2c389509c96 100644 --- a/setup.ts +++ b/setup.ts @@ -1,8 +1,6 @@ import { setup } from "./src/setup"; -import { restoreEnvFile } from "./src/setup"; setup().catch((err) => { - restoreEnvFile(); console.error("An error occurred during setup:", err); process.exit(1); }); diff --git a/src/setup.ts b/src/setup.ts index 8b447077fee..878c285970f 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -5,6 +5,10 @@ import dotenv from "dotenv"; import inquirer from "inquirer"; import { updateEnvVariable } from "./setup/updateEnvVariable"; +let answers: Record = {}; + +const envFileName = ".env"; + export function generateJwtSecret(): string { try { return crypto.randomBytes(64).toString("hex"); @@ -55,38 +59,9 @@ function checkEnvFile(): boolean { return false; } -let originalEnvContent: string | null = null; -const envFileName = ".env"; - -function backupEnvFile(): void { - if (fs.existsSync(envFileName)) { - originalEnvContent = fs.readFileSync(envFileName, "utf-8"); - } else { - originalEnvContent = null; - } -} - -export function restoreEnvFile(): void { - try { - if (originalEnvContent !== null) { - fs.writeFileSync(envFileName, originalEnvContent, "utf-8"); - console.log("\nChanges undone. Restored the original environment file."); - } else if (fs.existsSync(envFileName)) { - fs.unlinkSync(envFileName); - console.log("\nChanges undone. Removed the environment file."); - } - } catch (err) { - console.error("Error restoring env file:", err); - } -} - export function initializeEnvFile(): void { const envFileToUse = - process.env.CI === "true" - ? "envFiles/.env.ci" - : "envFiles/.env.devcontainer"; - - const envFileName = ".env"; + answers.CI === "true" ? "envFiles/.env.ci" : "envFiles/.env.devcontainer"; const parsedEnv = dotenv.parse(fs.readFileSync(envFileToUse)); @@ -108,14 +83,14 @@ export async function setCI(): Promise { default: "false", }, ]); - updateEnvVariable({ CI }); + answers.CI = CI; } catch (err) { console.error(err); abort(); } } -export async function setNodeEnvironment(): Promise { +export async function setNodeEnvironment(): Promise> { try { const { NODE_ENV } = await inquirer.prompt([ { @@ -126,14 +101,15 @@ export async function setNodeEnvironment(): Promise { default: "production", }, ]); - updateEnvVariable({ NODE_ENV }); + answers.NODE_ENV = NODE_ENV; } catch (err) { console.error(err); abort(); } + return answers; } -export async function administratorEmail(): Promise { +export async function administratorEmail(): Promise> { try { const { API_ADMINISTRATOR_USER_EMAIL_ADDRESS } = await inquirer.prompt([ { @@ -144,14 +120,16 @@ export async function administratorEmail(): Promise { validate: validateEmail, }, ]); - updateEnvVariable({ API_ADMINISTRATOR_USER_EMAIL_ADDRESS }); + answers.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = + API_ADMINISTRATOR_USER_EMAIL_ADDRESS; } catch (err) { console.log(err); abort(); } + return answers; } -export async function apiSetup(): Promise { +export async function apiSetup(): Promise> { const { API_BASE_URL } = await inquirer.prompt([ { type: "input", @@ -161,7 +139,7 @@ export async function apiSetup(): Promise { validate: validateURL, }, ]); - updateEnvVariable({ API_BASE_URL }); + answers.API_BASE_URL = API_BASE_URL; const { API_HOST } = await inquirer.prompt([ { @@ -171,7 +149,7 @@ export async function apiSetup(): Promise { default: "0.0.0.0", }, ]); - updateEnvVariable({ API_HOST }); + answers.API_HOST = API_HOST; const { API_PORT } = await inquirer.prompt([ { @@ -182,7 +160,7 @@ export async function apiSetup(): Promise { validate: validatePort, }, ]); - updateEnvVariable({ API_PORT }); + answers.API_PORT = API_PORT; const { API_IS_APPLY_DRIZZLE_MIGRATIONS } = await inquirer.prompt([ { @@ -193,7 +171,7 @@ export async function apiSetup(): Promise { default: "true", }, ]); - updateEnvVariable({ API_IS_APPLY_DRIZZLE_MIGRATIONS }); + answers.API_IS_APPLY_DRIZZLE_MIGRATIONS = API_IS_APPLY_DRIZZLE_MIGRATIONS; const { API_IS_GRAPHIQL } = await inquirer.prompt([ { @@ -201,10 +179,10 @@ export async function apiSetup(): Promise { name: "API_IS_GRAPHIQL", message: "Enable GraphQL?", choices: ["true", "false"], - default: process.env.CI === "false" ? "false" : "true", + default: answers.CI === "false" ? "false" : "true", }, ]); - updateEnvVariable({ API_IS_GRAPHIQL }); + answers.API_IS_GRAPHIQL = API_IS_GRAPHIQL; const { API_IS_PINO_PRETTY } = await inquirer.prompt([ { @@ -212,10 +190,10 @@ export async function apiSetup(): Promise { name: "API_IS_PINO_PRETTY", message: "Enable Pino Pretty logs?", choices: ["true", "false"], - default: process.env.CI === "false" ? "false" : "true", + default: answers.CI === "false" ? "false" : "true", }, ]); - updateEnvVariable({ API_IS_PINO_PRETTY }); + answers.API_IS_PINO_PRETTY = API_IS_PINO_PRETTY; const { API_JWT_EXPIRES_IN } = await inquirer.prompt([ { @@ -225,7 +203,7 @@ export async function apiSetup(): Promise { default: "2592000000", }, ]); - updateEnvVariable({ API_JWT_EXPIRES_IN }); + answers.API_JWT_EXPIRES_IN = API_JWT_EXPIRES_IN; const jwtSecret = generateJwtSecret(); @@ -236,14 +214,14 @@ export async function apiSetup(): Promise { message: "JWT secret:", default: jwtSecret, validate: (input: string) => { - if (input.length < 64) { - return "JWT secret must be at least 64 characters long."; + if (input.length < 128) { + return "JWT secret must be at least 128 characters long."; } return true; }, }, ]); - updateEnvVariable({ API_JWT_SECRET }); + answers.API_JWT_SECRET = API_JWT_SECRET; const { API_LOG_LEVEL } = await inquirer.prompt([ { @@ -251,10 +229,10 @@ export async function apiSetup(): Promise { name: "API_LOG_LEVEL", message: "LOG level:", choices: ["info", "debug"], - default: process.env.CI === "true" ? "info" : "debug", + default: answers.CI === "true" ? "info" : "debug", }, ]); - updateEnvVariable({ API_LOG_LEVEL }); + answers.API_LOG_LEVEL = API_LOG_LEVEL; const { API_MINIO_ACCESS_KEY } = await inquirer.prompt([ { @@ -264,7 +242,7 @@ export async function apiSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ API_MINIO_ACCESS_KEY }); + answers.API_MINIO_ACCESS_KEY = API_MINIO_ACCESS_KEY; const { API_MINIO_END_POINT } = await inquirer.prompt([ { @@ -274,7 +252,7 @@ export async function apiSetup(): Promise { default: "minio", }, ]); - updateEnvVariable({ API_MINIO_END_POINT }); + answers.API_MINIO_END_POINT = API_MINIO_END_POINT; const { API_MINIO_PORT } = await inquirer.prompt([ { @@ -284,7 +262,7 @@ export async function apiSetup(): Promise { default: "9000", }, ]); - updateEnvVariable({ API_MINIO_PORT }); + answers.API_MINIO_PORT = API_MINIO_PORT; const { API_MINIO_SECRET_KEY } = await inquirer.prompt([ { @@ -294,7 +272,7 @@ export async function apiSetup(): Promise { default: "password", }, ]); - updateEnvVariable({ API_MINIO_SECRET_KEY }); + answers.API_MINIO_SECRET_KEY = API_MINIO_SECRET_KEY; const { API_MINIO_TEST_END_POINT } = await inquirer.prompt([ { @@ -304,7 +282,7 @@ export async function apiSetup(): Promise { default: "minio-test", }, ]); - updateEnvVariable({ API_MINIO_TEST_END_POINT }); + answers.API_MINIO_TEST_END_POINT = API_MINIO_TEST_END_POINT; const { API_MINIO_USE_SSL } = await inquirer.prompt([ { @@ -315,7 +293,7 @@ export async function apiSetup(): Promise { default: "false", }, ]); - updateEnvVariable({ API_MINIO_USE_SSL }); + answers.API_MINIO_USE_SSL = API_MINIO_USE_SSL; const { API_POSTGRES_DATABASE } = await inquirer.prompt([ { @@ -325,7 +303,7 @@ export async function apiSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ API_POSTGRES_DATABASE }); + answers.API_POSTGRES_DATABASE = API_POSTGRES_DATABASE; const { API_POSTGRES_HOST } = await inquirer.prompt([ { @@ -335,7 +313,7 @@ export async function apiSetup(): Promise { default: "postgres", }, ]); - updateEnvVariable({ API_POSTGRES_HOST }); + answers.API_POSTGRES_HOST = API_POSTGRES_HOST; const { API_POSTGRES_PASSWORD } = await inquirer.prompt([ { @@ -345,7 +323,7 @@ export async function apiSetup(): Promise { default: "password", }, ]); - updateEnvVariable({ API_POSTGRES_PASSWORD }); + answers.API_POSTGRES_PASSWORD = API_POSTGRES_PASSWORD; const { API_POSTGRES_PORT } = await inquirer.prompt([ { @@ -355,7 +333,7 @@ export async function apiSetup(): Promise { default: "5432", }, ]); - updateEnvVariable({ API_POSTGRES_PORT }); + answers.API_POSTGRES_PORT = API_POSTGRES_PORT; const { API_POSTGRES_SSL_MODE } = await inquirer.prompt([ { @@ -366,7 +344,7 @@ export async function apiSetup(): Promise { default: "false", }, ]); - updateEnvVariable({ API_POSTGRES_SSL_MODE }); + answers.API_POSTGRES_SSL_MODE = API_POSTGRES_SSL_MODE; const { API_POSTGRES_TEST_HOST } = await inquirer.prompt([ { @@ -376,7 +354,7 @@ export async function apiSetup(): Promise { default: "postgres-test", }, ]); - updateEnvVariable({ API_POSTGRES_TEST_HOST }); + answers.API_POSTGRES_TEST_HOST = API_POSTGRES_TEST_HOST; const { API_POSTGRES_USER } = await inquirer.prompt([ { @@ -386,11 +364,11 @@ export async function apiSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ API_POSTGRES_USER }); - console.log("Environment variables updated."); + answers.API_POSTGRES_USER = API_POSTGRES_USER; + return answers; } -export async function cloudbeaverSetup(): Promise { +export async function cloudbeaverSetup(): Promise> { const { CLOUDBEAVER_ADMIN_NAME } = await inquirer.prompt([ { type: "input", @@ -399,7 +377,7 @@ export async function cloudbeaverSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ CLOUDBEAVER_ADMIN_NAME }); + answers.CLOUDBEAVER_ADMIN_NAME = CLOUDBEAVER_ADMIN_NAME; const { CLOUDBEAVER_ADMIN_PASSWORD } = await inquirer.prompt([ { @@ -409,7 +387,7 @@ export async function cloudbeaverSetup(): Promise { default: "password", }, ]); - updateEnvVariable({ CLOUDBEAVER_ADMIN_PASSWORD }); + answers.CLOUDBEAVER_ADMIN_PASSWORD = CLOUDBEAVER_ADMIN_PASSWORD; const { CLOUDBEAVER_MAPPED_HOST_IP } = await inquirer.prompt([ { @@ -419,7 +397,7 @@ export async function cloudbeaverSetup(): Promise { default: "127.0.0.1", }, ]); - updateEnvVariable({ CLOUDBEAVER_MAPPED_HOST_IP }); + answers.CLOUDBEAVER_MAPPED_HOST_IP = CLOUDBEAVER_MAPPED_HOST_IP; const { CLOUDBEAVER_MAPPED_PORT } = await inquirer.prompt([ { @@ -430,7 +408,7 @@ export async function cloudbeaverSetup(): Promise { validate: validatePort, }, ]); - updateEnvVariable({ CLOUDBEAVER_MAPPED_PORT }); + answers.CLOUDBEAVER_MAPPED_PORT = CLOUDBEAVER_MAPPED_PORT; const { CLOUDBEAVER_SERVER_NAME } = await inquirer.prompt([ { @@ -440,7 +418,7 @@ export async function cloudbeaverSetup(): Promise { default: "Talawa CloudBeaver Server", }, ]); - updateEnvVariable({ CLOUDBEAVER_SERVER_NAME }); + answers.CLOUDBEAVER_SERVER_NAME = CLOUDBEAVER_SERVER_NAME; const { CLOUDBEAVER_SERVER_URL } = await inquirer.prompt([ { @@ -451,22 +429,22 @@ export async function cloudbeaverSetup(): Promise { validate: validateURL, }, ]); - updateEnvVariable({ CLOUDBEAVER_SERVER_URL }); - console.log("CloudBeaver environment variables updated."); + answers.CLOUDBEAVER_SERVER_URL = CLOUDBEAVER_SERVER_URL; + return answers; } -export async function minioSetup(): Promise { +export async function minioSetup(): Promise> { const { MINIO_BROWSER } = await inquirer.prompt([ { type: "input", name: "MINIO_BROWSER", message: "Minio browser (on/off):", - default: process.env.CI === "true" ? "off" : "on", + default: answers.CI === "true" ? "off" : "on", }, ]); - updateEnvVariable({ MINIO_BROWSER }); + answers.MINIO_BROWSER = MINIO_BROWSER; - if (process.env.CI === "false") { + if (answers.CI === "false") { const { MINIO_API_MAPPED_HOST_IP } = await inquirer.prompt([ { type: "input", @@ -475,7 +453,7 @@ export async function minioSetup(): Promise { default: "127.0.0.1", }, ]); - updateEnvVariable({ MINIO_API_MAPPED_HOST_IP }); + answers.MINIO_API_MAPPED_HOST_IP = MINIO_API_MAPPED_HOST_IP; const { MINIO_API_MAPPED_PORT } = await inquirer.prompt([ { @@ -486,7 +464,7 @@ export async function minioSetup(): Promise { validate: validatePort, }, ]); - updateEnvVariable({ MINIO_API_MAPPED_PORT }); + answers.MINIO_API_MAPPED_PORT = MINIO_API_MAPPED_PORT; const { MINIO_CONSOLE_MAPPED_HOST_IP } = await inquirer.prompt([ { @@ -496,7 +474,7 @@ export async function minioSetup(): Promise { default: "127.0.0.1", }, ]); - updateEnvVariable({ MINIO_CONSOLE_MAPPED_HOST_IP }); + answers.MINIO_CONSOLE_MAPPED_HOST_IP = MINIO_CONSOLE_MAPPED_HOST_IP; const { MINIO_CONSOLE_MAPPED_PORT } = await inquirer.prompt([ { @@ -507,7 +485,7 @@ export async function minioSetup(): Promise { validate: validatePort, }, ]); - updateEnvVariable({ MINIO_CONSOLE_MAPPED_PORT }); + answers.MINIO_CONSOLE_MAPPED_PORT = MINIO_CONSOLE_MAPPED_PORT; } const { MINIO_ROOT_PASSWORD } = await inquirer.prompt([ @@ -518,7 +496,7 @@ export async function minioSetup(): Promise { default: "password", }, ]); - updateEnvVariable({ MINIO_ROOT_PASSWORD }); + answers.MINIO_ROOT_PASSWORD = MINIO_ROOT_PASSWORD; const { MINIO_ROOT_USER } = await inquirer.prompt([ { @@ -528,13 +506,11 @@ export async function minioSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ MINIO_ROOT_USER }); - console.log("Minio environment variables updated."); + answers.MINIO_ROOT_USER = MINIO_ROOT_USER; + return answers; } -export async function postgresSetup(): Promise { - console.log("\n--- Postgres Setup ---"); - +export async function postgresSetup(): Promise> { const { POSTGRES_DB } = await inquirer.prompt([ { type: "input", @@ -543,9 +519,9 @@ export async function postgresSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ POSTGRES_DB }); + answers.POSTGRES_DB = POSTGRES_DB; - if (process.env.CI === "false") { + if (answers.CI === "false") { const { POSTGRES_MAPPED_HOST_IP } = await inquirer.prompt([ { type: "input", @@ -554,7 +530,7 @@ export async function postgresSetup(): Promise { default: "127.0.0.1", }, ]); - updateEnvVariable({ POSTGRES_MAPPED_HOST_IP }); + answers.POSTGRES_MAPPED_HOST_IP = POSTGRES_MAPPED_HOST_IP; const { POSTGRES_MAPPED_PORT } = await inquirer.prompt([ { @@ -565,7 +541,7 @@ export async function postgresSetup(): Promise { validate: validatePort, }, ]); - updateEnvVariable({ POSTGRES_MAPPED_PORT }); + answers.POSTGRES_MAPPED_PORT = POSTGRES_MAPPED_PORT; } const { POSTGRES_PASSWORD } = await inquirer.prompt([ @@ -576,7 +552,7 @@ export async function postgresSetup(): Promise { default: "password", }, ]); - updateEnvVariable({ POSTGRES_PASSWORD }); + answers.POSTGRES_PASSWORD = POSTGRES_PASSWORD; const { POSTGRES_USER } = await inquirer.prompt([ { @@ -586,31 +562,31 @@ export async function postgresSetup(): Promise { default: "talawa", }, ]); - updateEnvVariable({ POSTGRES_USER }); + answers.POSTGRES_USER = POSTGRES_USER; + return answers; } export async function setup(): Promise { if (checkEnvFile()) { - const { envExists } = await inquirer.prompt([ + const { envReconfigure } = await inquirer.prompt([ { type: "confirm", - name: "envExists", + name: "envReconfigure", message: "Env file found, Do you want to re-configure? (Y)/N", default: true, }, ]); - if (!envExists) { + if (!envReconfigure) { process.exit(1); } } dotenv.config({ path: envFileName }); - backupEnvFile(); await setCI(); initializeEnvFile(); process.on("SIGINT", () => { console.log("\nProcess interrupted! Undoing changes..."); - restoreEnvFile(); + answers = {}; process.exit(1); }); await setNodeEnvironment(); @@ -640,7 +616,7 @@ export async function setup(): Promise { await minioSetup(); } - if (process.env.CI === "false") { + if (answers.CI === "false") { const { useDefaultCloudbeaver } = await inquirer.prompt([ { type: "confirm", @@ -669,5 +645,6 @@ export async function setup(): Promise { } await administratorEmail(); + updateEnvVariable(answers); console.log("Configuration complete."); } diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts index 1c808649ad7..7125a506c8f 100644 --- a/test/setup/administratorEmail.test.ts +++ b/test/setup/administratorEmail.test.ts @@ -19,14 +19,18 @@ describe("Setup -> askForAdministratorEmail", () => { API_ADMINISTRATOR_USER_EMAIL_ADDRESS: mockedEmail, }); - await administratorEmail(); + const answers = await administratorEmail(); - expect(process.env.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); + expect(answers.API_ADMINISTRATOR_USER_EMAIL_ADDRESS).toBe(mockedEmail); }); it("should return true for valid email addresses", () => { expect(validateEmail("user@example.com")).toBe(true); expect(validateEmail("test.email@domain.io")).toBe(true); + expect(validateEmail("user+tag@example.co.uk")).toBe(true); + expect(validateEmail("user@xn--80ak6aa92e.com")).toBe(true); + expect(validateEmail("user+tag@example.co.uk")).toBe(true); + expect(validateEmail("user@xn--80ak6aa92e.com")).toBe(true); }); it("should return an error message for invalid email addresses", () => { diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts index 6e99d1f2e58..e27c63b99c3 100644 --- a/test/setup/apiSetup.test.ts +++ b/test/setup/apiSetup.test.ts @@ -49,7 +49,7 @@ describe("Setup -> apiSetup", () => { promptMock.mockResolvedValueOnce(response); } - await apiSetup(); + const answers = await apiSetup(); const expectedEnv = { API_BASE_URL: "http://localhost:5000", @@ -77,7 +77,7 @@ describe("Setup -> apiSetup", () => { }; for (const [key, value] of Object.entries(expectedEnv)) { - expect(process.env[key]).toBe(value); + expect(answers[key]).toBe(value); } }); }); diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts index 6475e1ad827..5c18c2da59e 100644 --- a/test/setup/cloudbeaverSetup.test.ts +++ b/test/setup/cloudbeaverSetup.test.ts @@ -28,7 +28,7 @@ describe("Setup -> cloudbeaverSetup", () => { promptMock.mockResolvedValueOnce(response); } - await cloudbeaverSetup(); + const answers = await cloudbeaverSetup(); const expectedEnv = { CLOUDBEAVER_ADMIN_NAME: "mocked-admin", @@ -40,7 +40,7 @@ describe("Setup -> cloudbeaverSetup", () => { }; for (const [key, value] of Object.entries(expectedEnv)) { - expect(process.env[key]).toBe(value); + expect(answers[key]).toBe(value); } }); }); diff --git a/test/setup/minioSetup.test.ts b/test/setup/minioSetup.test.ts index cebf7d172ba..7893b9e15a8 100644 --- a/test/setup/minioSetup.test.ts +++ b/test/setup/minioSetup.test.ts @@ -25,7 +25,7 @@ describe("Setup -> minioSetup", () => { promptMock.mockResolvedValueOnce(response); } - await minioSetup(); + const answers = await minioSetup(); const expectedEnv = { MINIO_BROWSER: "off", @@ -34,7 +34,7 @@ describe("Setup -> minioSetup", () => { }; for (const [key, value] of Object.entries(expectedEnv)) { - expect(process.env[key]).toBe(value); + expect(answers[key]).toBe(value); } }); }); diff --git a/test/setup/postgresSetup.test.ts b/test/setup/postgresSetup.test.ts index 063cdf80244..ba242115864 100644 --- a/test/setup/postgresSetup.test.ts +++ b/test/setup/postgresSetup.test.ts @@ -24,7 +24,7 @@ describe("Setup -> postgresSetup", () => { promptMock.mockResolvedValueOnce(response); } - await postgresSetup(); + const answers = await postgresSetup(); const expectedEnv = { POSTGRES_DB: "mocked-db", @@ -33,7 +33,7 @@ describe("Setup -> postgresSetup", () => { }; for (const [key, value] of Object.entries(expectedEnv)) { - expect(process.env[key]).toBe(value); + expect(answers[key]).toBe(value); } }); }); diff --git a/test/setup/setNodeEnvironment.test.ts b/test/setup/setNodeEnvironment.test.ts index 783444562e4..825b9e219d7 100644 --- a/test/setup/setNodeEnvironment.test.ts +++ b/test/setup/setNodeEnvironment.test.ts @@ -5,10 +5,7 @@ import { setNodeEnvironment } from "~/src/setup"; vi.mock("inquirer"); describe("Setup -> setNodeEnvironment", () => { - const originalNodeEnv = process.env.NODE_ENV; - afterEach(() => { - process.env.NODE_ENV = originalNodeEnv; vi.resetAllMocks(); }); it("should update NODE_ENV when selection is made", async () => { @@ -16,32 +13,8 @@ describe("Setup -> setNodeEnvironment", () => { vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ NODE_ENV: mockedNodeEnv, }); - await setNodeEnvironment(); - - expect(process.env.NODE_ENV).toBe(mockedNodeEnv); - }); - - it("should prompt the user for NODE_ENV when NODE_ENV is 'test'", async () => { - const mockedNodeEnv = "production"; - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - NODE_ENV: mockedNodeEnv, - }); - process.env.NODE_ENV = "test"; - - await setNodeEnvironment(); - - expect(process.env.NODE_ENV).toBe(mockedNodeEnv); - }); - - it("should set NODE_ENV to 'development' by default when NODE_ENV is 'test' and no selection is made", async () => { - vi.spyOn(inquirer, "prompt").mockResolvedValueOnce({ - NODE_ENV: "development", - }); - - process.env.NODE_ENV = "test"; - - await setNodeEnvironment(); + const answers = await setNodeEnvironment(); - expect(process.env.NODE_ENV).toBe("development"); + expect(answers.NODE_ENV).toBe(mockedNodeEnv); }); }); From 4dd9878baea9f33b2e761a7b351087eec5947c89 Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Sun, 2 Feb 2025 22:03:13 +0530 Subject: [PATCH 07/10] increasing coverage --- setup.ts | 13 +- src/setup.ts | 650 -------------------------- src/setup/setup.ts | 496 ++++++++++++++++++++ test/setup/administratorEmail.test.ts | 6 +- test/setup/apiSetup.test.ts | 2 +- test/setup/cloudbeaverSetup.test.ts | 2 +- test/setup/envSetup.spec.ts | 67 +++ test/setup/minioSetup.test.ts | 2 +- test/setup/postgresSetup.test.ts | 2 +- test/setup/setNodeEnvironment.test.ts | 2 +- test/setup/updateEnvVariable.test.ts | 80 ++++ 11 files changed, 660 insertions(+), 662 deletions(-) delete mode 100644 src/setup.ts create mode 100644 src/setup/setup.ts create mode 100644 test/setup/envSetup.spec.ts create mode 100644 test/setup/updateEnvVariable.test.ts diff --git a/setup.ts b/setup.ts index 2c389509c96..66209a0ed44 100644 --- a/setup.ts +++ b/setup.ts @@ -1,6 +1,13 @@ -import { setup } from "./src/setup"; +import { setup } from "~/src/setup/setup"; setup().catch((err) => { - console.error("An error occurred during setup:", err); - process.exit(1); + setup().catch((err) => { + console.error(`Setup failed: ${err.message}`); + console.error("Error details:", { + type: err.name, + code: err.code, + stack: err.stack, + }); + process.exit(1); + }); }); diff --git a/src/setup.ts b/src/setup.ts deleted file mode 100644 index 878c285970f..00000000000 --- a/src/setup.ts +++ /dev/null @@ -1,650 +0,0 @@ -import crypto from "node:crypto"; -import fs from "node:fs"; -import { abort } from "node:process"; -import dotenv from "dotenv"; -import inquirer from "inquirer"; -import { updateEnvVariable } from "./setup/updateEnvVariable"; - -let answers: Record = {}; - -const envFileName = ".env"; - -export function generateJwtSecret(): string { - try { - return crypto.randomBytes(64).toString("hex"); - } catch (err) { - console.error("Failed to generate JWT secret:", err); - throw new Error("Failed to generate JWT secret"); - } -} - -export function validateURL(input: string): true | string { - try { - new URL(input); - return true; - } catch { - return "Please enter a valid URL."; - } -} - -export function validatePort(input: string): true | string { - const portNumber = Number(input); - if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { - return "Please enter a valid port number (1-65535)."; - } - return true; -} - -export function validateEmail(input: string): true | string { - if (!input.trim()) { - console.log("Email cannot be empty."); - return "Email cannot be empty."; - } - - if (input.length > 254) { - return "Email is too long."; - } - - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(input)) { - return "Invalid email format. Please enter a valid email address."; - } - return true; -} - -function checkEnvFile(): boolean { - if (fs.existsSync(envFileName)) { - return true; - } - return false; -} - -export function initializeEnvFile(): void { - const envFileToUse = - answers.CI === "true" ? "envFiles/.env.ci" : "envFiles/.env.devcontainer"; - - const parsedEnv = dotenv.parse(fs.readFileSync(envFileToUse)); - - dotenv.config({ path: envFileName }); - const content = Object.entries(parsedEnv) - .map(([key, value]) => `${key}=${value}`) - .join("\n"); - fs.writeFileSync(envFileName, content, { encoding: "utf-8" }); -} - -export async function setCI(): Promise { - try { - const { CI } = await inquirer.prompt([ - { - type: "list", - name: "CI", - message: "Set CI:", - choices: ["true", "false"], - default: "false", - }, - ]); - answers.CI = CI; - } catch (err) { - console.error(err); - abort(); - } -} - -export async function setNodeEnvironment(): Promise> { - try { - const { NODE_ENV } = await inquirer.prompt([ - { - type: "list", - name: "NODE_ENV", - message: "Select Node environment:", - choices: ["development", "production", "test"], - default: "production", - }, - ]); - answers.NODE_ENV = NODE_ENV; - } catch (err) { - console.error(err); - abort(); - } - return answers; -} - -export async function administratorEmail(): Promise> { - try { - const { API_ADMINISTRATOR_USER_EMAIL_ADDRESS } = await inquirer.prompt([ - { - type: "input", - name: "API_ADMINISTRATOR_USER_EMAIL_ADDRESS", - message: "Enter email :", - default: "administrator@email.com", - validate: validateEmail, - }, - ]); - answers.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = - API_ADMINISTRATOR_USER_EMAIL_ADDRESS; - } catch (err) { - console.log(err); - abort(); - } - return answers; -} - -export async function apiSetup(): Promise> { - const { API_BASE_URL } = await inquirer.prompt([ - { - type: "input", - name: "API_BASE_URL", - message: "API base URL:", - default: "http://127.0.0.1:4000", - validate: validateURL, - }, - ]); - answers.API_BASE_URL = API_BASE_URL; - - const { API_HOST } = await inquirer.prompt([ - { - type: "input", - name: "API_HOST", - message: "API host:", - default: "0.0.0.0", - }, - ]); - answers.API_HOST = API_HOST; - - const { API_PORT } = await inquirer.prompt([ - { - type: "input", - name: "API_PORT", - message: "API port:", - default: "4000", - validate: validatePort, - }, - ]); - answers.API_PORT = API_PORT; - - const { API_IS_APPLY_DRIZZLE_MIGRATIONS } = await inquirer.prompt([ - { - type: "list", - name: "API_IS_APPLY_DRIZZLE_MIGRATIONS", - message: "Apply Drizzle migrations?", - choices: ["true", "false"], - default: "true", - }, - ]); - answers.API_IS_APPLY_DRIZZLE_MIGRATIONS = API_IS_APPLY_DRIZZLE_MIGRATIONS; - - const { API_IS_GRAPHIQL } = await inquirer.prompt([ - { - type: "list", - name: "API_IS_GRAPHIQL", - message: "Enable GraphQL?", - choices: ["true", "false"], - default: answers.CI === "false" ? "false" : "true", - }, - ]); - answers.API_IS_GRAPHIQL = API_IS_GRAPHIQL; - - const { API_IS_PINO_PRETTY } = await inquirer.prompt([ - { - type: "list", - name: "API_IS_PINO_PRETTY", - message: "Enable Pino Pretty logs?", - choices: ["true", "false"], - default: answers.CI === "false" ? "false" : "true", - }, - ]); - answers.API_IS_PINO_PRETTY = API_IS_PINO_PRETTY; - - const { API_JWT_EXPIRES_IN } = await inquirer.prompt([ - { - type: "input", - name: "API_JWT_EXPIRES_IN", - message: "JWT expiration (ms):", - default: "2592000000", - }, - ]); - answers.API_JWT_EXPIRES_IN = API_JWT_EXPIRES_IN; - - const jwtSecret = generateJwtSecret(); - - const { API_JWT_SECRET } = await inquirer.prompt([ - { - type: "input", - name: "API_JWT_SECRET", - message: "JWT secret:", - default: jwtSecret, - validate: (input: string) => { - if (input.length < 128) { - return "JWT secret must be at least 128 characters long."; - } - return true; - }, - }, - ]); - answers.API_JWT_SECRET = API_JWT_SECRET; - - const { API_LOG_LEVEL } = await inquirer.prompt([ - { - type: "input", - name: "API_LOG_LEVEL", - message: "LOG level:", - choices: ["info", "debug"], - default: answers.CI === "true" ? "info" : "debug", - }, - ]); - answers.API_LOG_LEVEL = API_LOG_LEVEL; - - const { API_MINIO_ACCESS_KEY } = await inquirer.prompt([ - { - type: "input", - name: "API_MINIO_ACCESS_KEY", - message: "Minio access key:", - default: "talawa", - }, - ]); - answers.API_MINIO_ACCESS_KEY = API_MINIO_ACCESS_KEY; - - const { API_MINIO_END_POINT } = await inquirer.prompt([ - { - type: "input", - name: "API_MINIO_END_POINT", - message: "Minio endpoint:", - default: "minio", - }, - ]); - answers.API_MINIO_END_POINT = API_MINIO_END_POINT; - - const { API_MINIO_PORT } = await inquirer.prompt([ - { - type: "input", - name: "API_MINIO_PORT", - message: "Minio port:", - default: "9000", - }, - ]); - answers.API_MINIO_PORT = API_MINIO_PORT; - - const { API_MINIO_SECRET_KEY } = await inquirer.prompt([ - { - type: "input", - name: "API_MINIO_SECRET_KEY", - message: "Minio secret key:", - default: "password", - }, - ]); - answers.API_MINIO_SECRET_KEY = API_MINIO_SECRET_KEY; - - const { API_MINIO_TEST_END_POINT } = await inquirer.prompt([ - { - type: "input", - name: "API_MINIO_TEST_END_POINT", - message: "Minio test endpoint:", - default: "minio-test", - }, - ]); - answers.API_MINIO_TEST_END_POINT = API_MINIO_TEST_END_POINT; - - const { API_MINIO_USE_SSL } = await inquirer.prompt([ - { - type: "list", - name: "API_MINIO_USE_SSL", - message: "Use Minio SSL?", - choices: ["true", "false"], - default: "false", - }, - ]); - answers.API_MINIO_USE_SSL = API_MINIO_USE_SSL; - - const { API_POSTGRES_DATABASE } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_DATABASE", - message: "Postgres database:", - default: "talawa", - }, - ]); - answers.API_POSTGRES_DATABASE = API_POSTGRES_DATABASE; - - const { API_POSTGRES_HOST } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_HOST", - message: "Postgres host:", - default: "postgres", - }, - ]); - answers.API_POSTGRES_HOST = API_POSTGRES_HOST; - - const { API_POSTGRES_PASSWORD } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password", - }, - ]); - answers.API_POSTGRES_PASSWORD = API_POSTGRES_PASSWORD; - - const { API_POSTGRES_PORT } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_PORT", - message: "Postgres port:", - default: "5432", - }, - ]); - answers.API_POSTGRES_PORT = API_POSTGRES_PORT; - - const { API_POSTGRES_SSL_MODE } = await inquirer.prompt([ - { - type: "list", - name: "API_POSTGRES_SSL_MODE", - message: "Use Postgres SSL?", - choices: ["true", "false"], - default: "false", - }, - ]); - answers.API_POSTGRES_SSL_MODE = API_POSTGRES_SSL_MODE; - - const { API_POSTGRES_TEST_HOST } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_TEST_HOST", - message: "Postgres test host:", - default: "postgres-test", - }, - ]); - answers.API_POSTGRES_TEST_HOST = API_POSTGRES_TEST_HOST; - - const { API_POSTGRES_USER } = await inquirer.prompt([ - { - type: "input", - name: "API_POSTGRES_USER", - message: "Postgres user:", - default: "talawa", - }, - ]); - answers.API_POSTGRES_USER = API_POSTGRES_USER; - return answers; -} - -export async function cloudbeaverSetup(): Promise> { - const { CLOUDBEAVER_ADMIN_NAME } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_ADMIN_NAME", - message: "CloudBeaver admin name:", - default: "talawa", - }, - ]); - answers.CLOUDBEAVER_ADMIN_NAME = CLOUDBEAVER_ADMIN_NAME; - - const { CLOUDBEAVER_ADMIN_PASSWORD } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_ADMIN_PASSWORD", - message: "CloudBeaver admin password:", - default: "password", - }, - ]); - answers.CLOUDBEAVER_ADMIN_PASSWORD = CLOUDBEAVER_ADMIN_PASSWORD; - - const { CLOUDBEAVER_MAPPED_HOST_IP } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_MAPPED_HOST_IP", - message: "CloudBeaver mapped host IP:", - default: "127.0.0.1", - }, - ]); - answers.CLOUDBEAVER_MAPPED_HOST_IP = CLOUDBEAVER_MAPPED_HOST_IP; - - const { CLOUDBEAVER_MAPPED_PORT } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_MAPPED_PORT", - message: "CloudBeaver mapped port:", - default: "8978", - validate: validatePort, - }, - ]); - answers.CLOUDBEAVER_MAPPED_PORT = CLOUDBEAVER_MAPPED_PORT; - - const { CLOUDBEAVER_SERVER_NAME } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_SERVER_NAME", - message: "CloudBeaver server name:", - default: "Talawa CloudBeaver Server", - }, - ]); - answers.CLOUDBEAVER_SERVER_NAME = CLOUDBEAVER_SERVER_NAME; - - const { CLOUDBEAVER_SERVER_URL } = await inquirer.prompt([ - { - type: "input", - name: "CLOUDBEAVER_SERVER_URL", - message: "CloudBeaver server URL:", - default: "http://127.0.0.1:8978", - validate: validateURL, - }, - ]); - answers.CLOUDBEAVER_SERVER_URL = CLOUDBEAVER_SERVER_URL; - return answers; -} - -export async function minioSetup(): Promise> { - const { MINIO_BROWSER } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_BROWSER", - message: "Minio browser (on/off):", - default: answers.CI === "true" ? "off" : "on", - }, - ]); - answers.MINIO_BROWSER = MINIO_BROWSER; - - if (answers.CI === "false") { - const { MINIO_API_MAPPED_HOST_IP } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_API_MAPPED_HOST_IP", - message: "Minio API mapped host IP:", - default: "127.0.0.1", - }, - ]); - answers.MINIO_API_MAPPED_HOST_IP = MINIO_API_MAPPED_HOST_IP; - - const { MINIO_API_MAPPED_PORT } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_API_MAPPED_PORT", - message: "Minio API mapped port:", - default: "9000", - validate: validatePort, - }, - ]); - answers.MINIO_API_MAPPED_PORT = MINIO_API_MAPPED_PORT; - - const { MINIO_CONSOLE_MAPPED_HOST_IP } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_HOST_IP", - message: "Minio console mapped host IP:", - default: "127.0.0.1", - }, - ]); - answers.MINIO_CONSOLE_MAPPED_HOST_IP = MINIO_CONSOLE_MAPPED_HOST_IP; - - const { MINIO_CONSOLE_MAPPED_PORT } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_CONSOLE_MAPPED_PORT", - message: "Minio console mapped port:", - default: "9001", - validate: validatePort, - }, - ]); - answers.MINIO_CONSOLE_MAPPED_PORT = MINIO_CONSOLE_MAPPED_PORT; - } - - const { MINIO_ROOT_PASSWORD } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_ROOT_PASSWORD", - message: "Minio root password:", - default: "password", - }, - ]); - answers.MINIO_ROOT_PASSWORD = MINIO_ROOT_PASSWORD; - - const { MINIO_ROOT_USER } = await inquirer.prompt([ - { - type: "input", - name: "MINIO_ROOT_USER", - message: "Minio root user:", - default: "talawa", - }, - ]); - answers.MINIO_ROOT_USER = MINIO_ROOT_USER; - return answers; -} - -export async function postgresSetup(): Promise> { - const { POSTGRES_DB } = await inquirer.prompt([ - { - type: "input", - name: "POSTGRES_DB", - message: "Postgres database:", - default: "talawa", - }, - ]); - answers.POSTGRES_DB = POSTGRES_DB; - - if (answers.CI === "false") { - const { POSTGRES_MAPPED_HOST_IP } = await inquirer.prompt([ - { - type: "input", - name: "POSTGRES_MAPPED_HOST_IP", - message: "Postgres mapped host IP:", - default: "127.0.0.1", - }, - ]); - answers.POSTGRES_MAPPED_HOST_IP = POSTGRES_MAPPED_HOST_IP; - - const { POSTGRES_MAPPED_PORT } = await inquirer.prompt([ - { - type: "input", - name: "POSTGRES_MAPPED_PORT", - message: "Postgres mapped port:", - default: "5432", - validate: validatePort, - }, - ]); - answers.POSTGRES_MAPPED_PORT = POSTGRES_MAPPED_PORT; - } - - const { POSTGRES_PASSWORD } = await inquirer.prompt([ - { - type: "input", - name: "POSTGRES_PASSWORD", - message: "Postgres password:", - default: "password", - }, - ]); - answers.POSTGRES_PASSWORD = POSTGRES_PASSWORD; - - const { POSTGRES_USER } = await inquirer.prompt([ - { - type: "input", - name: "POSTGRES_USER", - message: "Postgres user:", - default: "talawa", - }, - ]); - answers.POSTGRES_USER = POSTGRES_USER; - return answers; -} - -export async function setup(): Promise { - if (checkEnvFile()) { - const { envReconfigure } = await inquirer.prompt([ - { - type: "confirm", - name: "envReconfigure", - message: "Env file found, Do you want to re-configure? (Y)/N", - default: true, - }, - ]); - if (!envReconfigure) { - process.exit(1); - } - } - dotenv.config({ path: envFileName }); - await setCI(); - initializeEnvFile(); - - process.on("SIGINT", () => { - console.log("\nProcess interrupted! Undoing changes..."); - answers = {}; - process.exit(1); - }); - await setNodeEnvironment(); - - const { useDefaultApi } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultApi", - message: "Do you want to use the recommended default API settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultApi) { - await apiSetup(); - } - - const { useDefaultMinio } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultMinio", - message: - "Do you want to use the recommended default Minio settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultMinio) { - await minioSetup(); - } - - if (answers.CI === "false") { - const { useDefaultCloudbeaver } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultCloudbeaver", - message: - "Do you want to use the recommended default CloudBeaver settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultCloudbeaver) { - await cloudbeaverSetup(); - } - } - - const { useDefaultPostgres } = await inquirer.prompt([ - { - type: "confirm", - name: "useDefaultPostgres", - message: - "Do you want to use the recommended default Postgres settings? (Y)/N", - default: true, - }, - ]); - if (!useDefaultPostgres) { - await postgresSetup(); - } - await administratorEmail(); - - updateEnvVariable(answers); - console.log("Configuration complete."); -} diff --git a/src/setup/setup.ts b/src/setup/setup.ts new file mode 100644 index 00000000000..c14d77ad99b --- /dev/null +++ b/src/setup/setup.ts @@ -0,0 +1,496 @@ +import crypto from "node:crypto"; +import fs from "node:fs"; +import { abort } from "node:process"; +import dotenv from "dotenv"; +import inquirer from "inquirer"; +import { updateEnvVariable } from "./updateEnvVariable"; + +async function promptInput( + name: string, + message: string, + defaultValue?: string, + validate?: (input: string) => true | string, +): Promise { + const { [name]: result } = await inquirer.prompt([ + { type: "input", name, message, default: defaultValue, validate }, + ]); + return result; +} + +async function promptList( + name: string, + message: string, + choices: string[], + defaultValue?: string, +): Promise { + const { [name]: result } = await inquirer.prompt([ + { type: "list", name, message, choices, default: defaultValue }, + ]); + return result; +} + +let answers: Record = {}; +const envFileName = ".env"; + +export function generateJwtSecret(): string { + try { + return crypto.randomBytes(64).toString("hex"); + } catch (err) { + console.error("Failed to generate JWT secret:", err); + throw new Error("Failed to generate JWT secret"); + } +} + +export function validateURL(input: string): true | string { + try { + new URL(input); + return true; + } catch { + return "Please enter a valid URL."; + } +} + +export function validatePort(input: string): true | string { + const portNumber = Number(input); + if (Number.isNaN(portNumber) || portNumber <= 0 || portNumber > 65535) { + return "Please enter a valid port number (1-65535)."; + } + return true; +} + +export function validateEmail(input: string): true | string { + if (!input.trim()) { + console.log("Email cannot be empty."); + return "Email cannot be empty."; + } + if (input.length > 254) { + return "Email is too long."; + } + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(input)) { + return "Invalid email format. Please enter a valid email address."; + } + return true; +} + +export function checkEnvFile(): boolean { + return fs.existsSync(envFileName); +} + +export function initializeEnvFile(): void { + console.log("answer: ", answers.CI); + const envFileToUse = + answers.CI === "true" ? "envFiles/.env.ci" : "envFiles/.env.devcontainer"; + console.log("Reading from:", envFileToUse); + + const parsedEnv = dotenv.parse(fs.readFileSync(envFileToUse)); + dotenv.config({ path: envFileName }); + + const safeContent = Object.entries(parsedEnv) + .map(([key, value]) => `${key}="${value.replace(/"/g, '\\"')}"`) + .join("\n"); + + fs.writeFileSync(envFileName, safeContent, { encoding: "utf-8" }); +} + +export async function setCI(): Promise> { + try { + answers.CI = await promptList("CI", "Set CI:", ["true", "false"], "false"); + } catch (err) { + console.error(err); + abort(); + } + return answers; +} + +export async function setNodeEnvironment(): Promise> { + try { + answers.NODE_ENV = await promptList( + "NODE_ENV", + "Select Node environment:", + ["development", "production", "test"], + "production", + ); + } catch (err) { + console.error(err); + abort(); + } + return answers; +} + +export async function administratorEmail(): Promise> { + try { + answers.API_ADMINISTRATOR_USER_EMAIL_ADDRESS = await promptInput( + "API_ADMINISTRATOR_USER_EMAIL_ADDRESS", + "Enter email:", + "administrator@email.com", + validateEmail, + ); + } catch (err) { + console.log(err); + abort(); + } + return answers; +} + +export async function apiSetup(): Promise> { + answers.API_BASE_URL = await promptInput( + "API_BASE_URL", + "API base URL:", + "http://127.0.0.1:4000", + validateURL, + ); + answers.API_HOST = await promptInput("API_HOST", "API host:", "0.0.0.0"); + + answers.API_PORT = await promptInput( + "API_PORT", + "API port:", + "4000", + validatePort, + ); + + answers.API_IS_APPLY_DRIZZLE_MIGRATIONS = await promptList( + "API_IS_APPLY_DRIZZLE_MIGRATIONS", + "Apply Drizzle migrations?", + ["true", "false"], + "true", + ); + + answers.API_IS_GRAPHIQL = await promptList( + "API_IS_GRAPHIQL", + "Enable GraphQL?", + ["true", "false"], + answers.CI === "false" ? "false" : "true", + ); + + answers.API_IS_PINO_PRETTY = await promptList( + "API_IS_PINO_PRETTY", + "Enable Pino Pretty logs?", + ["true", "false"], + answers.CI === "false" ? "false" : "true", + ); + + answers.API_JWT_EXPIRES_IN = await promptInput( + "API_JWT_EXPIRES_IN", + "JWT expiration (ms):", + "2592000000", + ); + + const jwtSecret = generateJwtSecret(); + answers.API_JWT_SECRET = await promptInput( + "API_JWT_SECRET", + "JWT secret:", + jwtSecret, + (input: string) => { + const trimmed = input.trim(); + if (trimmed.length < 128) { + return "JWT secret must be at least 128 characters long."; + } + return true; + }, + ); + + answers.API_LOG_LEVEL = await promptList( + "API_LOG_LEVEL", + "Log level:", + ["info", "debug"], + answers.CI === "true" ? "info" : "debug", + ); + + answers.API_MINIO_ACCESS_KEY = await promptInput( + "API_MINIO_ACCESS_KEY", + "Minio access key:", + "talawa", + ); + + answers.API_MINIO_END_POINT = await promptInput( + "API_MINIO_END_POINT", + "Minio endpoint:", + "minio", + ); + + answers.API_MINIO_PORT = await promptInput( + "API_MINIO_PORT", + "Minio port:", + "9000", + ); + + answers.API_MINIO_SECRET_KEY = await promptInput( + "API_MINIO_SECRET_KEY", + "Minio secret key:", + "password", + ); + + answers.API_MINIO_TEST_END_POINT = await promptInput( + "API_MINIO_TEST_END_POINT", + "Minio test endpoint:", + "minio-test", + ); + + answers.API_MINIO_USE_SSL = await promptList( + "API_MINIO_USE_SSL", + "Use Minio SSL?", + ["true", "false"], + "false", + ); + + answers.API_POSTGRES_DATABASE = await promptInput( + "API_POSTGRES_DATABASE", + "Postgres database:", + "talawa", + ); + + answers.API_POSTGRES_HOST = await promptInput( + "API_POSTGRES_HOST", + "Postgres host:", + "postgres", + ); + + answers.API_POSTGRES_PASSWORD = await promptInput( + "API_POSTGRES_PASSWORD", + "Postgres password:", + "password", + ); + + answers.API_POSTGRES_PORT = await promptInput( + "API_POSTGRES_PORT", + "Postgres port:", + "5432", + ); + + answers.API_POSTGRES_SSL_MODE = await promptList( + "API_POSTGRES_SSL_MODE", + "Use Postgres SSL?", + ["true", "false"], + "false", + ); + + answers.API_POSTGRES_TEST_HOST = await promptInput( + "API_POSTGRES_TEST_HOST", + "Postgres test host:", + "postgres-test", + ); + + answers.API_POSTGRES_USER = await promptInput( + "API_POSTGRES_USER", + "Postgres user:", + "talawa", + ); + + return answers; +} + +export async function cloudbeaverSetup(): Promise> { + answers.CLOUDBEAVER_ADMIN_NAME = await promptInput( + "CLOUDBEAVER_ADMIN_NAME", + "CloudBeaver admin name:", + "talawa", + ); + + answers.CLOUDBEAVER_ADMIN_PASSWORD = await promptInput( + "CLOUDBEAVER_ADMIN_PASSWORD", + "CloudBeaver admin password:", + "password", + ); + + answers.CLOUDBEAVER_MAPPED_HOST_IP = await promptInput( + "CLOUDBEAVER_MAPPED_HOST_IP", + "CloudBeaver mapped host IP:", + "127.0.0.1", + ); + + answers.CLOUDBEAVER_MAPPED_PORT = await promptInput( + "CLOUDBEAVER_MAPPED_PORT", + "CloudBeaver mapped port:", + "8978", + validatePort, + ); + + answers.CLOUDBEAVER_SERVER_NAME = await promptInput( + "CLOUDBEAVER_SERVER_NAME", + "CloudBeaver server name:", + "Talawa CloudBeaver Server", + ); + + answers.CLOUDBEAVER_SERVER_URL = await promptInput( + "CLOUDBEAVER_SERVER_URL", + "CloudBeaver server URL:", + "http://127.0.0.1:8978", + validateURL, + ); + + return answers; +} + +export async function minioSetup(): Promise> { + answers.MINIO_BROWSER = await promptInput( + "MINIO_BROWSER", + "Minio browser (on/off):", + answers.CI === "true" ? "off" : "on", + ); + + if (answers.CI === "false") { + answers.MINIO_API_MAPPED_HOST_IP = await promptInput( + "MINIO_API_MAPPED_HOST_IP", + "Minio API mapped host IP:", + "127.0.0.1", + ); + + answers.MINIO_API_MAPPED_PORT = await promptInput( + "MINIO_API_MAPPED_PORT", + "Minio API mapped port:", + "9000", + validatePort, + ); + + answers.MINIO_CONSOLE_MAPPED_HOST_IP = await promptInput( + "MINIO_CONSOLE_MAPPED_HOST_IP", + "Minio console mapped host IP:", + "127.0.0.1", + ); + + answers.MINIO_CONSOLE_MAPPED_PORT = await promptInput( + "MINIO_CONSOLE_MAPPED_PORT", + "Minio console mapped port:", + "9001", + validatePort, + ); + } + + answers.MINIO_ROOT_PASSWORD = await promptInput( + "MINIO_ROOT_PASSWORD", + "Minio root password:", + "password", + ); + + answers.MINIO_ROOT_USER = await promptInput( + "MINIO_ROOT_USER", + "Minio root user:", + "talawa", + ); + + return answers; +} + +export async function postgresSetup(): Promise> { + answers.POSTGRES_DB = await promptInput( + "POSTGRES_DB", + "Postgres database:", + "talawa", + ); + + if (answers.CI === "false") { + answers.POSTGRES_MAPPED_HOST_IP = await promptInput( + "POSTGRES_MAPPED_HOST_IP", + "Postgres mapped host IP:", + "127.0.0.1", + ); + + answers.POSTGRES_MAPPED_PORT = await promptInput( + "POSTGRES_MAPPED_PORT", + "Postgres mapped port:", + "5432", + validatePort, + ); + } + + answers.POSTGRES_PASSWORD = await promptInput( + "POSTGRES_PASSWORD", + "Postgres password:", + "password", + ); + + answers.POSTGRES_USER = await promptInput( + "POSTGRES_USER", + "Postgres user:", + "talawa", + ); + + return answers; +} + +export async function setup(): Promise { + if (checkEnvFile()) { + const { envReconfigure } = await inquirer.prompt([ + { + type: "confirm", + name: "envReconfigure", + message: "Env file found. Re-configure? (Y)/N", + default: true, + }, + ]); + if (!envReconfigure) { + process.exit(1); + } + } + + dotenv.config({ path: envFileName }); + + process.on("SIGINT", () => { + console.log("\nProcess interrupted! Undoing changes..."); + if (fs.existsSync(".env.backup")) { + fs.copyFileSync(".env.backup", ".env"); + } + answers = {}; + process.exit(1); + }); + + await setCI(); + initializeEnvFile(); + await setNodeEnvironment(); + + const { useDefaultApi } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultApi", + message: "Use recommended default API settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultApi) { + await apiSetup(); + } + + const { useDefaultMinio } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultMinio", + message: "Use recommended default Minio settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultMinio) { + await minioSetup(); + } + + if (answers.CI === "false") { + const { useDefaultCloudbeaver } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultCloudbeaver", + message: "Use recommended default CloudBeaver settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultCloudbeaver) { + await cloudbeaverSetup(); + } + } + + const { useDefaultPostgres } = await inquirer.prompt([ + { + type: "confirm", + name: "useDefaultPostgres", + message: "Use recommended default Postgres settings? (Y)/N", + default: true, + }, + ]); + if (!useDefaultPostgres) { + await postgresSetup(); + } + + await administratorEmail(); + + updateEnvVariable(answers); + console.log("Configuration complete."); +} diff --git a/test/setup/administratorEmail.test.ts b/test/setup/administratorEmail.test.ts index 7125a506c8f..2101b4e6cc3 100644 --- a/test/setup/administratorEmail.test.ts +++ b/test/setup/administratorEmail.test.ts @@ -1,7 +1,7 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { administratorEmail } from "~/src/setup"; -import { validateEmail } from "~/src/setup"; +import { administratorEmail } from "~/src/setup/setup"; +import { validateEmail } from "~/src/setup/setup"; vi.mock("inquirer"); @@ -29,8 +29,6 @@ describe("Setup -> askForAdministratorEmail", () => { expect(validateEmail("test.email@domain.io")).toBe(true); expect(validateEmail("user+tag@example.co.uk")).toBe(true); expect(validateEmail("user@xn--80ak6aa92e.com")).toBe(true); - expect(validateEmail("user+tag@example.co.uk")).toBe(true); - expect(validateEmail("user@xn--80ak6aa92e.com")).toBe(true); }); it("should return an error message for invalid email addresses", () => { diff --git a/test/setup/apiSetup.test.ts b/test/setup/apiSetup.test.ts index e27c63b99c3..ddffc3af69c 100644 --- a/test/setup/apiSetup.test.ts +++ b/test/setup/apiSetup.test.ts @@ -5,7 +5,7 @@ import { generateJwtSecret, validatePort, validateURL, -} from "~/src/setup"; +} from "~/src/setup/setup"; vi.mock("inquirer"); diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts index 5c18c2da59e..9e3ea726a2f 100644 --- a/test/setup/cloudbeaverSetup.test.ts +++ b/test/setup/cloudbeaverSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { cloudbeaverSetup } from "~/src/setup"; +import { cloudbeaverSetup } from "~/src/setup/setup"; vi.mock("inquirer"); diff --git a/test/setup/envSetup.spec.ts b/test/setup/envSetup.spec.ts new file mode 100644 index 00000000000..46a85401e17 --- /dev/null +++ b/test/setup/envSetup.spec.ts @@ -0,0 +1,67 @@ +import fs from "node:fs"; +import inquirer from "inquirer"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { checkEnvFile, initializeEnvFile, setCI } from "~/src/setup/setup"; + +vi.mock("dotenv", async (importOriginal) => { + const actual = await importOriginal(); + return { + default: actual, + config: vi.fn(), + parse: vi.fn((content) => { + if (content.includes("KEY1")) return { KEY1: "VAL1", KEY2: "VAL2" }; + if (content.includes("FOO")) return { FOO: "bar" }; + return {}; + }), + }; +}); + +const envFileName = ".env"; + +describe("checkEnvFile", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("should return true if .env file exists", () => { + vi.spyOn(fs, "existsSync").mockReturnValue(true); + + const result = checkEnvFile(); + expect(fs.existsSync).toHaveBeenCalledWith(envFileName); + expect(result).toBe(true); + }); + + it("should return false if .env file does not exist", () => { + vi.spyOn(fs, "existsSync").mockReturnValue(false); + + const result = checkEnvFile(); + expect(fs.existsSync).toHaveBeenCalledWith(envFileName); + expect(result).toBe(false); + }); +}); + +describe("initializeEnvFile", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("should read from .env.devcontainer when answers.CI is 'false'", async () => { + vi.spyOn(inquirer, "prompt").mockResolvedValue({ CI: "false" }); + await setCI(); + vi.spyOn(fs, "readFileSync").mockReturnValue("KEY1=VAL1\nKEY2=VAL2"); + + initializeEnvFile(); + + expect(fs.readFileSync).toHaveBeenCalledWith("envFiles/.env.devcontainer"); + }); + + it("should read from .env.ci when answers.CI is 'true'", async () => { + vi.spyOn(inquirer, "prompt").mockResolvedValue({ CI: "true" }); + await setCI(); + vi.spyOn(fs, "readFileSync").mockReturnValue("FOO=bar"); + + initializeEnvFile(); + + expect(fs.readFileSync).toHaveBeenCalledWith("envFiles/.env.ci"); + }); +}); diff --git a/test/setup/minioSetup.test.ts b/test/setup/minioSetup.test.ts index 7893b9e15a8..0b8229f4e27 100644 --- a/test/setup/minioSetup.test.ts +++ b/test/setup/minioSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { minioSetup } from "~/src/setup"; +import { minioSetup } from "~/src/setup/setup"; vi.mock("inquirer"); diff --git a/test/setup/postgresSetup.test.ts b/test/setup/postgresSetup.test.ts index ba242115864..dad4ffd2fd4 100644 --- a/test/setup/postgresSetup.test.ts +++ b/test/setup/postgresSetup.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { postgresSetup } from "~/src/setup"; +import { postgresSetup } from "~/src/setup/setup"; vi.mock("inquirer"); diff --git a/test/setup/setNodeEnvironment.test.ts b/test/setup/setNodeEnvironment.test.ts index 825b9e219d7..3583f7b0bb0 100644 --- a/test/setup/setNodeEnvironment.test.ts +++ b/test/setup/setNodeEnvironment.test.ts @@ -1,6 +1,6 @@ import inquirer from "inquirer"; import { afterEach, describe, expect, it, vi } from "vitest"; -import { setNodeEnvironment } from "~/src/setup"; +import { setNodeEnvironment } from "~/src/setup/setup"; vi.mock("inquirer"); diff --git a/test/setup/updateEnvVariable.test.ts b/test/setup/updateEnvVariable.test.ts new file mode 100644 index 00000000000..6315651b8b1 --- /dev/null +++ b/test/setup/updateEnvVariable.test.ts @@ -0,0 +1,80 @@ +import fs from "node:fs"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { updateEnvVariable } from "~/src/setup/updateEnvVariable"; + +vi.mock("fs"); + +describe("updateEnvVariable", () => { + const envFileName = ".env"; + const backupFile = `${envFileName}.backup`; + + beforeEach(() => { + vi.resetAllMocks(); + vi.spyOn(fs, "existsSync").mockReturnValue(true); // Assume `.env` exists + }); + + it("should update an existing variable in .env", () => { + vi.spyOn(fs, "readFileSync").mockReturnValue("EXISTING_VAR=old_value"); + const writeSpy = vi.spyOn(fs, "writeFileSync").mockImplementation(() => {}); + + updateEnvVariable({ EXISTING_VAR: "new_value" }); + + expect(writeSpy).toHaveBeenCalledWith( + envFileName, + expect.stringContaining("EXISTING_VAR=new_value"), + "utf8", + ); + expect(process.env.EXISTING_VAR).toBe("new_value"); + }); + + it("should add a new variable if it does not exist", () => { + vi.spyOn(fs, "readFileSync").mockReturnValue("EXISTING_VAR=old_value"); + const writeSpy = vi.spyOn(fs, "writeFileSync").mockImplementation(() => {}); + + updateEnvVariable({ NEW_VAR: "new_value" }); + + expect(writeSpy).toHaveBeenCalledWith( + envFileName, + expect.stringContaining("NEW_VAR=new_value"), + "utf8", + ); + expect(process.env.NEW_VAR).toBe("new_value"); + }); + + it("should create a backup before updating .env", () => { + const copySpy = vi.spyOn(fs, "copyFileSync").mockImplementation(() => {}); + vi.spyOn(fs, "readFileSync").mockReturnValue("EXISTING_VAR=old_value"); + + updateEnvVariable({ EXISTING_VAR: "new_value" }); + + expect(copySpy).toHaveBeenCalledWith(envFileName, backupFile); + }); + + it("should restore from backup if an error occurs", () => { + const copySpy = vi.spyOn(fs, "copyFileSync").mockImplementation(() => {}); + vi.spyOn(fs, "readFileSync").mockReturnValue("EXISTING_VAR=old_value"); + vi.spyOn(fs, "writeFileSync").mockImplementation(() => { + throw new Error("Write failed"); + }); + + expect(() => updateEnvVariable({ EXISTING_VAR: "new_value" })).toThrow( + "Write failed", + ); + + expect(copySpy).toHaveBeenCalledWith(backupFile, envFileName); + }); + + it("should create .env if it does not exist", () => { + vi.spyOn(fs, "existsSync").mockReturnValue(false); + const writeSpy = vi.spyOn(fs, "writeFileSync").mockImplementation(() => {}); + + updateEnvVariable({ NEW_VAR: "new_value" }); + + expect(writeSpy).toHaveBeenCalledWith( + envFileName, + expect.stringContaining("NEW_VAR=new_value"), + "utf8", + ); + expect(process.env.NEW_VAR).toBe("new_value"); + }); +}); From 303b872d6a15bb9964c746a7a028351713e9164e Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Thu, 6 Feb 2025 20:36:55 +0530 Subject: [PATCH 08/10] setup test --- package.json | 4 +- pnpm-lock.yaml | 268 ++++++++++++++++------------ setup.ts | 14 +- src/setup/setup.ts | 9 +- test/setup/cloudbeaverSetup.test.ts | 2 +- test/setup/setup.test.ts | 116 ++++++++++++ 6 files changed, 287 insertions(+), 126 deletions(-) diff --git a/package.json b/package.json index c1196798a02..728aa381808 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,16 @@ "@sinclair/typebox": "^0.34.15", "ajv-formats": "^3.0.1", "close-with-grace": "^2.2.0", + "dotenv": "^16.4.7", "drizzle-orm": "^0.39.1", "drizzle-zod": "0.6.1", - "dotenv": "^16.0.3", "env-schema": "^6.0.1", "fastify": "^5.2.1", "fastify-plugin": "^5.0.1", "graphql": "^16.10.0", "graphql-scalars": "^1.24.0", "graphql-upload-minimal": "^1.6.1", + "inquirer": "^12.4.1", "mercurius": "^16.0.1", "mercurius-upload": "^8.0.0", "minio": "^8.0.4", @@ -94,6 +95,7 @@ "start_development_server_with_debugger": "tsx watch --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./src/index.ts", "start_production_server": "pnpm build_production && node ./dist/index.js", "start_production_server_with_debugger": "pnpm build_production && node --inspect=${API_DEBUGGER_HOST:-127.0.0.1}:${API_DEBUGGER_PORT:-9229} ./dist/index.js", + "setup": "tsx setup.ts", "upgrade_drizzle_metadata": "drizzle-kit up" }, "type": "module", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fe529994a7..9b9f46086b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,11 +42,7 @@ importers: specifier: ^2.2.0 version: 2.2.0 dotenv: -<<<<<<< HEAD specifier: ^16.4.7 -======= - specifier: ^16.0.3 ->>>>>>> 8dca2acb28bed4a1f48534f6f79807752e48d54a version: 16.4.7 drizzle-orm: specifier: ^0.39.1 @@ -73,8 +69,8 @@ importers: specifier: ^1.6.1 version: 1.6.1(graphql@16.10.0) inquirer: - specifier: ^12.3.2 - version: 12.3.2(@types/node@22.10.7) + specifier: ^12.4.1 + version: 12.4.1(@types/node@22.10.7) mercurius: specifier: ^16.0.1 version: 16.0.1(graphql@16.10.0) @@ -112,9 +108,6 @@ importers: '@swc/core': specifier: ^1.10.9 version: 1.10.9 - '@types/inquirer': - specifier: ^9.0.7 - version: 9.0.7 '@types/node': specifier: ^22.10.7 version: 22.10.7 @@ -912,85 +905,126 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@inquirer/checkbox@4.0.6': - resolution: {integrity: sha512-PgP35JfmGjHU0LSXOyRew0zHuA9N6OJwOlos1fZ20b7j8ISeAdib3L+n0jIxBtX958UeEpte6xhG/gxJ5iUqMw==} + '@inquirer/checkbox@4.1.1': + resolution: {integrity: sha512-os5kFd/52gZTl/W6xqMfhaKVJHQM8V/U1P8jcSaQJ/C4Qhdrf2jEXdA/HaxfQs9iiUA/0yzYhk5d3oRHTxGDDQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/confirm@5.1.3': - resolution: {integrity: sha512-fuF9laMmHoOgWapF9h9hv6opA5WvmGFHsTYGCmuFxcghIhEhb3dN0CdQR4BUMqa2H506NCj8cGX4jwMsE4t6dA==} + '@inquirer/confirm@5.1.5': + resolution: {integrity: sha512-ZB2Cz8KeMINUvoeDi7IrvghaVkYT2RB0Zb31EaLWOE87u276w4wnApv0SH2qWaJ3r0VSUa3BIuz7qAV2ZvsZlg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/core@10.1.4': - resolution: {integrity: sha512-5y4/PUJVnRb4bwWY67KLdebWOhOc7xj5IP2J80oWXa64mVag24rwQ1VAdnj7/eDY/odhguW0zQ1Mp1pj6fO/2w==} + '@inquirer/core@10.1.6': + resolution: {integrity: sha512-Bwh/Zk6URrHwZnSSzAZAKH7YgGYi0xICIBDFOqBQoXNNAzBHw/bgXgLmChfp+GyR3PnChcTbiCTZGC6YJNJkMA==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/editor@4.2.3': - resolution: {integrity: sha512-S9KnIOJuTZpb9upeRSBBhoDZv7aSV3pG9TECrBj0f+ZsFwccz886hzKBrChGrXMJwd4NKY+pOA9Vy72uqnd6Eg==} + '@inquirer/editor@4.2.6': + resolution: {integrity: sha512-l0smvr8g/KAVdXx4I92sFxZiaTG4kFc06cFZw+qqwTirwdUHMFLnouXBB9OafWhpO3cfEkEz2CdPoCmor3059A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/expand@4.0.6': - resolution: {integrity: sha512-TRTfi1mv1GeIZGyi9PQmvAaH65ZlG4/FACq6wSzs7Vvf1z5dnNWsAAXBjWMHt76l+1hUY8teIqJFrWBk5N6gsg==} + '@inquirer/expand@4.0.8': + resolution: {integrity: sha512-k0ouAC6L+0Yoj/j0ys2bat0fYcyFVtItDB7h+pDFKaDDSFJey/C/YY1rmIOqkmFVZ5rZySeAQuS8zLcKkKRLmg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/figures@1.0.9': - resolution: {integrity: sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==} + '@inquirer/figures@1.0.10': + resolution: {integrity: sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==} engines: {node: '>=18'} - '@inquirer/input@4.1.3': - resolution: {integrity: sha512-zeo++6f7hxaEe7OjtMzdGZPHiawsfmCZxWB9X1NpmYgbeoyerIbWemvlBxxl+sQIlHC0WuSAG19ibMq3gbhaqQ==} + '@inquirer/input@4.1.5': + resolution: {integrity: sha512-bB6wR5wBCz5zbIVBPnhp94BHv/G4eKbUEjlpCw676pI2chcvzTx1MuwZSCZ/fgNOdqDlAxkhQ4wagL8BI1D3Zg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/number@3.0.6': - resolution: {integrity: sha512-xO07lftUHk1rs1gR0KbqB+LJPhkUNkyzV/KhH+937hdkMazmAYHLm1OIrNKpPelppeV1FgWrgFDjdUD8mM+XUg==} + '@inquirer/number@3.0.8': + resolution: {integrity: sha512-CTKs+dT1gw8dILVWATn8Ugik1OHLkkfY82J+Musb57KpmF6EKyskv8zmMiEJPzOnLTZLo05X/QdMd8VH9oulXw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/password@4.0.6': - resolution: {integrity: sha512-QLF0HmMpHZPPMp10WGXh6F+ZPvzWE7LX6rNoccdktv/Rov0B+0f+eyXkAcgqy5cH9V+WSpbLxu2lo3ysEVK91w==} + '@inquirer/password@4.0.8': + resolution: {integrity: sha512-MgA+Z7o3K1df2lGY649fyOBowHGfrKRz64dx3+b6c1w+h2W7AwBoOkHhhF/vfhbs5S4vsKNCuDzS3s9r5DpK1g==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/prompts@7.2.3': - resolution: {integrity: sha512-hzfnm3uOoDySDXfDNOm9usOuYIaQvTgKp/13l1uJoe6UNY+Zpcn2RYt0jXz3yA+yemGHvDOxVzqWl3S5sQq53Q==} + '@inquirer/prompts@7.3.1': + resolution: {integrity: sha512-r1CiKuDV86BDpvj9DRFR+V+nIjsVBOsa2++dqdPqLYAef8kgHYvmQ8ySdP/ZeAIOWa27YGJZRkENdP3dK0H3gg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/rawlist@4.0.6': - resolution: {integrity: sha512-QoE4s1SsIPx27FO4L1b1mUjVcoHm1pWE/oCmm4z/Hl+V1Aw5IXl8FYYzGmfXaBT0l/sWr49XmNSiq7kg3Kd/Lg==} + '@inquirer/rawlist@4.0.8': + resolution: {integrity: sha512-hl7rvYW7Xl4un8uohQRUgO6uc2hpn7PKqfcGkCOWC0AA4waBxAv6MpGOFCEDrUaBCP+pXPVqp4LmnpWmn1E1+g==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/search@3.0.6': - resolution: {integrity: sha512-eFZ2hiAq0bZcFPuFFBmZEtXU1EarHLigE+ENCtpO+37NHCl4+Yokq1P/d09kUblObaikwfo97w+0FtG/EXl5Ng==} + '@inquirer/search@3.0.8': + resolution: {integrity: sha512-ihSE9D3xQAupNg/aGDZaukqoUSXG2KfstWosVmFCG7jbMQPaj2ivxWtsB+CnYY/T4D6LX1GHKixwJLunNCffww==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/select@4.0.6': - resolution: {integrity: sha512-yANzIiNZ8fhMm4NORm+a74+KFYHmf7BZphSOBovIzYPVLquseTGEkU5l2UTnBOf5k0VLmTgPighNDLE9QtbViQ==} + '@inquirer/select@4.0.8': + resolution: {integrity: sha512-Io2prxFyN2jOCcu4qJbVoilo19caiD3kqkD3WR0q3yDA5HUCo83v4LrRtg55ZwniYACW64z36eV7gyVbOfORjA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/type@3.0.2': - resolution: {integrity: sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==} + '@inquirer/type@3.0.4': + resolution: {integrity: sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -1446,9 +1480,6 @@ packages: '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - '@types/inquirer@9.0.7': - resolution: {integrity: sha512-Q0zyBupO6NxGRZut/JdmqYKOnN95Eg5V8Csg3PGKkP+FnvsUZx1jAyK7fztIszxxMuoBA6E3KXWvdZVXIpx60g==} - '@types/node@22.10.7': resolution: {integrity: sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg==} @@ -2245,11 +2276,14 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inquirer@12.3.2: - resolution: {integrity: sha512-YjQCIcDd3yyDuQrbII0FBtm/ZqNoWtvaC71yeCnd5Vbg4EgzsAGaemzfpzmqfvIZEp2roSwuZZKdM0C65hA43g==} + inquirer@12.4.1: + resolution: {integrity: sha512-/V7OyFkeUBFO2jAokUq5emSlcVMHVvzg8bwwZnzmCwErPgbeftsthmPUg71AIi5mR0YmiJOLQ+bTiHVWEjOw7A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true inspect-with-kind@1.0.5: resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} @@ -3680,110 +3714,120 @@ snapshots: dependencies: graphql: 16.10.0 - '@inquirer/checkbox@4.0.6(@types/node@22.10.7)': + '@inquirer/checkbox@4.1.1(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/figures': 1.0.9 - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@22.10.7) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/confirm@5.1.3(@types/node@22.10.7)': + '@inquirer/confirm@5.1.5(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) + optionalDependencies: '@types/node': 22.10.7 - '@inquirer/core@10.1.4(@types/node@22.10.7)': + '@inquirer/core@10.1.6(@types/node@22.10.7)': dependencies: - '@inquirer/figures': 1.0.9 - '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@22.10.7) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 - strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - transitivePeerDependencies: - - '@types/node' + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/editor@4.2.3(@types/node@22.10.7)': + '@inquirer/editor@4.2.6(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) external-editor: 3.1.0 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/expand@4.0.6(@types/node@22.10.7)': + '@inquirer/expand@4.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/figures@1.0.9': {} + '@inquirer/figures@1.0.10': {} - '@inquirer/input@4.1.3(@types/node@22.10.7)': + '@inquirer/input@4.1.5(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) + optionalDependencies: '@types/node': 22.10.7 - '@inquirer/number@3.0.6(@types/node@22.10.7)': + '@inquirer/number@3.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) + optionalDependencies: '@types/node': 22.10.7 - '@inquirer/password@4.0.6(@types/node@22.10.7)': + '@inquirer/password@4.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/prompts@7.2.3(@types/node@22.10.7)': - dependencies: - '@inquirer/checkbox': 4.0.6(@types/node@22.10.7) - '@inquirer/confirm': 5.1.3(@types/node@22.10.7) - '@inquirer/editor': 4.2.3(@types/node@22.10.7) - '@inquirer/expand': 4.0.6(@types/node@22.10.7) - '@inquirer/input': 4.1.3(@types/node@22.10.7) - '@inquirer/number': 3.0.6(@types/node@22.10.7) - '@inquirer/password': 4.0.6(@types/node@22.10.7) - '@inquirer/rawlist': 4.0.6(@types/node@22.10.7) - '@inquirer/search': 3.0.6(@types/node@22.10.7) - '@inquirer/select': 4.0.6(@types/node@22.10.7) + '@inquirer/prompts@7.3.1(@types/node@22.10.7)': + dependencies: + '@inquirer/checkbox': 4.1.1(@types/node@22.10.7) + '@inquirer/confirm': 5.1.5(@types/node@22.10.7) + '@inquirer/editor': 4.2.6(@types/node@22.10.7) + '@inquirer/expand': 4.0.8(@types/node@22.10.7) + '@inquirer/input': 4.1.5(@types/node@22.10.7) + '@inquirer/number': 3.0.8(@types/node@22.10.7) + '@inquirer/password': 4.0.8(@types/node@22.10.7) + '@inquirer/rawlist': 4.0.8(@types/node@22.10.7) + '@inquirer/search': 3.0.8(@types/node@22.10.7) + '@inquirer/select': 4.0.8(@types/node@22.10.7) + optionalDependencies: '@types/node': 22.10.7 - '@inquirer/rawlist@4.0.6(@types/node@22.10.7)': + '@inquirer/rawlist@4.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/search@3.0.6(@types/node@22.10.7)': + '@inquirer/search@3.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/figures': 1.0.9 - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@22.10.7) yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/select@4.0.6(@types/node@22.10.7)': + '@inquirer/select@4.0.8(@types/node@22.10.7)': dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/figures': 1.0.9 - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/figures': 1.0.10 + '@inquirer/type': 3.0.4(@types/node@22.10.7) ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.10.7 - '@inquirer/type@3.0.2(@types/node@22.10.7)': - dependencies: + '@inquirer/type@3.0.4(@types/node@22.10.7)': + optionalDependencies: '@types/node': 22.10.7 '@isaacs/cliui@8.0.2': @@ -4119,11 +4163,6 @@ snapshots: '@types/http-cache-semantics@4.0.4': {} - '@types/inquirer@9.0.7': - dependencies: - '@types/through': 0.0.33 - rxjs: 7.8.1 - '@types/node@22.10.7': dependencies: undici-types: 6.20.0 @@ -5000,16 +5039,17 @@ snapshots: inherits@2.0.4: {} - inquirer@12.3.2(@types/node@22.10.7): + inquirer@12.4.1(@types/node@22.10.7): dependencies: - '@inquirer/core': 10.1.4(@types/node@22.10.7) - '@inquirer/prompts': 7.2.3(@types/node@22.10.7) - '@inquirer/type': 3.0.2(@types/node@22.10.7) - '@types/node': 22.10.7 + '@inquirer/core': 10.1.6(@types/node@22.10.7) + '@inquirer/prompts': 7.3.1(@types/node@22.10.7) + '@inquirer/type': 3.0.4(@types/node@22.10.7) ansi-escapes: 4.3.2 mute-stream: 2.0.0 run-async: 3.0.0 rxjs: 7.8.1 + optionalDependencies: + '@types/node': 22.10.7 inspect-with-kind@1.0.5: dependencies: @@ -5914,4 +5954,6 @@ snapshots: buffer-crc32: 0.2.13 pend: 1.2.0 + yoctocolors-cjs@2.1.2: {} + zod@3.24.1: {} diff --git a/setup.ts b/setup.ts index f7ffac4c67b..3d9d85985b9 100644 --- a/setup.ts +++ b/setup.ts @@ -1,11 +1,11 @@ import { setup } from "~/src/setup/setup"; setup().catch((err) => { - console.error(`Setup failed: ${err.message}`); - console.error("Error details:", { - type: err.name, - code: err.code, - stack: err.stack, - }); - process.exit(1); + console.error(`Setup failed: ${err.message}`); + console.error("Error details:", { + type: err.name, + code: err.code, + stack: err.stack, }); + process.exit(1); +}); diff --git a/src/setup/setup.ts b/src/setup/setup.ts index 1ba0033c99a..2b1b922f9dc 100644 --- a/src/setup/setup.ts +++ b/src/setup/setup.ts @@ -81,7 +81,7 @@ export function initializeEnvFile(): void { if (fs.existsSync(envFileName)) { fs.copyFileSync(envFileName, `${envFileName}.backup`); console.log(`Backup created at ${envFileName}.backup`); - } + } const envFileToUse = answers.CI === "true" ? "envFiles/.env.ci" : "envFiles/.env.devcontainer"; @@ -162,14 +162,14 @@ export async function apiSetup(): Promise> { "API_IS_GRAPHIQL", "Enable GraphQL?", ["true", "false"], - answers.CI === "false" ? "false" : "true", + answers.CI === "false" ? "true" : "false", ); answers.API_IS_PINO_PRETTY = await promptList( "API_IS_PINO_PRETTY", "Enable Pino Pretty logs?", ["true", "false"], - answers.CI === "false" ? "false" : "true", + answers.CI === "false" ? "true" : "false", ); answers.API_JWT_EXPIRES_IN = await promptInput( @@ -411,7 +411,7 @@ export async function postgresSetup(): Promise> { return answers; } -export async function setup(): Promise { +export async function setup(): Promise> { if (checkEnvFile()) { const { envReconfigure } = await inquirer.prompt([ { @@ -495,4 +495,5 @@ export async function setup(): Promise { updateEnvVariable(answers); console.log("Configuration complete."); + return answers; } diff --git a/test/setup/cloudbeaverSetup.test.ts b/test/setup/cloudbeaverSetup.test.ts index c3c2645d7be..db9de7ae9bc 100644 --- a/test/setup/cloudbeaverSetup.test.ts +++ b/test/setup/cloudbeaverSetup.test.ts @@ -36,7 +36,7 @@ describe("Setup -> cloudbeaverSetup", () => { CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", CLOUDBEAVER_MAPPED_PORT: "8080", CLOUDBEAVER_SERVER_NAME: "Mocked Server", - CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8080", + CLOUDBEAVER_SERVER_URL: "https://127.0.0.1:8080", }; for (const [key, value] of Object.entries(expectedEnv)) { diff --git a/test/setup/setup.test.ts b/test/setup/setup.test.ts index e69de29bb2d..e4f9bd56494 100644 --- a/test/setup/setup.test.ts +++ b/test/setup/setup.test.ts @@ -0,0 +1,116 @@ +import dotenv from "dotenv"; +import inquirer from "inquirer"; +import { afterEach, describe, expect, it, vi } from "vitest"; +import { setup } from "~/src/setup/setup"; + +vi.mock("inquirer"); +describe("Setup", () => { + const originalEnv = { ...process.env }; + + afterEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + it("should set up environment variables with default configuration when CI=false", async () => { + const mockResponses = [ + { CI: "false" }, + { NODE_ENV: "production" }, + { useDefaultApi: "true" }, + { useDefaultMinio: "true" }, + { useDefaultCloudbeaver: "true" }, + { useDefaultPostgres: "true" }, + { API_ADMINISTRATOR_USER_EMAIL_ADDRESS: "test@email.com" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await setup(); + + const expectedEnv = { + API_BASE_URL: "http://127.0.0.1:4000", + API_HOST: "0.0.0.0", + API_PORT: "4000", + API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", + API_JWT_EXPIRES_IN: "2592000000", + API_LOG_LEVEL: "info", + API_MINIO_ACCESS_KEY: "talawa", + API_MINIO_END_POINT: "minio", + API_MINIO_PORT: "9000", + API_MINIO_TEST_END_POINT: "minio-test", + API_MINIO_USE_SSL: "false", + API_POSTGRES_DATABASE: "talawa", + API_POSTGRES_HOST: "postgres", + API_POSTGRES_PORT: "5432", + API_POSTGRES_SSL_MODE: "false", + API_POSTGRES_TEST_HOST: "postgres-test", + API_POSTGRES_USER: "talawa", + CI: "false", + MINIO_ROOT_USER: "talawa", + CLOUDBEAVER_ADMIN_NAME: "talawa", + CLOUDBEAVER_MAPPED_HOST_IP: "127.0.0.1", + CLOUDBEAVER_MAPPED_PORT: "8978", + CLOUDBEAVER_SERVER_NAME: "Talawa CloudBeaver Server", + CLOUDBEAVER_SERVER_URL: "http://127.0.0.1:8978", + }; + + dotenv.config({ path: ".env" }); + + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } + }); + + it("should correctly set up environment variables when CI=true (skips CloudBeaver)", async () => { + const mockResponses = [ + { envReconfigure: "true" }, + { CI: "true" }, + { NODE_ENV: "production" }, + { useDefaultApi: "true" }, + { useDefaultMinio: "true" }, + { useDefaultPostgres: "true" }, + { API_ADMINISTRATOR_USER_EMAIL_ADDRESS: "test@email.com" }, + ]; + + const promptMock = vi.spyOn(inquirer, "prompt"); + for (const response of mockResponses) { + promptMock.mockResolvedValueOnce(response); + } + + await setup(); + + const expectedEnv = { + API_BASE_URL: "http://127.0.0.1:4000", + API_HOST: "0.0.0.0", + API_PORT: "4000", + API_IS_APPLY_DRIZZLE_MIGRATIONS: "true", + API_IS_GRAPHIQL: "false", + API_IS_PINO_PRETTY: "false", + API_JWT_EXPIRES_IN: "2592000000", + API_LOG_LEVEL: "info", + API_MINIO_ACCESS_KEY: "talawa", + API_MINIO_END_POINT: "minio", + API_MINIO_PORT: "9000", + API_MINIO_SECRET_KEY: "password", + API_MINIO_TEST_END_POINT: "minio-test", + API_MINIO_USE_SSL: "false", + API_POSTGRES_DATABASE: "talawa", + API_POSTGRES_HOST: "postgres", + API_POSTGRES_PASSWORD: "password", + API_POSTGRES_PORT: "5432", + API_POSTGRES_SSL_MODE: "false", + API_POSTGRES_TEST_HOST: "postgres-test", + API_POSTGRES_USER: "talawa", + CI: "true", + MINIO_ROOT_PASSWORD: "password", + MINIO_ROOT_USER: "talawa", + }; + + for (const [key, value] of Object.entries(expectedEnv)) { + expect(process.env[key]).toBe(value); + } + }); +}); From 0038171a3adab890a52e8e72565b76217152c29b Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Thu, 6 Feb 2025 20:42:51 +0530 Subject: [PATCH 09/10] codeql fix --- src/setup/setup.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/setup/setup.ts b/src/setup/setup.ts index 2b1b922f9dc..7d741e82c4b 100644 --- a/src/setup/setup.ts +++ b/src/setup/setup.ts @@ -89,7 +89,11 @@ export function initializeEnvFile(): void { dotenv.config({ path: envFileName }); const safeContent = Object.entries(parsedEnv) - .map(([key, value]) => `${key}="${value.replace(/"/g, '\\"')}"`) + .map(([key, value]) => { + const safeValue = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); + + return `${key}="${safeValue}"`; + }) .join("\n"); fs.writeFileSync(envFileName, safeContent, { encoding: "utf-8" }); From 33852b12d43bba2fc2d232412bb4f72ea4983b51 Mon Sep 17 00:00:00 2001 From: prayansh_chhablani Date: Thu, 6 Feb 2025 20:57:54 +0530 Subject: [PATCH 10/10] suggestions --- src/setup/setup.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/setup/setup.ts b/src/setup/setup.ts index 7d741e82c4b..cf178fd36ec 100644 --- a/src/setup/setup.ts +++ b/src/setup/setup.ts @@ -1,6 +1,6 @@ import crypto from "node:crypto"; import fs from "node:fs"; -import { abort } from "node:process"; +import process from "node:process"; import dotenv from "dotenv"; import inquirer from "inquirer"; import { updateEnvVariable } from "./updateEnvVariable"; @@ -60,7 +60,6 @@ export function validatePort(input: string): true | string { export function validateEmail(input: string): true | string { if (!input.trim()) { - console.log("Email cannot be empty."); return "Email cannot be empty."; } if (input.length > 254) { @@ -104,7 +103,7 @@ export async function setCI(): Promise> { answers.CI = await promptList("CI", "Set CI:", ["true", "false"], "false"); } catch (err) { console.error(err); - abort(); + process.exit(1); } return answers; } @@ -119,7 +118,7 @@ export async function setNodeEnvironment(): Promise> { ); } catch (err) { console.error(err); - abort(); + process.exit(1); } return answers; } @@ -134,7 +133,7 @@ export async function administratorEmail(): Promise> { ); } catch (err) { console.log(err); - abort(); + process.exit(1); } return answers; } @@ -426,7 +425,7 @@ export async function setup(): Promise> { }, ]); if (!envReconfigure) { - process.exit(1); + process.exit(0); } } @@ -499,5 +498,8 @@ export async function setup(): Promise> { updateEnvVariable(answers); console.log("Configuration complete."); + if (fs.existsSync(".env.backup")) { + fs.unlinkSync(".env.backup"); + } return answers; }