Add installer flag to regenerate admin credentials
Test and Lint / backend-test (push) Successful in 1m42s
Test and Lint / frontend-test (push) Successful in 1m54s

This commit is contained in:
2025-09-24 17:33:30 +02:00
parent a4595e2ab2
commit b5399aaa9b
4 changed files with 91 additions and 25 deletions
+11 -1
View File
@@ -420,7 +420,17 @@ Upon first login, the system will **automatically redirect** you to change your
If you lose your admin credentials after the first login, you'll need to manually reset the password in the database or create a new admin user through the database. If you lose your admin credentials after the first login, you'll need to manually reset the password in the database or create a new admin user through the database.
**Note**: The credentials file (`ADMIN_CREDENTIALS.txt`) is only created during initial deployment and contains the first admin password. After changing the password, this file becomes outdated but is kept for reference. **Note**: The credentials file (`ADMIN_CREDENTIALS.txt`) is only created during initial deployment and contains the first admin password. After changing the password, this file becomes outdated but is kept for reference. If you need to regenerate the password and file during a reinstall, re-run the installer with the `--force-admin-password-reset` flag:
```bash
# Native reinstall example
sudo ./setup.sh --native --force-admin-password-reset
# Docker reinstall example
sudo ./setup.sh --docker --force-admin-password-reset
```
The flag calls `scripts/reset-admin-password.js` in non-interactive mode, writes a fresh random password into `data/ADMIN_CREDENTIALS.txt`, and prints the new credentials at the end of the installer run.
#### Configuring Admin Email #### Configuring Admin Email
+54 -22
View File
@@ -7,12 +7,32 @@ const fs = require('fs').promises;
const path = require('path'); const path = require('path');
const readline = require('readline'); const readline = require('readline');
const rl = readline.createInterface({ const args = process.argv.slice(2);
const hasFlag = (flag) => args.includes(flag);
const getOption = (name) => {
const index = args.indexOf(`--${name}`);
if (index !== -1 && index + 1 < args.length) {
return args[index + 1];
}
return null;
};
const force = hasFlag('--force') || hasFlag('--yes') || hasFlag('--non-interactive');
const credentialsFileArg = getOption('credentials-file');
const resolvedCredentialsFile = credentialsFileArg
? path.resolve(process.cwd(), credentialsFileArg)
: path.join(__dirname, '..', '..', 'ADMIN_PASSWORD_RESET.txt');
const rl = force ? null : readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout
}); });
async function question(prompt) { async function ask(prompt) {
if (force) {
return 'yes';
}
return new Promise((resolve) => { return new Promise((resolve) => {
rl.question(prompt, resolve); rl.question(prompt, resolve);
}); });
@@ -37,13 +57,18 @@ async function resetAdminPassword() {
console.log('Found admin user:', admin.username); console.log('Found admin user:', admin.username);
console.log('Email:', admin.email); console.log('Email:', admin.email);
console.log('\nThis will reset the password for this admin account.'); if (!force) {
console.log('\nThis will reset the password for this admin account.');
const confirm = await question('\nDo you want to continue? (yes/no): '); }
if (confirm.toLowerCase() !== 'yes' && confirm.toLowerCase() !== 'y') { const confirm = await ask('\nDo you want to continue? (yes/no): ');
console.log('\n❌ Password reset cancelled.');
process.exit(0); if (!force) {
const normalized = confirm.trim().toLowerCase();
if (normalized !== 'yes' && normalized !== 'y') {
console.log('\n❌ Password reset cancelled.');
process.exit(0);
}
} }
// Generate new password // Generate new password
@@ -60,39 +85,44 @@ async function resetAdminPassword() {
}); });
// Save to file // Save to file
const resetInfoPath = path.join(__dirname, '..', '..', 'ADMIN_PASSWORD_RESET.txt'); const credentialsDir = path.dirname(resolvedCredentialsFile);
await fs.mkdir(credentialsDir, { recursive: true });
const adminUrl = `${process.env.ADMIN_URL || 'http://localhost:3001'}/admin`;
const resetInfo = ` const resetInfo = `
======================================== ========================================
PicPeak Admin Password Reset PicPeak Admin Credentials
======================================== ========================================
Password has been reset for admin account: Your admin account has been reset with these credentials:
Username: admin Username: ${admin.username}
New Password: ${newPassword} Email: ${admin.email}
Password: ${newPassword}
IMPORTANT: IMPORTANT SECURITY NOTES:
1. You MUST change this password on next login 1. You MUST change this password after first login
2. This file contains sensitive information 2. This file contains sensitive information
3. Delete this file after noting the password 3. Delete this file after noting the password
Login URL: ${process.env.ADMIN_URL || 'http://localhost:3001'}/admin Login URL: ${adminUrl}
Reset performed on: ${new Date().toISOString()} Reset performed on: ${new Date().toISOString()}
======================================== ========================================
`; `;
await fs.writeFile(resetInfoPath, resetInfo, 'utf8'); await fs.writeFile(resolvedCredentialsFile, resetInfo, 'utf8');
console.log('\n✅ Password reset successful!\n'); console.log('\n✅ Password reset successful!\n');
console.log('========================================'); console.log('========================================');
console.log('New Credentials:'); console.log('New Credentials:');
console.log('========================================'); console.log('========================================');
console.log('Username: admin'); console.log(`Username: ${admin.username}`);
console.log(`Email: ${admin.email}`);
console.log(`Password: ${newPassword}`); console.log(`Password: ${newPassword}`);
console.log('\n⚠️ IMPORTANT:'); console.log('\n⚠️ IMPORTANT:');
console.log('1. You will be required to change this password on next login'); console.log('1. You will be required to change this password on next login');
console.log('2. Credentials are also saved in: ADMIN_PASSWORD_RESET.txt'); console.log(`2. Credentials are also saved in: ${resolvedCredentialsFile}`);
console.log('3. Delete the file after noting the password'); console.log('3. Delete the file after noting the password');
console.log('========================================\n'); console.log('========================================\n');
@@ -100,10 +130,12 @@ Reset performed on: ${new Date().toISOString()}
console.error('❌ Error resetting password:', error.message); console.error('❌ Error resetting password:', error.message);
process.exit(1); process.exit(1);
} finally { } finally {
rl.close(); if (rl) {
rl.close();
}
process.exit(0); process.exit(0);
} }
} }
// Run the reset // Run the reset
resetAdminPassword(); resetAdminPassword();
+3 -1
View File
@@ -108,6 +108,8 @@ If ADMIN_CREDENTIALS.txt is missing:
- Check the console output from when you ran migrations - Check the console output from when you ran migrations
- File is created in the backend directory root - File is created in the backend directory root
- File might have been deleted for security (as recommended) - File might have been deleted for security (as recommended)
- Regenerate it by running `node scripts/reset-admin-password.js --force --credentials-file data/ADMIN_CREDENTIALS.txt`
- When using the unified `setup.sh` installer for a reinstall, append `--force-admin-password-reset` to have the script perform the reset automatically
## Best Practices ## Best Practices
@@ -161,4 +163,4 @@ If upgrading from the old system with hardcoded `admin123`:
- [ ] Stored new password in password manager - [ ] Stored new password in password manager
- [ ] Tested login with new password - [ ] Tested login with new password
- [ ] Set up additional admin accounts if needed - [ ] Set up additional admin accounts if needed
- [ ] Configured password policies for organization - [ ] Configured password policies for organization
+23 -1
View File
@@ -55,6 +55,7 @@ CUSTOM_PORT=""
UNATTENDED=false UNATTENDED=false
UPDATE_MODE=false UPDATE_MODE=false
UNINSTALL_MODE=false UNINSTALL_MODE=false
FORCE_ADMIN_PASSWORD_RESET=false
################################################################################ ################################################################################
# Helper Functions # Helper Functions
@@ -487,6 +488,15 @@ EOF
# Run database migrations # Run database migrations
log_step "Running database migrations..." log_step "Running database migrations..."
docker compose exec -T backend npm run migrate docker compose exec -T backend npm run migrate
if [[ "$FORCE_ADMIN_PASSWORD_RESET" == "true" ]]; then
log_step "Resetting admin credentials..."
if docker compose exec -T backend node scripts/reset-admin-password.js --force --credentials-file data/ADMIN_CREDENTIALS.txt; then
docker compose cp backend:/app/data/ADMIN_CREDENTIALS.txt "$app_dir/data/ADMIN_CREDENTIALS.txt" 2>/dev/null || true
else
log_warn "Automatic admin password reset failed; run reset-admin-password.js inside the backend container."
fi
fi
log_success "Docker installation completed!" log_success "Docker installation completed!"
} }
@@ -737,6 +747,13 @@ EOF
log_step "Initializing database..." log_step "Initializing database..."
cd "$NATIVE_APP_DIR/app/backend" cd "$NATIVE_APP_DIR/app/backend"
run_as_user "npm run migrate" run_as_user "npm run migrate"
if [[ "$FORCE_ADMIN_PASSWORD_RESET" == "true" ]]; then
log_step "Resetting admin credentials..."
if ! run_as_user "node scripts/reset-admin-password.js --force --credentials-file data/ADMIN_CREDENTIALS.txt"; then
log_warn "Automatic admin password reset failed; please run reset-admin-password.js manually."
fi
fi
# Create systemd services # Create systemd services
create_systemd_services create_systemd_services
@@ -989,7 +1006,7 @@ print_success_message() {
fi fi
else else
echo -e "Email: ${CYAN}$ADMIN_EMAIL${NC}" echo -e "Email: ${CYAN}$ADMIN_EMAIL${NC}"
echo -e "Password: ${YELLOW}(credentials file not found)${NC}" echo -e "Password: ${YELLOW}(credentials file not found - rerun setup with --force-admin-password-reset or run node scripts/reset-admin-password.js manually)${NC}"
fi fi
echo echo
echo -e "${YELLOW}⚠️ IMPORTANT: Change the admin password on first login!${NC}" echo -e "${YELLOW}⚠️ IMPORTANT: Change the admin password on first login!${NC}"
@@ -1256,6 +1273,10 @@ parse_arguments() {
SMTP_PASS="$2" SMTP_PASS="$2"
shift 2 shift 2
;; ;;
--force-admin-password-reset)
FORCE_ADMIN_PASSWORD_RESET=true
shift
;;
--enable-ssl) --enable-ssl)
ENABLE_SSL=true ENABLE_SSL=true
shift shift
@@ -1301,6 +1322,7 @@ Options:
--smtp-port PORT SMTP server port --smtp-port PORT SMTP server port
--smtp-user USER SMTP username --smtp-user USER SMTP username
--smtp-pass PASS SMTP password --smtp-pass PASS SMTP password
--force-admin-password-reset Regenerate admin credentials after setup
--enable-ssl Enable HTTPS with Let's Encrypt --enable-ssl Enable HTTPS with Let's Encrypt
--port PORT Custom port (native only) --port PORT Custom port (native only)
--update Update existing installation --update Update existing installation