Fix initial user creating
parent
3565390a49
commit
66c84ecdeb
@ -1,29 +1,25 @@
|
|||||||
const bcrypt = require('bcrypt');
|
|
||||||
const pool = require('../src/config/database');
|
const pool = require('../src/config/database');
|
||||||
|
const { ensureBootstrapAdmin, readBootstrapAdminConfig } = require('../src/utils/bootstrapAdmin');
|
||||||
|
|
||||||
async function createAdmin() {
|
async function createAdmin() {
|
||||||
const username = 'admin';
|
const config = readBootstrapAdminConfig();
|
||||||
const password = 'admin123'; // Измените после первого входа!
|
|
||||||
const passwordHash = await bcrypt.hash(password, 10);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await pool.query(`
|
const result = await ensureBootstrapAdmin({
|
||||||
INSERT INTO users (username, password_hash, full_name, email, role, is_active)
|
resetPassword: true,
|
||||||
VALUES ($1, $2, $3, $4, $5, $6)
|
createIfActiveAdminExists: true,
|
||||||
ON CONFLICT (username) DO UPDATE
|
});
|
||||||
SET password_hash = $2
|
|
||||||
RETURNING id, username, role
|
|
||||||
`, [username, passwordHash, 'Главный администратор', 'admin@polotsk-transit.local', 'admin', true]);
|
|
||||||
|
|
||||||
console.log('✅ Мастер-аккаунт создан:', result.rows[0]);
|
console.log('Master account is ready:', result.user);
|
||||||
console.log('Username: admin');
|
console.log(`Username: ${config.username}`);
|
||||||
console.log('Password: admin123');
|
console.log(`Password: ${config.password}`);
|
||||||
console.log('⚠️ ОБЯЗАТЕЛЬНО смените пароль после первого входа!');
|
console.log('Change this password after the first login.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Ошибка:', error);
|
console.error('Error:', error);
|
||||||
|
process.exitCode = 1;
|
||||||
} finally {
|
} finally {
|
||||||
await pool.end();
|
await pool.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createAdmin();
|
createAdmin();
|
||||||
|
|||||||
@ -0,0 +1,122 @@
|
|||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
|
||||||
|
const pool = require('../config/database');
|
||||||
|
const logger = require('../config/logger');
|
||||||
|
|
||||||
|
const DEFAULT_ADMIN = {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'admin123',
|
||||||
|
fullName: 'Main administrator',
|
||||||
|
email: 'admin@polotsk-transit.local',
|
||||||
|
};
|
||||||
|
|
||||||
|
function readBoolean(value, defaultValue) {
|
||||||
|
if (value === undefined || value === null || value === '') {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['1', 'true', 'yes', 'on'].includes(String(value).toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBootstrapAdminConfig(env = process.env) {
|
||||||
|
return {
|
||||||
|
enabled: readBoolean(env.BOOTSTRAP_ADMIN, true),
|
||||||
|
username: env.ADMIN_USERNAME || DEFAULT_ADMIN.username,
|
||||||
|
password: env.ADMIN_PASSWORD || DEFAULT_ADMIN.password,
|
||||||
|
fullName: env.ADMIN_FULL_NAME || DEFAULT_ADMIN.fullName,
|
||||||
|
email: env.ADMIN_EMAIL || DEFAULT_ADMIN.email,
|
||||||
|
resetPassword: readBoolean(env.BOOTSTRAP_ADMIN_RESET_PASSWORD, false),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function activeAdminExists() {
|
||||||
|
const result = await pool.query(`
|
||||||
|
SELECT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM users
|
||||||
|
WHERE role = 'admin' AND is_active = true
|
||||||
|
) AS exists
|
||||||
|
`);
|
||||||
|
|
||||||
|
return result.rows[0]?.exists === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateConfig(config) {
|
||||||
|
if (!config.username) {
|
||||||
|
throw new Error('ADMIN_USERNAME cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.password || config.password.length < 6) {
|
||||||
|
throw new Error('ADMIN_PASSWORD must be at least 6 characters');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureBootstrapAdmin(options = {}) {
|
||||||
|
const config = {
|
||||||
|
...readBootstrapAdminConfig(),
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!config.enabled) {
|
||||||
|
logger.info('Bootstrap admin is disabled');
|
||||||
|
return { changed: false, reason: 'disabled' };
|
||||||
|
}
|
||||||
|
|
||||||
|
validateConfig(config);
|
||||||
|
|
||||||
|
const existingResult = await pool.query(
|
||||||
|
'SELECT id, username, role, is_active FROM users WHERE username = $1',
|
||||||
|
[config.username]
|
||||||
|
);
|
||||||
|
const existingUser = existingResult.rows[0];
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
const needsRepair = existingUser.role !== 'admin' || existingUser.is_active !== true;
|
||||||
|
const shouldResetPassword = config.resetPassword || needsRepair;
|
||||||
|
|
||||||
|
if (!needsRepair && !shouldResetPassword) {
|
||||||
|
logger.info(`Bootstrap admin already exists: ${config.username}`);
|
||||||
|
return { changed: false, user: existingUser };
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordHash = shouldResetPassword
|
||||||
|
? await bcrypt.hash(config.password, 10)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const updateResult = await pool.query(`
|
||||||
|
UPDATE users
|
||||||
|
SET role = 'admin',
|
||||||
|
is_active = true,
|
||||||
|
full_name = COALESCE($2, full_name),
|
||||||
|
email = COALESCE($3, email),
|
||||||
|
password_hash = COALESCE($4, password_hash)
|
||||||
|
WHERE username = $1
|
||||||
|
RETURNING id, username, role, is_active
|
||||||
|
`, [config.username, config.fullName || null, config.email || null, passwordHash]);
|
||||||
|
|
||||||
|
logger.info(`Bootstrap admin repaired: ${config.username}`);
|
||||||
|
return { changed: true, user: updateResult.rows[0] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldCreateWithExistingAdmin = options.createIfActiveAdminExists === true;
|
||||||
|
if (!shouldCreateWithExistingAdmin && await activeAdminExists()) {
|
||||||
|
logger.info('Active admin already exists; default bootstrap admin was not created');
|
||||||
|
return { changed: false, reason: 'active-admin-exists' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const passwordHash = await bcrypt.hash(config.password, 10);
|
||||||
|
const insertResult = await pool.query(`
|
||||||
|
INSERT INTO users (username, password_hash, full_name, email, role, is_active)
|
||||||
|
VALUES ($1, $2, $3, $4, 'admin', true)
|
||||||
|
RETURNING id, username, role, is_active
|
||||||
|
`, [config.username, passwordHash, config.fullName || null, config.email || null]);
|
||||||
|
|
||||||
|
logger.info(`Bootstrap admin created: ${config.username}`);
|
||||||
|
return { changed: true, user: insertResult.rows[0] };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
DEFAULT_ADMIN,
|
||||||
|
ensureBootstrapAdmin,
|
||||||
|
readBootstrapAdminConfig,
|
||||||
|
};
|
||||||
Binary file not shown.
@ -1,2 +1,2 @@
|
|||||||
VITE_API_URL=http://localhost:3000/api/v1
|
VITE_API_URL=/api/v1
|
||||||
VITE_API_KEY=dev_key_12345678
|
VITE_API_KEY=dev_key_12345678
|
||||||
|
|||||||
Loading…
Reference in New Issue