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();