mirror of
https://github.com/wukko/cobalt.git
synced 2024-11-15 12:50:01 +00:00
api/core: rate limit by token if it's present
This commit is contained in:
parent
c54294601b
commit
30c51b9fe8
1 changed files with 57 additions and 43 deletions
|
@ -35,6 +35,11 @@ const corsConfig = env.corsWildcard ? {} : {
|
||||||
optionsSuccessStatus: 200
|
optionsSuccessStatus: 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fail = (res, code) => {
|
||||||
|
const { status, body } = createResponse("error", { code });
|
||||||
|
res.status(status).json(body);
|
||||||
|
}
|
||||||
|
|
||||||
export function runAPI(express, app, __dirname) {
|
export function runAPI(express, app, __dirname) {
|
||||||
const startTime = new Date();
|
const startTime = new Date();
|
||||||
const startTimestamp = startTime.getTime();
|
const startTimestamp = startTime.getTime();
|
||||||
|
@ -52,7 +57,12 @@ export function runAPI(express, app, __dirname) {
|
||||||
max: env.rateLimitMax,
|
max: env.rateLimitMax,
|
||||||
standardHeaders: true,
|
standardHeaders: true,
|
||||||
legacyHeaders: false,
|
legacyHeaders: false,
|
||||||
keyGenerator: req => generateHmac(getIP(req), ipSalt),
|
keyGenerator: req => {
|
||||||
|
if (req.authorized) {
|
||||||
|
return generateHmac(req.header("Authorization"), ipSalt);
|
||||||
|
}
|
||||||
|
return generateHmac(getIP(req), ipSalt);
|
||||||
|
},
|
||||||
handler: (req, res) => {
|
handler: (req, res) => {
|
||||||
const { status, body } = createResponse("error", {
|
const { status, body } = createResponse("error", {
|
||||||
code: "error.rate_exceeded",
|
code: "error.rate_exceeded",
|
||||||
|
@ -86,9 +96,45 @@ export function runAPI(express, app, __dirname) {
|
||||||
'Ratelimit-Reset'
|
'Ratelimit-Reset'
|
||||||
],
|
],
|
||||||
...corsConfig,
|
...corsConfig,
|
||||||
}))
|
}));
|
||||||
|
|
||||||
app.use('/', apiLimiter);
|
app.post('/', (req, res, next) => {
|
||||||
|
try {
|
||||||
|
if (env.turnstileSecret && env.jwtSecret) {
|
||||||
|
const authorization = req.header("Authorization");
|
||||||
|
if (!authorization) {
|
||||||
|
return fail(res, "error.api.auth.jwt.missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!authorization.startsWith("Bearer ") || authorization.length > 256) {
|
||||||
|
return fail(res, "error.api.auth.jwt.invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyJwt = jwt.verify(
|
||||||
|
authorization.split("Bearer ", 2)[1]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!verifyJwt) {
|
||||||
|
return fail(res, "error.api.auth.jwt.invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acceptRegex.test(req.header('Accept'))) {
|
||||||
|
return fail(res, 'ErrorInvalidAcceptHeader');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!acceptRegex.test(req.header('Content-Type'))) {
|
||||||
|
return fail(res, 'ErrorInvalidContentType');
|
||||||
|
}
|
||||||
|
|
||||||
|
req.authorized = true;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return fail(res, "error.api.generic");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/', apiLimiter);
|
||||||
app.use('/stream', apiLimiterStream);
|
app.use('/stream', apiLimiterStream);
|
||||||
|
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
|
@ -117,13 +163,13 @@ export function runAPI(express, app, __dirname) {
|
||||||
|
|
||||||
app.post("/session", async (req, res) => {
|
app.post("/session", async (req, res) => {
|
||||||
if (!env.turnstileSecret || !env.jwtSecret) {
|
if (!env.turnstileSecret || !env.jwtSecret) {
|
||||||
return fail("error.api.auth.not_configured")
|
return fail(res, "error.api.auth.not_configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
const turnstileResponse = req.header("cf-turnstile-response");
|
const turnstileResponse = req.header("cf-turnstile-response");
|
||||||
|
|
||||||
if (!turnstileResponse) {
|
if (!turnstileResponse) {
|
||||||
return fail("error.api.auth.turnstile.missing");
|
return fail(res, "error.api.auth.turnstile.missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
const turnstileResult = await verifyTurnstileToken(
|
const turnstileResult = await verifyTurnstileToken(
|
||||||
|
@ -132,13 +178,13 @@ export function runAPI(express, app, __dirname) {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!turnstileResult) {
|
if (!turnstileResult) {
|
||||||
return fail("error.api.auth.turnstile.invalid");
|
return fail(res, "error.api.auth.turnstile.invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res.json(jwt.generate());
|
res.json(jwt.generate());
|
||||||
} catch {
|
} catch {
|
||||||
return fail("error.api.generic");
|
return fail(res, "error.api.generic");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,40 +192,8 @@ export function runAPI(express, app, __dirname) {
|
||||||
const request = req.body;
|
const request = req.body;
|
||||||
const lang = languageCode(req);
|
const lang = languageCode(req);
|
||||||
|
|
||||||
const fail = (code) => {
|
|
||||||
const { status, body } = createResponse("error", { code });
|
|
||||||
res.status(status).json(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.jwtSecret) {
|
|
||||||
const authorization = req.header("Authorization");
|
|
||||||
if (!authorization) {
|
|
||||||
return fail("error.api.auth.jwt.missing");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!authorization.startsWith("Bearer ") || authorization.length > 256) {
|
|
||||||
return fail("error.api.auth.jwt.invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
const verifyJwt = jwt.verify(
|
|
||||||
req.header("Authorization").split("Bearer ", 2)[1]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!verifyJwt) {
|
|
||||||
return fail("error.api.auth.jwt.invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!acceptRegex.test(req.header('Accept'))) {
|
|
||||||
return fail('ErrorInvalidAcceptHeader');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!acceptRegex.test(req.header('Content-Type'))) {
|
|
||||||
return fail('ErrorInvalidContentType');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.url) {
|
if (!request.url) {
|
||||||
return fail('ErrorNoLink');
|
return fail(res, 'ErrorNoLink');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.youtubeDubBrowserLang) {
|
if (request.youtubeDubBrowserLang) {
|
||||||
|
@ -188,12 +202,12 @@ export function runAPI(express, app, __dirname) {
|
||||||
|
|
||||||
const { success, data: normalizedRequest } = await normalizeRequest(request);
|
const { success, data: normalizedRequest } = await normalizeRequest(request);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return fail('ErrorCantProcess');
|
return fail(res, 'ErrorCantProcess');
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = extract(normalizedRequest.url);
|
const parsed = extract(normalizedRequest.url);
|
||||||
if (parsed === null) {
|
if (parsed === null) {
|
||||||
return fail('ErrorUnsupported');
|
return fail(res, 'ErrorUnsupported');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -203,7 +217,7 @@ export function runAPI(express, app, __dirname) {
|
||||||
|
|
||||||
res.status(result.status).json(result.body);
|
res.status(result.status).json(result.body);
|
||||||
} catch {
|
} catch {
|
||||||
fail('ErrorSomethingWentWrong');
|
fail(res, 'ErrorSomethingWentWrong');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue