This commit is contained in:
Hri7566 2024-09-15 17:11:41 -04:00
parent 85643184ea
commit 2112f730a8
16 changed files with 170 additions and 28 deletions

1
config/frontend.yml Normal file
View File

@ -0,0 +1 @@
topButtons: none

18
config/tags.yml Normal file
View File

@ -0,0 +1,18 @@
admin:
text: ADMIN
color: "#ff5555"
trialmod:
text: TRIAL MOD
color: "#ffbb00"
mod:
text: MOD
color: "#00aa00"
media:
text: MEDIA
color: "#ff55ff"
bot:
text: BOT
color: "#5555ff"
owner:
text: OWNER
color: "#aa0000"

View File

@ -11,7 +11,7 @@ defaultFlags:
# Whether or not to allow users to change their color.
# Based on some reports, the MPP.com server stopped allowing this around 2016.
enableColorChanging: true
enableColorChanging: false
# Whether to allow custom data inside note messages.
# This was in the original server, but not in MPP.net's server do to stricter sanitization.

View File

@ -16,7 +16,7 @@ model User {
name String @default("Anonymous")
color String @default("#ffffff")
flags String @default("{}") // JSON flags object
tag String // JSON tag
tag String? // JSON tag
tokens String @default("[]") // JSON tokens
group String @default("default") // Permission group
}
@ -34,7 +34,8 @@ model Channel {
}
model Role {
userId String @unique
id Int @id @unique @default(autoincrement())
userId String
roleId String
}

View File

@ -321,6 +321,7 @@ export class Channel extends EventEmitter {
this.on("user data update", (user: User) => {
try {
if (!this.ppl.map(p => p._id).includes(user.id)) return;
if (typeof user.name !== "string") return;
if (typeof user.color !== "string") return;
if (typeof user.id !== "string") return;

View File

@ -15,7 +15,8 @@ export const config = ConfigManager.loadConfig<Record<string, string[]>>(
"usersetOthers",
"siteBan",
"siteBanAnyReason",
"siteBanAnyDuration"
"siteBanAnyDuration",
"event.admin.*"
]
}
);

View File

@ -29,7 +29,7 @@ export async function giveRole(userId: string, roleId: string) {
}
export async function removeRole(userId: string, roleId: string) {
return await prisma.role.delete({
return await prisma.role.deleteMany({
where: {
userId,
roleId

View File

@ -19,7 +19,7 @@ import { loadForcedStartupChannels } from "./channel/forceLoad";
import { Logger } from "./util/Logger";
// docker hates this next one
import { startReadline } from "./util/readline";
import { loadDefaultPermissions } from "./data/permission";
import { loadDefaultPermissions } from "./data/permissions";
// wrapper for some reason
export function startServer() {

View File

@ -8,7 +8,8 @@ import {
loadDefaultPermissions,
removeAllRolePermissions,
removeRolePermission
} from "~/data/permission";
} from "~/data/permissions";
import { builtinTags, removeTag, setBuiltinTag } from "../tags";
Command.addCommand(
new Command(["help", "h", "commands", "cmds"], "help", msg => {
@ -94,11 +95,19 @@ Command.addCommand(
if (msg.args[1] === "add") {
if (!msg.args[3]) return "No role id provided";
await giveRole(msg.args[2], msg.args[3]);
await setBuiltinTag(msg.args[2], msg.args[3]);
return `Gave user ${msg.args[2]} role ${msg.args[3]}`;
} else if (msg.args[1] === "remove") {
if (!msg.args[3]) return "No role id provided";
await removeRole(msg.args[2], msg.args[3]);
if (Object.keys(builtinTags).includes(msg.args[3])) {
await removeTag(msg.args[2]);
}
return `Removed role ${msg.args[3]} from ${msg.args[2]}`;
} else if (msg.args[1] === "list") {
const roles = await getRoles(msg.args[2]);

78
src/util/tags.ts Normal file
View File

@ -0,0 +1,78 @@
import { Socket } from "~/ws/Socket";
import { ConfigManager } from "./config";
import { Tag } from "./types";
import { readUser, updateUser } from "~/data/user";
import { prisma } from "~/data/prisma";
import { User } from "@prisma/client";
import { ChannelList } from "~/channel/ChannelList";
export const builtinTags = ConfigManager.loadConfig<Record<string, Tag>>(
"config/tags.yml",
{
admin: {
text: "ADMIN",
color: "#ff5555"
},
trialmod: {
text: "TRIAL MOD",
color: "#ffbb00"
},
mod: {
text: "MOD",
color: "#00aa00"
},
media: {
text: "MEDIA",
color: "#ff55ff"
},
bot: {
text: "BOT",
color: "#5555ff"
},
owner: {
text: "OWNER",
color: "#aa0000"
}
}
);
function propogateUser(user: User) {
const channelList = ChannelList.getList();
for (const ch of channelList) {
ch.emit("user data update", user);
}
}
export async function setBuiltinTag(userId: string, tagId: string) {
const user = await readUser(userId);
if (!user) return;
const tag = builtinTags[tagId];
if (!tag) return;
user.tag = JSON.stringify(tag);
await updateUser(user.id, user);
propogateUser(user);
}
export async function setTag(userId: string, tag: Tag) {
const user = await readUser(userId);
if (!user) return;
user.tag = JSON.stringify(tag);
await updateUser(user.id, user);
propogateUser(user);
}
export async function removeTag(userId: string) {
const user = await readUser(userId);
if (!user) return;
user.tag = "";
await updateUser(user.id, user);
propogateUser(user);
}

12
src/util/types.d.ts vendored
View File

@ -231,6 +231,18 @@ declare interface ServerEvents {
name: string;
};
setcolor: {
m: "setcolor";
_id: string;
color: string;
};
setname: {
m: "setname";
_id: string;
color: string;
};
user_flag: {
m: "user_flag";
_id: string;

View File

@ -41,8 +41,9 @@ import {
getUserPermissions,
hasPermission,
validatePermission
} from "~/data/permission";
} from "~/data/permissions";
import { getRoles } from "~/data/role";
import { setTag } from "~/util/tags";
const logger = new Logger("Sockets");
@ -792,10 +793,8 @@ export class Socket extends EventEmitter {
**/
public setTag(text: string, color: string) {
//logger.debug("Setting tag:", text, color);
const user = this.getUser();
if (!user) return;
user.tag = JSON.stringify({ text, color });
updateUser(this.getUserID(), user);
if (!this.user) return;
setTag(this.user.id, { text, color });
}
/**
@ -836,6 +835,11 @@ export class Socket extends EventEmitter {
});
}
/**
* Check if this socket has a given permission
* @param perm Permission string
* @returns Whether this socket has the given permission
*/
public async hasPermission(perm: string) {
if (!this.user) return false;
@ -844,6 +848,8 @@ export class Socket extends EventEmitter {
for (const permission of permissions) {
if (validatePermission(perm, permission)) return true;
}
return false;
}
}

View File

@ -1,18 +1,18 @@
import type { ServerEventListener, ServerEvents } from "../util/types";
import type { EventID, ServerEventListener, ServerEvents } from "../util/types";
export class EventGroup {
public eventList = new Array<ServerEventListener<keyof ServerEvents>>();
public eventList = new Array<ServerEventListener<any>>();
constructor(public id: string) {}
public add(listener: ServerEventListener<keyof ServerEvents>) {
public add(listener: ServerEventListener<any>) {
this.eventList.push(listener);
}
public addMany(...listeners: ServerEventListener<keyof ServerEvents>[]) {
public addMany(...listeners: ServerEventListener<any>[]) {
for (const l of listeners) this.add(l);
}
public remove(listener: ServerEventListener<keyof ServerEvents>) {
public remove(listener: ServerEventListener<any>) {
this.eventList.splice(this.eventList.indexOf(listener), 1);
}
}

View File

@ -1,4 +1,4 @@
import { getUserPermissions } from "~/data/permission";
import { getUserPermissions } from "~/data/permissions";
import { Logger } from "~/util/Logger";
import { getMOTD } from "~/util/motd";
import { createToken, getToken, validateToken } from "~/util/token";

View File

@ -8,6 +8,8 @@ import env from "../util/env";
import { getMOTD } from "../util/motd";
import nunjucks from "nunjucks";
import type { ServerWebSocket } from "bun";
import { ConfigManager } from "~/util/config";
import { config as usersConfig } from "./usersConfig";
const logger = new Logger("WebSocket Server");
@ -15,6 +17,17 @@ const logger = new Logger("WebSocket Server");
// for checking if they visited the site and are also connected to the websocket
const httpIpCache = new Map<string, number>();
interface IFrontendConfig {
topButtons: "original" | "none";
}
const config = ConfigManager.loadConfig<IFrontendConfig>(
"config/frontend.yml",
{
topButtons: "original"
}
);
/**
* Get a rendered version of the index file
* @returns Response with html in it
@ -29,7 +42,9 @@ async function getIndex() {
const index = Bun.file("./public/index.html");
const rendered = nunjucks.renderString(await index.text(), {
motd: getMOTD()
motd: getMOTD(),
config,
usersConfig
});
const response = new Response(rendered);
@ -38,7 +53,7 @@ async function getIndex() {
return response;
}
type ServerWebSocketMPP = ServerWebSocket<{ ip: string, socket: Socket }>
type ServerWebSocketMPP = ServerWebSocket<{ ip: string; socket: Socket }>;
export const app = Bun.serve<{ ip: string }>({
port: env.PORT,