init to fetch guild slug

This commit is contained in:
41666 2020-12-10 20:15:14 -05:00
parent e25b9c96c6
commit 22cbde52dd
9 changed files with 117 additions and 19 deletions

View file

@ -27,7 +27,7 @@ It uses 3 KV namespaces per environment:
- All data is permanent (maybe doubly persisted to Firestore) - All data is permanent (maybe doubly persisted to Firestore)
- Guilds - Guilds
- Cache of Discord guild data - Cache of Discord guild + guild member data
- All data subject to a 5 minute TTL - All data subject to a 5 minute TTL
### App UI ### App UI

View file

@ -0,0 +1,9 @@
import { Guild } from "roleypoly/common/types";
import { discordFetch } from "../utils/api-tools";
export const GetSlug = async (request: Request): Promise<Response> => {
const reqURL = new URL(request.url)
const serverID = reqURL.pathname.split('/')[2]
const serverPayload = discordFetch<Guild>
};

View file

@ -5,7 +5,12 @@ import {
GuildSlug, GuildSlug,
SessionData, SessionData,
} from '../../common/types'; } from '../../common/types';
import { formData, parsePermissions, resolveFailures } from '../utils/api-tools'; import {
discordFetch,
formData,
parsePermissions,
resolveFailures,
} from '../utils/api-tools';
import { Bounce } from '../utils/bounce'; import { Bounce } from '../utils/bounce';
import { apiPublicURI, botClientID, botClientSecret, uiPublicURI } from '../utils/config'; import { apiPublicURI, botClientID, botClientSecret, uiPublicURI } from '../utils/config';
import { Sessions } from '../utils/kv'; import { Sessions } from '../utils/kv';
@ -89,20 +94,11 @@ export const LoginCallback = resolveFailures(
} }
); );
const discordFetch = async <T>(url: string, auth: string): Promise<T> => {
const response = await fetch('https://discord.com/api/v8' + url, {
headers: {
authorization: 'Bearer ' + auth,
},
});
return (await response.json()) as T;
};
const getUser = async (accessToken: string): Promise<DiscordUser> => { const getUser = async (accessToken: string): Promise<DiscordUser> => {
const { id, username, discriminator, bot, avatar } = await discordFetch<DiscordUser>( const { id, username, discriminator, bot, avatar } = await discordFetch<DiscordUser>(
'/users/@me', '/users/@me',
accessToken accessToken,
'Bearer'
); );
return { id, username, discriminator, bot, avatar }; return { id, username, discriminator, bot, avatar };
@ -120,7 +116,8 @@ type UserGuildsPayload = {
const getGuilds = async (accessToken: string) => { const getGuilds = async (accessToken: string) => {
const guilds = await discordFetch<UserGuildsPayload>( const guilds = await discordFetch<UserGuildsPayload>(
'/users/@me/guilds', '/users/@me/guilds',
accessToken accessToken,
'Bearer'
); );
const guildSlugs = guilds.map<GuildSlug>((guild) => ({ const guildSlugs = guilds.map<GuildSlug>((guild) => ({

View file

@ -1,5 +1,6 @@
import { BotJoin } from './handlers/bot-join'; import { BotJoin } from './handlers/bot-join';
import { GetSession } from './handlers/get-session'; import { GetSession } from './handlers/get-session';
import { GetSlug } from './handlers/get-slug';
import { LoginBounce } from './handlers/login-bounce'; import { LoginBounce } from './handlers/login-bounce';
import { LoginCallback } from './handlers/login-callback'; import { LoginCallback } from './handlers/login-callback';
import { Router } from './router'; import { Router } from './router';
@ -14,6 +15,7 @@ router.add('GET', 'bot-join', BotJoin);
router.add('GET', 'login-bounce', LoginBounce); router.add('GET', 'login-bounce', LoginBounce);
router.add('GET', 'login-callback', LoginCallback); router.add('GET', 'login-callback', LoginCallback);
router.add('GET', 'get-session', GetSession); router.add('GET', 'get-session', GetSession);
<<<<<<< HEAD
router.add('GET', 'x-headers', (request) => { router.add('GET', 'x-headers', (request) => {
const headers: { [x: string]: string } = {}; const headers: { [x: string]: string } = {};
@ -23,6 +25,9 @@ router.add('GET', 'x-headers', (request) => {
return new Response(JSON.stringify(headers)); return new Response(JSON.stringify(headers));
}); });
=======
router.add('GET', 'get-slug', GetSlug);
>>>>>>> init to fetch guild slug
addEventListener('fetch', (event: FetchEvent) => { addEventListener('fetch', (event: FetchEvent) => {
event.respondWith(router.handle(event.request)); event.respondWith(router.handle(event.request));

View file

@ -3,6 +3,7 @@ import {
evaluatePermission, evaluatePermission,
permissions as Permissions, permissions as Permissions,
} from '../../common/utils/hasPermission'; } from '../../common/utils/hasPermission';
import { WrappedKVNamespace } from './kv';
export const formData = (obj: Record<string, any>): string => { export const formData = (obj: Record<string, any>): string => {
return Object.keys(obj) return Object.keys(obj)
@ -53,3 +54,41 @@ export const getSessionID = (request: Request): { type: string; id: string } | n
return { type, id }; return { type, id };
}; };
export const discordFetch = async <T>(
url: string,
auth: string,
authType: 'Bearer' | 'Bot' = 'Bearer'
): Promise<T> => {
const response = await fetch('https://discord.com/api/v8' + url, {
headers: {
authorization: `${authType} ${auth}`,
},
});
return (await response.json()) as T;
};
export const cacheLayer = <Identity, Data>(
kv: WrappedKVNamespace,
keyFactory: (identity: Identity) => string,
missHandler: (identity: Identity) => Promise<Data | null>,
ttlSeconds?: number
) => async (identity: Identity): Promise<Data | null> => {
const key = keyFactory(identity);
const value = await kv.get<Data>(key);
if (value) {
return value;
}
const fallbackValue = await missHandler(identity);
if (!fallbackValue) {
return null;
}
await kv.put(key, fallbackValue, ttlSeconds);
return fallbackValue;
};

View file

@ -0,0 +1,49 @@
import { Guild, Member, Role, RoleSafety } from 'roleypoly/common/types';
import { cacheLayer, discordFetch } from './api-tools';
import { botToken } from './config';
import { Guilds } from './kv';
type APIGuild = {
// Only relevant stuff
id: string;
name: string;
icon: string;
roles: APIRole[];
};
type APIRole = {
id: string;
name: string;
color: number;
position: number;
permissions: string;
managed: boolean;
};
export const getGuild = cacheLayer(
Guilds,
(id: string) => `guilds/${id}`,
async (id: string) => {
const guildRaw = await discordFetch<APIGuild>(`/guilds/${id}`, botToken, 'Bot');
// Filters the raw guild data into data we actually want
const guild: Guild = {
id: guildRaw.id,
name: guildRaw.name,
icon: guildRaw.icon,
roles: guildRaw.roles.map<Role>((role) => ({
...role,
safety: RoleSafety.SAFE, // TODO: calculate safety
})),
};
return guild;
}
);
export const getGuildMember = async (
serverID: string,
userID: string
): Promise<Member> => {
return {} as any;
};

View file

@ -1,4 +1,4 @@
class WrappedKVNamespace { export class WrappedKVNamespace {
constructor(private kvNamespace: KVNamespace) {} constructor(private kvNamespace: KVNamespace) {}
async get<T>(key: string): Promise<T | null> { async get<T>(key: string): Promise<T | null> {

View file

@ -6,9 +6,7 @@ export type Guild = {
id: string; id: string;
name: string; name: string;
icon: string; icon: string;
ownerid: string; roles: Role[];
membercount: number;
splash: string;
}; };
export type GuildRoles = { export type GuildRoles = {

View file

@ -8,8 +8,9 @@ export type Role = {
id: string; id: string;
name: string; name: string;
color: number; color: number;
permissions: number;
managed: boolean; managed: boolean;
position: number; position: number;
safety: RoleSafety; safety: RoleSafety;
/** Permissions is should be used as a BigInt, NOT a number. */
permissions: string;
}; };