diff --git a/package.json b/package.json index 1f26ba1e..3e7d6f64 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "maxmind": "^4.3.0", "moment-timezone": "^0.5.31", "next": "^9.5.5", + "prompts": "2.3.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-intl": "^5.8.4", diff --git a/scripts/change-password.js b/scripts/change-password.js new file mode 100644 index 00000000..ff294bc3 --- /dev/null +++ b/scripts/change-password.js @@ -0,0 +1,98 @@ +require('dotenv').config(); +const bcrypt = require('bcrypt'); +const chalk = require('chalk'); +const prompts = require('prompts'); +const { PrismaClient } = require('@prisma/client'); + +const prisma = new PrismaClient(); +const SALT_ROUNDS = 10; + +const runQuery = async query => { + return query.catch(e => { + throw e; + }); +}; + +const updateAccountByUsername = (username, data) => { + return runQuery( + prisma.account.update({ + where: { + username, + }, + data, + }), + ); +}; + +const hashPassword = password => { + return bcrypt.hash(password, SALT_ROUNDS); +}; + +const changePassword = async (username, newPassword) => { + const password = await hashPassword(newPassword); + return updateAccountByUsername(username, { password }); +}; + +const getUsernameAndPassword = async () => { + let [username, password] = process.argv.slice(2); + if (username && password) { + return { username, password }; + } + + const questions = []; + if (!username) { + questions.push({ + type: 'text', + name: 'username', + message: 'Enter account to change password', + }); + } + if (!password) { + questions.push( + { + type: 'password', + name: 'password', + message: 'Enter new password', + }, + { + type: 'password', + name: 'confirmation', + message: 'Confirm new password', + }, + ); + } + + const answers = await prompts(questions); + if (answers.password !== answers.confirmation) { + throw new Error(`Passwords don't match`); + } + + return { + username: username || answers.username, + password: answers.password, + }; +}; + +(async () => { + let username, password; + + try { + ({ username, password } = await getUsernameAndPassword()); + } catch (error) { + console.log(chalk.redBright(error.message)); + return; + } + + try { + await changePassword(username, password); + console.log('Password changed for user', chalk.greenBright(username)); + } catch (error) { + if (error.message.includes('RecordNotFound')) { + console.log('Account not found:', chalk.redBright(username)); + } else { + throw error; + } + } + + prisma.$disconnect(); +})(); diff --git a/yarn.lock b/yarn.lock index 70310b2e..9e3a815b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5281,6 +5281,11 @@ kind-of@^6.0.0, kind-of@^6.0.2, kind-of@^6.0.3: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + klona@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" @@ -7187,6 +7192,14 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +prompts@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" + integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.4" + prop-types@15.7.2, prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -8028,6 +8041,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +sisteransi@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"