Skip to content

Commit

Permalink
Mod Pane: Add schedule server delete
Browse files Browse the repository at this point in the history
  • Loading branch information
SupertigerDev committed Jan 15, 2025
1 parent 3dcb97a commit ec27909
Show file tree
Hide file tree
Showing 14 changed files with 119 additions and 24 deletions.
3 changes: 1 addition & 2 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,7 @@ model Server {
bannedUsers BannedServerMember[]
verified Boolean @default(false)
// oops, it should be camel cased.
PublicServer PublicServer?
publicServer PublicServer?
attachments Attachment[]
Expand Down
4 changes: 2 additions & 2 deletions src/cache/ServerCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const getServerCache = async (serverId: string) => {

const server = await prisma.server.findFirst({
where: { id: serverId },
include: { PublicServer: true, scheduledForDeletion: { select: { scheduledAt: true } } },
include: { publicServer: true, scheduledForDeletion: { select: { scheduledAt: true } } },
});
if (!server) return null;

Expand All @@ -31,7 +31,7 @@ export const getServerCache = async (serverId: string) => {
avatar: server.avatar,
hexColor: server.hexColor,
defaultRoleId: server.defaultRoleId,
public: server.PublicServer ? true : false,
public: server.publicServer ? true : false,
scheduledForDeletion: server.scheduledForDeletion,
};
const serverCacheString = JSON.stringify(serverCache);
Expand Down
2 changes: 2 additions & 0 deletions src/common/ClientEventNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export const SERVER_CHANNEL_CREATED = 'server:channel_created';
export const SERVER_CHANNEL_UPDATED = 'server:channel_updated';
export const SERVER_CHANNEL_DELETED = 'server:channel_deleted';
export const SERVER_CHANNEL_PERMISSIONS_UPDATED = 'server:channel_permissions_updated';
export const SERVER_SCHEDULE_DELETE = 'server:schedule_delete';
export const SERVER_REMOVE_SCHEDULE_DELETE = 'server:remove_schedule_delete';

export const MESSAGE_CREATED = 'message:created';
export const MESSAGE_UPDATED = 'message:updated';
Expand Down
1 change: 1 addition & 0 deletions src/common/ModAuditLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export const ModAuditLogType = {
userSuspendUpdate: 6,
userWarned: 7,
ipBan: 8,
serverDeleteUndo: 9,
} as const;
12 changes: 11 additions & 1 deletion src/emits/Server.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MESSAGE_CREATED, MESSAGE_DELETED, MESSAGE_DELETED_BATCH, MESSAGE_REACTION_ADDED, MESSAGE_REACTION_REMOVED, MESSAGE_UPDATED, SERVER_CHANNEL_ORDER_UPDATED, SERVER_CHANNEL_PERMISSIONS_UPDATED, SERVER_EMOJI_ADD, SERVER_EMOJI_REMOVE, SERVER_EMOJI_UPDATE, SERVER_JOINED, SERVER_LEFT, SERVER_MEMBER_JOINED, SERVER_MEMBER_LEFT, SERVER_MEMBER_UPDATED, SERVER_ORDER_UPDATED, SERVER_ROLE_CREATED, SERVER_ROLE_DELETED, SERVER_ROLE_ORDER_UPDATED, SERVER_ROLE_UPDATED, SERVER_UPDATED } from '../common/ClientEventNames';
import { MESSAGE_CREATED, MESSAGE_DELETED, MESSAGE_DELETED_BATCH, MESSAGE_REACTION_ADDED, MESSAGE_REACTION_REMOVED, MESSAGE_UPDATED, SERVER_CHANNEL_ORDER_UPDATED, SERVER_CHANNEL_PERMISSIONS_UPDATED, SERVER_EMOJI_ADD, SERVER_EMOJI_REMOVE, SERVER_EMOJI_UPDATE, SERVER_JOINED, SERVER_LEFT, SERVER_MEMBER_JOINED, SERVER_MEMBER_LEFT, SERVER_MEMBER_UPDATED, SERVER_ORDER_UPDATED, SERVER_REMOVE_SCHEDULE_DELETE, SERVER_ROLE_CREATED, SERVER_ROLE_DELETED, SERVER_ROLE_ORDER_UPDATED, SERVER_ROLE_UPDATED, SERVER_SCHEDULE_DELETE, SERVER_UPDATED } from '../common/ClientEventNames';
import { getIO } from '../socket/socket';
import { Presence, UserCache } from '../cache/UserCache';
import { UpdateServerOptions } from '../services/Server';
Expand Down Expand Up @@ -27,6 +27,16 @@ interface ServerJoinOpts {
voiceChannelUsers: VoiceCacheFormatted[];
}

export const emitServerScheduleDelete = (serverId: string, scheduleAt: Date) => {
const io = getIO();
io.in(serverId).emit(SERVER_SCHEDULE_DELETE, { serverId, scheduleAt });
};

export const emitServerRemoveScheduleDelete = (serverId: string) => {
const io = getIO();
io.in(serverId).emit(SERVER_REMOVE_SCHEDULE_DELETE, { serverId });
};

export const emitServerJoined = (opts: ServerJoinOpts) => {
const io = getIO();
const serverId = opts.server.id;
Expand Down
2 changes: 2 additions & 0 deletions src/routes/moderation/Router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { announcementPostCreate } from './announcementPostCreate';
import { announcementPostRemove } from './announcementPostRemove';
import { getUsersAuditLog } from './getUsersAuditLog';
import { getMessages } from './getMessages';
import { serverUndoDelete } from './serverUndoDelete';

const ModerationRouter = Router();

Expand All @@ -49,6 +50,7 @@ getUsers(ModerationRouter);
getUsersOnline(ModerationRouter);

serverDelete(ModerationRouter);
serverUndoDelete(ModerationRouter);
getServer(ModerationRouter);
updateServer(ModerationRouter);
getServers(ModerationRouter);
Expand Down
1 change: 1 addition & 0 deletions src/routes/moderation/getServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ async function route(req: Request, res: Response) {
id: true,
scheduledForDeletion: true,
createdAt: true,
publicServer: { select: { id: true } },
createdBy: {
select: {
id: true,
Expand Down
1 change: 1 addition & 0 deletions src/routes/moderation/getServers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ async function route(req: Request, res: Response) {
...(after ? { cursor: { id: after } } : undefined),
select: {
scheduledForDeletion: true,
publicServer: { select: { id: true } },
name: true,
hexColor: true,
id: true,
Expand Down
1 change: 1 addition & 0 deletions src/routes/moderation/getUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ async function route(req: Request, res: Response) {
servers: {
select: {
scheduledForDeletion: true,
publicServer: { select: { id: true } },
verified: true,
name: true,
hexColor: true,
Expand Down
5 changes: 5 additions & 0 deletions src/routes/moderation/serverDelete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { generateId } from '../../common/flakeId';
import { ModAuditLogType } from '../../common/ModAuditLog';
import { checkUserPassword } from '../../services/UserAuthentication';
import { warnUsersBatch } from '../../services/Moderation';
import { deleteServerCache } from '../../cache/ServerCache';
import { emitServerRemoveScheduleDelete, emitServerScheduleDelete } from '../../emits/Server';

export function serverDelete(Router: Router) {
Router.delete<any>('/moderation/servers/:serverId', authenticate(), isModMiddleware, body('reason').not().isEmpty().withMessage('Reason is required.').isString().withMessage('Reason must be a string.').isLength({ min: 0, max: 500 }), body('password').isLength({ min: 4, max: 72 }).withMessage('Password must be between 4 and 72 characters long.').isString().withMessage('Password must be a string!').not().isEmpty().withMessage('Password is required'), route);
Expand Down Expand Up @@ -64,6 +66,7 @@ async function route(req: Request<Params, unknown, Body>, res: Response) {
if (!scheduledDeletion) {
return res.status(500).json(generateError('Failed to schedule server deletion.'));
}
await deleteServerCache(server.id);

await warnUsersBatch({
userIds: [server.createdById],
Expand All @@ -83,5 +86,7 @@ async function route(req: Request<Params, unknown, Body>, res: Response) {
},
});

emitServerScheduleDelete(server.id, scheduledDeletion.scheduledAt);

res.status(200).json({ success: true, scheduledDeletion });
}
76 changes: 76 additions & 0 deletions src/routes/moderation/serverUndoDelete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Request, Response, Router } from 'express';
import { body } from 'express-validator';
import { prisma } from '../../common/database';
import { customExpressValidatorResult, generateError } from '../../common/errorHandler';

import { authenticate } from '../../middleware/authenticate';
import { isModMiddleware } from './isModMiddleware';
import { generateId } from '../../common/flakeId';
import { ModAuditLogType } from '../../common/ModAuditLog';
import { checkUserPassword } from '../../services/UserAuthentication';
import { deleteServerCache } from '../../cache/ServerCache';
import { emitServerRemoveScheduleDelete, emitServerScheduleDelete } from '../../emits/Server';

export function serverUndoDelete(Router: Router) {
Router.delete<any>('/moderation/servers/:serverId/schedule-delete', authenticate(), isModMiddleware, body('password').isLength({ min: 4, max: 72 }).withMessage('Password must be between 4 and 72 characters long.').isString().withMessage('Password must be a string!').not().isEmpty().withMessage('Password is required'), route);
}

interface Body {
password: string;
}

interface Params {
serverId: string;
}

async function route(req: Request<Params, unknown, Body>, res: Response) {
const validateError = customExpressValidatorResult(req);
if (validateError) {
return res.status(400).json(validateError);
}

const account = await prisma.account.findFirst({
where: { id: req.userCache.account!.id },
select: { password: true },
});
if (!account) return res.status(404).json(generateError('Something went wrong. Try again later.'));

const isPasswordValid = await checkUserPassword(account.password, req.body.password);
if (!isPasswordValid) return res.status(403).json(generateError('Invalid password.', 'password'));

const server = await prisma.server.findUnique({
where: { id: req.params.serverId },
include: { scheduledForDeletion: true },
});

if (!server) return res.status(404).json(generateError('Server does not exist.'));
if (!server.scheduledForDeletion) return res.status(404).json(generateError('Server is already not scheduled to delete.'));

const scheduledDeletion = await prisma.scheduleServerDelete
.delete({
where: { serverId: server.id },
})
.catch((e) => {
console.error(e);
return null;
});
if (!scheduledDeletion) {
return res.status(500).json(generateError('Failed to de-schedule server deletion.'));
}

await prisma.modAuditLog.create({
data: {
id: generateId(),
actionType: ModAuditLogType.serverDeleteUndo,
actionById: req.userCache.id,
serverName: server.name,
serverId: server.id,
},
});

await deleteServerCache(server.id);

emitServerRemoveScheduleDelete(server.id);

res.status(200).json({ success: true });
}
12 changes: 2 additions & 10 deletions src/routes/servers/serverLeave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,11 @@ import { serverMemberVerification } from '../../middleware/serverMemberVerificat
import { leaveServer } from '../../services/Server';

export function serverLeave(Router: Router) {
Router.post(
'/servers/:serverId/leave',
authenticate(),
serverMemberVerification(),
route
);
Router.post('/servers/:serverId/leave', authenticate(), serverMemberVerification({ ignoreScheduledDeletion: true }), route);
}

async function route(req: Request, res: Response) {
const [status, error] = await leaveServer(
req.userCache.id,
req.serverCache.id
);
const [status, error] = await leaveServer(req.userCache.id, req.serverCache.id);
if (error) {
return res.status(500).json(error);
}
Expand Down
12 changes: 6 additions & 6 deletions src/services/Explore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getPublicServers = async (sort: 'most_bumps' | 'most_members' | 're
};

const publicServers = await prisma.publicServer.findMany({
where: where(),
where: { AND: [where(), { server: { scheduledForDeletion: null } }] },
orderBy: orderBy(),
include: {
server: { include: { _count: { select: { serverMembers: true } } } },
Expand Down Expand Up @@ -51,14 +51,14 @@ export const getPublicServer = async (serverId: string): Promise<CustomResult<Pu

export const bumpPublicServer = async (serverId: string, bumpedByUserId: string): Promise<CustomResult<PublicServer, CustomError>> => {
const publicServer = await prisma.publicServer.findUnique({
where: { serverId },
where: { serverId, server: { scheduledForDeletion: null } },
include: {
server: {
select: {
systemChannelId: true
}
}
}
systemChannelId: true,
},
},
},
});

if (!publicServer) {
Expand Down
11 changes: 8 additions & 3 deletions src/services/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ export const getServers = async (userId: string) => {
include: {
servers: {
include: {
scheduledForDeletion: {
select: {
scheduledAt: true,
},
},
_count: { select: { welcomeQuestions: true } },
channels: {
where: { deleting: null },
Expand Down Expand Up @@ -375,9 +380,9 @@ export const deleteServer = async (serverId: string, deletedByUserId: string) =>
};

export const leaveServer = async (userId: string, serverId: string, ban = false, leaveMessage = true): Promise<CustomResult<boolean, CustomError>> => {
const server = await prisma.server.findFirst({
const server = await prisma.server.findUnique({
where: { id: serverId },
include: { channels: { select: { id: true } } },
include: { channels: { select: { id: true } }, scheduledForDeletion: true },
});
if (!server) {
return [null, generateError('Server does not exist.')];
Expand Down Expand Up @@ -453,7 +458,7 @@ export const leaveServer = async (userId: string, serverId: string, ban = false,

deleteAllInboxCache(userId);
await removeServerIdFromAccountOrder(userId, serverId);
if (server.systemChannelId && leaveMessage) {
if (!server.scheduledForDeletion && server.systemChannelId && leaveMessage) {
await createMessage({
channelId: server.systemChannelId,
type: MessageType.LEAVE_SERVER,
Expand Down

0 comments on commit ec27909

Please sign in to comment.