This commit is contained in:
Madeline
2022-10-28 15:25:58 +11:00
parent c00c70985c
commit e991e00f32
35 changed files with 185 additions and 1519 deletions
+2
View File
@@ -25,6 +25,8 @@ export const NO_AUTHORIZATION_ROUTES = [
"/track",
// Public policy pages
"/policies/instance",
// Oauth callback
"/oauth2/callback",
// Asset delivery
/\/guilds\/\d+\/widget\.(json|png)/,
];
+38
View File
@@ -0,0 +1,38 @@
import { Router, Request, Response } from "express";
import { route, OauthCallbackHandlers } from "@fosscord/api";
import { FieldErrors, generateToken, User } from "@fosscord/util";
const router = Router();
router.get("/:type", route({}), async (req: Request, res: Response) => {
const { type } = req.params;
const handler = OauthCallbackHandlers[type];
if (!handler) throw FieldErrors({
type: {
code: "BASE_TYPE_CHOICES",
message: `Value must be one of (${Object.keys(OauthCallbackHandlers).join(", ")}).`,
}
});
const { code } = req.query;
if (!code || typeof code !== "string") throw FieldErrors({ code: { code: "BASE_TYPE_REQUIRED", message: req.t("common:field.BASE_TYPE_REQUIRED"), } });
const access = await handler.getAccessToken(code);
const oauthUser = await handler.getUserDetals(access.access_token);
let user = await User.findOne({ where: { email: oauthUser.email } });
if (!user) {
user = await User.register({
email: oauthUser.email,
username: oauthUser.username,
req
});
// TODO: upload pfp, banner?
}
const token = await generateToken(user.id);
return { token };
});
export default router;
+21
View File
@@ -0,0 +1,21 @@
import { Router, Request, Response } from "express";
import { route } from "@fosscord/api";
import { Attachment, Config, Guild, Message, RateLimit, Session, User } from "@fosscord/util";
const router = Router();
router.get("/", route({}), async (req: Request, res: Response) => {
res.json({
all_time: {
users: await User.count(),
guilds: await Guild.count(),
messages: await Message.count(),
attachments: await Attachment.count(),
},
now: {
sessions: await Session.count(),
rate_limits: await RateLimit.count(),
}
});
});
export default router;
+83
View File
@@ -0,0 +1,83 @@
// TODO: Puyo's connections PR would replace this file
import { Config } from "@fosscord/util";
import fetch from "node-fetch";
export interface OauthAccessToken {
access_token: string;
token_type: string;
expires_in: string;
refresh_token: string;
scope: string;
};
export interface OauthUserDetails {
id: string;
email: string;
username: string;
avatar_url: string | null;
}
interface Connection {
getAccessToken: (code: string) => Promise<OauthAccessToken>;
getUserDetals: (token: string) => Promise<OauthUserDetails>;
}
const DiscordConnection: Connection = {
getAccessToken: async (code) => {
const { external } = Config.get();
const { discord } = external;
if (!discord.id || !discord.secret || !discord.redirect)
throw new Error("Discord Oauth has not been configured.")
const body = new URLSearchParams(
Object.entries({
client_id: discord.id as string,
client_secret: discord.secret as string,
redirect_uri: discord.redirect as string,
code: code as string,
grant_type: "authorization_code",
})
).toString();
const resp = await fetch("https://discord.com/api/oauth2/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: body,
});
if (resp.status !== 200) throw new Error(`Failed to get access token.`,);
const json = await resp.json();
return json;
},
getUserDetals: async (token) => {
const resp = await fetch("https://discord.com/api/users/@me", {
headers: {
Authorization: `Bearer ${token}`
},
});
const json = await resp.json();
if (!json.username || !json.email) throw new Error("Failed to get user details via oauth");
return {
id: json.id,
email: json.email,
username: json.username,
avatar_url: json.avatar
? `https://cdn.discordapp.com/avatars/${json.id}/${json.avatar}?size=2048`
: null,
};
}
};
const OauthCallbackHandlers: { [key: string]: Connection; } = {
discord: DiscordConnection
};
export { OauthCallbackHandlers };
+2 -1
View File
@@ -7,4 +7,5 @@ export * from "./handlers/route";
export * from "./utility/String";
export * from "./handlers/Voice";
export * from "./utility/captcha";
export * from "./utility/EmbedHandlers";
export * from "./utility/EmbedHandlers";
export * from "./handlers/Oauth";