negyedik gyakorlat + megoldasok

This commit is contained in:
magdo
2026-03-04 20:02:39 +01:00
parent afc3777ac9
commit 388aa908de
217 changed files with 19791 additions and 0 deletions
@@ -0,0 +1,116 @@
const CreateUserCommand = require('../../application/user/command/CreateUserCommand');
const CreateUserHandler = require('../../application/user/command/CreateUserHandler');
const UpdateUserCommand = require('../../application/user/command/UpdateUserCommand');
const UpdateUserHandler = require('../../application/user/command/UpdateUserHandler');
const DeleteUserCommand = require('../../application/user/command/DeleteUserCommand');
const DeleteUserHandler = require('../../application/user/command/DeleteUserHandler');
const GetUserQuery = require('../../application/user/query/GetUserQuery');
const GetUserHandler = require('../../application/user/query/GetUserHandler');
const GetAllUsersQuery = require('../../application/user/query/GetAllUsersQuery');
const GetAllUsersHandler = require('../../application/user/query/GetAllUsersHandler');
class UserController {
constructor() {
this.createUserHandler = new CreateUserHandler();
this.updateUserHandler = new UpdateUserHandler();
this.deleteUserHandler = new DeleteUserHandler();
this.getUserHandler = new GetUserHandler();
this.getAllUsersHandler = new GetAllUsersHandler();
}
async getAllUsers(req, res) {
try {
const query = new GetAllUsersQuery();
const users = await this.getAllUsersHandler.handle(query);
res.json({
success: true,
data: users
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
}
async getUserById(req, res) {
try {
const query = new GetUserQuery(parseInt(req.params.id));
const user = await this.getUserHandler.handle(query);
res.json({
success: true,
data: user
});
} catch (error) {
res.status(404).json({
success: false,
error: error.message
});
}
}
async createUser(req, res) {
try {
const { email, name, password, role } = req.body;
const command = new CreateUserCommand(email, name, password, role);
const user = await this.createUserHandler.handle(command);
res.status(201).json({
success: true,
data: user,
message: 'User sikeresen létrehozva'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}
async updateUser(req, res) {
try {
const { email, name, role } = req.body;
const command = new UpdateUserCommand(
parseInt(req.params.id),
email,
name,
role
);
const user = await this.updateUserHandler.handle(command);
res.json({
success: true,
data: user,
message: 'User sikeresen frissítve'
});
} catch (error) {
res.status(400).json({
success: false,
error: error.message
});
}
}
async deleteUser(req, res) {
try {
const command = new DeleteUserCommand(parseInt(req.params.id));
const result = await this.deleteUserHandler.handle(command);
res.json({
success: true,
message: result.message
});
} catch (error) {
res.status(404).json({
success: false,
error: error.message
});
}
}
}
module.exports = UserController;
@@ -0,0 +1,16 @@
const express = require('express');
const UserController = require('../controllers/UserController');
const router = express.Router();
const userController = new UserController();
// Query routes (olvasás)
router.get('/', (req, res) => userController.getAllUsers(req, res));
router.get('/:id', (req, res) => userController.getUserById(req, res));
// Command routes (írás)
router.post('/', (req, res) => userController.createUser(req, res));
router.put('/:id', (req, res) => userController.updateUser(req, res));
router.delete('/:id', (req, res) => userController.deleteUser(req, res));
module.exports = router;
@@ -0,0 +1,51 @@
require('dotenv').config();
const express = require('express');
const userRoutes = require('./routes/user.routes');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/users', userRoutes);
// Alapértelmezett route
app.get('/', (req, res) => {
res.json({
message: 'User Management API - MINTA Megoldás',
endpoints: {
users: {
getAll: 'GET /api/users',
getById: 'GET /api/users/:id',
create: 'POST /api/users',
update: 'PUT /api/users/:id',
delete: 'DELETE /api/users/:id'
}
}
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Endpoint nem található!'
});
});
// Error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: 'Szerver hiba történt!'
});
});
app.listen(PORT, () => {
console.log(`✅ Szerver fut a http://localhost:${PORT} címen`);
console.log(`📊 Prisma Studio: npx prisma studio`);
});
@@ -0,0 +1,10 @@
class CreateUserCommand {
constructor(email, name, password, role) {
this.email = email;
this.name = name;
this.password = password;
this.role = role;
}
}
module.exports = CreateUserCommand;
@@ -0,0 +1,41 @@
const UserRepository = require('../../../infrastructure/repositories/UserRepository');
class CreateUserHandler {
constructor() {
this.userRepository = new UserRepository();
}
async handle(command) {
// 1. Validáció
if (!command.email || !command.name || !command.password) {
throw new Error('Email, name és password kötelező');
}
// 2. Email formátum ellenőrzés
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(command.email)) {
throw new Error('Hibás email formátum');
}
// 3. Jelszó hossz ellenőrzés
if (command.password.length < 6) {
throw new Error('Jelszó minimum 6 karakter kell legyen');
}
// 4. Duplikáció ellenőrzés
const existing = await this.userRepository.findByEmail(command.email);
if (existing) {
throw new Error('Email már használatban van');
}
// 5. User létrehozása
return await this.userRepository.create({
email: command.email,
name: command.name,
password: command.password, // Production környezetben hash-elni kell!
role: command.role || 'user'
});
}
}
module.exports = CreateUserHandler;
@@ -0,0 +1,7 @@
class DeleteUserCommand {
constructor(id) {
this.id = id;
}
}
module.exports = DeleteUserCommand;
@@ -0,0 +1,22 @@
const UserRepository = require('../../../infrastructure/repositories/UserRepository');
class DeleteUserHandler {
constructor() {
this.userRepository = new UserRepository();
}
async handle(command) {
const user = await this.userRepository.findById(command.id);
if (!user) {
throw new Error('User nem található');
}
await this.userRepository.delete(command.id);
return {
message: 'User sikeresen törölve',
id: command.id
};
}
}
module.exports = DeleteUserHandler;
@@ -0,0 +1,10 @@
class UpdateUserCommand {
constructor(id, email, name, role) {
this.id = id;
this.email = email;
this.name = name;
this.role = role;
}
}
module.exports = UpdateUserCommand;
@@ -0,0 +1,34 @@
const UserRepository = require('../../../infrastructure/repositories/UserRepository');
class UpdateUserHandler {
constructor() {
this.userRepository = new UserRepository();
}
async handle(command) {
// 1. User létezésének ellenőrzése
const user = await this.userRepository.findById(command.id);
if (!user) {
throw new Error('User nem található');
}
// 2. Email egyediség ellenőrzése (ha változott)
if (command.email && command.email !== user.email) {
const existing = await this.userRepository.findByEmail(command.email);
if (existing) {
throw new Error('Email már használatban van');
}
}
// 3. Frissítendő adatok összeállítása
const updateData = {};
if (command.email) updateData.email = command.email;
if (command.name) updateData.name = command.name;
if (command.role) updateData.role = command.role;
// 4. Frissítés végrehajtása
return await this.userRepository.update(command.id, updateData);
}
}
module.exports = UpdateUserHandler;
@@ -0,0 +1,13 @@
const UserRepository = require('../../../infrastructure/repositories/UserRepository');
class GetAllUsersHandler {
constructor() {
this.userRepository = new UserRepository();
}
async handle(query) {
return await this.userRepository.findAll();
}
}
module.exports = GetAllUsersHandler;
@@ -0,0 +1,5 @@
class GetAllUsersQuery {
constructor() {}
}
module.exports = GetAllUsersQuery;
@@ -0,0 +1,17 @@
const UserRepository = require('../../../infrastructure/repositories/UserRepository');
class GetUserHandler {
constructor() {
this.userRepository = new UserRepository();
}
async handle(query) {
const user = await this.userRepository.findById(query.id);
if (!user) {
throw new Error('User nem található');
}
return user;
}
}
module.exports = GetUserHandler;
@@ -0,0 +1,7 @@
class GetUserQuery {
constructor(id) {
this.id = id;
}
}
module.exports = GetUserQuery;
@@ -0,0 +1,33 @@
/**
* IUserRepository Interface
*
* Ez definiálja a UserRepository-nak kötelező metódusokat.
* A Repository Pattern szerint az Infrastructure réteg implementálja ezt.
*/
class IUserRepository {
async create(userData) {
throw new Error('create() metódus nincs implementálva');
}
async findById(id) {
throw new Error('findById() metódus nincs implementálva');
}
async findByEmail(email) {
throw new Error('findByEmail() metódus nincs implementálva');
}
async findAll() {
throw new Error('findAll() metódus nincs implementálva');
}
async update(id, userData) {
throw new Error('update() metódus nincs implementálva');
}
async delete(id) {
throw new Error('delete() metódus nincs implementálva');
}
}
module.exports = IUserRepository;
@@ -0,0 +1,56 @@
class User {
constructor(data) {
this.id = data.id;
this.email = data.email;
this.name = data.name;
this.password = data.password;
this.role = data.role || 'user';
this.createdAt = data.createdAt;
this.updatedAt = data.updatedAt;
}
/**
* Ellenőrzi, hogy a user admin-e
*/
isAdmin() {
return this.role === 'admin';
}
/**
* Ellenőrzi, hogy a user szerkeszthet-e egy másik usert
* @param {User} targetUser - A szerkeszteni kívánt user
*/
canEdit(targetUser) {
return this.isAdmin() || this.id === targetUser.id;
}
/**
* User adatok validálása
*/
validate() {
if (!this.email || !this.name || !this.password) {
throw new Error('Email, name és password kötelező');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(this.email)) {
throw new Error('Hibás email formátum');
}
if (this.password.length < 6) {
throw new Error('Jelszó min. 6 karakter');
}
return true;
}
/**
* User objektum JSON-ként (jelszó nélkül!)
*/
toJSON() {
const { password, ...userWithoutPassword } = this;
return userWithoutPassword;
}
}
module.exports = User;
@@ -0,0 +1,7 @@
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient({
log: ['query', 'error', 'warn'],
});
module.exports = prisma;
@@ -0,0 +1,79 @@
const prisma = require('../database/prisma');
const IUserRepository = require('../../domain/interfaces/IUserRepository');
/**
* UserRepository - Prisma implementáció
*
* Ez az osztály implementálja az IUserRepository interfészt
* és Prisma ORM-et használ az adatbázis műveletekhez.
*/
class UserRepository extends IUserRepository {
/**
* User létrehozása
* @param {Object} userData - { email, name, password, role }
* @returns {Promise<User>}
*/
async create(userData) {
return await prisma.user.create({
data: userData
});
}
/**
* User keresése ID alapján
* @param {number} id
* @returns {Promise<User|null>}
*/
async findById(id) {
return await prisma.user.findUnique({
where: { id: parseInt(id) }
});
}
/**
* User keresése email alapján
* @param {string} email
* @returns {Promise<User|null>}
*/
async findByEmail(email) {
return await prisma.user.findUnique({
where: { email }
});
}
/**
* Összes user lekérése
* @returns {Promise<User[]>}
*/
async findAll() {
return await prisma.user.findMany({
orderBy: { createdAt: 'desc' }
});
}
/**
* User frissítése
* @param {number} id
* @param {Object} userData - Frissítendő mezők
* @returns {Promise<User>}
*/
async update(id, userData) {
return await prisma.user.update({
where: { id: parseInt(id) },
data: userData
});
}
/**
* User törlése
* @param {number} id
* @returns {Promise<User>}
*/
async delete(id) {
return await prisma.user.delete({
where: { id: parseInt(id) }
});
}
}
module.exports = UserRepository;