Skip to content

Commit 2f68501

Browse files
Docker setup in Setup Script (#3187)
* first commit * migrated jest to vitest in validateRecaptcha
1 parent c8a1290 commit 2f68501

File tree

10 files changed

+507
-150
lines changed

10 files changed

+507
-150
lines changed

setup.ts

+53-150
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,48 @@
11
import dotenv from 'dotenv';
22
import fs from 'fs';
33
import inquirer from 'inquirer';
4-
import { checkConnection } from './src/setup/checkConnection/checkConnection';
5-
import { askForTalawaApiUrl } from './src/setup/askForTalawaApiUrl/askForTalawaApiUrl';
64
import { checkEnvFile } from './src/setup/checkEnvFile/checkEnvFile';
75
import { validateRecaptcha } from './src/setup/validateRecaptcha/validateRecaptcha';
8-
import { askForCustomPort } from './src/setup/askForCustomPort/askForCustomPort';
9-
10-
export async function main(): Promise<void> {
11-
console.log('Welcome to the Talawa Admin setup! 🚀');
12-
13-
if (!fs.existsSync('.env')) {
14-
fs.openSync('.env', 'w');
15-
const config = dotenv.parse(fs.readFileSync('.env.example'));
16-
for (const key in config) {
17-
fs.appendFileSync('.env', `${key}=${config[key]}\n`);
18-
}
19-
} else {
20-
checkEnvFile();
21-
}
22-
23-
let shouldSetCustomPort: boolean;
24-
25-
if (process.env.PORT) {
26-
console.log(
27-
`\nCustom port for development server already exists with the value:\n${process.env.PORT}`,
28-
);
29-
shouldSetCustomPort = true;
30-
} else {
31-
const { shouldSetCustomPortResponse } = await inquirer.prompt({
6+
import askAndSetDockerOption from './src/setup/askAndSetDockerOption/askAndSetDockerOption';
7+
import updateEnvFile from './src/setup/updateEnvFile/updateEnvFile';
8+
import askAndUpdatePort from './src/setup/askAndUpdatePort/askAndUpdatePort';
9+
import { askAndUpdateTalawaApiUrl } from './src/setup/askForDocker/askForDocker';
10+
11+
// Ask and set up reCAPTCHA
12+
const askAndSetRecaptcha = async (): Promise<void> => {
13+
try {
14+
const { shouldUseRecaptcha } = await inquirer.prompt({
3215
type: 'confirm',
33-
name: 'shouldSetCustomPortResponse',
34-
message: 'Would you like to set up a custom port?',
16+
name: 'shouldUseRecaptcha',
17+
message: 'Would you like to set up reCAPTCHA?',
3518
default: true,
3619
});
37-
shouldSetCustomPort = shouldSetCustomPortResponse;
38-
}
39-
40-
if (shouldSetCustomPort) {
41-
const customPort = await askForCustomPort();
42-
43-
const port = dotenv.parse(fs.readFileSync('.env')).PORT;
44-
45-
fs.readFile('.env', 'utf8', (err, data) => {
46-
const result = data.replace(`PORT=${port}`, `PORT=${customPort}`);
47-
fs.writeFileSync('.env', result, 'utf8');
48-
});
49-
}
50-
51-
let shouldSetTalawaApiUrl: boolean;
52-
53-
if (process.env.REACT_APP_TALAWA_URL) {
54-
console.log(
55-
`\nEndpoint for accessing talawa-api graphql service already exists with the value:\n${process.env.REACT_APP_TALAWA_URL}`,
56-
);
57-
shouldSetTalawaApiUrl = true;
58-
} else {
59-
const { shouldSetTalawaApiUrlResponse } = await inquirer.prompt({
60-
type: 'confirm',
61-
name: 'shouldSetTalawaApiUrlResponse',
62-
message: 'Would you like to set up talawa-api endpoint?',
63-
default: true,
64-
});
65-
shouldSetTalawaApiUrl = shouldSetTalawaApiUrlResponse;
66-
}
67-
68-
if (shouldSetTalawaApiUrl) {
69-
let isConnected = false,
70-
endpoint = '';
71-
72-
while (!isConnected) {
73-
endpoint = await askForTalawaApiUrl();
74-
const url = new URL(endpoint);
75-
isConnected = await checkConnection(url.origin);
76-
}
77-
const envPath = '.env';
78-
const currentEnvContent = fs.readFileSync(envPath, 'utf8');
79-
const talawaApiUrl = dotenv.parse(currentEnvContent).REACT_APP_TALAWA_URL;
80-
81-
const updatedEnvContent = currentEnvContent.replace(
82-
`REACT_APP_TALAWA_URL=${talawaApiUrl}`,
83-
`REACT_APP_TALAWA_URL=${endpoint}`,
84-
);
85-
86-
fs.writeFileSync(envPath, updatedEnvContent, 'utf8');
87-
const websocketUrl = endpoint.replace(/^http(s)?:\/\//, 'ws$1://');
88-
const currentWebSocketUrl =
89-
dotenv.parse(updatedEnvContent).REACT_APP_BACKEND_WEBSOCKET_URL;
90-
91-
const finalEnvContent = updatedEnvContent.replace(
92-
`REACT_APP_BACKEND_WEBSOCKET_URL=${currentWebSocketUrl}`,
93-
`REACT_APP_BACKEND_WEBSOCKET_URL=${websocketUrl}`,
94-
);
95-
96-
fs.writeFileSync(envPath, finalEnvContent, 'utf8');
97-
}
98-
99-
const { shouldUseRecaptcha } = await inquirer.prompt({
100-
type: 'confirm',
101-
name: 'shouldUseRecaptcha',
102-
message: 'Would you like to set up ReCAPTCHA?',
103-
default: true,
104-
});
105-
106-
if (shouldUseRecaptcha) {
107-
const useRecaptcha = dotenv.parse(
108-
fs.readFileSync('.env'),
109-
).REACT_APP_USE_RECAPTCHA;
110-
111-
fs.readFile('.env', 'utf8', (err, data) => {
112-
const result = data.replace(
113-
`REACT_APP_USE_RECAPTCHA=${useRecaptcha}`,
114-
`REACT_APP_USE_RECAPTCHA=yes`,
115-
);
116-
fs.writeFileSync('.env', result, 'utf8');
117-
});
118-
let shouldSetRecaptchaSiteKey: boolean;
119-
if (process.env.REACT_APP_RECAPTCHA_SITE_KEY) {
120-
console.log(
121-
`\nreCAPTCHA site key already exists with the value ${process.env.REACT_APP_RECAPTCHA_SITE_KEY}`,
122-
);
123-
shouldSetRecaptchaSiteKey = true;
124-
} else {
125-
const { shouldSetRecaptchaSiteKeyResponse } = await inquirer.prompt({
126-
type: 'confirm',
127-
name: 'shouldSetRecaptchaSiteKeyResponse',
128-
message: 'Would you like to set up a reCAPTCHA site key?',
129-
default: true,
130-
});
131-
shouldSetRecaptchaSiteKey = shouldSetRecaptchaSiteKeyResponse;
132-
}
13320

134-
if (shouldSetRecaptchaSiteKey) {
21+
if (shouldUseRecaptcha) {
13522
const { recaptchaSiteKeyInput } = await inquirer.prompt([
13623
{
13724
type: 'input',
13825
name: 'recaptchaSiteKeyInput',
13926
message: 'Enter your reCAPTCHA site key:',
140-
validate: async (input: string): Promise<boolean | string> => {
141-
if (validateRecaptcha(input)) {
142-
return true;
143-
}
144-
return 'Invalid reCAPTCHA site key. Please try again.';
27+
validate: (input: string): boolean | string => {
28+
return (
29+
validateRecaptcha(input) ||
30+
'Invalid reCAPTCHA site key. Please try again.'
31+
);
14532
},
14633
},
14734
]);
14835

149-
const recaptchaSiteKey = dotenv.parse(
150-
fs.readFileSync('.env'),
151-
).REACT_APP_RECAPTCHA_SITE_KEY;
152-
153-
fs.readFile('.env', 'utf8', (err, data) => {
154-
const result = data.replace(
155-
`REACT_APP_RECAPTCHA_SITE_KEY=${recaptchaSiteKey}`,
156-
`REACT_APP_RECAPTCHA_SITE_KEY=${recaptchaSiteKeyInput}`,
157-
);
158-
fs.writeFileSync('.env', result, 'utf8');
159-
});
36+
updateEnvFile('REACT_APP_RECAPTCHA_SITE_KEY', recaptchaSiteKeyInput);
16037
}
38+
} catch (error) {
39+
console.error('Error setting up reCAPTCHA:', error);
40+
throw new Error(`Failed to set up reCAPTCHA: ${(error as Error).message}`);
16141
}
42+
};
16243

44+
// Ask and set up logging errors in the console
45+
const askAndSetLogErrors = async (): Promise<void> => {
16346
const { shouldLogErrors } = await inquirer.prompt({
16447
type: 'confirm',
16548
name: 'shouldLogErrors',
@@ -169,17 +52,37 @@ export async function main(): Promise<void> {
16952
});
17053

17154
if (shouldLogErrors) {
172-
const logErrors = dotenv.parse(fs.readFileSync('.env')).ALLOW_LOGS;
173-
174-
fs.readFile('.env', 'utf8', (err, data) => {
175-
const result = data.replace(`ALLOW_LOGS=${logErrors}`, 'ALLOW_LOGS=YES');
176-
fs.writeFileSync('.env', result, 'utf8');
177-
});
55+
updateEnvFile('ALLOW_LOGS', 'YES');
17856
}
57+
};
17958

180-
console.log(
181-
'\nCongratulations! Talawa Admin has been successfully setup! 🥂🎉',
182-
);
59+
// Main function to run the setup process
60+
export async function main(): Promise<void> {
61+
try {
62+
console.log('Welcome to the Talawa Admin setup! 🚀');
63+
64+
checkEnvFile();
65+
await askAndSetDockerOption();
66+
const envConfig = dotenv.parse(fs.readFileSync('.env', 'utf8'));
67+
const useDocker = envConfig.USE_DOCKER === 'YES';
68+
69+
// Only run these commands if Docker is NOT used
70+
if (!useDocker) {
71+
await askAndUpdatePort();
72+
await askAndUpdateTalawaApiUrl();
73+
}
74+
75+
await askAndSetRecaptcha();
76+
await askAndSetLogErrors();
77+
78+
console.log(
79+
'\nCongratulations! Talawa Admin has been successfully set up! 🥂🎉',
80+
);
81+
} catch (error) {
82+
console.error('\n❌ Setup failed:', error);
83+
console.log('\nPlease try again or contact support if the issue persists.');
84+
process.exit(1);
85+
}
18386
}
18487

18588
main();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
3+
// Mock modules
4+
vi.mock('inquirer', () => ({
5+
default: {
6+
prompt: vi.fn(),
7+
},
8+
}));
9+
10+
vi.mock('setup/updateEnvFile/updateEnvFile', () => ({
11+
default: vi.fn(),
12+
}));
13+
14+
vi.mock('setup/askForDocker/askForDocker', () => ({
15+
askForDocker: vi.fn(),
16+
}));
17+
18+
// Import after mocking
19+
import askAndSetDockerOption from './askAndSetDockerOption';
20+
import inquirer from 'inquirer';
21+
import updateEnvFile from 'setup/updateEnvFile/updateEnvFile';
22+
import { askForDocker } from 'setup/askForDocker/askForDocker';
23+
24+
describe('askAndSetDockerOption', () => {
25+
beforeEach(() => {
26+
vi.clearAllMocks();
27+
});
28+
29+
it('should set up Docker when user selects yes', async () => {
30+
(inquirer.prompt as unknown as jest.Mock).mockResolvedValueOnce({
31+
useDocker: true,
32+
});
33+
(askForDocker as jest.Mock).mockResolvedValueOnce(8080);
34+
35+
await askAndSetDockerOption();
36+
37+
expect(updateEnvFile).toHaveBeenCalledWith('USE_DOCKER', 'YES');
38+
expect(updateEnvFile).toHaveBeenCalledWith('DOCKER_PORT', 8080);
39+
});
40+
41+
it('should set up without Docker when user selects no', async () => {
42+
(inquirer.prompt as unknown as jest.Mock).mockResolvedValueOnce({
43+
useDocker: false,
44+
});
45+
46+
await askAndSetDockerOption();
47+
48+
expect(updateEnvFile).toHaveBeenCalledWith('USE_DOCKER', 'NO');
49+
});
50+
51+
it('should handle errors when askForDocker fails', async () => {
52+
(inquirer.prompt as unknown as jest.Mock).mockResolvedValueOnce({
53+
useDocker: true,
54+
});
55+
(askForDocker as jest.Mock).mockRejectedValueOnce(
56+
new Error('Docker error'),
57+
);
58+
59+
await expect(askAndSetDockerOption()).rejects.toThrow('Docker error');
60+
});
61+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import inquirer from 'inquirer';
2+
import updateEnvFile from 'setup/updateEnvFile/updateEnvFile';
3+
import { askForDocker } from 'setup/askForDocker/askForDocker';
4+
5+
// Function to manage Docker setup
6+
const askAndSetDockerOption = async (): Promise<void> => {
7+
const { useDocker } = await inquirer.prompt({
8+
type: 'confirm',
9+
name: 'useDocker',
10+
message: 'Would you like to set up with Docker?',
11+
default: false,
12+
});
13+
14+
if (useDocker) {
15+
console.log('Setting up with Docker...');
16+
updateEnvFile('USE_DOCKER', 'YES');
17+
const answers = await askForDocker();
18+
const DOCKER_PORT_NUMBER = answers;
19+
updateEnvFile('DOCKER_PORT', DOCKER_PORT_NUMBER);
20+
21+
const DOCKER_NAME = 'talawa-admin';
22+
console.log(`
23+
24+
Run the commands below after setup:-
25+
1. docker build -t ${DOCKER_NAME} .
26+
2. docker run -d -p ${DOCKER_PORT_NUMBER}:${DOCKER_PORT_NUMBER} ${DOCKER_NAME}
27+
28+
`);
29+
} else {
30+
console.log('Setting up without Docker...');
31+
updateEnvFile('USE_DOCKER', 'NO');
32+
}
33+
};
34+
35+
export default askAndSetDockerOption;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import updateEnvFile from 'setup/updateEnvFile/updateEnvFile';
2+
import { askForCustomPort } from 'setup/askForCustomPort/askForCustomPort';
3+
import inquirer from 'inquirer';
4+
5+
// Ask and update the custom port
6+
const askAndUpdatePort = async (): Promise<void> => {
7+
const { shouldSetCustomPortResponse } = await inquirer.prompt({
8+
type: 'confirm',
9+
name: 'shouldSetCustomPortResponse',
10+
message:
11+
'Would you like to set up a custom port for running Talawa Admin without Docker?',
12+
default: true,
13+
});
14+
15+
if (shouldSetCustomPortResponse) {
16+
const customPort = await askForCustomPort();
17+
if (customPort < 1024 || customPort > 65535) {
18+
throw new Error('Port must be between 1024 and 65535');
19+
}
20+
21+
updateEnvFile('PORT', String(customPort));
22+
}
23+
};
24+
25+
export default askAndUpdatePort;

0 commit comments

Comments
 (0)