Merge branch 'master' of https://github.com/fosscord/fosscord-api
This commit is contained in:
commit
fe8a1c1000
1
.npmignore
Normal file
1
.npmignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
!dist/
|
32
README.md
32
README.md
@ -1,39 +1,51 @@
|
|||||||
# Fosscord API Server
|
# Fosscord API Server
|
||||||
This repository contains the HTTP API Server
|
|
||||||
|
This repository contains the Fosscord HTTP API Server
|
||||||
|
|
||||||
## Bug Tracker
|
## Bug Tracker
|
||||||
|
|
||||||
[Project Board](https://github.com/fosscord/fosscord-api/projects/2)
|
[Project Board](https://github.com/fosscord/fosscord-api/projects/2)
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
We use [express](https://expressjs.com/) for the HTTP Server and
|
We use [express](https://expressjs.com/) for the HTTP Server and
|
||||||
[lambert-server](https://www.npmjs.com/package/lambert-server) for route handling and body validation (customized).
|
[lambert-server](https://www.npmjs.com/package/lambert-server) for route handling and body validation (customized).
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
You should be familiar with:
|
|
||||||
- [Git](https://git-scm.com/)
|
|
||||||
- [NodeJS](https://nodejs.org/)
|
|
||||||
- [TypeScript](https://www.typescriptlang.org/)
|
|
||||||
- [MongoDB/mongoose](http://mongoosejs.com/)
|
|
||||||
|
|
||||||
and the other technologies we use
|
You should be familiar with:
|
||||||
|
|
||||||
|
- [Git](https://git-scm.com/)
|
||||||
|
- [NodeJS](https://nodejs.org/)
|
||||||
|
- [TypeScript](https://www.typescriptlang.org/)
|
||||||
|
- [MongoDB/mongoose](http://mongoosejs.com/)
|
||||||
|
|
||||||
|
and the other technologies we use
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
|
|
||||||
Clone the Repository:
|
Clone the Repository:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/fosscord/fosscord-api
|
git clone https://github.com/fosscord/fosscord-api
|
||||||
cd discord-server
|
cd discord-server
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Install (dev)dependencies:
|
#### Install (dev)dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm install --only=dev
|
npm install --only=dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Starting:
|
#### Starting:
|
||||||
|
|
||||||
```
|
```
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Debugging:
|
#### Debugging:
|
||||||
|
|
||||||
**Vscode:**
|
**Vscode:**
|
||||||
The Launch file configuration is in ``./vscode/launch.json``,
|
The Launch file configuration is in `./vscode/launch.json`,
|
||||||
so you can just debug the server by pressing ``F5`` or the ``> Launch Server`` button
|
so you can just debug the server by pressing `F5` or the `> Launch Server` button
|
||||||
|
830
package-lock.json
generated
830
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@ -1,35 +1,42 @@
|
|||||||
{
|
{
|
||||||
"name": "fosscord-api",
|
"name": "@fosscord/api",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "This repository contains the HTTP API Server",
|
"description": "This repository contains the HTTP API Server",
|
||||||
"main": "index.js",
|
"main": "dist/Server.js",
|
||||||
|
"types": "dist/Server.d.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"start": "npm run build:util && npm run build && node dist/",
|
"start": "npm run build:util && npm run build && node dist/start",
|
||||||
"build": "tsc -b .",
|
"build": "tsc -b .",
|
||||||
"build:util": "tsc -b ./node_modules/fosscord-server-util/",
|
"build:util": "tsc -b ./node_modules/@fosscord/server-util/",
|
||||||
"postinstall": "npm i github:fosscord/fosscord-server-util && patch-package"
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/fosscord/fosscord-api.git"
|
"url": "git+https://github.com/fosscord/fosscord-api.git"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [
|
||||||
"author": "",
|
"discord",
|
||||||
|
"fosscord",
|
||||||
|
"fosscord-api",
|
||||||
|
"discord open source",
|
||||||
|
"discord-open-source"
|
||||||
|
],
|
||||||
|
"author": "Fosscord",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/fosscord/fosscord-api/issues"
|
"url": "https://github.com/fosscord/fosscord-api/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/fosscord/fosscord-api#readme",
|
"homepage": "https://github.com/fosscord/fosscord-api#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fosscord/server-util": "^1.0.4",
|
||||||
"@types/jest": "^26.0.22",
|
"@types/jest": "^26.0.22",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-validator": "^6.9.2",
|
"express-validator": "^6.9.2",
|
||||||
"fosscord-server-util": "github:fosscord/fosscord-server-util",
|
|
||||||
"i18next": "^19.8.5",
|
"i18next": "^19.8.5",
|
||||||
"i18next-http-middleware": "^3.1.0",
|
"i18next-http-middleware": "^3.1.0",
|
||||||
"i18next-node-fs-backend": "^2.1.3",
|
"i18next-node-fs-backend": "^2.1.3",
|
||||||
|
@ -4,7 +4,7 @@ import { Connection } from "mongoose";
|
|||||||
import { Server, ServerOptions } from "lambert-server";
|
import { Server, ServerOptions } from "lambert-server";
|
||||||
import { Authentication, GlobalRateLimit } from "./middlewares/";
|
import { Authentication, GlobalRateLimit } from "./middlewares/";
|
||||||
import Config from "./util/Config";
|
import Config from "./util/Config";
|
||||||
import { db } from "fosscord-server-util";
|
import { db } from "@fosscord/server-util";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import i18nextMiddleware, { I18next } from "i18next-http-middleware";
|
import i18nextMiddleware, { I18next } from "i18next-http-middleware";
|
||||||
import i18nextBackend from "i18next-node-fs-backend";
|
import i18nextBackend from "i18next-node-fs-backend";
|
||||||
@ -13,21 +13,21 @@ import { BodyParser } from "./middlewares/BodyParser";
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
export interface DiscordServerOptions extends ServerOptions {}
|
export interface FosscordServerOptions extends ServerOptions {}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace Express {
|
namespace Express {
|
||||||
interface Request {
|
interface Request {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
server: DiscordServer;
|
server: FosscordServer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DiscordServer extends Server {
|
export class FosscordServer extends Server {
|
||||||
public options: DiscordServerOptions;
|
public options: FosscordServerOptions;
|
||||||
|
|
||||||
constructor(opts?: Partial<DiscordServerOptions>) {
|
constructor(opts?: Partial<FosscordServerOptions>) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
super({ ...opts, errorHandler: false, jsonBody: false });
|
super({ ...opts, errorHandler: false, jsonBody: false });
|
||||||
}
|
}
|
||||||
|
36
src/index.ts
36
src/index.ts
@ -1,17 +1,19 @@
|
|||||||
process.on("uncaughtException", console.error);
|
export * from "./Server";
|
||||||
process.on("unhandledRejection", console.error);
|
export * from "./middlewares/";
|
||||||
|
export * from "./schema/Ban";
|
||||||
import "missing-native-js-functions";
|
export * from "./schema/Channel";
|
||||||
import { config } from "dotenv";
|
export * from "./schema/Guild";
|
||||||
config();
|
export * from "./schema/Invite";
|
||||||
import { DiscordServer } from "./Server";
|
export * from "./schema/Message";
|
||||||
|
export * from "./util/Captcha";
|
||||||
var port = Number(process.env.PORT);
|
export * from "./util/Config";
|
||||||
if (isNaN(port)) port = 1000;
|
export * from "./util/Constants";
|
||||||
|
export * from "./util/Event";
|
||||||
const server = new DiscordServer({ port });
|
export * from "./util/instanceOf";
|
||||||
server.start().catch(console.error);
|
export * from "./util/Event";
|
||||||
|
export * from "./util/instanceOf";
|
||||||
// @ts-ignore
|
export * from "./util/Member";
|
||||||
global.server = server;
|
export * from "./util/RandomInviteID";
|
||||||
export default server;
|
export * from "./util/String";
|
||||||
|
export * from "./util/User";
|
||||||
|
export { check as checkPassword } from "./util/passwordStrength";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { checkToken } from "fosscord-server-util";
|
import { checkToken } from "@fosscord/server-util";
|
||||||
|
|
||||||
export const NO_AUTHORIZATION_ROUTES = [
|
export const NO_AUTHORIZATION_ROUTES = [
|
||||||
"/api/v8/auth/login",
|
"/api/v8/auth/login",
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import { Authentication } from "./Authentication";
|
export * from "./GlobalRateLimit";
|
||||||
import { GlobalRateLimit } from "./GlobalRateLimit";
|
export * from "./Authentication";
|
||||||
|
export * from "./BodyParser";
|
||||||
export { Authentication, GlobalRateLimit };
|
export * from "./CORS";
|
||||||
|
export * from "./ErrorHandler";
|
||||||
|
export * from "./RateLimit";
|
||||||
|
@ -2,7 +2,7 @@ import { Request, Response, Router } from "express";
|
|||||||
import { check, FieldErrors, Length } from "../../util/instanceOf";
|
import { check, FieldErrors, Length } from "../../util/instanceOf";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import jwt from "jsonwebtoken";
|
import jwt from "jsonwebtoken";
|
||||||
import { User, UserModel } from "fosscord-server-util";
|
import { UserModel } from "@fosscord/server-util";
|
||||||
import Config from "../../util/Config";
|
import Config from "../../util/Config";
|
||||||
import { adjustEmail } from "./register";
|
import { adjustEmail } from "./register";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import Config from "../../util/Config";
|
import Config from "../../util/Config";
|
||||||
import { trimSpecial, User, Snowflake, UserModel } from "fosscord-server-util";
|
import { trimSpecial, User, Snowflake, UserModel } from "@fosscord/server-util";
|
||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
|
import { check, Email, EMAIL_REGEX, FieldErrors, Length } from "../../util/instanceOf";
|
||||||
import "missing-native-js-functions";
|
import "missing-native-js-functions";
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {"webhook_channel_id":"754001514330062952"}
|
||||||
|
*
|
||||||
|
* Creates a WebHook in the channel and returns the id of it
|
||||||
|
*
|
||||||
|
* @returns {"channel_id": "816382962056560690", "webhook_id": "834910735095037962"}
|
||||||
|
*/
|
||||||
|
@ -1,4 +1,30 @@
|
|||||||
|
import { ChannelModel, getPermission, toObject } from "@fosscord/server-util";
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO: delete channel
|
||||||
|
// TODO: Get channel
|
||||||
|
|
||||||
|
router.delete("/", async(req,res)=>{
|
||||||
|
const {channel_id} = req.params
|
||||||
|
|
||||||
|
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true, permission_overwrites: true }).exec();
|
||||||
|
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||||
|
if (channel.guild_id) {
|
||||||
|
const permission = await getPermission(req.user_id, channel.guild_id)
|
||||||
|
permission.hasThrow("MANAGE_CHANNELS")
|
||||||
|
|
||||||
|
// TODO Channel Update Gateway event will fire for each of them
|
||||||
|
await ChannelModel.updateMany({parent_id: channel_id}, {$set: {channel_id: null}}).exec()
|
||||||
|
|
||||||
|
await ChannelModel.deleteOne({id: channel_id})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Dm channel "close" not delete
|
||||||
|
|
||||||
|
const data = toObject(channel);
|
||||||
|
//TODO: Reload channel list if request successful
|
||||||
|
res.send(data)
|
||||||
|
})
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -7,7 +7,7 @@ import { emitEvent } from "../../../util/Event";
|
|||||||
|
|
||||||
import { InviteCreateSchema } from "../../../schema/Invite";
|
import { InviteCreateSchema } from "../../../schema/Invite";
|
||||||
|
|
||||||
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "fosscord-server-util";
|
import { getPermission, ChannelModel, InviteModel, InviteCreateEvent, toObject } from "@fosscord/server-util";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
@ -22,10 +22,7 @@ router.post("/", check(InviteCreateSchema), async (req: Request, res: Response)
|
|||||||
const { guild_id } = channel;
|
const { guild_id } = channel;
|
||||||
|
|
||||||
const permission = await getPermission(user_id, guild_id);
|
const permission = await getPermission(user_id, guild_id);
|
||||||
|
permission.hasThrow("CREATE_INSTANT_INVITE");
|
||||||
if (!permission.has("CREATE_INSTANT_INVITE")) {
|
|
||||||
throw new HTTPError("You aren't authorised to access this endpoint", 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invite = {
|
const invite = {
|
||||||
code: random(),
|
code: random(),
|
||||||
@ -55,10 +52,7 @@ router.get("/", async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
const { guild_id } = channel;
|
const { guild_id } = channel;
|
||||||
const permission = await getPermission(user_id, guild_id);
|
const permission = await getPermission(user_id, guild_id);
|
||||||
|
permission.hasThrow("MANAGE_CHANNELS");
|
||||||
if (!permission.has("MANAGE_CHANNELS")) {
|
|
||||||
throw new HTTPError("You aren't authorised to access this endpoint", 401);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invites = await InviteModel.find({ guild_id }).exec();
|
const invites = await InviteModel.find({ guild_id }).exec();
|
||||||
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// router.post("/", (req, res) => {});
|
||||||
|
|
||||||
|
export default router;
|
@ -0,0 +1,6 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
|
export default router;
|
@ -0,0 +1,6 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
|
export default router;
|
@ -1,5 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "fosscord-server-util";
|
import { ChannelModel, getPermission, MessageDeleteBulkEvent, MessageModel } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import Config from "../../../../util/Config";
|
import Config from "../../../../util/Config";
|
||||||
import { emitEvent } from "../../../../util/Event";
|
import { emitEvent } from "../../../../util/Event";
|
||||||
@ -13,12 +13,12 @@ export default router;
|
|||||||
// TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
|
// TODO: should this request fail, if you provide messages older than 14 days/invalid ids?
|
||||||
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
|
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages
|
||||||
router.post("/", check({ messages: [String] }), async (req, res) => {
|
router.post("/", check({ messages: [String] }), async (req, res) => {
|
||||||
const channel_id = req.params.channel_id
|
const channel_id = req.params.channel_id;
|
||||||
const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec();
|
const channel = await ChannelModel.findOne({ id: channel_id }, { permission_overwrites: true, guild_id: true }).exec();
|
||||||
if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
|
if (!channel?.guild_id) throw new HTTPError("Can't bulk delete dm channel messages", 400);
|
||||||
|
|
||||||
const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel });
|
const permission = await getPermission(req.user_id, channel?.guild_id, channel_id, { channel });
|
||||||
if (!permission.has("MANAGE_MESSAGES")) throw new HTTPError("You are missing the MANAGE_MESSAGES permissions");
|
permission.hasThrow("MANAGE_MESSAGES");
|
||||||
|
|
||||||
const { maxBulkDelete } = Config.get().limits.message;
|
const { maxBulkDelete } = Config.get().limits.message;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
MessageModel,
|
MessageModel,
|
||||||
Snowflake,
|
Snowflake,
|
||||||
toObject,
|
toObject,
|
||||||
} from "fosscord-server-util";
|
} from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { MessageCreateSchema } from "../../../../schema/Message";
|
import { MessageCreateSchema } from "../../../../schema/Message";
|
||||||
import { check, instanceOf, Length } from "../../../../util/instanceOf";
|
import { check, instanceOf, Length } from "../../../../util/instanceOf";
|
||||||
@ -22,7 +22,7 @@ const router: Router = Router();
|
|||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
||||||
function isTextChannel(type: ChannelType): boolean {
|
export function isTextChannel(type: ChannelType): boolean {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ChannelType.GUILD_VOICE:
|
case ChannelType.GUILD_VOICE:
|
||||||
case ChannelType.GUILD_CATEGORY:
|
case ChannelType.GUILD_CATEGORY:
|
||||||
@ -62,7 +62,8 @@ router.get("/", async (req, res) => {
|
|||||||
|
|
||||||
if (channel.guild_id) {
|
if (channel.guild_id) {
|
||||||
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
|
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
|
||||||
if (!permissions.has("VIEW_CHANNEL")) throw new HTTPError("You don't have permission to view this channel", 401);
|
permissions.hasThrow("VIEW_CHANNEL");
|
||||||
|
|
||||||
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
if (!permissions.has("READ_MESSAGE_HISTORY")) return res.json([]);
|
||||||
} else if (channel.recipients) {
|
} else if (channel.recipients) {
|
||||||
// group/dm channel
|
// group/dm channel
|
||||||
@ -106,11 +107,10 @@ router.post("/", check(MessageCreateSchema), async (req, res) => {
|
|||||||
|
|
||||||
if (channel.guild_id) {
|
if (channel.guild_id) {
|
||||||
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
|
const permissions = await getPermission(req.user_id, channel.guild_id, channel_id, { channel });
|
||||||
if (!permissions.has("SEND_MESSAGES")) throw new HTTPError("You don't have the SEND_MESSAGES permission");
|
permissions.hasThrow("SEND_MESSAGES");
|
||||||
if (body.tts && !permissions.has("SEND_TTS_MESSAGES")) throw new HTTPError("You are missing the SEND_TTS_MESSAGES permission");
|
if (body.tts) permissions.hasThrow("SEND_TTS_MESSAGES");
|
||||||
if (body.message_reference) {
|
if (body.message_reference) {
|
||||||
if (!permissions.has("READ_MESSAGE_HISTORY"))
|
permissions.hasThrow("READ_MESSAGE_HISTORY");
|
||||||
throw new HTTPError("You are missing the READ_MESSAGE_HISTORY permission to reply");
|
|
||||||
if (body.message_reference.guild_id !== channel.guild_id)
|
if (body.message_reference.guild_id !== channel.guild_id)
|
||||||
throw new HTTPError("You can only reference messages from this guild");
|
throw new HTTPError("You can only reference messages from this guild");
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -1,4 +1,27 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
|
import { check, Length } from "../../../util/instanceOf";
|
||||||
|
import { ChannelModel, getPermission, trimSpecial } from "@fosscord/server-util";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
|
import { isTextChannel } from "./messages/index";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
// TODO:
|
||||||
|
|
||||||
|
// TODO: use Image Data Type for avatar instead of String
|
||||||
|
router.post("/", check({ name: new Length(String, 1, 80), $avatar: String }), async (req, res) => {
|
||||||
|
const channel_id = req.params.channel_id;
|
||||||
|
const channel = await ChannelModel.findOne({ id: channel_id }, { guild_id: true, type: true }).exec();
|
||||||
|
if (!channel) throw new HTTPError("Channel not found", 404);
|
||||||
|
|
||||||
|
isTextChannel(channel.type);
|
||||||
|
if (!channel.guild_id) throw new HTTPError("Not a guild channel", 400);
|
||||||
|
|
||||||
|
const permission = await getPermission(req.user_id, channel.guild_id);
|
||||||
|
permission.hasThrow("MANAGE_WEBHOOKS");
|
||||||
|
|
||||||
|
var { avatar, name } = req.body as { name: string; avatar?: string };
|
||||||
|
name = trimSpecial(name);
|
||||||
|
if (name === "clyde") throw new HTTPError("Invalid name", 400);
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "fosscord-server-util";
|
import { BanModel, getPermission, GuildBanAddEvent, GuildBanRemoveEvent, GuildModel, toObject } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { getIpAdress } from "../../../middlewares/GlobalRateLimit";
|
import { getIpAdress } from "../../../middlewares/GlobalRateLimit";
|
||||||
import { BanCreateSchema } from "../../../schema/Ban";
|
import { BanCreateSchema } from "../../../schema/Ban";
|
||||||
@ -35,7 +35,7 @@ router.post("/:user_id", check(BanCreateSchema), async (req: Request, res: Respo
|
|||||||
|
|
||||||
const banned_user = await getPublicUser(banned_user_id);
|
const banned_user = await getPublicUser(banned_user_id);
|
||||||
const perms = await getPermission(req.user_id, guild_id);
|
const perms = await getPermission(req.user_id, guild_id);
|
||||||
if (!perms.has("BAN_MEMBERS")) throw new HTTPError("You don't have the permission to ban members", 403);
|
perms.hasThrow("BAN_MEMBERS");
|
||||||
if (req.user_id === banned_user_id) throw new HTTPError("You can't ban yourself", 400);
|
if (req.user_id === banned_user_id) throw new HTTPError("You can't ban yourself", 400);
|
||||||
|
|
||||||
await removeMember(banned_user_id, guild_id);
|
await removeMember(banned_user_id, guild_id);
|
||||||
@ -69,9 +69,7 @@ router.delete("/:user_id", async (req: Request, res: Response) => {
|
|||||||
if (!guild) throw new HTTPError("Guild not found", 404);
|
if (!guild) throw new HTTPError("Guild not found", 404);
|
||||||
|
|
||||||
const perms = await getPermission(req.user_id, guild_id);
|
const perms = await getPermission(req.user_id, guild_id);
|
||||||
if (!perms.has("BAN_MEMBERS")) {
|
perms.hasThrow("BAN_MEMBERS");
|
||||||
throw new HTTPError("No permissions", 403);
|
|
||||||
}
|
|
||||||
|
|
||||||
await BanModel.deleteOne({
|
await BanModel.deleteOne({
|
||||||
user_id: banned_user_id,
|
user_id: banned_user_id,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "fosscord-server-util";
|
import { ChannelCreateEvent, ChannelModel, ChannelType, GuildModel, Snowflake, toObject } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { ChannelModifySchema } from "../../../schema/Channel";
|
import { ChannelModifySchema } from "../../../schema/Channel";
|
||||||
import { emitEvent } from "../../../util/Event";
|
import { emitEvent } from "../../../util/Event";
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
RoleModel,
|
RoleModel,
|
||||||
toObject,
|
toObject,
|
||||||
UserModel,
|
UserModel,
|
||||||
} from "fosscord-server-util";
|
} from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { GuildUpdateSchema } from "../../../schema/Guild";
|
import { GuildUpdateSchema } from "../../../schema/Guild";
|
||||||
import { emitEvent } from "../../../util/Event";
|
import { emitEvent } from "../../../util/Event";
|
||||||
@ -41,7 +41,7 @@ router.patch("/", check(GuildUpdateSchema), async (req: Request, res: Response)
|
|||||||
// TODO: guild update check image
|
// TODO: guild update check image
|
||||||
|
|
||||||
const perms = await getPermission(req.user_id, guild_id);
|
const perms = await getPermission(req.user_id, guild_id);
|
||||||
if (!perms.has("MANAGE_GUILD")) throw new HTTPError("You do not have the MANAGE_GUILD permission", 401);
|
perms.hasThrow("MANAGE_GUILD");
|
||||||
|
|
||||||
const guild = await GuildModel.findOneAndUpdate({ id: guild_id }, body)
|
const guild = await GuildModel.findOneAndUpdate({ id: guild_id }, body)
|
||||||
.populate({ path: "joined_at", match: { id: req.user_id } })
|
.populate({ path: "joined_at", match: { id: req.user_id } })
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Request, Response, Router } from "express";
|
import { Request, Response, Router } from "express";
|
||||||
import { GuildModel, MemberModel, toObject } from "fosscord-server-util";
|
import { GuildModel, MemberModel, toObject } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { instanceOf, Length } from "../../../util/instanceOf";
|
import { instanceOf, Length } from "../../../util/instanceOf";
|
||||||
import { PublicMemberProjection } from "../../../util/Member";
|
import { PublicMemberProjection } from "../../../util/Member";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { RoleModel, GuildModel, Snowflake, Guild } from "fosscord-server-util";
|
import { RoleModel, GuildModel, Snowflake, Guild } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { check } from "./../../util/instanceOf";
|
import { check } from "./../../util/instanceOf";
|
||||||
import { GuildCreateSchema } from "../../schema/Guild";
|
import { GuildCreateSchema } from "../../schema/Guild";
|
||||||
@ -9,6 +9,8 @@ import { addMember } from "../../util/Member";
|
|||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
|
//TODO: create default channel
|
||||||
|
|
||||||
router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => {
|
router.post("/", check(GuildCreateSchema), async (req: Request, res: Response) => {
|
||||||
const body = req.body as GuildCreateSchema;
|
const body = req.body as GuildCreateSchema;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { getPermission, InviteModel, toObject } from "fosscord-server-util";
|
import { getPermission, InviteModel, toObject } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
|
||||||
@ -21,7 +21,8 @@ router.delete("/:invite_code", async (req: Request, res: Response) => {
|
|||||||
const { guild_id, channel_id } = invite;
|
const { guild_id, channel_id } = invite;
|
||||||
const perms = await getPermission(req.user_id, guild_id, channel_id);
|
const perms = await getPermission(req.user_id, guild_id, channel_id);
|
||||||
|
|
||||||
if (!perms.has("MANAGE_GUILD") || !perms.has("MANAGE_CHANNELS")) throw new HTTPError("You aren't allow", 401);
|
if (!perms.has("MANAGE_GUILD") && !perms.has("MANAGE_CHANNELS"))
|
||||||
|
throw new HTTPError("You missing the MANAGE_GUILD or MANAGE_CHANNELS permission", 401);
|
||||||
|
|
||||||
await InviteModel.deleteOne({ code }).exec();
|
await InviteModel.deleteOne({ code }).exec();
|
||||||
|
|
||||||
|
19
src/routes/users/#id/index.ts
Normal file
19
src/routes/users/#id/index.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Router, Request, Response } from "express";
|
||||||
|
import { UserModel, toObject } from "@fosscord/server-util";
|
||||||
|
import { getPublicUser } from "../../../util/User";
|
||||||
|
import { HTTPError } from "lambert-server";
|
||||||
|
import { UserUpdateSchema } from "../../../schema/User";
|
||||||
|
import { check } from "../../../util/instanceOf";
|
||||||
|
|
||||||
|
const router: Router = Router();
|
||||||
|
|
||||||
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
const user = await getPublicUser(id);
|
||||||
|
if (!user) throw new HTTPError("User not found", 404);
|
||||||
|
|
||||||
|
res.json(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default router;
|
69
src/routes/users/@me/channels.ts
Normal file
69
src/routes/users/@me/channels.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import {
|
||||||
|
Router,
|
||||||
|
Request,
|
||||||
|
Response
|
||||||
|
} from "express";
|
||||||
|
import {
|
||||||
|
ChannelModel,
|
||||||
|
ChannelCreateEvent,
|
||||||
|
DMChannel,
|
||||||
|
UserModel,
|
||||||
|
toObject,
|
||||||
|
ChannelType,
|
||||||
|
Snowflake
|
||||||
|
} from "@fosscord/server-util";
|
||||||
|
import {
|
||||||
|
HTTPError
|
||||||
|
} from "lambert-server";
|
||||||
|
import {
|
||||||
|
emitEvent
|
||||||
|
} from "../../../util/Event";
|
||||||
|
import {
|
||||||
|
getPublicUser
|
||||||
|
} from "../../../util/User";
|
||||||
|
import {
|
||||||
|
DmChannelCreateSchema
|
||||||
|
} from "../../../schema/Channel";
|
||||||
|
import {
|
||||||
|
check
|
||||||
|
} from "../../../util/instanceOf";
|
||||||
|
|
||||||
|
const router: Router = Router();
|
||||||
|
|
||||||
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
|
const user = await UserModel.findOne({
|
||||||
|
id: req.user_id
|
||||||
|
}, {
|
||||||
|
guilds: true
|
||||||
|
}).exec();
|
||||||
|
if (!user) throw new HTTPError("User not found", 404);
|
||||||
|
|
||||||
|
var testID = "829044530203328513"; //FOR TEST
|
||||||
|
|
||||||
|
var channels = await ChannelModel.find({
|
||||||
|
recipients: req.user_id,
|
||||||
|
type: 1
|
||||||
|
}).exec();
|
||||||
|
|
||||||
|
res.json(toObject(channels));
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post("/", check(DmChannelCreateSchema), async (req, res) => {
|
||||||
|
const body = req.body as DmChannelCreateSchema;
|
||||||
|
|
||||||
|
const channel = {
|
||||||
|
...body,
|
||||||
|
owner_id: req.user_id,
|
||||||
|
id: Snowflake.generate(),
|
||||||
|
type: ChannelType.DM,
|
||||||
|
created_at: new Date(),
|
||||||
|
};
|
||||||
|
await new ChannelModel(channel).save();
|
||||||
|
|
||||||
|
/*Event({ event: "CHANNEL_CREATE", data: channel } as ChannelCreateEvent);*/
|
||||||
|
|
||||||
|
|
||||||
|
res.json(channel);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -1,5 +1,5 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "fosscord-server-util";
|
import { GuildModel, MemberModel, UserModel, GuildDeleteEvent, GuildMemberRemoveEvent, toObject } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import { emitEvent } from "../../../util/Event";
|
import { emitEvent } from "../../../util/Event";
|
||||||
import { getPublicUser } from "../../../util/User";
|
import { getPublicUser } from "../../../util/User";
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Router, Request, Response } from "express";
|
import { Router, Request, Response } from "express";
|
||||||
import { UserModel } from "fosscord-server-util";
|
import { UserModel } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
const router: Router = Router();
|
const router: Router = Router();
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ChannelType } from "@fosscord/server-util";
|
||||||
import { Length } from "../util/instanceOf";
|
import { Length } from "../util/instanceOf";
|
||||||
|
|
||||||
export const ChannelModifySchema = {
|
export const ChannelModifySchema = {
|
||||||
@ -20,6 +21,24 @@ export const ChannelModifySchema = {
|
|||||||
$nsfw: Boolean,
|
$nsfw: Boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DmChannelCreateSchema = {
|
||||||
|
owner_id: String,
|
||||||
|
$id: String,
|
||||||
|
$created_at: Date,
|
||||||
|
name: String,
|
||||||
|
type: Number,
|
||||||
|
recipients: [String]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DmChannelCreateSchema {
|
||||||
|
owner_id: String;
|
||||||
|
id?: String;
|
||||||
|
created_at?: Date;
|
||||||
|
name: String;
|
||||||
|
type: Number;
|
||||||
|
recipients: String[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ChannelModifySchema {
|
export interface ChannelModifySchema {
|
||||||
name: string;
|
name: string;
|
||||||
type: number;
|
type: number;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ChannelSchema, GuildChannel } from "fosscord-server-util";
|
import { ChannelSchema, GuildChannel } from "@fosscord/server-util";
|
||||||
import { Length } from "../util/instanceOf";
|
import { Length } from "../util/instanceOf";
|
||||||
|
|
||||||
export const GuildCreateSchema = {
|
export const GuildCreateSchema = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Embed, EmbedImage } from "fosscord-server-util";
|
import { Embed, EmbedImage } from "@fosscord/server-util";
|
||||||
import { Length } from "../util/instanceOf";
|
import { Length } from "../util/instanceOf";
|
||||||
|
|
||||||
export const MessageCreateSchema = {
|
export const MessageCreateSchema = {
|
||||||
|
43
src/schema/User.ts
Normal file
43
src/schema/User.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
export const UserUpdateSchema = {
|
||||||
|
id: String,
|
||||||
|
username: String,
|
||||||
|
discriminator: String,
|
||||||
|
avatar: String || null,
|
||||||
|
$phone: String,
|
||||||
|
desktop: Boolean,
|
||||||
|
mobile: Boolean,
|
||||||
|
premium: Boolean,
|
||||||
|
premium_type: Number,
|
||||||
|
bot: Boolean,
|
||||||
|
system: Boolean,
|
||||||
|
nsfw_allowed: Boolean,
|
||||||
|
mfa_enabled: Boolean,
|
||||||
|
created_at: Date,
|
||||||
|
verified: Boolean,
|
||||||
|
$email: String,
|
||||||
|
flags: BigInt,
|
||||||
|
public_flags: BigInt,
|
||||||
|
$guilds: [String],
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface UserUpdateSchema {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
discriminator: string;
|
||||||
|
avatar: string | null;
|
||||||
|
phone?: string;
|
||||||
|
desktop: boolean;
|
||||||
|
mobile: boolean;
|
||||||
|
premium: boolean;
|
||||||
|
premium_type: number;
|
||||||
|
bot: boolean;
|
||||||
|
system: boolean;
|
||||||
|
nsfw_allowed: boolean;
|
||||||
|
mfa_enabled: boolean;
|
||||||
|
created_at: Date;
|
||||||
|
verified: boolean;
|
||||||
|
email?: string;
|
||||||
|
flags: bigint;
|
||||||
|
public_flags: bigint;
|
||||||
|
guilds: string[];
|
||||||
|
}
|
33
src/start.ts
Normal file
33
src/start.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
process.on("uncaughtException", console.error);
|
||||||
|
process.on("unhandledRejection", console.error);
|
||||||
|
|
||||||
|
import "missing-native-js-functions";
|
||||||
|
import { config } from "dotenv";
|
||||||
|
config();
|
||||||
|
import { FosscordServer } from "./Server";
|
||||||
|
import cluster from "cluster";
|
||||||
|
import os from "os";
|
||||||
|
const cores = os.cpus().length;
|
||||||
|
|
||||||
|
if (cluster.isMaster && process.env.production == "true") {
|
||||||
|
console.log(`Primary ${process.pid} is running`);
|
||||||
|
|
||||||
|
// Fork workers.
|
||||||
|
for (let i = 0; i < cores; i++) {
|
||||||
|
cluster.fork();
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster.on("exit", (worker, code, signal) => {
|
||||||
|
console.log(`worker ${worker.process.pid} died, restart worker`);
|
||||||
|
cluster.fork();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var port = Number(process.env.PORT);
|
||||||
|
if (isNaN(port)) port = 1000;
|
||||||
|
|
||||||
|
const server = new FosscordServer({ port });
|
||||||
|
server.start().catch(console.error);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
global.server = server;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { getPermission } from "fosscord-server-util";
|
import { getPermission } from "@fosscord/server-util";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const t = await getPermission("811642917432066048", "812327318532915201");
|
const t = await getPermission("811642917432066048", "812327318532915201");
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export {};
|
@ -1,4 +1,4 @@
|
|||||||
import { Config, Snowflake } from "fosscord-server-util";
|
import { Config, Snowflake } from "@fosscord/server-util";
|
||||||
import crypto from "crypto";
|
import crypto from "crypto";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ export default {
|
|||||||
setAll: Config.setAll,
|
setAll: Config.setAll,
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface RateLimit {
|
export interface RateLimitOptions {
|
||||||
count: number;
|
count: number;
|
||||||
timespan: number;
|
timespan: number;
|
||||||
}
|
}
|
||||||
@ -62,8 +62,8 @@ export interface DefaultOptions {
|
|||||||
};
|
};
|
||||||
routes: {
|
routes: {
|
||||||
auth?: {
|
auth?: {
|
||||||
login?: RateLimit;
|
login?: RateLimitOptions;
|
||||||
register?: RateLimit;
|
register?: RateLimitOptions;
|
||||||
};
|
};
|
||||||
channel?: {};
|
channel?: {};
|
||||||
// TODO: rate limit configuration for all routes
|
// TODO: rate limit configuration for all routes
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Event, EventModel } from "fosscord-server-util";
|
import { Event, EventModel } from "@fosscord/server-util";
|
||||||
|
|
||||||
export async function emitEvent(payload: Omit<Event, "created_at">) {
|
export async function emitEvent(payload: Omit<Event, "created_at">) {
|
||||||
const obj = {
|
const obj = {
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
GuildModel,
|
GuildModel,
|
||||||
MemberModel,
|
MemberModel,
|
||||||
UserModel,
|
UserModel,
|
||||||
} from "fosscord-server-util";
|
} from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
import Config from "./Config";
|
import Config from "./Config";
|
||||||
import { emitEvent } from "./Event";
|
import { emitEvent } from "./Event";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { toObject, UserModel } from "fosscord-server-util";
|
import { toObject, UserModel } from "@fosscord/server-util";
|
||||||
import { HTTPError } from "lambert-server";
|
import { HTTPError } from "lambert-server";
|
||||||
|
|
||||||
export const PublicUserProjection = {
|
export const PublicUserProjection = {
|
||||||
|
@ -34,6 +34,9 @@ export function FieldErrors(fields: Record<string, { code?: string; message: str
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement Image data type: Data URI scheme that supports JPG, GIF, and PNG formats. An example Data URI format is: data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA
|
||||||
|
// Ensure you use the proper content type (image/jpeg, image/png, image/gif) that matches the image data being provided.
|
||||||
|
|
||||||
export class FieldError extends Error {
|
export class FieldError extends Error {
|
||||||
constructor(public code: string | number, public message: string, public errors?: any) {
|
constructor(public code: string | number, public message: string, public errors?: any) {
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"include": ["src/**/*.ts"],
|
"include": ["src/**/*.ts", "src/test/rethink_test.ts.disabled"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user