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)
|
- 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
|
||||||
|
|
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,
|
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) => ({
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
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) {}
|
constructor(private kvNamespace: KVNamespace) {}
|
||||||
|
|
||||||
async get<T>(key: string): Promise<T | null> {
|
async get<T>(key: string): Promise<T | null> {
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue