From 597761cb15a67c7e1070e431c02f6bcf09370402 Mon Sep 17 00:00:00 2001 From: Torsten Schulz Date: Sun, 21 Jul 2024 13:09:56 +0200 Subject: [PATCH] added login, first preparation for menu --- backend/app.js | 6 +- backend/controllers/authController.js | 82 ++------ backend/controllers/navigationController.js | 208 ++++++++++++++++++++ backend/models/community/user.js | 3 - backend/routers/navigationRouter.js | 7 + backend/services/authService.js | 78 ++++++++ frontend/src/components/AppNavigation.vue | 26 +-- frontend/src/store/index.js | 29 ++- frontend/src/utils/menuLoader.js | 19 ++ frontend/src/views/home/NoLoginView.vue | 33 ++-- 10 files changed, 391 insertions(+), 100 deletions(-) create mode 100644 backend/controllers/navigationController.js create mode 100644 backend/routers/navigationRouter.js create mode 100644 backend/services/authService.js create mode 100644 frontend/src/utils/menuLoader.js diff --git a/backend/app.js b/backend/app.js index 2ceb25b..e746827 100644 --- a/backend/app.js +++ b/backend/app.js @@ -3,6 +3,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import chatRouter from './routers/chatRouter.js'; import authRouter from './routers/authRouter.js'; +import navigationRouter from './routers/navigationRouter.js' import cors from 'cors'; const __filename = fileURLToPath(import.meta.url); @@ -15,10 +16,11 @@ app.use(express.json()); // To handle JSON request bodies app.use('/api/chat', chatRouter); app.use('/api/auth', authRouter); +app.use('/api/navigation', navigationRouter); app.use('/images', express.static(path.join(__dirname, '../frontend/public/images'))); -app.get('*', (req, res) => { - res.sendFile(path.join(__dirname, '../frontend/dist/index.html')); +app.use((req, res) => { + res.status(404).send('404 Not Found'); }); export default app; diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js index bb305f0..f40f57b 100644 --- a/backend/controllers/authController.js +++ b/backend/controllers/authController.js @@ -1,40 +1,11 @@ -import bcrypt from 'bcrypt'; -import { v4 as uuidv4 } from 'uuid'; -import User from '../models/community/user.js'; -import UserParam from '../models/community/user_param.js'; -import UserParamType from '../models/type/user_param.js'; -import { sendAccountActivationEmail, sendPasswordResetEmail } from '../services/emailService.js'; -import i18n from '../utils/i18n.js'; - -const saltRounds = 10; +import * as userService from '../services/authService.js'; export const register = async (req, res) => { const { email, username, password, language } = req.body; try { - const hashedPassword = await bcrypt.hash(password, saltRounds); - const resetToken = uuidv4(); - const user = await User.create({ - email, - username, - password: hashedPassword, - resetToken: resetToken, - active: false, - registration_date: new Date() - }); - const languageType = await UserParamType.findOne({ where: { description: 'language' } }); - if (!languageType) { - return res.status(500).json({ error: 'Language type not found' }); - } - console.log(user.id, languageType.id); - await UserParam.create({ - userId: user.id, - paramTypeId: languageType.id, - value: language - }); - const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`; - await sendAccountActivationEmail(email, activationLink, username, resetToken, language); - res.status(201).json({ id: user.hashedId, username: user.username, active: user.active }); + const result = await userService.registerUser({ email, username, password, language }); + res.status(201).json(result); } catch (error) { console.log(error); res.status(500).json({ error: error.message }); @@ -42,56 +13,33 @@ export const register = async (req, res) => { }; export const login = async (req, res) => { - const { email, password } = req.body; + const { username, password } = req.body; try { - const user = await User.findOne({ where: { email } }); - if (!user) { - return res.status(401).json({ error: 'Invalid email or password' }); - } - if (!user.active) { - return res.status(403).json({ error: 'Account not activated' }); - } - const match = await bcrypt.compare(password, user.password); - if (!match) { - return res.status(401).json({ error: 'Invalid email or password' }); - } - res.status(200).json({ id: user.hashed_id, username: user.username }); + const result = await userService.loginUser({ username, password }); + res.status(200).json(result); } catch (error) { - res.status(500).json({ error: 'Error logging in' }); + res.status(500).json({ error: error.message }); } }; export const forgotPassword = async (req, res) => { const { email } = req.body; + try { - const user = await User.findOne({ where: { email } }); - if (!user) { - return res.status(404).json({ error: 'Email not found' }); - } - const resetToken = uuidv4(); - const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`; - await user.update({ reset_token: resetToken }); - - const languageParam = await UserParam.findOne({ where: { user_id: user.id, param_type_id: languageType.id } }); - const userLanguage = languageParam ? languageParam.value : 'en'; - - await sendPasswordResetEmail(email, resetLink, userLanguage); - res.status(200).json({ message: 'Password reset email sent' }); + const result = await userService.handleForgotPassword({ email }); + res.status(200).json(result); } catch (error) { - res.status(500).json({ error: 'Error processing forgot password' }); + res.status(500).json({ error: error.message }); } }; export const activateAccount = async (req, res) => { const { token } = req.body; + try { - const user = await User.findOne({ where: { reset_token: token } }); - if (!user) { - return res.status(404).json({ error: 'Invalid token' }); - } - await user.update({ active: true, reset_token: null }); - res.status(200).json({ message: 'Account activated' }); + const result = await userService.activateUserAccount({ token }); + res.status(200).json(result); } catch (error) { - res.status(500).json({ error: 'Error activating account' }); + res.status(500).json({ error: error.message }); } }; diff --git a/backend/controllers/navigationController.js b/backend/controllers/navigationController.js new file mode 100644 index 0000000..a8f7006 --- /dev/null +++ b/backend/controllers/navigationController.js @@ -0,0 +1,208 @@ +const menuStructure = { + home: { + visible: ["all"], + children: {}, + path: "/" + }, + friends: { + visible: ["all"], + children: { + manageFriends : { + visible: ["all"], + path: "/socialnetwork/friends" + } + }, + showLoggedinFriends: 1 + }, + socialnetwork: { + visible: ["all"], + children: { + guestbook: { + visible: ["all"], + path: "/socialnetwork/guestbook" + }, + usersearch: { + visible: ["all"], + path: "/socialnetwork/search" + }, + forum: { + visible: ["all"], + path: "/socialnetwork/forum", + showForums: 1 + }, + gallery: { + visible: ["all"], + path: "/socialnetwork/gallery" + }, + blockedUsers: { + visible: ["all"], + path: "/socialnetwork/blocked" + }, + oneTimeInvitation: { + visible: ["all"], + path: "/socialnetwork/onetimeinvitation" + }, + diary: { + visible: ["all"], + path: "/socialnetwork/diary" + } + } + }, + chats: { + visible: ["all"], + children: { + multiChat: { + visible: ["over12"], + action: "openMultiChat" + }, + randomChat: { + visible: ["over12"], + action: "openRanomChat" + } + } + }, + falukant: { + visible: ["all"], + children: { + create: { + visible: ["nofalukantaccount"], + path: "/falukant/create" + }, + overview: { + visible: ["hasfalukantaccount"], + path: "/falukant/home" + }, + towns: { + visible: ["hasfalukantaccount"], + path: "/falukant/towns" + }, + directors: { + visible: ["hasfalukantaccount"], + path: "/falukant/directors" + }, + factory: { + visible: ["hasfalukantaccount"], + path: "/falukant/factory" + }, + family: { + visible: ["hasfalukantaccount"], + path: "/falukant/family" + }, + house: { + visible: ["hasfalukantaccount"], + path: "/falukant/house" + }, + nobility: { + visible: ["hasfalukantaccount"], + path: "/falukant/nobility" + }, + politics: { + visible: ["hasfalukantaccount"], + path: "/falukant/politics" + }, + education: { + visible: ["hasfalukantaccount"], + path: "/falukant/education" + }, + bank: { + visible: ["hasfalukantaccount"], + path: "/falukant/bank" + }, + darknet: { + visible: ["hasfalukantaccount"], + path: "/falukant/darknet" + }, + reputation: { + visible: ["hasfalukantaccount"], + path: "/falukant/reputation" + }, + moneyhistory: { + visible: ["hasfalukantaccount"], + path: "/falukant/moneyhistory" + } + } + }, + minigames: { + visible: ["all"], + }, + settings: { + visible: ["all"], + children: { + homepage: { + visible: ["all"], + path: "/settings/homepage" + }, + account: { + visible: ["all"], + path: "/settings/account" + }, + personal: { + visible: ["all"], + path: "/settings/account" + }, + view: { + visible: ["all"], + path: "/settings/account" + }, + interrests: { + visible: ["all"], + path: "/settings/interrests" + }, + sexuality: { + visible: ["over14"], + path: "/setting/sexuality" + }, + notifications: { + visible: ["all"], + path: "/settings/notifications" + } + } + }, + administration: { + visible: ["anyadmin"], + children: { + contactrequests: { + visible: ["mainadmin", "contactrequests"], + path: "/admin/contacts" + }, + useradministration: { + visible: ["mainadmin", "useradministration"], + path: "/admin/users" + }, + forum: { + visible: ["mainadmin", "forum"], + path: "/admin/forum" + }, + userrights: { + visible: ["mainadmin", "rights"], + path: "/admin/rights" + }, + interrests: { + visible: ["mainadmin", "interrests"], + path: "/admin/interrests" + }, + falukant: { + visible: ["mainadmin", "falukant"], + children: { + logentries: { + visible: ["mainadmin", "falukant"], + path: "/admin/falukant/logentries" + }, + edituser: { + visible: ["mainadmin", "falukant"], + path: "/admin/falukant/edituser" + }, + database: { + visible: ["mainadmin", "falukant"], + path: "/admin/falukant/database" + }, + } + } + } + } +}; + +export const menu = async (req, res) => { + const { userid } = req.params; + res.status(200).json({ userId: userid }); +} diff --git a/backend/models/community/user.js b/backend/models/community/user.js index c999d2c..ecde458 100644 --- a/backend/models/community/user.js +++ b/backend/models/community/user.js @@ -19,9 +19,6 @@ const User = sequelize.define('user', { password: { type: DataTypes.STRING, allowNull: false, - set(value) { - this.setDataValue('password', bcrypt.hashSync(value, 10)); - } }, registrationDate: { type: DataTypes.DATE, diff --git a/backend/routers/navigationRouter.js b/backend/routers/navigationRouter.js new file mode 100644 index 0000000..7cd50b2 --- /dev/null +++ b/backend/routers/navigationRouter.js @@ -0,0 +1,7 @@ +import { Router } from 'express'; +import { menu } from '../controllers/navigationController.js'; + +const router = Router(); +router.get('/:userid', menu); + +export default router; \ No newline at end of file diff --git a/backend/services/authService.js b/backend/services/authService.js new file mode 100644 index 0000000..0d0937b --- /dev/null +++ b/backend/services/authService.js @@ -0,0 +1,78 @@ +import bcrypt from 'bcrypt'; +import { v4 as uuidv4 } from 'uuid'; +import User from '../models/community/user.js'; +import UserParam from '../models/community/user_param.js'; +import UserParamType from '../models/type/user_param.js'; +import { sendAccountActivationEmail, sendPasswordResetEmail } from './emailService.js'; + +const saltRounds = 10; + +export const registerUser = async ({ email, username, password, language }) => { + const hashedPassword = await bcrypt.hash(password, saltRounds); + const resetToken = uuidv4(); + const user = await User.create({ + email, + username, + password: hashedPassword, + resetToken: resetToken, + active: false, + registration_date: new Date() + }); + + const languageType = await UserParamType.findOne({ where: { description: 'language' } }); + if (!languageType) { + throw new Error('Language type not found'); + } + + await UserParam.create({ + userId: user.id, + paramTypeId: languageType.id, + value: language + }); + + const activationLink = `${process.env.FRONTEND_URL}/activate?token=${resetToken}`; + await sendAccountActivationEmail(email, activationLink, username, resetToken, language); + + return { id: user.hashedId, username: user.username, active: user.active }; +}; + +export const loginUser = async ({ username, password }) => { + console.log('check login'); + const user = await User.findOne({ where: { username } }); + if (!user) { + throw new Error('credentialsinvalid'); + } + const match = await bcrypt.compare(password, user.password); + if (!match) { + throw new Error('credentialsinvalid'); + } + return { id: user.hashedId, username: user.username, active: user.active }; +}; + +export const handleForgotPassword = async ({ email }) => { + const user = await User.findOne({ where: { email } }); + if (!user) { + throw new Error('Email not found'); + } + + const resetToken = uuidv4(); + const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`; + await user.update({ reset_token: resetToken }); + + const languageParam = await UserParam.findOne({ where: { user_id: user.id, param_type_id: languageType.id } }); + const userLanguage = languageParam ? languageParam.value : 'en'; + + await sendPasswordResetEmail(email, resetLink, userLanguage); + + return { message: 'Password reset email sent' }; +}; + +export const activateUserAccount = async ({ token }) => { + const user = await User.findOne({ where: { reset_token: token } }); + if (!user) { + throw new Error('Invalid token'); + } + + await user.update({ active: true, reset_token: null }); + return { message: 'Account activated' }; +}; diff --git a/frontend/src/components/AppNavigation.vue b/frontend/src/components/AppNavigation.vue index b2235bf..55f3d5e 100644 --- a/frontend/src/components/AppNavigation.vue +++ b/frontend/src/components/AppNavigation.vue @@ -1,7 +1,7 @@