add/remove bot managers and mods from dashboard
This commit is contained in:
parent
4675ed8bdd
commit
0e4667a298
13 changed files with 432 additions and 20 deletions
|
@ -3,6 +3,7 @@ import { app, logger } from "..";
|
||||||
|
|
||||||
app.use('*', (req: Request, res: Response, next: () => void) => {
|
app.use('*', (req: Request, res: Response, next: () => void) => {
|
||||||
res.header('Access-Control-Allow-Origin', '*');
|
res.header('Access-Control-Allow-Origin', '*');
|
||||||
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, x-auth-user, x-auth-token");
|
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, x-auth-user, x-auth-token');
|
||||||
|
res.header('Access-Control-Allow-Methods', '*');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
import { app } from '../..';
|
import { app, db } from '../..';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { badRequest, isAuthenticated, unauthorized } from '../../utils';
|
import { badRequest, getPermissionLevel, isAuthenticated, unauthorized } from '../../utils';
|
||||||
import { botReq } from '../internal/ws';
|
import { botReq } from '../internal/ws';
|
||||||
|
|
||||||
|
type User = { id: string, username?: string, avatarURL?: string }
|
||||||
|
|
||||||
type ServerDetails = {
|
type ServerDetails = {
|
||||||
id: string,
|
id: string,
|
||||||
perms: 0|1|2,
|
perms: 0|1|2|3,
|
||||||
name: string,
|
name: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
iconURL?: string,
|
iconURL?: string,
|
||||||
bannerURL?: string,
|
bannerURL?: string,
|
||||||
serverConfig: any,
|
serverConfig: any,
|
||||||
|
users: User[],
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/dash/server/:server', async (req: Request, res: Response) => {
|
app.get('/dash/server/:server', async (req: Request, res: Response) => {
|
||||||
|
@ -30,3 +33,122 @@ app.get('/dash/server/:server', async (req: Request, res: Response) => {
|
||||||
const s: ServerDetails = response.server;
|
const s: ServerDetails = response.server;
|
||||||
res.send({ server: s });
|
res.send({ server: s });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.put('/dash/server/:server/:option', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const user = await isAuthenticated(req, res, true);
|
||||||
|
if (!user) return unauthorized(res);
|
||||||
|
|
||||||
|
const { server } = req.params;
|
||||||
|
const { item } = req.body;
|
||||||
|
if (!server || typeof server != 'string' || !item || typeof item != 'string') return badRequest(res);
|
||||||
|
|
||||||
|
const permissionLevelRes = await getPermissionLevel(user, server);
|
||||||
|
if (!permissionLevelRes.success)
|
||||||
|
return res.status(permissionLevelRes.statusCode || 500).send({ error: permissionLevelRes.error });
|
||||||
|
|
||||||
|
const servers = db.get('servers');
|
||||||
|
const permissionLevel: 0|1|2|3 = permissionLevelRes.level;
|
||||||
|
const settings = await servers.findOne({ id: server });
|
||||||
|
|
||||||
|
switch(req.params.option) {
|
||||||
|
case 'managers': {
|
||||||
|
if (permissionLevel < 3) return res.status(403).send({ error: 'You are not allowed to add other bot managers.' });
|
||||||
|
|
||||||
|
const userRes = await botReq('getUser', { user: item });
|
||||||
|
if (!userRes.success) {
|
||||||
|
return res.status(404).send({ error: 'User could not be found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.botManagers?.includes(userRes.user.id) === true) {
|
||||||
|
return res.status(400).send({ error: 'This user is already manager' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newManagers = [ ...(settings.botManagers ?? []), userRes.user.id ];
|
||||||
|
await servers.update({ id: server }, { $set: { botManagers: newManagers } });
|
||||||
|
res.send({
|
||||||
|
success: true,
|
||||||
|
managers: newManagers,
|
||||||
|
users: [ userRes.user ],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'mods': {
|
||||||
|
if (permissionLevel < 2) return res.status(403).send({ error: 'You are not allowed to add other moderators.' });
|
||||||
|
|
||||||
|
const userRes = await botReq('getUser', { user: item });
|
||||||
|
if (!userRes.success) {
|
||||||
|
return res.status(404).send({ error: 'User could not be found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.moderators?.includes(userRes.user.id) === true) {
|
||||||
|
return res.status(400).send({ error: 'This user is already moderator' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMods = [ ...(settings.moderators ?? []), userRes.user.id ];
|
||||||
|
await servers.update({ id: server }, { $set: { moderators: newMods } });
|
||||||
|
res.send({
|
||||||
|
success: true,
|
||||||
|
mods: newMods,
|
||||||
|
users: [ userRes.user ],
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: return badRequest(res);
|
||||||
|
}
|
||||||
|
} catch(e: any) {
|
||||||
|
res.status(500).send({ error: e });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/dash/server/:server/:option/:target', async (req: Request, res: Response) => {
|
||||||
|
const user = await isAuthenticated(req, res, true);
|
||||||
|
if (!user) return unauthorized(res);
|
||||||
|
|
||||||
|
const { server, target, option } = req.params;
|
||||||
|
if (!server || typeof server != 'string' || !target || typeof target != 'string') return badRequest(res);
|
||||||
|
|
||||||
|
const permissionLevelRes = await getPermissionLevel(user, server);
|
||||||
|
if (!permissionLevelRes.success)
|
||||||
|
return res.status(permissionLevelRes.statusCode || 500).send({ error: permissionLevelRes.error });
|
||||||
|
|
||||||
|
const servers = db.get('servers');
|
||||||
|
const permissionLevel: 0|1|2|3 = permissionLevelRes.level;
|
||||||
|
const settings = await servers.findOne({ id: server });
|
||||||
|
|
||||||
|
switch(option) {
|
||||||
|
case 'managers': {
|
||||||
|
if (permissionLevel < 3) return res.status(403).send({ error: 'You are not allowed to remove bot managers.' });
|
||||||
|
|
||||||
|
if (!settings.botManagers?.includes(target)) {
|
||||||
|
return res.status(400).send({ error: 'This user is not manager' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newManagers = (settings.botManagers ?? []).filter((i: string) => i != target);
|
||||||
|
await servers.update({ id: server }, { $set: { botManagers: newManagers } });
|
||||||
|
res.send({
|
||||||
|
success: true,
|
||||||
|
managers: newManagers,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case 'mods': {
|
||||||
|
if (permissionLevel < 2) return res.status(403).send({ error: 'You are not allowed to remove moderators.' });
|
||||||
|
|
||||||
|
if (!settings.moderators?.includes(target)) {
|
||||||
|
return res.status(400).send({ error: 'This user is not moderator' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMods = (settings.moderators ?? []).filter((i: string) => i != target);
|
||||||
|
await servers.update({ id: server }, { $set: { moderators: newMods } });
|
||||||
|
res.send({
|
||||||
|
success: true,
|
||||||
|
mods: newMods,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: return badRequest(res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Request, Response } from 'express';
|
||||||
import { isAuthenticated, unauthorized } from '../../utils';
|
import { isAuthenticated, unauthorized } from '../../utils';
|
||||||
import { botReq } from '../internal/ws';
|
import { botReq } from '../internal/ws';
|
||||||
|
|
||||||
type Server = { id: string, perms: 0|1|2, name: string, iconURL?: string, bannerURL?: string }
|
type Server = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string }
|
||||||
|
|
||||||
app.get('/dash/servers', async (req: Request, res: Response) => {
|
app.get('/dash/servers', async (req: Request, res: Response) => {
|
||||||
const user = await isAuthenticated(req, res, true);
|
const user = await isAuthenticated(req, res, true);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
import { FindOneResult } from "monk";
|
import { FindOneResult } from "monk";
|
||||||
import { db } from ".";
|
import { db } from ".";
|
||||||
|
import { botReq } from "./routes/internal/ws";
|
||||||
|
|
||||||
class Session {
|
class Session {
|
||||||
user: string;
|
user: string;
|
||||||
|
@ -44,4 +45,8 @@ function unauthorized(res: Response) {
|
||||||
res.status(401).send(JSON.stringify({ "error": "Unauthorized" }, null, 4));
|
res.status(401).send(JSON.stringify({ "error": "Unauthorized" }, null, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
export { isAuthenticated, getSessionInfo, badRequest, unauthorized }
|
async function getPermissionLevel(user: string, server: string) {
|
||||||
|
return await botReq('getPermissionLevel', { user, server });
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isAuthenticated, getSessionInfo, badRequest, unauthorized, getPermissionLevel }
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import { Member } from "revolt.js/dist/maps/Members";
|
import { Member } from "revolt.js/dist/maps/Members";
|
||||||
import { User } from "revolt.js/dist/maps/Users";
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
import { client } from "../../..";
|
import { client } from "../../..";
|
||||||
import AutomodSettings from "../../../struct/antispam/AutomodSettings";
|
|
||||||
import ServerConfig from "../../../struct/ServerConfig";
|
import ServerConfig from "../../../struct/ServerConfig";
|
||||||
import { getPermissionLevel } from "../../util";
|
import { getPermissionLevel } from "../../util";
|
||||||
import { wsEvents, WSResponse } from "../api_communication";
|
import { wsEvents, WSResponse } from "../api_communication";
|
||||||
|
|
||||||
type ReqData = { user: string, server: string }
|
type ReqData = { user: string, server: string }
|
||||||
|
type APIUser = { id: string, username?: string, avatarURL?: string }
|
||||||
|
|
||||||
type ServerDetails = {
|
type ServerDetails = {
|
||||||
id: string,
|
id: string,
|
||||||
perms: 0|1|2,
|
perms: 0|1|2|3,
|
||||||
name: string,
|
name: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
iconURL?: string,
|
iconURL?: string,
|
||||||
bannerURL?: string,
|
bannerURL?: string,
|
||||||
serverConfig?: ServerConfig,
|
serverConfig?: ServerConfig,
|
||||||
|
users: APIUser[],
|
||||||
}
|
}
|
||||||
|
|
||||||
wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSResponse) => void) => {
|
wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSResponse) => void) => {
|
||||||
|
@ -43,6 +44,20 @@ wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSRespo
|
||||||
|
|
||||||
// todo: remove unwanted keys from server config
|
// todo: remove unwanted keys from server config
|
||||||
|
|
||||||
|
async function fetchUser(id: string) {
|
||||||
|
try {
|
||||||
|
return client.users.get(id) || await client.users.fetch(id);
|
||||||
|
} catch(e) {
|
||||||
|
throw id; // this is stupid but idc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const users = await Promise.allSettled([
|
||||||
|
...(serverConfig.botManagers?.map(u => fetchUser(u)) ?? []),
|
||||||
|
...(serverConfig.moderators?.map(u => fetchUser(u)) ?? []),
|
||||||
|
fetchUser(user._id),
|
||||||
|
]);
|
||||||
|
|
||||||
const response: ServerDetails = {
|
const response: ServerDetails = {
|
||||||
id: server._id,
|
id: server._id,
|
||||||
name: server.name,
|
name: server.name,
|
||||||
|
@ -51,6 +66,11 @@ wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSRespo
|
||||||
bannerURL: server.generateBannerURL(),
|
bannerURL: server.generateBannerURL(),
|
||||||
iconURL: server.generateIconURL(),
|
iconURL: server.generateIconURL(),
|
||||||
serverConfig,
|
serverConfig,
|
||||||
|
users: users.map(
|
||||||
|
u => u.status == 'fulfilled'
|
||||||
|
? { id: u.value._id, avatarURL: u.value.generateAvatarURL(), username: u.value.username }
|
||||||
|
: { id: u.reason }
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
cb({ success: true, server: response });
|
cb({ success: true, server: response });
|
||||||
|
@ -59,3 +79,5 @@ wsEvents.on('req:getUserServerDetails', async (data: ReqData, cb: (data: WSRespo
|
||||||
cb({ success: false, error: `${e}` });
|
cb({ success: false, error: `${e}` });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export { APIUser }
|
||||||
|
|
|
@ -17,7 +17,7 @@ wsEvents.on('req:getUserServers', async (data: ReqData, cb: (data: WSResponse) =
|
||||||
|
|
||||||
const mutuals = await user.fetchMutual();
|
const mutuals = await user.fetchMutual();
|
||||||
|
|
||||||
type ServerResponse = { id: string, perms: 0|1|2, name: string, iconURL?: string, bannerURL?: string }
|
type ServerResponse = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string }
|
||||||
|
|
||||||
const promises: Promise<ServerResponse>[] = [];
|
const promises: Promise<ServerResponse>[] = [];
|
||||||
|
|
||||||
|
|
38
bot/src/bot/modules/api/users.ts
Normal file
38
bot/src/bot/modules/api/users.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { User } from "revolt.js/dist/maps/Users";
|
||||||
|
import { client } from "../../..";
|
||||||
|
import { getPermissionLevel, parseUser } from "../../util";
|
||||||
|
import { wsEvents, WSResponse } from "../api_communication";
|
||||||
|
import { APIUser } from "./server_details";
|
||||||
|
|
||||||
|
wsEvents.on('req:getPermissionLevel', async (data: { user: string, server: string }, cb: (data: WSResponse) => void) => {
|
||||||
|
try {
|
||||||
|
const server = client.servers.get(data.server);
|
||||||
|
if (!server) return cb({ success: false, error: 'The requested server could not be found', statusCode: 404 });
|
||||||
|
|
||||||
|
let user: User;
|
||||||
|
try {
|
||||||
|
user = client.users.get(data.user) || await client.users.fetch(data.user);
|
||||||
|
} catch(e) {
|
||||||
|
cb({ success: false, error: 'The requested user could not be found', statusCode: 404 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cb({ success: true, level: await getPermissionLevel(user, server) })
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
cb({ success: false, error: `${e}` });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wsEvents.on('req:getUser', async (data: { user: string }, cb: (data: WSResponse) => void) => {
|
||||||
|
try {
|
||||||
|
const user = await parseUser(data.user);
|
||||||
|
if (!user)
|
||||||
|
cb({ success: false, statusCode: 404, error: 'User could not be found' });
|
||||||
|
else
|
||||||
|
cb({ success: true, user: { id: user._id, username: user.username, avatarURL: user.generateAvatarURL() } as APIUser });
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
cb({ success: false, error: `${e}` });
|
||||||
|
}
|
||||||
|
});
|
|
@ -135,3 +135,4 @@ export { wsEvents, wsSend, WSResponse }
|
||||||
|
|
||||||
import('./api/servers');
|
import('./api/servers');
|
||||||
import('./api/server_details');
|
import('./api/server_details');
|
||||||
|
import('./api/users');
|
||||||
|
|
|
@ -99,13 +99,13 @@ async function checkSudoPermission(message: Message): Promise<boolean> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function getPermissionLevel(user: User|Member, server: Server): Promise<0|1|2> {
|
async function getPermissionLevel(user: User|Member, server: Server): Promise<0|1|2|3> {
|
||||||
if (isSudo(user instanceof User ? user : (user.user || await client.users.fetch(user._id.user)))) return 2;
|
if (isSudo(user instanceof User ? user : (user.user || await client.users.fetch(user._id.user)))) return 2;
|
||||||
|
|
||||||
const member = user instanceof User ? await server.fetchMember(user) : user;
|
const member = user instanceof User ? await server.fetchMember(user) : user;
|
||||||
if (user instanceof Member) user = user.user!;
|
if (user instanceof Member) user = user.user!;
|
||||||
|
|
||||||
if (hasPerm(member, 'ManageServer')) return 2;
|
if (hasPerm(member, 'ManageServer')) return 3;
|
||||||
if (hasPerm(member, 'KickMembers')) return 1;
|
if (hasPerm(member, 'KickMembers')) return 1;
|
||||||
|
|
||||||
const config = (await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig;
|
const config = (await client.db.get('servers').findOne({ id: server._id }) || {}) as ServerConfig;
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@mdi/js": "^6.5.95",
|
||||||
|
"@mdi/react": "^1.5.0",
|
||||||
"@revoltchat/ui": "^1.0.24",
|
"@revoltchat/ui": "^1.0.24",
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"@types/core-js": "^2.5.5",
|
"@types/core-js": "^2.5.5",
|
||||||
|
@ -14,6 +16,7 @@
|
||||||
"axios": "^0.25.0",
|
"axios": "^0.25.0",
|
||||||
"core-js": "^3.20.3",
|
"core-js": "^3.20.3",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
|
"prop-types": "^15.8.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-router-dom": "^6.2.1",
|
"react-router-dom": "^6.2.1",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { H2 } from '@revoltchat/ui/lib/components/atoms/heading/H2';
|
||||||
import { API_URL } from "../App";
|
import { API_URL } from "../App";
|
||||||
import { getAuthHeaders } from "../utils";
|
import { getAuthHeaders } from "../utils";
|
||||||
|
|
||||||
type Server = { id: string, perms: 0|1|2, name: string, iconURL?: string, bannerURL?: string }
|
type Server = { id: string, perms: 0|1|2|3, name: string, iconURL?: string, bannerURL?: string }
|
||||||
|
|
||||||
function permissionName(p: number) {
|
function permissionName(p: number) {
|
||||||
switch(p) {
|
switch(p) {
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
import localforage from "localforage";
|
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { FunctionComponent, useCallback, useEffect, useState } from "react";
|
import { FunctionComponent, useCallback, useEffect, useState } from "react";
|
||||||
import { Button } from '@revoltchat/ui/lib/components/atoms/inputs/Button';
|
import { Button } from '@revoltchat/ui/lib/components/atoms/inputs/Button';
|
||||||
import { InputBox } from '@revoltchat/ui/lib/components/atoms/inputs/InputBox';
|
import { InputBox } from '@revoltchat/ui/lib/components/atoms/inputs/InputBox';
|
||||||
import { Checkbox } from '@revoltchat/ui/lib/components/atoms/inputs/Checkbox';
|
import { Checkbox } from '@revoltchat/ui/lib/components/atoms/inputs/Checkbox';
|
||||||
|
import { LineDivider } from '@revoltchat/ui/lib/components/atoms/layout/LineDivider';
|
||||||
import { H1 } from '@revoltchat/ui/lib/components/atoms/heading/H1';
|
import { H1 } from '@revoltchat/ui/lib/components/atoms/heading/H1';
|
||||||
import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
|
import { H3 } from '@revoltchat/ui/lib/components/atoms/heading/H3';
|
||||||
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
|
import { H4 } from '@revoltchat/ui/lib/components/atoms/heading/H4';
|
||||||
|
import { Icon } from '@mdi/react';
|
||||||
|
import { mdiCloseBox } from '@mdi/js';
|
||||||
import { API_URL } from "../App";
|
import { API_URL } from "../App";
|
||||||
import { getAuthHeaders } from "../utils";
|
import { getAuthHeaders } from "../utils";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
type Server = { id?: string, perms?: 0|1|2, name?: string, description?: string, iconURL?: string, bannerURL?: string, serverConfig?: any }
|
type User = { id: string, username?: string, avatarURL?: string }
|
||||||
|
|
||||||
|
type Server = {
|
||||||
|
id?: string,
|
||||||
|
perms?: 0|1|2|3,
|
||||||
|
name?: string,
|
||||||
|
description?: string,
|
||||||
|
iconURL?: string,
|
||||||
|
bannerURL?: string,
|
||||||
|
serverConfig?: { [key: string]: any },
|
||||||
|
users: User[],
|
||||||
|
}
|
||||||
|
|
||||||
const ServerDashboard: FunctionComponent = () => {
|
const ServerDashboard: FunctionComponent = () => {
|
||||||
const [serverInfo, setServerInfo] = useState({} as Server);
|
const [serverInfo, setServerInfo] = useState({} as Server);
|
||||||
|
@ -19,6 +32,9 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
|
|
||||||
const [prefix, setPrefix] = useState('' as string|undefined);
|
const [prefix, setPrefix] = useState('' as string|undefined);
|
||||||
const [prefixAllowSpace, setPrefixAllowSpace] = useState(false);
|
const [prefixAllowSpace, setPrefixAllowSpace] = useState(false);
|
||||||
|
|
||||||
|
const [botManagers, setBotManagers] = useState([] as string[]);
|
||||||
|
const [moderators, setModerators] = useState([] as string[]);
|
||||||
|
|
||||||
const { serverid } = useParams();
|
const { serverid } = useParams();
|
||||||
|
|
||||||
|
@ -30,11 +46,15 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get(`${API_URL}/dash/server/${serverid}`, { headers: await getAuthHeaders() });
|
const res = await axios.get(`${API_URL}/dash/server/${serverid}`, { headers: await getAuthHeaders() });
|
||||||
console.log(res.data);
|
console.log(res.data);
|
||||||
|
|
||||||
const server: Server = res.data.server;
|
const server: Server = res.data.server;
|
||||||
setServerInfo(server);
|
setServerInfo(server);
|
||||||
|
|
||||||
setPrefix(server.serverConfig?.prefix || undefined);
|
setPrefix(server.serverConfig?.prefix || '');
|
||||||
setPrefixAllowSpace(!!server.serverConfig?.spaceAfterPrefix);
|
setPrefixAllowSpace(!!server.serverConfig?.spaceAfterPrefix);
|
||||||
|
|
||||||
|
setBotManagers(server.serverConfig?.botManagers ?? []);
|
||||||
|
setModerators(server.serverConfig?.moderators ?? []);
|
||||||
} catch(e: any) {
|
} catch(e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setStatus(`${e?.message ?? e}`);
|
setStatus(`${e?.message ?? e}`);
|
||||||
|
@ -50,7 +70,7 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
<div hidden={Object.keys(serverInfo).length == 0}>
|
<div hidden={Object.keys(serverInfo).length == 0}>
|
||||||
<H4>{serverInfo.description ?? <i>No server description set</i>}</H4>
|
<H4>{serverInfo.description ?? <i>No server description set</i>}</H4>
|
||||||
<br/>
|
<br/>
|
||||||
<div style={{ paddingLeft: '10px' }}>
|
<div style={{ paddingLeft: '10px', paddingRight: '10px' }}>
|
||||||
<H3>Prefix</H3>
|
<H3>Prefix</H3>
|
||||||
<InputBox
|
<InputBox
|
||||||
style={{ width: '150px', }}
|
style={{ width: '150px', }}
|
||||||
|
@ -61,7 +81,7 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
style={{ width: '400px' }}
|
style={{ maxWidth: '400px' }}
|
||||||
value={prefixAllowSpace}
|
value={prefixAllowSpace}
|
||||||
onChange={() => {
|
onChange={() => {
|
||||||
setPrefixAllowSpace(!prefixAllowSpace);
|
setPrefixAllowSpace(!prefixAllowSpace);
|
||||||
|
@ -73,10 +93,191 @@ const ServerDashboard: FunctionComponent = () => {
|
||||||
style={{ marginTop: "16px" }}
|
style={{ marginTop: "16px" }}
|
||||||
onClick={saveConfig}
|
onClick={saveConfig}
|
||||||
>Save</Button>
|
>Save</Button>
|
||||||
|
|
||||||
|
<LineDivider />
|
||||||
|
|
||||||
|
<H3>Bot Managers</H3>
|
||||||
|
<H4>
|
||||||
|
Only users with "Manage Server" permission are allowed to add/remove other
|
||||||
|
bot managers and are automatically considered bot manager.
|
||||||
|
</H4>
|
||||||
|
<UserListTypeContainer>
|
||||||
|
<UserListContainer disabled={(serverInfo.perms ?? 0) < 3}>
|
||||||
|
{botManagers.map((uid: string) => {
|
||||||
|
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
||||||
|
return (
|
||||||
|
<UserListEntry type='MANAGER' user={user} key={uid} />
|
||||||
|
)})}
|
||||||
|
<UserListAddField type='MANAGER' />
|
||||||
|
</UserListContainer>
|
||||||
|
</UserListTypeContainer>
|
||||||
|
|
||||||
|
<H3>Moderators</H3>
|
||||||
|
<H4>
|
||||||
|
Only bot managers are allowed to add/remove moderators.
|
||||||
|
All bot managers are also moderators.
|
||||||
|
</H4>
|
||||||
|
<UserListTypeContainer>
|
||||||
|
<UserListContainer disabled={(serverInfo.perms ?? 0) < 2}>
|
||||||
|
{moderators.map((uid: string) => {
|
||||||
|
const user = serverInfo.users.find(u => u.id == uid) || { id: uid }
|
||||||
|
return (
|
||||||
|
<UserListEntry type='MOD' user={user} key={uid} />
|
||||||
|
)})}
|
||||||
|
<UserListAddField type='MOD' />
|
||||||
|
</UserListContainer>
|
||||||
|
</UserListTypeContainer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
|
function UserListEntry(props: { user: User, type: 'MANAGER'|'MOD' }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={props.user.id}
|
||||||
|
style={{
|
||||||
|
display: 'block',
|
||||||
|
margin: '4px 6px',
|
||||||
|
padding: '4px',
|
||||||
|
backgroundColor: 'var(--tertiary-background)',
|
||||||
|
borderRadius: '5px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={props.user.avatarURL ?? 'https://amogus.org/amogus.png'}
|
||||||
|
width={28}
|
||||||
|
height={28}
|
||||||
|
style={{
|
||||||
|
borderRadius: '50%',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
color: 'var(--foreground)',
|
||||||
|
fontSize: '20px',
|
||||||
|
paddingLeft: '6px',
|
||||||
|
marginBottom: '2px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
}}
|
||||||
|
>{props.user.username ?? 'Unknown'}</span>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginLeft: '4px',
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
display: 'inline-block',
|
||||||
|
height: '30px',
|
||||||
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
const res = await axios.delete(
|
||||||
|
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}/${props.user.id}`,
|
||||||
|
{ headers: await getAuthHeaders() }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (props.type == 'MANAGER') {
|
||||||
|
setBotManagers(res.data.managers);
|
||||||
|
}
|
||||||
|
else if (props.type == 'MOD') {
|
||||||
|
setModerators(res.data.mods);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon // todo: hover effect
|
||||||
|
path={mdiCloseBox}
|
||||||
|
color='var(--tertiary-foreground)'
|
||||||
|
size='30px'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserListContainer(props: { disabled: boolean, children: any }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
...(props.disabled ? {
|
||||||
|
filter: 'grayscale(100%)',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
} : {})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserListTypeContainer(props: any) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
backgroundColor: 'var(--secondary-background)',
|
||||||
|
borderRadius: '10px',
|
||||||
|
marginTop: '15px',
|
||||||
|
paddingTop: '5px',
|
||||||
|
paddingBottom: '5px',
|
||||||
|
}}
|
||||||
|
>{props.children}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserListAddField(props: { type: 'MANAGER'|'MOD' }) {
|
||||||
|
const [content, setContent] = useState('');
|
||||||
|
|
||||||
|
const onConfirm = useCallback(async () => {
|
||||||
|
if (content.length) {
|
||||||
|
const res = await axios.put(
|
||||||
|
`${API_URL}/dash/server/${serverid}/${props.type == 'MANAGER' ? 'managers' : 'mods'}`,
|
||||||
|
{ item: content },
|
||||||
|
{ headers: await getAuthHeaders() }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.data.users?.length) {
|
||||||
|
res.data.users.forEach((user: User) => {
|
||||||
|
if (!serverInfo.users.find(u => u.id == user.id)) serverInfo.users.push(user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.type == 'MANAGER') {
|
||||||
|
setBotManagers(res.data.managers);
|
||||||
|
}
|
||||||
|
else if (props.type == 'MOD') {
|
||||||
|
setModerators(res.data.mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [content]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<InputBox
|
||||||
|
placeholder={`Add a ${props.type == 'MANAGER' ? 'bot manager' : 'moderator'}...`}
|
||||||
|
value={content}
|
||||||
|
onChange={e => setContent(e.currentTarget.value)}
|
||||||
|
style={{
|
||||||
|
float: 'left',
|
||||||
|
width: '180px',
|
||||||
|
height: '38px',
|
||||||
|
margin: '4px 8px',
|
||||||
|
}}
|
||||||
|
onKeyDown={e => e.key == 'Enter' && onConfirm()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
float: 'left',
|
||||||
|
width: '40px',
|
||||||
|
height: '38px',
|
||||||
|
margin: '4px 8px',
|
||||||
|
}}
|
||||||
|
onClick={onConfirm}
|
||||||
|
>Ok</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
export default ServerDashboard;
|
export default ServerDashboard;
|
||||||
|
|
|
@ -265,6 +265,16 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
|
||||||
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
|
||||||
|
|
||||||
|
"@mdi/js@^6.5.95":
|
||||||
|
version "6.5.95"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mdi/js/-/js-6.5.95.tgz#2d895b013408f213252b77c30e0fdaaba6dc8b4b"
|
||||||
|
integrity sha512-x/bwEoAGP+Mo10Dfk5audNIPi7Yz8ZBrILcbXLW3ShOI/njpgodzpgpC2WYK3D2ZSC392peRRemIFb/JsyzzYQ==
|
||||||
|
|
||||||
|
"@mdi/react@^1.5.0":
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mdi/react/-/react-1.5.0.tgz#461d2064ba12d509723bffc95e2f7169a6ac884a"
|
||||||
|
integrity sha512-NztRgUxSYD+ImaKN94Tg66VVVqXj4SmlDGzZoz48H9riJ+Awha56sfXH2fegw819NWo7KI3oeS1Es0lNQqwr0w==
|
||||||
|
|
||||||
"@revoltchat/ui@^1.0.24":
|
"@revoltchat/ui@^1.0.24":
|
||||||
version "1.0.24"
|
version "1.0.24"
|
||||||
resolved "https://registry.yarnpkg.com/@revoltchat/ui/-/ui-1.0.24.tgz#ce55cc225ec92eb07dd5865b9255d1f160d06ce2"
|
resolved "https://registry.yarnpkg.com/@revoltchat/ui/-/ui-1.0.24.tgz#ce55cc225ec92eb07dd5865b9255d1f160d06ce2"
|
||||||
|
@ -719,7 +729,7 @@ lodash@^4.17.11:
|
||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
loose-envify@^1.1.0:
|
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
@ -780,6 +790,15 @@ postcss@^8.4.5:
|
||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.1"
|
source-map-js "^1.0.1"
|
||||||
|
|
||||||
|
prop-types@^15.8.1:
|
||||||
|
version "15.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||||
|
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
react-is "^16.13.1"
|
||||||
|
|
||||||
react-dom@^17.0.2:
|
react-dom@^17.0.2:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
|
||||||
|
@ -789,7 +808,7 @@ react-dom@^17.0.2:
|
||||||
object-assign "^4.1.1"
|
object-assign "^4.1.1"
|
||||||
scheduler "^0.20.2"
|
scheduler "^0.20.2"
|
||||||
|
|
||||||
react-is@^16.7.0:
|
react-is@^16.13.1, react-is@^16.7.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
Loading…
Reference in a new issue