Modifiche
This commit is contained in:
@@ -22,7 +22,7 @@ COPY package*.json ./
|
||||
FROM base AS dependencies
|
||||
|
||||
# Install ALL dependencies (including devDependencies for build)
|
||||
RUN npm ci
|
||||
RUN npm install
|
||||
|
||||
# ========================================
|
||||
# Stage 3: Development
|
||||
@@ -50,7 +50,7 @@ CMD ["npm", "start"]
|
||||
FROM base AS production-dependencies
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm ci --only=production && \
|
||||
RUN npm install --only=production && \
|
||||
npm cache clean --force
|
||||
|
||||
# ========================================
|
||||
|
||||
@@ -5,6 +5,24 @@ class BetsController {
|
||||
this.betsService = betsService;
|
||||
}
|
||||
|
||||
async getUserBets(req, res, next) {
|
||||
try {
|
||||
const bets = await this.betsService.getUserBets(req.userId);
|
||||
return ResponseHandler.success(res, bets, 'User bets retrieved successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getPublicBets(req, res, next) {
|
||||
try {
|
||||
const bets = await this.betsService.getPublicBetsExcludingUser(req.userId);
|
||||
return ResponseHandler.success(res, bets, 'Public bets retrieved successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async getAllBets(req, res, next) {
|
||||
try {
|
||||
const bets = await this.betsService.getAllBets();
|
||||
|
||||
@@ -7,7 +7,7 @@ class GroupsController {
|
||||
|
||||
async getAllGroups(req, res, next) {
|
||||
try {
|
||||
const groups = await this.groupsService.getAllGroups();
|
||||
const groups = await this.groupsService.getAllGroups(req.userId);
|
||||
return ResponseHandler.success(res, groups, 'Groups retrieved successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -52,10 +52,7 @@ class GroupsController {
|
||||
|
||||
async addMembers(req, res, next) {
|
||||
try {
|
||||
const group = await this.groupsService.addMembersToGroup(
|
||||
req.params.id,
|
||||
req.body.userIds
|
||||
);
|
||||
const group = await this.groupsService.addMembersToGroup(req.params.id, req.body.userIds);
|
||||
return ResponseHandler.success(res, group, 'Members added successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
const ResponseHandler = require('../shared/response.handler');
|
||||
|
||||
class InitialDataController {
|
||||
constructor({ usersService, friendsService, groupsService, betsService }) {
|
||||
this.usersService = usersService;
|
||||
this.friendsService = friendsService;
|
||||
this.groupsService = groupsService;
|
||||
this.betsService = betsService;
|
||||
}
|
||||
|
||||
async getInitialData(req, res, next) {
|
||||
try {
|
||||
const currentUser = await this.usersService.getUserById(req.userId);
|
||||
const users = await this.usersService.getAllUsers();
|
||||
const friends = await this.friendsService.getFriendsByUserId(req.userId);
|
||||
const friendRequests = await this.friendsService.getPendingRequests(req.userId);
|
||||
const groups = await this.groupsService.getAllGroups();
|
||||
const userBets = await this.betsService.getUserBets(req.userId);
|
||||
const publicBets = await this.betsService.getPublicBetsExcludingUser(req.userId);
|
||||
|
||||
const data = {
|
||||
currentUser,
|
||||
users,
|
||||
friends,
|
||||
friendRequests,
|
||||
groups,
|
||||
userBets,
|
||||
publicBets,
|
||||
};
|
||||
|
||||
return ResponseHandler.success(res, data, 'Initial data retrieved successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InitialDataController;
|
||||
@@ -5,6 +5,15 @@ class UsersController {
|
||||
this.usersService = usersService;
|
||||
}
|
||||
|
||||
async getCurrentUser(req, res, next) {
|
||||
try {
|
||||
const user = await this.usersService.getUserById(req.userId);
|
||||
return ResponseHandler.success(res, user, 'User retrieved successfully');
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async updateCurrentUser(req, res, next) {
|
||||
try {
|
||||
const user = await this.usersService.updateUserProfile(req.userId, req.body);
|
||||
|
||||
@@ -18,13 +18,13 @@ function setupContainer() {
|
||||
const modelsDir = path.join(__dirname, '../models');
|
||||
fs.readdirSync(modelsDir)
|
||||
.filter(
|
||||
(file) =>
|
||||
file =>
|
||||
file.indexOf('.') !== 0 &&
|
||||
file.slice(-3) === '.js' &&
|
||||
file !== 'bet.model.js' &&
|
||||
file !== 'associations.js'
|
||||
)
|
||||
.forEach((file) => {
|
||||
.forEach(file => {
|
||||
require(path.join(modelsDir, file))(sequelize);
|
||||
});
|
||||
|
||||
@@ -38,27 +38,19 @@ function setupContainer() {
|
||||
});
|
||||
|
||||
container.register({
|
||||
usersRepository: asClass(
|
||||
require('../repositories/postgres/users.repository')
|
||||
).scoped(),
|
||||
groupsRepository: asClass(
|
||||
require('../repositories/postgres/groups.repository')
|
||||
).scoped(),
|
||||
usersRepository: asClass(require('../repositories/postgres/users.repository')).scoped(),
|
||||
groupsRepository: asClass(require('../repositories/postgres/groups.repository')).scoped(),
|
||||
friendshipsRepository: asClass(
|
||||
require('../repositories/postgres/friendships.repository')
|
||||
).scoped(),
|
||||
friendRequestsRepository: asClass(
|
||||
require('../repositories/postgres/friendRequests.repository')
|
||||
).scoped(),
|
||||
betsRepository: asClass(
|
||||
require('../repositories/mongo/bets.repository')
|
||||
).scoped(),
|
||||
betsRepository: asClass(require('../repositories/mongo/bets.repository')).scoped(),
|
||||
});
|
||||
|
||||
container.register({
|
||||
dataStitchingService: asClass(
|
||||
require('../services/data-stitching.service')
|
||||
).scoped(),
|
||||
dataStitchingService: asClass(require('../services/data-stitching.service')).scoped(),
|
||||
usersService: asClass(require('../services/users.service')).scoped(),
|
||||
groupsService: asClass(require('../services/groups.service')).scoped(),
|
||||
friendsService: asClass(require('../services/friends.service')).scoped(),
|
||||
@@ -70,9 +62,6 @@ function setupContainer() {
|
||||
groupsController: asClass(require('../controllers/groups.controller')).scoped(),
|
||||
usersController: asClass(require('../controllers/users.controller')).scoped(),
|
||||
friendsController: asClass(require('../controllers/friends.controller')).scoped(),
|
||||
initialDataController: asClass(
|
||||
require('../controllers/initialData.controller')
|
||||
).scoped(),
|
||||
});
|
||||
|
||||
return container;
|
||||
|
||||
@@ -11,6 +11,7 @@ DROP TABLE IF EXISTS users CASCADE;
|
||||
-- users table
|
||||
CREATE TABLE users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
"keycloakId" VARCHAR(255) UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
"email" VARCHAR(255) NOT NULL UNIQUE,
|
||||
"avatarUrl" VARCHAR(255) NOT NULL,
|
||||
@@ -63,6 +64,7 @@ CREATE TABLE friend_requests (
|
||||
|
||||
-- User <-> Group many-to-many join table
|
||||
CREATE TABLE "UserGroups" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
"userId" INTEGER NOT NULL,
|
||||
"groupId" INTEGER NOT NULL,
|
||||
CONSTRAINT usergroups_user_fk FOREIGN KEY ("userId") REFERENCES users(id) ON DELETE CASCADE,
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
const jwksClient = require('jwks-rsa');
|
||||
const jwt = require('jsonwebtoken');
|
||||
|
||||
const client = jwksClient({
|
||||
jwksUri:
|
||||
process.env.KEYCLOAK_JWKS_URI ||
|
||||
'http://localhost:8080/realms/scommettiapp/protocol/openid-connect/certs',
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 5,
|
||||
});
|
||||
|
||||
function getKey(header, callback) {
|
||||
client.getSigningKey(header.kid, (err, key) => {
|
||||
if (err) return callback(err);
|
||||
callback(null, key.getPublicKey());
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = sequelize => {
|
||||
const { User } = sequelize.models;
|
||||
|
||||
return (req, res, next) => {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ success: false, message: 'Token mancante o non valido' });
|
||||
}
|
||||
|
||||
const token = authHeader.slice(7);
|
||||
|
||||
jwt.verify(token, getKey, { algorithms: ['RS256'] }, async (err, decoded) => {
|
||||
if (err) {
|
||||
return res.status(401).json({ success: false, message: 'Token non valido o scaduto' });
|
||||
}
|
||||
|
||||
try {
|
||||
const keycloakId = decoded.sub;
|
||||
const name =
|
||||
decoded.name ||
|
||||
`${decoded.given_name || ''} ${decoded.family_name || ''}`.trim() ||
|
||||
decoded.preferred_username ||
|
||||
'Utente';
|
||||
const email = decoded.email || `${keycloakId.slice(0, 8)}@scommetti.app`;
|
||||
const avatarUrl = `https://picsum.photos/seed/${keycloakId.slice(0, 8)}/200/200`;
|
||||
|
||||
// Prima cerca per keycloakId (caso normale dopo il primo login)
|
||||
let user = await User.findOne({ where: { keycloakId } });
|
||||
|
||||
if (!user) {
|
||||
// Migrazione: l'utente potrebbe esistere già per email
|
||||
// (creato prima che keycloakId fosse aggiunto al modello)
|
||||
user = await User.findOne({ where: { email } });
|
||||
if (user) {
|
||||
await user.update({ keycloakId });
|
||||
} else {
|
||||
user = await User.create({ keycloakId, name, email, avatarUrl, score: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
req.userId = user.id;
|
||||
next();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -1,21 +1,46 @@
|
||||
module.exports = (sequelize) => {
|
||||
const { User, Group, FriendRequest, Friendship } = sequelize.models;
|
||||
// eslint-disable-next-line import/no-import-module-exports
|
||||
import { DataTypes } from 'sequelize';
|
||||
|
||||
// User <-> Group: Many-to-Many
|
||||
// A User can be in many Groups, and a Group can have many Users (members).
|
||||
const UserGroup = sequelize.define('UserGroup', {}, { timestamps: false });
|
||||
User.belongsToMany(Group, { through: UserGroup, foreignKey: 'userId' });
|
||||
Group.belongsToMany(User, { through: UserGroup, as: 'members', foreignKey: 'groupId' });
|
||||
module.exports = sequelize => {
|
||||
const { User, Group, FriendRequest, Friendship } = sequelize.models;
|
||||
|
||||
// Friendship is a direct link between two users.
|
||||
// user1Id will always be less than user2Id to ensure uniqueness.
|
||||
Friendship.belongsTo(User, { as: 'user1', foreignKey: 'user1Id' });
|
||||
Friendship.belongsTo(User, { as: 'user2', foreignKey: 'user2Id' });
|
||||
// User <-> Group: Many-to-Many
|
||||
// Il modello dichiara esplicitamente la colonna id per evitare che sync() la cerchi
|
||||
// sulla tabella UserGroups senza trovarla (causando errori su RETURNING "id").
|
||||
const UserGroup = sequelize.define(
|
||||
'UserGroup',
|
||||
{
|
||||
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
|
||||
userId: { type: DataTypes.INTEGER, allowNull: false },
|
||||
groupId: { type: DataTypes.INTEGER, allowNull: false },
|
||||
},
|
||||
{ timestamps: false }
|
||||
);
|
||||
User.belongsToMany(Group, { through: UserGroup, foreignKey: 'userId', otherKey: 'groupId' });
|
||||
Group.belongsToMany(User, {
|
||||
through: UserGroup,
|
||||
as: 'members',
|
||||
foreignKey: 'groupId',
|
||||
otherKey: 'userId',
|
||||
});
|
||||
|
||||
// User <-> FriendRequest: One-to-Many
|
||||
// A User can send many friend requests and receive many friend requests.
|
||||
User.hasMany(FriendRequest, { as: 'sentRequests', foreignKey: 'fromUserId', onDelete: 'CASCADE' });
|
||||
User.hasMany(FriendRequest, { as: 'receivedRequests', foreignKey: 'toUserId', onDelete: 'CASCADE' });
|
||||
FriendRequest.belongsTo(User, { as: 'fromUser', foreignKey: 'fromUserId' });
|
||||
FriendRequest.belongsTo(User, { as: 'toUser', foreignKey: 'toUserId' });
|
||||
// Friendship is a direct link between two users.
|
||||
// user1Id will always be less than user2Id to ensure uniqueness.
|
||||
Friendship.belongsTo(User, { as: 'user1', foreignKey: 'user1Id' });
|
||||
Friendship.belongsTo(User, { as: 'user2', foreignKey: 'user2Id' });
|
||||
|
||||
// User <-> FriendRequest: One-to-Many
|
||||
// A User can send many friend requests and receive many friend requests.
|
||||
User.hasMany(FriendRequest, {
|
||||
as: 'sentRequests',
|
||||
foreignKey: 'fromUserId',
|
||||
onDelete: 'CASCADE',
|
||||
});
|
||||
User.hasMany(FriendRequest, {
|
||||
as: 'receivedRequests',
|
||||
foreignKey: 'toUserId',
|
||||
onDelete: 'CASCADE',
|
||||
});
|
||||
FriendRequest.belongsTo(User, { as: 'fromUser', foreignKey: 'fromUserId' });
|
||||
FriendRequest.belongsTo(User, { as: 'toUser', foreignKey: 'toUserId' });
|
||||
};
|
||||
|
||||
@@ -1,21 +1,34 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const FriendRequest = sequelize.define('FriendRequest', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true
|
||||
module.exports = sequelize => {
|
||||
const FriendRequest = sequelize.define(
|
||||
'FriendRequest',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'PENDING',
|
||||
allowNull: false,
|
||||
},
|
||||
// Foreign keys 'fromUserId' and 'toUserId' are added via associations
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING,
|
||||
defaultValue: 'PENDING',
|
||||
allowNull: false
|
||||
{
|
||||
// Uses default `createdAt` and `updatedAt` timestamps from Sequelize
|
||||
tableName: 'friend_requests',
|
||||
}
|
||||
// Foreign keys 'fromUserId' and 'toUserId' are added via associations
|
||||
}, {
|
||||
// Uses default `createdAt` and `updatedAt` timestamps from Sequelize
|
||||
tableName: 'friend_requests'
|
||||
});
|
||||
);
|
||||
|
||||
FriendRequest.prototype.toJSON = function () {
|
||||
const values = { ...this.get() };
|
||||
if (values.id !== undefined) values.id = String(values.id);
|
||||
if (values.fromUserId !== undefined) values.fromUserId = String(values.fromUserId);
|
||||
if (values.toUserId !== undefined) values.toUserId = String(values.toUserId);
|
||||
return values;
|
||||
};
|
||||
|
||||
return FriendRequest;
|
||||
};
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const Group = sequelize.define('Group', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true
|
||||
module.exports = sequelize => {
|
||||
const Group = sequelize.define(
|
||||
'Group',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: 'groups',
|
||||
}
|
||||
}, {
|
||||
timestamps: false,
|
||||
tableName: 'groups'
|
||||
});
|
||||
);
|
||||
|
||||
Group.prototype.toJSON = function () {
|
||||
const values = { ...this.get() };
|
||||
if (values.id !== undefined) values.id = String(values.id);
|
||||
return values;
|
||||
};
|
||||
|
||||
return Group;
|
||||
};
|
||||
|
||||
@@ -1,35 +1,51 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const User = sequelize.define('User', {
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true
|
||||
module.exports = sequelize => {
|
||||
const User = sequelize.define(
|
||||
'User',
|
||||
{
|
||||
id: {
|
||||
type: DataTypes.INTEGER,
|
||||
autoIncrement: true,
|
||||
primaryKey: true,
|
||||
},
|
||||
keycloakId: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: true,
|
||||
unique: true,
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
validate: {
|
||||
isEmail: true,
|
||||
},
|
||||
},
|
||||
avatarUrl: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
score: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0,
|
||||
},
|
||||
},
|
||||
name: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
email: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false,
|
||||
unique: true,
|
||||
validate: {
|
||||
isEmail: true
|
||||
}
|
||||
},
|
||||
avatarUrl: {
|
||||
type: DataTypes.STRING,
|
||||
allowNull: false
|
||||
},
|
||||
score: {
|
||||
type: DataTypes.INTEGER,
|
||||
defaultValue: 0
|
||||
},
|
||||
}, {
|
||||
timestamps: false,
|
||||
tableName: 'users'
|
||||
});
|
||||
{
|
||||
timestamps: false,
|
||||
tableName: 'users',
|
||||
}
|
||||
);
|
||||
|
||||
User.prototype.toJSON = function () {
|
||||
const values = { ...this.get() };
|
||||
if (values.id !== undefined) values.id = String(values.id);
|
||||
return values;
|
||||
};
|
||||
|
||||
return User;
|
||||
};
|
||||
|
||||
Generated
+205
@@ -13,6 +13,8 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"joi": "^17.12.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwks-rsa": "^3.1.0",
|
||||
"mongoose": "^8.4.0",
|
||||
"pg": "^8.11.5",
|
||||
"pg-hstore": "^2.3.4",
|
||||
@@ -294,6 +296,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/jsonwebtoken": {
|
||||
"version": "9.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
|
||||
"integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/ms": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ms": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||
@@ -653,6 +665,12 @@
|
||||
"node": ">=16.20.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -1003,6 +1021,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
@@ -2640,6 +2667,15 @@
|
||||
"@sideway/pinpoint": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.15.9",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
|
||||
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
@@ -2687,6 +2723,94 @@
|
||||
"json5": "lib/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
|
||||
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jws": "^4.0.1",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jwa": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
|
||||
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-equal-constant-time": "^1.0.1",
|
||||
"ecdsa-sig-formatter": "1.0.11",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/jwks-rsa": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.2.2.tgz",
|
||||
"integrity": "sha512-BqTyEDV+lS8F2trk3A+qJnxV5Q9EqKCBJOPti3W97r7qTympCZjb7h2X6f2kc+0K3rsSTY1/6YG2eaXKoj497w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jsonwebtoken": "^9.0.4",
|
||||
"debug": "^4.3.4",
|
||||
"jose": "^4.15.4",
|
||||
"limiter": "^1.1.5",
|
||||
"lru-memoizer": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/jwks-rsa/node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/jwks-rsa/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jws": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
|
||||
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jwa": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/kareem": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
|
||||
@@ -2720,6 +2844,11 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/limiter": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||
"integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@@ -2742,6 +2871,48 @@
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@@ -2749,6 +2920,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lower-case": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
|
||||
@@ -2758,6 +2935,28 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-memoizer": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz",
|
||||
"integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lru-cache": "6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -4763,6 +4962,12 @@
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yocto-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"joi": "^17.12.1",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jwks-rsa": "^3.1.0",
|
||||
"mongoose": "^8.4.0",
|
||||
"pg": "^8.11.5",
|
||||
"pg-hstore": "^2.3.4",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { NotFoundError, DatabaseError } = require('../../shared/errors');
|
||||
const { Op } = require('sequelize');
|
||||
const { NotFoundError, DatabaseError } = require('../../shared/errors');
|
||||
|
||||
class FriendRequestsRepository {
|
||||
constructor({ sequelize }) {
|
||||
@@ -30,7 +30,7 @@ class FriendRequestsRepository {
|
||||
try {
|
||||
return await this.FriendRequest.findAll({
|
||||
where: {
|
||||
toUserId: userId,
|
||||
[Op.or]: [{ toUserId: userId }, { fromUserId: userId }],
|
||||
status: 'PENDING',
|
||||
},
|
||||
include: [
|
||||
@@ -39,10 +39,7 @@ class FriendRequestsRepository {
|
||||
],
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError(
|
||||
`Failed to fetch pending requests for user ${userId}`,
|
||||
error
|
||||
);
|
||||
throw new DatabaseError(`Failed to fetch pending requests for user ${userId}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +61,12 @@ class FriendRequestsRepository {
|
||||
|
||||
async create(fromUserId, toUserId) {
|
||||
try {
|
||||
return await this.FriendRequest.create({
|
||||
const created = await this.FriendRequest.create({
|
||||
fromUserId,
|
||||
toUserId,
|
||||
status: 'PENDING',
|
||||
});
|
||||
return await this.findById(created.id);
|
||||
} catch (error) {
|
||||
throw new DatabaseError('Failed to create friend request', error);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { Op } = require('sequelize');
|
||||
const { NotFoundError, DatabaseError } = require('../../shared/errors');
|
||||
|
||||
class GroupsRepository {
|
||||
@@ -15,6 +16,29 @@ class GroupsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
async findByUserId(userId) {
|
||||
try {
|
||||
return await this.Group.findAll({
|
||||
include: [
|
||||
{
|
||||
model: this.User,
|
||||
as: 'members',
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
where: {
|
||||
id: {
|
||||
[Op.in]: this.sequelize.literal(
|
||||
`(SELECT "groupId" FROM "UserGroups" WHERE "userId" = ${parseInt(userId, 10)})`
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
throw new DatabaseError('Failed to fetch groups for user', error);
|
||||
}
|
||||
}
|
||||
|
||||
async findById(id, includeMembers = false) {
|
||||
try {
|
||||
const options = {};
|
||||
|
||||
+24
-25
@@ -1,31 +1,30 @@
|
||||
const express = require('express');
|
||||
|
||||
// Il router è ora una factory function che riceve l'istanza di sequelize
|
||||
module.exports = (sequelize) => {
|
||||
const router = express.Router();
|
||||
|
||||
// --- Middleware per simulare un utente loggato ---
|
||||
const { User } = sequelize.models;
|
||||
const MOCK_USER_NAME = 'Mario Rossi';
|
||||
let MOCK_USER_ID = null;
|
||||
module.exports = sequelize => {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(async (req, res, next) => {
|
||||
if (!MOCK_USER_ID) {
|
||||
const user = await User.findOne({ where: { name: MOCK_USER_NAME } });
|
||||
if (user) {
|
||||
MOCK_USER_ID = user.id;
|
||||
}
|
||||
}
|
||||
req.userId = MOCK_USER_ID;
|
||||
next();
|
||||
});
|
||||
// --- Middleware per simulare un utente loggato ---
|
||||
const { User } = sequelize.models;
|
||||
const MOCK_USER_NAME = 'Mario Rossi';
|
||||
let MOCK_USER_ID = null;
|
||||
|
||||
// --- Monta i router da file separati ---
|
||||
router.use(require('./initial-data.routes')(sequelize));
|
||||
router.use('/bets', require('./bets.routes')(sequelize));
|
||||
router.use('/groups', require('./groups.routes')(sequelize));
|
||||
router.use('/friend-requests', require('./friends.routes')(sequelize));
|
||||
router.use('/users', require('./users.routes')(sequelize));
|
||||
|
||||
return router;
|
||||
router.use(async (req, res, next) => {
|
||||
if (!MOCK_USER_ID) {
|
||||
const user = await User.findOne({ where: { name: MOCK_USER_NAME } });
|
||||
if (user) {
|
||||
MOCK_USER_ID = user.id;
|
||||
}
|
||||
}
|
||||
req.userId = MOCK_USER_ID;
|
||||
next();
|
||||
});
|
||||
|
||||
// --- Monta i router da file separati ---
|
||||
router.use('/bets', require('./bets.routes')(sequelize));
|
||||
router.use('/groups', require('./groups.routes')(sequelize));
|
||||
router.use('/friend-requests', require('./friends.routes')(sequelize));
|
||||
router.use('/users', require('./users.routes')(sequelize));
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { Op } = require('sequelize');
|
||||
|
||||
// Modello Mongoose per le Scommesse
|
||||
const Bet = require('../models/bet.model');
|
||||
|
||||
module.exports = (sequelize) => {
|
||||
const { User, Friendship, FriendRequest } = sequelize.models;
|
||||
const { populateBetsWithUsers } = require('../utils/populateBets')(sequelize);
|
||||
|
||||
// GET /api/initial-data
|
||||
router.get('/initial-data', async (req, res) => {
|
||||
try {
|
||||
// --- Query PostgreSQL ---
|
||||
const currentUser = await User.findByPk(req.userId);
|
||||
const users = await User.findAll();
|
||||
|
||||
const friendships = await Friendship.findAll({
|
||||
where: { [Op.or]: [{ user1Id: req.userId }, { user2Id: req.userId }] },
|
||||
include: [{ model: User, as: 'user1' }, { model: User, as: 'user2' }]
|
||||
});
|
||||
const friends = friendships.map(f => (f.user1Id === req.userId ? f.user2 : f.user1));
|
||||
|
||||
const receivedFriendRequests = await FriendRequest.findAll({
|
||||
where: { toUserId: req.userId, status: 'PENDING' },
|
||||
include: [{ model: User, as: 'fromUser' }, { model: User, as: 'toUser' }]
|
||||
});
|
||||
|
||||
const sentFriendRequests = await FriendRequest.findAll({
|
||||
where: { fromUserId: req.userId, status: 'PENDING' },
|
||||
include: [{ model: User, as: 'fromUser' }, { model: User, as: 'toUser' }]
|
||||
});
|
||||
|
||||
const userGroups = await currentUser.getGroups({
|
||||
include: [{ model: User, as: 'members', through: { attributes: [] } }]
|
||||
});
|
||||
|
||||
// --- Query MongoDB ---
|
||||
const mongoBets = await Bet.find({ 'participants.user': req.userId }).sort({ startDate: -1 });
|
||||
const mongoPublicBets = await Bet.find({ isPublic: true, 'participants.user': { $ne: req.userId } });
|
||||
|
||||
// --- Data Stitching ---
|
||||
const bets = await populateBetsWithUsers(mongoBets);
|
||||
const publicBets = await populateBetsWithUsers(mongoPublicBets);
|
||||
|
||||
res.json({
|
||||
currentUser, users, friends, receivedFriendRequests, sentFriendRequests,
|
||||
bets, publicBets, groups: userGroups
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching initial data:', error);
|
||||
res.status(500).json({ message: 'Error fetching initial data', error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
@@ -1,14 +1,13 @@
|
||||
const express = require('express');
|
||||
const { validate } = require('../../middleware/validation');
|
||||
const {
|
||||
createBetSchema,
|
||||
updateBetSchema,
|
||||
} = require('../../shared/validators/schemas/bet.schema');
|
||||
const { createBetSchema, updateBetSchema } = require('../../shared/validators/schemas/bet.schema');
|
||||
|
||||
module.exports = ({ betsController }) => {
|
||||
const router = express.Router();
|
||||
|
||||
const controller = {
|
||||
getUserBets: (req, res, next) => betsController.getUserBets(req, res, next),
|
||||
getPublicBets: (req, res, next) => betsController.getPublicBets(req, res, next),
|
||||
getAllBets: (req, res, next) => betsController.getAllBets(req, res, next),
|
||||
getBetById: (req, res, next) => betsController.getBetById(req, res, next),
|
||||
createBet: (req, res, next) => betsController.createBet(req, res, next),
|
||||
@@ -16,6 +15,8 @@ module.exports = ({ betsController }) => {
|
||||
deleteBet: (req, res, next) => betsController.deleteBet(req, res, next),
|
||||
};
|
||||
|
||||
router.get('/user', controller.getUserBets);
|
||||
router.get('/public', controller.getPublicBets);
|
||||
router.get('/', controller.getAllBets);
|
||||
router.get('/:id', controller.getBetById);
|
||||
router.post('/', validate(createBetSchema), controller.createBet);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const express = require('express');
|
||||
|
||||
module.exports = (container) => {
|
||||
module.exports = container => {
|
||||
const router = express.Router();
|
||||
|
||||
const controllers = {
|
||||
@@ -8,14 +8,12 @@ module.exports = (container) => {
|
||||
betsController: container.resolve('betsController'),
|
||||
usersController: container.resolve('usersController'),
|
||||
friendsController: container.resolve('friendsController'),
|
||||
initialDataController: container.resolve('initialDataController'),
|
||||
};
|
||||
|
||||
router.use('/groups', require('./groups.routes')(controllers));
|
||||
router.use('/bets', require('./bets.routes')(controllers));
|
||||
router.use('/users', require('./users.routes')(controllers));
|
||||
router.use('/friend-requests', require('./friends.routes')(controllers));
|
||||
router.use('/initial-data', require('./initialData.routes')(controllers));
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
const express = require('express');
|
||||
|
||||
module.exports = ({ initialDataController }) => {
|
||||
const router = express.Router();
|
||||
|
||||
const controller = {
|
||||
getInitialData: (req, res, next) => initialDataController.getInitialData(req, res, next),
|
||||
};
|
||||
|
||||
router.get('/', controller.getInitialData);
|
||||
|
||||
return router;
|
||||
};
|
||||
@@ -6,9 +6,11 @@ module.exports = ({ usersController }) => {
|
||||
const router = express.Router();
|
||||
|
||||
const controller = {
|
||||
getCurrentUser: (req, res, next) => usersController.getCurrentUser(req, res, next),
|
||||
updateCurrentUser: (req, res, next) => usersController.updateCurrentUser(req, res, next),
|
||||
};
|
||||
|
||||
router.get('/current', controller.getCurrentUser);
|
||||
router.put('/current', validate(updateUserSchema), controller.updateCurrentUser);
|
||||
|
||||
return router;
|
||||
|
||||
@@ -4,8 +4,7 @@ const cors = require('cors');
|
||||
const config = require('./config');
|
||||
const setupContainer = require('./di/container');
|
||||
const errorHandler = require('./middleware/errorHandler');
|
||||
const mockAuthMiddleware = require('./middleware/auth.mock');
|
||||
const { seedDatabase } = require('./seed');
|
||||
const keycloakAuthMiddleware = require('./middleware/auth.keycloak');
|
||||
|
||||
const legacyApiRoutes = require('./routes/api');
|
||||
|
||||
@@ -21,20 +20,22 @@ const sequelize = container.resolve('sequelize');
|
||||
mongoose
|
||||
.connect(config.database.mongo.uri, config.database.mongo.options)
|
||||
.then(() => console.log('Connected to MongoDB'))
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.error('MongoDB connection error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
app.use('/api', mockAuthMiddleware(sequelize));
|
||||
app.use('/api', keycloakAuthMiddleware(sequelize));
|
||||
|
||||
app.use('/api/legacy', legacyApiRoutes(sequelize));
|
||||
|
||||
const v1Routes = require('./routes/v1');
|
||||
|
||||
app.use('/api/v1', v1Routes(container));
|
||||
app.use('/api', v1Routes(container));
|
||||
|
||||
const healthRoutes = require('./routes/health.routes');
|
||||
|
||||
app.use('/', healthRoutes({ sequelize, mongoose }));
|
||||
|
||||
app.use(errorHandler);
|
||||
@@ -43,11 +44,10 @@ sequelize
|
||||
.authenticate()
|
||||
.then(() => {
|
||||
console.log('Connected to PostgreSQL');
|
||||
return sequelize.sync({ force: true });
|
||||
return sequelize.sync({ alter: true });
|
||||
})
|
||||
.then(() => {
|
||||
console.log('PostgreSQL models synchronized');
|
||||
return seedDatabase(sequelize);
|
||||
})
|
||||
.then(() => {
|
||||
app.listen(config.port, () => {
|
||||
@@ -58,7 +58,7 @@ sequelize
|
||||
console.log(`Health check available at /health`);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(err => {
|
||||
console.error('PostgreSQL connection or sync error:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
const { ValidationError, ConflictError } = require('../shared/errors');
|
||||
const { Op } = require('sequelize');
|
||||
const { ValidationError, ConflictError } = require('../shared/errors');
|
||||
|
||||
class FriendsService {
|
||||
constructor({
|
||||
friendshipsRepository,
|
||||
friendRequestsRepository,
|
||||
usersRepository,
|
||||
sequelize,
|
||||
}) {
|
||||
constructor({ friendshipsRepository, friendRequestsRepository, usersRepository, sequelize }) {
|
||||
this.friendshipsRepository = friendshipsRepository;
|
||||
this.friendRequestsRepository = friendRequestsRepository;
|
||||
this.usersRepository = usersRepository;
|
||||
@@ -33,9 +28,7 @@ class FriendsService {
|
||||
});
|
||||
|
||||
if (!toUser) {
|
||||
throw new ValidationError(
|
||||
'User not found with provided name or email'
|
||||
);
|
||||
throw new ValidationError('User not found with provided name or email');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,18 +36,12 @@ class FriendsService {
|
||||
throw new ValidationError('Cannot send friend request to yourself');
|
||||
}
|
||||
|
||||
const friendshipExists = await this.friendshipsRepository.exists(
|
||||
fromUserId,
|
||||
toUser.id
|
||||
);
|
||||
const friendshipExists = await this.friendshipsRepository.exists(fromUserId, toUser.id);
|
||||
if (friendshipExists) {
|
||||
throw new ConflictError('You are already friends with this user');
|
||||
}
|
||||
|
||||
const existingRequest = await this.friendRequestsRepository.findExisting(
|
||||
fromUserId,
|
||||
toUser.id
|
||||
);
|
||||
const existingRequest = await this.friendRequestsRepository.findExisting(fromUserId, toUser.id);
|
||||
if (existingRequest) {
|
||||
throw new ConflictError('Friend request already exists');
|
||||
}
|
||||
@@ -73,6 +60,8 @@ class FriendsService {
|
||||
throw new ValidationError('This request has already been processed');
|
||||
}
|
||||
|
||||
const newFriend = request.fromUser;
|
||||
|
||||
const t = await this.sequelize.transaction();
|
||||
try {
|
||||
await this.friendRequestsRepository.updateStatus(requestId, 'ACCEPTED');
|
||||
@@ -80,7 +69,7 @@ class FriendsService {
|
||||
await this.friendRequestsRepository.delete(requestId);
|
||||
await t.commit();
|
||||
|
||||
return { message: 'Friend request accepted successfully' };
|
||||
return { message: 'Friend request accepted successfully', newFriend };
|
||||
} catch (error) {
|
||||
await t.rollback();
|
||||
throw error;
|
||||
|
||||
@@ -6,16 +6,8 @@ class GroupsService {
|
||||
this.usersRepository = usersRepository;
|
||||
}
|
||||
|
||||
async getAllGroups() {
|
||||
return await this.groupsRepository.findAll({
|
||||
include: [
|
||||
{
|
||||
model: this.usersRepository.User,
|
||||
as: 'members',
|
||||
through: { attributes: [] },
|
||||
},
|
||||
],
|
||||
});
|
||||
async getAllGroups(userId) {
|
||||
return await this.groupsRepository.findByUserId(userId);
|
||||
}
|
||||
|
||||
async getGroupById(groupId) {
|
||||
|
||||
Reference in New Issue
Block a user