mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-10 04:32:35 +01:00
commit
009a3ad27f
13406
assets/openapi.json
13406
assets/openapi.json
File diff suppressed because it is too large
Load Diff
488557
assets/schemas.json
488557
assets/schemas.json
File diff suppressed because it is too large
Load Diff
@ -27,34 +27,46 @@ require("missing-native-js-functions");
|
||||
|
||||
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
|
||||
const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json");
|
||||
let schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
|
||||
|
||||
for (var schema in schemas) {
|
||||
const part = schemas[schema];
|
||||
for (var key in part.properties) {
|
||||
if (part.properties[key].anyOf) {
|
||||
const nullIndex = part.properties[key].anyOf.findIndex(
|
||||
(x) => x.type == "null",
|
||||
);
|
||||
if (nullIndex != -1) {
|
||||
part.properties[key].nullable = true;
|
||||
part.properties[key].anyOf.splice(nullIndex, 1);
|
||||
|
||||
if (part.properties[key].anyOf.length == 1) {
|
||||
Object.assign(
|
||||
part.properties[key],
|
||||
part.properties[key].anyOf[0],
|
||||
);
|
||||
delete part.properties[key].anyOf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const specification = JSON.parse(
|
||||
fs.readFileSync(openapiPath, { encoding: "utf8" }),
|
||||
);
|
||||
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
|
||||
// const specification = JSON.parse(
|
||||
// fs.readFileSync(openapiPath, { encoding: "utf8" }),
|
||||
// );
|
||||
let specification = {
|
||||
openapi: "3.1.0",
|
||||
info: {
|
||||
title: "Spacebar Server",
|
||||
description:
|
||||
"Spacebar is a free open source selfhostable discord compatible chat, voice and video platform",
|
||||
license: {
|
||||
name: "AGPLV3",
|
||||
url: "https://www.gnu.org/licenses/agpl-3.0.en.html",
|
||||
},
|
||||
version: "1.0.0",
|
||||
},
|
||||
externalDocs: {
|
||||
description: "Spacebar Docs",
|
||||
url: "https://docs.spacebar.chat",
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: "https://old.server.spacebar.chat/api/",
|
||||
description: "Official Spacebar Instance",
|
||||
},
|
||||
],
|
||||
components: {
|
||||
securitySchemes: {
|
||||
bearer: {
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
description: "Bearer/Bot prefixes are not required.",
|
||||
bearerFormat: "JWT",
|
||||
in: "header",
|
||||
},
|
||||
},
|
||||
},
|
||||
tags: [],
|
||||
paths: {},
|
||||
};
|
||||
|
||||
function combineSchemas(schemas) {
|
||||
var definitions = {};
|
||||
@ -72,6 +84,11 @@ function combineSchemas(schemas) {
|
||||
}
|
||||
|
||||
for (const key in definitions) {
|
||||
const reg = new RegExp(/^[a-zA-Z0-9.\-_]+$/, "gm");
|
||||
if (!reg.test(key)) {
|
||||
console.error(`Invalid schema name: ${key} (${reg.test(key)})`);
|
||||
continue;
|
||||
}
|
||||
specification.components = specification.components || {};
|
||||
specification.components.schemas =
|
||||
specification.components.schemas || {};
|
||||
@ -102,30 +119,20 @@ function getTag(key) {
|
||||
function apiRoutes() {
|
||||
const routes = getRouteDescriptions();
|
||||
|
||||
const tags = Array.from(routes.keys()).map((x) => getTag(x));
|
||||
specification.tags = specification.tags || [];
|
||||
specification.tags = [...specification.tags.map((x) => x.name), ...tags]
|
||||
.unique()
|
||||
.map((x) => ({ name: x }));
|
||||
|
||||
specification.components = specification.components || {};
|
||||
specification.components.securitySchemes = {
|
||||
bearer: {
|
||||
type: "http",
|
||||
scheme: "bearer",
|
||||
description: "Bearer/Bot prefixes are not required.",
|
||||
},
|
||||
};
|
||||
// populate tags
|
||||
const tags = Array.from(routes.keys())
|
||||
.map((x) => getTag(x))
|
||||
.sort((a, b) => a.localeCompare(b));
|
||||
specification.tags = tags.unique().map((x) => ({ name: x }));
|
||||
|
||||
routes.forEach((route, pathAndMethod) => {
|
||||
const [p, method] = pathAndMethod.split("|");
|
||||
const path = p.replace(/:(\w+)/g, "{$1}");
|
||||
|
||||
specification.paths = specification.paths || {};
|
||||
let obj = specification.paths[path]?.[method] || {};
|
||||
obj["x-right-required"] = route.right;
|
||||
obj["x-permission-required"] = route.permission;
|
||||
obj["x-fires-event"] = route.test?.event;
|
||||
obj["x-fires-event"] = route.event;
|
||||
|
||||
if (
|
||||
!NO_AUTHORIZATION_ROUTES.some((x) => {
|
||||
@ -136,48 +143,56 @@ function apiRoutes() {
|
||||
obj.security = [{ bearer: [] }];
|
||||
}
|
||||
|
||||
if (route.body) {
|
||||
if (route.description) obj.description = route.description;
|
||||
if (route.summary) obj.summary = route.summary;
|
||||
if (route.deprecated) obj.deprecated = route.deprecated;
|
||||
|
||||
if (route.requestBody) {
|
||||
obj.requestBody = {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: { $ref: `#/components/schemas/${route.body}` },
|
||||
schema: {
|
||||
$ref: `#/components/schemas/${route.requestBody}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
}.merge(obj.requestBody);
|
||||
}
|
||||
|
||||
if (route.test?.response) {
|
||||
const status = route.test.response.status || 200;
|
||||
let schema = {
|
||||
allOf: [
|
||||
{
|
||||
$ref: `#/components/schemas/${route.test.response.body}`,
|
||||
},
|
||||
{
|
||||
example: route.test.body,
|
||||
},
|
||||
],
|
||||
};
|
||||
if (!route.test.body) schema = schema.allOf[0];
|
||||
if (route.responses) {
|
||||
for (const [k, v] of Object.entries(route.responses)) {
|
||||
let schema = {
|
||||
$ref: `#/components/schemas/${v.body}`,
|
||||
};
|
||||
|
||||
obj.responses = {
|
||||
[status]: {
|
||||
...(route.test.response.body
|
||||
? {
|
||||
description:
|
||||
obj?.responses?.[status]?.description || "",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: schema,
|
||||
obj.responses = {
|
||||
[k]: {
|
||||
...(v.body
|
||||
? {
|
||||
description:
|
||||
obj?.responses?.[k]?.description || "",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: schema,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
: {
|
||||
description: "No description available",
|
||||
}),
|
||||
},
|
||||
}.merge(obj.responses);
|
||||
}
|
||||
} else {
|
||||
obj.responses = {
|
||||
default: {
|
||||
description: "No description available",
|
||||
},
|
||||
}.merge(obj.responses);
|
||||
delete obj.responses.default;
|
||||
};
|
||||
}
|
||||
|
||||
// handles path parameters
|
||||
if (p.includes(":")) {
|
||||
obj.parameters = p.match(/:\w+/g)?.map((x) => ({
|
||||
name: x.replace(":", ""),
|
||||
@ -187,16 +202,33 @@ function apiRoutes() {
|
||||
description: x.replace(":", ""),
|
||||
}));
|
||||
}
|
||||
|
||||
if (route.query) {
|
||||
// map to array
|
||||
const query = Object.entries(route.query).map(([k, v]) => ({
|
||||
name: k,
|
||||
in: "query",
|
||||
required: v.required,
|
||||
schema: { type: v.type },
|
||||
description: v.description,
|
||||
}));
|
||||
|
||||
obj.parameters = [...(obj.parameters || []), ...query];
|
||||
}
|
||||
|
||||
obj.tags = [...(obj.tags || []), getTag(p)].unique();
|
||||
|
||||
specification.paths[path] = {
|
||||
...specification.paths[path],
|
||||
[method]: obj,
|
||||
};
|
||||
specification.paths[path] = Object.assign(
|
||||
specification.paths[path] || {},
|
||||
{
|
||||
[method]: obj,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
console.log("Generating OpenAPI Specification...");
|
||||
combineSchemas(schemas);
|
||||
apiRoutes();
|
||||
|
||||
|
@ -57,6 +57,8 @@ const Excluded = [
|
||||
"PropertiesSchema",
|
||||
"AsyncSchema",
|
||||
"AnySchema",
|
||||
"SMTPConnection.CustomAuthenticationResponse",
|
||||
"TransportMakeRequestResponse",
|
||||
];
|
||||
|
||||
function modify(obj) {
|
||||
@ -75,14 +77,14 @@ function main() {
|
||||
const generator = TJS.buildGenerator(program, settings);
|
||||
if (!generator || !program) return;
|
||||
|
||||
let schemas = generator
|
||||
.getUserSymbols()
|
||||
.filter(
|
||||
(x) =>
|
||||
(x.endsWith("Schema") || x.endsWith("Response")) &&
|
||||
!Excluded.includes(x),
|
||||
let schemas = generator.getUserSymbols().filter((x) => {
|
||||
return (
|
||||
(x.endsWith("Schema") ||
|
||||
x.endsWith("Response") ||
|
||||
x.startsWith("API")) &&
|
||||
!Excluded.includes(x)
|
||||
);
|
||||
console.log(schemas);
|
||||
});
|
||||
|
||||
var definitions = {};
|
||||
|
||||
@ -133,7 +135,7 @@ function main() {
|
||||
definitions = { ...definitions, [name]: { ...part } };
|
||||
}
|
||||
|
||||
modify(definitions);
|
||||
//modify(definitions);
|
||||
|
||||
fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4));
|
||||
}
|
||||
|
@ -1,80 +1,64 @@
|
||||
const express = require("express");
|
||||
const path = require("path");
|
||||
const { traverseDirectory } = require("lambert-server");
|
||||
const RouteUtility = require("../../dist/api/util/handlers/route.js");
|
||||
|
||||
const methods = ["get", "post", "put", "delete", "patch"];
|
||||
const routes = new Map();
|
||||
let currentFile = "";
|
||||
let currentPath = "";
|
||||
|
||||
/*
|
||||
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
|
||||
Copyright (C) 2023 Spacebar and Spacebar Contributors
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
For some reason, if a route exports multiple functions, it won't be registered here!
|
||||
If someone could fix that I'd really appreciate it, but for now just, don't do that :p
|
||||
*/
|
||||
|
||||
const { traverseDirectory } = require("lambert-server");
|
||||
const path = require("path");
|
||||
const express = require("express");
|
||||
const RouteUtility = require("../../dist/api/util/handlers/route.js");
|
||||
const Router = express.Router;
|
||||
|
||||
const routes = new Map();
|
||||
let currentPath = "";
|
||||
let currentFile = "";
|
||||
const methods = ["get", "post", "put", "delete", "patch"];
|
||||
|
||||
function registerPath(file, method, prefix, path, ...args) {
|
||||
const urlPath = prefix + path;
|
||||
const sourceFile = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
||||
const opts = args.find((x) => typeof x === "object");
|
||||
if (opts) {
|
||||
routes.set(urlPath + "|" + method, opts);
|
||||
opts.file = sourceFile;
|
||||
// console.log(method, urlPath, opts);
|
||||
} else {
|
||||
console.log(
|
||||
`${sourceFile}\nrouter.${method}("${path}") is missing the "route()" description middleware\n`,
|
||||
const proxy = (file, method, prefix, path, ...args) => {
|
||||
const opts = args.find((x) => x?.prototype?.OPTS_MARKER == true);
|
||||
if (!opts)
|
||||
return console.error(
|
||||
`${file} has route without route() description middleware`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function routeOptions(opts) {
|
||||
return opts;
|
||||
}
|
||||
console.log(prefix + path + " - " + method);
|
||||
opts.file = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
||||
routes.set(prefix + path + "|" + method, opts());
|
||||
};
|
||||
|
||||
RouteUtility.route = routeOptions;
|
||||
express.Router = () => {
|
||||
return Object.fromEntries(
|
||||
methods.map((method) => [
|
||||
method,
|
||||
proxy.bind(null, currentFile, method, currentPath),
|
||||
]),
|
||||
);
|
||||
};
|
||||
|
||||
express.Router = (opts) => {
|
||||
const path = currentPath;
|
||||
const file = currentFile;
|
||||
const router = Router(opts);
|
||||
|
||||
for (const method of methods) {
|
||||
router[method] = registerPath.bind(null, file, method, path);
|
||||
}
|
||||
|
||||
return router;
|
||||
RouteUtility.route = (opts) => {
|
||||
const func = function () {
|
||||
return opts;
|
||||
};
|
||||
func.prototype.OPTS_MARKER = true;
|
||||
return func;
|
||||
};
|
||||
|
||||
module.exports = function getRouteDescriptions() {
|
||||
const root = path.join(__dirname, "..", "..", "dist", "api", "routes", "/");
|
||||
traverseDirectory({ dirname: root, recursive: true }, (file) => {
|
||||
currentFile = file;
|
||||
let path = file.replace(root.slice(0, -1), "");
|
||||
path = path.split(".").slice(0, -1).join("."); // trancate .js/.ts file extension of path
|
||||
path = path.replaceAll("#", ":").replaceAll("\\", "/"); // replace # with : for path parameters and windows paths with slashes
|
||||
if (path.endsWith("/index")) path = path.slice(0, "/index".length * -1); // delete index from path
|
||||
currentPath = path;
|
||||
|
||||
currentPath = file.replace(root.slice(0, -1), "");
|
||||
currentPath = currentPath.split(".").slice(0, -1).join("."); // trancate .js/.ts file extension of path
|
||||
currentPath = currentPath.replaceAll("#", ":").replaceAll("\\", "/"); // replace # with : for path parameters and windows paths with slashes
|
||||
if (currentPath.endsWith("/index"))
|
||||
currentPath = currentPath.slice(0, "/index".length * -1); // delete index from path
|
||||
|
||||
try {
|
||||
require(file);
|
||||
} catch (error) {
|
||||
console.error("error loading file " + file, error);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
|
||||
return routes;
|
||||
};
|
||||
|
@ -16,78 +16,114 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Application,
|
||||
generateToken,
|
||||
User,
|
||||
BotModifySchema,
|
||||
handleFile,
|
||||
DiscordApiErrors,
|
||||
User,
|
||||
generateToken,
|
||||
handleFile,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { verifyToken } from "node-2fa";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["owner"],
|
||||
});
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {
|
||||
body: "TokenOnlyResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["owner"],
|
||||
});
|
||||
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
|
||||
const user = await User.register({
|
||||
username: app.name,
|
||||
password: undefined,
|
||||
id: app.id,
|
||||
req,
|
||||
});
|
||||
const user = await User.register({
|
||||
username: app.name,
|
||||
password: undefined,
|
||||
id: app.id,
|
||||
req,
|
||||
});
|
||||
|
||||
user.id = app.id;
|
||||
user.premium_since = new Date();
|
||||
user.bot = true;
|
||||
user.id = app.id;
|
||||
user.premium_since = new Date();
|
||||
user.bot = true;
|
||||
|
||||
await user.save();
|
||||
await user.save();
|
||||
|
||||
// flags is NaN here?
|
||||
app.assign({ bot: user, flags: app.flags || 0 });
|
||||
// flags is NaN here?
|
||||
app.assign({ bot: user, flags: app.flags || 0 });
|
||||
|
||||
await app.save();
|
||||
await app.save();
|
||||
|
||||
res.send({
|
||||
token: await generateToken(user.id),
|
||||
}).status(204);
|
||||
});
|
||||
res.send({
|
||||
token: await generateToken(user.id),
|
||||
}).status(204);
|
||||
},
|
||||
);
|
||||
|
||||
router.post("/reset", route({}), async (req: Request, res: Response) => {
|
||||
const bot = await User.findOneOrFail({ where: { id: req.params.id } });
|
||||
const owner = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
router.post(
|
||||
"/reset",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "TokenResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const bot = await User.findOneOrFail({ where: { id: req.params.id } });
|
||||
const owner = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
|
||||
if (owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
if (owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
|
||||
if (
|
||||
owner.totp_secret &&
|
||||
(!req.body.code || verifyToken(owner.totp_secret, req.body.code))
|
||||
)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
if (
|
||||
owner.totp_secret &&
|
||||
(!req.body.code || verifyToken(owner.totp_secret, req.body.code))
|
||||
)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
|
||||
bot.data = { hash: undefined, valid_tokens_since: new Date() };
|
||||
bot.data = { hash: undefined, valid_tokens_since: new Date() };
|
||||
|
||||
await bot.save();
|
||||
await bot.save();
|
||||
|
||||
const token = await generateToken(bot.id);
|
||||
const token = await generateToken(bot.id);
|
||||
|
||||
res.json({ token }).status(200);
|
||||
});
|
||||
res.json({ token }).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "BotModifySchema" }),
|
||||
route({
|
||||
requestBody: "BotModifySchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Application",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as BotModifySchema;
|
||||
if (!body.avatar?.trim()) delete body.avatar;
|
||||
|
@ -16,15 +16,25 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
//const { exclude_consumed } = req.query;
|
||||
res.status(200).send([]);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "ApplicationEntitlementsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
// TODO:
|
||||
//const { exclude_consumed } = req.query;
|
||||
res.status(200).send([]);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,32 +16,55 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Application,
|
||||
DiscordApiErrors,
|
||||
ApplicationModifySchema,
|
||||
DiscordApiErrors,
|
||||
} from "@spacebar/util";
|
||||
import { verifyToken } from "node-2fa";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { verifyToken } from "node-2fa";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["owner", "bot"],
|
||||
});
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Application",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["owner", "bot"],
|
||||
});
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
|
||||
return res.json(app);
|
||||
});
|
||||
return res.json(app);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "ApplicationModifySchema" }),
|
||||
route({
|
||||
requestBody: "ApplicationModifySchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Application",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as ApplicationModifySchema;
|
||||
|
||||
@ -73,23 +96,35 @@ router.patch(
|
||||
},
|
||||
);
|
||||
|
||||
router.post("/delete", route({}), async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["bot", "owner"],
|
||||
});
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
router.post(
|
||||
"/delete",
|
||||
route({
|
||||
responses: {
|
||||
200: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const app = await Application.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["bot", "owner"],
|
||||
});
|
||||
if (app.owner.id != req.user_id)
|
||||
throw DiscordApiErrors.ACTION_NOT_AUTHORIZED_ON_APPLICATION;
|
||||
|
||||
if (
|
||||
app.owner.totp_secret &&
|
||||
(!req.body.code || verifyToken(app.owner.totp_secret, req.body.code))
|
||||
)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
if (
|
||||
app.owner.totp_secret &&
|
||||
(!req.body.code ||
|
||||
verifyToken(app.owner.totp_secret, req.body.code))
|
||||
)
|
||||
throw new HTTPError(req.t("auth:login.INVALID_TOTP_CODE"), 60008);
|
||||
|
||||
await Application.delete({ id: app.id });
|
||||
await Application.delete({ id: app.id });
|
||||
|
||||
res.send().status(200);
|
||||
});
|
||||
res.send().status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,13 +16,23 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
res.json([]).status(200);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "ApplicationSkusResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
res.json([]).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,14 +16,24 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.send([]).status(200);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "ApplicationDetectableResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.send([]).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,28 +16,45 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Application,
|
||||
ApplicationCreateSchema,
|
||||
trimSpecial,
|
||||
User,
|
||||
trimSpecial,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const results = await Application.find({
|
||||
where: { owner: { id: req.user_id } },
|
||||
relations: ["owner", "bot"],
|
||||
});
|
||||
res.json(results).status(200);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIApplicationArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const results = await Application.find({
|
||||
where: { owner: { id: req.user_id } },
|
||||
relations: ["owner", "bot"],
|
||||
});
|
||||
res.json(results).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "ApplicationCreateSchema" }),
|
||||
route({
|
||||
requestBody: "ApplicationCreateSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Application",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as ApplicationCreateSchema;
|
||||
const user = await User.findOneOrFail({ where: { id: req.user_id } });
|
||||
|
@ -30,7 +30,18 @@ const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "ForgotPasswordSchema" }),
|
||||
route({
|
||||
requestBody: "ForgotPasswordSchema",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorOrCaptchaResponse",
|
||||
},
|
||||
500: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { login, captcha_key } = req.body as ForgotPasswordSchema;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route, random } from "@spacebar/api";
|
||||
import { random, route } from "@spacebar/api";
|
||||
import { Config, ValidRegistrationToken } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
@ -25,7 +25,22 @@ export default router;
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ right: "OPERATOR" }),
|
||||
route({
|
||||
query: {
|
||||
count: {
|
||||
type: "number",
|
||||
description:
|
||||
"The number of registration tokens to generate. Defaults to 1.",
|
||||
},
|
||||
length: {
|
||||
type: "number",
|
||||
description:
|
||||
"The length of each registration token. Defaults to 255.",
|
||||
},
|
||||
},
|
||||
right: "OPERATOR",
|
||||
responses: { 200: { body: "GenerateRegistrationTokensResponse" } },
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const count = req.query.count ? parseInt(req.query.count as string) : 1;
|
||||
const length = req.query.length
|
||||
|
@ -16,20 +16,29 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { getIpAdress, IPAnalysis } from "@spacebar/api";
|
||||
import { IPAnalysis, getIpAdress, route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
|
||||
const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
|
||||
res.json({
|
||||
consent_required: false,
|
||||
country_code: country_code,
|
||||
promotional_email_opt_in: { required: true, pre_checked: false },
|
||||
});
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "LocationMetadataResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
//Note: It's most likely related to legal. At the moment Discord hasn't finished this too
|
||||
const country_code = (await IPAnalysis(getIpAdress(req))).country_code;
|
||||
res.json({
|
||||
consent_required: false,
|
||||
country_code: country_code,
|
||||
promotional_email_opt_in: { required: true, pre_checked: false },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -36,7 +36,17 @@ export default router;
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "LoginSchema" }),
|
||||
route({
|
||||
requestBody: "LoginSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "LoginResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorOrCaptchaResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { login, password, captcha_key, undelete } =
|
||||
req.body as LoginSchema;
|
||||
|
@ -22,14 +22,25 @@ import { Request, Response, Router } from "express";
|
||||
const router: Router = Router();
|
||||
export default router;
|
||||
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
if (req.body.provider != null || req.body.voip_provider != null) {
|
||||
console.log(`[LOGOUT]: provider or voip provider not null!`, req.body);
|
||||
} else {
|
||||
delete req.body.provider;
|
||||
delete req.body.voip_provider;
|
||||
if (Object.keys(req.body).length != 0)
|
||||
console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
|
||||
}
|
||||
res.status(204).send();
|
||||
});
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.body.provider != null || req.body.voip_provider != null) {
|
||||
console.log(
|
||||
`[LOGOUT]: provider or voip provider not null!`,
|
||||
req.body,
|
||||
);
|
||||
} else {
|
||||
delete req.body.provider;
|
||||
delete req.body.voip_provider;
|
||||
if (Object.keys(req.body).length != 0)
|
||||
console.log(`[LOGOUT]: Extra fields sent in logout!`, req.body);
|
||||
}
|
||||
res.status(204).send();
|
||||
},
|
||||
);
|
||||
|
@ -16,16 +16,26 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { BackupCode, generateToken, User, TotpSchema } from "@spacebar/util";
|
||||
import { verifyToken } from "node-2fa";
|
||||
import { BackupCode, TotpSchema, User, generateToken } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { verifyToken } from "node-2fa";
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "TotpSchema" }),
|
||||
route({
|
||||
requestBody: "TotpSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "TokenResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// const { code, ticket, gift_code_sku_id, login_source } =
|
||||
const { code, ticket } = req.body as TotpSchema;
|
||||
|
@ -41,7 +41,13 @@ function toArrayBuffer(buf: Buffer) {
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "WebAuthnTotpSchema" }),
|
||||
route({
|
||||
requestBody: "WebAuthnTotpSchema",
|
||||
responses: {
|
||||
200: { body: "TokenResponse" },
|
||||
400: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
if (!WebAuthn.fido2) {
|
||||
// TODO: I did this for typescript and I can't use !
|
||||
|
@ -16,25 +16,25 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import {
|
||||
Config,
|
||||
generateToken,
|
||||
Invite,
|
||||
FieldErrors,
|
||||
User,
|
||||
adjustEmail,
|
||||
RegisterSchema,
|
||||
ValidRegistrationToken,
|
||||
} from "@spacebar/util";
|
||||
import {
|
||||
route,
|
||||
getIpAdress,
|
||||
IPAnalysis,
|
||||
getIpAdress,
|
||||
isProxy,
|
||||
route,
|
||||
verifyCaptcha,
|
||||
} from "@spacebar/api";
|
||||
import {
|
||||
Config,
|
||||
FieldErrors,
|
||||
Invite,
|
||||
RegisterSchema,
|
||||
User,
|
||||
ValidRegistrationToken,
|
||||
adjustEmail,
|
||||
generateToken,
|
||||
} from "@spacebar/util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { MoreThan } from "typeorm";
|
||||
|
||||
@ -42,7 +42,13 @@ const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "RegisterSchema" }),
|
||||
route({
|
||||
requestBody: "RegisterSchema",
|
||||
responses: {
|
||||
200: { body: "TokenOnlyResponse" },
|
||||
400: { body: "APIErrorOrCaptchaResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as RegisterSchema;
|
||||
const { register, security, limits } = Config.get();
|
||||
|
@ -31,9 +31,20 @@ import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// TODO: the response interface also returns settings, but this route doesn't actually return that.
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "PasswordResetSchema" }),
|
||||
route({
|
||||
requestBody: "PasswordResetSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "TokenOnlyResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorOrCaptchaResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { password, token } = req.body as PasswordResetSchema;
|
||||
|
||||
|
@ -37,9 +37,20 @@ async function getToken(user: User) {
|
||||
return { token };
|
||||
}
|
||||
|
||||
// TODO: the response interface also returns settings, but this route doesn't actually return that.
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "VerifyEmailSchema" }),
|
||||
route({
|
||||
requestBody: "VerifyEmailSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "TokenResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorOrCaptchaResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { captcha_key, token } = req.body;
|
||||
|
||||
|
@ -24,7 +24,18 @@ const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ right: "RESEND_VERIFICATION_EMAIL" }),
|
||||
route({
|
||||
right: "RESEND_VERIFICATION_EMAIL",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
500: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
|
@ -16,15 +16,21 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { FieldErrors, User, BackupCodesChallengeSchema } from "@spacebar/util";
|
||||
import { BackupCodesChallengeSchema, FieldErrors, User } from "@spacebar/util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "BackupCodesChallengeSchema" }),
|
||||
route({
|
||||
requestBody: "BackupCodesChallengeSchema",
|
||||
responses: {
|
||||
200: { body: "BackupCodesChallengeResponse" },
|
||||
400: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { password } = req.body as BackupCodesChallengeSchema;
|
||||
|
||||
|
@ -16,18 +16,18 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelDeleteEvent,
|
||||
ChannelModifySchema,
|
||||
ChannelType,
|
||||
ChannelUpdateEvent,
|
||||
emitEvent,
|
||||
Recipient,
|
||||
emitEvent,
|
||||
handleFile,
|
||||
ChannelModifySchema,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
|
||||
const router: Router = Router();
|
||||
// TODO: delete channel
|
||||
@ -35,7 +35,15 @@ const router: Router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "VIEW_CHANNEL" }),
|
||||
route({
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Channel",
|
||||
},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
|
||||
@ -49,7 +57,15 @@ router.get(
|
||||
|
||||
router.delete(
|
||||
"/",
|
||||
route({ permission: "MANAGE_CHANNELS" }),
|
||||
route({
|
||||
permission: "MANAGE_CHANNELS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Channel",
|
||||
},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
|
||||
@ -90,7 +106,19 @@ router.delete(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
|
||||
route({
|
||||
requestBody: "ChannelModifySchema",
|
||||
permission: "MANAGE_CHANNELS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Channel",
|
||||
},
|
||||
404: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const payload = req.body as ChannelModifySchema;
|
||||
const { channel_id } = req.params;
|
||||
|
@ -16,29 +16,37 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { random } from "@spacebar/api";
|
||||
import { random, route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
Guild,
|
||||
Invite,
|
||||
InviteCreateEvent,
|
||||
emitEvent,
|
||||
User,
|
||||
Guild,
|
||||
PublicInviteRelation,
|
||||
User,
|
||||
emitEvent,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import { isTextChannel } from "./messages";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
body: "InviteCreateSchema",
|
||||
requestBody: "InviteCreateSchema",
|
||||
permission: "CREATE_INSTANT_INVITE",
|
||||
right: "CREATE_INVITES",
|
||||
responses: {
|
||||
201: {
|
||||
body: "Invite",
|
||||
},
|
||||
404: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { user_id } = req;
|
||||
@ -84,7 +92,15 @@ router.post(
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "MANAGE_CHANNELS" }),
|
||||
route({
|
||||
permission: "MANAGE_CHANNELS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIInviteArray",
|
||||
},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
|
@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
emitEvent,
|
||||
getPermission,
|
||||
@ -23,7 +24,6 @@ import {
|
||||
ReadState,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -33,7 +33,13 @@ const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "MessageAcknowledgeSchema" }),
|
||||
route({
|
||||
requestBody: "MessageAcknowledgeSchema",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, message_id } = req.params;
|
||||
|
||||
|
@ -16,14 +16,21 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ permission: "MANAGE_MESSAGES" }),
|
||||
route({
|
||||
permission: "MANAGE_MESSAGES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Message",
|
||||
},
|
||||
},
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
// TODO:
|
||||
res.json({
|
||||
|
@ -19,24 +19,23 @@
|
||||
import {
|
||||
Attachment,
|
||||
Channel,
|
||||
emitEvent,
|
||||
SpacebarApiErrors,
|
||||
getPermission,
|
||||
getRights,
|
||||
Message,
|
||||
MessageCreateEvent,
|
||||
MessageCreateSchema,
|
||||
MessageDeleteEvent,
|
||||
MessageEditSchema,
|
||||
MessageUpdateEvent,
|
||||
Snowflake,
|
||||
SpacebarApiErrors,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
uploadFile,
|
||||
MessageCreateSchema,
|
||||
MessageEditSchema,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import multer from "multer";
|
||||
import { route } from "@spacebar/api";
|
||||
import { handleMessage, postHandleMessage } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import multer from "multer";
|
||||
import { handleMessage, postHandleMessage, route } from "../../../../../util";
|
||||
|
||||
const router = Router();
|
||||
// TODO: message content/embed string length limit
|
||||
@ -53,9 +52,19 @@ const messageUpload = multer({
|
||||
router.patch(
|
||||
"/",
|
||||
route({
|
||||
body: "MessageEditSchema",
|
||||
requestBody: "MessageEditSchema",
|
||||
permission: "SEND_MESSAGES",
|
||||
right: "SEND_MESSAGES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Message",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
@ -143,9 +152,19 @@ router.put(
|
||||
next();
|
||||
},
|
||||
route({
|
||||
body: "MessageCreateSchema",
|
||||
requestBody: "MessageCreateSchema",
|
||||
permission: "SEND_MESSAGES",
|
||||
right: "SEND_BACKDATED_EVENTS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Message",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, message_id } = req.params;
|
||||
@ -230,7 +249,19 @@ router.put(
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "VIEW_CHANNEL" }),
|
||||
route({
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Message",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
||||
@ -252,38 +283,54 @@ router.get(
|
||||
},
|
||||
);
|
||||
|
||||
router.delete("/", route({}), async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
||||
const channel = await Channel.findOneOrFail({ where: { id: channel_id } });
|
||||
const message = await Message.findOneOrFail({ where: { id: message_id } });
|
||||
|
||||
const rights = await getRights(req.user_id);
|
||||
|
||||
if (message.author_id !== req.user_id) {
|
||||
if (!rights.has("MANAGE_MESSAGES")) {
|
||||
const permission = await getPermission(
|
||||
req.user_id,
|
||||
channel.guild_id,
|
||||
channel_id,
|
||||
);
|
||||
permission.hasThrow("MANAGE_MESSAGES");
|
||||
}
|
||||
} else rights.hasThrow("SELF_DELETE_MESSAGES");
|
||||
|
||||
await Message.delete({ id: message_id });
|
||||
|
||||
await emitEvent({
|
||||
event: "MESSAGE_DELETE",
|
||||
channel_id,
|
||||
data: {
|
||||
id: message_id,
|
||||
channel_id,
|
||||
guild_id: channel.guild_id,
|
||||
router.delete(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
},
|
||||
} as MessageDeleteEvent);
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
||||
res.sendStatus(204);
|
||||
});
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
const message = await Message.findOneOrFail({
|
||||
where: { id: message_id },
|
||||
});
|
||||
|
||||
const rights = await getRights(req.user_id);
|
||||
|
||||
if (message.author_id !== req.user_id) {
|
||||
if (!rights.has("MANAGE_MESSAGES")) {
|
||||
const permission = await getPermission(
|
||||
req.user_id,
|
||||
channel.guild_id,
|
||||
channel_id,
|
||||
);
|
||||
permission.hasThrow("MANAGE_MESSAGES");
|
||||
}
|
||||
} else rights.hasThrow("SELF_DELETE_MESSAGES");
|
||||
|
||||
await Message.delete({ id: message_id });
|
||||
|
||||
await emitEvent({
|
||||
event: "MESSAGE_DELETE",
|
||||
channel_id,
|
||||
data: {
|
||||
id: message_id,
|
||||
channel_id,
|
||||
guild_id: channel.guild_id,
|
||||
},
|
||||
} as MessageDeleteEvent);
|
||||
|
||||
res.sendStatus(204);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
emitEvent,
|
||||
@ -32,8 +33,7 @@ import {
|
||||
PublicUserProjection,
|
||||
User,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { In } from "typeorm";
|
||||
|
||||
@ -57,7 +57,17 @@ function getEmoji(emoji: string): PartialEmoji {
|
||||
|
||||
router.delete(
|
||||
"/",
|
||||
route({ permission: "MANAGE_MESSAGES" }),
|
||||
route({
|
||||
permission: "MANAGE_MESSAGES",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
||||
@ -83,7 +93,17 @@ router.delete(
|
||||
|
||||
router.delete(
|
||||
"/:emoji",
|
||||
route({ permission: "MANAGE_MESSAGES" }),
|
||||
route({
|
||||
permission: "MANAGE_MESSAGES",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
const emoji = getEmoji(req.params.emoji);
|
||||
@ -120,7 +140,19 @@ router.delete(
|
||||
|
||||
router.get(
|
||||
"/:emoji",
|
||||
route({ permission: "VIEW_CHANNEL" }),
|
||||
route({
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
200: {
|
||||
body: "PublicUser",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id } = req.params;
|
||||
const emoji = getEmoji(req.params.emoji);
|
||||
@ -148,7 +180,18 @@ router.get(
|
||||
|
||||
router.put(
|
||||
"/:emoji/:user_id",
|
||||
route({ permission: "READ_MESSAGE_HISTORY", right: "SELF_ADD_REACTIONS" }),
|
||||
route({
|
||||
permission: "READ_MESSAGE_HISTORY",
|
||||
right: "SELF_ADD_REACTIONS",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { message_id, channel_id, user_id } = req.params;
|
||||
if (user_id !== "@me") throw new HTTPError("Invalid user");
|
||||
@ -219,7 +262,16 @@ router.put(
|
||||
|
||||
router.delete(
|
||||
"/:emoji/:user_id",
|
||||
route({}),
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
let { user_id } = req.params;
|
||||
const { message_id, channel_id } = req.params;
|
||||
|
@ -16,18 +16,18 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
Config,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
MessageDeleteBulkEvent,
|
||||
Message,
|
||||
MessageDeleteBulkEvent,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -38,7 +38,17 @@ export default router;
|
||||
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "BulkDeleteSchema" }),
|
||||
route({
|
||||
requestBody: "BulkDeleteSchema",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
|
@ -16,165 +16,171 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { handleMessage, postHandleMessage, route } from "@spacebar/api";
|
||||
import {
|
||||
Attachment,
|
||||
Channel,
|
||||
ChannelType,
|
||||
Config,
|
||||
DmChannelDTO,
|
||||
emitEvent,
|
||||
FieldErrors,
|
||||
getPermission,
|
||||
Member,
|
||||
Message,
|
||||
MessageCreateEvent,
|
||||
Snowflake,
|
||||
uploadFile,
|
||||
Member,
|
||||
MessageCreateSchema,
|
||||
Reaction,
|
||||
ReadState,
|
||||
Rights,
|
||||
Reaction,
|
||||
Snowflake,
|
||||
User,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
isTextChannel,
|
||||
uploadFile,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { handleMessage, postHandleMessage, route } from "@spacebar/api";
|
||||
import multer from "multer";
|
||||
import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
|
||||
import { URL } from "url";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
export default router;
|
||||
|
||||
export function isTextChannel(type: ChannelType): boolean {
|
||||
switch (type) {
|
||||
case ChannelType.GUILD_STORE:
|
||||
case ChannelType.GUILD_VOICE:
|
||||
case ChannelType.GUILD_STAGE_VOICE:
|
||||
case ChannelType.GUILD_CATEGORY:
|
||||
case ChannelType.GUILD_FORUM:
|
||||
case ChannelType.DIRECTORY:
|
||||
throw new HTTPError("not a text channel", 400);
|
||||
case ChannelType.DM:
|
||||
case ChannelType.GROUP_DM:
|
||||
case ChannelType.GUILD_NEWS:
|
||||
case ChannelType.GUILD_NEWS_THREAD:
|
||||
case ChannelType.GUILD_PUBLIC_THREAD:
|
||||
case ChannelType.GUILD_PRIVATE_THREAD:
|
||||
case ChannelType.GUILD_TEXT:
|
||||
case ChannelType.ENCRYPTED:
|
||||
case ChannelType.ENCRYPTED_THREAD:
|
||||
return true;
|
||||
default:
|
||||
throw new HTTPError("unimplemented", 400);
|
||||
}
|
||||
}
|
||||
|
||||
// https://discord.com/developers/docs/resources/channel#create-message
|
||||
// get messages
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const channel_id = req.params.channel_id;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
query: {
|
||||
around: {
|
||||
type: "string",
|
||||
},
|
||||
before: {
|
||||
type: "string",
|
||||
},
|
||||
after: {
|
||||
type: "string",
|
||||
},
|
||||
limit: {
|
||||
type: "number",
|
||||
description:
|
||||
"max number of messages to return (1-100). defaults to 50",
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIMessageArray",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const channel_id = req.params.channel_id;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
});
|
||||
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||
|
||||
isTextChannel(channel.type);
|
||||
const around = req.query.around ? `${req.query.around}` : undefined;
|
||||
const before = req.query.before ? `${req.query.before}` : undefined;
|
||||
const after = req.query.after ? `${req.query.after}` : undefined;
|
||||
const limit = Number(req.query.limit) || 50;
|
||||
if (limit < 1 || limit > 100)
|
||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||
isTextChannel(channel.type);
|
||||
const around = req.query.around ? `${req.query.around}` : undefined;
|
||||
const before = req.query.before ? `${req.query.before}` : undefined;
|
||||
const after = req.query.after ? `${req.query.after}` : undefined;
|
||||
const limit = Number(req.query.limit) || 50;
|
||||
if (limit < 1 || limit > 100)
|
||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||
|
||||
const halfLimit = Math.floor(limit / 2);
|
||||
const halfLimit = Math.floor(limit / 2);
|
||||
|
||||
const permissions = await getPermission(
|
||||
req.user_id,
|
||||
channel.guild_id,
|
||||
channel_id,
|
||||
);
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
||||
const permissions = await getPermission(
|
||||
req.user_id,
|
||||
channel.guild_id,
|
||||
channel_id,
|
||||
);
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
||||
|
||||
const query: FindManyOptions<Message> & {
|
||||
where: { id?: FindOperator<string> | FindOperator<string>[] };
|
||||
} = {
|
||||
order: { timestamp: "DESC" },
|
||||
take: limit,
|
||||
where: { channel_id },
|
||||
relations: [
|
||||
"author",
|
||||
"webhook",
|
||||
"application",
|
||||
"mentions",
|
||||
"mention_roles",
|
||||
"mention_channels",
|
||||
"sticker_items",
|
||||
"attachments",
|
||||
],
|
||||
};
|
||||
const query: FindManyOptions<Message> & {
|
||||
where: { id?: FindOperator<string> | FindOperator<string>[] };
|
||||
} = {
|
||||
order: { timestamp: "DESC" },
|
||||
take: limit,
|
||||
where: { channel_id },
|
||||
relations: [
|
||||
"author",
|
||||
"webhook",
|
||||
"application",
|
||||
"mentions",
|
||||
"mention_roles",
|
||||
"mention_channels",
|
||||
"sticker_items",
|
||||
"attachments",
|
||||
],
|
||||
};
|
||||
|
||||
if (after) {
|
||||
if (BigInt(after) > BigInt(Snowflake.generate()))
|
||||
return res.status(422);
|
||||
query.where.id = MoreThan(after);
|
||||
} else if (before) {
|
||||
if (BigInt(before) < BigInt(req.params.channel_id))
|
||||
return res.status(422);
|
||||
query.where.id = LessThan(before);
|
||||
} else if (around) {
|
||||
query.where.id = [
|
||||
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
|
||||
LessThan((BigInt(around) + BigInt(halfLimit)).toString()),
|
||||
];
|
||||
if (after) {
|
||||
if (BigInt(after) > BigInt(Snowflake.generate()))
|
||||
return res.status(422);
|
||||
query.where.id = MoreThan(after);
|
||||
} else if (before) {
|
||||
if (BigInt(before) < BigInt(req.params.channel_id))
|
||||
return res.status(422);
|
||||
query.where.id = LessThan(before);
|
||||
} else if (around) {
|
||||
query.where.id = [
|
||||
MoreThan((BigInt(around) - BigInt(halfLimit)).toString()),
|
||||
LessThan((BigInt(around) + BigInt(halfLimit)).toString()),
|
||||
];
|
||||
|
||||
return res.json([]); // TODO: fix around
|
||||
}
|
||||
return res.json([]); // TODO: fix around
|
||||
}
|
||||
|
||||
const messages = await Message.find(query);
|
||||
const endpoint = Config.get().cdn.endpointPublic;
|
||||
const messages = await Message.find(query);
|
||||
const endpoint = Config.get().cdn.endpointPublic;
|
||||
|
||||
return res.json(
|
||||
messages.map((x: Partial<Message>) => {
|
||||
(x.reactions || []).forEach((y: Partial<Reaction>) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
|
||||
delete y.user_ids;
|
||||
});
|
||||
if (!x.author)
|
||||
x.author = User.create({
|
||||
id: "4",
|
||||
discriminator: "0000",
|
||||
username: "Spacebar Ghost",
|
||||
public_flags: 0,
|
||||
return res.json(
|
||||
messages.map((x: Partial<Message>) => {
|
||||
(x.reactions || []).forEach((y: Partial<Reaction>) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
if ((y.user_ids || []).includes(req.user_id)) y.me = true;
|
||||
delete y.user_ids;
|
||||
});
|
||||
if (!x.author)
|
||||
x.author = User.create({
|
||||
id: "4",
|
||||
discriminator: "0000",
|
||||
username: "Spacebar Ghost",
|
||||
public_flags: 0,
|
||||
});
|
||||
x.attachments?.forEach((y: Attachment) => {
|
||||
// dynamically set attachment proxy_url in case the endpoint changed
|
||||
const uri = y.proxy_url.startsWith("http")
|
||||
? y.proxy_url
|
||||
: `https://example.org${y.proxy_url}`;
|
||||
y.proxy_url = `${endpoint == null ? "" : endpoint}${
|
||||
new URL(uri).pathname
|
||||
}`;
|
||||
});
|
||||
x.attachments?.forEach((y: Attachment) => {
|
||||
// dynamically set attachment proxy_url in case the endpoint changed
|
||||
const uri = y.proxy_url.startsWith("http")
|
||||
? y.proxy_url
|
||||
: `https://example.org${y.proxy_url}`;
|
||||
y.proxy_url = `${endpoint == null ? "" : endpoint}${
|
||||
new URL(uri).pathname
|
||||
}`;
|
||||
});
|
||||
|
||||
/**
|
||||
/**
|
||||
Some clients ( discord.js ) only check if a property exists within the response,
|
||||
which causes errors when, say, the `application` property is `null`.
|
||||
**/
|
||||
|
||||
// for (var curr in x) {
|
||||
// if (x[curr] === null)
|
||||
// delete x[curr];
|
||||
// }
|
||||
// for (var curr in x) {
|
||||
// if (x[curr] === null)
|
||||
// delete x[curr];
|
||||
// }
|
||||
|
||||
return x;
|
||||
}),
|
||||
);
|
||||
});
|
||||
return x;
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: config max upload size
|
||||
const messageUpload = multer({
|
||||
@ -205,9 +211,19 @@ router.post(
|
||||
next();
|
||||
},
|
||||
route({
|
||||
body: "MessageCreateSchema",
|
||||
requestBody: "MessageCreateSchema",
|
||||
permission: "SEND_MESSAGES",
|
||||
right: "SEND_MESSAGES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Message",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
@ -366,3 +382,5 @@ router.post(
|
||||
return res.json(message);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -19,13 +19,13 @@
|
||||
import {
|
||||
Channel,
|
||||
ChannelPermissionOverwrite,
|
||||
ChannelPermissionOverwriteSchema,
|
||||
ChannelUpdateEvent,
|
||||
emitEvent,
|
||||
Member,
|
||||
Role,
|
||||
ChannelPermissionOverwriteSchema,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
@ -36,8 +36,14 @@ const router: Router = Router();
|
||||
router.put(
|
||||
"/:overwrite_id",
|
||||
route({
|
||||
body: "ChannelPermissionOverwriteSchema",
|
||||
requestBody: "ChannelPermissionOverwriteSchema",
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
204: {},
|
||||
404: {},
|
||||
501: {},
|
||||
400: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, overwrite_id } = req.params;
|
||||
@ -92,7 +98,7 @@ router.put(
|
||||
// TODO: check permission hierarchy
|
||||
router.delete(
|
||||
"/:overwrite_id",
|
||||
route({ permission: "MANAGE_ROLES" }),
|
||||
route({ permission: "MANAGE_ROLES", responses: { 204: {}, 404: {} } }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, overwrite_id } = req.params;
|
||||
|
||||
|
@ -16,23 +16,33 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelPinsUpdateEvent,
|
||||
Config,
|
||||
DiscordApiErrors,
|
||||
emitEvent,
|
||||
Message,
|
||||
MessageUpdateEvent,
|
||||
DiscordApiErrors,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.put(
|
||||
"/:message_id",
|
||||
route({ permission: "VIEW_CHANNEL" }),
|
||||
route({
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {},
|
||||
404: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, message_id } = req.params;
|
||||
|
||||
@ -74,7 +84,17 @@ router.put(
|
||||
|
||||
router.delete(
|
||||
"/:message_id",
|
||||
route({ permission: "VIEW_CHANNEL" }),
|
||||
route({
|
||||
permission: "VIEW_CHANNEL",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {},
|
||||
404: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, message_id } = req.params;
|
||||
|
||||
@ -114,7 +134,17 @@ router.delete(
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: ["READ_MESSAGE_HISTORY"] }),
|
||||
route({
|
||||
permission: ["READ_MESSAGE_HISTORY"],
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIMessageArray",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
|
||||
|
@ -16,20 +16,20 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { isTextChannel } from "./messages";
|
||||
import { FindManyOptions, Between, Not, FindOperator } from "typeorm";
|
||||
import {
|
||||
Channel,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
Message,
|
||||
MessageDeleteBulkEvent,
|
||||
PurgeSchema,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { Between, FindManyOptions, FindOperator, Not } from "typeorm";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -42,6 +42,14 @@ router.post(
|
||||
"/",
|
||||
route({
|
||||
/*body: "PurgeSchema",*/
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
|
@ -16,7 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelRecipientAddEvent,
|
||||
@ -28,80 +28,98 @@ import {
|
||||
Recipient,
|
||||
User,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.put("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
const { channel_id, user_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
relations: ["recipients"],
|
||||
});
|
||||
router.put(
|
||||
"/:user_id",
|
||||
route({
|
||||
responses: {
|
||||
201: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, user_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
relations: ["recipients"],
|
||||
});
|
||||
|
||||
if (channel.type !== ChannelType.GROUP_DM) {
|
||||
const recipients = [
|
||||
...(channel.recipients?.map((r) => r.user_id) || []),
|
||||
user_id,
|
||||
].unique();
|
||||
if (channel.type !== ChannelType.GROUP_DM) {
|
||||
const recipients = [
|
||||
...(channel.recipients?.map((r) => r.user_id) || []),
|
||||
user_id,
|
||||
].unique();
|
||||
|
||||
const new_channel = await Channel.createDMChannel(
|
||||
recipients,
|
||||
req.user_id,
|
||||
);
|
||||
return res.status(201).json(new_channel);
|
||||
} else {
|
||||
if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
const new_channel = await Channel.createDMChannel(
|
||||
recipients,
|
||||
req.user_id,
|
||||
);
|
||||
return res.status(201).json(new_channel);
|
||||
} else {
|
||||
if (channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
channel.recipients?.push(
|
||||
Recipient.create({ channel_id: channel_id, user_id: user_id }),
|
||||
);
|
||||
await channel.save();
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_CREATE",
|
||||
data: await DmChannelDTO.from(channel, [user_id]),
|
||||
user_id: user_id,
|
||||
});
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_RECIPIENT_ADD",
|
||||
data: {
|
||||
channel_id: channel_id,
|
||||
user: await User.findOneOrFail({
|
||||
where: { id: user_id },
|
||||
select: PublicUserProjection,
|
||||
}),
|
||||
},
|
||||
channel_id: channel_id,
|
||||
} as ChannelRecipientAddEvent);
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:user_id",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id, user_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
relations: ["recipients"],
|
||||
});
|
||||
if (
|
||||
!(
|
||||
channel.type === ChannelType.GROUP_DM &&
|
||||
(channel.owner_id === req.user_id || user_id === req.user_id)
|
||||
)
|
||||
)
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS;
|
||||
|
||||
if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
channel.recipients?.push(
|
||||
Recipient.create({ channel_id: channel_id, user_id: user_id }),
|
||||
);
|
||||
await channel.save();
|
||||
await Channel.removeRecipientFromChannel(channel, user_id);
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_CREATE",
|
||||
data: await DmChannelDTO.from(channel, [user_id]),
|
||||
user_id: user_id,
|
||||
});
|
||||
|
||||
await emitEvent({
|
||||
event: "CHANNEL_RECIPIENT_ADD",
|
||||
data: {
|
||||
channel_id: channel_id,
|
||||
user: await User.findOneOrFail({
|
||||
where: { id: user_id },
|
||||
select: PublicUserProjection,
|
||||
}),
|
||||
},
|
||||
channel_id: channel_id,
|
||||
} as ChannelRecipientAddEvent);
|
||||
return res.sendStatus(204);
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/:user_id", route({}), async (req: Request, res: Response) => {
|
||||
const { channel_id, user_id } = req.params;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
where: { id: channel_id },
|
||||
relations: ["recipients"],
|
||||
});
|
||||
if (
|
||||
!(
|
||||
channel.type === ChannelType.GROUP_DM &&
|
||||
(channel.owner_id === req.user_id || user_id === req.user_id)
|
||||
)
|
||||
)
|
||||
throw DiscordApiErrors.MISSING_PERMISSIONS;
|
||||
|
||||
if (!channel.recipients?.map((r) => r.user_id).includes(user_id)) {
|
||||
throw DiscordApiErrors.INVALID_RECIPIENT; //TODO is this the right error?
|
||||
}
|
||||
|
||||
await Channel.removeRecipientFromChannel(channel, user_id);
|
||||
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,15 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Channel, emitEvent, Member, TypingStartEvent } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { Channel, emitEvent, Member, TypingStartEvent } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ permission: "SEND_MESSAGES" }),
|
||||
route({
|
||||
permission: "SEND_MESSAGES",
|
||||
responses: {
|
||||
204: {},
|
||||
404: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { channel_id } = req.params;
|
||||
const user_id = req.user_id;
|
||||
|
@ -16,34 +16,56 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
Config,
|
||||
handleFile,
|
||||
trimSpecial,
|
||||
DiscordApiErrors,
|
||||
User,
|
||||
Webhook,
|
||||
WebhookCreateSchema,
|
||||
WebhookType,
|
||||
handleFile,
|
||||
trimSpecial,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { isTextChannel } from "./messages/index";
|
||||
import { DiscordApiErrors } from "@spacebar/util";
|
||||
import crypto from "crypto";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
//TODO: implement webhooks
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
res.json([]);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIWebhookArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
res.json([]);
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: use Image Data Type for avatar instead of String
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "WebhookCreateSchema", permission: "MANAGE_WEBHOOKS" }),
|
||||
route({
|
||||
requestBody: "WebhookCreateSchema",
|
||||
permission: "MANAGE_WEBHOOKS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "WebhookCreateResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const channel_id = req.params.channel_id;
|
||||
const channel = await Channel.findOneOrFail({
|
||||
|
@ -29,7 +29,7 @@ const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "ConnectionCallbackSchema" }),
|
||||
route({ requestBody: "ConnectionCallbackSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { connection_name } = req.params;
|
||||
const connection = ConnectionStore.connections.get(connection_name);
|
||||
|
@ -16,49 +16,61 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Guild, Config } from "@spacebar/util";
|
||||
import { Config, Guild } from "@spacebar/util";
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Like } from "typeorm";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { offset, limit, categories } = req.query;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
const configLimit = Config.get().guild.discovery.limit;
|
||||
let guilds;
|
||||
if (categories == undefined) {
|
||||
guilds = showAllGuilds
|
||||
? await Guild.find({ take: Math.abs(Number(limit || configLimit)) })
|
||||
: await Guild.find({
|
||||
where: { features: Like(`%DISCOVERABLE%`) },
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
});
|
||||
} else {
|
||||
guilds = showAllGuilds
|
||||
? await Guild.find({
|
||||
where: { primary_category_id: categories.toString() },
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
})
|
||||
: await Guild.find({
|
||||
where: {
|
||||
primary_category_id: categories.toString(),
|
||||
features: Like("%DISCOVERABLE%"),
|
||||
},
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
});
|
||||
}
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "DiscoverableGuildsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { offset, limit, categories } = req.query;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
const configLimit = Config.get().guild.discovery.limit;
|
||||
let guilds;
|
||||
if (categories == undefined) {
|
||||
guilds = showAllGuilds
|
||||
? await Guild.find({
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
})
|
||||
: await Guild.find({
|
||||
where: { features: Like(`%DISCOVERABLE%`) },
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
});
|
||||
} else {
|
||||
guilds = showAllGuilds
|
||||
? await Guild.find({
|
||||
where: { primary_category_id: categories.toString() },
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
})
|
||||
: await Guild.find({
|
||||
where: {
|
||||
primary_category_id: categories.toString(),
|
||||
features: Like("%DISCOVERABLE%"),
|
||||
},
|
||||
take: Math.abs(Number(limit || configLimit)),
|
||||
});
|
||||
}
|
||||
|
||||
const total = guilds ? guilds.length : undefined;
|
||||
const total = guilds ? guilds.length : undefined;
|
||||
|
||||
res.send({
|
||||
total: total,
|
||||
guilds: guilds,
|
||||
offset: Number(offset || Config.get().guild.discovery.offset),
|
||||
limit: Number(limit || configLimit),
|
||||
});
|
||||
});
|
||||
res.send({
|
||||
total: total,
|
||||
guilds: guilds,
|
||||
offset: Number(offset || Config.get().guild.discovery.offset),
|
||||
limit: Number(limit || configLimit),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,24 +16,34 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Categories } from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Categories } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/categories", route({}), async (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
// Get locale instead
|
||||
router.get(
|
||||
"/categories",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIDiscoveryCategoryArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
// Get locale instead
|
||||
|
||||
// const { locale, primary_only } = req.query;
|
||||
const { primary_only } = req.query;
|
||||
// const { locale, primary_only } = req.query;
|
||||
const { primary_only } = req.query;
|
||||
|
||||
const out = primary_only
|
||||
? await Categories.find()
|
||||
: await Categories.find({ where: { is_primary: true } });
|
||||
const out = primary_only
|
||||
? await Categories.find()
|
||||
: await Categories.find({ where: { is_primary: true } });
|
||||
|
||||
res.send(out);
|
||||
});
|
||||
res.send(out);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,32 +16,43 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { FieldErrors, Release } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { platform } = req.query;
|
||||
|
||||
if (!platform)
|
||||
throw FieldErrors({
|
||||
platform: {
|
||||
code: "BASE_TYPE_REQUIRED",
|
||||
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
302: {},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { platform } = req.query;
|
||||
|
||||
if (!platform)
|
||||
throw FieldErrors({
|
||||
platform: {
|
||||
code: "BASE_TYPE_REQUIRED",
|
||||
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
||||
},
|
||||
});
|
||||
|
||||
const release = await Release.findOneOrFail({
|
||||
where: {
|
||||
enabled: true,
|
||||
platform: platform as string,
|
||||
},
|
||||
order: { pub_date: "DESC" },
|
||||
});
|
||||
|
||||
const release = await Release.findOneOrFail({
|
||||
where: {
|
||||
enabled: true,
|
||||
platform: platform as string,
|
||||
},
|
||||
order: { pub_date: "DESC" },
|
||||
});
|
||||
|
||||
res.redirect(release.url);
|
||||
});
|
||||
res.redirect(release.url);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,32 +16,34 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route, RouteOptions } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const options: RouteOptions = {
|
||||
test: {
|
||||
response: {
|
||||
body: "GatewayBotResponse",
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GatewayBotResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
const { endpointPublic } = Config.get().gateway;
|
||||
res.json({
|
||||
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||
shards: 1,
|
||||
session_start_limit: {
|
||||
total: 1000,
|
||||
remaining: 999,
|
||||
reset_after: 14400000,
|
||||
max_concurrency: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
router.get("/", route(options), (req: Request, res: Response) => {
|
||||
const { endpointPublic } = Config.get().gateway;
|
||||
res.json({
|
||||
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||
shards: 1,
|
||||
session_start_limit: {
|
||||
total: 1000,
|
||||
remaining: 999,
|
||||
reset_after: 14400000,
|
||||
max_concurrency: 1,
|
||||
},
|
||||
});
|
||||
});
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,25 +16,27 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route, RouteOptions } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
const options: RouteOptions = {
|
||||
test: {
|
||||
response: {
|
||||
body: "GatewayResponse",
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GatewayResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
const { endpointPublic } = Config.get().gateway;
|
||||
res.json({
|
||||
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
router.get("/", route(options), (req: Request, res: Response) => {
|
||||
const { endpointPublic } = Config.get().gateway;
|
||||
res.json({
|
||||
url: endpointPublic || process.env.GATEWAY || "ws://localhost:3001",
|
||||
});
|
||||
});
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,34 +16,62 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { TenorMediaTypes, getGifApiKey, parseGifResult } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { route } from "@spacebar/api";
|
||||
import { getGifApiKey, parseGifResult } from "./trending";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
const { q, media_format, locale } = req.query;
|
||||
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const response = await fetch(
|
||||
`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
query: {
|
||||
q: {
|
||||
type: "string",
|
||||
required: true,
|
||||
description: "Search query",
|
||||
},
|
||||
media_format: {
|
||||
type: "string",
|
||||
description: "Media format",
|
||||
values: Object.keys(TenorMediaTypes).filter((key) =>
|
||||
isNaN(Number(key)),
|
||||
),
|
||||
},
|
||||
locale: {
|
||||
type: "string",
|
||||
description: "Locale",
|
||||
},
|
||||
},
|
||||
);
|
||||
responses: {
|
||||
200: {
|
||||
body: "TenorGifsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
const { q, media_format, locale } = req.query;
|
||||
|
||||
const { results } = await response.json();
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
});
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const response = await fetch(
|
||||
`https://g.tenor.com/v1/search?q=${q}&media_format=${media_format}&locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
|
||||
const { results } = await response.json();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,34 +16,57 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { TenorMediaTypes, getGifApiKey, parseGifResult } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { route } from "@spacebar/api";
|
||||
import { getGifApiKey, parseGifResult } from "./trending";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
const { media_format, locale } = req.query;
|
||||
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const response = await fetch(
|
||||
`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
query: {
|
||||
media_format: {
|
||||
type: "string",
|
||||
description: "Media format",
|
||||
values: Object.keys(TenorMediaTypes).filter((key) =>
|
||||
isNaN(Number(key)),
|
||||
),
|
||||
},
|
||||
locale: {
|
||||
type: "string",
|
||||
description: "Locale",
|
||||
},
|
||||
},
|
||||
);
|
||||
responses: {
|
||||
200: {
|
||||
body: "TenorGifsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
const { media_format, locale } = req.query;
|
||||
|
||||
const { results } = await response.json();
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
});
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const response = await fetch(
|
||||
`https://g.tenor.com/v1/trending?media_format=${media_format}&locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
|
||||
const { results } = await response.json();
|
||||
|
||||
res.json(results.map(parseGifResult)).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,126 +16,76 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
TenorCategoriesResults,
|
||||
TenorTrendingResults,
|
||||
getGifApiKey,
|
||||
parseGifResult,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// TODO: Move somewhere else
|
||||
enum TENOR_GIF_TYPES {
|
||||
gif,
|
||||
mediumgif,
|
||||
tinygif,
|
||||
nanogif,
|
||||
mp4,
|
||||
loopedmp4,
|
||||
tinymp4,
|
||||
nanomp4,
|
||||
webm,
|
||||
tinywebm,
|
||||
nanowebm,
|
||||
}
|
||||
|
||||
type TENOR_MEDIA = {
|
||||
preview: string;
|
||||
url: string;
|
||||
dims: number[];
|
||||
size: number;
|
||||
};
|
||||
|
||||
type TENOR_GIF = {
|
||||
created: number;
|
||||
hasaudio: boolean;
|
||||
id: string;
|
||||
media: { [type in keyof typeof TENOR_GIF_TYPES]: TENOR_MEDIA }[];
|
||||
tags: string[];
|
||||
title: string;
|
||||
itemurl: string;
|
||||
hascaption: boolean;
|
||||
url: string;
|
||||
};
|
||||
|
||||
type TENOR_CATEGORY = {
|
||||
searchterm: string;
|
||||
path: string;
|
||||
image: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
type TENOR_CATEGORIES_RESULTS = {
|
||||
tags: TENOR_CATEGORY[];
|
||||
};
|
||||
|
||||
type TENOR_TRENDING_RESULTS = {
|
||||
next: string;
|
||||
results: TENOR_GIF[];
|
||||
};
|
||||
|
||||
export function parseGifResult(result: TENOR_GIF) {
|
||||
return {
|
||||
id: result.id,
|
||||
title: result.title,
|
||||
url: result.itemurl,
|
||||
src: result.media[0].mp4.url,
|
||||
gif_src: result.media[0].gif.url,
|
||||
width: result.media[0].mp4.dims[0],
|
||||
height: result.media[0].mp4.dims[1],
|
||||
preview: result.media[0].mp4.preview,
|
||||
};
|
||||
}
|
||||
|
||||
export function getGifApiKey() {
|
||||
const { enabled, provider, apiKey } = Config.get().gif;
|
||||
if (!enabled) throw new HTTPError(`Gifs are disabled`);
|
||||
if (provider !== "tenor" || !apiKey)
|
||||
throw new HTTPError(`${provider} gif provider not supported`);
|
||||
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
// TODO: return gifs as mp4
|
||||
// const { media_format, locale } = req.query;
|
||||
const { locale } = req.query;
|
||||
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const [responseSource, trendGifSource] = await Promise.all([
|
||||
fetch(
|
||||
`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
query: {
|
||||
locale: {
|
||||
type: "string",
|
||||
description: "Locale",
|
||||
},
|
||||
),
|
||||
fetch(
|
||||
`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
body: "TenorTrendingResponse",
|
||||
},
|
||||
),
|
||||
]);
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: Custom providers
|
||||
// TODO: return gifs as mp4
|
||||
// const { media_format, locale } = req.query;
|
||||
const { locale } = req.query;
|
||||
|
||||
const { tags } = (await responseSource.json()) as TENOR_CATEGORIES_RESULTS;
|
||||
const { results } = (await trendGifSource.json()) as TENOR_TRENDING_RESULTS;
|
||||
const apiKey = getGifApiKey();
|
||||
|
||||
res.json({
|
||||
categories: tags.map((x) => ({
|
||||
name: x.searchterm,
|
||||
src: x.image,
|
||||
})),
|
||||
gifs: [parseGifResult(results[0])],
|
||||
}).status(200);
|
||||
});
|
||||
const agent = new ProxyAgent();
|
||||
|
||||
const [responseSource, trendGifSource] = await Promise.all([
|
||||
fetch(
|
||||
`https://g.tenor.com/v1/categories?locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
),
|
||||
fetch(
|
||||
`https://g.tenor.com/v1/trending?locale=${locale}&key=${apiKey}`,
|
||||
{
|
||||
agent,
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
||||
const { tags } =
|
||||
(await responseSource.json()) as TenorCategoriesResults;
|
||||
const { results } =
|
||||
(await trendGifSource.json()) as TenorTrendingResults;
|
||||
|
||||
res.json({
|
||||
categories: tags.map((x) => ({
|
||||
name: x.searchterm,
|
||||
src: x.image,
|
||||
})),
|
||||
gifs: [parseGifResult(results[0])],
|
||||
}).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,34 +16,44 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Guild, Config } from "@spacebar/util";
|
||||
import { Config, Guild } from "@spacebar/util";
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Like } from "typeorm";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// const { limit, personalization_disabled } = req.query;
|
||||
const { limit } = req.query;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildRecommendationsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// const { limit, personalization_disabled } = req.query;
|
||||
const { limit } = req.query;
|
||||
const showAllGuilds = Config.get().guild.discovery.showAllGuilds;
|
||||
|
||||
const genLoadId = (size: number) =>
|
||||
[...Array(size)]
|
||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||
.join("");
|
||||
const genLoadId = (size: number) =>
|
||||
[...Array(size)]
|
||||
.map(() => Math.floor(Math.random() * 16).toString(16))
|
||||
.join("");
|
||||
|
||||
const guilds = showAllGuilds
|
||||
? await Guild.find({ take: Math.abs(Number(limit || 24)) })
|
||||
: await Guild.find({
|
||||
where: { features: Like("%DISCOVERABLE%") },
|
||||
take: Math.abs(Number(limit || 24)),
|
||||
});
|
||||
res.send({
|
||||
recommended_guilds: guilds,
|
||||
load_id: `server_recs/${genLoadId(32)}`,
|
||||
}).status(200);
|
||||
});
|
||||
const guilds = showAllGuilds
|
||||
? await Guild.find({ take: Math.abs(Number(limit || 24)) })
|
||||
: await Guild.find({
|
||||
where: { features: Like("%DISCOVERABLE%") },
|
||||
take: Math.abs(Number(limit || 24)),
|
||||
});
|
||||
res.send({
|
||||
recommended_guilds: guilds,
|
||||
load_id: `server_recs/${genLoadId(32)}`,
|
||||
}).status(200);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,20 +16,20 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { getIpAdress, route } from "@spacebar/api";
|
||||
import {
|
||||
Ban,
|
||||
BanModeratorSchema,
|
||||
BanRegistrySchema,
|
||||
DiscordApiErrors,
|
||||
emitEvent,
|
||||
GuildBanAddEvent,
|
||||
GuildBanRemoveEvent,
|
||||
Ban,
|
||||
User,
|
||||
Member,
|
||||
BanRegistrySchema,
|
||||
BanModeratorSchema,
|
||||
User,
|
||||
emitEvent,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { getIpAdress, route } from "@spacebar/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -37,7 +37,17 @@ const router: Router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "BAN_MEMBERS" }),
|
||||
route({
|
||||
permission: "BAN_MEMBERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildBansResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
@ -73,7 +83,20 @@ router.get(
|
||||
|
||||
router.get(
|
||||
"/:user",
|
||||
route({ permission: "BAN_MEMBERS" }),
|
||||
route({
|
||||
permission: "BAN_MEMBERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "BanModeratorSchema",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const user_id = req.params.ban;
|
||||
@ -97,7 +120,21 @@ router.get(
|
||||
|
||||
router.put(
|
||||
"/:user_id",
|
||||
route({ body: "BanCreateSchema", permission: "BAN_MEMBERS" }),
|
||||
route({
|
||||
requestBody: "BanCreateSchema",
|
||||
permission: "BAN_MEMBERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Ban",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const banned_user_id = req.params.user_id;
|
||||
@ -143,7 +180,20 @@ router.put(
|
||||
|
||||
router.put(
|
||||
"/@me",
|
||||
route({ body: "BanCreateSchema" }),
|
||||
route({
|
||||
requestBody: "BanCreateSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Ban",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
@ -182,7 +232,18 @@ router.put(
|
||||
|
||||
router.delete(
|
||||
"/:user_id",
|
||||
route({ permission: "BAN_MEMBERS" }),
|
||||
route({
|
||||
permission: "BAN_MEMBERS",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, user_id } = req.params;
|
||||
|
||||
|
@ -16,28 +16,52 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelUpdateEvent,
|
||||
emitEvent,
|
||||
ChannelModifySchema,
|
||||
ChannelReorderSchema,
|
||||
ChannelUpdateEvent,
|
||||
emitEvent,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const channels = await Channel.find({ where: { guild_id } });
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
201: {
|
||||
body: "APIChannelArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const channels = await Channel.find({ where: { guild_id } });
|
||||
|
||||
res.json(channels);
|
||||
});
|
||||
res.json(channels);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }),
|
||||
route({
|
||||
requestBody: "ChannelModifySchema",
|
||||
permission: "MANAGE_CHANNELS",
|
||||
responses: {
|
||||
201: {
|
||||
body: "Channel",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// creates a new guild channel https://discord.com/developers/docs/resources/guild#create-guild-channel
|
||||
const { guild_id } = req.params;
|
||||
@ -54,7 +78,19 @@ router.post(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "ChannelReorderSchema", permission: "MANAGE_CHANNELS" }),
|
||||
route({
|
||||
requestBody: "ChannelReorderSchema",
|
||||
permission: "MANAGE_CHANNELS",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// changes guild channel position
|
||||
const { guild_id } = req.params;
|
||||
|
@ -16,37 +16,51 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { emitEvent, GuildDeleteEvent, Guild } from "@spacebar/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Guild, GuildDeleteEvent, emitEvent } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// discord prefixes this route with /delete instead of using the delete method
|
||||
// docs are wrong https://discord.com/developers/docs/resources/guild#delete-guild
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
select: ["owner_id"],
|
||||
});
|
||||
if (guild.owner_id !== req.user_id)
|
||||
throw new HTTPError("You are not the owner of this guild", 401);
|
||||
|
||||
await Promise.all([
|
||||
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guild_id,
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildDeleteEvent),
|
||||
]);
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
select: ["owner_id"],
|
||||
});
|
||||
if (guild.owner_id !== req.user_id)
|
||||
throw new HTTPError("You are not the owner of this guild", 401);
|
||||
|
||||
await Promise.all([
|
||||
Guild.delete({ id: guild_id }), // this will also delete all guild related data
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guild_id,
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildDeleteEvent),
|
||||
]);
|
||||
|
||||
return res.sendStatus(204);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,40 +16,50 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
// TODO:
|
||||
// Load from database
|
||||
// Admin control, but for now it allows anyone to be discoverable
|
||||
|
||||
res.send({
|
||||
guild_id: guild_id,
|
||||
safe_environment: true,
|
||||
healthy: true,
|
||||
health_score_pending: false,
|
||||
size: true,
|
||||
nsfw_properties: {},
|
||||
protected: true,
|
||||
sufficient: true,
|
||||
sufficient_without_grace_period: true,
|
||||
valid_rules_channel: true,
|
||||
retention_healthy: true,
|
||||
engagement_healthy: true,
|
||||
age: true,
|
||||
minimum_age: 0,
|
||||
health_score: {
|
||||
avg_nonnew_participators: 0,
|
||||
avg_nonnew_communicators: 0,
|
||||
num_intentful_joiners: 0,
|
||||
perc_ret_w1_intentful: 0,
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildDiscoveryRequirementsResponse",
|
||||
},
|
||||
},
|
||||
minimum_size: 0,
|
||||
});
|
||||
});
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
// TODO:
|
||||
// Load from database
|
||||
// Admin control, but for now it allows anyone to be discoverable
|
||||
|
||||
res.send({
|
||||
guild_id: guild_id,
|
||||
safe_environment: true,
|
||||
healthy: true,
|
||||
health_score_pending: false,
|
||||
size: true,
|
||||
nsfw_properties: {},
|
||||
protected: true,
|
||||
sufficient: true,
|
||||
sufficient_without_grace_period: true,
|
||||
valid_rules_channel: true,
|
||||
retention_healthy: true,
|
||||
engagement_healthy: true,
|
||||
age: true,
|
||||
minimum_age: 0,
|
||||
health_score: {
|
||||
avg_nonnew_participators: 0,
|
||||
avg_nonnew_communicators: 0,
|
||||
num_intentful_joiners: 0,
|
||||
perc_ret_w1_intentful: 0,
|
||||
},
|
||||
minimum_size: 0,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,55 +16,95 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Config,
|
||||
DiscordApiErrors,
|
||||
emitEvent,
|
||||
Emoji,
|
||||
EmojiCreateSchema,
|
||||
EmojiModifySchema,
|
||||
GuildEmojisUpdateEvent,
|
||||
handleFile,
|
||||
Member,
|
||||
Snowflake,
|
||||
User,
|
||||
EmojiCreateSchema,
|
||||
EmojiModifySchema,
|
||||
emitEvent,
|
||||
handleFile,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIEmojiArray",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
const emojis = await Emoji.find({
|
||||
where: { guild_id: guild_id },
|
||||
relations: ["user"],
|
||||
});
|
||||
const emojis = await Emoji.find({
|
||||
where: { guild_id: guild_id },
|
||||
relations: ["user"],
|
||||
});
|
||||
|
||||
return res.json(emojis);
|
||||
});
|
||||
return res.json(emojis);
|
||||
},
|
||||
);
|
||||
|
||||
router.get("/:emoji_id", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, emoji_id } = req.params;
|
||||
router.get(
|
||||
"/:emoji_id",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Emoji",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, emoji_id } = req.params;
|
||||
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
const emoji = await Emoji.findOneOrFail({
|
||||
where: { guild_id: guild_id, id: emoji_id },
|
||||
relations: ["user"],
|
||||
});
|
||||
const emoji = await Emoji.findOneOrFail({
|
||||
where: { guild_id: guild_id, id: emoji_id },
|
||||
relations: ["user"],
|
||||
});
|
||||
|
||||
return res.json(emoji);
|
||||
});
|
||||
return res.json(emoji);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
body: "EmojiCreateSchema",
|
||||
requestBody: "EmojiCreateSchema",
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
responses: {
|
||||
201: {
|
||||
body: "Emoji",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
@ -113,8 +153,16 @@ router.post(
|
||||
router.patch(
|
||||
"/:emoji_id",
|
||||
route({
|
||||
body: "EmojiModifySchema",
|
||||
requestBody: "EmojiModifySchema",
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Emoji",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { emoji_id, guild_id } = req.params;
|
||||
@ -141,7 +189,15 @@ router.patch(
|
||||
|
||||
router.delete(
|
||||
"/:emoji_id",
|
||||
route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
||||
route({
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { emoji_id, guild_id } = req.params;
|
||||
|
||||
|
@ -16,46 +16,79 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
DiscordApiErrors,
|
||||
Guild,
|
||||
GuildUpdateEvent,
|
||||
GuildUpdateSchema,
|
||||
Member,
|
||||
SpacebarApiErrors,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
Guild,
|
||||
GuildUpdateEvent,
|
||||
handleFile,
|
||||
Member,
|
||||
GuildUpdateSchema,
|
||||
SpacebarApiErrors,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
"200": {
|
||||
body: "APIGuildWithJoinedAt",
|
||||
},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const [guild, member] = await Promise.all([
|
||||
Guild.findOneOrFail({ where: { id: guild_id } }),
|
||||
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
||||
]);
|
||||
if (!member)
|
||||
throw new HTTPError(
|
||||
"You are not a member of the guild you are trying to access",
|
||||
401,
|
||||
);
|
||||
const [guild, member] = await Promise.all([
|
||||
Guild.findOneOrFail({ where: { id: guild_id } }),
|
||||
Member.findOne({ where: { guild_id: guild_id, id: req.user_id } }),
|
||||
]);
|
||||
if (!member)
|
||||
throw new HTTPError(
|
||||
"You are not a member of the guild you are trying to access",
|
||||
401,
|
||||
);
|
||||
|
||||
return res.send({
|
||||
...guild,
|
||||
joined_at: member?.joined_at,
|
||||
});
|
||||
});
|
||||
return res.send({
|
||||
...guild,
|
||||
joined_at: member?.joined_at,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "GuildUpdateSchema", permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
requestBody: "GuildUpdateSchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
"200": {
|
||||
body: "GuildUpdateSchema",
|
||||
},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as GuildUpdateSchema;
|
||||
const { guild_id } = req.params;
|
||||
|
@ -16,15 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Invite, PublicInviteRelation } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Invite, PublicInviteRelation } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIInviteArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
|
@ -16,17 +16,27 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: member verification
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: member verification
|
||||
|
||||
res.status(404).json({
|
||||
message: "Unknown Guild Member Verification Form",
|
||||
code: 10068,
|
||||
});
|
||||
});
|
||||
res.status(404).json({
|
||||
message: "Unknown Guild Member Verification Form",
|
||||
code: 10068,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,38 +16,70 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Member,
|
||||
emitEvent,
|
||||
Emoji,
|
||||
getPermission,
|
||||
getRights,
|
||||
Role,
|
||||
GuildMemberUpdateEvent,
|
||||
emitEvent,
|
||||
Sticker,
|
||||
Emoji,
|
||||
Guild,
|
||||
GuildMemberUpdateEvent,
|
||||
handleFile,
|
||||
Member,
|
||||
MemberChangeSchema,
|
||||
Role,
|
||||
Sticker,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, member_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Member",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, member_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
const member = await Member.findOneOrFail({
|
||||
where: { id: member_id, guild_id },
|
||||
});
|
||||
const member = await Member.findOneOrFail({
|
||||
where: { id: member_id, guild_id },
|
||||
});
|
||||
|
||||
return res.json(member);
|
||||
});
|
||||
return res.json(member);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "MemberChangeSchema" }),
|
||||
route({
|
||||
requestBody: "MemberChangeSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Member",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const member_id =
|
||||
@ -119,54 +151,81 @@ router.patch(
|
||||
},
|
||||
);
|
||||
|
||||
router.put("/", route({}), async (req: Request, res: Response) => {
|
||||
// TODO: Lurker mode
|
||||
router.put(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "MemberJoinGuildResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// TODO: Lurker mode
|
||||
|
||||
const rights = await getRights(req.user_id);
|
||||
const rights = await getRights(req.user_id);
|
||||
|
||||
const { guild_id } = req.params;
|
||||
let { member_id } = req.params;
|
||||
if (member_id === "@me") {
|
||||
member_id = req.user_id;
|
||||
rights.hasThrow("JOIN_GUILDS");
|
||||
} else {
|
||||
// TODO: join others by controller
|
||||
}
|
||||
const { guild_id } = req.params;
|
||||
let { member_id } = req.params;
|
||||
if (member_id === "@me") {
|
||||
member_id = req.user_id;
|
||||
rights.hasThrow("JOIN_GUILDS");
|
||||
} else {
|
||||
// TODO: join others by controller
|
||||
}
|
||||
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
});
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
});
|
||||
|
||||
const emoji = await Emoji.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
const emoji = await Emoji.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
const roles = await Role.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
const roles = await Role.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
const stickers = await Sticker.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
const stickers = await Sticker.find({
|
||||
where: { guild_id: guild_id },
|
||||
});
|
||||
|
||||
await Member.addToGuild(member_id, guild_id);
|
||||
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
||||
});
|
||||
await Member.addToGuild(member_id, guild_id);
|
||||
res.send({ ...guild, emojis: emoji, roles: roles, stickers: stickers });
|
||||
},
|
||||
);
|
||||
|
||||
router.delete("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, member_id } = req.params;
|
||||
const permission = await getPermission(req.user_id, guild_id);
|
||||
const rights = await getRights(req.user_id);
|
||||
if (member_id === "@me" || member_id === req.user_id) {
|
||||
// TODO: unless force-joined
|
||||
rights.hasThrow("SELF_LEAVE_GROUPS");
|
||||
} else {
|
||||
rights.hasThrow("KICK_BAN_MEMBERS");
|
||||
permission.hasThrow("KICK_MEMBERS");
|
||||
}
|
||||
router.delete(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, member_id } = req.params;
|
||||
const permission = await getPermission(req.user_id, guild_id);
|
||||
const rights = await getRights(req.user_id);
|
||||
if (member_id === "@me" || member_id === req.user_id) {
|
||||
// TODO: unless force-joined
|
||||
rights.hasThrow("SELF_LEAVE_GROUPS");
|
||||
} else {
|
||||
rights.hasThrow("KICK_BAN_MEMBERS");
|
||||
permission.hasThrow("KICK_MEMBERS");
|
||||
}
|
||||
|
||||
await Member.removeFromGuild(member_id, guild_id);
|
||||
res.sendStatus(204);
|
||||
});
|
||||
await Member.removeFromGuild(member_id, guild_id);
|
||||
res.sendStatus(204);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,15 +16,26 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getPermission, Member, PermissionResolvable } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { getPermission, Member, PermissionResolvable } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "MemberNickChangeSchema" }),
|
||||
route({
|
||||
requestBody: "MemberNickChangeSchema",
|
||||
responses: {
|
||||
200: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
let permissionString: PermissionResolvable = "MANAGE_NICKNAMES";
|
||||
|
@ -16,15 +16,23 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Member } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Member } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.delete(
|
||||
"/",
|
||||
route({ permission: "MANAGE_ROLES" }),
|
||||
route({
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id, member_id } = req.params;
|
||||
|
||||
@ -35,7 +43,13 @@ router.delete(
|
||||
|
||||
router.put(
|
||||
"/",
|
||||
route({ permission: "MANAGE_ROLES" }),
|
||||
route({
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id, member_id } = req.params;
|
||||
|
||||
|
@ -16,35 +16,58 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Member, PublicMemberProjection } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { MoreThan } from "typeorm";
|
||||
import { Member, PublicMemberProjection } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { MoreThan } from "typeorm";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// TODO: send over websocket
|
||||
// TODO: check for GUILD_MEMBERS intent
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const limit = Number(req.query.limit) || 1;
|
||||
if (limit > 1000 || limit < 1)
|
||||
throw new HTTPError("Limit must be between 1 and 1000");
|
||||
const after = `${req.query.after}`;
|
||||
const query = after ? { id: MoreThan(after) } : {};
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
query: {
|
||||
limit: {
|
||||
type: "number",
|
||||
description:
|
||||
"max number of members to return (1-1000). default 1",
|
||||
},
|
||||
after: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIMemberArray",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const limit = Number(req.query.limit) || 1;
|
||||
if (limit > 1000 || limit < 1)
|
||||
throw new HTTPError("Limit must be between 1 and 1000");
|
||||
const after = `${req.query.after}`;
|
||||
const query = after ? { id: MoreThan(after) } : {};
|
||||
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
const members = await Member.find({
|
||||
where: { guild_id, ...query },
|
||||
select: PublicMemberProjection,
|
||||
take: limit,
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
const members = await Member.find({
|
||||
where: { guild_id, ...query },
|
||||
select: PublicMemberProjection,
|
||||
take: limit,
|
||||
order: { id: "ASC" },
|
||||
});
|
||||
|
||||
return res.json(members);
|
||||
});
|
||||
return res.json(members);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -18,140 +18,159 @@
|
||||
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { getPermission, FieldErrors, Message, Channel } from "@spacebar/util";
|
||||
import { Channel, FieldErrors, Message, getPermission } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { FindManyOptions, In, Like } from "typeorm";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const {
|
||||
channel_id,
|
||||
content,
|
||||
// include_nsfw, // TODO
|
||||
offset,
|
||||
sort_order,
|
||||
// sort_by, // TODO: Handle 'relevance'
|
||||
limit,
|
||||
author_id,
|
||||
} = req.query;
|
||||
|
||||
const parsedLimit = Number(limit) || 50;
|
||||
if (parsedLimit < 1 || parsedLimit > 100)
|
||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||
|
||||
if (sort_order) {
|
||||
if (
|
||||
typeof sort_order != "string" ||
|
||||
["desc", "asc"].indexOf(sort_order) == -1
|
||||
)
|
||||
throw FieldErrors({
|
||||
sort_order: {
|
||||
message: "Value must be one of ('desc', 'asc').",
|
||||
code: "BASE_TYPE_CHOICES",
|
||||
},
|
||||
}); // todo this is wrong
|
||||
}
|
||||
|
||||
const permissions = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
channel_id as string | undefined,
|
||||
);
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
||||
return res.json({ messages: [], total_results: 0 });
|
||||
|
||||
const query: FindManyOptions<Message> = {
|
||||
order: {
|
||||
timestamp: sort_order
|
||||
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
||||
: "DESC",
|
||||
},
|
||||
take: parsedLimit || 0,
|
||||
where: {
|
||||
guild: {
|
||||
id: req.params.guild_id,
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildMessagesSearchResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
422: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
relations: [
|
||||
"author",
|
||||
"webhook",
|
||||
"application",
|
||||
"mentions",
|
||||
"mention_roles",
|
||||
"mention_channels",
|
||||
"sticker_items",
|
||||
"attachments",
|
||||
],
|
||||
skip: offset ? Number(offset) : 0,
|
||||
};
|
||||
//@ts-ignore
|
||||
if (channel_id) query.where.channel = { id: channel_id };
|
||||
else {
|
||||
// get all channel IDs that this user can access
|
||||
const channels = await Channel.find({
|
||||
where: { guild_id: req.params.guild_id },
|
||||
select: ["id"],
|
||||
});
|
||||
const ids = [];
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const {
|
||||
channel_id,
|
||||
content,
|
||||
// include_nsfw, // TODO
|
||||
offset,
|
||||
sort_order,
|
||||
// sort_by, // TODO: Handle 'relevance'
|
||||
limit,
|
||||
author_id,
|
||||
} = req.query;
|
||||
|
||||
for (const channel of channels) {
|
||||
const perm = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
channel.id,
|
||||
);
|
||||
if (!perm.has("VIEW_CHANNEL") || !perm.has("READ_MESSAGE_HISTORY"))
|
||||
continue;
|
||||
ids.push(channel.id);
|
||||
const parsedLimit = Number(limit) || 50;
|
||||
if (parsedLimit < 1 || parsedLimit > 100)
|
||||
throw new HTTPError("limit must be between 1 and 100", 422);
|
||||
|
||||
if (sort_order) {
|
||||
if (
|
||||
typeof sort_order != "string" ||
|
||||
["desc", "asc"].indexOf(sort_order) == -1
|
||||
)
|
||||
throw FieldErrors({
|
||||
sort_order: {
|
||||
message: "Value must be one of ('desc', 'asc').",
|
||||
code: "BASE_TYPE_CHOICES",
|
||||
},
|
||||
}); // todo this is wrong
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
query.where.channel = { id: In(ids) };
|
||||
}
|
||||
//@ts-ignore
|
||||
if (author_id) query.where.author = { id: author_id };
|
||||
//@ts-ignore
|
||||
if (content) query.where.content = Like(`%${content}%`);
|
||||
const permissions = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
channel_id as string | undefined,
|
||||
);
|
||||
permissions.hasThrow("VIEW_CHANNEL");
|
||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
||||
return res.json({ messages: [], total_results: 0 });
|
||||
|
||||
const messages: Message[] = await Message.find(query);
|
||||
|
||||
const messagesDto = messages.map((x) => [
|
||||
{
|
||||
id: x.id,
|
||||
type: x.type,
|
||||
content: x.content,
|
||||
channel_id: x.channel_id,
|
||||
author: {
|
||||
id: x.author?.id,
|
||||
username: x.author?.username,
|
||||
avatar: x.author?.avatar,
|
||||
avatar_decoration: null,
|
||||
discriminator: x.author?.discriminator,
|
||||
public_flags: x.author?.public_flags,
|
||||
const query: FindManyOptions<Message> = {
|
||||
order: {
|
||||
timestamp: sort_order
|
||||
? (sort_order.toUpperCase() as "ASC" | "DESC")
|
||||
: "DESC",
|
||||
},
|
||||
attachments: x.attachments,
|
||||
embeds: x.embeds,
|
||||
mentions: x.mentions,
|
||||
mention_roles: x.mention_roles,
|
||||
pinned: x.pinned,
|
||||
mention_everyone: x.mention_everyone,
|
||||
tts: x.tts,
|
||||
timestamp: x.timestamp,
|
||||
edited_timestamp: x.edited_timestamp,
|
||||
flags: x.flags,
|
||||
components: x.components,
|
||||
hit: true,
|
||||
},
|
||||
]);
|
||||
take: parsedLimit || 0,
|
||||
where: {
|
||||
guild: {
|
||||
id: req.params.guild_id,
|
||||
},
|
||||
},
|
||||
relations: [
|
||||
"author",
|
||||
"webhook",
|
||||
"application",
|
||||
"mentions",
|
||||
"mention_roles",
|
||||
"mention_channels",
|
||||
"sticker_items",
|
||||
"attachments",
|
||||
],
|
||||
skip: offset ? Number(offset) : 0,
|
||||
};
|
||||
//@ts-ignore
|
||||
if (channel_id) query.where.channel = { id: channel_id };
|
||||
else {
|
||||
// get all channel IDs that this user can access
|
||||
const channels = await Channel.find({
|
||||
where: { guild_id: req.params.guild_id },
|
||||
select: ["id"],
|
||||
});
|
||||
const ids = [];
|
||||
|
||||
return res.json({
|
||||
messages: messagesDto,
|
||||
total_results: messages.length,
|
||||
});
|
||||
});
|
||||
for (const channel of channels) {
|
||||
const perm = await getPermission(
|
||||
req.user_id,
|
||||
req.params.guild_id,
|
||||
channel.id,
|
||||
);
|
||||
if (
|
||||
!perm.has("VIEW_CHANNEL") ||
|
||||
!perm.has("READ_MESSAGE_HISTORY")
|
||||
)
|
||||
continue;
|
||||
ids.push(channel.id);
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
query.where.channel = { id: In(ids) };
|
||||
}
|
||||
//@ts-ignore
|
||||
if (author_id) query.where.author = { id: author_id };
|
||||
//@ts-ignore
|
||||
if (content) query.where.content = Like(`%${content}%`);
|
||||
|
||||
const messages: Message[] = await Message.find(query);
|
||||
|
||||
const messagesDto = messages.map((x) => [
|
||||
{
|
||||
id: x.id,
|
||||
type: x.type,
|
||||
content: x.content,
|
||||
channel_id: x.channel_id,
|
||||
author: {
|
||||
id: x.author?.id,
|
||||
username: x.author?.username,
|
||||
avatar: x.author?.avatar,
|
||||
avatar_decoration: null,
|
||||
discriminator: x.author?.discriminator,
|
||||
public_flags: x.author?.public_flags,
|
||||
},
|
||||
attachments: x.attachments,
|
||||
embeds: x.embeds,
|
||||
mentions: x.mentions,
|
||||
mention_roles: x.mention_roles,
|
||||
pinned: x.pinned,
|
||||
mention_everyone: x.mention_everyone,
|
||||
tts: x.tts,
|
||||
timestamp: x.timestamp,
|
||||
edited_timestamp: x.edited_timestamp,
|
||||
flags: x.flags,
|
||||
components: x.components,
|
||||
hit: true,
|
||||
},
|
||||
]);
|
||||
|
||||
return res.json({
|
||||
messages: messagesDto,
|
||||
total_results: messages.length,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -31,7 +31,20 @@ const router = Router();
|
||||
|
||||
router.patch(
|
||||
"/:member_id",
|
||||
route({ body: "MemberChangeProfileSchema" }),
|
||||
route({
|
||||
requestBody: "MemberChangeProfileSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Member",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
// const member_id =
|
||||
|
@ -16,14 +16,14 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { Guild, Member, Snowflake } from "@spacebar/util";
|
||||
import { LessThan, IsNull } from "typeorm";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Guild, Member, Snowflake } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { IsNull, LessThan } from "typeorm";
|
||||
const router = Router();
|
||||
|
||||
//Returns all inactive members, respecting role hierarchy
|
||||
export const inactiveMembers = async (
|
||||
const inactiveMembers = async (
|
||||
guild_id: string,
|
||||
user_id: string,
|
||||
days: number,
|
||||
@ -80,25 +80,46 @@ export const inactiveMembers = async (
|
||||
return members;
|
||||
};
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const days = parseInt(req.query.days as string);
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
"200": {
|
||||
body: "GuildPruneResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const days = parseInt(req.query.days as string);
|
||||
|
||||
let roles = req.query.include_roles;
|
||||
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
||||
let roles = req.query.include_roles;
|
||||
if (typeof roles === "string") roles = [roles]; //express will return array otherwise
|
||||
|
||||
const members = await inactiveMembers(
|
||||
req.params.guild_id,
|
||||
req.user_id,
|
||||
days,
|
||||
roles as string[],
|
||||
);
|
||||
const members = await inactiveMembers(
|
||||
req.params.guild_id,
|
||||
req.user_id,
|
||||
days,
|
||||
roles as string[],
|
||||
);
|
||||
|
||||
res.send({ pruned: members.length });
|
||||
});
|
||||
res.send({ pruned: members.length });
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }),
|
||||
route({
|
||||
permission: "KICK_MEMBERS",
|
||||
right: "KICK_BAN_MEMBERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildPurgeResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const days = parseInt(req.body.days);
|
||||
|
||||
|
@ -16,22 +16,35 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getIpAdress, getVoiceRegions, route } from "@spacebar/api";
|
||||
import { Guild } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { getVoiceRegions, route, getIpAdress } from "@spacebar/api";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
//TODO we should use an enum for guild's features and not hardcoded strings
|
||||
return res.json(
|
||||
await getVoiceRegions(
|
||||
getIpAdress(req),
|
||||
guild.features.includes("VIP_REGIONS"),
|
||||
),
|
||||
);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIGuildVoiceRegion",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
//TODO we should use an enum for guild's features and not hardcoded strings
|
||||
return res.json(
|
||||
await getVoiceRegions(
|
||||
getIpAdress(req),
|
||||
guild.features.includes("VIP_REGIONS"),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,31 +16,63 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Role,
|
||||
Member,
|
||||
GuildRoleUpdateEvent,
|
||||
GuildRoleDeleteEvent,
|
||||
emitEvent,
|
||||
GuildRoleDeleteEvent,
|
||||
GuildRoleUpdateEvent,
|
||||
handleFile,
|
||||
Member,
|
||||
Role,
|
||||
RoleModifySchema,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
const role = await Role.findOneOrFail({ where: { guild_id, id: role_id } });
|
||||
return res.json(role);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Role",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
const role = await Role.findOneOrFail({
|
||||
where: { guild_id, id: role_id },
|
||||
});
|
||||
return res.json(role);
|
||||
},
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/",
|
||||
route({ permission: "MANAGE_ROLES" }),
|
||||
route({
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id } = req.params;
|
||||
if (role_id === guild_id)
|
||||
@ -69,7 +101,24 @@ router.delete(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }),
|
||||
route({
|
||||
requestBody: "RoleModifySchema",
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Role",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { role_id, guild_id } = req.params;
|
||||
const body = req.body as RoleModifySchema;
|
||||
|
@ -16,21 +16,20 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Role,
|
||||
getPermission,
|
||||
Member,
|
||||
GuildRoleCreateEvent,
|
||||
GuildRoleUpdateEvent,
|
||||
emitEvent,
|
||||
Config,
|
||||
DiscordApiErrors,
|
||||
emitEvent,
|
||||
GuildRoleCreateEvent,
|
||||
GuildRoleUpdateEvent,
|
||||
Member,
|
||||
Role,
|
||||
RoleModifySchema,
|
||||
RolePositionUpdateSchema,
|
||||
Snowflake,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Not } from "typeorm";
|
||||
|
||||
const router: Router = Router();
|
||||
@ -47,7 +46,21 @@ router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "RoleModifySchema", permission: "MANAGE_ROLES" }),
|
||||
route({
|
||||
requestBody: "RoleModifySchema",
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Role",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const guild_id = req.params.guild_id;
|
||||
const body = req.body as RoleModifySchema;
|
||||
@ -104,14 +117,25 @@ router.post(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "RolePositionUpdateSchema" }),
|
||||
route({
|
||||
requestBody: "RolePositionUpdateSchema",
|
||||
permission: "MANAGE_ROLES",
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIRoleArray",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const body = req.body as RolePositionUpdateSchema;
|
||||
|
||||
const perms = await getPermission(req.user_id, guild_id);
|
||||
perms.hasThrow("MANAGE_ROLES");
|
||||
|
||||
await Promise.all(
|
||||
body.map(async (x) =>
|
||||
Role.update({ guild_id, id: x.id }, { position: x.position }),
|
||||
|
@ -16,29 +16,42 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
emitEvent,
|
||||
GuildStickersUpdateEvent,
|
||||
Member,
|
||||
ModifyGuildStickerSchema,
|
||||
Snowflake,
|
||||
Sticker,
|
||||
StickerFormatType,
|
||||
StickerType,
|
||||
emitEvent,
|
||||
uploadFile,
|
||||
ModifyGuildStickerSchema,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import multer from "multer";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import multer from "multer";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIStickerArray",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
res.json(await Sticker.find({ where: { guild_id } }));
|
||||
});
|
||||
res.json(await Sticker.find({ where: { guild_id } }));
|
||||
},
|
||||
);
|
||||
|
||||
const bodyParser = multer({
|
||||
limits: {
|
||||
@ -54,7 +67,18 @@ router.post(
|
||||
bodyParser,
|
||||
route({
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
body: "ModifyGuildStickerSchema",
|
||||
requestBody: "ModifyGuildStickerSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Sticker",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
if (!req.file) throw new HTTPError("missing file");
|
||||
@ -81,7 +105,7 @@ router.post(
|
||||
},
|
||||
);
|
||||
|
||||
export function getStickerFormat(mime_type: string) {
|
||||
function getStickerFormat(mime_type: string) {
|
||||
switch (mime_type) {
|
||||
case "image/apng":
|
||||
return StickerFormatType.APNG;
|
||||
@ -98,20 +122,46 @@ export function getStickerFormat(mime_type: string) {
|
||||
}
|
||||
}
|
||||
|
||||
router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
router.get(
|
||||
"/:sticker_id",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Sticker",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
res.json(
|
||||
await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }),
|
||||
);
|
||||
});
|
||||
res.json(
|
||||
await Sticker.findOneOrFail({
|
||||
where: { guild_id, id: sticker_id },
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:sticker_id",
|
||||
route({
|
||||
body: "ModifyGuildStickerSchema",
|
||||
requestBody: "ModifyGuildStickerSchema",
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Sticker",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
@ -141,7 +191,15 @@ async function sendStickerUpdateEvent(guild_id: string) {
|
||||
|
||||
router.delete(
|
||||
"/:sticker_id",
|
||||
route({ permission: "MANAGE_EMOJIS_AND_STICKERS" }),
|
||||
route({
|
||||
permission: "MANAGE_EMOJIS_AND_STICKERS",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id, sticker_id } = req.params;
|
||||
|
||||
|
@ -16,11 +16,10 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { generateCode, route } from "@spacebar/api";
|
||||
import { Guild, Template } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { generateCode } from "@spacebar/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -41,19 +40,46 @@ const TemplateGuildProjection: (keyof Guild)[] = [
|
||||
"icon",
|
||||
];
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APITemplateArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const templates = await Template.find({
|
||||
where: { source_guild_id: guild_id },
|
||||
});
|
||||
const templates = await Template.find({
|
||||
where: { source_guild_id: guild_id },
|
||||
});
|
||||
|
||||
return res.json(templates);
|
||||
});
|
||||
return res.json(templates);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "TemplateCreateSchema", permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
requestBody: "TemplateCreateSchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: {
|
||||
body: "Template",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({
|
||||
@ -81,7 +107,13 @@ router.post(
|
||||
|
||||
router.delete(
|
||||
"/:code",
|
||||
route({ permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: { body: "Template" },
|
||||
403: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code, guild_id } = req.params;
|
||||
|
||||
@ -96,7 +128,13 @@ router.delete(
|
||||
|
||||
router.put(
|
||||
"/:code",
|
||||
route({ permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: { body: "Template" },
|
||||
403: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code, guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({
|
||||
@ -115,7 +153,14 @@ router.put(
|
||||
|
||||
router.patch(
|
||||
"/:code",
|
||||
route({ body: "TemplateModifySchema", permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
requestBody: "TemplateModifySchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: { body: "Template" },
|
||||
403: { body: "APIErrorResponse" },
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code, guild_id } = req.params;
|
||||
const { name, description } = req.body;
|
||||
|
@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelType,
|
||||
@ -23,8 +24,7 @@ import {
|
||||
Invite,
|
||||
VanityUrlSchema,
|
||||
} from "@spacebar/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
@ -33,7 +33,20 @@ const InviteRegex = /\W/g;
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildVanityUrlResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
@ -60,7 +73,21 @@ router.get(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
requestBody: "VanityUrlSchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildVanityUrlCreateResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
const body = req.body as VanityUrlSchema;
|
||||
|
@ -16,6 +16,7 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
ChannelType,
|
||||
@ -26,7 +27,6 @@ import {
|
||||
VoiceStateUpdateEvent,
|
||||
VoiceStateUpdateSchema,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
@ -34,7 +34,21 @@ const router = Router();
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "VoiceStateUpdateSchema" }),
|
||||
route({
|
||||
requestBody: "VoiceStateUpdateSchema",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as VoiceStateUpdateSchema;
|
||||
const { guild_id } = req.params;
|
||||
|
@ -16,27 +16,49 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Guild, Member, GuildUpdateWelcomeScreenSchema } from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Guild, GuildUpdateWelcomeScreenSchema, Member } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const guild_id = req.params.guild_id;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildWelcomeScreen",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const guild_id = req.params.guild_id;
|
||||
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
await Member.IsInGuildOrFail(req.user_id, guild_id);
|
||||
|
||||
res.json(guild.welcome_screen);
|
||||
});
|
||||
res.json(guild.welcome_screen);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({
|
||||
body: "GuildUpdateWelcomeScreenSchema",
|
||||
requestBody: "GuildUpdateWelcomeScreenSchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const guild_id = req.params.guild_id;
|
||||
|
@ -16,10 +16,10 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Permissions, Guild, Invite, Channel, Member } from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { random, route } from "@spacebar/api";
|
||||
import { Channel, Guild, Invite, Member, Permissions } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -32,77 +32,90 @@ const router: Router = Router();
|
||||
|
||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget
|
||||
// TODO: Cache the response for a guild for 5 minutes regardless of response
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildWidgetJsonResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
if (!guild.widget_enabled) throw new HTTPError("Widget Disabled", 404);
|
||||
|
||||
// Fetch existing widget invite for widget channel
|
||||
let invite = await Invite.findOne({
|
||||
where: { channel_id: guild.widget_channel_id },
|
||||
});
|
||||
// Fetch existing widget invite for widget channel
|
||||
let invite = await Invite.findOne({
|
||||
where: { channel_id: guild.widget_channel_id },
|
||||
});
|
||||
|
||||
if (guild.widget_channel_id && !invite) {
|
||||
// Create invite for channel if none exists
|
||||
// TODO: Refactor invite create code to a shared function
|
||||
const max_age = 86400; // 24 hours
|
||||
const expires_at = new Date(max_age * 1000 + Date.now());
|
||||
if (guild.widget_channel_id && !invite) {
|
||||
// Create invite for channel if none exists
|
||||
// TODO: Refactor invite create code to a shared function
|
||||
const max_age = 86400; // 24 hours
|
||||
const expires_at = new Date(max_age * 1000 + Date.now());
|
||||
|
||||
invite = await Invite.create({
|
||||
code: random(),
|
||||
temporary: false,
|
||||
uses: 0,
|
||||
max_uses: 0,
|
||||
max_age: max_age,
|
||||
expires_at,
|
||||
created_at: new Date(),
|
||||
guild_id,
|
||||
channel_id: guild.widget_channel_id,
|
||||
}).save();
|
||||
}
|
||||
|
||||
// Fetch voice channels, and the @everyone permissions object
|
||||
const channels: { id: string; name: string; position: number }[] = [];
|
||||
|
||||
(
|
||||
await Channel.find({
|
||||
where: { guild_id: guild_id, type: 2 },
|
||||
order: { position: "ASC" },
|
||||
})
|
||||
).filter((doc) => {
|
||||
// Only return channels where @everyone has the CONNECT permission
|
||||
if (
|
||||
doc.permission_overwrites === undefined ||
|
||||
Permissions.channelPermission(
|
||||
doc.permission_overwrites,
|
||||
Permissions.FLAGS.CONNECT,
|
||||
) === Permissions.FLAGS.CONNECT
|
||||
) {
|
||||
channels.push({
|
||||
id: doc.id,
|
||||
name: doc.name ?? "Unknown channel",
|
||||
position: doc.position ?? 0,
|
||||
});
|
||||
invite = await Invite.create({
|
||||
code: random(),
|
||||
temporary: false,
|
||||
uses: 0,
|
||||
max_uses: 0,
|
||||
max_age: max_age,
|
||||
expires_at,
|
||||
created_at: new Date(),
|
||||
guild_id,
|
||||
channel_id: guild.widget_channel_id,
|
||||
}).save();
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch members
|
||||
// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
|
||||
const members = await Member.find({ where: { guild_id: guild_id } });
|
||||
// Fetch voice channels, and the @everyone permissions object
|
||||
const channels: { id: string; name: string; position: number }[] = [];
|
||||
|
||||
// Construct object to respond with
|
||||
const data = {
|
||||
id: guild_id,
|
||||
name: guild.name,
|
||||
instant_invite: invite?.code,
|
||||
channels: channels,
|
||||
members: members,
|
||||
presence_count: guild.presence_count,
|
||||
};
|
||||
(
|
||||
await Channel.find({
|
||||
where: { guild_id: guild_id, type: 2 },
|
||||
order: { position: "ASC" },
|
||||
})
|
||||
).filter((doc) => {
|
||||
// Only return channels where @everyone has the CONNECT permission
|
||||
if (
|
||||
doc.permission_overwrites === undefined ||
|
||||
Permissions.channelPermission(
|
||||
doc.permission_overwrites,
|
||||
Permissions.FLAGS.CONNECT,
|
||||
) === Permissions.FLAGS.CONNECT
|
||||
) {
|
||||
channels.push({
|
||||
id: doc.id,
|
||||
name: doc.name ?? "Unknown channel",
|
||||
position: doc.position ?? 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
res.set("Cache-Control", "public, max-age=300");
|
||||
return res.json(data);
|
||||
});
|
||||
// Fetch members
|
||||
// TODO: Understand how Discord's max 100 random member sample works, and apply to here (see top of this file)
|
||||
const members = await Member.find({ where: { guild_id: guild_id } });
|
||||
|
||||
// Construct object to respond with
|
||||
const data = {
|
||||
id: guild_id,
|
||||
name: guild.name,
|
||||
instant_invite: invite?.code,
|
||||
channels: channels,
|
||||
members: members,
|
||||
presence_count: guild.presence_count,
|
||||
};
|
||||
|
||||
res.set("Cache-Control", "public, max-age=300");
|
||||
return res.json(data);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -18,11 +18,11 @@
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Guild } from "@spacebar/util";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Guild } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fs from "fs";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import path from "path";
|
||||
|
||||
const router: Router = Router();
|
||||
@ -31,130 +31,178 @@ const router: Router = Router();
|
||||
|
||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget-image
|
||||
// TODO: Cache the response
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
if (!guild.widget_enabled) throw new HTTPError("Unknown Guild", 404);
|
||||
|
||||
// Fetch guild information
|
||||
const icon = guild.icon;
|
||||
const name = guild.name;
|
||||
const presence = guild.presence_count + " ONLINE";
|
||||
// Fetch guild information
|
||||
const icon = guild.icon;
|
||||
const name = guild.name;
|
||||
const presence = guild.presence_count + " ONLINE";
|
||||
|
||||
// Fetch parameter
|
||||
const style = req.query.style?.toString() || "shield";
|
||||
if (
|
||||
!["shield", "banner1", "banner2", "banner3", "banner4"].includes(style)
|
||||
) {
|
||||
throw new HTTPError(
|
||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
// Setup canvas
|
||||
const { createCanvas } = require("canvas");
|
||||
const { loadImage } = require("canvas");
|
||||
const sizeOf = require("image-size");
|
||||
|
||||
// TODO: Widget style templates need Spacebar branding
|
||||
const source = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"assets",
|
||||
"widget",
|
||||
`${style}.png`,
|
||||
);
|
||||
if (!fs.existsSync(source)) {
|
||||
throw new HTTPError("Widget template does not exist.", 400);
|
||||
}
|
||||
|
||||
// Create base template image for parameter
|
||||
const { width, height } = await sizeOf(source);
|
||||
const canvas = createCanvas(width, height);
|
||||
const ctx = canvas.getContext("2d");
|
||||
const template = await loadImage(source);
|
||||
ctx.drawImage(template, 0, 0);
|
||||
|
||||
// Add the guild specific information to the template asset image
|
||||
switch (style) {
|
||||
case "shield":
|
||||
ctx.textAlign = "center";
|
||||
await drawText(
|
||||
ctx,
|
||||
73,
|
||||
13,
|
||||
"#FFFFFF",
|
||||
"thin 10px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner1":
|
||||
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
||||
await drawText(ctx, 83, 51, "#FFFFFF", "12px Verdana", name, 22);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
66,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner2":
|
||||
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
||||
await drawText(ctx, 62, 34, "#FFFFFF", "12px Verdana", name, 15);
|
||||
await drawText(
|
||||
ctx,
|
||||
62,
|
||||
49,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner3":
|
||||
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
||||
await drawText(ctx, 83, 44, "#FFFFFF", "12px Verdana", name, 27);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
58,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner4":
|
||||
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
||||
await drawText(ctx, 84, 156, "#FFFFFF", "13px Verdana", name, 27);
|
||||
await drawText(
|
||||
ctx,
|
||||
84,
|
||||
171,
|
||||
"#C9D2F0FF",
|
||||
"thin 12px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// Fetch parameter
|
||||
const style = req.query.style?.toString() || "shield";
|
||||
if (
|
||||
!["shield", "banner1", "banner2", "banner3", "banner4"].includes(
|
||||
style,
|
||||
)
|
||||
) {
|
||||
throw new HTTPError(
|
||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
||||
400,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Return final image
|
||||
const buffer = canvas.toBuffer("image/png");
|
||||
res.set("Content-Type", "image/png");
|
||||
res.set("Cache-Control", "public, max-age=3600");
|
||||
return res.send(buffer);
|
||||
});
|
||||
// Setup canvas
|
||||
const { createCanvas } = require("canvas");
|
||||
const { loadImage } = require("canvas");
|
||||
const sizeOf = require("image-size");
|
||||
|
||||
// TODO: Widget style templates need Spacebar branding
|
||||
const source = path.join(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"assets",
|
||||
"widget",
|
||||
`${style}.png`,
|
||||
);
|
||||
if (!fs.existsSync(source)) {
|
||||
throw new HTTPError("Widget template does not exist.", 400);
|
||||
}
|
||||
|
||||
// Create base template image for parameter
|
||||
const { width, height } = await sizeOf(source);
|
||||
const canvas = createCanvas(width, height);
|
||||
const ctx = canvas.getContext("2d");
|
||||
const template = await loadImage(source);
|
||||
ctx.drawImage(template, 0, 0);
|
||||
|
||||
// Add the guild specific information to the template asset image
|
||||
switch (style) {
|
||||
case "shield":
|
||||
ctx.textAlign = "center";
|
||||
await drawText(
|
||||
ctx,
|
||||
73,
|
||||
13,
|
||||
"#FFFFFF",
|
||||
"thin 10px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner1":
|
||||
if (icon) await drawIcon(ctx, 20, 27, 50, icon);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
51,
|
||||
"#FFFFFF",
|
||||
"12px Verdana",
|
||||
name,
|
||||
22,
|
||||
);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
66,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner2":
|
||||
if (icon) await drawIcon(ctx, 13, 19, 36, icon);
|
||||
await drawText(
|
||||
ctx,
|
||||
62,
|
||||
34,
|
||||
"#FFFFFF",
|
||||
"12px Verdana",
|
||||
name,
|
||||
15,
|
||||
);
|
||||
await drawText(
|
||||
ctx,
|
||||
62,
|
||||
49,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner3":
|
||||
if (icon) await drawIcon(ctx, 20, 20, 50, icon);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
44,
|
||||
"#FFFFFF",
|
||||
"12px Verdana",
|
||||
name,
|
||||
27,
|
||||
);
|
||||
await drawText(
|
||||
ctx,
|
||||
83,
|
||||
58,
|
||||
"#C9D2F0FF",
|
||||
"thin 11px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
case "banner4":
|
||||
if (icon) await drawIcon(ctx, 21, 136, 50, icon);
|
||||
await drawText(
|
||||
ctx,
|
||||
84,
|
||||
156,
|
||||
"#FFFFFF",
|
||||
"13px Verdana",
|
||||
name,
|
||||
27,
|
||||
);
|
||||
await drawText(
|
||||
ctx,
|
||||
84,
|
||||
171,
|
||||
"#C9D2F0FF",
|
||||
"thin 12px Verdana",
|
||||
presence,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new HTTPError(
|
||||
"Value must be one of ('shield', 'banner1', 'banner2', 'banner3', 'banner4').",
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
// Return final image
|
||||
const buffer = canvas.toBuffer("image/png");
|
||||
res.set("Content-Type", "image/png");
|
||||
res.set("Cache-Control", "public, max-age=3600");
|
||||
return res.send(buffer);
|
||||
},
|
||||
);
|
||||
|
||||
async function drawIcon(
|
||||
canvas: any,
|
||||
|
@ -16,28 +16,55 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Guild, WidgetModifySchema } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Guild, WidgetModifySchema } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
// https://discord.com/developers/docs/resources/guild#get-guild-widget-settings
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "GuildWidgetSettingsResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { guild_id } = req.params;
|
||||
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
const guild = await Guild.findOneOrFail({ where: { id: guild_id } });
|
||||
|
||||
return res.json({
|
||||
enabled: guild.widget_enabled || false,
|
||||
channel_id: guild.widget_channel_id || null,
|
||||
});
|
||||
});
|
||||
return res.json({
|
||||
enabled: guild.widget_enabled || false,
|
||||
channel_id: guild.widget_channel_id || null,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// https://discord.com/developers/docs/resources/guild#modify-guild-widget
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "WidgetModifySchema", permission: "MANAGE_GUILD" }),
|
||||
route({
|
||||
requestBody: "WidgetModifySchema",
|
||||
permission: "MANAGE_GUILD",
|
||||
responses: {
|
||||
200: {
|
||||
body: "WidgetModifySchema",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as WidgetModifySchema;
|
||||
const { guild_id } = req.params;
|
||||
|
@ -16,16 +16,16 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import {
|
||||
Guild,
|
||||
Config,
|
||||
getRights,
|
||||
Member,
|
||||
DiscordApiErrors,
|
||||
GuildCreateSchema,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Config,
|
||||
DiscordApiErrors,
|
||||
Guild,
|
||||
GuildCreateSchema,
|
||||
Member,
|
||||
getRights,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
@ -33,7 +33,21 @@ const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }),
|
||||
route({
|
||||
requestBody: "GuildCreateSchema",
|
||||
right: "CREATE_GUILDS",
|
||||
responses: {
|
||||
201: {
|
||||
body: "GuildCreateResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as GuildCreateSchema;
|
||||
|
||||
|
@ -16,72 +16,91 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Template,
|
||||
Config,
|
||||
DiscordApiErrors,
|
||||
Guild,
|
||||
GuildTemplateCreateSchema,
|
||||
Member,
|
||||
Role,
|
||||
Snowflake,
|
||||
Config,
|
||||
Member,
|
||||
GuildTemplateCreateSchema,
|
||||
Template,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { DiscordApiErrors } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/:code", route({}), async (req: Request, res: Response) => {
|
||||
const { allowDiscordTemplates, allowRaws, enabled } =
|
||||
Config.get().templates;
|
||||
if (!enabled)
|
||||
res.json({
|
||||
code: 403,
|
||||
message: "Template creation & usage is disabled on this instance.",
|
||||
}).sendStatus(403);
|
||||
|
||||
const { code } = req.params;
|
||||
|
||||
if (code.startsWith("discord:")) {
|
||||
if (!allowDiscordTemplates)
|
||||
return res
|
||||
.json({
|
||||
code: 403,
|
||||
message:
|
||||
"Discord templates cannot be used on this instance.",
|
||||
})
|
||||
.sendStatus(403);
|
||||
const discordTemplateID = code.split("discord:", 2)[1];
|
||||
|
||||
const discordTemplateData = await fetch(
|
||||
`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`,
|
||||
{
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
router.get(
|
||||
"/:code",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Template",
|
||||
},
|
||||
);
|
||||
return res.json(await discordTemplateData.json());
|
||||
}
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { allowDiscordTemplates, allowRaws, enabled } =
|
||||
Config.get().templates;
|
||||
if (!enabled)
|
||||
res.json({
|
||||
code: 403,
|
||||
message:
|
||||
"Template creation & usage is disabled on this instance.",
|
||||
}).sendStatus(403);
|
||||
|
||||
if (code.startsWith("external:")) {
|
||||
if (!allowRaws)
|
||||
return res
|
||||
.json({
|
||||
code: 403,
|
||||
message: "Importing raws is disabled on this instance.",
|
||||
})
|
||||
.sendStatus(403);
|
||||
const { code } = req.params;
|
||||
|
||||
return res.json(code.split("external:", 2)[1]);
|
||||
}
|
||||
if (code.startsWith("discord:")) {
|
||||
if (!allowDiscordTemplates)
|
||||
return res
|
||||
.json({
|
||||
code: 403,
|
||||
message:
|
||||
"Discord templates cannot be used on this instance.",
|
||||
})
|
||||
.sendStatus(403);
|
||||
const discordTemplateID = code.split("discord:", 2)[1];
|
||||
|
||||
const template = await Template.findOneOrFail({ where: { code: code } });
|
||||
res.json(template);
|
||||
});
|
||||
const discordTemplateData = await fetch(
|
||||
`https://discord.com/api/v9/guilds/templates/${discordTemplateID}`,
|
||||
{
|
||||
method: "get",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
},
|
||||
);
|
||||
return res.json(await discordTemplateData.json());
|
||||
}
|
||||
|
||||
if (code.startsWith("external:")) {
|
||||
if (!allowRaws)
|
||||
return res
|
||||
.json({
|
||||
code: 403,
|
||||
message: "Importing raws is disabled on this instance.",
|
||||
})
|
||||
.sendStatus(403);
|
||||
|
||||
return res.json(code.split("external:", 2)[1]);
|
||||
}
|
||||
|
||||
const template = await Template.findOneOrFail({
|
||||
where: { code: code },
|
||||
});
|
||||
res.json(template);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:code",
|
||||
route({ body: "GuildTemplateCreateSchema" }),
|
||||
route({ requestBody: "GuildTemplateCreateSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const {
|
||||
enabled,
|
||||
|
@ -16,35 +16,64 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
emitEvent,
|
||||
getPermission,
|
||||
Guild,
|
||||
Invite,
|
||||
InviteDeleteEvent,
|
||||
User,
|
||||
PublicInviteRelation,
|
||||
User,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/:code", route({}), async (req: Request, res: Response) => {
|
||||
const { code } = req.params;
|
||||
router.get(
|
||||
"/:code",
|
||||
route({
|
||||
responses: {
|
||||
"200": {
|
||||
body: "Invite",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code } = req.params;
|
||||
|
||||
const invite = await Invite.findOneOrFail({
|
||||
where: { code },
|
||||
relations: PublicInviteRelation,
|
||||
});
|
||||
const invite = await Invite.findOneOrFail({
|
||||
where: { code },
|
||||
relations: PublicInviteRelation,
|
||||
});
|
||||
|
||||
res.status(200).send(invite);
|
||||
});
|
||||
res.status(200).send(invite);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:code",
|
||||
route({ right: "USE_MASS_INVITES" }),
|
||||
route({
|
||||
right: "USE_MASS_INVITES",
|
||||
responses: {
|
||||
"200": {
|
||||
body: "Invite",
|
||||
},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code } = req.params;
|
||||
const { guild_id } = await Invite.findOneOrFail({
|
||||
@ -75,33 +104,56 @@ router.post(
|
||||
);
|
||||
|
||||
// * cant use permission of route() function because path doesn't have guild_id/channel_id
|
||||
router.delete("/:code", route({}), async (req: Request, res: Response) => {
|
||||
const { code } = req.params;
|
||||
const invite = await Invite.findOneOrFail({ where: { code } });
|
||||
const { guild_id, channel_id } = invite;
|
||||
router.delete(
|
||||
"/:code",
|
||||
route({
|
||||
responses: {
|
||||
"200": {
|
||||
body: "Invite",
|
||||
},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { code } = req.params;
|
||||
const invite = await Invite.findOneOrFail({ where: { code } });
|
||||
const { guild_id, channel_id } = invite;
|
||||
|
||||
const permission = await getPermission(req.user_id, guild_id, channel_id);
|
||||
|
||||
if (!permission.has("MANAGE_GUILD") && !permission.has("MANAGE_CHANNELS"))
|
||||
throw new HTTPError(
|
||||
"You missing the MANAGE_GUILD or MANAGE_CHANNELS permission",
|
||||
401,
|
||||
const permission = await getPermission(
|
||||
req.user_id,
|
||||
guild_id,
|
||||
channel_id,
|
||||
);
|
||||
|
||||
await Promise.all([
|
||||
Invite.delete({ code }),
|
||||
emitEvent({
|
||||
event: "INVITE_DELETE",
|
||||
guild_id: guild_id,
|
||||
data: {
|
||||
channel_id: channel_id,
|
||||
guild_id: guild_id,
|
||||
code: code,
|
||||
},
|
||||
} as InviteDeleteEvent),
|
||||
]);
|
||||
if (
|
||||
!permission.has("MANAGE_GUILD") &&
|
||||
!permission.has("MANAGE_CHANNELS")
|
||||
)
|
||||
throw new HTTPError(
|
||||
"You missing the MANAGE_GUILD or MANAGE_CHANNELS permission",
|
||||
401,
|
||||
);
|
||||
|
||||
res.json({ invite: invite });
|
||||
});
|
||||
await Promise.all([
|
||||
Invite.delete({ code }),
|
||||
emitEvent({
|
||||
event: "INVITE_DELETE",
|
||||
guild_id: guild_id,
|
||||
data: {
|
||||
channel_id: channel_id,
|
||||
guild_id: guild_id,
|
||||
code: code,
|
||||
},
|
||||
} as InviteDeleteEvent),
|
||||
]);
|
||||
|
||||
res.json({ invite: invite });
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,126 +16,168 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
ApiError,
|
||||
Application,
|
||||
ApplicationAuthorizeSchema,
|
||||
getPermission,
|
||||
DiscordApiErrors,
|
||||
Member,
|
||||
Permissions,
|
||||
User,
|
||||
getPermission,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
// TODO: scopes, other oauth types
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
// const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
const { client_id } = req.query;
|
||||
|
||||
const app = await Application.findOne({
|
||||
where: {
|
||||
id: client_id as string,
|
||||
},
|
||||
relations: ["bot"],
|
||||
});
|
||||
|
||||
// TODO: use DiscordApiErrors
|
||||
// findOneOrFail throws code 404
|
||||
if (!app) throw DiscordApiErrors.UNKNOWN_APPLICATION;
|
||||
if (!app.bot) throw DiscordApiErrors.OAUTH2_APPLICATION_BOT_ABSENT;
|
||||
|
||||
const bot = app.bot;
|
||||
delete app.bot;
|
||||
|
||||
const user = await User.findOneOrFail({
|
||||
where: {
|
||||
id: req.user_id,
|
||||
bot: false,
|
||||
},
|
||||
select: ["id", "username", "avatar", "discriminator", "public_flags"],
|
||||
});
|
||||
|
||||
const guilds = await Member.find({
|
||||
where: {
|
||||
user: {
|
||||
id: req.user_id,
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
// TODO: I really didn't feel like typing all of it out
|
||||
200: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
relations: ["guild", "roles"],
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
// prettier-ignore
|
||||
select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"],
|
||||
});
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
const { client_id } = req.query;
|
||||
|
||||
const guildsWithPermissions = guilds.map((x) => {
|
||||
const perms =
|
||||
x.guild.owner_id === user.id
|
||||
? new Permissions(Permissions.FLAGS.ADMINISTRATOR)
|
||||
: Permissions.finalPermission({
|
||||
user: {
|
||||
id: user.id,
|
||||
roles: x.roles?.map((x) => x.id) || [],
|
||||
},
|
||||
guild: {
|
||||
roles: x?.roles || [],
|
||||
},
|
||||
});
|
||||
const app = await Application.findOne({
|
||||
where: {
|
||||
id: client_id as string,
|
||||
},
|
||||
relations: ["bot"],
|
||||
});
|
||||
|
||||
return {
|
||||
id: x.guild.id,
|
||||
name: x.guild.name,
|
||||
icon: x.guild.icon,
|
||||
mfa_level: x.guild.mfa_level,
|
||||
permissions: perms.bitfield.toString(),
|
||||
};
|
||||
});
|
||||
// TODO: use DiscordApiErrors
|
||||
// findOneOrFail throws code 404
|
||||
if (!app) throw DiscordApiErrors.UNKNOWN_APPLICATION;
|
||||
if (!app.bot) throw DiscordApiErrors.OAUTH2_APPLICATION_BOT_ABSENT;
|
||||
|
||||
return res.json({
|
||||
guilds: guildsWithPermissions,
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
avatar: user.avatar,
|
||||
avatar_decoration: null, // TODO
|
||||
discriminator: user.discriminator,
|
||||
public_flags: user.public_flags,
|
||||
},
|
||||
application: {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
icon: app.icon,
|
||||
description: app.description,
|
||||
summary: app.summary,
|
||||
type: app.type,
|
||||
hook: app.hook,
|
||||
guild_id: null, // TODO support guilds
|
||||
bot_public: app.bot_public,
|
||||
bot_require_code_grant: app.bot_require_code_grant,
|
||||
verify_key: app.verify_key,
|
||||
flags: app.flags,
|
||||
},
|
||||
bot: {
|
||||
id: bot.id,
|
||||
username: bot.username,
|
||||
avatar: bot.avatar,
|
||||
avatar_decoration: null, // TODO
|
||||
discriminator: bot.discriminator,
|
||||
public_flags: bot.public_flags,
|
||||
bot: true,
|
||||
approximated_guild_count: 0, // TODO
|
||||
},
|
||||
authorized: false,
|
||||
});
|
||||
});
|
||||
const bot = app.bot;
|
||||
delete app.bot;
|
||||
|
||||
const user = await User.findOneOrFail({
|
||||
where: {
|
||||
id: req.user_id,
|
||||
bot: false,
|
||||
},
|
||||
select: [
|
||||
"id",
|
||||
"username",
|
||||
"avatar",
|
||||
"discriminator",
|
||||
"public_flags",
|
||||
],
|
||||
});
|
||||
|
||||
const guilds = await Member.find({
|
||||
where: {
|
||||
user: {
|
||||
id: req.user_id,
|
||||
},
|
||||
},
|
||||
relations: ["guild", "roles"],
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
// prettier-ignore
|
||||
select: ["guild.id", "guild.name", "guild.icon", "guild.mfa_level", "guild.owner_id", "roles.id"],
|
||||
});
|
||||
|
||||
const guildsWithPermissions = guilds.map((x) => {
|
||||
const perms =
|
||||
x.guild.owner_id === user.id
|
||||
? new Permissions(Permissions.FLAGS.ADMINISTRATOR)
|
||||
: Permissions.finalPermission({
|
||||
user: {
|
||||
id: user.id,
|
||||
roles: x.roles?.map((x) => x.id) || [],
|
||||
},
|
||||
guild: {
|
||||
roles: x?.roles || [],
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: x.guild.id,
|
||||
name: x.guild.name,
|
||||
icon: x.guild.icon,
|
||||
mfa_level: x.guild.mfa_level,
|
||||
permissions: perms.bitfield.toString(),
|
||||
};
|
||||
});
|
||||
|
||||
return res.json({
|
||||
guilds: guildsWithPermissions,
|
||||
user: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
avatar: user.avatar,
|
||||
avatar_decoration: null, // TODO
|
||||
discriminator: user.discriminator,
|
||||
public_flags: user.public_flags,
|
||||
},
|
||||
application: {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
icon: app.icon,
|
||||
description: app.description,
|
||||
summary: app.summary,
|
||||
type: app.type,
|
||||
hook: app.hook,
|
||||
guild_id: null, // TODO support guilds
|
||||
bot_public: app.bot_public,
|
||||
bot_require_code_grant: app.bot_require_code_grant,
|
||||
verify_key: app.verify_key,
|
||||
flags: app.flags,
|
||||
},
|
||||
bot: {
|
||||
id: bot.id,
|
||||
username: bot.username,
|
||||
avatar: bot.avatar,
|
||||
avatar_decoration: null, // TODO
|
||||
discriminator: bot.discriminator,
|
||||
public_flags: bot.public_flags,
|
||||
bot: true,
|
||||
approximated_guild_count: 0, // TODO
|
||||
},
|
||||
authorized: false,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "ApplicationAuthorizeSchema" }),
|
||||
route({
|
||||
requestBody: "ApplicationAuthorizeSchema",
|
||||
query: {
|
||||
client_id: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
body: "OAuthAuthorizeResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as ApplicationAuthorizeSchema;
|
||||
// const { client_id, scope, response_type, redirect_url } = req.query;
|
||||
|
@ -16,29 +16,39 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), (req: Request, res: Response) => {
|
||||
const { general } = Config.get();
|
||||
res.send({
|
||||
ping: "pong!",
|
||||
instance: {
|
||||
id: general.instanceId,
|
||||
name: general.instanceName,
|
||||
description: general.instanceDescription,
|
||||
image: general.image,
|
||||
|
||||
correspondenceEmail: general.correspondenceEmail,
|
||||
correspondenceUserID: general.correspondenceUserID,
|
||||
|
||||
frontPage: general.frontPage,
|
||||
tosPage: general.tosPage,
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "InstancePingResponse",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
const { general } = Config.get();
|
||||
res.send({
|
||||
ping: "pong!",
|
||||
instance: {
|
||||
id: general.instanceId,
|
||||
name: general.instanceName,
|
||||
description: general.instanceDescription,
|
||||
image: general.image,
|
||||
|
||||
correspondenceEmail: general.correspondenceEmail,
|
||||
correspondenceUserID: general.correspondenceUserID,
|
||||
|
||||
frontPage: general.frontPage,
|
||||
tosPage: general.tosPage,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,25 +16,38 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { cdn, gateway, api } = Config.get();
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "InstanceDomainsResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { cdn, gateway, api } = Config.get();
|
||||
|
||||
const IdentityForm = {
|
||||
cdn: cdn.endpointPublic || process.env.CDN || "http://localhost:3001",
|
||||
gateway:
|
||||
gateway.endpointPublic ||
|
||||
process.env.GATEWAY ||
|
||||
"ws://localhost:3001",
|
||||
defaultApiVersion: api.defaultVersion ?? 9,
|
||||
apiEndpoint: api.endpointPublic ?? "http://localhost:3001/api/",
|
||||
};
|
||||
const IdentityForm = {
|
||||
cdn:
|
||||
cdn.endpointPublic ||
|
||||
process.env.CDN ||
|
||||
"http://localhost:3001",
|
||||
gateway:
|
||||
gateway.endpointPublic ||
|
||||
process.env.GATEWAY ||
|
||||
"ws://localhost:3001",
|
||||
defaultApiVersion: api.defaultVersion ?? 9,
|
||||
apiEndpoint: api.endpointPublic ?? "http://localhost:3001/api/",
|
||||
};
|
||||
|
||||
res.json(IdentityForm);
|
||||
});
|
||||
res.json(IdentityForm);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,14 +16,24 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { general } = Config.get();
|
||||
res.json(general);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIGeneralConfiguration",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { general } = Config.get();
|
||||
res.json(general);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,14 +16,24 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Config } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { limits } = Config.get();
|
||||
res.json(limits);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APILimitsConfiguration",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { limits } = Config.get();
|
||||
res.json(limits);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -28,20 +28,33 @@ import {
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
if (!Config.get().security.statsWorldReadable) {
|
||||
const rights = await getRights(req.user_id);
|
||||
rights.hasThrow("VIEW_SERVER_STATS");
|
||||
}
|
||||
|
||||
res.json({
|
||||
counts: {
|
||||
user: await User.count(),
|
||||
guild: await Guild.count(),
|
||||
message: await Message.count(),
|
||||
members: await Member.count(),
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "InstanceStatsResponse",
|
||||
},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
if (!Config.get().security.statsWorldReadable) {
|
||||
const rights = await getRights(req.user_id);
|
||||
rights.hasThrow("VIEW_SERVER_STATS");
|
||||
}
|
||||
|
||||
res.json({
|
||||
counts: {
|
||||
user: await User.count(),
|
||||
guild: await Guild.count(),
|
||||
message: await Message.count(),
|
||||
members: await Member.count(),
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,14 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { AckBulkSchema, ReadState } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "AckBulkSchema" }),
|
||||
route({
|
||||
requestBody: "AckBulkSchema",
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as AckBulkSchema;
|
||||
|
||||
|
@ -16,14 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", route({}), (req: Request, res: Response) => {
|
||||
// TODO:
|
||||
res.sendStatus(204);
|
||||
});
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
},
|
||||
}),
|
||||
(req: Request, res: Response) => {
|
||||
// TODO:
|
||||
res.sendStatus(204);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,16 +16,28 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { StickerPack } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const sticker_packs = await StickerPack.find({ relations: ["stickers"] });
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIStickerPackArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const sticker_packs = await StickerPack.find({
|
||||
relations: ["stickers"],
|
||||
});
|
||||
|
||||
res.json({ sticker_packs });
|
||||
});
|
||||
res.json({ sticker_packs });
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,15 +16,25 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Sticker } from "@spacebar/util";
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Sticker } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { sticker_id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "Sticker",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { sticker_id } = req.params;
|
||||
|
||||
res.json(await Sticker.find({ where: { id: sticker_id } }));
|
||||
});
|
||||
res.json(await Sticker.find({ where: { id: sticker_id } }));
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,14 +16,22 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ right: "OPERATOR" }),
|
||||
route({
|
||||
right: "OPERATOR",
|
||||
responses: {
|
||||
200: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
console.log(`/stop was called by ${req.user_id} at ${new Date()}`);
|
||||
res.sendStatus(200);
|
||||
|
@ -16,37 +16,53 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { FieldErrors, Release } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const platform = req.query.platform;
|
||||
|
||||
if (!platform)
|
||||
throw FieldErrors({
|
||||
platform: {
|
||||
code: "BASE_TYPE_REQUIRED",
|
||||
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "UpdatesResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const platform = req.query.platform;
|
||||
|
||||
if (!platform)
|
||||
throw FieldErrors({
|
||||
platform: {
|
||||
code: "BASE_TYPE_REQUIRED",
|
||||
message: req.t("common:field.BASE_TYPE_REQUIRED"),
|
||||
},
|
||||
});
|
||||
|
||||
const release = await Release.findOneOrFail({
|
||||
where: {
|
||||
enabled: true,
|
||||
platform: platform as string,
|
||||
},
|
||||
order: { pub_date: "DESC" },
|
||||
});
|
||||
|
||||
const release = await Release.findOneOrFail({
|
||||
where: {
|
||||
enabled: true,
|
||||
platform: platform as string,
|
||||
},
|
||||
order: { pub_date: "DESC" },
|
||||
});
|
||||
|
||||
res.json({
|
||||
name: release.name,
|
||||
pub_date: release.pub_date,
|
||||
url: release.url,
|
||||
notes: release.notes,
|
||||
});
|
||||
});
|
||||
res.json({
|
||||
name: release.name,
|
||||
pub_date: release.pub_date,
|
||||
url: release.url,
|
||||
notes: release.notes,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -30,7 +30,18 @@ const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ right: "MANAGE_USERS" }),
|
||||
route({
|
||||
right: "MANAGE_USERS",
|
||||
responses: {
|
||||
204: {},
|
||||
403: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
await User.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
|
@ -16,16 +16,26 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { User } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { User } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIPublicUser",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
|
||||
res.json(await User.getPublicUser(id));
|
||||
});
|
||||
res.json(await User.getPublicUser(id));
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,23 +16,23 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import {
|
||||
User,
|
||||
Member,
|
||||
UserProfileModifySchema,
|
||||
handleFile,
|
||||
PrivateUserProjection,
|
||||
emitEvent,
|
||||
UserUpdateEvent,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Member,
|
||||
PrivateUserProjection,
|
||||
User,
|
||||
UserProfileModifySchema,
|
||||
UserUpdateEvent,
|
||||
emitEvent,
|
||||
handleFile,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ test: { response: { body: "UserProfileResponse" } } }),
|
||||
route({ responses: { 200: { body: "UserProfileResponse" } } }),
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.params.id === "@me") req.params.id = req.user_id;
|
||||
|
||||
@ -151,7 +151,7 @@ router.get(
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "UserProfileModifySchema" }),
|
||||
route({ requestBody: "UserProfileModifySchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as UserProfileModifySchema;
|
||||
|
||||
|
@ -16,17 +16,25 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { User } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { User, UserRelationsResponse } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get(
|
||||
"/",
|
||||
route({ test: { response: { body: "UserRelationsResponse" } } }),
|
||||
route({
|
||||
responses: {
|
||||
200: { body: "UserRelationsResponse" },
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const mutual_relations: object[] = [];
|
||||
const mutual_relations: UserRelationsResponse = [];
|
||||
|
||||
const requested_relations = await User.findOneOrFail({
|
||||
where: { id: req.params.id },
|
||||
relations: ["relationships"],
|
||||
|
@ -16,32 +16,51 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Recipient,
|
||||
DmChannelDTO,
|
||||
Channel,
|
||||
DmChannelCreateSchema,
|
||||
DmChannelDTO,
|
||||
Recipient,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const recipients = await Recipient.find({
|
||||
where: { user_id: req.user_id, closed: false },
|
||||
relations: ["channel", "channel.recipients"],
|
||||
});
|
||||
res.json(
|
||||
await Promise.all(
|
||||
recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id])),
|
||||
),
|
||||
);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIDMChannelArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const recipients = await Recipient.find({
|
||||
where: { user_id: req.user_id, closed: false },
|
||||
relations: ["channel", "channel.recipients"],
|
||||
});
|
||||
res.json(
|
||||
await Promise.all(
|
||||
recipients.map((r) =>
|
||||
DmChannelDTO.from(r.channel, [req.user_id]),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "DmChannelCreateSchema" }),
|
||||
route({
|
||||
requestBody: "DmChannelCreateSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "DmChannelDTO",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as DmChannelCreateSchema;
|
||||
res.json(
|
||||
|
@ -29,7 +29,7 @@ const router = Router();
|
||||
// TODO: connection update schema
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "ConnectionUpdateSchema" }),
|
||||
route({ requestBody: "ConnectionUpdateSchema" }),
|
||||
async (req: Request, res: Response) => {
|
||||
const { connection_name, connection_id } = req.params;
|
||||
const body = req.body as ConnectionUpdateSchema;
|
||||
|
@ -16,41 +16,58 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { Member, User } from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Member, User } from "@spacebar/util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
select: ["data"],
|
||||
}); //User object
|
||||
let correctpass = true;
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
401: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
select: ["data"],
|
||||
}); //User object
|
||||
let correctpass = true;
|
||||
|
||||
if (user.data.hash) {
|
||||
// guest accounts can delete accounts without password
|
||||
correctpass = await bcrypt.compare(req.body.password, user.data.hash);
|
||||
if (!correctpass) {
|
||||
throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
|
||||
if (user.data.hash) {
|
||||
// guest accounts can delete accounts without password
|
||||
correctpass = await bcrypt.compare(
|
||||
req.body.password,
|
||||
user.data.hash,
|
||||
);
|
||||
if (!correctpass) {
|
||||
throw new HTTPError(req.t("auth:login.INVALID_PASSWORD"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: decrement guild member count
|
||||
// TODO: decrement guild member count
|
||||
|
||||
if (correctpass) {
|
||||
await Promise.all([
|
||||
User.delete({ id: req.user_id }),
|
||||
Member.delete({ id: req.user_id }),
|
||||
]);
|
||||
if (correctpass) {
|
||||
await Promise.all([
|
||||
User.delete({ id: req.user_id }),
|
||||
Member.delete({ id: req.user_id }),
|
||||
]);
|
||||
|
||||
res.sendStatus(204);
|
||||
} else {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
});
|
||||
res.sendStatus(204);
|
||||
} else {
|
||||
res.sendStatus(401);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,35 +16,52 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { User } from "@spacebar/util";
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import { User } from "@spacebar/util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/", route({}), async (req: Request, res: Response) => {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
select: ["data"],
|
||||
}); //User object
|
||||
let correctpass = true;
|
||||
router.post(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const user = await User.findOneOrFail({
|
||||
where: { id: req.user_id },
|
||||
select: ["data"],
|
||||
}); //User object
|
||||
let correctpass = true;
|
||||
|
||||
if (user.data.hash) {
|
||||
// guest accounts can delete accounts without password
|
||||
correctpass = await bcrypt.compare(req.body.password, user.data.hash); //Not sure if user typed right password :/
|
||||
}
|
||||
if (user.data.hash) {
|
||||
// guest accounts can delete accounts without password
|
||||
correctpass = await bcrypt.compare(
|
||||
req.body.password,
|
||||
user.data.hash,
|
||||
); //Not sure if user typed right password :/
|
||||
}
|
||||
|
||||
if (correctpass) {
|
||||
await User.update({ id: req.user_id }, { disabled: true });
|
||||
if (correctpass) {
|
||||
await User.update({ id: req.user_id }, { disabled: true });
|
||||
|
||||
res.sendStatus(204);
|
||||
} else {
|
||||
res.status(400).json({
|
||||
message: "Password does not match",
|
||||
code: 50018,
|
||||
});
|
||||
}
|
||||
});
|
||||
res.sendStatus(204);
|
||||
} else {
|
||||
res.status(400).json({
|
||||
message: "Password does not match",
|
||||
code: 50018,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,79 +16,106 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Config,
|
||||
Guild,
|
||||
Member,
|
||||
User,
|
||||
GuildDeleteEvent,
|
||||
GuildMemberRemoveEvent,
|
||||
Member,
|
||||
User,
|
||||
emitEvent,
|
||||
Config,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { route } from "@spacebar/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const members = await Member.find({
|
||||
relations: ["guild"],
|
||||
where: { id: req.user_id },
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIGuildArray",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const members = await Member.find({
|
||||
relations: ["guild"],
|
||||
where: { id: req.user_id },
|
||||
});
|
||||
|
||||
let guild = members.map((x) => x.guild);
|
||||
let guild = members.map((x) => x.guild);
|
||||
|
||||
if ("with_counts" in req.query && req.query.with_counts == "true") {
|
||||
guild = []; // TODO: Load guilds with user role permissions number
|
||||
}
|
||||
if ("with_counts" in req.query && req.query.with_counts == "true") {
|
||||
guild = []; // TODO: Load guilds with user role permissions number
|
||||
}
|
||||
|
||||
res.json(guild);
|
||||
});
|
||||
res.json(guild);
|
||||
},
|
||||
);
|
||||
|
||||
// user send to leave a certain guild
|
||||
router.delete("/:guild_id", route({}), async (req: Request, res: Response) => {
|
||||
const { autoJoin } = Config.get().guild;
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
select: ["owner_id"],
|
||||
});
|
||||
|
||||
if (!guild) throw new HTTPError("Guild doesn't exist", 404);
|
||||
if (guild.owner_id === req.user_id)
|
||||
throw new HTTPError("You can't leave your own guild", 400);
|
||||
if (
|
||||
autoJoin.enabled &&
|
||||
autoJoin.guilds.includes(guild_id) &&
|
||||
!autoJoin.canLeave
|
||||
) {
|
||||
throw new HTTPError("You can't leave instance auto join guilds", 400);
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
Member.delete({ id: req.user_id, guild_id: guild_id }),
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guild_id,
|
||||
router.delete(
|
||||
"/:guild_id",
|
||||
route({
|
||||
responses: {
|
||||
204: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
user_id: req.user_id,
|
||||
} as GuildDeleteEvent),
|
||||
]);
|
||||
|
||||
const user = await User.getPublicUser(req.user_id);
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_MEMBER_REMOVE",
|
||||
data: {
|
||||
guild_id: guild_id,
|
||||
user: user,
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildMemberRemoveEvent);
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const { autoJoin } = Config.get().guild;
|
||||
const { guild_id } = req.params;
|
||||
const guild = await Guild.findOneOrFail({
|
||||
where: { id: guild_id },
|
||||
select: ["owner_id"],
|
||||
});
|
||||
|
||||
return res.sendStatus(204);
|
||||
});
|
||||
if (!guild) throw new HTTPError("Guild doesn't exist", 404);
|
||||
if (guild.owner_id === req.user_id)
|
||||
throw new HTTPError("You can't leave your own guild", 400);
|
||||
if (
|
||||
autoJoin.enabled &&
|
||||
autoJoin.guilds.includes(guild_id) &&
|
||||
!autoJoin.canLeave
|
||||
) {
|
||||
throw new HTTPError(
|
||||
"You can't leave instance auto join guilds",
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
Member.delete({ id: req.user_id, guild_id: guild_id }),
|
||||
emitEvent({
|
||||
event: "GUILD_DELETE",
|
||||
data: {
|
||||
id: guild_id,
|
||||
},
|
||||
user_id: req.user_id,
|
||||
} as GuildDeleteEvent),
|
||||
]);
|
||||
|
||||
const user = await User.getPublicUser(req.user_id);
|
||||
|
||||
await emitEvent({
|
||||
event: "GUILD_MEMBER_REMOVE",
|
||||
data: {
|
||||
guild_id: guild_id,
|
||||
user: user,
|
||||
},
|
||||
guild_id: guild_id,
|
||||
} as GuildMemberRemoveEvent);
|
||||
|
||||
return res.sendStatus(204);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -16,29 +16,49 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Response, Request } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Channel,
|
||||
Member,
|
||||
OrmUtils,
|
||||
UserGuildSettingsSchema,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// GET doesn't exist on discord.com
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
const user = await Member.findOneOrFail({
|
||||
where: { id: req.user_id, guild_id: req.params.guild_id },
|
||||
select: ["settings"],
|
||||
});
|
||||
return res.json(user.settings);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {},
|
||||
404: {},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const user = await Member.findOneOrFail({
|
||||
where: { id: req.user_id, guild_id: req.params.guild_id },
|
||||
select: ["settings"],
|
||||
});
|
||||
return res.json(user.settings);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "UserGuildSettingsSchema" }),
|
||||
route({
|
||||
requestBody: "UserGuildSettingsSchema",
|
||||
responses: {
|
||||
200: {},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as UserGuildSettingsSchema;
|
||||
|
||||
|
@ -16,36 +16,59 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
User,
|
||||
PrivateUserProjection,
|
||||
emitEvent,
|
||||
UserUpdateEvent,
|
||||
handleFile,
|
||||
FieldErrors,
|
||||
adjustEmail,
|
||||
Config,
|
||||
UserModifySchema,
|
||||
emitEvent,
|
||||
FieldErrors,
|
||||
generateToken,
|
||||
handleFile,
|
||||
PrivateUserProjection,
|
||||
User,
|
||||
UserModifySchema,
|
||||
UserUpdateEvent,
|
||||
} from "@spacebar/util";
|
||||
import { route } from "@spacebar/api";
|
||||
import bcrypt from "bcrypt";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
res.json(
|
||||
await User.findOne({
|
||||
select: PrivateUserProjection,
|
||||
where: { id: req.user_id },
|
||||
}),
|
||||
);
|
||||
});
|
||||
router.get(
|
||||
"/",
|
||||
route({
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIPrivateUser",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
res.json(
|
||||
await User.findOne({
|
||||
select: PrivateUserProjection,
|
||||
where: { id: req.user_id },
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/",
|
||||
route({ body: "UserModifySchema" }),
|
||||
route({
|
||||
requestBody: "UserModifySchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "UserUpdateResponse",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
const body = req.body as UserModifySchema;
|
||||
|
||||
|
@ -16,21 +16,34 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from "express";
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
BackupCode,
|
||||
generateMfaBackupCodes,
|
||||
User,
|
||||
CodesVerificationSchema,
|
||||
DiscordApiErrors,
|
||||
User,
|
||||
generateMfaBackupCodes,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post(
|
||||
"/",
|
||||
route({ body: "CodesVerificationSchema" }),
|
||||
route({
|
||||
requestBody: "CodesVerificationSchema",
|
||||
responses: {
|
||||
200: {
|
||||
body: "APIBackupCodeArray",
|
||||
},
|
||||
400: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
404: {
|
||||
body: "APIErrorResponse",
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (req: Request, res: Response) => {
|
||||
// const { key, nonce, regenerate } = req.body as CodesVerificationSchema;
|
||||
const { regenerate } = req.body as CodesVerificationSchema;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user