- Backend: Node.js/TypeScript with Prisma ORM - Frontend: Vite + TypeScript - Project configuration and documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
273 lines
7.2 KiB
TypeScript
273 lines
7.2 KiB
TypeScript
import { Response } from 'express';
|
|
import bcrypt from 'bcryptjs';
|
|
import prisma from '../config/database';
|
|
import { successResponse, errorResponse, paginatedResponse, parseQueryInt, getParam, getQueryString } from '../utils/helpers';
|
|
import { AuthRequest } from '../middleware/auth.middleware';
|
|
|
|
// Jednoduchý zoznam aktívnych používateľov (pre select/dropdown)
|
|
// Podporuje server-side vyhľadávanie pre lepší výkon pri veľkom počte používateľov
|
|
export const getUsersSimple = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const search = getQueryString(req, 'search');
|
|
const limit = parseQueryInt(req.query.limit, 50); // Default 50, max 100
|
|
const actualLimit = Math.min(limit, 100);
|
|
|
|
const where = {
|
|
active: true,
|
|
...(search && {
|
|
OR: [
|
|
{ name: { contains: search, mode: 'insensitive' as const } },
|
|
{ email: { contains: search, mode: 'insensitive' as const } },
|
|
],
|
|
}),
|
|
};
|
|
|
|
const users = await prisma.user.findMany({
|
|
where,
|
|
take: actualLimit,
|
|
orderBy: { name: 'asc' },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
email: true,
|
|
},
|
|
});
|
|
|
|
successResponse(res, users);
|
|
} catch (error) {
|
|
console.error('Error fetching users simple:', error);
|
|
errorResponse(res, 'Chyba pri načítaní používateľov.', 500);
|
|
}
|
|
};
|
|
|
|
export const getUsers = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const page = parseQueryInt(req.query.page, 1);
|
|
const limit = parseQueryInt(req.query.limit, 20);
|
|
const skip = (page - 1) * limit;
|
|
const search = getQueryString(req, 'search');
|
|
const active = getQueryString(req, 'active');
|
|
const roleId = getQueryString(req, 'roleId');
|
|
|
|
const where = {
|
|
...(active !== undefined && { active: active === 'true' }),
|
|
...(roleId && { roleId }),
|
|
...(search && {
|
|
OR: [
|
|
{ name: { contains: search, mode: 'insensitive' as const } },
|
|
{ email: { contains: search, mode: 'insensitive' as const } },
|
|
],
|
|
}),
|
|
};
|
|
|
|
const [users, total] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where,
|
|
skip,
|
|
take: limit,
|
|
orderBy: { createdAt: 'desc' },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
active: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
role: {
|
|
select: {
|
|
id: true,
|
|
code: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
prisma.user.count({ where }),
|
|
]);
|
|
|
|
paginatedResponse(res, users, total, page, limit);
|
|
} catch (error) {
|
|
console.error('Error fetching users:', error);
|
|
errorResponse(res, 'Chyba pri načítaní používateľov.', 500);
|
|
}
|
|
};
|
|
|
|
export const getUser = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const id = getParam(req, 'id');
|
|
|
|
const user = await prisma.user.findUnique({
|
|
where: { id },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
active: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
role: {
|
|
select: {
|
|
id: true,
|
|
code: true,
|
|
name: true,
|
|
permissions: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!user) {
|
|
errorResponse(res, 'Používateľ nebol nájdený.', 404);
|
|
return;
|
|
}
|
|
|
|
successResponse(res, user);
|
|
} catch (error) {
|
|
console.error('Error fetching user:', error);
|
|
errorResponse(res, 'Chyba pri načítaní používateľa.', 500);
|
|
}
|
|
};
|
|
|
|
export const updateUser = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const id = getParam(req, 'id');
|
|
const { email, name, active, password } = req.body;
|
|
|
|
const existingUser = await prisma.user.findUnique({ where: { id } });
|
|
|
|
if (!existingUser) {
|
|
errorResponse(res, 'Používateľ nebol nájdený.', 404);
|
|
return;
|
|
}
|
|
|
|
// Check email uniqueness if changing
|
|
if (email && email !== existingUser.email) {
|
|
const emailExists = await prisma.user.findUnique({ where: { email } });
|
|
if (emailExists) {
|
|
errorResponse(res, 'Email je už používaný.', 409);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const updateData: Record<string, unknown> = {};
|
|
if (email) updateData.email = email;
|
|
if (name) updateData.name = name;
|
|
if (active !== undefined) updateData.active = active;
|
|
if (password) updateData.password = await bcrypt.hash(password, 10);
|
|
|
|
const user = await prisma.user.update({
|
|
where: { id },
|
|
data: updateData,
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
active: true,
|
|
role: {
|
|
select: {
|
|
id: true,
|
|
code: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (req.logActivity) {
|
|
await req.logActivity('UPDATE', 'User', id, updateData);
|
|
}
|
|
|
|
successResponse(res, user, 'Používateľ bol aktualizovaný.');
|
|
} catch (error) {
|
|
console.error('Error updating user:', error);
|
|
errorResponse(res, 'Chyba pri aktualizácii používateľa.', 500);
|
|
}
|
|
};
|
|
|
|
export const deleteUser = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const id = getParam(req, 'id');
|
|
|
|
// Prevent self-deletion
|
|
if (req.user?.userId === id) {
|
|
errorResponse(res, 'Nemôžete vymazať vlastný účet.', 400);
|
|
return;
|
|
}
|
|
|
|
const user = await prisma.user.findUnique({ where: { id } });
|
|
|
|
if (!user) {
|
|
errorResponse(res, 'Používateľ nebol nájdený.', 404);
|
|
return;
|
|
}
|
|
|
|
// Soft delete - just deactivate
|
|
await prisma.user.update({
|
|
where: { id },
|
|
data: { active: false },
|
|
});
|
|
|
|
if (req.logActivity) {
|
|
await req.logActivity('DELETE', 'User', id);
|
|
}
|
|
|
|
successResponse(res, null, 'Používateľ bol deaktivovaný.');
|
|
} catch (error) {
|
|
console.error('Error deleting user:', error);
|
|
errorResponse(res, 'Chyba pri mazaní používateľa.', 500);
|
|
}
|
|
};
|
|
|
|
export const updateUserRole = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const id = getParam(req, 'id');
|
|
const { roleId } = req.body;
|
|
|
|
// Prevent changing own role
|
|
if (req.user?.userId === id) {
|
|
errorResponse(res, 'Nemôžete zmeniť vlastnú rolu.', 400);
|
|
return;
|
|
}
|
|
|
|
const user = await prisma.user.findUnique({ where: { id } });
|
|
|
|
if (!user) {
|
|
errorResponse(res, 'Používateľ nebol nájdený.', 404);
|
|
return;
|
|
}
|
|
|
|
const role = await prisma.userRole.findUnique({ where: { id: roleId } });
|
|
|
|
if (!role) {
|
|
errorResponse(res, 'Rola neexistuje.', 404);
|
|
return;
|
|
}
|
|
|
|
const updatedUser = await prisma.user.update({
|
|
where: { id },
|
|
data: { roleId },
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
name: true,
|
|
role: {
|
|
select: {
|
|
id: true,
|
|
code: true,
|
|
name: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (req.logActivity) {
|
|
await req.logActivity('UPDATE', 'User', id, { roleId, roleName: role.name });
|
|
}
|
|
|
|
successResponse(res, updatedUser, 'Rola používateľa bola zmenená.');
|
|
} catch (error) {
|
|
console.error('Error updating user role:', error);
|
|
errorResponse(res, 'Chyba pri zmene roly.', 500);
|
|
}
|
|
};
|