various improvements
This commit is contained in:
51
server/api/channels/[id]/index.get.ts
Normal file → Executable file
51
server/api/channels/[id]/index.get.ts
Normal file → Executable file
@@ -1,20 +1,20 @@
|
||||
import { IChannel, IServer, SafeUser } from '../../../../types'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
import { IChannel, IServer, SafeUser } from '~/types';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to view a channel.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!event.context.params.id) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'A channelId is required'
|
||||
}
|
||||
if (!event.context.params?.id) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'A channelId is required',
|
||||
});
|
||||
}
|
||||
|
||||
const channel = await prisma.channel.findFirst({
|
||||
@@ -58,7 +58,7 @@ export default defineEventHandler(async (event) => {
|
||||
name: true,
|
||||
participants: {
|
||||
select: {
|
||||
id: true
|
||||
id: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,6 @@ export default defineEventHandler(async (event) => {
|
||||
select: {
|
||||
id: true,
|
||||
emoji: true,
|
||||
count: true,
|
||||
users: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -114,7 +113,6 @@ export default defineEventHandler(async (event) => {
|
||||
select: {
|
||||
id: true,
|
||||
emoji: true,
|
||||
count: true,
|
||||
users: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -137,10 +135,10 @@ export default defineEventHandler(async (event) => {
|
||||
}) as IChannel | null;
|
||||
|
||||
if (!channel) {
|
||||
event.node.res.statusCode = 404;
|
||||
return {
|
||||
message: `Channel with id "${event.context.params.id}" not found`
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `Channel with id "${event.context.params.id}" not found`,
|
||||
});
|
||||
}
|
||||
|
||||
if (channel.serverId && !channel.DM) {
|
||||
@@ -156,15 +154,16 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
if (!server) return;
|
||||
|
||||
const userInServer: Array<SafeUser> | undefined = server?.participants.filter((e: SafeUser) => e.id === event.context.user.id)
|
||||
const userInServer: Array<SafeUser> | undefined = server.participants.filter((e: SafeUser) => e.id === event.context.user.id);
|
||||
|
||||
if (!userInServer) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: `You must be in the server to access a channel in that server`
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be in the server to access a channel in that server',
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return channel
|
||||
})
|
||||
return channel;
|
||||
});
|
||||
49
server/api/channels/[id]/messages/[messageId]/delete.delete.ts
Executable file
49
server/api/channels/[id]/messages/[messageId]/delete.delete.ts
Executable file
@@ -0,0 +1,49 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to send a message.',
|
||||
});
|
||||
}
|
||||
|
||||
const { id: channelId, messageId } = event.context.params;
|
||||
|
||||
const message = await prisma.message.findFirst({
|
||||
where: {
|
||||
id: messageId,
|
||||
channelId: channelId,
|
||||
},
|
||||
include: {
|
||||
creator: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `message in channel ${channelId} with id ${messageId} is not found.`,
|
||||
});
|
||||
}
|
||||
|
||||
if (event.context.user.id !== message.creator.id) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'you are not allowed to delete that message.',
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.message.delete({
|
||||
where: {
|
||||
id: message.id
|
||||
}
|
||||
});
|
||||
|
||||
global.io.emit(`message-${event.context.params?.id}`, { message: { id: message.id }, deleted: true });
|
||||
|
||||
return {
|
||||
message: 'message successfully deleted.'
|
||||
};
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import { IChannel, IServer, SafeUser } from '~/types'
|
||||
import emojiRegex from 'emoji-regex'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import parseBody from '~~/utils/parseMessageBody'
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to send a message.'
|
||||
}
|
||||
}
|
||||
|
||||
const { id: channelId, messageId } = event.context.params
|
||||
|
||||
const message = await prisma.message.findFirst({
|
||||
where: {
|
||||
id: messageId,
|
||||
channelId: channelId,
|
||||
},
|
||||
include: {
|
||||
creator: true
|
||||
}
|
||||
})
|
||||
|
||||
if (!message) {
|
||||
event.node.res.statusCode = 404;
|
||||
return {
|
||||
message: `message in channel ${channelId} with id ${messageId} is not found.`
|
||||
}
|
||||
}
|
||||
|
||||
if (event.context.user.id !== message.creator.id) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'you are not allowed to delete that message.'
|
||||
}
|
||||
}
|
||||
|
||||
await prisma.message.delete({
|
||||
where: {
|
||||
id: message.id
|
||||
}
|
||||
})
|
||||
|
||||
global.io.emit(`message-${event.context.params.id}`, { message: { id: message.id }, deleted: true });
|
||||
|
||||
return {
|
||||
message: 'message successfully deleted.'
|
||||
}
|
||||
})
|
||||
97
server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts
Normal file → Executable file
97
server/api/channels/[id]/messages/[messageId]/reactions/[name].post.ts
Normal file → Executable file
@@ -1,23 +1,29 @@
|
||||
import { IChannel, IServer, SafeUser } from '~/types'
|
||||
import emojiRegex from 'emoji-regex'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
import emojiRegex from 'emoji-regex';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to send a message.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to send a message.',
|
||||
});
|
||||
}
|
||||
|
||||
const emoji = decodeURIComponent(event.context.params.name)
|
||||
if (!event.context.params?.name) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Reaction must be defined.',
|
||||
});
|
||||
}
|
||||
|
||||
const emoji = decodeURIComponent(event.context.params.name);
|
||||
const match = emoji.match(emojiRegex());
|
||||
if (!match || match.length !== 1) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'reaction is not an emoji or more than one emoji.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'reaction is not an emoji or more than one emoji.',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -30,27 +36,10 @@ export default defineEventHandler(async (event) => {
|
||||
username: true
|
||||
}
|
||||
},
|
||||
invites: {
|
||||
select: {
|
||||
id: true,
|
||||
server: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
participants: {
|
||||
select: {
|
||||
id: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
reactions: {
|
||||
select: {
|
||||
id: true,
|
||||
emoji: true,
|
||||
count: true,
|
||||
users: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -59,31 +48,23 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const message = await prisma.message.findFirst({
|
||||
where: {
|
||||
id: event.context.params.messageId
|
||||
},
|
||||
select: messageSelect
|
||||
})
|
||||
});
|
||||
|
||||
if (!message.id) {
|
||||
event.node.res.statusCode = 404;
|
||||
return {
|
||||
message: `message with id "${event.context.params.messageId}" not found.`
|
||||
}
|
||||
if (!message || !message.id) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `message with id "${event.context.params.messageId}" not found.`,
|
||||
});
|
||||
}
|
||||
|
||||
const reactionInMessage = message.reactions.find((e) => e.emoji.name === emoji)
|
||||
|
||||
let count;
|
||||
|
||||
if (reactionInMessage?.count) {
|
||||
count = reactionInMessage.count + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
const reactionInMessage = message.reactions.find((e) => e.emoji === emoji);
|
||||
|
||||
if (reactionInMessage && reactionInMessage.users.find((e) => e.id === event.context.user.id)) {
|
||||
// remove reaction
|
||||
@@ -92,23 +73,22 @@ export default defineEventHandler(async (event) => {
|
||||
id: reactionInMessage.id
|
||||
},
|
||||
data: {
|
||||
count: reactionInMessage.count - 1,
|
||||
users: {
|
||||
disconnect: [{ id: event.context.user.id }]
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const updatedMessage = await prisma.message.findFirst({
|
||||
where: {
|
||||
id: event.context.params.messageId
|
||||
},
|
||||
select: messageSelect
|
||||
})
|
||||
});
|
||||
|
||||
global.io.emit(`message-${event.context.params.id}`, { message: updatedMessage });
|
||||
|
||||
return { message: updatedMessage }
|
||||
return { message: updatedMessage };
|
||||
}
|
||||
|
||||
let reaction;
|
||||
@@ -119,22 +99,17 @@ export default defineEventHandler(async (event) => {
|
||||
id: reactionInMessage.id
|
||||
},
|
||||
data: {
|
||||
count,
|
||||
users: {
|
||||
connect: [{
|
||||
id: event.context.user.id,
|
||||
}]
|
||||
},
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
reaction = await prisma.reaction.create({
|
||||
data: {
|
||||
emoji: {
|
||||
name: emoji,
|
||||
id: null
|
||||
},
|
||||
count: count,
|
||||
emoji,
|
||||
users: {
|
||||
connect: [{
|
||||
id: event.context.user.id,
|
||||
@@ -146,7 +121,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (!reaction.messageId) return;
|
||||
@@ -156,9 +131,9 @@ export default defineEventHandler(async (event) => {
|
||||
id: reaction.messageId,
|
||||
},
|
||||
select: messageSelect
|
||||
})
|
||||
});
|
||||
|
||||
global.io.emit(`message-${event.context.params.id}`, { message: updatedMessage });
|
||||
|
||||
return { message: updatedMessage }
|
||||
})
|
||||
return { message: updatedMessage };
|
||||
});
|
||||
96
server/api/channels/sendMessage.post.ts → server/api/channels/[id]/sendMessage.post.ts
Normal file → Executable file
96
server/api/channels/sendMessage.post.ts → server/api/channels/[id]/sendMessage.post.ts
Normal file → Executable file
@@ -1,28 +1,40 @@
|
||||
import { IChannel, IServer, SafeUser, IMessage } from '~/types'
|
||||
import { Server } from 'socket.io'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { registerRuntimeHelpers } from '@vue/compiler-core'
|
||||
const prisma = new PrismaClient()
|
||||
import { IChannel, IServer, SafeUser, IMessage } from '~/types';
|
||||
import { Server } from 'socket.io';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
declare global {
|
||||
var io: Server
|
||||
let io: Server;
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to send a message.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to send a message.',
|
||||
});
|
||||
}
|
||||
|
||||
let { body, channelId } = await readBody(event)
|
||||
if (!event.context.params?.id) return;
|
||||
|
||||
if (!body || !channelId) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'A body or channelId is required to send a message.'
|
||||
}
|
||||
const req = await readBody(event);
|
||||
|
||||
const channelId = event.context.params.id;
|
||||
|
||||
if (!req || !channelId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'A body is required to send a message.',
|
||||
});
|
||||
}
|
||||
|
||||
let { body } = req;
|
||||
|
||||
if (body.length > 5000) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Message is larger than 5000 characters.'
|
||||
});
|
||||
}
|
||||
|
||||
const channel = await prisma.channel.findFirst({
|
||||
@@ -34,6 +46,7 @@ export default defineEventHandler(async (event) => {
|
||||
name: true,
|
||||
messages: false,
|
||||
DM: true,
|
||||
serverId: true,
|
||||
dmParticipants: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -44,10 +57,10 @@ export default defineEventHandler(async (event) => {
|
||||
}) as IChannel | null;
|
||||
|
||||
if (!channel) {
|
||||
event.node.res.statusCode = 404;
|
||||
return {
|
||||
message: `Channel with id "${channelId}" not found.`
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: `Channel with id "${channelId}" not found.`,
|
||||
});
|
||||
}
|
||||
|
||||
if (!channel.DM) {
|
||||
@@ -88,43 +101,44 @@ export default defineEventHandler(async (event) => {
|
||||
}) as IServer | null;
|
||||
|
||||
if (!server) {
|
||||
throw new Error(`server with id "${channel.serverId}" is not found but channel with id "${channel.id}" is not a dm?`)
|
||||
throw new Error(`server with id "${channel.serverId}" is not found but channel with id "${channel.id}" is not a dm?`);
|
||||
}
|
||||
|
||||
const userInServer: SafeUser | undefined = server.participants.find((e: SafeUser) => e.id === event.context.user.id)
|
||||
const userInServer: SafeUser | undefined = server.participants.find((e: SafeUser) => e.id === event.context.user.id);
|
||||
|
||||
if (!userInServer) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be in the server to send a message.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be in the server to send a message.',
|
||||
});
|
||||
}
|
||||
|
||||
if (!server) {
|
||||
event.node.res.statusCode = 404;
|
||||
return {
|
||||
message: 'Server not found'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Server not found',
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const userInDM: SafeUser | undefined = channel.dmParticipants?.find((e) => e.id === event.context.user.id)
|
||||
const userInDM: SafeUser | undefined = channel.dmParticipants?.find((e) => e.id === event.context.user.id);
|
||||
|
||||
if (!userInDM) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be in the DM to send a message.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be in the DM to send a message.',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make this client side or something because it's bug ridden right now lmao
|
||||
const inviteCodes = body.match(/<&([a-z]|[0-9]){25}>/g);
|
||||
|
||||
let invites: { id: string; }[] = [];
|
||||
const invites: { id: string; }[] = [];
|
||||
if (inviteCodes) {
|
||||
inviteCodes.forEach((e: string) => {
|
||||
if (!e) return
|
||||
if (!e) return;
|
||||
const opBody = body;
|
||||
body = body.split(e).join('')
|
||||
body = body.split(e).join('');
|
||||
if (opBody === body) return;
|
||||
const id = e.split('<&')[1]?.split('>')[0];
|
||||
if (!id) return;
|
||||
@@ -174,10 +188,10 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}) as unknown as IMessage
|
||||
}) as unknown as IMessage;
|
||||
|
||||
|
||||
global.io.emit(`message-${channel.id}`, { message });
|
||||
|
||||
return message
|
||||
})
|
||||
return message;
|
||||
});
|
||||
42
server/api/channels/create.post.ts
Normal file → Executable file
42
server/api/channels/create.post.ts
Normal file → Executable file
@@ -1,35 +1,37 @@
|
||||
import { IServer } from '~/types'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
import { IServer } from '~/types';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to view a channel.',
|
||||
});
|
||||
}
|
||||
|
||||
const { serverName } = await readBody(event)
|
||||
const body = await readBody(event);
|
||||
|
||||
if (!serverName) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'channel name is required to create a channel.'
|
||||
}
|
||||
if (!body) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Server name is required to create a Server.',
|
||||
});
|
||||
}
|
||||
|
||||
const { serverName } = body;
|
||||
|
||||
const preExistingServer = await prisma.server.findFirst({
|
||||
where: {
|
||||
name: serverName
|
||||
}
|
||||
}) as IServer
|
||||
}) as IServer | null;
|
||||
|
||||
if (preExistingServer) {
|
||||
event.node.res.statusCode = 409;
|
||||
return {
|
||||
message: `Server with name ${serverName} already exists.`
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
statusMessage: `Server with name ${serverName} already exists.`,
|
||||
});
|
||||
}
|
||||
|
||||
const server = await prisma.server.create({
|
||||
@@ -87,5 +89,5 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}) as unknown as IServer;
|
||||
|
||||
return server
|
||||
})
|
||||
return server;
|
||||
});
|
||||
50
server/api/channels/createDM.post.ts
Normal file → Executable file
50
server/api/channels/createDM.post.ts
Normal file → Executable file
@@ -1,22 +1,22 @@
|
||||
import { IChannel, SafeUser } from '~/types'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
const prisma = new PrismaClient()
|
||||
import { IChannel, SafeUser } from '~/types';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
if (!event.context.user.authenticated) {
|
||||
event.node.res.statusCode = 401;
|
||||
return {
|
||||
message: 'You must be logged in to view a channel.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: 'You must be logged in to view a channel.',
|
||||
});
|
||||
}
|
||||
|
||||
const { partnerId } = await readBody(event)
|
||||
const { partnerId } = await readBody(event);
|
||||
|
||||
if (!partnerId) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'A friend is required to create a DM.'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'A friend is required to create a DM.',
|
||||
});
|
||||
}
|
||||
|
||||
const partner = await prisma.user.findFirst({
|
||||
@@ -32,14 +32,14 @@ export default defineEventHandler(async (event) => {
|
||||
}) as SafeUser | null;
|
||||
|
||||
if (!partner) {
|
||||
event.node.res.statusCode = 400;
|
||||
return {
|
||||
message: 'No partner found'
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'No partner found',
|
||||
});
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
throw new Error('user not found?')
|
||||
throw new Error('user not found?');
|
||||
}
|
||||
|
||||
const preExistingServer = await prisma.channel.findFirst({
|
||||
@@ -47,13 +47,13 @@ export default defineEventHandler(async (event) => {
|
||||
name: `${user.id}-${partner.id}`,
|
||||
DM: true
|
||||
}
|
||||
}) as IChannel
|
||||
}) as IChannel;
|
||||
|
||||
if (preExistingServer) {
|
||||
event.node.res.statusCode = 409;
|
||||
return {
|
||||
message: `DM already exists.`
|
||||
}
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
statusMessage: 'DM already exists.',
|
||||
});
|
||||
}
|
||||
|
||||
const server = await prisma.channel.create({
|
||||
@@ -74,7 +74,7 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}) as IChannel
|
||||
}) as IChannel;
|
||||
|
||||
return server
|
||||
})
|
||||
return server;
|
||||
});
|
||||
Reference in New Issue
Block a user