Implemented approve/reject access
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
import Club from '../models/Club.js';
|
||||
import UserClub from '../models/UserClub.js';
|
||||
import User from '../models/User.js';
|
||||
import Member from '../models/Member.js';
|
||||
import { Op, fn, where, col } from 'sequelize';
|
||||
import ClubService from '../services/clubService.js';
|
||||
import { getUserByToken } from '../utils/userUtils.js';
|
||||
|
||||
const getClubs = async (req, res) => {
|
||||
export const getClubs = async (req, res) => {
|
||||
try {
|
||||
console.log('[getClubs] - get clubs');
|
||||
const clubs = await Club.findAll();
|
||||
const clubs = await ClubService.getAllClubs();
|
||||
console.log('[getClubs] - prepare response');
|
||||
res.status(200).json(clubs);
|
||||
console.log('[getClubs] - done');
|
||||
@@ -19,31 +15,37 @@ const getClubs = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
const addClub = async (req, res) => {
|
||||
export const addClub = async (req, res) => {
|
||||
console.log('[addClub] - Read out parameters');
|
||||
const { authcode: token } = req.headers;
|
||||
const { name: clubName } = req.body;
|
||||
console.log('[addClub] - find club by name');
|
||||
const club = await Club.findOne({
|
||||
where: where(fn('LOWER', col('name')), 'LIKE', `%${clubName.toLowerCase()}%`)
|
||||
});
|
||||
console.log('[addClub] - get user');
|
||||
const user = await getUserByToken(token);
|
||||
console.log('[addClub] - check if club already exists');
|
||||
if (club) {
|
||||
res.status(409).json({ error: "alreadyexists" });
|
||||
return;
|
||||
}
|
||||
console.log('[addClub] - create club');
|
||||
const newClub = await Club.create({ name: clubName });
|
||||
console.log('[addClub] - add user to new club');
|
||||
await UserClub.create({ userId: user.id, clubId: newClub.id, approved: true });
|
||||
console.log('[addClub] - prepare response');
|
||||
res.status(200).json(newClub);
|
||||
console.log('[addClub] - done');
|
||||
}
|
||||
|
||||
const getClub = async (req, res) => {
|
||||
try {
|
||||
console.log('[addClub] - find club by name');
|
||||
const club = await ClubService.findClubByName(clubName);
|
||||
console.log('[addClub] - get user');
|
||||
const user = await getUserByToken(token);
|
||||
console.log('[addClub] - check if club already exists');
|
||||
if (club) {
|
||||
res.status(409).json({ error: "alreadyexists" });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[addClub] - create club');
|
||||
const newClub = await ClubService.createClub(clubName);
|
||||
console.log('[addClub] - add user to new club');
|
||||
await ClubService.addUserToClub(user.id, newClub.id);
|
||||
console.log('[addClub] - prepare response');
|
||||
res.status(200).json(newClub);
|
||||
console.log('[addClub] - done');
|
||||
} catch (error) {
|
||||
console.log('[addClub] - error');
|
||||
console.log(error);
|
||||
res.status(500).json({ error: "internalerror" });
|
||||
}
|
||||
};
|
||||
|
||||
export const getClub = async (req, res) => {
|
||||
console.log('[getClub] - start');
|
||||
try {
|
||||
const { authcode: token } = req.headers;
|
||||
@@ -51,41 +53,20 @@ const getClub = async (req, res) => {
|
||||
console.log('[getClub] - get user');
|
||||
const user = await getUserByToken(token);
|
||||
console.log('[getClub] - get users club');
|
||||
const access = await UserClub.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
clubId: clubId,
|
||||
}
|
||||
});
|
||||
const access = await ClubService.getUserClubAccess(user.id, clubId);
|
||||
console.log('[getClub] - check access');
|
||||
if (access.length === 0 || !access[0].approved) {
|
||||
res.status(403).json({ error: "noaccess", status: access.length === 0 ? "notrequested" : "requested" });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[getClub] - get club');
|
||||
const club = await Club.findByPk(clubId, {
|
||||
include: [
|
||||
{
|
||||
model: Member,
|
||||
as: 'members',
|
||||
order: [
|
||||
['lastName', 'ASC'], // Sortiere nach Nachname aufsteigend
|
||||
['firstName', 'ASC'] // Sortiere nach Vorname aufsteigend
|
||||
],
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'Users',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
const club = await ClubService.findClubById(clubId);
|
||||
console.log('[getClub] - check club exists');
|
||||
if (!club) {
|
||||
return res.status(404).json({ message: 'Club not found' });
|
||||
}
|
||||
|
||||
console.log('[getClub] - set response');
|
||||
res.status(200).json(club);
|
||||
console.log('[getClub] - done');
|
||||
@@ -93,76 +74,75 @@ const getClub = async (req, res) => {
|
||||
console.log(error);
|
||||
res.status(500).json({ message: 'Server error' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const approveClubAccess = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubid: clubId, approveemail: toApproveEmail } = req.body;
|
||||
const user = await getUserByToken(token);
|
||||
const access = await UserClub.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
clubId: clubId,
|
||||
approved: true
|
||||
}
|
||||
});
|
||||
if (access.length === 0) {
|
||||
res.status(403).json({ error: "noaccess" });
|
||||
return;
|
||||
}
|
||||
const toApproveUser = await User.findOne({
|
||||
where: {
|
||||
email: toApproveEmail
|
||||
}
|
||||
});
|
||||
if (!toApproveUser) {
|
||||
res.status(404).json({ error: 'usernotfound' });
|
||||
return;
|
||||
}
|
||||
const toApproveUserClub = UserClub.findAll({
|
||||
where: {
|
||||
userId: toApproveUser.id,
|
||||
clubId: clubId
|
||||
}
|
||||
});
|
||||
if (!toApproveUserClub) {
|
||||
res.status(404).json({ error: 'norequest' });
|
||||
return;
|
||||
}
|
||||
await toApproveUserClub.update({ approved: true });
|
||||
res.status(200).json({ status: 'ok' });
|
||||
}
|
||||
|
||||
const requestClubAccess = async (req, res) => {
|
||||
export const requestClubAccess = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubid: clubId } = req.params;
|
||||
const user = await getUserByToken(token);
|
||||
console.log(user);
|
||||
const access = await UserClub.findAll({
|
||||
where: {
|
||||
userId: user.id,
|
||||
clubId: clubId,
|
||||
}
|
||||
});
|
||||
if (access.length > 0) {
|
||||
res.status(409).json({ err: "alreadyrequested" });
|
||||
return;
|
||||
}
|
||||
const club = Club.findOne({
|
||||
where: {
|
||||
id: clubId
|
||||
}
|
||||
});
|
||||
if (!club) {
|
||||
res.status(404).json({ err: "clubnotfound" });
|
||||
return;
|
||||
}
|
||||
UserClub.create({
|
||||
userId: user.id,
|
||||
clubId: clubId,
|
||||
approved: false
|
||||
});
|
||||
res.status(200).json({});
|
||||
}
|
||||
|
||||
export { getClubs, addClub, getClub, approveClubAccess, requestClubAccess };
|
||||
try {
|
||||
const user = await getUserByToken(token);
|
||||
console.log(user);
|
||||
|
||||
await ClubService.requestAccessToClub(user.id, clubId);
|
||||
res.status(200).json({});
|
||||
} catch (error) {
|
||||
if (error.message === 'alreadyrequested') {
|
||||
res.status(409).json({ err: "alreadyrequested" });
|
||||
} else if (error.message === 'clubnotfound') {
|
||||
res.status(404).json({ err: "clubnotfound" });
|
||||
} else {
|
||||
res.status(500).json({ err: "internalerror" });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const approveClubAccess = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubid: clubId, userid: toApproveUserId } = req.body;
|
||||
try {
|
||||
await ClubService.approveUserClubAccess(token, clubId, toApproveUserId);
|
||||
res.status(200).json({ status: 'approved' });
|
||||
} catch (error) {
|
||||
if (error.message === 'norequest') {
|
||||
res.status(404).json({ error: 'norequest' });
|
||||
} else if (error.message === 'noaccess') {
|
||||
res.status(403).json({ error: 'noaccess' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'internalerror' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const rejectClubAccess = async (req, res) => {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubid: clubId, userid: toRejectUserId } = req.body;
|
||||
try {
|
||||
await ClubService.rejectUserClubAccess(token, clubId, toRejectUserId);
|
||||
res.status(200).json({ status: 'rejected' });
|
||||
} catch (error) {
|
||||
if (error.message === 'norequest') {
|
||||
res.status(404).json({ error: 'norequest' });
|
||||
} else if (error.message === 'noaccess') {
|
||||
res.status(403).json({ error: 'noaccess' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'internalerror' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getPendingApprovals = async (req, res) => {
|
||||
try {
|
||||
const { authcode: token } = req.headers;
|
||||
const { clubid: clubId } = req.params;
|
||||
const pendingApprovals = await ClubService.getPendingUserApprovals(token, clubId);
|
||||
res.status(200).json(pendingApprovals);
|
||||
} catch (error) {
|
||||
console.error('[getPendingApprovals] - Error:', error);
|
||||
if (error.message === 'noaccess') {
|
||||
res.status(403).json({ error: 'noaccess' });
|
||||
} else {
|
||||
res.status(500).json({ error: 'internalerror' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from 'express';
|
||||
import { authenticate } from '../middleware/authMiddleware.js';
|
||||
import { getClubs, addClub, getClub, requestClubAccess } from '../controllers/clubsController.js';
|
||||
import { getClubs, addClub, getClub, requestClubAccess, getPendingApprovals, approveClubAccess, rejectClubAccess } from '../controllers/clubsController.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -8,5 +8,8 @@ router.get('/', authenticate, getClubs);
|
||||
router.post('/', authenticate, addClub);
|
||||
router.get('/:clubid', authenticate, getClub);
|
||||
router.get('/request/:clubid', authenticate, requestClubAccess);
|
||||
router.get('/pending/:clubid', authenticate, getPendingApprovals);
|
||||
router.post('/approve', authenticate, approveClubAccess);
|
||||
router.post('/reject', authenticate, rejectClubAccess);
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
||||
128
backend/services/clubService.js
Normal file
128
backend/services/clubService.js
Normal file
@@ -0,0 +1,128 @@
|
||||
import Club from '../models/Club.js';
|
||||
import UserClub from '../models/UserClub.js';
|
||||
import User from '../models/User.js';
|
||||
import Member from '../models/Member.js';
|
||||
import { Op, fn, where, col } from 'sequelize';
|
||||
import { checkAccess } from '../utils/userUtils.js';
|
||||
|
||||
class ClubService {
|
||||
async getAllClubs() {
|
||||
return await Club.findAll();
|
||||
}
|
||||
|
||||
async findClubByName(clubName) {
|
||||
return await Club.findOne({
|
||||
where: where(fn('LOWER', col('name')), 'LIKE', `%${clubName.toLowerCase()}%`)
|
||||
});
|
||||
}
|
||||
|
||||
async createClub(clubName) {
|
||||
return await Club.create({ name: clubName });
|
||||
}
|
||||
|
||||
async addUserToClub(userId, clubId) {
|
||||
return await UserClub.create({ userId: userId, clubId: clubId, approved: true });
|
||||
}
|
||||
|
||||
async getUserClubAccess(userId, clubId) {
|
||||
return await UserClub.findAll({
|
||||
where: {
|
||||
userId: userId,
|
||||
clubId: clubId,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async findClubById(clubId) {
|
||||
return await Club.findByPk(clubId, {
|
||||
include: [
|
||||
{
|
||||
model: Member,
|
||||
as: 'members',
|
||||
order: [
|
||||
['lastName', 'ASC'], // Sortiere nach Nachname aufsteigend
|
||||
['firstName', 'ASC'] // Sortiere nach Vorname aufsteigend
|
||||
],
|
||||
},
|
||||
{
|
||||
model: User,
|
||||
as: 'Users',
|
||||
through: {
|
||||
attributes: []
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async approveUserClubAccess(userToken, clubId, toApproveUserId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const toApproveUserClub = await UserClub.findOne({
|
||||
where: {
|
||||
userId: toApproveUserId,
|
||||
clubId: clubId
|
||||
}
|
||||
});
|
||||
if (!toApproveUserClub) {
|
||||
throw new Error('norequest');
|
||||
}
|
||||
return await toApproveUserClub.update({ approved: true });
|
||||
}
|
||||
|
||||
async requestAccessToClub(userId, clubId) {
|
||||
const access = await UserClub.findAll({
|
||||
where: {
|
||||
userId: userId,
|
||||
clubId: clubId,
|
||||
}
|
||||
});
|
||||
if (access.length > 0) {
|
||||
throw new Error('alreadyrequested');
|
||||
}
|
||||
const club = await Club.findOne({
|
||||
where: {
|
||||
id: clubId
|
||||
}
|
||||
});
|
||||
if (!club) {
|
||||
throw new Error('clubnotfound');
|
||||
}
|
||||
return await UserClub.create({
|
||||
userId: userId,
|
||||
clubId: clubId,
|
||||
approved: false
|
||||
});
|
||||
}
|
||||
|
||||
async getPendingUserApprovals(userToken, clubId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
return await UserClub.findAll({
|
||||
where: {
|
||||
clubId: clubId,
|
||||
approved: false
|
||||
},
|
||||
include: [
|
||||
{
|
||||
model: User,
|
||||
attributes: ['id', 'firstName', 'lastName', 'email']
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async rejectUserClubAccess(userToken, clubId, toRejectUserId) {
|
||||
await checkAccess(userToken, clubId);
|
||||
const toRejectUserClub = await UserClub.findOne({
|
||||
where: {
|
||||
userId: toRejectUserId,
|
||||
clubId: clubId
|
||||
}
|
||||
});
|
||||
if (!toRejectUserClub) {
|
||||
throw new Error('norequest');
|
||||
}
|
||||
return await toRejectUserClub.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export default new ClubService();
|
||||
@@ -13,7 +13,7 @@
|
||||
<template v-if="selectedClub">
|
||||
<a href="/members">Mitglieder</a>
|
||||
<a href="/diary">Tagebuch</a>
|
||||
<button>Freigaben</button>
|
||||
<a href="/pending-approvals">Freigaben</a>
|
||||
</template>
|
||||
<div>
|
||||
<button @click="logout()">Ausloggen</button>
|
||||
|
||||
@@ -7,6 +7,7 @@ import CreateClub from './views/CreateClub.vue';
|
||||
import ClubView from './views/ClubView.vue';
|
||||
import MembersView from './views/MembersView.vue';
|
||||
import DiaryView from './views/DiaryView.vue';
|
||||
import PendingApprovalsView from './views/PendingApprovalsView.vue';
|
||||
|
||||
const routes = [
|
||||
{ path: '/register', component: Register },
|
||||
@@ -17,6 +18,7 @@ const routes = [
|
||||
{ path: '/showclub/:1', component: ClubView },
|
||||
{ path: '/members', component: MembersView },
|
||||
{ path: '/diary', component: DiaryView },
|
||||
{ path: '/pending-approvals', component: PendingApprovalsView},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
|
||||
110
frontend/src/views/PendingApprovalsView.vue
Normal file
110
frontend/src/views/PendingApprovalsView.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2>Ausstehende Benutzeranfragen</h2>
|
||||
<div v-if="pendingUsers.length > 0">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Vorname</th>
|
||||
<th>Nachname</th>
|
||||
<th>Email</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="user in pendingUsers" :key="user.id">
|
||||
<td>{{ user.firstName }}</td>
|
||||
<td>{{ user.lastName }}</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<button @click="approveUser(user.id)">Genehmigen</button>
|
||||
<button @click="rejectUser(user.id)">Ablehnen</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p>Keine ausstehenden Benutzeranfragen.</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import apiClient from '../apiClient.js';
|
||||
|
||||
export default {
|
||||
name: 'PendingApprovalsView',
|
||||
data() {
|
||||
return {
|
||||
pendingUsers: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['currentClub']),
|
||||
},
|
||||
async created() {
|
||||
await this.loadPendingApprovals();
|
||||
},
|
||||
methods: {
|
||||
async loadPendingApprovals() {
|
||||
try {
|
||||
const response = await apiClient.get(`/clubs/pending/${this.currentClub}`);
|
||||
this.pendingUsers = response.data.map(entry => entry.User);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der ausstehenden Anfragen:', error);
|
||||
}
|
||||
},
|
||||
async approveUser(userId) {
|
||||
try {
|
||||
await apiClient.post('/clubs/approve', {
|
||||
clubid: this.currentClub,
|
||||
userid: userId,
|
||||
});
|
||||
this.pendingUsers = this.pendingUsers.filter(user => user.id !== userId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Genehmigen des Benutzers:', error);
|
||||
}
|
||||
},
|
||||
async rejectUser(userId) {
|
||||
try {
|
||||
await apiClient.post('/clubs/reject', {
|
||||
clubid: this.currentClub,
|
||||
userid: userId,
|
||||
});
|
||||
this.pendingUsers = this.pendingUsers.filter(user => user.id !== userId);
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Ablehnen des Benutzers:', error);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-right: 5px;
|
||||
padding: 5px 10px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user