websockets implemented
This commit is contained in:
@@ -7,9 +7,11 @@ import navigationRouter from './routers/navigationRouter.js';
|
|||||||
import settingsRouter from './routers/settingsRouter.js';
|
import settingsRouter from './routers/settingsRouter.js';
|
||||||
import adminRouter from './routers/adminRouter.js';
|
import adminRouter from './routers/adminRouter.js';
|
||||||
import contactRouter from './routers/contactRouter.js';
|
import contactRouter from './routers/contactRouter.js';
|
||||||
import cors from 'cors';
|
|
||||||
import socialnetworkRouter from './routers/socialnetworkRouter.js';
|
import socialnetworkRouter from './routers/socialnetworkRouter.js';
|
||||||
import forumRouter from './routers/forumRouter.js';
|
import forumRouter from './routers/forumRouter.js';
|
||||||
|
import falukantRouter from './routers/falukantRouter.js';
|
||||||
|
import friendshipRouter from './routers/friendshipRouter.js';
|
||||||
|
import cors from 'cors';
|
||||||
import './jobs/sessionCleanup.js';
|
import './jobs/sessionCleanup.js';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
@@ -36,6 +38,8 @@ app.use('/images', express.static(path.join(__dirname, '../frontend/public/image
|
|||||||
app.use('/api/contact', contactRouter);
|
app.use('/api/contact', contactRouter);
|
||||||
app.use('/api/socialnetwork', socialnetworkRouter);
|
app.use('/api/socialnetwork', socialnetworkRouter);
|
||||||
app.use('/api/forum', forumRouter);
|
app.use('/api/forum', forumRouter);
|
||||||
|
app.use('/api/falukant', falukantRouter);
|
||||||
|
app.use('/api/friendships', friendshipRouter);
|
||||||
|
|
||||||
app.use((req, res) => {
|
app.use((req, res) => {
|
||||||
res.status(404).send('404 Not Found');
|
res.status(404).send('404 Not Found');
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class AuthController {
|
|||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
try {
|
try {
|
||||||
const result = await userService.loginUser({ username, password });
|
const result = await userService.loginUser({ username, password });
|
||||||
|
console.log('User logged in successfully', result);
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message === 'credentialsinvalid') {
|
if (error.message === 'credentialsinvalid') {
|
||||||
@@ -37,6 +38,7 @@ class AuthController {
|
|||||||
async logout(req, res) {
|
async logout(req, res) {
|
||||||
const { userid: hashedUserId } = req.headers;
|
const { userid: hashedUserId } = req.headers;
|
||||||
await userService.logoutUser(hashedUserId);
|
await userService.logoutUser(hashedUserId);
|
||||||
|
res.status(200).json({ result: 'loggedout' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async forgotPassword(req, res) {
|
async forgotPassword(req, res) {
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
import {
|
import chatService from '../services/chatService.js';
|
||||||
getMessages as getMessagesService,
|
|
||||||
findMatch,
|
|
||||||
registerUser as registerUserService,
|
|
||||||
addMessage,
|
|
||||||
endChat,
|
|
||||||
removeUser as removeUserService
|
|
||||||
} from '../services/chatService.js';
|
|
||||||
|
|
||||||
class ChatController {
|
class ChatController {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -15,12 +8,15 @@ class ChatController {
|
|||||||
this.sendMessage = this.sendMessage.bind(this);
|
this.sendMessage = this.sendMessage.bind(this);
|
||||||
this.stopChat = this.stopChat.bind(this);
|
this.stopChat = this.stopChat.bind(this);
|
||||||
this.removeUser = this.removeUser.bind(this);
|
this.removeUser = this.removeUser.bind(this);
|
||||||
|
this.initOneToOne = this.initOneToOne.bind(this);
|
||||||
|
this.sendOneToOneMessage = this.sendOneToOneMessage.bind(this);
|
||||||
|
this.getOneToOneMessageHistory = this.getOneToOneMessageHistory.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMessages(req, res) {
|
async getMessages(req, res) {
|
||||||
const { to, from } = req.body;
|
const { to, from } = req.body;
|
||||||
try {
|
try {
|
||||||
const messages = await getMessagesService(to, from);
|
const messages = await chatService.getMessages(to, from);
|
||||||
res.status(200).json(messages);
|
res.status(200).json(messages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
@@ -30,7 +26,7 @@ class ChatController {
|
|||||||
async findRandomChatMatch(req, res) {
|
async findRandomChatMatch(req, res) {
|
||||||
const { genders, age, id } = req.body;
|
const { genders, age, id } = req.body;
|
||||||
try {
|
try {
|
||||||
const match = await findMatch(genders, age, id);
|
const match = await chatService.findMatch(genders, age, id);
|
||||||
if (match) {
|
if (match) {
|
||||||
res.status(200).json({ status: 'matched', user: match });
|
res.status(200).json({ status: 'matched', user: match });
|
||||||
} else {
|
} else {
|
||||||
@@ -44,7 +40,7 @@ class ChatController {
|
|||||||
async registerUser(req, res) {
|
async registerUser(req, res) {
|
||||||
const { gender, age } = req.body;
|
const { gender, age } = req.body;
|
||||||
try {
|
try {
|
||||||
const userId = await registerUserService(gender, age);
|
const userId = await chatService.registerUser(gender, age);
|
||||||
res.status(200).json({ id: userId });
|
res.status(200).json({ id: userId });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
@@ -54,7 +50,7 @@ class ChatController {
|
|||||||
async sendMessage(req, res) {
|
async sendMessage(req, res) {
|
||||||
const { from, to, text } = req.body;
|
const { from, to, text } = req.body;
|
||||||
try {
|
try {
|
||||||
const message = await addMessage(from, to, text);
|
const message = await chatService.addMessage(from, to, text);
|
||||||
res.status(200).json(message);
|
res.status(200).json(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
@@ -64,7 +60,7 @@ class ChatController {
|
|||||||
async removeUser(req, res) {
|
async removeUser(req, res) {
|
||||||
const { id } = req.body;
|
const { id } = req.body;
|
||||||
try {
|
try {
|
||||||
await removeUserService(id);
|
await chatService.removeUser(id);
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
@@ -74,12 +70,43 @@ class ChatController {
|
|||||||
async stopChat(req, res) {
|
async stopChat(req, res) {
|
||||||
const { id } = req.body;
|
const { id } = req.body;
|
||||||
try {
|
try {
|
||||||
await endChat(id);
|
await chatService.endChat(id);
|
||||||
res.sendStatus(200);
|
res.sendStatus(200);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async initOneToOne(req, res) {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { partnerHashId } = req.body;
|
||||||
|
try {
|
||||||
|
await chatService.initOneToOne(hashedUserId, partnerHashId);
|
||||||
|
res.status(200).json({ message: 'One-to-one chat initialization is pending implementation.' });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendOneToOneMessage(req, res) {
|
||||||
|
const { user1HashId, user2HashId, message } = req.body;
|
||||||
|
try {
|
||||||
|
await chatService.sendOneToOneMessage(user1HashId, user2HashId, message);
|
||||||
|
res.status(200).json({ status: 'message sent' });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOneToOneMessageHistory(req, res) {
|
||||||
|
const { user1HashId, user2HashId } = req.query;
|
||||||
|
try {
|
||||||
|
const history = await chatService.getOneToOneMessageHistory(user1HashId, user2HashId);
|
||||||
|
res.status(200).json({ history });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChatController;
|
export default ChatController;
|
||||||
|
|||||||
18
backend/controllers/falukantController.js
Normal file
18
backend/controllers/falukantController.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import * as falukantService from '../services/falukantService.js';
|
||||||
|
|
||||||
|
class FalukantController {
|
||||||
|
constructor() {
|
||||||
|
this.exampleMethod = this.exampleMethod.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async exampleMethod(req, res) {
|
||||||
|
try {
|
||||||
|
const result = await falukantService.exampleMethod();
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FalukantController;
|
||||||
69
backend/controllers/friendshipController.js
Normal file
69
backend/controllers/friendshipController.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import friendshipService from '../services/friendshipService.js';
|
||||||
|
|
||||||
|
const friendshipController = {
|
||||||
|
async endFriendship(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { friendUserId } = req.body;
|
||||||
|
|
||||||
|
await friendshipService.endFriendship(hashedUserId, friendUserId);
|
||||||
|
res.status(200).json({ message: 'Friendship ended successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in endFriendship:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async acceptFriendship(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { friendUserId } = req.body;
|
||||||
|
|
||||||
|
await friendshipService.acceptFriendship(hashedUserId, friendUserId);
|
||||||
|
res.status(200).json({ message: 'Friendship accepted successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in acceptFriendship:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async rejectFriendship(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { friendUserId } = req.body;
|
||||||
|
|
||||||
|
await friendshipService.rejectFriendship(hashedUserId, friendUserId);
|
||||||
|
res.status(200).json({ message: 'Friendship rejected successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in rejectFriendship:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async withdrawRequest(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { friendUserId } = req.body;
|
||||||
|
await friendshipService.withdrawRequest(hashedUserId, friendUserId);
|
||||||
|
res.status(200).json({ message: 'Friendship request withdrawn successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in withdrawRequest:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getFriendships(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { acceptedOnly } = req.query;
|
||||||
|
console.log('Friendships:', acceptedOnly);
|
||||||
|
const friendships = await friendshipService.getFriendships(hashedUserId, acceptedOnly === 'true');
|
||||||
|
res.status(200).json(friendships);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in getFriendships:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default friendshipController;
|
||||||
@@ -16,7 +16,7 @@ const menuStructure = {
|
|||||||
children: {
|
children: {
|
||||||
manageFriends: {
|
manageFriends: {
|
||||||
visible: ["all"],
|
visible: ["all"],
|
||||||
path: "/socialnetwork/friends",
|
path: "/friends",
|
||||||
icon: "friends24.png"
|
icon: "friends24.png"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -26,12 +26,14 @@ class SocialNetworkController {
|
|||||||
this.addFriend = this.addFriend.bind(this);
|
this.addFriend = this.addFriend.bind(this);
|
||||||
this.removeFriend = this.removeFriend.bind(this);
|
this.removeFriend = this.removeFriend.bind(this);
|
||||||
this.acceptFriendship = this.acceptFriendship.bind(this);
|
this.acceptFriendship = this.acceptFriendship.bind(this);
|
||||||
|
this.getLoggedInFriends = this.getLoggedInFriends.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async userSearch(req, res) {
|
async userSearch(req, res) {
|
||||||
try {
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
const { username, ageFrom, ageTo, genders } = req.body;
|
const { username, ageFrom, ageTo, genders } = req.body;
|
||||||
const users = await this.socialNetworkService.searchUsers({ username, ageFrom, ageTo, genders });
|
const users = await this.socialNetworkService.searchUsers({ hashedUserId, username, ageFrom, ageTo, genders });
|
||||||
res.status(200).json(users);
|
res.status(200).json(users);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in userSearch:', error);
|
console.error('Error in userSearch:', error);
|
||||||
@@ -295,6 +297,7 @@ class SocialNetworkController {
|
|||||||
try {
|
try {
|
||||||
const { userid: hashedUserid } = req.headers;
|
const { userid: hashedUserid } = req.headers;
|
||||||
const { friendUserid } = req.body;
|
const { friendUserid } = req.body;
|
||||||
|
console.log('--------', friendUserid, hashedUserid);
|
||||||
await this.socialNetworkService.addFriend(hashedUserid, friendUserid);
|
await this.socialNetworkService.addFriend(hashedUserid, friendUserid);
|
||||||
res.status(201).json({ message: 'added' });
|
res.status(201).json({ message: 'added' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -326,6 +329,20 @@ class SocialNetworkController {
|
|||||||
res.status(500).json({ error: error.message });
|
res.status(500).json({ error: error.message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLoggedInFriends(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: userId } = req.headers;
|
||||||
|
if (!userId) {
|
||||||
|
return res.status(400).json({ error: 'Missing user ID' });
|
||||||
|
}
|
||||||
|
const loggedInFriends = await this.socialNetworkService.getLoggedInFriends(userId);
|
||||||
|
res.status(200).json(loggedInFriends);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in getLoggedInFriends:', error);
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SocialNetworkController;
|
export default SocialNetworkController;
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import User from '../models/community/user.js';
|
|||||||
import { updateUserTimestamp } from '../utils/redis.js';
|
import { updateUserTimestamp } from '../utils/redis.js';
|
||||||
|
|
||||||
export const authenticate = async (req, res, next) => {
|
export const authenticate = async (req, res, next) => {
|
||||||
const userId = req.headers.userid;
|
const { userid: userId, authcode: authCode } = req.headers;
|
||||||
const authCode = req.headers.authcode;
|
|
||||||
if (!userId || !authCode) {
|
if (!userId || !authCode) {
|
||||||
return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
|
return res.status(401).json({ error: 'Unauthorized: Missing credentials' });
|
||||||
}
|
}
|
||||||
@@ -12,7 +11,7 @@ export const authenticate = async (req, res, next) => {
|
|||||||
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
|
return res.status(401).json({ error: 'Unauthorized: Invalid credentials' });
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await updateUserTimestamp(userId);
|
await updateUserTimestamp(user.id);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
|
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ import ForumPermission from './forum/forum_permission.js';
|
|||||||
import ForumUserPermission from './forum/forum_user_permission.js';
|
import ForumUserPermission from './forum/forum_user_permission.js';
|
||||||
import ForumForumPermission from './forum/forum_forum_permission.js';
|
import ForumForumPermission from './forum/forum_forum_permission.js';
|
||||||
import Friendship from './community/friendship.js';
|
import Friendship from './community/friendship.js';
|
||||||
|
import FalukantUser from './falukant/data/user.js';
|
||||||
|
import RegionType from './falukant/type/region.js';
|
||||||
|
import RegionData from './falukant/data/region.js';
|
||||||
|
|
||||||
export default function setupAssociations() {
|
export default function setupAssociations() {
|
||||||
// UserParam related associations
|
// UserParam related associations
|
||||||
@@ -40,12 +43,13 @@ export default function setupAssociations() {
|
|||||||
User.hasMany(UserParam, { foreignKey: 'userId', as: 'user_params' });
|
User.hasMany(UserParam, { foreignKey: 'userId', as: 'user_params' });
|
||||||
UserParam.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
UserParam.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||||
|
|
||||||
// UserRight related associations
|
UserParamValue.belongsTo(UserParamType, { foreignKey: 'userParamTypeId', as: 'user_param_value_type' });
|
||||||
|
UserParamType.hasMany(UserParamValue, { foreignKey: 'userParamTypeId', as: 'user_param_type_value' });
|
||||||
|
|
||||||
UserRight.belongsTo(User, { foreignKey: 'userId', as: 'user_with_rights' });
|
UserRight.belongsTo(User, { foreignKey: 'userId', as: 'user_with_rights' });
|
||||||
UserRight.belongsTo(UserRightType, { foreignKey: 'rightTypeId', as: 'rightType' });
|
UserRight.belongsTo(UserRightType, { foreignKey: 'rightTypeId', as: 'rightType' });
|
||||||
UserRightType.hasMany(UserRight, { foreignKey: 'rightTypeId', as: 'user_rights' });
|
UserRightType.hasMany(UserRight, { foreignKey: 'rightTypeId', as: 'user_rights' });
|
||||||
|
|
||||||
// UserParamVisibility related associations
|
|
||||||
UserParam.hasMany(UserParamVisibility, { foreignKey: 'param_id', as: 'param_visibilities' });
|
UserParam.hasMany(UserParamVisibility, { foreignKey: 'param_id', as: 'param_visibilities' });
|
||||||
UserParamVisibility.belongsTo(UserParam, { foreignKey: 'param_id', as: 'param' });
|
UserParamVisibility.belongsTo(UserParam, { foreignKey: 'param_id', as: 'param' });
|
||||||
|
|
||||||
@@ -162,4 +166,19 @@ export default function setupAssociations() {
|
|||||||
Friendship.belongsTo(User, { foreignKey: 'user2Id', as: 'friendReceiver' });
|
Friendship.belongsTo(User, { foreignKey: 'user2Id', as: 'friendReceiver' });
|
||||||
User.hasMany(Friendship, { foreignKey: 'user1Id', as: 'friendSender' });
|
User.hasMany(Friendship, { foreignKey: 'user1Id', as: 'friendSender' });
|
||||||
User.hasMany(Friendship, { foreignKey: 'user2Id', as: 'friendReceiver' });
|
User.hasMany(Friendship, { foreignKey: 'user2Id', as: 'friendReceiver' });
|
||||||
|
|
||||||
|
User.hasMany(FalukantUser, { foreignKey: 'userId', as: 'falukantData' });
|
||||||
|
FalukantUser.belongsTo(User, { foreignKey: 'userId', as: 'user' });
|
||||||
|
|
||||||
|
RegionType.hasMany(RegionType, { foreignKey: 'parentId', as: 'children' });
|
||||||
|
RegionType.belongsTo(RegionType, { foreignKey: 'parentId', as: 'parent' });
|
||||||
|
|
||||||
|
RegionData.hasMany(RegionData, { foreignKey: 'parentId', as: 'children' });
|
||||||
|
RegionData.belongsTo(RegionData, { foreignKey: 'parentId', as: 'parent' });
|
||||||
|
|
||||||
|
RegionData.belongsTo(RegionType, { foreignKey: 'regionTypeId', as: 'regionType' });
|
||||||
|
RegionType.hasMany(RegionData, { foreignKey: 'regionTypeId', as: 'regions' });
|
||||||
|
|
||||||
|
FalukantUser.belongsTo(RegionData, { foreignKey: 'mainBranchRegionId', as: 'mainBranchRegion' });
|
||||||
|
RegionData.hasMany(FalukantUser, { foreignKey: 'mainBranchRegionId', as: 'users' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,22 @@ const User = sequelize.define('user', {
|
|||||||
unique: true,
|
unique: true,
|
||||||
set(value) {
|
set(value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.setDataValue('email', Buffer.from(encrypt(value), 'hex'));
|
const encrypted = encrypt(value);
|
||||||
|
this.setDataValue('email', encrypted);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
get() {
|
||||||
|
const encrypted = this.getDataValue('email');
|
||||||
|
if (encrypted) {
|
||||||
|
return decrypt(encrypted);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
salt: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: () => crypto.randomBytes(16).toString('hex')
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
@@ -58,11 +71,6 @@ const User = sequelize.define('user', {
|
|||||||
user.hashedId = hashedId;
|
user.hashedId = hashedId;
|
||||||
await user.save();
|
await user.save();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
getterMethods: {
|
|
||||||
email() {
|
|
||||||
return decrypt(this.getDataValue('email').toString('hex'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { sequelize } from '../../utils/sequelize.js';
|
|||||||
import { DataTypes } from 'sequelize';
|
import { DataTypes } from 'sequelize';
|
||||||
import User from './user.js';
|
import User from './user.js';
|
||||||
import UserParamType from '../type/user_param.js';
|
import UserParamType from '../type/user_param.js';
|
||||||
import { encrypt, decrypt, generateIv } from '../../utils/encryption.js';
|
import { encrypt, decrypt } from '../../utils/encryption.js';
|
||||||
|
|
||||||
const UserParam = sequelize.define('user_param', {
|
const UserParam = sequelize.define('user_param', {
|
||||||
userId: {
|
userId: {
|
||||||
@@ -10,27 +10,29 @@ const UserParam = sequelize.define('user_param', {
|
|||||||
allowNull: false,
|
allowNull: false,
|
||||||
references: {
|
references: {
|
||||||
model: User,
|
model: User,
|
||||||
key: 'id'
|
key: 'id',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
paramTypeId: {
|
paramTypeId: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
references: {
|
references: {
|
||||||
model: UserParamType,
|
model: UserParamType,
|
||||||
key: 'id'
|
key: 'id',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
type: DataTypes.STRING,
|
type: DataTypes.STRING,
|
||||||
allowNull: false,
|
allowNull: false,
|
||||||
set(value) {
|
set(value) {
|
||||||
|
console.log('.... [set param value]', value);
|
||||||
if (value) {
|
if (value) {
|
||||||
try {
|
try {
|
||||||
const iv = generateIv();
|
const encrypted = encrypt(value.toString());
|
||||||
this.setDataValue('iv', iv.toString('hex'));
|
console.log('.... [encrypted param value]', encrypted);
|
||||||
this.setDataValue('value', encrypt(value.toString(), iv));
|
this.setDataValue('value', encrypted);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('.... Error encrypting param value:', error);
|
||||||
this.setDataValue('value', '');
|
this.setDataValue('value', '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,17 +40,13 @@ const UserParam = sequelize.define('user_param', {
|
|||||||
get() {
|
get() {
|
||||||
try {
|
try {
|
||||||
const value = this.getDataValue('value');
|
const value = this.getDataValue('value');
|
||||||
const iv = Buffer.from(this.getDataValue('iv'), 'hex');
|
return decrypt(value);
|
||||||
return decrypt(value, iv);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('.... Error decrypting param value:', error);
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
iv: {
|
},
|
||||||
type: DataTypes.STRING,
|
|
||||||
allowNull: false
|
|
||||||
}
|
|
||||||
}, {
|
}, {
|
||||||
tableName: 'user_param',
|
tableName: 'user_param',
|
||||||
schema: 'community',
|
schema: 'community',
|
||||||
@@ -56,9 +54,9 @@ const UserParam = sequelize.define('user_param', {
|
|||||||
indexes: [
|
indexes: [
|
||||||
{
|
{
|
||||||
unique: true,
|
unique: true,
|
||||||
fields: ['user_id', 'param_type_id']
|
fields: ['user_id', 'param_type_id'],
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export default UserParam;
|
export default UserParam;
|
||||||
|
|||||||
39
backend/models/falukant/data/region.js
Normal file
39
backend/models/falukant/data/region.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
import RegionType from '../type/region.js';
|
||||||
|
|
||||||
|
class RegionData extends Model { }
|
||||||
|
|
||||||
|
RegionData.init({
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
regionTypeId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: RegionType,
|
||||||
|
key: 'id',
|
||||||
|
schema: 'falukant_type'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'region',
|
||||||
|
key: 'id',
|
||||||
|
schema: 'falukant_data',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'RegionData',
|
||||||
|
tableName: 'region',
|
||||||
|
schema: 'falukant_data',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RegionData;
|
||||||
57
backend/models/falukant/data/user.js
Normal file
57
backend/models/falukant/data/user.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
import RegionData from './region.js';
|
||||||
|
|
||||||
|
class FalukantUser extends Model { }
|
||||||
|
|
||||||
|
FalukantUser.init({
|
||||||
|
userId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: {
|
||||||
|
tableName: 'user',
|
||||||
|
schema: 'community'
|
||||||
|
},
|
||||||
|
key: 'id'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
money: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0.00,
|
||||||
|
},
|
||||||
|
creditAmount: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0.00,
|
||||||
|
},
|
||||||
|
todayCreditTaken: {
|
||||||
|
type: DataTypes.DECIMAL(10, 2),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0.00,
|
||||||
|
},
|
||||||
|
creditInterestRate: {
|
||||||
|
type: DataTypes.DECIMAL(5, 2),
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 0.00,
|
||||||
|
},
|
||||||
|
mainBranchRegionId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: RegionData,
|
||||||
|
key: 'id',
|
||||||
|
schema: 'falukant_data'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'FalukantUser',
|
||||||
|
tableName: 'falukant_user',
|
||||||
|
schema: 'falukant_data',
|
||||||
|
timestamps: true,
|
||||||
|
underscored: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default FalukantUser;
|
||||||
29
backend/models/falukant/type/region.js
Normal file
29
backend/models/falukant/type/region.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
|
||||||
|
class RegionType extends Model { }
|
||||||
|
|
||||||
|
RegionType.init({
|
||||||
|
labelTr: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
parentId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: 'region',
|
||||||
|
key: 'id',
|
||||||
|
schema: 'falukant_type',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'RegionType',
|
||||||
|
tableName: 'region',
|
||||||
|
schema: 'falukant_type',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default RegionType;
|
||||||
@@ -32,6 +32,9 @@ import MessageHistory from './forum/message_history.js';
|
|||||||
import MessageImage from './forum/message_image.js';
|
import MessageImage from './forum/message_image.js';
|
||||||
import ForumForumPermission from './forum/forum_forum_permission.js';
|
import ForumForumPermission from './forum/forum_forum_permission.js';
|
||||||
import Friendship from './community/friendship.js';
|
import Friendship from './community/friendship.js';
|
||||||
|
import FalukantUser from './falukant/data/user.js';
|
||||||
|
import RegionType from './falukant/type/region.js';
|
||||||
|
import RegionData from './falukant/data/region.js';
|
||||||
|
|
||||||
const models = {
|
const models = {
|
||||||
SettingsType,
|
SettingsType,
|
||||||
@@ -68,6 +71,9 @@ const models = {
|
|||||||
MessageHistory,
|
MessageHistory,
|
||||||
MessageImage,
|
MessageImage,
|
||||||
Friendship,
|
Friendship,
|
||||||
|
RegionType,
|
||||||
|
RegionData,
|
||||||
|
FalukantUser,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default models;
|
export default models;
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import { DataTypes } from 'sequelize';
|
|||||||
import UserParam from './user_param.js';
|
import UserParam from './user_param.js';
|
||||||
|
|
||||||
const UserParamValue = sequelize.define('user_param_value', {
|
const UserParamValue = sequelize.define('user_param_value', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true
|
||||||
|
},
|
||||||
userParamTypeId: {
|
userParamTypeId: {
|
||||||
type: DataTypes.INTEGER,
|
type: DataTypes.INTEGER,
|
||||||
allowNull: false
|
allowNull: false
|
||||||
|
|||||||
105
backend/package-lock.json
generated
105
backend/package-lock.json
generated
@@ -29,7 +29,8 @@
|
|||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0",
|
||||||
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sequelize-cli": "^6.6.2"
|
"sequelize-cli": "^6.6.2"
|
||||||
@@ -1200,9 +1201,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cookie": {
|
"node_modules/cookie": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
|
||||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
@@ -1481,16 +1482,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io": {
|
"node_modules/engine.io": {
|
||||||
"version": "6.5.5",
|
"version": "6.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz",
|
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz",
|
||||||
"integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==",
|
"integrity": "sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/cookie": "^0.4.1",
|
"@types/cookie": "^0.4.1",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/node": ">=10.0.0",
|
"@types/node": ">=10.0.0",
|
||||||
"accepts": "~1.3.4",
|
"accepts": "~1.3.4",
|
||||||
"base64id": "2.0.0",
|
"base64id": "2.0.0",
|
||||||
"cookie": "~0.4.1",
|
"cookie": "~0.7.2",
|
||||||
"cors": "~2.8.5",
|
"cors": "~2.8.5",
|
||||||
"debug": "~4.3.1",
|
"debug": "~4.3.1",
|
||||||
"engine.io-parser": "~5.2.1",
|
"engine.io-parser": "~5.2.1",
|
||||||
@@ -1509,13 +1510,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/engine.io/node_modules/cookie": {
|
"node_modules/engine.io/node_modules/cookie": {
|
||||||
"version": "0.4.2",
|
"version": "0.7.2",
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
||||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io/node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -1646,16 +1667,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.21.0",
|
"version": "4.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||||
"integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
|
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
"body-parser": "1.20.3",
|
"body-parser": "1.20.3",
|
||||||
"content-disposition": "0.5.4",
|
"content-disposition": "0.5.4",
|
||||||
"content-type": "~1.0.4",
|
"content-type": "~1.0.4",
|
||||||
"cookie": "0.6.0",
|
"cookie": "0.7.1",
|
||||||
"cookie-signature": "1.0.6",
|
"cookie-signature": "1.0.6",
|
||||||
"debug": "2.6.9",
|
"debug": "2.6.9",
|
||||||
"depd": "2.0.0",
|
"depd": "2.0.0",
|
||||||
@@ -2497,26 +2518,6 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/jsdom/node_modules/ws": {
|
|
||||||
"version": "8.18.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
|
||||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"bufferutil": "^4.0.1",
|
|
||||||
"utf-8-validate": ">=5.0.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"bufferutil": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"utf-8-validate": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
@@ -3647,15 +3648,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/socket.io": {
|
"node_modules/socket.io": {
|
||||||
"version": "4.7.5",
|
"version": "4.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
|
||||||
"integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==",
|
"integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.4",
|
"accepts": "~1.3.4",
|
||||||
"base64id": "~2.0.0",
|
"base64id": "~2.0.0",
|
||||||
"cors": "~2.8.5",
|
"cors": "~2.8.5",
|
||||||
"debug": "~4.3.2",
|
"debug": "~4.3.2",
|
||||||
"engine.io": "~6.5.2",
|
"engine.io": "~6.6.0",
|
||||||
"socket.io-adapter": "~2.5.2",
|
"socket.io-adapter": "~2.5.2",
|
||||||
"socket.io-parser": "~4.2.4"
|
"socket.io-parser": "~4.2.4"
|
||||||
},
|
},
|
||||||
@@ -3672,6 +3673,26 @@
|
|||||||
"ws": "~8.17.1"
|
"ws": "~8.17.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-adapter/node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/socket.io-parser": {
|
"node_modules/socket.io-parser": {
|
||||||
"version": "4.2.4",
|
"version": "4.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
@@ -4120,9 +4141,9 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.17.1",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,8 @@
|
|||||||
"sequelize": "^6.37.3",
|
"sequelize": "^6.37.3",
|
||||||
"sharp": "^0.33.5",
|
"sharp": "^0.33.5",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0",
|
||||||
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"sequelize-cli": "^6.6.2"
|
"sequelize-cli": "^6.6.2"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import ChatController from '../controllers/chatController.js';
|
import ChatController from '../controllers/chatController.js';
|
||||||
|
import { authenticate } from '../middleware/authMiddleware.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
const chatController = new ChatController();
|
const chatController = new ChatController();
|
||||||
@@ -10,5 +11,8 @@ router.post('/register', chatController.registerUser);
|
|||||||
router.post('/sendMessage', chatController.sendMessage);
|
router.post('/sendMessage', chatController.sendMessage);
|
||||||
router.post('/leave', chatController.stopChat);
|
router.post('/leave', chatController.stopChat);
|
||||||
router.post('/exit', chatController.removeUser);
|
router.post('/exit', chatController.removeUser);
|
||||||
|
router.post('/initOneToOne', authenticate, chatController.initOneToOne);
|
||||||
|
router.post('/oneToOne/sendMessage', authenticate, chatController.sendOneToOneMessage); // Neue Route zum Senden einer Nachricht
|
||||||
|
router.get('/oneToOne/messageHistory', authenticate, chatController.getOneToOneMessageHistory); // Neue Route zum Abrufen der Nachrichtengeschichte
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
9
backend/routers/falukantRouter.js
Normal file
9
backend/routers/falukantRouter.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import express from 'express';
|
||||||
|
import FalukantController from '../controllers/falukantController.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
const falukantController = new FalukantController();
|
||||||
|
|
||||||
|
router.get('/example', falukantController.exampleMethod);
|
||||||
|
|
||||||
|
export default router;
|
||||||
15
backend/routers/friendshipRouter.js
Normal file
15
backend/routers/friendshipRouter.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Router } from 'express';
|
||||||
|
import friendshipController from '../controllers/friendshipController.js';
|
||||||
|
import { authenticate } from '../middleware/authMiddleware.js';
|
||||||
|
|
||||||
|
const friendshipRouter = Router();
|
||||||
|
|
||||||
|
friendshipRouter.use(authenticate);
|
||||||
|
|
||||||
|
friendshipRouter.post('/end', friendshipController.endFriendship);
|
||||||
|
friendshipRouter.post('/accept', friendshipController.acceptFriendship);
|
||||||
|
friendshipRouter.post('/reject', friendshipController.rejectFriendship);
|
||||||
|
friendshipRouter.post('/withdraw', friendshipController.withdrawRequest);
|
||||||
|
friendshipRouter.get('/', friendshipController.getFriendships);
|
||||||
|
|
||||||
|
export default friendshipRouter;
|
||||||
@@ -32,5 +32,6 @@ router.get('/diary/:page', socialNetworkController.getDiaryEntries);
|
|||||||
router.post('/friend', socialNetworkController.addFriend);
|
router.post('/friend', socialNetworkController.addFriend);
|
||||||
router.delete('/friend/:friendUserId', socialNetworkController.removeFriend);
|
router.delete('/friend/:friendUserId', socialNetworkController.removeFriend);
|
||||||
router.put('/friend/:friendUserId', socialNetworkController.acceptFriendship);
|
router.put('/friend/:friendUserId', socialNetworkController.acceptFriendship);
|
||||||
|
router.get('/friends/loggedin', socialNetworkController.getLoggedInFriends);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -1,44 +1,17 @@
|
|||||||
import http from 'http';
|
import http from 'http';
|
||||||
import { Server } from 'socket.io';
|
|
||||||
import amqp from 'amqplib/callback_api.js';
|
|
||||||
import app from './app.js';
|
import app from './app.js';
|
||||||
|
import { setupWebSocket } from './utils/socket.js';
|
||||||
import { syncDatabase } from './utils/syncDatabase.js';
|
import { syncDatabase } from './utils/syncDatabase.js';
|
||||||
|
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const io = new Server(server);
|
|
||||||
|
|
||||||
const RABBITMQ_URL = 'amqp://localhost';
|
setupWebSocket(server);
|
||||||
const QUEUE = 'chat_messages';
|
|
||||||
|
|
||||||
amqp.connect(RABBITMQ_URL, (err, connection) => {
|
syncDatabase().then(() => {
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.createChannel((err, channel) => {
|
|
||||||
if (err) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
channel.assertQueue(QUEUE, { durable: false });
|
|
||||||
io.on('connection', (socket) => {
|
|
||||||
channel.consume(QUEUE, (msg) => {
|
|
||||||
const message = JSON.parse(msg.content.toString());
|
|
||||||
io.emit('newMessage', message);
|
|
||||||
}, { noAck: true });
|
|
||||||
socket.on('newMessage', (message) => {
|
|
||||||
channel.sendToQueue(QUEUE, Buffer.from(JSON.stringify(message)));
|
|
||||||
});
|
|
||||||
socket.on('disconnect', () => {
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
syncDatabase().then(() => {
|
|
||||||
server.listen(3001, () => {
|
server.listen(3001, () => {
|
||||||
console.log('Server is running on port 3001');
|
console.log('Server is running on port 3001');
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.error('Failed to sync database:', err);
|
console.error('Failed to sync database:', err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,9 +9,11 @@ import UserRightType from '../models/type/user_right.js';
|
|||||||
|
|
||||||
class BaseService {
|
class BaseService {
|
||||||
async getUserByHashedId(hashedId) {
|
async getUserByHashedId(hashedId) {
|
||||||
const user = await User.findOne({ where: { hashedId } });
|
console.log('async getUserByHashedId: ', hashedId);
|
||||||
|
const user = await User.findOne({ where: { hashedId: hashedId } });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('User not found');
|
console.log('User not found: ', hashedId);
|
||||||
|
throw new Error('User not found: ', hashedId);
|
||||||
}
|
}
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,64 @@ import UserParam from '../models/community/user_param.js';
|
|||||||
import UserParamType from '../models/type/user_param.js';
|
import UserParamType from '../models/type/user_param.js';
|
||||||
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
|
import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js';
|
||||||
import { sequelize } from '../utils/sequelize.js';
|
import { sequelize } from '../utils/sequelize.js';
|
||||||
|
import { Op } from 'sequelize';
|
||||||
import { setUserSession, deleteUserSession } from '../utils/redis.js';
|
import { setUserSession, deleteUserSession } from '../utils/redis.js';
|
||||||
|
import { encrypt } from '../utils/encryption.js';
|
||||||
|
import { notifyUser } from '../utils/socket.js';
|
||||||
|
import Friendship from '../models/community/friendship.js';
|
||||||
|
|
||||||
const saltRounds = 10;
|
const saltRounds = 10;
|
||||||
|
|
||||||
export const registerUser = async ({ email, username, password, language }) => {
|
const getFriends = async (userId) => {
|
||||||
const encryptionKey = process.env.SECRET_KEY;
|
console.log('getFriends', userId);
|
||||||
const results = await sequelize.query(
|
try {
|
||||||
`SELECT * FROM community.user WHERE pgp_sym_decrypt(email::bytea, :key) = :email`,
|
const friendships = await Friendship.findAll({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{ user1Id: userId },
|
||||||
|
{ user2Id: userId },
|
||||||
|
],
|
||||||
|
accepted: true,
|
||||||
|
},
|
||||||
|
include: [
|
||||||
{
|
{
|
||||||
replacements: { key: encryptionKey, email },
|
model: User,
|
||||||
type: sequelize.QueryTypes.SELECT
|
as: 'friendSender',
|
||||||
|
attributes: ['hashedId', 'username'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: User,
|
||||||
|
as: 'friendReceiver',
|
||||||
|
attributes: ['hashedId', 'username'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('did read out friends');
|
||||||
|
return friendships.map((friendship) => {
|
||||||
|
if (friendship.user1Id === userId) {
|
||||||
|
return friendship.friendReceiver;
|
||||||
|
} else {
|
||||||
|
return friendship.friendSender;
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
if (results.length > 0) {
|
} catch (error) {
|
||||||
|
console.error('Error fetching friends:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const registerUser = async ({ email, username, password, language }) => {
|
||||||
|
const encryptedEmail = encrypt(email);
|
||||||
|
const query = `
|
||||||
|
SELECT id FROM community.user
|
||||||
|
WHERE email = :encryptedEmail
|
||||||
|
`;
|
||||||
|
const existingUser = await sequelize.query(query, {
|
||||||
|
replacements: { encryptedEmail },
|
||||||
|
type: sequelize.QueryTypes.SELECT,
|
||||||
|
});
|
||||||
|
if (existingUser.length > 0) {
|
||||||
throw new Error('emailinuse');
|
throw new Error('emailinuse');
|
||||||
}
|
}
|
||||||
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
||||||
@@ -59,6 +103,13 @@ export const loginUser = async ({ username, password }) => {
|
|||||||
const authCode = crypto.randomBytes(20).toString('hex');
|
const authCode = crypto.randomBytes(20).toString('hex');
|
||||||
user.authCode = authCode;
|
user.authCode = authCode;
|
||||||
await user.save();
|
await user.save();
|
||||||
|
const friends = await getFriends(user.id);
|
||||||
|
for (const friend of friends) {
|
||||||
|
await notifyUser(friend.hashedId, 'friendloginchanged', {
|
||||||
|
userId: user.hashedId,
|
||||||
|
status: 'online',
|
||||||
|
});
|
||||||
|
}
|
||||||
const sessionData = {
|
const sessionData = {
|
||||||
id: user.hashedId,
|
id: user.hashedId,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
@@ -93,7 +144,7 @@ export const loginUser = async ({ username, password }) => {
|
|||||||
|
|
||||||
export const logoutUser = async (hashedUserId) => {
|
export const logoutUser = async (hashedUserId) => {
|
||||||
try {
|
try {
|
||||||
const user = User.findOne({
|
const user = await User.findOne({
|
||||||
where: {
|
where: {
|
||||||
hashedId: hashedUserId
|
hashedId: hashedUserId
|
||||||
}
|
}
|
||||||
@@ -101,8 +152,14 @@ export const logoutUser = async (hashedUserId) => {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const friends = await getFriends(user.id);
|
||||||
|
for (const friend of friends) {
|
||||||
|
await notifyUser(friend.hashedId, 'friendloginchanged', {
|
||||||
|
userId: user.hashedId,
|
||||||
|
status: 'online',
|
||||||
|
});
|
||||||
|
}
|
||||||
await deleteUserSession(user.id);
|
await deleteUserSession(user.id);
|
||||||
console.log('Benutzer erfolgreich aus Redis entfernt:', userId);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Logout:', error);
|
console.error('Fehler beim Logout:', error);
|
||||||
throw new Error('logoutfailed');
|
throw new Error('logoutfailed');
|
||||||
|
|||||||
@@ -1,76 +1,137 @@
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import amqp from 'amqplib/callback_api.js';
|
||||||
|
|
||||||
let messages = [];
|
const RABBITMQ_URL = 'amqp://localhost';
|
||||||
let searchQueue = [];
|
const QUEUE = 'oneToOne_messages';
|
||||||
let users = [];
|
|
||||||
let currentChats = [];
|
|
||||||
|
|
||||||
export const getMessages = (toId, fromId) => {
|
class ChatService {
|
||||||
const userChats = currentChats.filter(chat => chat.includes(toId) && chat.includes(fromId));
|
constructor() {
|
||||||
|
this.messages = [];
|
||||||
|
this.searchQueue = [];
|
||||||
|
this.users = [];
|
||||||
|
this.randomChats = [];
|
||||||
|
this.oneToOneChats = [];
|
||||||
|
amqp.connect(RABBITMQ_URL, (err, connection) => {
|
||||||
|
if (err) throw err;
|
||||||
|
connection.createChannel((err, channel) => {
|
||||||
|
if (err) throw err;
|
||||||
|
this.channel = channel;
|
||||||
|
channel.assertQueue(QUEUE, { durable: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getMessages(toId, fromId) {
|
||||||
|
const userChats = this.randomChats.filter(chat => chat.includes(toId) && chat.includes(fromId));
|
||||||
if (userChats.length === 0) {
|
if (userChats.length === 0) {
|
||||||
fromId = '';
|
fromId = '';
|
||||||
}
|
}
|
||||||
const userMessages = messages.filter(message => message.to = toId && ["system", fromId].includes(message.from));
|
const userMessages = this.messages.filter(message => message.to === toId && ["system", fromId].includes(message.from));
|
||||||
messages = messages.filter(message => message.to === toId && ["system", fromId].includes(message.from));
|
this.messages = this.messages.filter(message => message.to === toId && ["system", fromId].includes(message.from));
|
||||||
return userMessages;
|
return userMessages;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const addMessage = (from, to, text) => {
|
async addMessage(from, to, text) {
|
||||||
const userChats = currentChats.filter(chat => chat.includes(from) && chat.includes(to));
|
const userChats = this.randomChats.filter(chat => chat.includes(from) && chat.includes(to));
|
||||||
if (userChats.length === 0) {
|
if (userChats.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
messages.push({ from: from, to: to, text: text });
|
this.messages.push({ from: from, to: to, text: text });
|
||||||
return { text: text };
|
return { text: text };
|
||||||
};
|
|
||||||
|
|
||||||
export const findMatch = (genders, age, id) => {
|
|
||||||
const currentUsersChat = currentChats.filter(chat => chat.includes(id));
|
|
||||||
if (currentUsersChat.length > 0) {
|
|
||||||
return findUser(currentUsersChat[0][0] === id ? currentUsersChat[0][1] : currentUsersChat[0][0]);
|
|
||||||
}
|
}
|
||||||
let filteredSearchQueue = users.filter(user =>
|
|
||||||
searchQueue.some(sq => sq.id === user.id) && user.id !== id
|
findMatch(genders, age, id) {
|
||||||
&& currentChats.filter(chat => chat.includes(user.id)).length === 0
|
const currentUsersChat = this.randomChats.filter(chat => chat.includes(id));
|
||||||
|
if (currentUsersChat.length > 0) {
|
||||||
|
return this.findUser(currentUsersChat[0][0] === id ? currentUsersChat[0][1] : currentUsersChat[0][0]);
|
||||||
|
}
|
||||||
|
let filteredSearchQueue = this.users.filter(user =>
|
||||||
|
this.searchQueue.some(sq => sq.id === user.id) && user.id !== id
|
||||||
|
&& this.randomChats.filter(chat => chat.includes(user.id)).length === 0
|
||||||
).sort(() => Math.random() - 0.5);
|
).sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
for (let i = 0; i < filteredSearchQueue.length; i++) {
|
for (let i = 0; i < filteredSearchQueue.length; i++) {
|
||||||
const user = filteredSearchQueue[i];
|
const user = filteredSearchQueue[i];
|
||||||
const ageMatch = user.age >= age.min && user.age <= age.max;
|
const ageMatch = user.age >= age.min && user.age <= age.max;
|
||||||
const genderMatch = genders.includes(user.gender);
|
const genderMatch = genders.includes(user.gender);
|
||||||
if (ageMatch && genderMatch) {
|
if (ageMatch && genderMatch) {
|
||||||
for (let j = searchQueue.length - 1; j >= 0; j--) {
|
for (let j = this.searchQueue.length - 1; j >= 0; j--) {
|
||||||
if ([id, user.id].includes(searchQueue[j].id)) {
|
if ([id, user.id].includes(this.searchQueue[j].id)) {
|
||||||
searchQueue.splice(j, 1);
|
this.searchQueue.splice(j, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentChats.push([user.id, id]);
|
this.randomChats.push([user.id, id]);
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!searchQueue.find(user => user.id === id)) {
|
if (!this.searchQueue.find(user => user.id === id)) {
|
||||||
searchQueue.push({ id, genders, age });
|
this.searchQueue.push({ id, genders, age });
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
}
|
||||||
|
|
||||||
const findUser = (id) => {
|
findUser(id) {
|
||||||
return users.find(user => user.id === id);
|
return this.users.find(user => user.id === id);
|
||||||
};
|
}
|
||||||
|
|
||||||
export const registerUser = (gender, age) => {
|
async registerUser(gender, age) {
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
users.push({ gender, age, id });
|
this.users.push({ gender, age, id });
|
||||||
return id;
|
return id;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const removeUser = (id) => {
|
async removeUser(id) {
|
||||||
searchQueue = searchQueue.filter(user => user.id !== id);
|
this.searchQueue = this.searchQueue.filter(user => user.id !== id);
|
||||||
users = users.filter(user => user.id !== id);
|
this.users = this.users.filter(user => user.id !== id);
|
||||||
currentChats = currentChats.filter(pair => pair[0] === id || pair[1] === id);
|
this.randomChats = this.randomChats.filter(pair => pair[0] === id || pair[1] === id);
|
||||||
messages = messages.filter(message => message.from === id || message.to === id);
|
this.messages = this.messages.filter(message => message.from === id || message.to === id);
|
||||||
};
|
}
|
||||||
|
|
||||||
export const endChat = (userId) => {
|
async endChat(userId) {
|
||||||
currentChats = currentChats.filter(chat => !chat.includes(userId));
|
this.randomChats = this.randomChats.filter(chat => !chat.includes(userId));
|
||||||
messages.push({ to: userId, from: 'system', activity: 'otheruserleft'})
|
this.messages.push({ to: userId, from: 'system', activity: 'otheruserleft' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async initOneToOne(user1HashId, user2HashId) {
|
||||||
|
const chat = this.searchOneToOneChat(user1HashId, user2HashId);
|
||||||
|
if (!chat) {
|
||||||
|
this.oneToOneChats.push({ user1Id: user1HashId, user2Id: user2HashId, history: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendOneToOneMessage(user1HashId, user2HashId, message) {
|
||||||
|
const messageBundle = {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
sender: user1HashId,
|
||||||
|
recipient: user2HashId,
|
||||||
|
message: message,
|
||||||
|
};
|
||||||
|
const chat = this.searchOneToOneChat(user1HashId, user2HashId);
|
||||||
|
if (chat) {
|
||||||
|
chat.history.push(messageBundle);
|
||||||
|
} else {
|
||||||
|
this.oneToOneChats.push({
|
||||||
|
user1Id: user1HashId,
|
||||||
|
user2Id: user2HashId,
|
||||||
|
history: [messageBundle],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.channel) {
|
||||||
|
this.channel.sendToQueue(QUEUE, Buffer.from(JSON.stringify(messageBundle)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOneToOneMessageHistory(user1HashId, user2HashId) {
|
||||||
|
const chat = this.searchOneToOneChat(user1HashId, user2HashId);
|
||||||
|
return chat ? chat.history : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
searchOneToOneChat(user1HashId, user2HashId) {
|
||||||
|
return this.oneToOneChats.find(chat =>
|
||||||
|
(chat.user1Id === user1HashId && chat.user2Id === user2HashId) ||
|
||||||
|
(chat.user1Id === user2HashId && chat.user2Id === user1HashId)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default new ChatService();
|
||||||
|
|||||||
7
backend/services/falukantService.js
Normal file
7
backend/services/falukantService.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
class FalukantService {
|
||||||
|
async exampleMethod() {
|
||||||
|
// Logik für die Methode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new FalukantService();
|
||||||
@@ -10,6 +10,7 @@ import User from '../models/community/user.js';
|
|||||||
import ForumForumPermission from '../models/forum/forum_forum_permission.js';
|
import ForumForumPermission from '../models/forum/forum_forum_permission.js';
|
||||||
import Title from '../models/forum/title.js';
|
import Title from '../models/forum/title.js';
|
||||||
import Message from '../models/forum/message.js';
|
import Message from '../models/forum/message.js';
|
||||||
|
import { notifyAllUsers } from '../utils/socket.js';
|
||||||
|
|
||||||
class ForumService extends BaseService {
|
class ForumService extends BaseService {
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ class ForumService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await notifyAllUsers('forumschanged', {});
|
||||||
return newForum;
|
return newForum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,6 +62,7 @@ class ForumService extends BaseService {
|
|||||||
await forum.destroy({ transaction });
|
await forum.destroy({ transaction });
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
await notifyAllUsers('forumschanged', {});
|
||||||
return forum;
|
return forum;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
@@ -193,6 +196,7 @@ class ForumService extends BaseService {
|
|||||||
}
|
}
|
||||||
const newTopic = await Title.create({ title, forumId, createdBy: user.id });
|
const newTopic = await Title.create({ title, forumId, createdBy: user.id });
|
||||||
await Message.create({ titleId: newTopic.id, text: content, createdBy: user.id})
|
await Message.create({ titleId: newTopic.id, text: content, createdBy: user.id})
|
||||||
|
await notifyAllUsers('topicschanged', { forumId, topic: newTopic });
|
||||||
return this.getForum(hashedUserId, forumId, 1);
|
return this.getForum(hashedUserId, forumId, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +293,7 @@ class ForumService extends BaseService {
|
|||||||
}
|
}
|
||||||
console.log('[ForumService.addMessage] - create new message');
|
console.log('[ForumService.addMessage] - create new message');
|
||||||
await Message.create({ titleId: topicId, text: content, createdBy: user.id });
|
await Message.create({ titleId: topicId, text: content, createdBy: user.id });
|
||||||
|
await notifyAllUsers('messageschanged', { topicId, message });
|
||||||
console.log('[ForumService.addMessage] - return topic');
|
console.log('[ForumService.addMessage] - return topic');
|
||||||
return this.getTopic(hashedUserId, topicId);
|
return this.getTopic(hashedUserId, topicId);
|
||||||
}
|
}
|
||||||
|
|||||||
180
backend/services/friendshipService.js
Normal file
180
backend/services/friendshipService.js
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
import BaseService from './BaseService.js';
|
||||||
|
import Friendship from '../models/community/friendship.js';
|
||||||
|
import User from '../models/community/user.js';
|
||||||
|
import { Op } from 'sequelize';
|
||||||
|
import UserParam from '../models/community/user_param.js';
|
||||||
|
import UserParamType from '../models/type/user_param.js';
|
||||||
|
import UserParamValue from '../models/type/user_param_value.js';
|
||||||
|
import { notifyUser } from '../utils/socket.js';
|
||||||
|
|
||||||
|
class FriendshipService extends BaseService {
|
||||||
|
genders = {};
|
||||||
|
|
||||||
|
async endFriendship(hashedUserId, friendUserId) {
|
||||||
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
|
const friend = await this.getUserByHashedId(friendUserId);
|
||||||
|
if (!user) throw new Error('User not found.');
|
||||||
|
|
||||||
|
const friendship = await Friendship.findOne({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{ user1Id: user.id, user2Id: friend.id },
|
||||||
|
{ user1Id: friend.id, user2Id: user.id },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!friendship) throw new Error('Friendship not found.');
|
||||||
|
await friendship.destroy();
|
||||||
|
notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async acceptFriendship(hashedUserId, friendUserId) {
|
||||||
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
|
const friend = await this.getUserByHashedId(friendUserId);
|
||||||
|
if (!user) throw new Error('User not found.');
|
||||||
|
const friendship = await Friendship.findOne({
|
||||||
|
where: { user1Id: friend.id, user2Id: user.id, accepted: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!friendship) throw new Error('Cannot accept this friendship.');
|
||||||
|
friendship.accepted = true;
|
||||||
|
await friendship.save();
|
||||||
|
notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async rejectFriendship(hashedUserId, friendUserId) {
|
||||||
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
|
const friend = await this.getUserByHashedId(friendUserId);
|
||||||
|
if (!user) throw new Error('User not found.');
|
||||||
|
const friendship = await Friendship.findOne({
|
||||||
|
where: { user1Id: friend.id, user2Id: user.id, accepted: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!friendship) throw new Error('Cannot reject this friendship.');
|
||||||
|
friendship.denied = true;
|
||||||
|
await friendship.save();
|
||||||
|
notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async withdrawRequest(hashedUserId, friendUserId) {
|
||||||
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
|
const friend = await this.getUserByHashedId(friendUserId);
|
||||||
|
if (!user) throw new Error('User not found.');
|
||||||
|
|
||||||
|
const friendship = await Friendship.findOne({
|
||||||
|
where: { user1Id: user.id, user2Id: friend.id, accepted: false },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!friendship) throw new Error('Cannot withdraw this request.');
|
||||||
|
await friendship.destroy();
|
||||||
|
notifyUser(friend.hashedId, 'friendshipChanged', { userId: user.hashedId });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async getFriendships(hashedUserId, acceptedOnly) {
|
||||||
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
|
if (!user) throw new Error('User not found.');
|
||||||
|
|
||||||
|
const whereCondition = acceptedOnly
|
||||||
|
? { accepted: true, withdrawn: false, denied: false }
|
||||||
|
: {};
|
||||||
|
|
||||||
|
const friendships = await Friendship.findAll({
|
||||||
|
where: {
|
||||||
|
...whereCondition,
|
||||||
|
[Op.or]: [
|
||||||
|
{ user1Id: user.id },
|
||||||
|
{ user2Id: user.id },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: User,
|
||||||
|
as: 'friendSender',
|
||||||
|
attributes: ['username', 'hashedId'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserParam,
|
||||||
|
as: 'user_params',
|
||||||
|
required: false,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserParamType,
|
||||||
|
as: 'paramType',
|
||||||
|
required: true,
|
||||||
|
where: { description: 'gender' },
|
||||||
|
attributes: ['description'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attributes: ['value'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: User,
|
||||||
|
as: 'friendReceiver',
|
||||||
|
attributes: ['username', 'hashedId'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserParam,
|
||||||
|
as: 'user_params',
|
||||||
|
required: false,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserParamType,
|
||||||
|
as: 'paramType',
|
||||||
|
required: true,
|
||||||
|
where: { description: 'gender' },
|
||||||
|
attributes: ['description'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attributes: ['value'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const processedFriendships = await Promise.all(
|
||||||
|
friendships.map(async (friendship) => {
|
||||||
|
const isInitiator = friendship.user1Id === user.id;
|
||||||
|
const otherUser = isInitiator ? friendship.friendReceiver : friendship.friendSender;
|
||||||
|
const genderParam = otherUser.user_params?.find(param => param.paramType.description === 'gender');
|
||||||
|
const gender = genderParam ? await this.getGender(genderParam) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: friendship.id,
|
||||||
|
user: {
|
||||||
|
username: otherUser.username,
|
||||||
|
hashedId: otherUser.hashedId,
|
||||||
|
gender,
|
||||||
|
},
|
||||||
|
accepted: friendship.accepted,
|
||||||
|
denied: friendship.denied,
|
||||||
|
withdrawn: friendship.withdrawn,
|
||||||
|
isInitiator,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return processedFriendships;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGender(genderParam) {
|
||||||
|
if (!this.genders) {
|
||||||
|
this.genders = {};
|
||||||
|
}
|
||||||
|
if (this.genders[genderParam.value]) return this.genders[genderParam.value];
|
||||||
|
const genderObject = await UserParamValue.findOne({
|
||||||
|
where: { id: genderParam.value },
|
||||||
|
});
|
||||||
|
if (genderObject) {
|
||||||
|
this.genders[genderParam.value] = genderObject.value;
|
||||||
|
return genderObject.value;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new FriendshipService();
|
||||||
@@ -156,7 +156,7 @@ class SettingsService extends BaseService{
|
|||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: UserParamType,
|
model: UserParamType,
|
||||||
as: 'user_param_type',
|
as: 'user_param_value_type',
|
||||||
where: { description: type }
|
where: { description: type }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -264,7 +264,6 @@ class SettingsService extends BaseService{
|
|||||||
async updateVisibility(hashedUserId, userParamTypeId, visibilityId) {
|
async updateVisibility(hashedUserId, userParamTypeId, visibilityId) {
|
||||||
try {
|
try {
|
||||||
const user = await this.getUserByHashedId(hashedUserId);
|
const user = await this.getUserByHashedId(hashedUserId);
|
||||||
console.log(JSON.stringify(user));
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('User not found');
|
throw new Error('User not found');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,22 @@ import DOMPurify from 'dompurify';
|
|||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import Diary from '../models/community/diary.js';
|
import Diary from '../models/community/diary.js';
|
||||||
import Friendship from '../models/community/friendship.js';
|
import Friendship from '../models/community/friendship.js';
|
||||||
|
import { getUserSession } from '../utils/redis.js';
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
class SocialNetworkService extends BaseService {
|
class SocialNetworkService extends BaseService {
|
||||||
async searchUsers({ username, ageFrom, ageTo, genders }) {
|
async searchUsers({ hashedUserId, username, ageFrom, ageTo, genders }) {
|
||||||
const whereClause = this.buildSearchWhereClause(username);
|
const whereClause = this.buildSearchWhereClause(username);
|
||||||
|
const user = await this.loadUserByHash(hashedUserId);
|
||||||
|
console.log(hashedUserId, user);
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('User not found');
|
||||||
|
}
|
||||||
|
whereClause.id = { [Op.ne]: user.id };
|
||||||
const users = await User.findAll({ where: whereClause, include: this.getUserParamsInclude() });
|
const users = await User.findAll({ where: whereClause, include: this.getUserParamsInclude() });
|
||||||
return this.filterUsersByCriteria(users, ageFrom, ageTo, genders);
|
return await this.filterUsersByCriteria(users, ageFrom, ageTo, genders);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProfile(hashedUserId, requestingUserId) {
|
async getProfile(hashedUserId, requestingUserId) {
|
||||||
@@ -48,16 +55,18 @@ class SocialNetworkService extends BaseService {
|
|||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('User not found');
|
throw new Error('User not found');
|
||||||
}
|
}
|
||||||
|
console.log('given data', data, folderId);
|
||||||
const parentFolder = data.parentId ? await Folder.findOne({
|
const parentFolder = data.parentId ? await Folder.findOne({
|
||||||
where: { id: data.parentId, userId: user.id }
|
where: { id: data.parentId, userId: user.id }
|
||||||
}) : null;
|
}) : null;
|
||||||
if (data.parentId && !parentFolder) {
|
if (data.parentId && !parentFolder) {
|
||||||
throw new Error('Parent folder not found');
|
throw new Error('Parent folder not found');
|
||||||
}
|
}
|
||||||
|
console.log('parentFolder', parentFolder);
|
||||||
let newFolder;
|
let newFolder;
|
||||||
if (folderId === 0) {
|
if (parentFolder) {
|
||||||
newFolder = await Folder.create({
|
newFolder = await Folder.create({
|
||||||
parentId: data.parentId || null,
|
parentId: parentFolder.id || null,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
name: data.name
|
name: data.name
|
||||||
});
|
});
|
||||||
@@ -267,7 +276,8 @@ class SocialNetworkService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadUserByHash(hashedId) {
|
async loadUserByHash(hashedId) {
|
||||||
return await User.findOne({ hashedId });
|
console.log('Loading user by hashedId:', hashedId);
|
||||||
|
return await User.findOne({ where: { hashedId: hashedId } });
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadUserByName(userName) {
|
async loadUserByName(userName) {
|
||||||
@@ -300,10 +310,10 @@ class SocialNetworkService extends BaseService {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
filterUsersByCriteria(users, ageFrom, ageTo, genders) {
|
async filterUsersByCriteria(users, ageFrom, ageTo, genders) {
|
||||||
const results = [];
|
const results = [];
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
const userDetails = this.extractUserDetails(user);
|
const userDetails = await this.extractUserDetails(user);
|
||||||
if (this.isUserValid(userDetails, ageFrom, ageTo, genders)) {
|
if (this.isUserValid(userDetails, ageFrom, ageTo, genders)) {
|
||||||
results.push(userDetails);
|
results.push(userDetails);
|
||||||
}
|
}
|
||||||
@@ -311,11 +321,11 @@ class SocialNetworkService extends BaseService {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
extractUserDetails(user) {
|
async extractUserDetails(user) {
|
||||||
const birthdateParam = user.user_params.find(param => param.paramType.description === 'birthdate');
|
const birthdateParam = user.user_params.find(param => param.paramType.description === 'birthdate');
|
||||||
const genderParam = user.user_params.find(param => param.paramType.description === 'gender');
|
const genderParam = user.user_params.find(param => param.paramType.description === 'gender');
|
||||||
const age = birthdateParam ? this.calculateAge(birthdateParam.value) : null;
|
const age = birthdateParam ? this.calculateAge(birthdateParam.value) : null;
|
||||||
const gender = genderParam ? this.getGenderValue(genderParam.value) : null;
|
const gender = genderParam ? await this.getGenderValue(genderParam.value) : null;
|
||||||
return {
|
return {
|
||||||
id: user.hashedId,
|
id: user.hashedId,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
@@ -735,8 +745,9 @@ class SocialNetworkService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async addFriend(hashedUserid, friendUserid) {
|
async addFriend(hashedUserid, friendUserid) {
|
||||||
|
console.log('--------', friendUserid, hashedUserid);
|
||||||
const requestingUserId = await this.checkUserAccess(hashedUserid);
|
const requestingUserId = await this.checkUserAccess(hashedUserid);
|
||||||
const friend = await this.loadUserByHash(friendUserid);
|
const friend = await User.findOne({ where: { hashedId: friendUserid } });
|
||||||
if (!friend) {
|
if (!friend) {
|
||||||
throw new Error('notfound');
|
throw new Error('notfound');
|
||||||
}
|
}
|
||||||
@@ -748,10 +759,10 @@ class SocialNetworkService extends BaseService {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
console.log('friendship', friend, requestingUserId);
|
||||||
if (friendship) {
|
if (friendship) {
|
||||||
if (friendship.withdrawn) {
|
if (friendship.withdrawn && friendship.user1Id === requestingUserId) {
|
||||||
friendship.withdrawn = false;
|
friendship.update({ withdrawn: false });
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('alreadyexists');
|
throw new Error('alreadyexists');
|
||||||
}
|
}
|
||||||
@@ -811,5 +822,37 @@ class SocialNetworkService extends BaseService {
|
|||||||
throw new Error('notfound');
|
throw new Error('notfound');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLoggedInFriends(hashedUserId) {
|
||||||
|
const userId = await this.checkUserAccess(hashedUserId);
|
||||||
|
const activeFriendships = await Friendship.findAll({
|
||||||
|
where: {
|
||||||
|
accepted: true,
|
||||||
|
denied: false,
|
||||||
|
withdrawn: false,
|
||||||
|
[Op.or]: [
|
||||||
|
{ user1Id: userId },
|
||||||
|
{ user2Id: userId }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const friendIds = activeFriendships.map(friendship =>
|
||||||
|
friendship.user1Id === userId ? friendship.user2Id : friendship.user1Id
|
||||||
|
);
|
||||||
|
const loggedInFriends = [];
|
||||||
|
for (const friendId of friendIds) {
|
||||||
|
const session = await getUserSession(friendId);
|
||||||
|
if (session && session.id) {
|
||||||
|
const friend = await User.findOne({ where: { hashedId: session.id } });
|
||||||
|
if (friend) {
|
||||||
|
loggedInFriends.push({
|
||||||
|
id: friend.hashedId,
|
||||||
|
username: friend.username,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return loggedInFriends;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export default SocialNetworkService;
|
export default SocialNetworkService;
|
||||||
|
|||||||
39
backend/services/webSocketService.js
Normal file
39
backend/services/webSocketService.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// services/webSocketService.js
|
||||||
|
import { Server } from 'socket.io';
|
||||||
|
import amqp from 'amqplib/callback_api.js';
|
||||||
|
|
||||||
|
const RABBITMQ_URL = 'amqp://localhost';
|
||||||
|
const QUEUE = 'chat_messages';
|
||||||
|
|
||||||
|
export function setupWebSocket(server) {
|
||||||
|
const io = new Server(server);
|
||||||
|
|
||||||
|
amqp.connect(RABBITMQ_URL, (err, connection) => {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
connection.createChannel((err, channel) => {
|
||||||
|
if (err) throw err;
|
||||||
|
|
||||||
|
channel.assertQueue(QUEUE, { durable: false });
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log('Client connected via WebSocket');
|
||||||
|
|
||||||
|
// Konsumiert Nachrichten aus RabbitMQ und sendet sie an den WebSocket-Client
|
||||||
|
channel.consume(QUEUE, (msg) => {
|
||||||
|
const message = JSON.parse(msg.content.toString());
|
||||||
|
io.emit('newMessage', message); // Broadcast an alle Clients
|
||||||
|
}, { noAck: true });
|
||||||
|
|
||||||
|
// Empfangt eine Nachricht vom WebSocket-Client und sendet sie an die RabbitMQ-Warteschlange
|
||||||
|
socket.on('newMessage', (message) => {
|
||||||
|
channel.sendToQueue(QUEUE, Buffer.from(JSON.stringify(message)));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('Client disconnected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
|
|
||||||
const algorithm = 'aes-256-ecb';
|
const algorithm = 'aes-256-ecb';
|
||||||
|
|
||||||
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32);
|
const key = crypto.scryptSync(process.env.SECRET_KEY, 'salt', 32);
|
||||||
|
|
||||||
export const generateIv = () => {
|
export const generateIv = () => {
|
||||||
return crypto.randomBytes(16);
|
return crypto.randomBytes(16).toString('base64');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const encrypt = (text) => {
|
export const encrypt = (text) => {
|
||||||
@@ -15,8 +16,13 @@ export const encrypt = (text) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const decrypt = (text) => {
|
export const decrypt = (text) => {
|
||||||
|
try {
|
||||||
const decipher = crypto.createDecipheriv(algorithm, key, null);
|
const decipher = crypto.createDecipheriv(algorithm, key, null);
|
||||||
let decrypted = decipher.update(text, 'hex', 'utf8');
|
let decrypted = decipher.update(text, 'hex', 'utf8');
|
||||||
decrypted += decipher.final('utf8');
|
decrypted += decipher.final('utf8');
|
||||||
return decrypted;
|
return decrypted;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
81
backend/utils/falukant/initializeFalukantTypes.js
Normal file
81
backend/utils/falukant/initializeFalukantTypes.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import RegionData from "../../models/falukant/data/region.js";
|
||||||
|
import RegionType from "../../models/falukant/type/region.js";
|
||||||
|
|
||||||
|
const regionTypes = [];
|
||||||
|
const regionTypeTrs = [
|
||||||
|
"country",
|
||||||
|
"duchy",
|
||||||
|
"markgravate",
|
||||||
|
"shire",
|
||||||
|
"county",
|
||||||
|
"city"
|
||||||
|
];
|
||||||
|
const regions = [
|
||||||
|
{ labelTr: "falukant", regionType: "country", parentTr: null },
|
||||||
|
{ labelTr: "duchy1", regionType: "duchy", parentTr: "falukant" },
|
||||||
|
{ labelTr: "markgravate", regionType: "markgravate", parentTr: "duchy1" },
|
||||||
|
{ labelTr: "shire1", regionType: "shire", parentTr: "markgravate" },
|
||||||
|
{ labelTr: "county1", regionType: "county", parentTr: "shire1" },
|
||||||
|
{ labelTr: "town1", regionType: "city", parentTr: "county1" },
|
||||||
|
{ labelTr: "town2", regionType: "city", parentTr: "county1" },
|
||||||
|
{ labelTr: "town3", regionType: "city", parentTr: "county1" },
|
||||||
|
{ labelTr: "town4", regionType: "city", parentTr: "county1" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// Step 1: Initialize region types
|
||||||
|
export const initializeFalukantTypes = async () => {
|
||||||
|
await initializeFalukantTypeRegions();
|
||||||
|
}
|
||||||
|
|
||||||
|
const initializeFalukantTypeRegions = async () => {
|
||||||
|
for (const regionType of regionTypeTrs) {
|
||||||
|
const [regionTypeRecord] = await RegionType.findOrCreate({
|
||||||
|
where: { labelTr: regionType },
|
||||||
|
defaults: {
|
||||||
|
parentId: regionTypes[regionTypes.length - 1]?.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
regionTypes.push(regionTypeRecord);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility: Find region type object by region type
|
||||||
|
const findRegionTypeObjectByRegionType = async (regionType) => {
|
||||||
|
return regionTypes.find(region => region.labelTr === regionType) || null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility: Find region object by label
|
||||||
|
const findRegionObjectByLabelTr = async (labelTr) => {
|
||||||
|
return await RegionData.findOne({ where: { name: labelTr } });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 2: Initialize regions
|
||||||
|
export const initializeFalukantRegions = async () => {
|
||||||
|
for (const region of regions) {
|
||||||
|
const regionType = await findRegionTypeObjectByRegionType(region.regionType);
|
||||||
|
if (!regionType) {
|
||||||
|
console.error(`Region type not found for: ${region.regionType}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentRegion = region.parentTr
|
||||||
|
? await findRegionObjectByLabelTr(region.parentTr)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (region.parentTr && !parentRegion) {
|
||||||
|
console.error(`Parent region not found for: ${region.parentTr}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Creating/Fetching Region:', region.labelTr, 'Type:', regionType.labelTr, 'Parent:', parentRegion?.name);
|
||||||
|
|
||||||
|
await RegionData.findOrCreate({
|
||||||
|
where: { name: region.labelTr },
|
||||||
|
defaults: {
|
||||||
|
regionTypeId: regionType.id,
|
||||||
|
parentId: parentRegion?.id || null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
8
backend/utils/initializeFalukant.js
Normal file
8
backend/utils/initializeFalukant.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { initializeFalukantTypes, initializeFalukantRegions } from './falukant/initializeFalukantTypes.js';
|
||||||
|
|
||||||
|
const initializeFalukant = async () => {
|
||||||
|
await initializeFalukantTypes();
|
||||||
|
await initializeFalukantRegions();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default initializeFalukant;
|
||||||
@@ -16,7 +16,10 @@ redisClient.connect().catch(console.error);
|
|||||||
|
|
||||||
const setUserSession = async (userId, sessionData) => {
|
const setUserSession = async (userId, sessionData) => {
|
||||||
try {
|
try {
|
||||||
await redisClient.hSet(`user:${userId}`, sessionData);
|
await redisClient.sendCommand(['JSON.SET', `user:${userId}`, '.', JSON.stringify(sessionData)]);
|
||||||
|
console.log(userId, sessionData);
|
||||||
|
const sessionDataStr = await redisClient.sendCommand(['JSON.GET', `user:${userId}`]);
|
||||||
|
console.log(sessionDataStr);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Setzen der Benutzersitzung:', error);
|
console.error('Fehler beim Setzen der Benutzersitzung:', error);
|
||||||
}
|
}
|
||||||
@@ -24,30 +27,43 @@ const setUserSession = async (userId, sessionData) => {
|
|||||||
|
|
||||||
const deleteUserSession = async (userId) => {
|
const deleteUserSession = async (userId) => {
|
||||||
try {
|
try {
|
||||||
await redisClient.del(`user:${userId}`);
|
const result = await redisClient.del(`user:${userId}`);
|
||||||
|
if (result === 1) {
|
||||||
|
console.log(`Benutzersitzung für Benutzer ${userId} erfolgreich gelöscht.`);
|
||||||
|
} else {
|
||||||
|
console.warn(`Benutzersitzung für Benutzer ${userId} war nicht vorhanden.`);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Löschen der Benutzersitzung:', error);
|
console.error('Fehler beim Löschen der Benutzersitzung:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const convertToOriginalType = (value) => {
|
||||||
|
if (value === 'true') return true;
|
||||||
|
if (value === 'false') return false;
|
||||||
|
if (!isNaN(value) && value.trim() !== '') return Number(value);
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
const getUserSession = async (userId) => {
|
const getUserSession = async (userId) => {
|
||||||
try {
|
try {
|
||||||
return await redisClient.hGetAll(`user:${userId}`);
|
const sessionData = await redisClient.sendCommand(['JSON.GET', `user:${userId}`]);
|
||||||
|
return JSON.parse(sessionData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Abrufen der Benutzersitzung:', error);
|
console.error('Fehler beim Abrufen der Benutzersitzung:', error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateUserTimestamp = async (hashedId) => {
|
const updateUserTimestamp = async (userId) => {
|
||||||
try {
|
try {
|
||||||
const userKey = `user:${hashedId}`;
|
const userKey = `user:${userId}`;
|
||||||
const userExists = await redisClient.exists(userKey);
|
const userExists = await redisClient.exists(userKey);
|
||||||
if (userExists) {
|
if (userExists) {
|
||||||
await redisClient.hSet(userKey, 'timestamp', Date.now());
|
const sessionDataString = await redisClient.sendCommand(['JSON.GET', `user:${userId}`]);
|
||||||
console.log(`Zeitstempel für Benutzer ${hashedId} aktualisiert.`);
|
const sessionData = JSON.parse(sessionDataString);
|
||||||
} else {
|
sessionData.timestamp = Date.now();
|
||||||
console.warn(`Benutzer mit der hashedId ${hashedId} wurde nicht gefunden.`);
|
await redisClient.sendCommand(['JSON.SET', `user:${userId}`, '.', JSON.stringify(sessionData)]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
|
console.error('Fehler beim Aktualisieren des Zeitstempels:', error);
|
||||||
@@ -58,17 +74,29 @@ const cleanupExpiredSessions = async () => {
|
|||||||
try {
|
try {
|
||||||
const keys = await redisClient.keys('user:*');
|
const keys = await redisClient.keys('user:*');
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
const session = await redisClient.hGetAll(key);
|
try {
|
||||||
|
const sessionStr = await redisClient.sendCommand(['JSON.GET', key]);
|
||||||
|
if (sessionStr) {
|
||||||
|
const session = JSON.parse(sessionStr);
|
||||||
if (session.timestamp && now - parseInt(session.timestamp) > EXPIRATION_TIME) {
|
if (session.timestamp && now - parseInt(session.timestamp) > EXPIRATION_TIME) {
|
||||||
const userId = key.split(':')[1];
|
const userId = key.split(':')[1];
|
||||||
await redisClient.del(key);
|
await redisClient.del(key);
|
||||||
await User.update({ authCode: '' }, { where: { hashedId: userId } });
|
await User.update({ authCode: '' }, { where: { hashedId: userId } });
|
||||||
console.log(`Abgelaufene Sitzung für Benutzer ${userId} gelöscht.`);
|
console.log(`Abgelaufene Sitzung für Benutzer ${userId} mit RedisJSON gelöscht.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Bereinigen abgelaufener Sitzungen:', error);
|
if (error.message.includes('WRONGTYPE')) {
|
||||||
|
console.warn(`Schlüssel ${key} ist kein JSON-Objekt, wird übersprungen.`);
|
||||||
|
} else {
|
||||||
|
console.error(`Fehler beim Bereinigen für Schlüssel ${key}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Bereinigen abgelaufener Sitzungen mit RedisJSON:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ const createSchemas = async () => {
|
|||||||
await sequelize.query('CREATE SCHEMA IF NOT EXISTS type');
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS type');
|
||||||
await sequelize.query('CREATE SCHEMA IF NOT EXISTS service');
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS service');
|
||||||
await sequelize.query('CREATE SCHEMA IF NOT EXISTS forum');
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS forum');
|
||||||
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_data');
|
||||||
|
await sequelize.query('CREATE SCHEMA IF NOT EXISTS falukant_type');
|
||||||
};
|
};
|
||||||
|
|
||||||
const initializeDatabase = async () => {
|
const initializeDatabase = async () => {
|
||||||
|
|||||||
73
backend/utils/socket.js
Normal file
73
backend/utils/socket.js
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import { Server } from 'socket.io';
|
||||||
|
import BaseService from '../services/BaseService.js';
|
||||||
|
|
||||||
|
const baseService = new BaseService();
|
||||||
|
|
||||||
|
let io;
|
||||||
|
const userSockets = {};
|
||||||
|
|
||||||
|
export function setupWebSocket(server) {
|
||||||
|
io = new Server(server, {
|
||||||
|
cors: {
|
||||||
|
origin: '*',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
socket.on('setUserId', (userId) => {
|
||||||
|
if (userId) {
|
||||||
|
socket.userId = userId;
|
||||||
|
userSockets[userId] = socket.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
if (socket.userId) {
|
||||||
|
delete userSockets[socket.userId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIo() {
|
||||||
|
if (!io) {
|
||||||
|
throw new Error('Socket.io ist nicht initialisiert!');
|
||||||
|
}
|
||||||
|
return io;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUserSockets() {
|
||||||
|
return userSockets;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function notifyUser(recipientHashedUserId, event, data) {
|
||||||
|
const io = getIo();
|
||||||
|
const userSockets = getUserSockets();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const recipientUser = await baseService.getUserByHashedId(recipientHashedUserId);
|
||||||
|
if (recipientUser) {
|
||||||
|
const socketId = userSockets[recipientUser.hashedId];
|
||||||
|
if (socketId) {
|
||||||
|
io.to(socketId).emit(event, data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`Benutzer mit gehashter ID ${recipientHashedUserId} nicht gefunden.`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Senden der Benachrichtigung:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function notifyAllUsers(event, data) {
|
||||||
|
const io = getIo();
|
||||||
|
const userSockets = getUserSockets();
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (const [userId, socketId] of Object.entries(userSockets)) {
|
||||||
|
io.to(socketId).emit(event, data);
|
||||||
|
console.log(`Benachrichtigung an Benutzer mit ID ${userId} gesendet.`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Senden der Benachrichtigung an alle Benutzer:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import initializeTypes from './initializeTypes.js';
|
|||||||
import initializeSettings from './initializeSettings.js';
|
import initializeSettings from './initializeSettings.js';
|
||||||
import initializeUserRights from './initializeUserRights.js';
|
import initializeUserRights from './initializeUserRights.js';
|
||||||
import initializeImageTypes from './initializeImageTypes.js';
|
import initializeImageTypes from './initializeImageTypes.js';
|
||||||
|
import initializeFalukant from './initializeFalukant.js';
|
||||||
import setupAssociations from '../models/associations.js';
|
import setupAssociations from '../models/associations.js';
|
||||||
import models from '../models/index.js';
|
import models from '../models/index.js';
|
||||||
import { createTriggers } from '../models/trigger.js';
|
import { createTriggers } from '../models/trigger.js';
|
||||||
@@ -13,31 +14,34 @@ import initializeForum from './initializeForum.js';
|
|||||||
const syncDatabase = async () => {
|
const syncDatabase = async () => {
|
||||||
try {
|
try {
|
||||||
console.log("Initializing database schemas...");
|
console.log("Initializing database schemas...");
|
||||||
await initializeDatabase(); // Stellt sicher, dass alle Schemas erstellt sind
|
await initializeDatabase();
|
||||||
|
|
||||||
console.log("Synchronizing models...");
|
console.log("Synchronizing models...");
|
||||||
await syncModels(models); // Modelle synchronisieren
|
await syncModels(models);
|
||||||
|
|
||||||
console.log("Setting up associations...");
|
console.log("Setting up associations...");
|
||||||
setupAssociations(); // Assoziationen definieren
|
setupAssociations();
|
||||||
|
|
||||||
console.log("Creating triggers...");
|
console.log("Creating triggers...");
|
||||||
await createTriggers(); // Trigger erstellen
|
await createTriggers();
|
||||||
|
|
||||||
console.log("Initializing settings...");
|
console.log("Initializing settings...");
|
||||||
await initializeSettings(); // Einstellungsdaten initialisieren
|
await initializeSettings();
|
||||||
|
|
||||||
console.log("Initializing types...");
|
console.log("Initializing types...");
|
||||||
await initializeTypes(); // Typen initialisieren
|
await initializeTypes();
|
||||||
|
|
||||||
console.log("Initializing user rights...");
|
console.log("Initializing user rights...");
|
||||||
await initializeUserRights(); // Benutzerrechte initialisieren
|
await initializeUserRights();
|
||||||
|
|
||||||
console.log("Initializing image types...");
|
console.log("Initializing image types...");
|
||||||
await initializeImageTypes(); // Bildtypen initialisieren
|
await initializeImageTypes();
|
||||||
|
|
||||||
console.log("Initializing forums...");
|
console.log("Initializing forums...");
|
||||||
await initializeForum(); // Foren initialisieren
|
await initializeForum();
|
||||||
|
|
||||||
|
console.log("Initializing Falukant...");
|
||||||
|
await initializeFalukant();
|
||||||
|
|
||||||
console.log('Database synchronization complete.');
|
console.log('Database synchronization complete.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
364
frontend/package-lock.json
generated
364
frontend/package-lock.json
generated
@@ -12,11 +12,14 @@
|
|||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"tinymce": "^7.3.0",
|
"tinymce": "^7.3.0",
|
||||||
"vue": "~3.4.31",
|
"vue": "~3.4.31",
|
||||||
"vue-i18n": "^10.0.0-beta.2",
|
"vue-i18n": "^10.0.0-beta.2",
|
||||||
"vue-multiselect": "^3.1.0",
|
"vue-multiselect": "^3.1.0",
|
||||||
"vue-router": "^4.0.13",
|
"vue-router": "^4.0.13",
|
||||||
|
"vuetify": "^3.7.4",
|
||||||
"vuex": "^4.1.0"
|
"vuex": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -464,12 +467,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/core-base": {
|
"node_modules/@intlify/core-base": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.5.tgz",
|
||||||
"integrity": "sha512-6kpRGjhos95ph7QmEtP4tnWFTW102s71CLQAQwfsIGqOAcoJhzcYFpzIQ0gKXzqAIXsMD/hwM5qJ4ewqMHw3gg==",
|
"integrity": "sha512-F3snDTQs0MdvnnyzTDTVkOYVAZOE/MHwRvF7mn7Jw1yuih4NrFYLNYIymGlLmq4HU2iIdzYsZ7f47bOcwY73XQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/message-compiler": "10.0.1",
|
"@intlify/message-compiler": "10.0.5",
|
||||||
"@intlify/shared": "10.0.1"
|
"@intlify/shared": "10.0.5"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
@@ -479,11 +482,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/message-compiler": {
|
"node_modules/@intlify/message-compiler": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.5.tgz",
|
||||||
"integrity": "sha512-fPeykrcgVT5eOIlshTHiPCN8FV3AZyBOdMS3XaXzfQ6eL5wqfc29I/EdIv5YXVW5X8e/BgYeWjBC0Cuznsl/2g==",
|
"integrity": "sha512-6GT1BJ852gZ0gItNZN2krX5QAmea+cmdjMvsWohArAZ3GmHdnNANEcF9JjPXAMRtQ6Ux5E269ymamg/+WU6tQA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/shared": "10.0.1",
|
"@intlify/shared": "10.0.5",
|
||||||
"source-map-js": "^1.0.2"
|
"source-map-js": "^1.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -494,9 +497,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@intlify/shared": {
|
"node_modules/@intlify/shared": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.5.tgz",
|
||||||
"integrity": "sha512-b4h7IWdZl710DnAhET8lgfgZ4Y9A2IZx/gbli3Ec/zHtYCoPqLHmiM7kUNBrSZj7d/SSjcMMZHuz5I09x3PYZw==",
|
"integrity": "sha512-bmsP4L2HqBF6i6uaMqJMcFBONVjKt+siGluRq4Ca4C0q7W2eMaVZr8iCgF9dKbcVXutftkC7D6z2SaSMmLiDyA==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16"
|
"node": ">= 16"
|
||||||
},
|
},
|
||||||
@@ -510,213 +513,262 @@
|
|||||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
|
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz",
|
||||||
"integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==",
|
"integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz",
|
||||||
"integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==",
|
"integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"android"
|
"android"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz",
|
||||||
"integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==",
|
"integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz",
|
||||||
"integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==",
|
"integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"darwin"
|
"darwin"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
|
"version": "4.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz",
|
||||||
|
"integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
|
"version": "4.26.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz",
|
||||||
|
"integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz",
|
||||||
"integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==",
|
"integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz",
|
||||||
"integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==",
|
"integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz",
|
||||||
"integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==",
|
"integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz",
|
||||||
"integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==",
|
"integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz",
|
||||||
"integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==",
|
"integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz",
|
||||||
"integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==",
|
"integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz",
|
||||||
"integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==",
|
"integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz",
|
||||||
"integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==",
|
"integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz",
|
||||||
"integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==",
|
"integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"linux"
|
"linux"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz",
|
||||||
"integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==",
|
"integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz",
|
||||||
"integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==",
|
"integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz",
|
||||||
"integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==",
|
"integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@socket.io/component-emitter": {
|
||||||
|
"version": "3.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||||
|
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
|
||||||
|
},
|
||||||
"node_modules/@tinymce/tinymce-vue": {
|
"node_modules/@tinymce/tinymce-vue": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-6.0.1.tgz",
|
||||||
@@ -729,10 +781,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||||
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
|
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "5.1.3",
|
"version": "5.1.3",
|
||||||
@@ -991,6 +1044,22 @@
|
|||||||
"url": "https://github.com/sponsors/kossnocorp"
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/debug": {
|
||||||
|
"version": "4.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
|
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "^2.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"supports-color": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/define-data-property": {
|
"node_modules/define-data-property": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
@@ -1044,6 +1113,26 @@
|
|||||||
"url": "https://dotenvx.com"
|
"url": "https://dotenvx.com"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/engine.io-client": {
|
||||||
|
"version": "6.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz",
|
||||||
|
"integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1",
|
||||||
|
"engine.io-parser": "~5.2.1",
|
||||||
|
"ws": "~8.17.1",
|
||||||
|
"xmlhttprequest-ssl": "~2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/engine.io-parser": {
|
||||||
|
"version": "5.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||||
|
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -1468,6 +1557,17 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mitt": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"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=="
|
||||||
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.7",
|
"version": "3.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||||
@@ -1622,12 +1722,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.21.3",
|
"version": "4.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz",
|
||||||
"integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==",
|
"integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.5"
|
"@types/estree": "1.0.6"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@@ -1637,22 +1738,24 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.21.3",
|
"@rollup/rollup-android-arm-eabi": "4.26.0",
|
||||||
"@rollup/rollup-android-arm64": "4.21.3",
|
"@rollup/rollup-android-arm64": "4.26.0",
|
||||||
"@rollup/rollup-darwin-arm64": "4.21.3",
|
"@rollup/rollup-darwin-arm64": "4.26.0",
|
||||||
"@rollup/rollup-darwin-x64": "4.21.3",
|
"@rollup/rollup-darwin-x64": "4.26.0",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.21.3",
|
"@rollup/rollup-freebsd-arm64": "4.26.0",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.21.3",
|
"@rollup/rollup-freebsd-x64": "4.26.0",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.21.3",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.26.0",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.21.3",
|
"@rollup/rollup-linux-arm-musleabihf": "4.26.0",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.21.3",
|
"@rollup/rollup-linux-arm64-gnu": "4.26.0",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.21.3",
|
"@rollup/rollup-linux-arm64-musl": "4.26.0",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.21.3",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.26.0",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.21.3",
|
"@rollup/rollup-linux-riscv64-gnu": "4.26.0",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.21.3",
|
"@rollup/rollup-linux-s390x-gnu": "4.26.0",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.21.3",
|
"@rollup/rollup-linux-x64-gnu": "4.26.0",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.21.3",
|
"@rollup/rollup-linux-x64-musl": "4.26.0",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.21.3",
|
"@rollup/rollup-win32-arm64-msvc": "4.26.0",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.26.0",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.26.0",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1761,6 +1864,32 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/socket.io-client": {
|
||||||
|
"version": "4.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||||
|
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.2",
|
||||||
|
"engine.io-client": "~6.6.1",
|
||||||
|
"socket.io-parser": "~4.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/socket.io-parser": {
|
||||||
|
"version": "4.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||||
|
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||||
|
"dependencies": {
|
||||||
|
"@socket.io/component-emitter": "~3.1.0",
|
||||||
|
"debug": "~4.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/source-map-js": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
@@ -1919,12 +2048,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-i18n": {
|
"node_modules/vue-i18n": {
|
||||||
"version": "10.0.1",
|
"version": "10.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.5.tgz",
|
||||||
"integrity": "sha512-SQVlSm/1S6AaG1wexvwq3ebXUrrkx75ZHD78UAs4/rYD/X3tsQxfm6ElpT4ZPegJQEgRtOJjGripqSrfqAENtg==",
|
"integrity": "sha512-9/gmDlCblz3i8ypu/afiIc/SUIfTTE1mr0mZhb9pk70xo2csHAM9mp2gdQ3KD2O0AM3Hz/5ypb+FycTj/lHlPQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@intlify/core-base": "10.0.1",
|
"@intlify/core-base": "10.0.5",
|
||||||
"@intlify/shared": "10.0.1",
|
"@intlify/shared": "10.0.5",
|
||||||
"@vue/devtools-api": "^6.5.0"
|
"@vue/devtools-api": "^6.5.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -1960,6 +2089,35 @@
|
|||||||
"vue": "^3.2.0"
|
"vue": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vuetify": {
|
||||||
|
"version": "3.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.7.4.tgz",
|
||||||
|
"integrity": "sha512-Y8UU5wUDQXC3oz2uumPb8IOdvB4XMCxtxnmqdOc+LihNuPlkSgxIwf92ndRzbOtJFKHsggFUxpyLqpQp+A+5kg==",
|
||||||
|
"engines": {
|
||||||
|
"node": "^12.20 || >=14.13"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/johnleider"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">=4.7",
|
||||||
|
"vite-plugin-vuetify": ">=1.0.0",
|
||||||
|
"vue": "^3.3.0",
|
||||||
|
"webpack-plugin-vuetify": ">=2.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vite-plugin-vuetify": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"webpack-plugin-vuetify": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vuex": {
|
"node_modules/vuex": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz",
|
||||||
@@ -1989,6 +2147,34 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||||
|
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlhttprequest-ssl": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,14 @@
|
|||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
"mitt": "^3.0.1",
|
||||||
|
"socket.io-client": "^4.8.1",
|
||||||
"tinymce": "^7.3.0",
|
"tinymce": "^7.3.0",
|
||||||
"vue": "~3.4.31",
|
"vue": "~3.4.31",
|
||||||
"vue-i18n": "^10.0.0-beta.2",
|
"vue-i18n": "^10.0.0-beta.2",
|
||||||
"vue-multiselect": "^3.1.0",
|
"vue-multiselect": "^3.1.0",
|
||||||
"vue-router": "^4.0.13",
|
"vue-router": "^4.0.13",
|
||||||
|
"vuetify": "^3.7.4",
|
||||||
"vuex": "^4.1.0"
|
"vuex": "^4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
22
frontend/src/api/friendshipApi.js
Normal file
22
frontend/src/api/friendshipApi.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import apiClient from "@/utils/axios.js";
|
||||||
|
|
||||||
|
export const getFriendships = async (acceptedOnly) => {
|
||||||
|
const response = await apiClient.get(`/api/friendships?acceptedOnly=${acceptedOnly}`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const endFriendship = async (friendUserId) => {
|
||||||
|
await apiClient.post("/api/friendships/end", { friendUserId });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const acceptFriendship = async (friendUserId) => {
|
||||||
|
await apiClient.post("/api/friendships/accept", { friendUserId });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const rejectFriendship = async (friendUserId) => {
|
||||||
|
await apiClient.post("/api/friendships/reject", { friendUserId });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const withdrawRequest = async (friendUserId) => {
|
||||||
|
await apiClient.post("/api/friendships/withdraw", { friendUserId });
|
||||||
|
};
|
||||||
@@ -93,3 +93,23 @@ span.button:hover {
|
|||||||
color: #7E471B;
|
color: #7E471B;
|
||||||
border: 1px solid #7E471B;
|
border: 1px solid #7E471B;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.font-color-gender-male {
|
||||||
|
color: #1E90FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-female {
|
||||||
|
color: #FF69B4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-transmale {
|
||||||
|
color: #00CED1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-transfemale {
|
||||||
|
color: #FFB6C1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-nonbinary {
|
||||||
|
color: #DAA520;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem" @click="openPage(item.path ?? null, !!item.children)">
|
<li v-for="(item, key) in menu" :key="key" class="mainmenuitem"
|
||||||
|
@click="openPage(item.path ?? null, !!item.children)">
|
||||||
<span v-if="item.icon" :style="`background-image:url('/images/icons/${item.icon}')`"
|
<span v-if="item.icon" :style="`background-image:url('/images/icons/${item.icon}')`"
|
||||||
class="menu-icon"> </span>
|
class="menu-icon"> </span>
|
||||||
<span>{{ $t(`navigation.${key}`) }}</span>
|
<span>{{ $t(`navigation.${key}`) }}</span>
|
||||||
@@ -18,6 +19,15 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
<li v-if="item.showLoggedinFriends === 1 && friendsList.length > 0" v-for="friend in friendsList" :key="friend.id">
|
||||||
|
{{ friend.username }}
|
||||||
|
<ul class="submenu2">
|
||||||
|
<li @click="openChat(friend.id)">{{ $t('navigation.m-friends.chat') }}</li>
|
||||||
|
<li @click="openProfile(friend.id)">{{ $t('navigation.m-friends.profile') }}</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="submenu1">
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -39,28 +49,44 @@ export default {
|
|||||||
name: 'AppNavigation',
|
name: 'AppNavigation',
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
forumList: []
|
forumList: [],
|
||||||
|
friendsList: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters(['menu', 'user']),
|
...mapGetters(['menu', 'user', 'menuNeedsUpdate']),
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
menuNeedsUpdate(newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
console.log('Menu needs update, loading menu...');
|
||||||
|
this.loadMenu();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (this.user && this.user.id) {
|
if (this.user && this.user.id) {
|
||||||
this.loadMenu();
|
this.loadMenu();
|
||||||
this.fetchForums();
|
this.fetchForums();
|
||||||
|
this.fetchFriends();
|
||||||
} else {
|
} else {
|
||||||
console.log(this.user);
|
console.log(this.user);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.forumInterval = setInterval(() => {
|
if (this.$store.getters.socket) {
|
||||||
|
this.$store.getters.socket.on('forumschanged', (data) => {
|
||||||
this.fetchForums();
|
this.fetchForums();
|
||||||
}, 60000);
|
});
|
||||||
|
this.$store.getters.socket.on('friendloginchanged', () => {
|
||||||
|
this.fetchFriends();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeUnmount() {
|
||||||
if (this.forumInterval) {
|
if (this.$store.getters.socket) {
|
||||||
clearInterval(this.forumInterval);
|
this.$store.getters.socket.off('forumschanged');
|
||||||
|
this.$store.getters.socket.off('friendloginchanged');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -70,7 +96,6 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (url) {
|
if (url) {
|
||||||
console.log('openPage', url);
|
|
||||||
this.$router.push(url);
|
this.$router.push(url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -87,6 +112,29 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching forums:', error);
|
console.error('Error fetching forums:', error);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchFriends() {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get('/api/socialnetwork/friends/loggedin');
|
||||||
|
this.friendsList = response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching friends:', error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async openProfile(hashedId) {
|
||||||
|
console.log(hashedId);
|
||||||
|
this.$root.$refs.userProfileDialog.userId = hashedId;
|
||||||
|
this.$root.$refs.userProfileDialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
async openChat(hashedId) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
<span @click="$emit('delete-folder', folder)" class="icon delete-icon" title="Delete folder">✖</span>
|
<span @click="$emit('delete-folder', folder)" class="icon delete-icon" title="Delete folder">✖</span>
|
||||||
</span>
|
</span>
|
||||||
<template v-for="i in depth">
|
<template v-for="i in depth">
|
||||||
<span v-if="showPipe(i)" class="marker filler">|</span>
|
<span v-if="showPipe(i)" class="marker filler tee">|</span>
|
||||||
<span v-else class="marker filler"> </span>
|
<span v-else class="marker filler"> </span>
|
||||||
</template>
|
</template>
|
||||||
<span v-if="isLastItem" class="end-marker marker">⌞</span>
|
<span v-if="isLastItem" class="end-marker marker">⌞</span>
|
||||||
<span v-else class="marker">├</span>
|
<span v-else class="marker tee">├</span>
|
||||||
<span class="folder-name-text"> {{ folder.name }}</span>
|
<span class="folder-name-text"> {{ folder.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -115,4 +115,8 @@ export default {
|
|||||||
.folder-name-text {
|
.folder-name-text {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tee {
|
||||||
|
margin: 0 1px 0 -1px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,12 +5,10 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ $t("socialnetwork.gallery.create_folder_dialog.parent_folder") }}</label>
|
<label>{{ $t("socialnetwork.gallery.create_folder_dialog.parent_folder") }}</label>
|
||||||
<!-- Hier wird der übergeordnete Ordner angezeigt, aber nicht bearbeitbar -->
|
|
||||||
<input type="text" :value="parentFolder.name" disabled />
|
<input type="text" :value="parentFolder.name" disabled />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="folderTitle">{{ $t("socialnetwork.gallery.create_folder_dialog.folder_title") }}</label>
|
<label for="folderTitle">{{ $t("socialnetwork.gallery.create_folder_dialog.folder_title") }}</label>
|
||||||
<!-- Setze den Titel des Ordners für Bearbeiten -->
|
|
||||||
<input type="text" v-model="folderTitle"
|
<input type="text" v-model="folderTitle"
|
||||||
:placeholder="$t('socialnetwork.gallery.create_folder_dialog.folder_title')" required />
|
:placeholder="$t('socialnetwork.gallery.create_folder_dialog.folder_title')" required />
|
||||||
</div>
|
</div>
|
||||||
@@ -42,6 +40,7 @@ import Multiselect from 'vue-multiselect';
|
|||||||
import DialogWidget from '@/components/DialogWidget.vue';
|
import DialogWidget from '@/components/DialogWidget.vue';
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import apiClient from '@/utils/axios.js';
|
import apiClient from '@/utils/axios.js';
|
||||||
|
import { EventBus } from '@/utils/eventBus.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CreateFolderDialog',
|
name: 'CreateFolderDialog',
|
||||||
@@ -90,7 +89,6 @@ export default {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading visibility options:', error);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async createFolder() {
|
async createFolder() {
|
||||||
@@ -105,11 +103,11 @@ export default {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (this.parentFolder.id) {
|
if (this.parentFolder.id) {
|
||||||
await apiClient.put(`/api/socialnetwork/folders/${this.parentFolder.id}`, payload);
|
await apiClient.post(`/api/socialnetwork/folders/${this.parentFolder.id}`, payload);
|
||||||
} else {
|
} else {
|
||||||
await apiClient.post(`/api/socialnetwork/folders/${this.folderId}`, payload);
|
await apiClient.post(`/api/socialnetwork/folders/${this.folderId}`, payload);
|
||||||
}
|
}
|
||||||
this.$emit('created', payload);
|
EventBus.emit('folderCreated');
|
||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Erstellen/Bearbeiten des Ordners:', error);
|
console.error('Fehler beim Erstellen/Bearbeiten des Ordners:', error);
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
name="UserProfileDialog" display="flex">
|
name="UserProfileDialog" display="flex">
|
||||||
<div class="activities">
|
<div class="activities">
|
||||||
<span>{{ $t(`socialnetwork.friendship.state.${friendshipState}`) }}</span>
|
<span>{{ $t(`socialnetwork.friendship.state.${friendshipState}`) }}</span>
|
||||||
<img v-if="['none', 'denied', 'withdrawn'].includes(friendshipState)" src="/images/icons/request-friendship.png"
|
<img :src="'/images/icons/' +
|
||||||
@click="handleFriendship()" />
|
(['none', 'denied', 'withdrawn'].includes(friendshipState) ? 'request-friendship.png' : 'cancel-friendship.png')"
|
||||||
<img v-else-if="['accepted', 'open']" src="/images/icons/cancel-friendship.png"
|
|
||||||
@click="handleFriendship()" />
|
@click="handleFriendship()" />
|
||||||
</div>
|
</div>
|
||||||
<div class="profile-content">
|
<div class="profile-content">
|
||||||
@@ -320,7 +319,7 @@ export default {
|
|||||||
},
|
},
|
||||||
async handleFriendship() {
|
async handleFriendship() {
|
||||||
console.log(this.friendshipState);
|
console.log(this.friendshipState);
|
||||||
if (this.friendshipState === 'none') {
|
if (['none', 'withdrawn'].includes(this.friendshipState)) {
|
||||||
this.requestFriendship();
|
this.requestFriendship();
|
||||||
} else if (this.friendshipState === 'waiting') {
|
} else if (this.friendshipState === 'waiting') {
|
||||||
this.cancelFriendship();
|
this.cancelFriendship();
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import enActivate from './locales/en/activate.json';
|
|||||||
import enSettings from './locales/en/settings.json';
|
import enSettings from './locales/en/settings.json';
|
||||||
import enAdmin from './locales/en/admin.json';
|
import enAdmin from './locales/en/admin.json';
|
||||||
import enSocialNetwork from './locales/en/socialnetwork.json';
|
import enSocialNetwork from './locales/en/socialnetwork.json';
|
||||||
|
import enFriends from './locales/en/friends.json';
|
||||||
|
|
||||||
import deGeneral from './locales/de/general.json';
|
import deGeneral from './locales/de/general.json';
|
||||||
import deHeader from './locales/de/header.json';
|
import deHeader from './locales/de/header.json';
|
||||||
@@ -24,6 +25,7 @@ import deActivate from './locales/de/activate.json';
|
|||||||
import deSettings from './locales/de/settings.json';
|
import deSettings from './locales/de/settings.json';
|
||||||
import deAdmin from './locales/de/admin.json';
|
import deAdmin from './locales/de/admin.json';
|
||||||
import deSocialNetwork from './locales/de/socialnetwork.json';
|
import deSocialNetwork from './locales/de/socialnetwork.json';
|
||||||
|
import deFriends from './locales/de/friends.json';
|
||||||
|
|
||||||
const messages = {
|
const messages = {
|
||||||
en: {
|
en: {
|
||||||
@@ -38,6 +40,7 @@ const messages = {
|
|||||||
...enSettings,
|
...enSettings,
|
||||||
...enAdmin,
|
...enAdmin,
|
||||||
...enSocialNetwork,
|
...enSocialNetwork,
|
||||||
|
...enFriends,
|
||||||
},
|
},
|
||||||
de: {
|
de: {
|
||||||
'Ok': 'Ok',
|
'Ok': 'Ok',
|
||||||
@@ -52,6 +55,7 @@ const messages = {
|
|||||||
...deSettings,
|
...deSettings,
|
||||||
...deAdmin,
|
...deAdmin,
|
||||||
...deSocialNetwork,
|
...deSocialNetwork,
|
||||||
|
...deFriends,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
23
frontend/src/i18n/locales/de/friends.json
Normal file
23
frontend/src/i18n/locales/de/friends.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"friends": {
|
||||||
|
"title": "Freunde",
|
||||||
|
"tabs": {
|
||||||
|
"existing": "Bestehende",
|
||||||
|
"rejected": "Abgelehnte",
|
||||||
|
"pending": "Ausstehende",
|
||||||
|
"requested": "Angefragte"
|
||||||
|
},
|
||||||
|
"actions": {
|
||||||
|
"end": "Beenden",
|
||||||
|
"accept": "Annehmen",
|
||||||
|
"reject": "Ablehnen",
|
||||||
|
"withdraw": "Zurückziehen"
|
||||||
|
},
|
||||||
|
"headers": {
|
||||||
|
"name": "Name",
|
||||||
|
"age": "Alter",
|
||||||
|
"gender": "Geschlecht",
|
||||||
|
"actions": "Aktionen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,5 +31,15 @@
|
|||||||
"OK": "Ok",
|
"OK": "Ok",
|
||||||
"Cancel": "Abbrechen",
|
"Cancel": "Abbrechen",
|
||||||
"yes": "Ja",
|
"yes": "Ja",
|
||||||
"no": "Nein"
|
"no": "Nein",
|
||||||
|
"message": {
|
||||||
|
"close": "Schließen"
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"male": "Männlich",
|
||||||
|
"female": "Weiblich",
|
||||||
|
"transmale": "Trans-Mann",
|
||||||
|
"transfemale": "Trans-Frau",
|
||||||
|
"nonbinary": "Nichtbinär"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,9 @@
|
|||||||
"falukant": "Falukant"
|
"falukant": "Falukant"
|
||||||
},
|
},
|
||||||
"m-friends": {
|
"m-friends": {
|
||||||
"manageFriends": "Freunde verwalten"
|
"manageFriends": "Freunde verwalten",
|
||||||
|
"chat": "Chatten",
|
||||||
|
"profile": "Profil"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
frontend/src/i18n/locales/en/friends.json
Normal file
3
frontend/src/i18n/locales/en/friends.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -4,6 +4,9 @@ import store from './store';
|
|||||||
import router from './router';
|
import router from './router';
|
||||||
import './assets/styles.scss';
|
import './assets/styles.scss';
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
|
import { createVuetify } from 'vuetify';
|
||||||
|
import * as components from 'vuetify/components';
|
||||||
|
import * as directives from 'vuetify/directives';
|
||||||
|
|
||||||
function getBrowserLanguage() {
|
function getBrowserLanguage() {
|
||||||
const browserLanguage = navigator.language || navigator.languages[0];
|
const browserLanguage = navigator.language || navigator.languages[0];
|
||||||
@@ -14,6 +17,11 @@ function getBrowserLanguage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const vuetify = createVuetify({
|
||||||
|
components,
|
||||||
|
directives,
|
||||||
|
});
|
||||||
|
|
||||||
store.dispatch('setLanguage', getBrowserLanguage());
|
store.dispatch('setLanguage', getBrowserLanguage());
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
@@ -21,6 +29,7 @@ const app = createApp(App);
|
|||||||
app.use(store);
|
app.use(store);
|
||||||
app.use(router);
|
app.use(router);
|
||||||
app.use(i18n);
|
app.use(i18n);
|
||||||
|
app.use(vuetify);
|
||||||
|
|
||||||
store.dispatch('loadLoginState');
|
store.dispatch('loadLoginState');
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import DiaryView from '../views/social/DiaryView.vue';
|
|||||||
import ForumAdminView from '../dialogues/admin/ForumAdminView.vue';
|
import ForumAdminView from '../dialogues/admin/ForumAdminView.vue';
|
||||||
import ForumView from '../views/social/ForumView.vue';
|
import ForumView from '../views/social/ForumView.vue';
|
||||||
import ForumTopicView from '../views/social/ForumTopicView.vue';
|
import ForumTopicView from '../views/social/ForumTopicView.vue';
|
||||||
|
import FriendsView from '../views/social/FriendsView.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -29,6 +30,12 @@ const routes = [
|
|||||||
name: 'Activate page',
|
name: 'Activate page',
|
||||||
component: ActivateView
|
component: ActivateView
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/friends',
|
||||||
|
name: 'Friends',
|
||||||
|
component: FriendsView,
|
||||||
|
meta: { requiresAuth: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/socialnetwork/guestbook',
|
path: '/socialnetwork/guestbook',
|
||||||
name: 'Guestbook',
|
name: 'Guestbook',
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { createStore } from 'vuex';
|
|||||||
import dialogs from './modules/dialogs';
|
import dialogs from './modules/dialogs';
|
||||||
import loadMenu from '../utils/menuLoader.js';
|
import loadMenu from '../utils/menuLoader.js';
|
||||||
import router from '../router';
|
import router from '../router';
|
||||||
|
import apiClient from '../utils/axios.js';
|
||||||
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
state: {
|
state: {
|
||||||
@@ -9,6 +11,8 @@ const store = createStore({
|
|||||||
user: null,
|
user: null,
|
||||||
language: navigator.language.startsWith('de') ? 'de' : 'en',
|
language: navigator.language.startsWith('de') ? 'de' : 'en',
|
||||||
menu: [],
|
menu: [],
|
||||||
|
socket: null,
|
||||||
|
menuNeedsUpdate: false,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
async dologin(state, user) {
|
async dologin(state, user) {
|
||||||
@@ -16,48 +20,74 @@ const store = createStore({
|
|||||||
state.user = user;
|
state.user = user;
|
||||||
localStorage.setItem('isLoggedIn', 'true');
|
localStorage.setItem('isLoggedIn', 'true');
|
||||||
localStorage.setItem('user', JSON.stringify(user));
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
state.menuNeedsUpdate = true;
|
||||||
if (user.param.filter(param => ['birthdate', 'gender'].includes(param.name)).length < 2) {
|
if (user.param.filter(param => ['birthdate', 'gender'].includes(param.name)).length < 2) {
|
||||||
router.push({ path: '/settings/personal' });
|
router.push({ path: '/settings/personal' });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dologout(state) {
|
async dologout(state) {
|
||||||
state.isLoggedIn = false;
|
state.isLoggedIn = false;
|
||||||
state.user = null;
|
state.user = null;
|
||||||
localStorage.removeItem('isLoggedIn');
|
localStorage.removeItem('isLoggedIn');
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
localStorage.removeItem('menu');
|
localStorage.removeItem('menu');
|
||||||
},
|
state.menuNeedsUpdate = false;
|
||||||
loadLoginState(state) {
|
// await apiClient.get('/api/auth/logout');
|
||||||
const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
|
|
||||||
let userData = {};
|
|
||||||
try {
|
|
||||||
userData = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : {};
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
const menu = localStorage.getItem('menu');
|
|
||||||
const user = userData;
|
|
||||||
state.isLoggedIn = isLoggedIn;
|
|
||||||
state.user = user;
|
|
||||||
state.menu = menu;
|
|
||||||
},
|
},
|
||||||
setLanguage(state, language) {
|
setLanguage(state, language) {
|
||||||
state.language = language;
|
state.language = language;
|
||||||
},
|
},
|
||||||
setMenu(state, menu) {
|
setMenu(state, menu) {
|
||||||
state.menu = menu;
|
state.menu = menu;
|
||||||
}
|
state.menuNeedsUpdate = false;
|
||||||
|
},
|
||||||
|
setSocket(state, socket) {
|
||||||
|
state.socket = socket;
|
||||||
|
},
|
||||||
|
clearSocket(state) {
|
||||||
|
state.socket = null;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async login({ commit, dispatch }, user) {
|
async login({ commit, dispatch }, user) {
|
||||||
|
console.log('login', user);
|
||||||
await commit('dologin', user);
|
await commit('dologin', user);
|
||||||
|
await dispatch('initializeSocket');
|
||||||
|
const socket = this.getters.socket;
|
||||||
|
if (socket) {
|
||||||
|
console.log('Emitting setUserId:', user.id);
|
||||||
|
socket.emit('setUserId', user.id);
|
||||||
|
console.log('setUserId emitted successfully');
|
||||||
|
} else {
|
||||||
|
console.error('Socket not initialized');
|
||||||
|
}
|
||||||
await dispatch('loadMenu');
|
await dispatch('loadMenu');
|
||||||
dispatch('startMenuReload');
|
dispatch('startMenuReload');
|
||||||
},
|
},
|
||||||
logout({ commit }) {
|
logout({ commit, state }) {
|
||||||
|
if (state.socket) {
|
||||||
|
state.socket.disconnect();
|
||||||
|
commit('clearSocket');
|
||||||
|
}
|
||||||
commit('dologout');
|
commit('dologout');
|
||||||
router.push('/');
|
router.push('/');
|
||||||
},
|
},
|
||||||
|
initializeSocket({ commit, state }) {
|
||||||
|
if (state.isLoggedIn && state.user) {
|
||||||
|
const socket = io(import.meta.env.VITE_API_BASE_URL); // oder Ihre URL
|
||||||
|
console.log('Socket initialized:', socket);
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('Socket connected:', socket.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', (reason) => {
|
||||||
|
console.log('Socket disconnected:', reason);
|
||||||
|
});
|
||||||
|
|
||||||
|
commit('setSocket', socket);
|
||||||
|
}
|
||||||
|
},
|
||||||
loadLoginState({ commit }) {
|
loadLoginState({ commit }) {
|
||||||
commit('loadLoginState');
|
commit('loadLoginState');
|
||||||
},
|
},
|
||||||
@@ -73,17 +103,13 @@ const store = createStore({
|
|||||||
commit('setMenu', []);
|
commit('setMenu', []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
startMenuReload({ dispatch }) {
|
|
||||||
setInterval(() => {
|
|
||||||
dispatch('loadMenu');
|
|
||||||
}, 10000);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
isLoggedIn: state => state.isLoggedIn,
|
isLoggedIn: state => state.isLoggedIn,
|
||||||
user: state => state.user,
|
user: state => state.user,
|
||||||
language: state => state.language,
|
language: state => state.language,
|
||||||
menu: state => state.menu,
|
menu: state => state.menu,
|
||||||
|
socket: state => state.socket,
|
||||||
},
|
},
|
||||||
modules: {
|
modules: {
|
||||||
dialogs,
|
dialogs,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import axios from 'axios';
|
|||||||
import store from '../store';
|
import store from '../store';
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
baseURL: import.meta.env.VUE_APP_API_BASE_URL || 'http://localhost:3001',
|
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
@@ -19,4 +19,13 @@ apiClient.interceptors.request.use(config => {
|
|||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
apiClient.interceptors.response.use(response => {
|
||||||
|
return response;
|
||||||
|
}, error => {
|
||||||
|
if (error.response && error.response.status === 401) {
|
||||||
|
store.dispatch('logout');
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
export default apiClient;
|
export default apiClient;
|
||||||
|
|||||||
2
frontend/src/utils/eventBus.js
Normal file
2
frontend/src/utils/eventBus.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
import mitt from 'mitt';
|
||||||
|
export const EventBus = mitt();
|
||||||
@@ -10,7 +10,6 @@ const loadMenu = async () => {
|
|||||||
const response = await apiClient.get('/api/navigation/' + userId);
|
const response = await apiClient.get('/api/navigation/' + userId);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
208
frontend/src/views/social/FriendsView.vue
Normal file
208
frontend/src/views/social/FriendsView.vue
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>{{ $t('friends.title') }}</h2>
|
||||||
|
<div class="tabs-container">
|
||||||
|
<div class="tab" v-for="(tab, index) in tabs" :key="tab.name" :class="{ active: activeTab === index }"
|
||||||
|
@click="selectTab(index)">
|
||||||
|
{{ $t(tab.label) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-for="(tab, index) in tabs" v-show="activeTab === index" :key="tab.name">
|
||||||
|
<v-data-table :items="paginatedData(tab.data, tab.pagination.page)" :headers="headers"
|
||||||
|
:items-per-page="tab.pagination.itemsPerPage" class="elevation-1">
|
||||||
|
<template v-slot:body="{ items }">
|
||||||
|
<tr v-for="(item, index) in items" :key="index">
|
||||||
|
<td :class="'font-color-gender-' + item.user.gender.toLowerCase().replace(' ', '')">
|
||||||
|
{{ item.user.username }}
|
||||||
|
</td>
|
||||||
|
<td>{{ item.user.age }}</td>
|
||||||
|
<td>{{ $t(`gender.${item.user.gender}`) }}</td>
|
||||||
|
<td>
|
||||||
|
<template v-if="tab.name === 'existingFriends'">
|
||||||
|
<v-btn color="error" @click="endFriendship(item.user.hashedId)">{{ $t('friends.actions.end')
|
||||||
|
}}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-if="tab.name === 'pendingFriends'">
|
||||||
|
<v-btn color="success" @click="acceptFriendship(item.user.hashedId)">{{
|
||||||
|
$t('friends.actions.accept') }}</v-btn>
|
||||||
|
<v-btn color="error" @click="rejectFriendship(item.user.hashedId)">{{
|
||||||
|
$t('friends.actions.reject') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-if="tab.name === 'requestedFriends'">
|
||||||
|
<v-btn color="warning" @click="withdrawRequest(item.user.hashedId)">{{
|
||||||
|
$t('friends.actions.withdraw') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
<template v-if="tab.name === 'rejectedFriends'">
|
||||||
|
<v-btn color="success" @click="acceptFriendship(item.user.hashedId)">{{
|
||||||
|
$t('friends.actions.accept') }}</v-btn>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
<v-pagination v-model="tab.pagination.page"
|
||||||
|
:length="Math.ceil(tab.data.length / tab.pagination.itemsPerPage)" :total-visible="5"
|
||||||
|
class="mt-4"></v-pagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getFriendships, endFriendship, acceptFriendship, rejectFriendship, withdrawRequest } from "@/api/friendshipApi";
|
||||||
|
import { mapActions, mapGetters } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "FriendsView",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeTab: 0,
|
||||||
|
headers: [
|
||||||
|
{ text: this.$t("friends.headers.name"), value: "name" },
|
||||||
|
{ text: this.$t("friends.headers.age"), value: "age" },
|
||||||
|
{ text: this.$t("friends.headers.gender"), value: "gender" },
|
||||||
|
{ text: this.$t("friends.headers.actions"), value: "actions", sortable: false },
|
||||||
|
],
|
||||||
|
tabs: [
|
||||||
|
{ name: "existingFriends", label: "friends.tabs.existing", data: [], pagination: { page: 1, itemsPerPage: 10 } },
|
||||||
|
{ name: "pendingFriends", label: "friends.tabs.pending", data: [], pagination: { page: 1, itemsPerPage: 10 } },
|
||||||
|
{ name: "requestedFriends", label: "friends.tabs.requested", data: [], pagination: { page: 1, itemsPerPage: 10 } },
|
||||||
|
{ name: "rejectedFriends", label: "friends.tabs.rejected", data: [], pagination: { page: 1, itemsPerPage: 10 } },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapGetters(['socket', 'user']),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchFriendships() {
|
||||||
|
try {
|
||||||
|
const friendships = await getFriendships(false);
|
||||||
|
this.distributeFriendships(friendships);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching friendships:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
distributeFriendships(friendships) {
|
||||||
|
this.tabs.forEach(tab => (tab.data = []));
|
||||||
|
friendships.forEach(friendship => {
|
||||||
|
if (friendship.accepted) {
|
||||||
|
this.tabs.find(tab => tab.name === "existingFriends").data.push(friendship);
|
||||||
|
} else if (friendship.denied) {
|
||||||
|
const tabName = friendship.initiatorId === this.$store.state.user.hashedId
|
||||||
|
? "requestedFriends"
|
||||||
|
: "rejectedFriends";
|
||||||
|
this.tabs.find(tab => tab.name === tabName).data.push(friendship);
|
||||||
|
} else if (friendship.withdrawn) {
|
||||||
|
this.tabs.find(tab => tab.name === "requestedFriends").data.push(friendship);
|
||||||
|
} else {
|
||||||
|
const tabName = friendship.isInitiator
|
||||||
|
? "requestedFriends"
|
||||||
|
: "pendingFriends";
|
||||||
|
this.tabs.find(tab => tab.name === tabName).data.push(friendship);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectTab(index) {
|
||||||
|
this.activeTab = index;
|
||||||
|
},
|
||||||
|
paginatedData(data, page) {
|
||||||
|
const start = (page - 1) * 10;
|
||||||
|
const end = start + 10;
|
||||||
|
return data.slice(start, end);
|
||||||
|
},
|
||||||
|
async endFriendship(friendUserId) {
|
||||||
|
try {
|
||||||
|
await endFriendship(friendUserId);
|
||||||
|
this.fetchFriendships();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error ending friendship:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async acceptFriendship(friendUserId) {
|
||||||
|
try {
|
||||||
|
await acceptFriendship(friendUserId);
|
||||||
|
this.fetchFriendships();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error accepting friendship:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async rejectFriendship(friendUserId) {
|
||||||
|
try {
|
||||||
|
await rejectFriendship(friendUserId);
|
||||||
|
this.fetchFriendships();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error rejecting friendship:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async withdrawRequest(friendUserId) {
|
||||||
|
try {
|
||||||
|
await withdrawRequest(friendUserId);
|
||||||
|
this.fetchFriendships();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error withdrawing request:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setupSocketListener() {
|
||||||
|
if (this.socket) {
|
||||||
|
console.log("Setting up friendshipChanged listener");
|
||||||
|
this.socket.on("friendshipChanged", (data) => {
|
||||||
|
console.log("Friendship changed:", data);
|
||||||
|
this.fetchFriendships();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error("Socket not initialized");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchFriendships();
|
||||||
|
this.setupSocketListener();
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.socket) {
|
||||||
|
this.socket.off("friendshipChanged");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.tabs-container {
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid #999;
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 2px 4px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid #999;
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab:not(.active):hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background-color: #F9A22C;
|
||||||
|
color: #7E471B;
|
||||||
|
border-color: #F9A22C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-male {
|
||||||
|
color: #1E90FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-female {
|
||||||
|
color: #FF69B4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-color-gender-nonbinary {
|
||||||
|
color: #DAA520;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -82,6 +82,7 @@ import apiClient from '@/utils/axios.js';
|
|||||||
import Multiselect from 'vue-multiselect';
|
import Multiselect from 'vue-multiselect';
|
||||||
import FolderItem from '../../components/FolderItem.vue';
|
import FolderItem from '../../components/FolderItem.vue';
|
||||||
import 'vue-multiselect/dist/vue-multiselect.min.css';
|
import 'vue-multiselect/dist/vue-multiselect.min.css';
|
||||||
|
import { EventBus } from '@/utils/eventBus.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -107,6 +108,10 @@ export default {
|
|||||||
if (this.folders) {
|
if (this.folders) {
|
||||||
this.selectFolder(this.folders);
|
this.selectFolder(this.folders);
|
||||||
}
|
}
|
||||||
|
EventBus.on('folderCreated', this.loadFolders);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
EventBus.off('folderCreated', this.loadFolders);
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadFolders() {
|
async loadFolders() {
|
||||||
|
|||||||
@@ -23,5 +23,5 @@ export default defineConfig({
|
|||||||
util: 'util',
|
util: 'util',
|
||||||
assert: 'assert',
|
assert: 'assert',
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
759
package-lock.json
generated
759
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user