mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-09 12:12:35 +01:00
Rewrite getRouteDescriptions, fix message route not appearing in openapi spec
This commit is contained in:
parent
a263ebb1e5
commit
b438f2b071
@ -14646,6 +14646,147 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/channels/{channel_id}/messages/": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/APIMessageArray"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/APIErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "No description available"
|
||||
},
|
||||
"404": {
|
||||
"description": "No description available"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channel_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "channel_id"
|
||||
},
|
||||
{
|
||||
"name": "around",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
},
|
||||
"description": "max number of messages to return (1-100). defaults to 50"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"channels"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"x-right-required": "SEND_MESSAGES",
|
||||
"x-permission-required": "SEND_MESSAGES",
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/MessageCreateSchema"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Message"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/APIErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "No description available"
|
||||
},
|
||||
"404": {
|
||||
"description": "No description available"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "channel_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "channel_id"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"channels"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/channels/{channel_id}/messages/bulk-delete/": {
|
||||
"post": {
|
||||
"security": [
|
||||
|
@ -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) {
|
||||
console.log(prefix + path + " - " + method);
|
||||
opts.file = file.replace("/dist/", "/src/").replace(".js", ".ts");
|
||||
routes.set(prefix + path + "|" + method, opts());
|
||||
};
|
||||
|
||||
express.Router = () => {
|
||||
return Object.fromEntries(
|
||||
methods.map((method) => [
|
||||
method,
|
||||
proxy.bind(null, currentFile, method, currentPath),
|
||||
]),
|
||||
);
|
||||
};
|
||||
|
||||
RouteUtility.route = (opts) => {
|
||||
const func = function () {
|
||||
return opts;
|
||||
}
|
||||
|
||||
RouteUtility.route = routeOptions;
|
||||
|
||||
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;
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
@ -25,10 +25,10 @@ import {
|
||||
PublicInviteRelation,
|
||||
User,
|
||||
emitEvent,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { isTextChannel } from "./messages";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
User,
|
||||
emitEvent,
|
||||
getPermission,
|
||||
isTextChannel,
|
||||
uploadFile,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
@ -45,32 +46,6 @@ 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(
|
||||
@ -407,3 +382,5 @@ router.post(
|
||||
return res.json(message);
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
@ -25,11 +25,11 @@ import {
|
||||
emitEvent,
|
||||
getPermission,
|
||||
getRights,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { Between, FindManyOptions, FindOperator, Not } from "typeorm";
|
||||
import { isTextChannel } from "./messages";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
|
@ -27,11 +27,11 @@ import {
|
||||
WebhookType,
|
||||
handleFile,
|
||||
trimSpecial,
|
||||
isTextChannel,
|
||||
} from "@spacebar/util";
|
||||
import crypto from "crypto";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { isTextChannel } from "./messages/index";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
|
@ -17,11 +17,10 @@
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import { TenorMediaTypes } from "@spacebar/util";
|
||||
import { TenorMediaTypes, getGifApiKey, parseGifResult } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { getGifApiKey, parseGifResult } from "./trending";
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
@ -17,11 +17,10 @@
|
||||
*/
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import { TenorMediaTypes } from "@spacebar/util";
|
||||
import { TenorMediaTypes, getGifApiKey, parseGifResult } from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
import { getGifApiKey, parseGifResult } from "./trending";
|
||||
|
||||
const router = Router();
|
||||
|
||||
|
@ -18,40 +18,17 @@
|
||||
|
||||
import { route } from "@spacebar/api";
|
||||
import {
|
||||
Config,
|
||||
TenorCategoriesResults,
|
||||
TenorGif,
|
||||
TenorTrendingResults,
|
||||
getGifApiKey,
|
||||
parseGifResult,
|
||||
} from "@spacebar/util";
|
||||
import { Request, Response, Router } from "express";
|
||||
import { HTTPError } from "lambert-server";
|
||||
import fetch from "node-fetch";
|
||||
import ProxyAgent from "proxy-agent";
|
||||
|
||||
const router = Router();
|
||||
|
||||
export function parseGifResult(result: TenorGif) {
|
||||
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({
|
||||
|
@ -23,7 +23,7 @@ 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,
|
||||
|
@ -105,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;
|
||||
|
@ -482,3 +482,27 @@ export enum ChannelPermissionOverwriteType {
|
||||
member = 1,
|
||||
group = 2,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
25
src/util/util/Gifs.ts
Normal file
25
src/util/util/Gifs.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { HTTPError } from "lambert-server";
|
||||
import { Config } from "./Config";
|
||||
import { TenorGif } from "..";
|
||||
|
||||
export function parseGifResult(result: TenorGif) {
|
||||
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;
|
||||
}
|
@ -41,3 +41,4 @@ export * from "./String";
|
||||
export * from "./Token";
|
||||
export * from "./TraverseDirectory";
|
||||
export * from "./WebAuthn";
|
||||
export * from "./Gifs";
|
||||
|
Loading…
Reference in New Issue
Block a user