mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
chore: update codestyle due to prettier/rule updates
This commit is contained in:
parent
f632bfa6e5
commit
10e095656f
16 changed files with 298 additions and 292 deletions
|
@ -3,8 +3,8 @@ FROM node:14 AS builder
|
||||||
# Create the user and group files that will be used in the running container to
|
# Create the user and group files that will be used in the running container to
|
||||||
# run the process as an unprivileged user.
|
# run the process as an unprivileged user.
|
||||||
RUN mkdir /user \
|
RUN mkdir /user \
|
||||||
&& echo 'nobody:x:65534:65534:nobody:/:' >/user/passwd \
|
&& echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd \
|
||||||
&& echo 'nobody:x:65534:' >/user/group
|
&& echo 'nobody:x:65534:' > /user/group
|
||||||
|
|
||||||
# Set the working directory outside $GOPATH to enable the support for modules.
|
# Set the working directory outside $GOPATH to enable the support for modules.
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
|
@ -10,41 +10,42 @@ import {
|
||||||
} from '../utils/responses';
|
} from '../utils/responses';
|
||||||
|
|
||||||
export const ClearGuildCache = withSession(
|
export const ClearGuildCache = withSession(
|
||||||
(session) => async (request: Request): Promise<Response> => {
|
(session) =>
|
||||||
const url = new URL(request.url);
|
async (request: Request): Promise<Response> => {
|
||||||
const [, , guildID] = url.pathname.split('/');
|
const url = new URL(request.url);
|
||||||
if (!guildID) {
|
const [, , guildID] = url.pathname.split('/');
|
||||||
return missingParameters();
|
if (!guildID) {
|
||||||
}
|
return missingParameters();
|
||||||
|
}
|
||||||
|
|
||||||
const rateLimit = useGuildRateLimiter(
|
const rateLimit = useGuildRateLimiter(
|
||||||
guildID,
|
guildID,
|
||||||
GuildRateLimiterKey.cacheClear,
|
GuildRateLimiterKey.cacheClear,
|
||||||
60 * 5
|
60 * 5
|
||||||
); // 5 minute RL TTL, 288 times per day.
|
); // 5 minute RL TTL, 288 times per day.
|
||||||
|
|
||||||
if (!isRoot(session.user.id)) {
|
if (!isRoot(session.user.id)) {
|
||||||
const guild = session.guilds.find((guild) => guild.id === guildID);
|
const guild = session.guilds.find((guild) => guild.id === guildID);
|
||||||
if (!guild) {
|
if (!guild) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
guild?.permissionLevel !== UserGuildPermissions.Manager &&
|
||||||
|
guild?.permissionLevel !== UserGuildPermissions.Admin
|
||||||
|
) {
|
||||||
|
return lowPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await rateLimit()) {
|
||||||
|
return rateLimited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await getGuild(guildID, { skipCachePull: true });
|
||||||
|
if (!result) {
|
||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
return ok();
|
||||||
if (
|
|
||||||
guild?.permissionLevel !== UserGuildPermissions.Manager &&
|
|
||||||
guild?.permissionLevel !== UserGuildPermissions.Admin
|
|
||||||
) {
|
|
||||||
return lowPermissions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await rateLimit()) {
|
|
||||||
return rateLimited();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await getGuild(guildID, { skipCachePull: true });
|
|
||||||
if (!result) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
return ok();
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,52 +5,53 @@ import { getGuild, getGuildData, getGuildMemberRoles } from '../utils/guild';
|
||||||
const fail = () => respond({ error: 'guild not found' }, { status: 404 });
|
const fail = () => respond({ error: 'guild not found' }, { status: 404 });
|
||||||
|
|
||||||
export const GetPickerData = withSession(
|
export const GetPickerData = withSession(
|
||||||
(session: SessionData) => async (request: Request): Promise<Response> => {
|
(session: SessionData) =>
|
||||||
const url = new URL(request.url);
|
async (request: Request): Promise<Response> => {
|
||||||
const [, , guildID] = url.pathname.split('/');
|
const url = new URL(request.url);
|
||||||
|
const [, , guildID] = url.pathname.split('/');
|
||||||
|
|
||||||
if (!guildID) {
|
if (!guildID) {
|
||||||
return respond({ error: 'missing guild id' }, { status: 400 });
|
return respond({ error: 'missing guild id' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id: userID } = session.user as DiscordUser;
|
||||||
|
const guilds = session.guilds as GuildSlug[];
|
||||||
|
|
||||||
|
// Save a Discord API request by checking if this user is a member by session first
|
||||||
|
const checkGuild = guilds.find((guild) => guild.id === guildID);
|
||||||
|
if (!checkGuild) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = await getGuild(guildID, {
|
||||||
|
skipCachePull: url.searchParams.has('__no_cache'),
|
||||||
|
});
|
||||||
|
if (!guild) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
const memberRolesP = getGuildMemberRoles({
|
||||||
|
serverID: guildID,
|
||||||
|
userID,
|
||||||
|
});
|
||||||
|
|
||||||
|
const guildDataP = getGuildData(guildID);
|
||||||
|
|
||||||
|
const [guildData, memberRoles] = await Promise.all([guildDataP, memberRolesP]);
|
||||||
|
if (!memberRoles) {
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
const presentableGuild: PresentableGuild = {
|
||||||
|
id: guildID,
|
||||||
|
guild: checkGuild,
|
||||||
|
roles: guild.roles,
|
||||||
|
member: {
|
||||||
|
roles: memberRoles,
|
||||||
|
},
|
||||||
|
data: guildData,
|
||||||
|
};
|
||||||
|
|
||||||
|
return respond(presentableGuild);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id: userID } = session.user as DiscordUser;
|
|
||||||
const guilds = session.guilds as GuildSlug[];
|
|
||||||
|
|
||||||
// Save a Discord API request by checking if this user is a member by session first
|
|
||||||
const checkGuild = guilds.find((guild) => guild.id === guildID);
|
|
||||||
if (!checkGuild) {
|
|
||||||
return fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
const guild = await getGuild(guildID, {
|
|
||||||
skipCachePull: url.searchParams.has('__no_cache'),
|
|
||||||
});
|
|
||||||
if (!guild) {
|
|
||||||
return fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
const memberRolesP = getGuildMemberRoles({
|
|
||||||
serverID: guildID,
|
|
||||||
userID,
|
|
||||||
});
|
|
||||||
|
|
||||||
const guildDataP = getGuildData(guildID);
|
|
||||||
|
|
||||||
const [guildData, memberRoles] = await Promise.all([guildDataP, memberRolesP]);
|
|
||||||
if (!memberRoles) {
|
|
||||||
return fail();
|
|
||||||
}
|
|
||||||
|
|
||||||
const presentableGuild: PresentableGuild = {
|
|
||||||
id: guildID,
|
|
||||||
guild: checkGuild,
|
|
||||||
roles: guild.roles,
|
|
||||||
member: {
|
|
||||||
roles: memberRoles,
|
|
||||||
},
|
|
||||||
data: guildData,
|
|
||||||
};
|
|
||||||
|
|
||||||
return respond(presentableGuild);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,60 +17,61 @@ import {
|
||||||
} from '../utils/responses';
|
} from '../utils/responses';
|
||||||
|
|
||||||
export const SyncFromLegacy = withSession(
|
export const SyncFromLegacy = withSession(
|
||||||
(session) => async (request: Request): Promise<Response> => {
|
(session) =>
|
||||||
const url = new URL(request.url);
|
async (request: Request): Promise<Response> => {
|
||||||
const [, , guildID] = url.pathname.split('/');
|
const url = new URL(request.url);
|
||||||
if (!guildID) {
|
const [, , guildID] = url.pathname.split('/');
|
||||||
return missingParameters();
|
if (!guildID) {
|
||||||
}
|
return missingParameters();
|
||||||
|
}
|
||||||
|
|
||||||
const rateLimit = useGuildRateLimiter(
|
const rateLimit = useGuildRateLimiter(
|
||||||
guildID,
|
guildID,
|
||||||
GuildRateLimiterKey.legacyImport,
|
GuildRateLimiterKey.legacyImport,
|
||||||
60 * 20
|
60 * 20
|
||||||
); // 20 minute RL TTL, 72 times per day.
|
); // 20 minute RL TTL, 72 times per day.
|
||||||
|
|
||||||
// Allow root users to trigger this too, just in case.
|
// Allow root users to trigger this too, just in case.
|
||||||
if (!isRoot(session.user.id)) {
|
if (!isRoot(session.user.id)) {
|
||||||
const guild = session.guilds.find((guild) => guild.id === guildID);
|
const guild = session.guilds.find((guild) => guild.id === guildID);
|
||||||
if (!guild) {
|
if (!guild) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
guild?.permissionLevel !== UserGuildPermissions.Manager &&
|
||||||
|
guild?.permissionLevel !== UserGuildPermissions.Admin
|
||||||
|
) {
|
||||||
|
return lowPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await rateLimit()) {
|
||||||
|
return rateLimited();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shouldForce = url.searchParams.get('force') === 'yes';
|
||||||
|
|
||||||
|
// Not using getGuildData as we want null feedback, not a zeroed out object.
|
||||||
|
const checkGuild = await GuildData.get<GuildDataT>(guildID);
|
||||||
|
// Don't force, and guild exists in our side, but LegacyGuild flag is set,
|
||||||
|
// fail this request.
|
||||||
|
if (
|
||||||
|
!shouldForce &&
|
||||||
|
checkGuild &&
|
||||||
|
(checkGuild.features & Features.LegacyGuild) === Features.LegacyGuild
|
||||||
|
) {
|
||||||
|
return conflict();
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyGuild = await fetchLegacyServer(guildID);
|
||||||
|
if (!legacyGuild) {
|
||||||
return notFound();
|
return notFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const newGuildData = transformLegacyGuild(legacyGuild);
|
||||||
guild?.permissionLevel !== UserGuildPermissions.Manager &&
|
await GuildData.put(guildID, newGuildData);
|
||||||
guild?.permissionLevel !== UserGuildPermissions.Admin
|
|
||||||
) {
|
|
||||||
return lowPermissions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await rateLimit()) {
|
return ok();
|
||||||
return rateLimited();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldForce = url.searchParams.get('force') === 'yes';
|
|
||||||
|
|
||||||
// Not using getGuildData as we want null feedback, not a zeroed out object.
|
|
||||||
const checkGuild = await GuildData.get<GuildDataT>(guildID);
|
|
||||||
// Don't force, and guild exists in our side, but LegacyGuild flag is set,
|
|
||||||
// fail this request.
|
|
||||||
if (
|
|
||||||
!shouldForce &&
|
|
||||||
checkGuild &&
|
|
||||||
(checkGuild.features & Features.LegacyGuild) === Features.LegacyGuild
|
|
||||||
) {
|
|
||||||
return conflict();
|
|
||||||
}
|
|
||||||
|
|
||||||
const legacyGuild = await fetchLegacyServer(guildID);
|
|
||||||
if (!legacyGuild) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const newGuildData = transformLegacyGuild(legacyGuild);
|
|
||||||
await GuildData.put(guildID, newGuildData);
|
|
||||||
|
|
||||||
return ok();
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,70 +21,71 @@ import {
|
||||||
const notFound = () => respond({ error: 'guild not found' }, { status: 404 });
|
const notFound = () => respond({ error: 'guild not found' }, { status: 404 });
|
||||||
|
|
||||||
export const UpdateRoles = withSession(
|
export const UpdateRoles = withSession(
|
||||||
({ guilds, user: { id: userID } }: SessionData) => async (request: Request) => {
|
({ guilds, user: { id: userID } }: SessionData) =>
|
||||||
const updateRequest = (await request.json()) as RoleUpdate;
|
async (request: Request) => {
|
||||||
const [, , guildID] = new URL(request.url).pathname.split('/');
|
const updateRequest = (await request.json()) as RoleUpdate;
|
||||||
|
const [, , guildID] = new URL(request.url).pathname.split('/');
|
||||||
|
|
||||||
if (!guildID) {
|
if (!guildID) {
|
||||||
return respond({ error: 'guild ID missing from URL' }, { status: 400 });
|
return respond({ error: 'guild ID missing from URL' }, { status: 400 });
|
||||||
}
|
|
||||||
|
|
||||||
if (updateRequest.transactions.length === 0) {
|
|
||||||
return respond({ error: 'must have as least one transaction' }, { status: 400 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const guildCheck = guilds.find((guild) => guild.id === guildID);
|
|
||||||
if (!guildCheck) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const guild = await getGuild(guildID);
|
|
||||||
if (!guild) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const guildMemberRoles = await getGuildMemberRoles(
|
|
||||||
{ serverID: guildID, userID },
|
|
||||||
{ skipCachePull: true }
|
|
||||||
);
|
|
||||||
if (!guildMemberRoles) {
|
|
||||||
return notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
const newRoles = calculateNewRoles({
|
|
||||||
currentRoles: guildMemberRoles,
|
|
||||||
guildRoles: guild.roles,
|
|
||||||
guildData: await getGuildData(guildID),
|
|
||||||
updateRequest,
|
|
||||||
});
|
|
||||||
|
|
||||||
const patchMemberRoles = await discordFetch<Member>(
|
|
||||||
`/guilds/${guildID}/members/${userID}`,
|
|
||||||
botToken,
|
|
||||||
AuthType.Bot,
|
|
||||||
{
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
roles: newRoles,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if (!patchMemberRoles) {
|
if (updateRequest.transactions.length === 0) {
|
||||||
return respond({ error: 'discord rejected the request' }, { status: 500 });
|
return respond({ error: 'must have as least one transaction' }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildCheck = guilds.find((guild) => guild.id === guildID);
|
||||||
|
if (!guildCheck) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const guild = await getGuild(guildID);
|
||||||
|
if (!guild) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildMemberRoles = await getGuildMemberRoles(
|
||||||
|
{ serverID: guildID, userID },
|
||||||
|
{ skipCachePull: true }
|
||||||
|
);
|
||||||
|
if (!guildMemberRoles) {
|
||||||
|
return notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const newRoles = calculateNewRoles({
|
||||||
|
currentRoles: guildMemberRoles,
|
||||||
|
guildRoles: guild.roles,
|
||||||
|
guildData: await getGuildData(guildID),
|
||||||
|
updateRequest,
|
||||||
|
});
|
||||||
|
|
||||||
|
const patchMemberRoles = await discordFetch<Member>(
|
||||||
|
`/guilds/${guildID}/members/${userID}`,
|
||||||
|
botToken,
|
||||||
|
AuthType.Bot,
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
roles: newRoles,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!patchMemberRoles) {
|
||||||
|
return respond({ error: 'discord rejected the request' }, { status: 500 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedMember: Member = {
|
||||||
|
roles: patchMemberRoles.roles,
|
||||||
|
};
|
||||||
|
|
||||||
|
await updateGuildMemberRoles({ serverID: guildID, userID }, patchMemberRoles.roles);
|
||||||
|
|
||||||
|
return respond(updatedMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedMember: Member = {
|
|
||||||
roles: patchMemberRoles.roles,
|
|
||||||
};
|
|
||||||
|
|
||||||
await updateGuildMemberRoles({ serverID: guildID, userID }, patchMemberRoles.roles);
|
|
||||||
|
|
||||||
return respond(updatedMember);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const calculateNewRoles = ({
|
const calculateNewRoles = ({
|
||||||
|
|
|
@ -27,17 +27,19 @@ export const addCORS = (init: ResponseInit = {}) => ({
|
||||||
export const respond = (obj: Record<string, any>, init: ResponseInit = {}) =>
|
export const respond = (obj: Record<string, any>, init: ResponseInit = {}) =>
|
||||||
new Response(JSON.stringify(obj), addCORS(init));
|
new Response(JSON.stringify(obj), addCORS(init));
|
||||||
|
|
||||||
export const resolveFailures = (
|
export const resolveFailures =
|
||||||
handleWith: () => Response,
|
(
|
||||||
handler: (request: Request) => Promise<Response> | Response
|
handleWith: () => Response,
|
||||||
) => async (request: Request): Promise<Response> => {
|
handler: (request: Request) => Promise<Response> | Response
|
||||||
try {
|
) =>
|
||||||
return handler(request);
|
async (request: Request): Promise<Response> => {
|
||||||
} catch (e) {
|
try {
|
||||||
console.error(e);
|
return handler(request);
|
||||||
return handleWith() || respond({ error: 'internal server error' }, { status: 500 });
|
} catch (e) {
|
||||||
}
|
console.error(e);
|
||||||
};
|
return handleWith() || respond({ error: 'internal server error' }, { status: 500 });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const parsePermissions = (
|
export const parsePermissions = (
|
||||||
permissions: bigint,
|
permissions: bigint,
|
||||||
|
@ -106,33 +108,35 @@ export const discordFetch = async <T>(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cacheLayer = <Identity, Data>(
|
export const cacheLayer =
|
||||||
kv: WrappedKVNamespace,
|
<Identity, Data>(
|
||||||
keyFactory: (identity: Identity) => string,
|
kv: WrappedKVNamespace,
|
||||||
missHandler: (identity: Identity) => Promise<Data | null>,
|
keyFactory: (identity: Identity) => string,
|
||||||
ttlSeconds?: number
|
missHandler: (identity: Identity) => Promise<Data | null>,
|
||||||
) => async (
|
ttlSeconds?: number
|
||||||
identity: Identity,
|
) =>
|
||||||
options: { skipCachePull?: boolean } = {}
|
async (
|
||||||
): Promise<Data | null> => {
|
identity: Identity,
|
||||||
const key = keyFactory(identity);
|
options: { skipCachePull?: boolean } = {}
|
||||||
|
): Promise<Data | null> => {
|
||||||
|
const key = keyFactory(identity);
|
||||||
|
|
||||||
if (!options.skipCachePull) {
|
if (!options.skipCachePull) {
|
||||||
const value = await kv.get<Data>(key);
|
const value = await kv.get<Data>(key);
|
||||||
if (value) {
|
if (value) {
|
||||||
return value;
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const fallbackValue = await missHandler(identity);
|
const fallbackValue = await missHandler(identity);
|
||||||
if (!fallbackValue) {
|
if (!fallbackValue) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await kv.put(key, fallbackValue, ttlSeconds);
|
await kv.put(key, fallbackValue, ttlSeconds);
|
||||||
|
|
||||||
return fallbackValue;
|
return fallbackValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NotAuthenticated = (extra?: string) =>
|
const NotAuthenticated = (extra?: string) =>
|
||||||
respond(
|
respond(
|
||||||
|
@ -142,21 +146,21 @@ const NotAuthenticated = (extra?: string) =>
|
||||||
{ status: 403 }
|
{ status: 403 }
|
||||||
);
|
);
|
||||||
|
|
||||||
export const withSession = (
|
export const withSession =
|
||||||
wrappedHandler: (session: SessionData) => Handler
|
(wrappedHandler: (session: SessionData) => Handler): Handler =>
|
||||||
): Handler => async (request: Request): Promise<Response> => {
|
async (request: Request): Promise<Response> => {
|
||||||
const sessionID = getSessionID(request);
|
const sessionID = getSessionID(request);
|
||||||
if (!sessionID) {
|
if (!sessionID) {
|
||||||
return NotAuthenticated('missing authentication');
|
return NotAuthenticated('missing authentication');
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = await Sessions.get<SessionData>(sessionID.id);
|
const session = await Sessions.get<SessionData>(sessionID.id);
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return NotAuthenticated('authentication expired or not found');
|
return NotAuthenticated('authentication expired or not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await wrappedHandler(session)(request);
|
return await wrappedHandler(session)(request);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupStateSession = async <T>(data: T): Promise<string> => {
|
export const setupStateSession = async <T>(data: T): Promise<string> => {
|
||||||
const stateID = (await KSUID.random()).string;
|
const stateID = (await KSUID.random()).string;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const self = (global as any) as Record<string, string>;
|
const self = global as any as Record<string, string>;
|
||||||
|
|
||||||
const env = (key: string) => self[key] ?? '';
|
const env = (key: string) => self[key] ?? '';
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,7 @@ class EmulatedKV implements KVNamespace {
|
||||||
this.data.delete(key);
|
this.data.delete(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
async list(options?: {
|
async list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{
|
||||||
prefix?: string;
|
|
||||||
limit?: number;
|
|
||||||
cursor?: string;
|
|
||||||
}): Promise<{
|
|
||||||
keys: { name: string; expiration?: number; metadata?: unknown }[];
|
keys: { name: string; expiration?: number; metadata?: unknown }[];
|
||||||
list_complete: boolean;
|
list_complete: boolean;
|
||||||
cursor: string;
|
cursor: string;
|
||||||
|
@ -83,7 +79,7 @@ class EmulatedKV implements KVNamespace {
|
||||||
const kvOrLocal = (namespace: KVNamespace | null): KVNamespace =>
|
const kvOrLocal = (namespace: KVNamespace | null): KVNamespace =>
|
||||||
namespace || new EmulatedKV();
|
namespace || new EmulatedKV();
|
||||||
|
|
||||||
const self = (global as any) as Record<string, any>;
|
const self = global as any as Record<string, any>;
|
||||||
|
|
||||||
export const Sessions = new WrappedKVNamespace(kvOrLocal(self.KV_SESSIONS ?? null));
|
export const Sessions = new WrappedKVNamespace(kvOrLocal(self.KV_SESSIONS ?? null));
|
||||||
export const GuildData = new WrappedKVNamespace(kvOrLocal(self.KV_GUILD_DATA ?? null));
|
export const GuildData = new WrappedKVNamespace(kvOrLocal(self.KV_GUILD_DATA ?? null));
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import { WrappedKVNamespace } from './kv';
|
import { WrappedKVNamespace } from './kv';
|
||||||
|
|
||||||
export const useRateLimiter = (
|
export const useRateLimiter =
|
||||||
kv: WrappedKVNamespace,
|
(kv: WrappedKVNamespace, key: string, timeoutSeconds: number) =>
|
||||||
key: string,
|
async (): Promise<boolean> => {
|
||||||
timeoutSeconds: number
|
const value = await kv.get<boolean>(key);
|
||||||
) => async (): Promise<boolean> => {
|
if (value) {
|
||||||
const value = await kv.get<boolean>(key);
|
return true;
|
||||||
if (value) {
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
await kv.put(key, true, timeoutSeconds);
|
await kv.put(key, true, timeoutSeconds);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { roleypolyTheme } from './theme';
|
|
||||||
import { mdxComponents } from '../atoms/typography/mdx';
|
import { mdxComponents } from '../atoms/typography/mdx';
|
||||||
|
import { roleypolyTheme } from './theme';
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||||
|
|
|
@ -10,9 +10,10 @@ type DynamicLogoProps = LogoFlagProps & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DynamicLogomark = (props: Partial<DynamicLogoProps>) => {
|
export const DynamicLogomark = (props: Partial<DynamicLogoProps>) => {
|
||||||
const variant = React.useMemo(() => getRelevantVariant(props.currentDate), [
|
const variant = React.useMemo(
|
||||||
props.currentDate,
|
() => getRelevantVariant(props.currentDate),
|
||||||
]);
|
[props.currentDate]
|
||||||
|
);
|
||||||
|
|
||||||
if (!variant) {
|
if (!variant) {
|
||||||
return <Logomark {...props} />;
|
return <Logomark {...props} />;
|
||||||
|
@ -35,9 +36,10 @@ export const DynamicLogomark = (props: Partial<DynamicLogoProps>) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DynamicLogotype = (props: Partial<DynamicLogoProps>) => {
|
export const DynamicLogotype = (props: Partial<DynamicLogoProps>) => {
|
||||||
const variant = React.useMemo(() => getRelevantVariant(props.currentDate), [
|
const variant = React.useMemo(
|
||||||
props.currentDate,
|
() => getRelevantVariant(props.currentDate),
|
||||||
]);
|
[props.currentDate]
|
||||||
|
);
|
||||||
|
|
||||||
if (!variant) {
|
if (!variant) {
|
||||||
return <Logotype {...props} />;
|
return <Logotype {...props} />;
|
||||||
|
|
|
@ -38,14 +38,14 @@ export const EditorCategory = (props: Props) => {
|
||||||
const [roleSearchPopoverActive, setRoleSearchPopoverActive] = React.useState(false);
|
const [roleSearchPopoverActive, setRoleSearchPopoverActive] = React.useState(false);
|
||||||
const [roleSearchTerm, updateSearchTerm] = React.useState('');
|
const [roleSearchTerm, updateSearchTerm] = React.useState('');
|
||||||
|
|
||||||
const onUpdate = (key: keyof typeof props.category, pred?: (newValue: any) => any) => (
|
const onUpdate =
|
||||||
newValue: any
|
(key: keyof typeof props.category, pred?: (newValue: any) => any) =>
|
||||||
) => {
|
(newValue: any) => {
|
||||||
props.onChange({
|
props.onChange({
|
||||||
...props.category,
|
...props.category,
|
||||||
[key]: pred ? pred(newValue) : newValue,
|
[key]: pred ? pred(newValue) : newValue,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRoleSelect = (role: RoleType) => {
|
const handleRoleSelect = (role: RoleType) => {
|
||||||
setRoleSearchPopoverActive(false);
|
setRoleSearchPopoverActive(false);
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { FeatureFlag, FeatureFlagProvider, FeatureFlagsContext } from './FeatureFlags';
|
import { FeatureFlag, FeatureFlagProvider, FeatureFlagsContext } from './FeatureFlags';
|
||||||
|
|
||||||
export const FeatureFlagDecorator = (flags: FeatureFlag[]) => (
|
export const FeatureFlagDecorator =
|
||||||
storyFn: () => React.ReactNode
|
(flags: FeatureFlag[]) => (storyFn: () => React.ReactNode) => {
|
||||||
) => {
|
return (
|
||||||
return (
|
<FeatureFlagsContext.Provider value={new FeatureFlagProvider(flags)}>
|
||||||
<FeatureFlagsContext.Provider value={new FeatureFlagProvider(flags)}>
|
{storyFn()}
|
||||||
{storyFn()}
|
</FeatureFlagsContext.Provider>
|
||||||
</FeatureFlagsContext.Provider>
|
);
|
||||||
);
|
};
|
||||||
};
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
export const withContext = <T, K extends T>(
|
export const withContext =
|
||||||
Context: React.Context<T>,
|
<T, K extends T>(
|
||||||
Component: React.ComponentType<K>
|
Context: React.Context<T>,
|
||||||
): React.FunctionComponent<K> => (props) => (
|
Component: React.ComponentType<K>
|
||||||
<Context.Consumer>
|
): React.FunctionComponent<K> =>
|
||||||
{(context) => <Component {...props} {...context} />}
|
(props) =>
|
||||||
</Context.Consumer>
|
(
|
||||||
);
|
<Context.Consumer>
|
||||||
|
{(context) => <Component {...props} {...context} />}
|
||||||
|
</Context.Consumer>
|
||||||
|
);
|
||||||
|
|
|
@ -47,9 +47,8 @@ export const useSessionContext = () => React.useContext(SessionContext);
|
||||||
|
|
||||||
export const SessionContextProvider = (props: { children: React.ReactNode }) => {
|
export const SessionContextProvider = (props: { children: React.ReactNode }) => {
|
||||||
const { fetch } = useApiContext();
|
const { fetch } = useApiContext();
|
||||||
const [sessionID, setSessionID] = React.useState<SessionContextT['sessionID']>(
|
const [sessionID, setSessionID] =
|
||||||
undefined
|
React.useState<SessionContextT['sessionID']>(undefined);
|
||||||
);
|
|
||||||
const [sessionState, setSessionState] = React.useState<SessionState>(
|
const [sessionState, setSessionState] = React.useState<SessionState>(
|
||||||
SessionState.NoAuth
|
SessionState.NoAuth
|
||||||
);
|
);
|
||||||
|
|
|
@ -41,9 +41,10 @@ const Picker = (props: PickerProps) => {
|
||||||
fetchPickerData();
|
fetchPickerData();
|
||||||
}, [props.serverID, authedFetch, pushRecentGuild]);
|
}, [props.serverID, authedFetch, pushRecentGuild]);
|
||||||
|
|
||||||
React.useCallback((serverID) => pushRecentGuild(serverID), [pushRecentGuild])(
|
React.useCallback(
|
||||||
props.serverID
|
(serverID) => pushRecentGuild(serverID),
|
||||||
);
|
[pushRecentGuild]
|
||||||
|
)(props.serverID);
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return <Redirect to={`/auth/login?r=${props.serverID}`} replace />;
|
return <Redirect to={`/auth/login?r=${props.serverID}`} replace />;
|
||||||
|
|
Loading…
Add table
Reference in a new issue