Add Role Icons (#574)
* Role Icons Co-authored-by: Erkin Alp Güney <erkinalp9035@gmail.com> * Cache coherency rules Co-authored-by: MANIKILLER <manikillrorg@gmail.com> Co-authored-by: ImAaronFR <96433859+ImAaronFR@users.noreply.github.com>
This commit is contained in:
parent
0292633721
commit
aaf5df14e1
@ -8,7 +8,8 @@ import {
|
||||
GuildRoleDeleteEvent,
|
||||
emitEvent,
|
||||
Config,
|
||||
DiscordApiErrors
|
||||
DiscordApiErrors,
|
||||
handleFile
|
||||
} from "@fosscord/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@fosscord/api";
|
||||
@ -22,6 +23,8 @@ export interface RoleModifySchema {
|
||||
hoist?: boolean; // whether the role should be displayed separately in the sidebar
|
||||
mentionable?: boolean; // whether the role should be mentionable
|
||||
position?: number;
|
||||
icon?: string;
|
||||
unicode_emoji?: string;
|
||||
}
|
||||
|
||||
export type RolePositionUpdateSchema = {
|
||||
@ -58,7 +61,9 @@ router.post("/", route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" })
|
||||
guild_id: guild_id,
|
||||
managed: false,
|
||||
permissions: String(req.permission!.bitfield & BigInt(body.permissions || "0")),
|
||||
tags: undefined
|
||||
tags: undefined,
|
||||
icon: null,
|
||||
unicode_emoji: null
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
@ -105,6 +110,8 @@ router.patch("/:role_id", route({ body: "RoleModifySchema", permission: "MANAGE_
|
||||
const { role_id, guild_id } = req.params;
|
||||
const body = req.body as RoleModifySchema;
|
||||
|
||||
if (body.icon) body.icon = await handleFile(`/role-icons/${role_id}`, body.icon as string);
|
||||
|
||||
const role = new Role({
|
||||
...body,
|
||||
id: role_id,
|
||||
|
@ -107,4 +107,4 @@
|
||||
"ws": "^7.4.2",
|
||||
"nanocolors": "^0.2.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { Server, ServerOptions } from "lambert-server";
|
||||
import { Config, initDatabase, registerRoutes } from "@fosscord/util";
|
||||
import path from "path";
|
||||
import avatarsRoute from "./routes/avatars";
|
||||
import iconsRoute from "./routes/role-icons";
|
||||
import bodyParser from "body-parser";
|
||||
|
||||
export interface CDNServerOptions extends ServerOptions {}
|
||||
@ -40,6 +41,9 @@ export class CDNServer extends Server {
|
||||
this.app.use("/icons/", avatarsRoute);
|
||||
this.log("verbose", "[Server] Route /icons registered");
|
||||
|
||||
this.app.use("/role-icons/", iconsRoute);
|
||||
this.log("verbose", "[Server] Route /role-icons registered");
|
||||
|
||||
this.app.use("/emojis/", avatarsRoute);
|
||||
this.log("verbose", "[Server] Route /emojis registered");
|
||||
|
||||
|
102
cdn/src/routes/role-icons.ts
Normal file
102
cdn/src/routes/role-icons.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { Router, Response, Request } from "express";
|
||||
import { Config, Snowflake } from "@fosscord/util";
|
||||
import { storage } from "../util/Storage";
|
||||
import FileType from "file-type";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import crypto from "crypto";
|
||||
import { multer } from "../util/multer";
|
||||
|
||||
//Role icons ---> avatars.ts modified
|
||||
|
||||
// TODO: check premium and animated pfp are allowed in the config
|
||||
// TODO: generate different sizes of icon
|
||||
// TODO: generate different image types of icon
|
||||
// TODO: delete old icons
|
||||
|
||||
const STATIC_MIME_TYPES = [
|
||||
"image/png",
|
||||
"image/jpeg",
|
||||
"image/webp",
|
||||
"image/svg+xml",
|
||||
"image/svg",
|
||||
];
|
||||
const ALLOWED_MIME_TYPES = [...STATIC_MIME_TYPES];
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/:role_id",
|
||||
multer.single("file"),
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.headers.signature !== Config.get().security.requestSignature)
|
||||
throw new HTTPError("Invalid request signature");
|
||||
if (!req.file) throw new HTTPError("Missing file");
|
||||
const { buffer, mimetype, size, originalname, fieldname } = req.file;
|
||||
const { role_id } = req.params;
|
||||
|
||||
var hash = crypto
|
||||
.createHash("md5")
|
||||
.update(Snowflake.generate())
|
||||
.digest("hex");
|
||||
|
||||
const type = await FileType.fromBuffer(buffer);
|
||||
if (!type || !ALLOWED_MIME_TYPES.includes(type.mime))
|
||||
throw new HTTPError("Invalid file type");
|
||||
|
||||
const path = `role-icons/${role_id}/${hash}.png`;
|
||||
const endpoint =
|
||||
Config.get().cdn.endpointPublic || "http://localhost:3003";
|
||||
|
||||
await storage.set(path, buffer);
|
||||
|
||||
return res.json({
|
||||
id: hash,
|
||||
content_type: type.mime,
|
||||
size,
|
||||
url: `${endpoint}${req.baseUrl}/${role_id}/${hash}`,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
router.get("/:role_id", async (req: Request, res: Response) => {
|
||||
var { role_id } = req.params;
|
||||
//role_id = role_id.split(".")[0]; // remove .file extension
|
||||
const path = `role-icons/${role_id}`;
|
||||
|
||||
const file = await storage.get(path);
|
||||
if (!file) throw new HTTPError("not found", 404);
|
||||
const type = await FileType.fromBuffer(file);
|
||||
|
||||
res.set("Content-Type", type?.mime);
|
||||
res.set("Cache-Control", "public, max-age=31536000, must-revalidate");
|
||||
|
||||
return res.send(file);
|
||||
});
|
||||
|
||||
router.get("/:role_id/:hash", async (req: Request, res: Response) => {
|
||||
var { role_id, hash } = req.params;
|
||||
//hash = hash.split(".")[0]; // remove .file extension
|
||||
const path = `role-icons/${role_id}/${hash}`;
|
||||
|
||||
const file = await storage.get(path);
|
||||
if (!file) throw new HTTPError("not found", 404);
|
||||
const type = await FileType.fromBuffer(file);
|
||||
|
||||
res.set("Content-Type", type?.mime);
|
||||
res.set("Cache-Control", "public, max-age=31536000, must-revalidate");
|
||||
|
||||
return res.send(file);
|
||||
});
|
||||
|
||||
router.delete("/:role_id/:id", async (req: Request, res: Response) => {
|
||||
if (req.headers.signature !== Config.get().security.requestSignature)
|
||||
throw new HTTPError("Invalid request signature");
|
||||
const { role_id, id } = req.params;
|
||||
const path = `role-icons/${role_id}/${id}`;
|
||||
|
||||
await storage.delete(path);
|
||||
|
||||
return res.send({ success: true });
|
||||
});
|
||||
|
||||
export default router;
|
@ -340,6 +340,8 @@ export class Guild extends BaseClass {
|
||||
name: "@everyone",
|
||||
permissions: String("2251804225"),
|
||||
position: 0,
|
||||
icon: null,
|
||||
unicode_emoji: null
|
||||
}).save();
|
||||
|
||||
if (!body.channels || !body.channels.length) body.channels = [{ id: "01", type: 0, name: "general" }];
|
||||
|
@ -36,6 +36,12 @@ export class Role extends BaseClass {
|
||||
@Column()
|
||||
position: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
icon: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
unicode_emoji: string;
|
||||
|
||||
@Column({ type: "simple-json", nullable: true })
|
||||
tags?: {
|
||||
bot_id?: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user