mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
refactor(proxy): rely on auth header instead (#9422)
* refactor(proxy): rely on auth header instead * chore: typo * chore: language Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> * chore: more language Co-authored-by: Aura Román <kyradiscord@gmail.com> * chore: more language nitpicks Co-authored-by: ckohen <chaikohen@gmail.com> * fix: unnecessary async --------- Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com> Co-authored-by: Aura Román <kyradiscord@gmail.com> Co-authored-by: ckohen <chaikohen@gmail.com>
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
|
||||
Quickly spin up an instance:
|
||||
|
||||
`docker run -d --restart unless-stopped --name proxy -p 127.0.0.1:8080:8080 -e DISCORD_TOKEN=abc discordjs/proxy`
|
||||
`docker run -d --restart unless-stopped --name proxy -p 127.0.0.1:8080:8080 discordjs/proxy`
|
||||
|
||||
Use it:
|
||||
|
||||
@@ -48,6 +48,9 @@ const rest = new REST({
|
||||
});
|
||||
```
|
||||
|
||||
**Do note that you should not use the same proxy with multiple bots. We cannot guarantee you won't hit rate limits.
|
||||
Webhooks with tokens or other requests that don't include the Authorization header are okay, though!**
|
||||
|
||||
## Links
|
||||
|
||||
- [Website][website] ([source][website-source])
|
||||
|
||||
@@ -3,12 +3,8 @@ import process from 'node:process';
|
||||
import { proxyRequests } from '@discordjs/proxy';
|
||||
import { REST } from '@discordjs/rest';
|
||||
|
||||
if (!process.env.DISCORD_TOKEN) {
|
||||
throw new Error('A DISCORD_TOKEN env var is required');
|
||||
}
|
||||
|
||||
// We want to let upstream handle retrying
|
||||
const api = new REST({ rejectOnRateLimit: () => true, retries: 0 }).setToken(process.env.DISCORD_TOKEN);
|
||||
const api = new REST({ rejectOnRateLimit: () => true, retries: 0 });
|
||||
const server = createServer(proxyRequests(api));
|
||||
|
||||
const port = Number.parseInt(process.env.PORT ?? '8080', 10);
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
import { URL } from 'node:url';
|
||||
import {
|
||||
DiscordAPIError,
|
||||
HTTPError,
|
||||
RateLimitError,
|
||||
type RequestMethod,
|
||||
type REST,
|
||||
type RouteLike,
|
||||
} from '@discordjs/rest';
|
||||
import {
|
||||
populateAbortErrorResponse,
|
||||
populateGeneralErrorResponse,
|
||||
populateSuccessfulResponse,
|
||||
populateRatelimitErrorResponse,
|
||||
} from '../util/responseHelpers.js';
|
||||
import type { RequestMethod, REST, RouteLike } from '@discordjs/rest';
|
||||
import { populateSuccessfulResponse, populateErrorResponse } from '../util/responseHelpers.js';
|
||||
import type { RequestHandler } from '../util/util';
|
||||
|
||||
/**
|
||||
@@ -36,29 +24,33 @@ export function proxyRequests(rest: REST): RequestHandler {
|
||||
// eslint-disable-next-line unicorn/no-unsafe-regex, prefer-named-capture-group
|
||||
const fullRoute = parsedUrl.pathname.replace(/^\/api(\/v\d+)?/, '') as RouteLike;
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': req.headers['content-type']!,
|
||||
};
|
||||
|
||||
if (req.headers.authorization) {
|
||||
headers.authorization = req.headers.authorization;
|
||||
}
|
||||
|
||||
try {
|
||||
const discordResponse = await rest.raw({
|
||||
body: req,
|
||||
fullRoute,
|
||||
// This type cast is technically incorrect, but we want Discord to throw Method Not Allowed for us
|
||||
method: method as RequestMethod,
|
||||
// We forward the auth header anyway
|
||||
auth: false,
|
||||
passThroughBody: true,
|
||||
query: parsedUrl.searchParams,
|
||||
headers: {
|
||||
'Content-Type': req.headers['content-type']!,
|
||||
},
|
||||
headers,
|
||||
});
|
||||
|
||||
await populateSuccessfulResponse(res, discordResponse);
|
||||
} catch (error) {
|
||||
if (error instanceof DiscordAPIError || error instanceof HTTPError) {
|
||||
populateGeneralErrorResponse(res, error);
|
||||
} else if (error instanceof RateLimitError) {
|
||||
populateRatelimitErrorResponse(res, error);
|
||||
} else if (error instanceof Error && error.name === 'AbortError') {
|
||||
populateAbortErrorResponse(res);
|
||||
} else {
|
||||
// Unclear if there's better course of action here for unknown errors. Any web framework allows to pass in an error handler for something like this
|
||||
const knownError = populateErrorResponse(res, error);
|
||||
if (!knownError) {
|
||||
// Unclear if there's better course of action here for unknown errors.
|
||||
// Any web framework allows to pass in an error handler for something like this
|
||||
// at which point the user could dictate what to do with the error - otherwise we could just 500
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ServerResponse } from 'node:http';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
import type { DiscordAPIError, HTTPError, RateLimitError } from '@discordjs/rest';
|
||||
import { DiscordAPIError, HTTPError, RateLimitError } from '@discordjs/rest';
|
||||
import type { Dispatcher } from 'undici';
|
||||
|
||||
/**
|
||||
@@ -59,3 +59,24 @@ export function populateAbortErrorResponse(res: ServerResponse): void {
|
||||
res.statusCode = 504;
|
||||
res.statusMessage = 'Upstream timed out';
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to populate a server response from an error object
|
||||
*
|
||||
* @param res - The server response to populate
|
||||
* @param error - The error to check and use
|
||||
* @returns - True if the error is known and the response object was populated, otherwise false
|
||||
*/
|
||||
export function populateErrorResponse(res: ServerResponse, error: unknown): boolean {
|
||||
if (error instanceof DiscordAPIError || error instanceof HTTPError) {
|
||||
populateGeneralErrorResponse(res, error);
|
||||
} else if (error instanceof RateLimitError) {
|
||||
populateRatelimitErrorResponse(res, error);
|
||||
} else if (error instanceof Error && error.name === 'AbortError') {
|
||||
populateAbortErrorResponse(res);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user