mirror of
https://github.com/spacebarchat/server.git
synced 2024-11-05 10:22:31 +01:00
✨ generate openapi documentation
This commit is contained in:
parent
eb2f447d96
commit
2a094c603a
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
{
|
||||
"UserProfileResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user": {
|
||||
"$ref": "#/definitions/UserPublic"
|
||||
},
|
||||
"connected_accounts": {
|
||||
"$ref": "#/definitions/PublicConnectedAccount"
|
||||
},
|
||||
"premium_guild_since": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"premium_since": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"connected_accounts",
|
||||
"user"
|
||||
],
|
||||
"definitions": {
|
||||
"UserPublic": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"discriminator": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"public_flags": {
|
||||
"type": "string"
|
||||
},
|
||||
"avatar": {
|
||||
"type": "string"
|
||||
},
|
||||
"accent_color": {
|
||||
"type": "integer"
|
||||
},
|
||||
"banner": {
|
||||
"type": "string"
|
||||
},
|
||||
"bio": {
|
||||
"type": "string"
|
||||
},
|
||||
"bot": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"bio",
|
||||
"bot",
|
||||
"discriminator",
|
||||
"id",
|
||||
"public_flags",
|
||||
"username"
|
||||
]
|
||||
},
|
||||
"PublicConnectedAccount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"verifie": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name",
|
||||
"type",
|
||||
"verifie"
|
||||
]
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,15 @@
|
||||
import { traverseDirectory } from "lambert-server";
|
||||
import path from "path";
|
||||
import express from "express";
|
||||
import * as RouteUtility from "../dist/util/route";
|
||||
import { RouteOptions } from "../dist/util/route";
|
||||
const { traverseDirectory } = require("lambert-server");
|
||||
const path = require("path");
|
||||
const express = require("express");
|
||||
const RouteUtility = require("../dist/util/route");
|
||||
const Router = express.Router;
|
||||
|
||||
const routes = new Map<string, RouteUtility.RouteOptions>();
|
||||
/**
|
||||
* Some documentation.
|
||||
*
|
||||
* @type {Map<string, RouteUtility.RouteOptions>}
|
||||
*/
|
||||
const routes = new Map();
|
||||
let currentPath = "";
|
||||
let currentFile = "";
|
||||
const methods = ["get", "post", "put", "delete", "patch"];
|
||||
@ -13,13 +17,13 @@ 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: RouteOptions = args.find((x) => typeof x === "object");
|
||||
const opts = args.find((x) => typeof x === "object");
|
||||
if (opts) {
|
||||
routes.set(urlPath + "|" + method, opts); // @ts-ignore
|
||||
opts.file = sourceFile;
|
||||
// console.log(method, urlPath, opts);
|
||||
} else {
|
||||
console.log(`${sourceFile}\nrouter.${method}("${path}") is missing the "route()" description middleware\n`, args);
|
||||
console.log(`${sourceFile}\nrouter.${method}("${path}") is missing the "route()" description middleware\n`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +46,7 @@ express.Router = (opts) => {
|
||||
return router;
|
||||
};
|
||||
|
||||
export default function getRouteDescriptions() {
|
||||
module.exports = function getRouteDescriptions() {
|
||||
const root = path.join(__dirname, "..", "dist", "routes", "/");
|
||||
traverseDirectory({ dirname: root, recursive: true }, (file) => {
|
||||
currentFile = file;
|
||||
@ -52,7 +56,11 @@ export default function getRouteDescriptions() {
|
||||
if (path.endsWith("/index")) path = path.slice(0, "/index".length * -1); // delete index from path
|
||||
currentPath = path;
|
||||
|
||||
require(file);
|
||||
try {
|
||||
require(file);
|
||||
} catch (error) {
|
||||
console.error("error loading file " + file, error);
|
||||
}
|
||||
});
|
||||
return routes;
|
||||
}
|
||||
};
|
@ -4,9 +4,9 @@ import path from "path";
|
||||
import fs from "fs";
|
||||
import * as TJS from "typescript-json-schema";
|
||||
import "missing-native-js-functions";
|
||||
const schemaPath = path.join(__dirname, "..", "assets", "responses.json");
|
||||
const schemaPath = path.join(__dirname, "..", "assets", "schemas.json");
|
||||
|
||||
const settings: TJS.PartialArgs = {
|
||||
const settings = {
|
||||
required: true,
|
||||
ignoreErrors: true,
|
||||
excludePrivate: true,
|
||||
@ -14,10 +14,13 @@ const settings: TJS.PartialArgs = {
|
||||
noExtraProps: true,
|
||||
defaultProps: false
|
||||
};
|
||||
const compilerOptions: TJS.CompilerOptions = {
|
||||
const compilerOptions = {
|
||||
strictNullChecks: true
|
||||
};
|
||||
const ExcludedSchemas = [
|
||||
const Excluded = [
|
||||
"DefaultSchema",
|
||||
"Schema",
|
||||
"EntitySchema",
|
||||
"ServerResponse",
|
||||
"Http2ServerResponse",
|
||||
"global.Express.Response",
|
||||
@ -32,13 +35,13 @@ function main() {
|
||||
const generator = TJS.buildGenerator(program, settings);
|
||||
if (!generator || !program) return;
|
||||
|
||||
const schemas = generator.getUserSymbols().filter((x) => x.endsWith("Response") && !ExcludedSchemas.includes(x));
|
||||
const schemas = generator.getUserSymbols().filter((x) => (x.endsWith("Schema") || x.endsWith("Response")) && !Excluded.includes(x));
|
||||
console.log(schemas);
|
||||
|
||||
var definitions: any = {};
|
||||
var definitions = {};
|
||||
|
||||
for (const name of schemas) {
|
||||
const part = TJS.generateSchema(program, name, settings, [], generator as TJS.JsonSchemaGenerator);
|
||||
const part = TJS.generateSchema(program, name, settings, [], generator);
|
||||
if (!part) continue;
|
||||
|
||||
definitions = { ...definitions, [name]: { ...part } };
|
||||
@ -47,11 +50,10 @@ function main() {
|
||||
fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4));
|
||||
}
|
||||
|
||||
// #/definitions/
|
||||
main();
|
||||
|
||||
function walk(dir: string) {
|
||||
var results = [] as string[];
|
||||
function walk(dir) {
|
||||
var results = [];
|
||||
var list = fs.readdirSync(dir);
|
||||
list.forEach(function (file) {
|
||||
file = dir + "/" + file;
|
@ -1,60 +0,0 @@
|
||||
// https://mermade.github.io/openapi-gui/#
|
||||
// https://editor.swagger.io/
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import * as TJS from "typescript-json-schema";
|
||||
import "missing-native-js-functions";
|
||||
const schemaPath = path.join(__dirname, "..", "assets", "schemas.json");
|
||||
|
||||
const settings: TJS.PartialArgs = {
|
||||
required: true,
|
||||
ignoreErrors: true,
|
||||
excludePrivate: true,
|
||||
defaultNumberType: "integer",
|
||||
noExtraProps: true,
|
||||
defaultProps: false
|
||||
};
|
||||
const compilerOptions: TJS.CompilerOptions = {
|
||||
strictNullChecks: true
|
||||
};
|
||||
const ExcludedSchemas = ["DefaultSchema", "Schema", "EntitySchema"];
|
||||
|
||||
function main() {
|
||||
const program = TJS.getProgramFromFiles(walk(path.join(__dirname, "..", "src", "routes")), compilerOptions);
|
||||
const generator = TJS.buildGenerator(program, settings);
|
||||
if (!generator || !program) return;
|
||||
|
||||
const schemas = generator.getUserSymbols().filter((x) => x.endsWith("Schema") && !ExcludedSchemas.includes(x));
|
||||
console.log(schemas);
|
||||
|
||||
var definitions: any = {};
|
||||
|
||||
for (const name of schemas) {
|
||||
const part = TJS.generateSchema(program, name, settings, [], generator as TJS.JsonSchemaGenerator);
|
||||
if (!part) continue;
|
||||
|
||||
definitions = { ...definitions, [name]: { ...part } };
|
||||
}
|
||||
|
||||
fs.writeFileSync(schemaPath, JSON.stringify(definitions, null, 4));
|
||||
}
|
||||
|
||||
// #/definitions/
|
||||
main();
|
||||
|
||||
function walk(dir: string) {
|
||||
var results = [] as string[];
|
||||
var list = fs.readdirSync(dir);
|
||||
list.forEach(function (file) {
|
||||
file = dir + "/" + file;
|
||||
var stat = fs.statSync(file);
|
||||
if (stat && stat.isDirectory()) {
|
||||
/* Recurse into a subdirectory */
|
||||
results = results.concat(walk(file));
|
||||
} else {
|
||||
if (!file.endsWith(".ts")) return;
|
||||
results.push(file);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}
|
127
api/scripts/generate_openapi_schema.js
Normal file
127
api/scripts/generate_openapi_schema.js
Normal file
@ -0,0 +1,127 @@
|
||||
// https://mermade.github.io/openapi-gui/#
|
||||
// https://editor.swagger.io/
|
||||
const getRouteDescriptions = require("../jest/getRouteDescriptions");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
require("missing-native-js-functions");
|
||||
|
||||
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
|
||||
const SchemaPath = path.join(__dirname, "..", "assets", "schemas.json");
|
||||
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
|
||||
const specification = JSON.parse(fs.readFileSync(openapiPath, { encoding: "utf8" }));
|
||||
|
||||
function combineSchemas(schemas) {
|
||||
var definitions = {};
|
||||
|
||||
for (const name in schemas) {
|
||||
definitions = {
|
||||
...definitions,
|
||||
...schemas[name].definitions,
|
||||
[name]: { ...schemas[name], definitions: undefined, $schema: undefined }
|
||||
};
|
||||
}
|
||||
|
||||
for (const key in definitions) {
|
||||
specification.components.schemas[key] = definitions[key];
|
||||
delete definitions[key].additionalProperties;
|
||||
delete definitions[key].$schema;
|
||||
const definition = definitions[key];
|
||||
|
||||
if (typeof definition.properties === "object") {
|
||||
for (const property of Object.values(definition.properties)) {
|
||||
if (Array.isArray(property.type)) {
|
||||
if (property.type.includes("null")) {
|
||||
property.type = property.type.find((x) => x !== "null");
|
||||
property.nullable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
function getTag(key) {
|
||||
return key.match(/\/([\w-]+)/)[1];
|
||||
}
|
||||
|
||||
function apiRoutes() {
|
||||
const routes = getRouteDescriptions();
|
||||
|
||||
const tags = Array.from(routes.keys()).map((x) => getTag(x));
|
||||
specification.tags = [...specification.tags.map((x) => x.name), ...tags].unique().map((x) => ({ name: x }));
|
||||
|
||||
routes.forEach((route, pathAndMethod) => {
|
||||
const [p, method] = pathAndMethod.split("|");
|
||||
const path = p.replace(/:(\w+)/g, "{$1}");
|
||||
|
||||
let obj = specification.paths[path]?.[method] || {};
|
||||
if (!obj.description) {
|
||||
const permission = route.permission ? `##### Requires the \`\`${route.permission}\`\` permission\n` : "";
|
||||
const event = route.test?.event ? `##### Fires a \`\`${route.test?.event}\`\` event\n` : "";
|
||||
obj.description = permission + event;
|
||||
}
|
||||
if (route.body) {
|
||||
obj.requestBody = {
|
||||
required: true,
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: { $ref: `#/components/schemas/${route.body}` }
|
||||
}
|
||||
}
|
||||
}.merge(obj.requestBody);
|
||||
}
|
||||
if (!obj.responses) {
|
||||
obj.responses = {
|
||||
default: {
|
||||
description: "not documented"
|
||||
}
|
||||
};
|
||||
}
|
||||
if (route.test?.response) {
|
||||
const status = route.test.response.status || 200;
|
||||
obj.responses = {
|
||||
[status]: {
|
||||
...(route.test.response.body
|
||||
? {
|
||||
description: obj.responses[status].description || "",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: {
|
||||
$ref: `#/components/schemas/${route.test.response.body}`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
: {})
|
||||
}
|
||||
}.merge(obj.responses);
|
||||
delete obj.responses.default;
|
||||
}
|
||||
if (p.includes(":")) {
|
||||
obj.parameters = p.match(/:\w+/g)?.map((x) => ({
|
||||
name: x.replace(":", ""),
|
||||
in: "path",
|
||||
required: true,
|
||||
schema: { type: "string" },
|
||||
description: x.replace(":", "")
|
||||
}));
|
||||
}
|
||||
obj.tags = [...(obj.tags || []), getTag(p)].unique();
|
||||
|
||||
specification.paths[path] = { ...specification.paths[path], [method]: obj };
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
combineSchemas(schemas);
|
||||
apiRoutes();
|
||||
|
||||
fs.writeFileSync(
|
||||
openapiPath,
|
||||
JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number")
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
@ -1,92 +0,0 @@
|
||||
// https://mermade.github.io/openapi-gui/#
|
||||
// https://editor.swagger.io/
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import * as TJS from "typescript-json-schema";
|
||||
import "missing-native-js-functions";
|
||||
|
||||
const settings: TJS.PartialArgs = {
|
||||
required: true,
|
||||
ignoreErrors: true,
|
||||
excludePrivate: true,
|
||||
defaultNumberType: "integer",
|
||||
noExtraProps: true,
|
||||
defaultProps: false
|
||||
};
|
||||
const compilerOptions: TJS.CompilerOptions = {
|
||||
strictNullChecks: false
|
||||
};
|
||||
const openapiPath = path.join(__dirname, "..", "assets", "openapi.json");
|
||||
var specification = JSON.parse(fs.readFileSync(openapiPath, { encoding: "utf8" }));
|
||||
|
||||
async function utilSchemas() {
|
||||
const program = TJS.getProgramFromFiles([path.join(__dirname, "..", "..", "util", "src", "index.ts")], compilerOptions);
|
||||
const generator = TJS.buildGenerator(program, settings);
|
||||
|
||||
const schemas = ["UserPublic", "UserPrivate", "PublicConnectedAccount"];
|
||||
|
||||
// @ts-ignore
|
||||
combineSchemas({ schemas, generator, program });
|
||||
}
|
||||
|
||||
function combineSchemas(opts: { program: TJS.Program; generator: TJS.JsonSchemaGenerator; schemas: string[] }) {
|
||||
var definitions: any = {};
|
||||
|
||||
for (const name of opts.schemas) {
|
||||
const part = TJS.generateSchema(opts.program, name, settings, [], opts.generator as TJS.JsonSchemaGenerator);
|
||||
if (!part) continue;
|
||||
|
||||
definitions = { ...definitions, [name]: { ...part, definitions: undefined, $schema: undefined } };
|
||||
}
|
||||
|
||||
for (const key in definitions) {
|
||||
specification.components.schemas[key] = definitions[key];
|
||||
delete definitions[key].additionalProperties;
|
||||
delete definitions[key].$schema;
|
||||
}
|
||||
|
||||
return definitions;
|
||||
}
|
||||
|
||||
const ExcludedSchemas = [
|
||||
"DefaultSchema",
|
||||
"Schema",
|
||||
"EntitySchema",
|
||||
"ServerResponse",
|
||||
"Http2ServerResponse",
|
||||
"global.Express.Response",
|
||||
"Response",
|
||||
"e.Response",
|
||||
"request.Response",
|
||||
"supertest.Response"
|
||||
];
|
||||
|
||||
function apiSchemas() {
|
||||
const program = TJS.getProgramFromFiles([path.join(__dirname, "..", "src", "schema", "index.ts")], compilerOptions);
|
||||
const generator = TJS.buildGenerator(program, settings);
|
||||
|
||||
const schemas = generator
|
||||
.getUserSymbols()
|
||||
.filter((x) => x.endsWith("Response") && !ExcludedSchemas.includes(x))
|
||||
.concat(generator.getUserSymbols().filter((x) => x.endsWith("Schema") && !ExcludedSchemas.includes(x)));
|
||||
|
||||
// @ts-ignore
|
||||
combineSchemas({ schemas, generator, program });
|
||||
}
|
||||
|
||||
function addDefaultResponses() {
|
||||
Object.values(specification.paths).forEach((path: any) => Object.values(path).forEach((request: any) => {}));
|
||||
}
|
||||
|
||||
function main() {
|
||||
addDefaultResponses();
|
||||
utilSchemas();
|
||||
apiSchemas();
|
||||
|
||||
fs.writeFileSync(
|
||||
openapiPath,
|
||||
JSON.stringify(specification, null, 4).replaceAll("#/definitions", "#/components/schemas").replaceAll("bigint", "number")
|
||||
);
|
||||
}
|
||||
|
||||
main();
|
@ -1,9 +1,7 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { FieldErrors, route } from "@fosscord/api";
|
||||
import bcrypt from "bcrypt";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Config, User } from "@fosscord/util";
|
||||
import { adjustEmail } from "./register";
|
||||
import { Config, User, generateToken, adjustEmail } from "@fosscord/util";
|
||||
|
||||
const router: Router = Router();
|
||||
export default router;
|
||||
@ -68,25 +66,6 @@ router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Respo
|
||||
res.json({ token, settings: user.settings });
|
||||
});
|
||||
|
||||
export async function generateToken(id: string) {
|
||||
const iat = Math.floor(Date.now() / 1000);
|
||||
const algorithm = "HS256";
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
jwt.sign(
|
||||
{ id: id, iat },
|
||||
Config.get().security.jwtSecret,
|
||||
{
|
||||
algorithm
|
||||
},
|
||||
(err, token) => {
|
||||
if (err) return rej(err);
|
||||
return res(token);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/login
|
||||
* @argument { login: "email@gmail.com", password: "cleartextpassword", undelete: false, captcha_key: null, login_source: null, gift_code_sku_id: null, }
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { trimSpecial, User, Snowflake, Config, defaultSettings, Member, Invite } from "@fosscord/util";
|
||||
import { trimSpecial, User, Snowflake, Config, defaultSettings, generateToken, Invite, adjustEmail } from "@fosscord/util";
|
||||
import bcrypt from "bcrypt";
|
||||
import { EMAIL_REGEX, FieldErrors, route } from "@fosscord/api";
|
||||
import { FieldErrors, route, getIpAdress, IPAnalysis, isProxy } from "@fosscord/api";
|
||||
import "missing-native-js-functions";
|
||||
import { generateToken } from "./login";
|
||||
import { getIpAdress, IPAnalysis, isProxy } from "@fosscord/api";
|
||||
import { HTTPError } from "lambert-server";
|
||||
|
||||
const router: Router = Router();
|
||||
@ -228,24 +226,6 @@ router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Re
|
||||
return res.json({ token: await generateToken(user.id) });
|
||||
});
|
||||
|
||||
export function adjustEmail(email: string): string | undefined {
|
||||
if (!email) return email;
|
||||
// body parser already checked if it is a valid email
|
||||
const parts = <RegExpMatchArray>email.match(EMAIL_REGEX);
|
||||
// @ts-ignore
|
||||
if (!parts || parts.length < 5) return undefined;
|
||||
const domain = parts[5];
|
||||
const user = parts[1];
|
||||
|
||||
// TODO: check accounts with uncommon email domains
|
||||
if (domain === "gmail.com" || domain === "googlemail.com") {
|
||||
// replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator
|
||||
return user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
export default router;
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { Channel, ChannelRecipientAddEvent, ChannelType, DiscordApiErrors, DmChannelDTO, emitEvent, PublicUserProjection, Recipient, User } from "@fosscord/util";
|
||||
import { route } from "@fosscord/api"
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.put("/:user_id", async (req: Request, res: Response) => {
|
||||
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"] });
|
||||
|
||||
@ -39,7 +40,7 @@ router.put("/:user_id", async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/:user_id", async (req: Request, res: Response) => {
|
||||
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)))
|
||||
|
@ -4,14 +4,14 @@ import { Request, Response, Router } from "express";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.delete("/:member_id/roles/:role_id", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
|
||||
router.delete("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id, member_id } = req.params;
|
||||
|
||||
await Member.removeRole(member_id, guild_id, role_id);
|
||||
res.sendStatus(204);
|
||||
});
|
||||
|
||||
router.put("/:member_id/roles/:role_id", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
|
||||
router.put("/", route({ permission: "MANAGE_ROLES" }), async (req: Request, res: Response) => {
|
||||
const { guild_id, role_id, member_id } = req.params;
|
||||
|
||||
await Member.addRole(member_id, guild_id, role_id);
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.json({
|
||||
id: "",
|
||||
@ -15,4 +16,4 @@ router.get("/", async (req: Request, res: Response) => {
|
||||
}).status(200);
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { Request, Response, Router } from "express";
|
||||
import { route } from "@fosscord/api";
|
||||
|
||||
const router: Router = Router();
|
||||
|
||||
router.get("/", async (req: Request, res: Response) => {
|
||||
router.get("/", route({}), async (req: Request, res: Response) => {
|
||||
//TODO
|
||||
res.json({ sticker_packs: [] }).status(200);
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { Request } from "express";
|
||||
import { ntob } from "./Base64";
|
||||
import { FieldErrors } from "./FieldError";
|
||||
export const EMAIL_REGEX =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
||||
export function checkLength(str: string, min: number, max: number, key: string, req: Request) {
|
||||
if (str.length < min || str.length > max) {
|
||||
|
@ -9,7 +9,7 @@ import addFormats from "ajv-formats";
|
||||
import fetch from "node-fetch";
|
||||
import { User } from "@fosscord/util";
|
||||
|
||||
const SchemaPath = join(__dirname, "..", "assets", "responses.json");
|
||||
const SchemaPath = join(__dirname, "..", "assets", "schemas.json");
|
||||
const schemas = JSON.parse(fs.readFileSync(SchemaPath, { encoding: "utf8" }));
|
||||
export const ajv = new Ajv({
|
||||
allErrors: true,
|
||||
@ -64,7 +64,7 @@ describe("Automatic unit tests with route description middleware", () => {
|
||||
routes.forEach((route, pathAndMethod) => {
|
||||
const [path, method] = pathAndMethod.split("|");
|
||||
|
||||
test(path, async (done) => {
|
||||
test(`${method.toUpperCase()} ${path}`, async (done) => {
|
||||
if (!route.test) {
|
||||
console.log(`${(route as any).file}\nrouter.${method} is missing the test property`);
|
||||
return done();
|
||||
|
20
util/src/util/Email.ts
Normal file
20
util/src/util/Email.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export const EMAIL_REGEX =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
|
||||
export function adjustEmail(email: string): string | undefined {
|
||||
if (!email) return email;
|
||||
// body parser already checked if it is a valid email
|
||||
const parts = <RegExpMatchArray>email.match(EMAIL_REGEX);
|
||||
// @ts-ignore
|
||||
if (!parts || parts.length < 5) return undefined;
|
||||
const domain = parts[5];
|
||||
const user = parts[1];
|
||||
|
||||
// TODO: check accounts with uncommon email domains
|
||||
if (domain === "gmail.com" || domain === "googlemail.com") {
|
||||
// replace .dots and +alternatives -> Gmail Dot Trick https://support.google.com/mail/answer/7436150 and https://generator.email/blog/gmail-generator
|
||||
return user.replace(/[.]|(\+.*)/g, "") + "@gmail.com";
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import jwt, { VerifyOptions } from "jsonwebtoken";
|
||||
import { Config } from "./Config";
|
||||
import { User } from "../entities";
|
||||
|
||||
export const JWTOptions: VerifyOptions = { algorithms: ["HS256"] };
|
||||
@ -21,3 +22,22 @@ export function checkToken(token: string, jwtSecret: string): Promise<any> {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function generateToken(id: string) {
|
||||
const iat = Math.floor(Date.now() / 1000);
|
||||
const algorithm = "HS256";
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
jwt.sign(
|
||||
{ id: id, iat },
|
||||
Config.get().security.jwtSecret,
|
||||
{
|
||||
algorithm,
|
||||
},
|
||||
(err, token) => {
|
||||
if (err) return rej(err);
|
||||
return res(token);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
export * from "./ApiError";
|
||||
export * from "./BitField";
|
||||
export * from "./checkToken";
|
||||
export * from "./Token";
|
||||
export * from "./cdn";
|
||||
export * from "./Config";
|
||||
export * from "./Constants";
|
||||
export * from "./Database";
|
||||
export * from "./Event";
|
||||
export * from "./Email";
|
||||
export * from "./Intents";
|
||||
export * from "./MessageFlags";
|
||||
export * from "./Permissions";
|
||||
|
Loading…
Reference in New Issue
Block a user