Initial commit
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
class AppError extends Error {
|
||||
constructor(message, statusCode, isOperational = true, errors = null) {
|
||||
super(message);
|
||||
this.statusCode = statusCode;
|
||||
this.isOperational = isOperational;
|
||||
this.errors = errors;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AppError;
|
||||
@@ -0,0 +1,55 @@
|
||||
const AppError = require('./AppError');
|
||||
const config = require('../../config');
|
||||
|
||||
class ValidationError extends AppError {
|
||||
constructor(message, errors = null) {
|
||||
super(message, config.HTTP_STATUS.BAD_REQUEST, true, errors);
|
||||
this.name = 'ValidationError';
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundError extends AppError {
|
||||
constructor(message = 'Resource not found') {
|
||||
super(message, config.HTTP_STATUS.NOT_FOUND, true);
|
||||
this.name = 'NotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
class ConflictError extends AppError {
|
||||
constructor(message, errors = null) {
|
||||
super(message, config.HTTP_STATUS.CONFLICT, true, errors);
|
||||
this.name = 'ConflictError';
|
||||
}
|
||||
}
|
||||
|
||||
class UnauthorizedError extends AppError {
|
||||
constructor(message = 'Unauthorized') {
|
||||
super(message, config.HTTP_STATUS.UNAUTHORIZED, true);
|
||||
this.name = 'UnauthorizedError';
|
||||
}
|
||||
}
|
||||
|
||||
class ForbiddenError extends AppError {
|
||||
constructor(message = 'Forbidden') {
|
||||
super(message, config.HTTP_STATUS.FORBIDDEN, true);
|
||||
this.name = 'ForbiddenError';
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseError extends AppError {
|
||||
constructor(message = 'Database operation failed', originalError = null) {
|
||||
super(message, config.HTTP_STATUS.INTERNAL_SERVER_ERROR, false);
|
||||
this.name = 'DatabaseError';
|
||||
this.originalError = originalError;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AppError,
|
||||
ValidationError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
UnauthorizedError,
|
||||
ForbiddenError,
|
||||
DatabaseError,
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
const config = require('../config');
|
||||
|
||||
class ApiResponse {
|
||||
constructor(success, statusCode, data = null, message = null, errors = null) {
|
||||
this.success = success;
|
||||
this.statusCode = statusCode;
|
||||
this.data = data;
|
||||
this.message = message;
|
||||
this.errors = errors;
|
||||
this.timestamp = new Date().toISOString();
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const response = {
|
||||
success: this.success,
|
||||
timestamp: this.timestamp,
|
||||
};
|
||||
|
||||
if (this.message) response.message = this.message;
|
||||
if (this.data !== null) response.data = this.data;
|
||||
if (this.errors) response.errors = this.errors;
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
class ResponseHandler {
|
||||
static success(res, data, message = 'Success', statusCode = config.HTTP_STATUS.OK) {
|
||||
const response = new ApiResponse(true, statusCode, data, message);
|
||||
return res.status(statusCode).json(response.toJSON());
|
||||
}
|
||||
|
||||
static created(res, data, message = 'Resource created successfully') {
|
||||
return this.success(res, data, message, config.HTTP_STATUS.CREATED);
|
||||
}
|
||||
|
||||
static error(
|
||||
res,
|
||||
message,
|
||||
statusCode = config.HTTP_STATUS.INTERNAL_SERVER_ERROR,
|
||||
errors = null
|
||||
) {
|
||||
const response = new ApiResponse(false, statusCode, null, message, errors);
|
||||
return res.status(statusCode).json(response.toJSON());
|
||||
}
|
||||
|
||||
static badRequest(res, message = 'Bad request', errors = null) {
|
||||
return this.error(res, message, config.HTTP_STATUS.BAD_REQUEST, errors);
|
||||
}
|
||||
|
||||
static notFound(res, message = 'Resource not found') {
|
||||
return this.error(res, message, config.HTTP_STATUS.NOT_FOUND);
|
||||
}
|
||||
|
||||
static unauthorized(res, message = 'Unauthorized') {
|
||||
return this.error(res, message, config.HTTP_STATUS.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
static forbidden(res, message = 'Forbidden') {
|
||||
return this.error(res, message, config.HTTP_STATUS.FORBIDDEN);
|
||||
}
|
||||
|
||||
static conflict(res, message = 'Resource conflict', errors = null) {
|
||||
return this.error(res, message, config.HTTP_STATUS.CONFLICT, errors);
|
||||
}
|
||||
|
||||
static internalError(res, message = 'Internal server error', error = null) {
|
||||
if (error && config.isDevelopment) {
|
||||
console.error('Internal Error:', error);
|
||||
}
|
||||
return this.error(res, message, config.HTTP_STATUS.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResponseHandler;
|
||||
@@ -0,0 +1,49 @@
|
||||
const Joi = require('joi');
|
||||
const config = require('../../../config');
|
||||
|
||||
const participantSchema = Joi.object({
|
||||
user: Joi.number().integer().positive().required(),
|
||||
role: Joi.string()
|
||||
.valid(...Object.values(config.PARTICIPANT_ROLES))
|
||||
.required(),
|
||||
});
|
||||
|
||||
const createBetSchema = Joi.object({
|
||||
title: Joi.string().trim().min(1).max(200).required().messages({
|
||||
'string.empty': 'Bet title is required',
|
||||
'string.max': 'Bet title must not exceed 200 characters',
|
||||
}),
|
||||
description: Joi.string().trim().max(1000).optional().allow(''),
|
||||
stake: Joi.string().trim().min(1).required().messages({
|
||||
'string.empty': 'Bet stake is required',
|
||||
}),
|
||||
participants: Joi.array().items(participantSchema).min(1).required().messages({
|
||||
'array.min': 'At least one participant is required',
|
||||
}),
|
||||
isPublic: Joi.boolean().optional().default(false),
|
||||
groupId: Joi.string().optional().allow(null),
|
||||
});
|
||||
|
||||
const updateBetSchema = Joi.object({
|
||||
title: Joi.string().trim().min(1).max(200).optional(),
|
||||
description: Joi.string().trim().max(1000).optional().allow(''),
|
||||
stake: Joi.string().trim().min(1).optional(),
|
||||
status: Joi.string()
|
||||
.valid(...Object.values(config.BET_STATUS))
|
||||
.optional(),
|
||||
endDate: Joi.date().optional(),
|
||||
winner: Joi.number().integer().positive().optional().allow(null),
|
||||
});
|
||||
|
||||
const declareWinnerSchema = Joi.object({
|
||||
winnerId: Joi.number().integer().positive().required().messages({
|
||||
'number.base': 'Winner ID must be a number',
|
||||
'any.required': 'Winner ID is required',
|
||||
}),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createBetSchema,
|
||||
updateBetSchema,
|
||||
declareWinnerSchema,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
const Joi = require('joi');
|
||||
|
||||
const sendFriendRequestSchema = Joi.object({
|
||||
identifier: Joi.alternatives().try(Joi.number().integer().positive(), Joi.string().min(1)).required().messages({
|
||||
'any.required': 'User identifier (ID, name, or email) is required',
|
||||
}),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
sendFriendRequestSchema,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
const Joi = require('joi');
|
||||
|
||||
const createGroupSchema = Joi.object({
|
||||
name: Joi.string().trim().min(1).max(100).required().messages({
|
||||
'string.empty': 'Group name is required',
|
||||
'string.min': 'Group name must be at least 1 character',
|
||||
'string.max': 'Group name must not exceed 100 characters',
|
||||
}),
|
||||
members: Joi.array().items(Joi.number().integer().positive()).min(1).required().messages({
|
||||
'array.min': 'At least one member is required',
|
||||
'array.base': 'Members must be an array',
|
||||
}),
|
||||
});
|
||||
|
||||
const updateGroupSchema = Joi.object({
|
||||
name: Joi.string().trim().min(1).max(100).optional(),
|
||||
});
|
||||
|
||||
const addMembersSchema = Joi.object({
|
||||
userIds: Joi.array().items(Joi.number().integer().positive()).min(1).required().messages({
|
||||
'array.min': 'At least one user ID is required',
|
||||
}),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createGroupSchema,
|
||||
updateGroupSchema,
|
||||
addMembersSchema,
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
const Joi = require('joi');
|
||||
|
||||
const updateUserSchema = Joi.object({
|
||||
name: Joi.string().trim().min(1).max(100).optional(),
|
||||
email: Joi.string().email().optional(),
|
||||
avatarUrl: Joi.string().uri().optional().allow('', null),
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
updateUserSchema,
|
||||
};
|
||||
Reference in New Issue
Block a user