From 770fa91366ef33d09278c2b8add16d9860387d9c Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Fri, 19 Dec 2025 13:06:26 +0900 Subject: [PATCH] feat: Add interactive setup script Added an interactive Node.js script (setup.js) to guide users through the initial environment setup. This script prompts for necessary database credentials and generates JWT secrets, then creates/updates the .env file. A 'setup' script has been added to api.hyungi.net/package.json for easy execution. This improves the first-time setup experience by streamlining the .env file creation process. --- api.hyungi.net/package.json | 1 + setup.js | 82 +++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 setup.js diff --git a/api.hyungi.net/package.json b/api.hyungi.net/package.json index b706011..3be2186 100644 --- a/api.hyungi.net/package.json +++ b/api.hyungi.net/package.json @@ -11,6 +11,7 @@ "test:unit": "jest tests/unit", "test:integration": "jest tests/integration", "test:verbose": "jest --verbose", + "setup": "node ../setup.js", "db:migrate": "knex migrate:latest --knexfile knexfile.js", "db:migrate:make": "knex migrate:make --knexfile knexfile.js", "db:rollback": "knex migrate:rollback --knexfile knexfile.js", diff --git a/setup.js b/setup.js new file mode 100644 index 0000000..cc5ed84 --- /dev/null +++ b/setup.js @@ -0,0 +1,82 @@ +// setup.js +const fs = require('fs'); +const path = require('path'); +const readline = require('readline'); +const crypto = require('crypto'); + +const envExamplePath = path.join(__dirname, '.env.example'); +const envPath = path.join(__dirname, '.env'); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +function question(query) { + return new Promise(resolve => { + rl.question(query, resolve); + }); +} + +function generateSecret() { + return crypto.randomBytes(32).toString('hex'); +} + +async function main() { + console.log('๐Ÿš€ TK-FB-Project ํ™˜๊ฒฝ ์„ค์ • ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.'); + console.log('--------------------------------------------------'); + + if (fs.existsSync(envPath)) { + const answer = await question('โš ๏ธ `.env` ํŒŒ์ผ์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ๋ฎ์–ด์“ฐ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? (y/N): '); + if (answer.toLowerCase() !== 'y') { + console.log('๐Ÿšซ ์„ค์ •์„ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด .env ํŒŒ์ผ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.'); + rl.close(); + return; + } + console.log('๊ธฐ์กด `.env` ํŒŒ์ผ์„ ๋ฎ์–ด์”๋‹ˆ๋‹ค.'); + } + + let envExampleContent; + try { + envExampleContent = fs.readFileSync(envExamplePath, 'utf8'); + } catch (error) { + console.error(`โŒ '.env.example' ํŒŒ์ผ์„ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์„ค์ • ํ…œํ”Œ๋ฆฟ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.`); + rl.close(); + return; + } + + console.log('\n๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์•ˆ์ „ํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.'); + + const rootPassword = await question('๐Ÿ”‘ MariaDB/MySQL ROOT ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: '); + const userPassword = await question('๐Ÿ”‘ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์‚ฌ์šฉ์ž ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: '); + + if (!rootPassword || !userPassword) { + console.error('โŒ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ๋น„์›Œ๋‘˜ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”.'); + rl.close(); + return; + } + + console.log('\n๐Ÿ” JWT ์‹œํฌ๋ฆฟ ํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค...'); + const jwtSecret = generateSecret(); + const jwtRefreshSecret = generateSecret(); + console.log('โœ… JWT ์‹œํฌ๋ฆฟ ํ‚ค ์ƒ์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + + let newEnvContent = envExampleContent; + newEnvContent = newEnvContent.replace('change_this_root_password_min_12_chars', rootPassword); + newEnvContent = newEnvContent.replace(/change_this_user_password_min_12_chars/g, userPassword); + newEnvContent = newEnvContent.replace('change_this_to_random_string_min_32_chars', jwtSecret); + newEnvContent = newEnvContent.replace('change_this_to_another_random_string_min_32_chars', jwtRefreshSecret); + + try { + fs.writeFileSync(envPath, newEnvContent, 'utf8'); + console.log('\nโœ… `.env` ํŒŒ์ผ ์ƒ์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!'); + console.log('์ด์ œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:'); + console.log('\x1b[36m%s\x1b[0m', '\ndocker-compose up -d --build\n'); + } catch (error) { + console.error('โŒ `.env` ํŒŒ์ผ ์ €์žฅ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค:', error); + } + + rl.close(); +} + +main();