mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 11:29:12 +00:00
init to fetch guild slug
This commit is contained in:
parent
e25b9c96c6
commit
22cbde52dd
9 changed files with 117 additions and 19 deletions
|
@ -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
|
||||
|
|
9
src/backend-worker/handlers/get-slug.ts
Normal file
9
src/backend-worker/handlers/get-slug.ts
Normal 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>
|
||||
};
|
|
@ -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 <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 { id, username, discriminator, bot, avatar } = await discordFetch<DiscordUser>(
|
||||
'/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<UserGuildsPayload>(
|
||||
'/users/@me/guilds',
|
||||
accessToken
|
||||
accessToken,
|
||||
'Bearer'
|
||||
);
|
||||
|
||||
const guildSlugs = guilds.map<GuildSlug>((guild) => ({
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
evaluatePermission,
|
||||
permissions as Permissions,
|
||||
} from '../../common/utils/hasPermission';
|
||||
import { WrappedKVNamespace } from './kv';
|
||||
|
||||
export const formData = (obj: Record<string, any>): 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 <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;
|
||||
};
|
||||
|
|
49
src/backend-worker/utils/guild.ts
Normal file
49
src/backend-worker/utils/guild.ts
Normal 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;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
class WrappedKVNamespace {
|
||||
export class WrappedKVNamespace {
|
||||
constructor(private kvNamespace: KVNamespace) {}
|
||||
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
|
|
|
@ -6,9 +6,7 @@ export type Guild = {
|
|||
id: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
ownerid: string;
|
||||
membercount: number;
|
||||
splash: string;
|
||||
roles: Role[];
|
||||
};
|
||||
|
||||
export type GuildRoles = {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue