Move schemas to /src/util/schemas

This commit is contained in:
Madeline 2022-09-26 22:08:14 +10:00
parent 186f28516c
commit 849b257db7
94 changed files with 18692 additions and 18808 deletions

File diff suppressed because it is too large Load Diff

340
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -72,15 +72,16 @@
"sqlite3": "^5.1.1",
"typeorm": "^0.3.10",
"typescript-json-schema": "^0.50.1",
"ws": "^8.9.0"
},
"optionalDependencies": {
"ws": "^8.9.0",
"@aws-sdk/client-s3": "^3.178.0",
"@sentry/node": "^7.13.0",
"@sentry/tracing": "^7.13.0",
"@yukikaze-bot/erlpack": "^1.0.1",
"amqplib": "^0.10.3"
},
"optionalDependencies": {
"@yukikaze-bot/erlpack": "^1.0.1"
},
"_moduleAliases": {
"@fosscord/api": "dist/api",
"@fosscord/cdn": "dist/cdn",

View File

@ -3,7 +3,6 @@
const path = require("path");
const fs = require("fs");
const TJS = require("typescript-json-schema");
require("missing-native-js-functions");
const schemaPath = path.join(__dirname, "..", "assets", "schemas.json");
const settings = {

View File

@ -1,21 +1,12 @@
import { Request, Response, Router } from "express";
import { route, getIpAdress, verifyCaptcha } from "@fosscord/api";
import bcrypt from "bcrypt";
import { Config, User, generateToken, adjustEmail, FieldErrors } from "@fosscord/util";
import { Config, User, generateToken, adjustEmail, FieldErrors, LoginSchema } from "@fosscord/util";
import crypto from "crypto";
const router: Router = Router();
export default router;
export interface LoginSchema {
login: string;
password: string;
undelete?: boolean;
captcha_key?: string;
login_source?: string;
gift_code_sku_id?: string;
}
router.post("/", route({ body: "LoginSchema" }), async (req: Request, res: Response) => {
const { login, password, captcha_key, undelete } = req.body as LoginSchema;
const email = adjustEmail(login);

View File

@ -1,17 +1,10 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { BackupCode, FieldErrors, generateToken, User } from "@fosscord/util";
import { BackupCode, generateToken, User, TotpSchema } from "@fosscord/util";
import { verifyToken } from "node-2fa";
import { HTTPError } from "lambert-server";
const router = Router();
export interface TotpSchema {
code: string,
ticket: string,
gift_code_sku_id?: string | null,
login_source?: string | null,
}
router.post("/", route({ body: "TotpSchema" }), async (req: Request, res: Response) => {
const { code, ticket, gift_code_sku_id, login_source } = req.body as TotpSchema;

View File

@ -1,40 +1,11 @@
import { Request, Response, Router } from "express";
import { Config, generateToken, Invite, FieldErrors, User, adjustEmail } from "@fosscord/util";
import { Config, generateToken, Invite, FieldErrors, User, adjustEmail, RegisterSchema } from "@fosscord/util";
import { route, getIpAdress, IPAnalysis, isProxy, verifyCaptcha } from "@fosscord/api";
import "missing-native-js-functions";
import bcrypt from "bcrypt";
import { HTTPError } from "lambert-server";
const router: Router = Router();
export interface RegisterSchema {
/**
* @minLength 2
* @maxLength 32
*/
username: string;
/**
* @minLength 1
* @maxLength 72
*/
password?: string;
consent: boolean;
/**
* @TJS-format email
*/
email?: string;
fingerprint?: string;
invite?: string;
/**
* @TJS-type string
*/
date_of_birth?: Date; // "2000-04-03"
gift_code_sku_id?: string;
captcha_key?: string;
promotional_email_opt_in?: boolean;
}
router.post("/", route({ body: "RegisterSchema" }), async (req: Request, res: Response) => {
const body = req.body as RegisterSchema;
const { register, security } = Config.get();

View File

@ -1,13 +1,9 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { FieldErrors, User } from "@fosscord/util";
import { FieldErrors, User, BackupCodesChallengeSchema } from "@fosscord/util";
import bcrypt from "bcrypt";
const router = Router();
export interface BackupCodesChallengeSchema {
password: string;
}
router.post("/", route({ body: "BackupCodesChallengeSchema" }), async (req: Request, res: Response) => {
const { password } = req.body as BackupCodesChallengeSchema;

View File

@ -1,12 +1,12 @@
import {
Channel,
ChannelDeleteEvent,
ChannelPermissionOverwriteType,
ChannelType,
ChannelUpdateEvent,
emitEvent,
Recipient,
handleFile
handleFile,
ChannelModifySchema
} from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
@ -47,35 +47,6 @@ router.delete("/", route({ permission: "MANAGE_CHANNELS" }), async (req: Request
res.send(channel);
});
export interface ChannelModifySchema {
/**
* @maxLength 100
*/
name?: string;
type?: ChannelType;
topic?: string;
icon?: string | null;
bitrate?: number;
user_limit?: number;
rate_limit_per_user?: number;
position?: number;
permission_overwrites?: {
id: string;
type: ChannelPermissionOverwriteType;
allow: string;
deny: string;
}[];
parent_id?: string;
id?: string; // is not used (only for guild create)
nsfw?: boolean;
rtc_region?: string;
default_auto_archive_duration?: number;
default_reaction_emoji?: string | null;
flags?: number;
default_thread_rate_limit_per_user?: number;
video_quality_mode?: number;
}
router.patch("/", route({ body: "ChannelModifySchema", permission: "MANAGE_CHANNELS" }), async (req: Request, res: Response) => {
var payload = req.body as ChannelModifySchema;
const { channel_id } = req.params;

View File

@ -7,18 +7,6 @@ import { isTextChannel } from "./messages";
const router: Router = Router();
export interface InviteCreateSchema {
target_user_id?: string;
target_type?: string;
validate?: string; // ? what is this
max_age?: number;
max_uses?: number;
temporary?: boolean;
unique?: boolean;
target_user?: string;
target_user_type?: number;
}
router.post("/", route({ body: "InviteCreateSchema", permission: "CREATE_INSTANT_INVITE", right: "CREATE_INVITES" }),
async (req: Request, res: Response) => {
const { user_id } = req;

View File

@ -1,4 +1,4 @@
import { emitEvent, getPermission, MessageAckEvent, ReadState, Snowflake } from "@fosscord/util";
import { emitEvent, getPermission, MessageAckEvent, ReadState } from "@fosscord/util";
import { Request, Response, Router } from "express";
import { route } from "@fosscord/api";
@ -8,11 +8,6 @@ const router = Router();
// TODO: send read state event to all channel members
// TODO: advance-only notification cursor
export interface MessageAcknowledgeSchema {
manual?: boolean;
mention_count?: number;
}
router.post("/", route({ body: "MessageAcknowledgeSchema" }), async (req: Request, res: Response) => {
const { channel_id, message_id } = req.params;

View File

@ -1,8 +1,6 @@
import {
Attachment,
Channel,
Embed,
DiscordApiErrors,
emitEvent,
FosscordApiErrors,
getPermission,
@ -12,13 +10,13 @@ import {
MessageDeleteEvent,
MessageUpdateEvent,
Snowflake,
uploadFile
uploadFile,
MessageCreateSchema,
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import multer from "multer";
import { route } from "@fosscord/api";
import { handleMessage, postHandleMessage } from "@fosscord/api";
import { MessageCreateSchema } from "../index";
import { HTTPError } from "lambert-server";
const router = Router();

View File

@ -2,16 +2,11 @@ import { Router, Response, Request } from "express";
import { Channel, Config, emitEvent, getPermission, getRights, MessageDeleteBulkEvent, Message } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { In } from "typeorm";
const router: Router = Router();
export default router;
export interface BulkDeleteSchema {
messages: string[];
}
// should users be able to bulk delete messages or only bots? ANSWER: all users
// should this request fail, if you provide messages older than 14 days/invalid ids? ANSWER: NO
// https://discord.com/developers/docs/resources/channel#bulk-delete-messages

View File

@ -5,16 +5,15 @@ import {
ChannelType,
Config,
DmChannelDTO,
Embed,
emitEvent,
getPermission,
getRights,
Message,
MessageCreateEvent,
Snowflake,
uploadFile,
Member,
Role,
MessageCreateSchema,
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { handleMessage, postHandleMessage, route } from "@fosscord/api";
@ -50,38 +49,6 @@ export function isTextChannel(type: ChannelType): boolean {
}
}
export interface MessageCreateSchema {
type?: number;
content?: string;
nonce?: string;
channel_id?: string;
tts?: boolean;
flags?: string;
embeds?: Embed[];
embed?: Embed;
// TODO: ^ embed is deprecated in favor of embeds (https://discord.com/developers/docs/resources/channel#message-object)
allowed_mentions?: {
parse?: string[];
roles?: string[];
users?: string[];
replied_user?: boolean;
};
message_reference?: {
message_id: string;
channel_id: string;
guild_id?: string;
fail_if_not_exists?: boolean;
};
payload_json?: string;
file?: any;
/**
TODO: we should create an interface for attachments
TODO: OpenWAAO<-->attachment-style metadata conversion
**/
attachments?: any[];
sticker_ids?: string[];
}
// https://discord.com/developers/docs/resources/channel#create-message
// get messages
router.get("/", async (req: Request, res: Response) => {

View File

@ -3,33 +3,21 @@ import { route } from "@fosscord/api";
import { isTextChannel } from "./messages";
import { FindManyOptions, Between, Not } from "typeorm";
import {
Attachment,
Channel,
Config,
Embed,
DiscordApiErrors,
emitEvent,
FosscordApiErrors,
getPermission,
getRights,
Message,
MessageDeleteBulkEvent,
Snowflake,
uploadFile
PurgeSchema,
} from "@fosscord/util";
import { Router, Response, Request } from "express";
import multer from "multer";
import { handleMessage, postHandleMessage } from "@fosscord/api";
const router: Router = Router();
export default router;
export interface PurgeSchema {
before: string;
after: string;
}
/**
TODO: apply the delete bit by bit to prevent client and database stress
**/

View File

@ -1,19 +1,12 @@
import { Router, Response, Request } from "express";
import { route } from "@fosscord/api";
import { Channel, Config, getPermission, trimSpecial, Webhook } from "@fosscord/util";
import { Channel, Config, trimSpecial, Webhook } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { isTextChannel } from "./messages/index";
import { DiscordApiErrors } from "@fosscord/util";
const router: Router = Router();
// TODO: webhooks
export interface WebhookCreateSchema {
/**
* @maxLength 80
*/
name: string;
avatar: string;
}
//TODO: implement webhooks
router.get("/", route({}), async (req: Request, res: Response) => {
res.json([]);

View File

@ -1,8 +1,5 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../../channels/#channel_id";
const router = Router();
//TODO: implement audit logs

View File

@ -1,30 +1,8 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, GuildBanAddEvent, GuildBanRemoveEvent, Ban, User, Member } from "@fosscord/util";
import { DiscordApiErrors, emitEvent, GuildBanAddEvent, GuildBanRemoveEvent, Ban, User, Member, BanRegistrySchema, BanModeratorSchema } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { getIpAdress, route } from "@fosscord/api";
export interface BanCreateSchema {
delete_message_days?: string;
reason?: string;
};
export interface BanRegistrySchema {
id: string;
user_id: string;
guild_id: string;
executor_id: string;
ip?: string;
reason?: string | undefined;
};
export interface BanModeratorSchema {
id: string;
user_id: string;
guild_id: string;
executor_id: string;
reason?: string | undefined;
};
const router: Router = Router();
/* TODO: Deleting the secrets is just a temporary go-around. Views should be implemented for both safety and better handling. */

View File

@ -1,8 +1,7 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { Channel, ChannelUpdateEvent, emitEvent, ChannelModifySchema } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../../channels/#channel_id";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {

View File

@ -1,21 +1,9 @@
import { Router, Request, Response } from "express";
import { Config, DiscordApiErrors, emitEvent, Emoji, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User } from "@fosscord/util";
import { Config, DiscordApiErrors, emitEvent, Emoji, GuildEmojisUpdateEvent, handleFile, Member, Snowflake, User, EmojiCreateSchema, EmojiModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api";
const router = Router();
export interface EmojiCreateSchema {
name?: string;
image: string;
require_colons?: boolean | null;
roles?: string[];
}
export interface EmojiModifySchema {
name?: string;
roles?: string[];
}
router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params;

View File

@ -1,9 +1,7 @@
import { Request, Response, Router } from "express";
import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, handleFile, Member } from "@fosscord/util";
import { DiscordApiErrors, emitEvent, getPermission, getRights, Guild, GuildUpdateEvent, handleFile, Member, GuildCreateSchema } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import "missing-native-js-functions";
import { GuildCreateSchema } from "../index";
const router = Router();

View File

@ -1,8 +1,5 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../../channels/#channel_id";
const router = Router();
//TODO: implement integrations list

View File

@ -1,15 +1,9 @@
import { Request, Response, Router } from "express";
import { Member, getPermission, getRights, Role, GuildMemberUpdateEvent, emitEvent, Sticker, Emoji, Rights, Guild } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { Member, getPermission, getRights, Role, GuildMemberUpdateEvent, emitEvent, Sticker, Emoji, Guild, MemberChangeSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
const router = Router();
export interface MemberChangeSchema {
roles?: string[];
nick?: string;
}
router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id, member_id } = req.params;
await Member.IsInGuildOrFail(req.user_id, guild_id);

View File

@ -4,10 +4,6 @@ import { Request, Response, Router } from "express";
const router = Router();
export interface MemberNickChangeSchema {
nick: string;
}
router.patch("/", route({ body: "MemberNickChangeSchema" }), async (req: Request, res: Response) => {
var { guild_id, member_id } = req.params;
var permissionString: PermissionResolvable = "MANAGE_NICKNAMES";

View File

@ -62,13 +62,6 @@ router.get("/", route({}), async (req: Request, res: Response) => {
res.send({ pruned: members.length });
});
export interface PruneSchema {
/**
* @min 0
*/
days: number;
}
router.post("/", route({ permission: "KICK_MEMBERS", right: "KICK_BAN_MEMBERS" }), async (req: Request, res: Response) => {
const days = parseInt(req.body.days);

View File

@ -1,8 +1,7 @@
import { Router, Request, Response } from "express";
import { Role, Member, GuildRoleUpdateEvent, GuildRoleDeleteEvent, emitEvent, handleFile } from "@fosscord/util";
import { Role, Member, GuildRoleUpdateEvent, GuildRoleDeleteEvent, emitEvent, handleFile, RoleModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import { HTTPError } from "lambert-server";
import { RoleModifySchema } from "../";
const router = Router();

View File

@ -5,28 +5,15 @@ import {
Member,
GuildRoleCreateEvent,
GuildRoleUpdateEvent,
GuildRoleDeleteEvent,
emitEvent,
Config,
DiscordApiErrors,
handleFile
RoleModifySchema,
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
const router: Router = Router();
export interface RoleModifySchema {
name?: string;
permissions?: string;
color?: number;
hoist?: boolean; // whether the role should be displayed separately in the sidebar
mentionable?: boolean; // whether the role should be mentionable
position?: number;
icon?: string;
unicode_emoji?: string;
}
export type RolePositionUpdateSchema = {
id: string;
position: number;

View File

@ -1,13 +1,13 @@
import {
emitEvent,
GuildStickersUpdateEvent,
handleFile,
Member,
Snowflake,
Sticker,
StickerFormatType,
StickerType,
uploadFile
uploadFile,
ModifyGuildStickerSchema,
} from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
@ -82,22 +82,6 @@ router.get("/:sticker_id", route({}), async (req: Request, res: Response) => {
res.json(await Sticker.findOneOrFail({ where: { guild_id, id: sticker_id } }));
});
export interface ModifyGuildStickerSchema {
/**
* @minLength 2
* @maxLength 30
*/
name: string;
/**
* @maxLength 100
*/
description?: string;
/**
* @maxLength 200
*/
tags: string;
}
router.patch(
"/:sticker_id",
route({ body: "ModifyGuildStickerSchema", permission: "MANAGE_EMOJIS_AND_STICKERS" }),

View File

@ -23,16 +23,6 @@ const TemplateGuildProjection: (keyof Guild)[] = [
"icon"
];
export interface TemplateCreateSchema {
name: string;
description?: string;
}
export interface TemplateModifySchema {
name: string;
description?: string;
}
router.get("/", route({}), async (req: Request, res: Response) => {
const { guild_id } = req.params;

View File

@ -1,4 +1,4 @@
import { Channel, ChannelType, getPermission, Guild, Invite, trimSpecial } from "@fosscord/util";
import { Channel, ChannelType, Guild, Invite, VanityUrlSchema } from "@fosscord/util";
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { HTTPError } from "lambert-server";
@ -24,14 +24,6 @@ router.get("/", route({ permission: "MANAGE_GUILD" }), async (req: Request, res:
}
});
export interface VanityUrlSchema {
/**
* @minLength 1
* @maxLength 20
*/
code?: string;
}
router.patch("/", route({ body: "VanityUrlSchema", permission: "MANAGE_GUILD" }), async (req: Request, res: Response) => {
const { guild_id } = req.params;
const body = req.body as VanityUrlSchema;

View File

@ -1,20 +1,10 @@
import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent } from "@fosscord/util";
import { Channel, ChannelType, DiscordApiErrors, emitEvent, getPermission, VoiceState, VoiceStateUpdateEvent, VoiceStateUpdateSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import { Request, Response, Router } from "express";
const router = Router();
//TODO need more testing when community guild and voice stage channel are working
export interface VoiceStateUpdateSchema {
channel_id: string;
guild_id?: string;
suppress?: boolean;
request_to_speak_timestamp?: Date;
self_mute?: boolean;
self_deaf?: boolean;
self_video?: boolean;
}
router.patch("/", route({ body: "VoiceStateUpdateSchema" }), async (req: Request, res: Response) => {
const body = req.body as VoiceStateUpdateSchema;
var { guild_id, user_id } = req.params;

View File

@ -1,8 +1,5 @@
import { Router, Response, Request } from "express";
import { Channel, ChannelUpdateEvent, getPermission, emitEvent } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../../channels/#channel_id";
const router = Router();
//TODO: implement webhooks

View File

@ -1,21 +1,10 @@
import { Request, Response, Router } from "express";
import { Guild, getPermission, Snowflake, Member } from "@fosscord/util";
import { Guild, Member, GuildUpdateWelcomeScreenSchema } from "@fosscord/util";
import { HTTPError } from "lambert-server";
import { route } from "@fosscord/api";
const router: Router = Router();
export interface GuildUpdateWelcomeScreenSchema {
welcome_channels?: {
channel_id: string;
description: string;
emoji_id?: string;
emoji_name?: string;
}[];
enabled?: boolean;
description?: string;
}
router.get("/", route({}), async (req: Request, res: Response) => {
const guild_id = req.params.guild_id;

View File

@ -1,12 +1,7 @@
import { Request, Response, Router } from "express";
import { Guild } from "@fosscord/util";
import { Guild, WidgetModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api";
export interface WidgetModifySchema {
enabled: boolean; // whether the widget is enabled
channel_id: string; // the widget channel id
}
const router: Router = Router();
// https://discord.com/developers/docs/resources/guild#get-guild-widget-settings

View File

@ -1,23 +1,9 @@
import { Router, Request, Response } from "express";
import { Role, Guild, Snowflake, Config, getRights, Member, Channel, DiscordApiErrors, handleFile } from "@fosscord/util";
import { Role, Guild, Config, getRights, Member, DiscordApiErrors, GuildCreateSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import { ChannelModifySchema } from "../channels/#channel_id";
const router: Router = Router();
export interface GuildCreateSchema {
/**
* @maxLength 100
*/
name?: string;
region?: string;
icon?: string | null;
channels?: ChannelModifySchema[];
guild_template_code?: string;
system_channel_id?: string;
rules_channel_id?: string;
}
//TODO: create default channel
router.post("/", route({ body: "GuildCreateSchema", right: "CREATE_GUILDS" }), async (req: Request, res: Response) => {

View File

@ -1,15 +1,10 @@
import { Request, Response, Router } from "express";
import { Template, Guild, Role, Snowflake, Config, User, Member } from "@fosscord/util";
import { Template, Guild, Role, Snowflake, Config, Member, GuildTemplateCreateSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import { DiscordApiErrors } from "@fosscord/util";
import fetch from "node-fetch";
const router: Router = Router();
export interface GuildTemplateCreateSchema {
name: string;
avatar?: string | null;
}
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);

View File

@ -1,5 +1,5 @@
import { Request, Response, Router } from "express";
import { Recipient, DmChannelDTO, Channel } from "@fosscord/util";
import { Recipient, DmChannelDTO, Channel, DmChannelCreateSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
const router: Router = Router();
@ -12,11 +12,6 @@ router.get("/", route({}), async (req: Request, res: Response) => {
res.json(await Promise.all(recipients.map((r) => DmChannelDTO.from(r.channel, [req.user_id]))));
});
export interface DmChannelCreateSchema {
name?: string;
recipients: string[];
}
router.post("/", route({ body: "DmChannelCreateSchema" }), async (req: Request, res: Response) => {
const body = req.body as DmChannelCreateSchema;
res.json(await Channel.createDMChannel(body.recipients, req.user_id, body.name));

View File

@ -1,31 +1,11 @@
import { Router, Request, Response } from "express";
import { User, PrivateUserProjection, emitEvent, UserUpdateEvent, handleFile, FieldErrors, adjustEmail, Config } from "@fosscord/util";
import { User, PrivateUserProjection, emitEvent, UserUpdateEvent, handleFile, FieldErrors, adjustEmail, Config, UserModifySchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import bcrypt from "bcrypt";
import { HTTPError } from "lambert-server";
const router: Router = Router();
export interface UserModifySchema {
/**
* @minLength 1
* @maxLength 100
*/
username?: string;
avatar?: string | null;
/**
* @maxLength 1024
*/
bio?: string;
accent_color?: number;
banner?: string | null;
password?: string;
new_password?: string;
code?: string;
email?: string;
discriminator?: string;
}
router.get("/", route({}), async (req: Request, res: Response) => {
res.json(await User.findOne({ select: PrivateUserProjection, where: { id: req.user_id } }));
});

View File

@ -1,15 +1,9 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { BackupCode, generateMfaBackupCodes, User } from "@fosscord/util";
import { BackupCode, generateMfaBackupCodes, User, CodesVerificationSchema } from "@fosscord/util";
const router = Router();
export interface CodesVerificationSchema {
key: string;
nonce: string;
regenerate?: boolean;
}
router.post("/", route({ body: "CodesVerificationSchema" }), async (req: Request, res: Response) => {
const { key, nonce, regenerate } = req.body as CodesVerificationSchema;

View File

@ -1,15 +1,10 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { BackupCode, FieldErrors, generateMfaBackupCodes, User } from "@fosscord/util";
import { BackupCode, FieldErrors, generateMfaBackupCodes, User, MfaCodesSchema } from "@fosscord/util";
import bcrypt from "bcrypt";
const router = Router();
export interface MfaCodesSchema {
password: string;
regenerate?: boolean;
}
// TODO: This route is replaced with users/@me/mfa/codes-verification in newer clients
router.post("/", route({ body: "MfaCodesSchema" }), async (req: Request, res: Response) => {

View File

@ -2,14 +2,10 @@ import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { verifyToken } from 'node-2fa';
import { HTTPError } from "lambert-server";
import { User, generateToken, BackupCode } from "@fosscord/util";
import { User, generateToken, BackupCode, TotpDisableSchema } from "@fosscord/util";
const router = Router();
export interface TotpDisableSchema {
code: string;
}
router.post("/", route({ body: "TotpDisableSchema" }), async (req: Request, res: Response) => {
const body = req.body as TotpDisableSchema;

View File

@ -1,19 +1,12 @@
import { Router, Request, Response } from "express";
import { User, generateToken, BackupCode, generateMfaBackupCodes } from "@fosscord/util";
import { User, generateToken, generateMfaBackupCodes, TotpEnableSchema } from "@fosscord/util";
import { route } from "@fosscord/api";
import bcrypt from "bcrypt";
import { HTTPError } from "lambert-server";
import { verifyToken } from 'node-2fa';
import crypto from "crypto";
const router = Router();
export interface TotpEnableSchema {
password: string;
code?: string;
secret?: string;
}
router.post("/", route({ body: "TotpEnableSchema" }), async (req: Request, res: Response) => {
const body = req.body as TotpEnableSchema;

View File

@ -37,10 +37,6 @@ router.get("/", route({}), async (req: Request, res: Response) => {
return res.json(related_users);
});
export interface RelationshipPutSchema {
type?: RelationshipType;
}
router.put("/:id", route({ body: "RelationshipPutSchema" }), async (req: Request, res: Response) => {
return await updateRelationship(
req,
@ -50,11 +46,6 @@ router.put("/:id", route({ body: "RelationshipPutSchema" }), async (req: Request
);
});
export interface RelationshipPostSchema {
discriminator: string;
username: string;
}
router.post("/", route({ body: "RelationshipPostSchema" }), async (req: Request, res: Response) => {
return await updateRelationship(
req,

View File

@ -22,11 +22,11 @@ import {
Attachment,
Config,
Sticker,
MessageCreateSchema,
} from "@fosscord/util";
import { HTTPError } from "lambert-server";
import fetch from "node-fetch";
import cheerio from "cheerio";
import { MessageCreateSchema } from "../../routes/channels/#channel_id/messages";
import { In } from "typeorm";
const allow_empty = false;
// TODO: check webhook, application, system author, stickers

View File

@ -36,7 +36,7 @@ export async function Message(this: WebSocket, buffer: WS.Data) {
data = bigIntJson.parse(buffer as string);
}
else if (typeof buffer == "string") {
data = bigIntJson.parse(buffer as string)
data = bigIntJson.parse(buffer as string);
}
else return;
@ -51,14 +51,14 @@ export async function Message(this: WebSocket, buffer: WS.Data) {
return;
}
// const transaction = Sentry.startTransaction({
// op: OPCODES[data.op],
// name: `GATEWAY ${OPCODES[data.op]}`,
// data: {
// ...data.d,
// token: data?.d?.token ? "[Redacted]" : undefined,
// },
// });
const transaction = Sentry.startTransaction({
op: OPCODES[data.op],
name: `GATEWAY ${OPCODES[data.op]}`,
data: {
...data.d,
token: data?.d?.token ? "[Redacted]" : undefined,
},
});
try {
var ret = await OPCodeHandler.call(this, data);
@ -66,7 +66,7 @@ export async function Message(this: WebSocket, buffer: WS.Data) {
return ret;
} catch (error) {
Sentry.captureException(error);
// transaction.finish();
transaction.finish();
console.error(`Error: Op ${data.op}`, error);
// if (!this.CLOSED && this.CLOSING)
return this.close(CLOSECODES.Unknown_error);

View File

@ -20,12 +20,12 @@ import {
PresenceUpdateEvent,
DefaultUserGuildSettings,
UserGuildSettings,
IdentifySchema,
} from "@fosscord/util";
import { Send } from "../util/Send";
import { CLOSECODES, OPCODES } from "../util/Constants";
import { genSessionId } from "../util/SessionUtils";
import { setupListener } from "../listener/listener";
import { IdentifySchema } from "../schema/Identify";
// import experiments from "./experiments.json";
const experiments: any = [];
import { check } from "./instanceOf";

View File

@ -1,6 +1,5 @@
import { getDatabase, getPermission, listenEvent, Member, Role, Session } from "@fosscord/util";
import { getDatabase, getPermission, listenEvent, Member, Role, Session, LazyRequestSchema } from "@fosscord/util";
import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send } from "@fosscord/gateway";
import { LazyRequest } from "../schema/LazyRequest";
import { check } from "./instanceOf";
// TODO: only show roles/members that have access to this channel
@ -137,8 +136,8 @@ async function getMembers(guild_id: string, range: [number, number]) {
export async function onLazyRequest(this: WebSocket, { d }: Payload) {
// TODO: check data
check.call(this, LazyRequest, d);
const { guild_id, typing, channels, activities } = d as LazyRequest;
check.call(this, LazyRequestSchema, d);
const { guild_id, typing, channels, activities } = d as LazyRequestSchema;
const channel_id = Object.keys(channels || {}).first();
if (!channel_id) return;

View File

@ -1,6 +1,5 @@
import { WebSocket, Payload } from "@fosscord/gateway";
import { emitEvent, PresenceUpdateEvent, Session, User } from "@fosscord/util";
import { ActivitySchema } from "../schema/Activity";
import { emitEvent, PresenceUpdateEvent, Session, User, ActivitySchema } from "@fosscord/util";
import { check } from "./instanceOf";
export async function onPresenceUpdate(this: WebSocket, { d }: Payload) {

View File

@ -1,4 +1,3 @@
import { VoiceStateUpdateSchema } from "../schema/VoiceStateUpdateSchema";
import { Payload, WebSocket } from "@fosscord/gateway";
import { genVoiceToken } from "../util/SessionUtils";
import { check } from "./instanceOf";
@ -11,6 +10,7 @@ import {
VoiceServerUpdateEvent,
VoiceState,
VoiceStateUpdateEvent,
VoiceStateUpdateSchema,
} from "@fosscord/util";
// TODO: check if a voice server is setup
// Notice: Bot users respect the voice channel's user limit, if set. When the voice channel is full, you will not receive the Voice State Update or Voice Server Update events in response to your own Voice State Update. Having MANAGE_CHANNELS permission bypasses this limit and allows you to join regardless of the channel being full or not.

View File

@ -0,0 +1,3 @@
export interface BackupCodesChallengeSchema {
password: string;
}

View File

@ -0,0 +1,4 @@
export interface BanCreateSchema {
delete_message_days?: string;
reason?: string;
};

View File

@ -0,0 +1,7 @@
export interface BanModeratorSchema {
id: string;
user_id: string;
guild_id: string;
executor_id: string;
reason?: string | undefined;
};

View File

@ -0,0 +1,8 @@
export interface BanRegistrySchema {
id: string;
user_id: string;
guild_id: string;
executor_id: string;
ip?: string;
reason?: string | undefined;
};

View File

@ -0,0 +1,3 @@
export interface BulkDeleteSchema {
messages: string[];
}

View File

@ -0,0 +1,30 @@
import { ChannelPermissionOverwriteType, ChannelType } from "@fosscord/util";
export interface ChannelModifySchema {
/**
* @maxLength 100
*/
name?: string;
type?: ChannelType;
topic?: string;
icon?: string | null;
bitrate?: number;
user_limit?: number;
rate_limit_per_user?: number;
position?: number;
permission_overwrites?: {
id: string;
type: ChannelPermissionOverwriteType;
allow: string;
deny: string;
}[];
parent_id?: string;
id?: string; // is not used (only for guild create)
nsfw?: boolean;
rtc_region?: string;
default_auto_archive_duration?: number;
default_reaction_emoji?: string | null;
flags?: number;
default_thread_rate_limit_per_user?: number;
video_quality_mode?: number;
}

View File

@ -0,0 +1,5 @@
export interface CodesVerificationSchema {
key: string;
nonce: string;
regenerate?: boolean;
}

View File

@ -0,0 +1,4 @@
export interface DmChannelCreateSchema {
name?: string;
recipients: string[];
}

View File

@ -0,0 +1,6 @@
export interface EmojiCreateSchema {
name?: string;
image: string;
require_colons?: boolean | null;
roles?: string[];
}

View File

@ -0,0 +1,4 @@
export interface EmojiModifySchema {
name?: string;
roles?: string[];
}

View File

@ -0,0 +1,14 @@
import { ChannelModifySchema } from ".";
export interface GuildCreateSchema {
/**
* @maxLength 100
*/
name?: string;
region?: string;
icon?: string | null;
channels?: ChannelModifySchema[];
guild_template_code?: string;
system_channel_id?: string;
rules_channel_id?: string;
}

View File

@ -0,0 +1,4 @@
export interface GuildTemplateCreateSchema {
name: string;
avatar?: string | null;
}

View File

@ -0,0 +1,10 @@
export interface GuildUpdateWelcomeScreenSchema {
welcome_channels?: {
channel_id: string;
description: string;
emoji_id?: string;
emoji_name?: string;
}[];
enabled?: boolean;
description?: string;
}

View File

@ -1,4 +1,4 @@
import { ActivitySchema } from "./Activity";
import { ActivitySchema } from "@fosscord/util";
export const IdentifySchema = {
token: String,

View File

@ -0,0 +1,11 @@
export interface InviteCreateSchema {
target_user_id?: string;
target_type?: string;
validate?: string; // ? what is this
max_age?: number;
max_uses?: number;
temporary?: boolean;
unique?: boolean;
target_user?: string;
target_user_type?: number;
}

View File

@ -1,4 +1,4 @@
export interface LazyRequest {
export interface LazyRequestSchema {
guild_id: string;
channels?: Record<string, [number, number][]>;
activities?: boolean;
@ -8,7 +8,7 @@ export interface LazyRequest {
thread_member_lists?: any[];
}
export const LazyRequest = {
export const LazyRequestSchema = {
guild_id: String,
$activities: Boolean,
$channels: Object,

View File

@ -0,0 +1,8 @@
export interface LoginSchema {
login: string;
password: string;
undelete?: boolean;
captcha_key?: string;
login_source?: string;
gift_code_sku_id?: string;
}

View File

@ -0,0 +1,4 @@
export interface MemberChangeSchema {
roles?: string[];
nick?: string;
}

View File

@ -0,0 +1,3 @@
export interface MemberNickChangeSchema {
nick: string;
}

View File

@ -0,0 +1,4 @@
export interface MessageAcknowledgeSchema {
manual?: boolean;
mention_count?: number;
}

View File

@ -0,0 +1,33 @@
import { Embed } from "@fosscord/util";
export interface MessageCreateSchema {
type?: number;
content?: string;
nonce?: string;
channel_id?: string;
tts?: boolean;
flags?: string;
embeds?: Embed[];
embed?: Embed;
// TODO: ^ embed is deprecated in favor of embeds (https://discord.com/developers/docs/resources/channel#message-object)
allowed_mentions?: {
parse?: string[];
roles?: string[];
users?: string[];
replied_user?: boolean;
};
message_reference?: {
message_id: string;
channel_id: string;
guild_id?: string;
fail_if_not_exists?: boolean;
};
payload_json?: string;
file?: any;
/**
TODO: we should create an interface for attachments
TODO: OpenWAAO<-->attachment-style metadata conversion
**/
attachments?: any[];
sticker_ids?: string[];
}

View File

@ -0,0 +1,4 @@
export interface MfaCodesSchema {
password: string;
regenerate?: boolean;
}

View File

@ -0,0 +1,15 @@
export interface ModifyGuildStickerSchema {
/**
* @minLength 2
* @maxLength 30
*/
name: string;
/**
* @maxLength 100
*/
description?: string;
/**
* @maxLength 200
*/
tags: string;
}

View File

@ -0,0 +1,6 @@
export interface PruneSchema {
/**
* @min 0
*/
days: number;
}

View File

@ -0,0 +1,4 @@
export interface PurgeSchema {
before: string;
after: string;
}

View File

@ -0,0 +1,27 @@
export interface RegisterSchema {
/**
* @minLength 2
* @maxLength 32
*/
username: string;
/**
* @minLength 1
* @maxLength 72
*/
password?: string;
consent: boolean;
/**
* @TJS-format email
*/
email?: string;
fingerprint?: string;
invite?: string;
/**
* @TJS-type string
*/
date_of_birth?: Date; // "2000-04-03"
gift_code_sku_id?: string;
captcha_key?: string;
promotional_email_opt_in?: boolean;
}

View File

@ -0,0 +1,4 @@
export interface RelationshipPostSchema {
discriminator: string;
username: string;
}

View File

@ -0,0 +1,5 @@
import { RelationshipType } from "@fosscord/util";
export interface RelationshipPutSchema {
type?: RelationshipType;
}

View File

@ -0,0 +1,10 @@
export interface RoleModifySchema {
name?: string;
permissions?: string;
color?: number;
hoist?: boolean; // whether the role should be displayed separately in the sidebar
mentionable?: boolean; // whether the role should be mentionable
position?: number;
icon?: string;
unicode_emoji?: string;
}

View File

@ -0,0 +1,19 @@
export interface SelectProtocolSchema {
protocol: "webrtc" | "udp";
data:
| string
| {
address: string;
port: number;
mode: string;
};
sdp?: string;
codecs?: {
name: "opus" | "VP8" | "VP9" | "H264";
type: "audio" | "video";
priority: number;
payload_type: number;
rtx_payload_type?: number | null;
}[];
rtc_connection_id?: string; // uuid
}

View File

@ -0,0 +1,4 @@
export interface TemplateCreateSchema {
name: string;
description?: string;
}

View File

@ -0,0 +1,4 @@
export interface TemplateModifySchema {
name: string;
description?: string;
}

View File

@ -0,0 +1,3 @@
export interface TotpDisableSchema {
code: string;
}

View File

@ -0,0 +1,5 @@
export interface TotpEnableSchema {
password: string;
code?: string;
secret?: string;
}

View File

@ -0,0 +1,6 @@
export interface TotpSchema {
code: string,
ticket: string,
gift_code_sku_id?: string | null,
login_source?: string | null,
}

View File

@ -0,0 +1,19 @@
export interface UserModifySchema {
/**
* @minLength 1
* @maxLength 100
*/
username?: string;
avatar?: string | null;
/**
* @maxLength 1024
*/
bio?: string;
accent_color?: number;
banner?: string | null;
password?: string;
new_password?: string;
code?: string;
email?: string;
discriminator?: string;
}

View File

@ -0,0 +1,7 @@
export interface VanityUrlSchema {
/**
* @minLength 1
* @maxLength 20
*/
code?: string;
}

View File

@ -0,0 +1,12 @@
export interface VoiceIdentifySchema {
server_id: string;
user_id: string;
session_id: string;
token: string;
video?: boolean;
streams?: {
type: string;
rid: string;
quality: number;
}[];
}

View File

@ -1,12 +1,4 @@
export const VoiceStateUpdateSchema = {
$guild_id: String,
$channel_id: String,
self_mute: Boolean,
self_deaf: Boolean,
$self_video: Boolean, //required in docs but bots don't always send it
$preferred_region: String,
};
//TODO need more testing when community guild and voice stage channel are working
export interface VoiceStateUpdateSchema {
guild_id?: string;
channel_id?: string;
@ -14,4 +6,17 @@ export interface VoiceStateUpdateSchema {
self_deaf: boolean;
self_video?: boolean;
preferred_region?: string;
request_to_speak_timestamp?: Date;
suppress?: boolean;
}
export const VoiceStateUpdateSchema = {
$guild_id: String,
$channel_id: String,
self_mute: Boolean,
self_deaf: Boolean,
$self_video: Boolean, //required in docs but bots don't always send it
$preferred_region: String,
$request_to_speak_timestamp: Date,
$suppress: Boolean,
};

View File

@ -0,0 +1,17 @@
export interface VoiceVideoSchema {
audio_ssrc: number;
video_ssrc: number;
rtx_ssrc?: number;
user_id?: string;
streams?: {
type: "video" | "audio";
rid: string;
ssrc: number;
active: boolean;
quality: number;
rtx_ssrc: number;
max_bitrate: number;
max_framerate: number;
max_resolution: { type: string; width: number; height: number; };
}[];
}

View File

@ -0,0 +1,8 @@
// TODO: webhooks
export interface WebhookCreateSchema {
/**
* @maxLength 80
*/
name: string;
avatar: string;
}

View File

@ -0,0 +1,4 @@
export interface WidgetModifySchema {
enabled: boolean; // whether the widget is enabled
channel_id: string; // the widget channel id
}

View File

@ -1,2 +1,41 @@
export * from "./Validator";
export * from "./voice";
export * from "./SelectProtocolSchema";
export * from "./LoginSchema";
export * from "./RegisterSchema";
export * from "./TotpSchema";
export * from "./BackupCodesChallengeSchema";
export * from "./ChannelModifySchema";
export * from "./InviteCreateSchema";
export * from "./PurgeSchema";
export * from "./WebhookCreateSchema";
export * from "./MessageCreateSchema";
export * from "./MessageAcknowledgeSchema";
export * from "./GuildCreateSchema";
export * from "./BanCreateSchema";
export * from "./BanModeratorSchema";
export * from "./BanRegistrySchema";
export * from "./EmojiCreateSchema";
export * from "./EmojiModifySchema";
export * from "./ModifyGuildStickerSchema";
export * from "./TemplateCreateSchema";
export * from "./TemplateModifySchema";
export * from "./VanityUrlSchema";
export * from "./GuildUpdateWelcomeScreenSchema";
export * from "./WidgetModifySchema";
export * from "./MemberChangeSchema";
export * from "./RoleModifySchema";
export * from "./GuildTemplateCreateSchema";
export * from "./DmChannelCreateSchema";
export * from "./UserModifySchema";
export * from "./RelationshipPostSchema";
export * from "./RelationshipPutSchema";
export * from "./CodesVerificationSchema";
export * from "./MfaCodesSchema";
export * from "./TotpDisableSchema";
export * from "./TotpEnableSchema";
export * from "./VoiceIdentifySchema";
export * from "./VoiceStateUpdateSchema";
export * from "./VoiceVideoSchema";
export * from "./IdentifySchema";
export * from "./ActivitySchema";
export * from "./LazyRequestSchema";

View File

@ -1,69 +0,0 @@
export interface VoiceVideoSchema {
audio_ssrc: number;
video_ssrc: number;
rtx_ssrc?: number;
user_id?: string;
streams?: {
type: "video" | "audio";
rid: string;
ssrc: number;
active: boolean;
quality: number;
rtx_ssrc: number;
max_bitrate: number;
max_framerate: number;
max_resolution: { type: string; width: number; height: number; };
}[];
}
export const VoiceStateUpdateSchema = {
$guild_id: String,
$channel_id: String,
self_mute: Boolean,
self_deaf: Boolean,
self_video: Boolean
};
//TODO need more testing when community guild and voice stage channel are working
export interface VoiceStateUpdateSchema {
channel_id: string;
guild_id?: string;
suppress?: boolean;
request_to_speak_timestamp?: Date;
self_mute?: boolean;
self_deaf?: boolean;
self_video?: boolean;
}
export interface VoiceIdentifySchema {
server_id: string;
user_id: string;
session_id: string;
token: string;
video?: boolean;
streams?: {
type: string;
rid: string;
quality: number;
}[];
}
export interface SelectProtocolSchema {
protocol: "webrtc" | "udp";
data:
| string
| {
address: string;
port: number;
mode: string;
};
sdp?: string;
codecs?: {
name: "opus" | "VP8" | "VP9" | "H264";
type: "audio" | "video";
priority: number;
payload_type: number;
rtx_payload_type?: number | null;
}[];
rtc_connection_id?: string; // uuid
}