Refactor: code quality improvements
- Extract admin.service.js from admin.controller.js (proper layering) - Remove console.log statements from todo.controller.js - Fix inconsistent error handling in auth.controller.js (return next) - Remove logger.debug calls from contact.controller.js - Add transaction management to contact.service.js addContact() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,5 @@
|
||||
import { db } from '../config/database.js';
|
||||
import { users, userEmailAccounts, emailAccounts } from '../db/schema.js';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import { hashPassword, generateTempPassword } from '../utils/password.js';
|
||||
import * as adminService from '../services/admin.service.js';
|
||||
import { logUserCreation, logRoleChange } from '../services/audit.service.js';
|
||||
import { ConflictError, NotFoundError } from '../utils/errors.js';
|
||||
import * as emailAccountService from '../services/email-account.service.js';
|
||||
|
||||
/**
|
||||
* Vytvorenie nového usera s automatic temporary password (admin only)
|
||||
@@ -18,83 +13,41 @@ export const createUser = async (req, res, next) => {
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
// Skontroluj či username už neexistuje
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser) {
|
||||
throw new ConflictError('Username už existuje');
|
||||
}
|
||||
|
||||
// Automaticky vygeneruj temporary password
|
||||
const tempPassword = generateTempPassword(12);
|
||||
const hashedTempPassword = await hashPassword(tempPassword);
|
||||
|
||||
// Validuj role - iba 'admin' alebo 'member'
|
||||
const validRole = role === 'admin' ? 'admin' : 'member';
|
||||
|
||||
// Vytvor usera
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username,
|
||||
tempPassword: hashedTempPassword,
|
||||
role: validRole,
|
||||
firstName: firstName || null,
|
||||
lastName: lastName || null,
|
||||
changedPassword: false,
|
||||
})
|
||||
.returning();
|
||||
|
||||
// Ak sú poskytnuté email credentials, vytvor email account (many-to-many)
|
||||
let emailAccountCreated = false;
|
||||
let emailAccountData = null;
|
||||
|
||||
if (email && emailPassword) {
|
||||
try {
|
||||
// Použij emailAccountService ktorý automaticky vytvorí many-to-many link
|
||||
const newEmailAccount = await emailAccountService.createEmailAccount(
|
||||
newUser.id,
|
||||
email,
|
||||
emailPassword
|
||||
);
|
||||
|
||||
emailAccountCreated = true;
|
||||
emailAccountData = {
|
||||
id: newEmailAccount.id,
|
||||
email: newEmailAccount.email,
|
||||
jmapAccountId: newEmailAccount.jmapAccountId,
|
||||
shared: newEmailAccount.shared,
|
||||
};
|
||||
} catch (emailError) {
|
||||
// Email account sa nepodarilo vytvoriť, ale user bol vytvorený
|
||||
// Admin môže pridať email account neskôr
|
||||
console.error('Failed to create email account:', emailError);
|
||||
}
|
||||
}
|
||||
const result = await adminService.createUser(
|
||||
username,
|
||||
firstName,
|
||||
lastName,
|
||||
role,
|
||||
email,
|
||||
emailPassword
|
||||
);
|
||||
|
||||
// Log user creation
|
||||
await logUserCreation(adminId, newUser.id, username, validRole, ipAddress, userAgent);
|
||||
await logUserCreation(
|
||||
adminId,
|
||||
result.user.id,
|
||||
username,
|
||||
result.user.role,
|
||||
ipAddress,
|
||||
userAgent
|
||||
);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
id: newUser.id,
|
||||
username: newUser.username,
|
||||
firstName: newUser.firstName,
|
||||
lastName: newUser.lastName,
|
||||
role: newUser.role,
|
||||
emailSetup: emailAccountCreated,
|
||||
emailAccount: emailAccountData,
|
||||
tempPassword: tempPassword, // Vráti plain text password pre admina aby ho mohol poslať userovi
|
||||
id: result.user.id,
|
||||
username: result.user.username,
|
||||
firstName: result.user.firstName,
|
||||
lastName: result.user.lastName,
|
||||
role: result.user.role,
|
||||
emailSetup: result.emailAccountCreated,
|
||||
emailAccount: result.emailAccountData,
|
||||
tempPassword: result.tempPassword,
|
||||
},
|
||||
},
|
||||
message: emailAccountCreated
|
||||
? emailAccountData.shared
|
||||
message: result.emailAccountCreated
|
||||
? result.emailAccountData.shared
|
||||
? 'Používateľ vytvorený a pripojený k existujúcemu zdieľanému email účtu.'
|
||||
: 'Používateľ úspešne vytvorený s novým emailovým účtom.'
|
||||
: 'Používateľ úspešne vytvorený. Email môže byť nastavený neskôr.',
|
||||
@@ -110,18 +63,7 @@ export const createUser = async (req, res, next) => {
|
||||
*/
|
||||
export const getAllUsers = async (req, res, next) => {
|
||||
try {
|
||||
const allUsers = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
firstName: users.firstName,
|
||||
lastName: users.lastName,
|
||||
role: users.role,
|
||||
changedPassword: users.changedPassword,
|
||||
lastLogin: users.lastLogin,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users);
|
||||
const allUsers = await adminService.getAllUsers();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
@@ -141,36 +83,12 @@ export const getUser = async (req, res, next) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
try {
|
||||
const [user] = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
username: users.username,
|
||||
firstName: users.firstName,
|
||||
lastName: users.lastName,
|
||||
role: users.role,
|
||||
changedPassword: users.changedPassword,
|
||||
lastLogin: users.lastLogin,
|
||||
createdAt: users.createdAt,
|
||||
updatedAt: users.updatedAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
// Get user's email accounts (cez many-to-many)
|
||||
const userEmailAccounts = await emailAccountService.getUserEmailAccounts(userId);
|
||||
const user = await adminService.getUserById(userId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
user: {
|
||||
...user,
|
||||
emailAccounts: userEmailAccounts,
|
||||
},
|
||||
user,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -190,38 +108,14 @@ export const changeUserRole = async (req, res, next) => {
|
||||
const userAgent = req.headers['user-agent'];
|
||||
|
||||
try {
|
||||
// Získaj starú rolu
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
const oldRole = user.role;
|
||||
|
||||
// Update role
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
role,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, userId));
|
||||
const result = await adminService.changeUserRole(userId, role);
|
||||
|
||||
// Log role change
|
||||
await logRoleChange(adminId, userId, oldRole, role, ipAddress, userAgent);
|
||||
await logRoleChange(adminId, userId, result.oldRole, result.newRole, ipAddress, userAgent);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: {
|
||||
userId,
|
||||
oldRole,
|
||||
newRole: role,
|
||||
},
|
||||
data: result,
|
||||
message: 'Rola používateľa bola zmenená',
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -237,58 +131,12 @@ export const deleteUser = async (req, res, next) => {
|
||||
const { userId } = req.params;
|
||||
|
||||
try {
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundError('Používateľ nenájdený');
|
||||
}
|
||||
|
||||
// Zabraň zmazaniu posledného admina
|
||||
if (user.role === 'admin') {
|
||||
const [adminCount] = await db
|
||||
.select({ count: db.$count(users) })
|
||||
.from(users)
|
||||
.where(eq(users.role, 'admin'));
|
||||
|
||||
if (adminCount.count <= 1) {
|
||||
throw new ConflictError('Nemôžete zmazať posledného administrátora');
|
||||
}
|
||||
}
|
||||
|
||||
// Get user's email account IDs before deletion
|
||||
const userEmailAccountLinks = await db
|
||||
.select({ emailAccountId: userEmailAccounts.emailAccountId })
|
||||
.from(userEmailAccounts)
|
||||
.where(eq(userEmailAccounts.userId, userId));
|
||||
|
||||
const emailAccountIds = userEmailAccountLinks.map(link => link.emailAccountId);
|
||||
|
||||
// Delete user (cascades userEmailAccounts links)
|
||||
await db.delete(users).where(eq(users.id, userId));
|
||||
|
||||
// Delete orphaned email accounts (no users linked)
|
||||
// This will cascade delete contacts and emails
|
||||
let deletedEmailAccounts = 0;
|
||||
for (const emailAccountId of emailAccountIds) {
|
||||
const [remainingLinks] = await db
|
||||
.select({ count: db.$count(userEmailAccounts) })
|
||||
.from(userEmailAccounts)
|
||||
.where(eq(userEmailAccounts.emailAccountId, emailAccountId));
|
||||
|
||||
if (remainingLinks.count === 0) {
|
||||
await db.delete(emailAccounts).where(eq(emailAccounts.id, emailAccountId));
|
||||
deletedEmailAccounts++;
|
||||
}
|
||||
}
|
||||
const result = await adminService.deleteUser(userId);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: 'Používateľ bol zmazaný',
|
||||
deletedEmailAccounts,
|
||||
deletedEmailAccounts: result.deletedEmailAccounts,
|
||||
});
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
|
||||
Reference in New Issue
Block a user