"use server"; import { Filter, MongoClient } from "mongodb"; import type { AccountStrike, Bot, Channel, Message, Report, Server, SessionInfo, SnapshotContent, User, } from "revolt-api"; import { ulid } from "ulid"; import { publishMessage } from "./redis"; import { checkPermission, hasPermissionFromSession } from "./accessPermissions"; import { PLATFORM_MOD_ID } from "./constants"; import { getServerSession } from "next-auth"; let client: MongoClient; function mongo() { if (!client) { client = new MongoClient(process.env.MONGODB!); } return client; } export default mongo; export async function insertAuditLog( permission: string, context: any, args?: any ) { const session = await getServerSession(); if (!session!.user!.email!) throw "invalid procedure call"; await mongo() .db("revolt") .collection<{ _id: string; moderator: string; permission: string; context: any; args: any; }>("safety_audit") .insertOne({ _id: ulid(), moderator: session!.user!.email!, permission, context, args, }); } export async function fetchBotById(id: string) { await checkPermission("bots/fetch/by-id", id); return await mongo() .db("revolt") .collection("bots") .findOne({ _id: id }); } export type Account = { _id: string; email: string; email_normalised: string; verification: | { status: "Verified"; } | { status: "Pending"; token: string; expiry: string; } | { status: "Moving"; new_email: string; token: string; expiry: string; }; password_reset: null | { token: string; expiry: string; }; deletion: | null | { status: "WaitingForVerification"; token: string; expiry: string; } | { status: "Scheduled"; after: string; }; disabled: boolean; lockout: null | { attempts: number; expiry: string; }; }; export async function fetchAccountById(id: string) { await checkPermission("accounts/fetch/by-id", id); return await mongo() .db("revolt") .collection("accounts") .findOne( { _id: id }, { projection: { password: 0, mfa: 0, }, } ); } export async function fetchSessionsByAccount(accountId: string) { await checkPermission("sessions/fetch/by-account-id", accountId); return await mongo() .db("revolt") .collection("sessions") .find( { user_id: accountId }, { projection: { token: 0, }, sort: { _id: -1, }, } ) .toArray(); } export async function fetchUserById(id: string) { await checkPermission("users/fetch/by-id", id); return await mongo() .db("revolt") .collection("users") .findOne( { _id: id }, { projection: (await hasPermissionFromSession("users/fetch/relations")) ? undefined : { relations: 0, }, } ); } export async function fetchUsersById(ids: string[]) { await checkPermission("users/fetch/by-id", ids); return await mongo() .db("revolt") .collection("users") .find( { _id: { $in: ids } }, { projection: (await hasPermissionFromSession("users/fetch/relations")) ? undefined : { relations: 0, }, } ) .toArray(); } export async function fetchChannelById(id: string) { await checkPermission("channels/fetch/by-id", id); return await mongo() .db("revolt") .collection("channels") .findOne({ _id: id }); } export async function fetchChannels(query: Filter) { await checkPermission("channels/fetch", query); return await mongo() .db("revolt") .collection("channels") .find(query) .toArray(); } export async function findDM(user_a: string, user_b: string) { await checkPermission("channels/fetch/dm", { user_a, user_b }); return await mongo() .db("revolt") .collection("channels") .findOne({ channel_type: "DirectMessage", recipients: { $all: [user_a, user_b], }, }); } export async function createDM( userA: string, userB: string, lastMessageId?: string ) { await checkPermission("channels/create/dm", { userA, userB }, lastMessageId); const newChannel: Channel & { channel_type: "DirectMessage" } = { _id: ulid(), channel_type: "DirectMessage", active: typeof lastMessageId !== "undefined", recipients: [userA, userB], }; await mongo() .db("revolt") .collection("channels") .insertOne(newChannel); await publishMessage(userA + "!", { type: "ChannelCreate", ...newChannel, }); await publishMessage(userB + "!", { type: "ChannelCreate", ...newChannel, }); return newChannel; } export async function fetchServerById(id: string) { await checkPermission("servers/fetch/by-id", id); return await mongo() .db("revolt") .collection("servers") .findOne({ _id: id }); } export async function fetchServers(query: Filter) { await checkPermission("servers/fetch", query); return await mongo() .db("revolt") .collection("servers") .find(query) .toArray(); } export async function fetchMessageById(id: string) { await checkPermission("messages/fetch/by-id", id); return await mongo() .db("revolt") .collection("messages") .findOne({ _id: id }); } export async function fetchMessagesByUser(userId: string) { await checkPermission("messages/fetch/by-user", userId); return await mongo() .db("revolt") .collection("messages") .find({ author: userId }, { sort: { _id: -1 }, limit: 50 }) .toArray(); } export async function fetchMessagesByChannel(channelId: string) { await checkPermission("messages/fetch/by-user", channelId); return await mongo() .db("revolt") .collection("messages") .find({ channel: channelId }, { sort: { _id: -1 }, limit: 50 }) .toArray(); } export async function fetchMessages(query: Filter, limit = 50) { await checkPermission("messages/fetch", query, { limit }); return await mongo() .db("revolt") .collection("messages") .find(query, { sort: { _id: -1 }, limit }) .toArray(); } export async function fetchOpenReports() { await checkPermission("reports/fetch/open", "all"); return await mongo() .db("revolt") .collection("safety_reports") .find( { status: "Created" }, { sort: { _id: -1, }, } ) .toArray(); } export async function fetchRelatedReportsByContent(contentId: string) { await checkPermission("reports/fetch/related/by-content", contentId); return await mongo() .db("revolt") .collection("safety_reports") .find( { status: "Created", "content.id": contentId }, { sort: { _id: -1, }, } ) .toArray(); } export async function fetchReportsByUser(userId: string) { await checkPermission("reports/fetch/related/by-user", userId); return await mongo() .db("revolt") .collection("safety_reports") .find( { author_id: userId }, { sort: { _id: -1, }, } ) .toArray(); } export async function fetchReportsAgainstUser(userId: string) { await checkPermission("reports/fetch/related/against-user", userId); const reportIdsInvolvingUser = await mongo() .db("revolt") .collection<{ _id: string; report_id: string; content: SnapshotContent }>( "safety_snapshots" ) .find({ $or: [{ "content._id": userId }, { "content.author": userId }], }) .toArray() .then((snapshots) => [ ...new Set(snapshots.map((snapshot) => snapshot.report_id)), ]); return await mongo() .db("revolt") .collection("safety_reports") .find( { _id: { $in: reportIdsInvolvingUser, }, }, { sort: { _id: -1, }, } ) .toArray(); } export async function fetchReports( query: Filter = { status: "Created" } ) { await checkPermission("reports/fetch", query); return await mongo() .db("revolt") .collection("safety_reports") .find(query as never, { sort: { _id: -1, }, }) .toArray(); } export async function fetchReportById(id: string) { await checkPermission("reports/fetch/by-id", id); return await mongo() .db("revolt") .collection("safety_reports") .findOne({ _id: id }); } export async function fetchMembershipsByUser(userId: string) { await checkPermission("users/fetch/memberships", userId); return await mongo() .db("revolt") .collection<{ _id: { user: string; server: string } }>("server_members") .find({ "_id.user": userId }) .toArray(); } export async function fetchSnapshots( query: Filter<{ _id: string; report_id: string; content: SnapshotContent }> ) { await checkPermission("reports/fetch/snapshots", query); return await mongo() .db("revolt") .collection<{ _id: string; report_id: string; content: SnapshotContent }>( "safety_snapshots" ) .find(query) .toArray(); } export async function fetchSnapshotsByReport(reportId: string) { await checkPermission("reports/fetch/snapshots/by-report", reportId); return await mongo() .db("revolt") .collection<{ content: SnapshotContent }>("safety_snapshots") .find({ report_id: reportId }) .toArray() .then((snapshots) => snapshots.map((snapshot) => snapshot!.content)); } export async function fetchStrikesByUser(userId: string) { await checkPermission("users/fetch/strikes", userId); return await mongo() .db("revolt") .collection("safety_strikes") .find( { user_id: userId }, { sort: { _id: -1, }, } ) .toArray(); } export async function fetchNoticesByUser(userId: string) { await checkPermission("users/fetch/notices", userId); const dm = await mongo() .db("revolt") .collection("channels") .findOne({ channel_type: "DirectMessage", recipients: { $all: [userId, PLATFORM_MOD_ID], }, }); if (!dm) return []; return await mongo() .db("revolt") .collection("messages") .find({ channel: dm!._id }, { sort: { _id: -1 } }) .toArray(); } export async function fetchBotsByUser(userId: string) { await checkPermission("bots/fetch/by-user", userId); return await mongo() .db("revolt") .collection("bots") .find({ owner: userId }) .toArray(); } export type EmailClassification = { _id: string; classification: string; }; export async function fetchAuthifierEmailClassification(provider: string) { await checkPermission("authifier", provider); return await mongo() .db("authifier") .collection("email_classification") .findOne({ _id: provider }); }