diff --git a/docs/infrastructure.md b/docs/infrastructure.md index 41d552d..e68bf96 100644 --- a/docs/infrastructure.md +++ b/docs/infrastructure.md @@ -27,7 +27,7 @@ It uses 3 KV namespaces per environment: - All data is permanent (maybe doubly persisted to Firestore) - Guilds - - Cache of Discord guild data + - Cache of Discord guild + guild member data - All data subject to a 5 minute TTL ### App UI diff --git a/src/backend-worker/handlers/get-slug.ts b/src/backend-worker/handlers/get-slug.ts new file mode 100644 index 0000000..bb042bd --- /dev/null +++ b/src/backend-worker/handlers/get-slug.ts @@ -0,0 +1,9 @@ +import { Guild } from "roleypoly/common/types"; +import { discordFetch } from "../utils/api-tools"; + +export const GetSlug = async (request: Request): Promise => { + const reqURL = new URL(request.url) + const serverID = reqURL.pathname.split('/')[2] + + const serverPayload = discordFetch +}; diff --git a/src/backend-worker/handlers/login-callback.ts b/src/backend-worker/handlers/login-callback.ts index e53f24f..4291d31 100644 --- a/src/backend-worker/handlers/login-callback.ts +++ b/src/backend-worker/handlers/login-callback.ts @@ -5,7 +5,12 @@ import { GuildSlug, SessionData, } 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 { apiPublicURI, botClientID, botClientSecret, uiPublicURI } from '../utils/config'; import { Sessions } from '../utils/kv'; @@ -89,20 +94,11 @@ export const LoginCallback = resolveFailures( } ); -const discordFetch = async (url: string, auth: string): Promise => { - 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 => { const { id, username, discriminator, bot, avatar } = await discordFetch( '/users/@me', - accessToken + accessToken, + 'Bearer' ); return { id, username, discriminator, bot, avatar }; @@ -120,7 +116,8 @@ type UserGuildsPayload = { const getGuilds = async (accessToken: string) => { const guilds = await discordFetch( '/users/@me/guilds', - accessToken + accessToken, + 'Bearer' ); const guildSlugs = guilds.map((guild) => ({ diff --git a/src/backend-worker/index.ts b/src/backend-worker/index.ts index 636d5b6..6b8b1c8 100644 --- a/src/backend-worker/index.ts +++ b/src/backend-worker/index.ts @@ -1,5 +1,6 @@ import { BotJoin } from './handlers/bot-join'; import { GetSession } from './handlers/get-session'; +import { GetSlug } from './handlers/get-slug'; import { LoginBounce } from './handlers/login-bounce'; import { LoginCallback } from './handlers/login-callback'; import { Router } from './router'; @@ -14,6 +15,7 @@ router.add('GET', 'bot-join', BotJoin); router.add('GET', 'login-bounce', LoginBounce); router.add('GET', 'login-callback', LoginCallback); router.add('GET', 'get-session', GetSession); +<<<<<<< HEAD router.add('GET', 'x-headers', (request) => { const headers: { [x: string]: string } = {}; @@ -23,6 +25,9 @@ router.add('GET', 'x-headers', (request) => { return new Response(JSON.stringify(headers)); }); +======= +router.add('GET', 'get-slug', GetSlug); +>>>>>>> init to fetch guild slug addEventListener('fetch', (event: FetchEvent) => { event.respondWith(router.handle(event.request)); diff --git a/src/backend-worker/utils/api-tools.ts b/src/backend-worker/utils/api-tools.ts index 8fbb06e..ee4fb86 100644 --- a/src/backend-worker/utils/api-tools.ts +++ b/src/backend-worker/utils/api-tools.ts @@ -3,6 +3,7 @@ import { evaluatePermission, permissions as Permissions, } from '../../common/utils/hasPermission'; +import { WrappedKVNamespace } from './kv'; export const formData = (obj: Record): string => { return Object.keys(obj) @@ -53,3 +54,41 @@ export const getSessionID = (request: Request): { type: string; id: string } | n return { type, id }; }; + +export const discordFetch = async ( + url: string, + auth: string, + authType: 'Bearer' | 'Bot' = 'Bearer' +): Promise => { + const response = await fetch('https://discord.com/api/v8' + url, { + headers: { + authorization: `${authType} ${auth}`, + }, + }); + + return (await response.json()) as T; +}; + +export const cacheLayer = ( + kv: WrappedKVNamespace, + keyFactory: (identity: Identity) => string, + missHandler: (identity: Identity) => Promise, + ttlSeconds?: number +) => async (identity: Identity): Promise => { + const key = keyFactory(identity); + + const value = await kv.get(key); + if (value) { + return value; + } + + const fallbackValue = await missHandler(identity); + + if (!fallbackValue) { + return null; + } + + await kv.put(key, fallbackValue, ttlSeconds); + + return fallbackValue; +}; diff --git a/src/backend-worker/utils/guild.ts b/src/backend-worker/utils/guild.ts new file mode 100644 index 0000000..da4d4c7 --- /dev/null +++ b/src/backend-worker/utils/guild.ts @@ -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(`/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, + safety: RoleSafety.SAFE, // TODO: calculate safety + })), + }; + + return guild; + } +); + +export const getGuildMember = async ( + serverID: string, + userID: string +): Promise => { + return {} as any; +}; diff --git a/src/backend-worker/utils/kv.ts b/src/backend-worker/utils/kv.ts index 8b86f71..b1b8e2e 100644 --- a/src/backend-worker/utils/kv.ts +++ b/src/backend-worker/utils/kv.ts @@ -1,4 +1,4 @@ -class WrappedKVNamespace { +export class WrappedKVNamespace { constructor(private kvNamespace: KVNamespace) {} async get(key: string): Promise { diff --git a/src/common/types/Guild.ts b/src/common/types/Guild.ts index a03cc42..3be9caf 100644 --- a/src/common/types/Guild.ts +++ b/src/common/types/Guild.ts @@ -6,9 +6,7 @@ export type Guild = { id: string; name: string; icon: string; - ownerid: string; - membercount: number; - splash: string; + roles: Role[]; }; export type GuildRoles = { diff --git a/src/common/types/Role.ts b/src/common/types/Role.ts index 5075233..3eae365 100644 --- a/src/common/types/Role.ts +++ b/src/common/types/Role.ts @@ -8,8 +8,9 @@ export type Role = { id: string; name: string; color: number; - permissions: number; managed: boolean; position: number; safety: RoleSafety; + /** Permissions is should be used as a BigInt, NOT a number. */ + permissions: string; };