mirror of
https://github.com/fergalmoran/snapp.git
synced 2026-01-03 23:44:02 +00:00
Fixes on api rate limits
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
# .dockerignore
|
# .dockerignore
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
AUTH_SECRET=lFNiU7T98/44Qlqb4hMUkVcLOpijEI7z722Kxhv4O2Y=
|
AUTH_SECRET= ## openssl rand -base64 32
|
||||||
DB_HOST=
|
DB_HOST=
|
||||||
DB_PASS=
|
DB_PASS=
|
||||||
DB_PORT=6379
|
DB_PORT=6379
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import isWhiteListed from './users/isWhiteListed';
|
|||||||
import isBlackListed from './users/isBlackListed';
|
import isBlackListed from './users/isBlackListed';
|
||||||
import isBlackListedEmail from './users/isEmailBlackListed';
|
import isBlackListedEmail from './users/isEmailBlackListed';
|
||||||
import trackMaxURLs from './settings/trackMaxURLs';
|
import trackMaxURLs from './settings/trackMaxURLs';
|
||||||
import trackRPDandRPM from './settings/trackRDPandRPM';
|
import trackRPDandRPM from './settings/trackRPDandRPM';
|
||||||
import hasWhiteList from './snapps/has_whitelist';
|
import hasWhiteList from './snapps/has_whitelist';
|
||||||
|
|
||||||
export const domainZList = 'settings:app:banlists:website' as const;
|
export const domainZList = 'settings:app:banlists:website' as const;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export default async function checkRPDLimit(this: Database, userId: string, rpdL
|
|||||||
|
|
||||||
// Check if the user has already made a request on the current date
|
// Check if the user has already made a request on the current date
|
||||||
const count = await this.redis.get(key);
|
const count = await this.redis.get(key);
|
||||||
|
|
||||||
if (count && parseInt(count) >= rpdLimit) {
|
if (count && parseInt(count) >= rpdLimit) {
|
||||||
// User has exceeded the RPD limit
|
// User has exceeded the RPD limit
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { error } from '@sveltejs/kit';
|
|||||||
import type { Database } from '..';
|
import type { Database } from '..';
|
||||||
|
|
||||||
export default async function trackMaxURLs(this: Database, apiKey: DBAPIKey, _EN?: Translation) {
|
export default async function trackMaxURLs(this: Database, apiKey: DBAPIKey, _EN?: Translation) {
|
||||||
if (apiKey.roles.includes('admin') || apiKey.roles.includes('superadmin')) return true;
|
if (apiKey.roles.includes('admin') || apiKey.roles.includes('superadmin')) return false;
|
||||||
|
|
||||||
const is_limited = await this.getSetting('settings:app:limits:enabled').then(
|
const is_limited = await this.getSetting('settings:app:limits:enabled').then(
|
||||||
(res) => res === 'true' || false
|
(res) => res === 'true' || false
|
||||||
);
|
);
|
||||||
if (!is_limited) return true;
|
if (!is_limited) return false;
|
||||||
|
|
||||||
const EN = _EN ? _EN : await getLanguage();
|
const EN = _EN ? _EN : await getLanguage();
|
||||||
|
|
||||||
@@ -28,6 +28,6 @@ export default async function trackMaxURLs(this: Database, apiKey: DBAPIKey, _EN
|
|||||||
const limit = user_limit?.urls ?? global_limit_urls;
|
const limit = user_limit?.urls ?? global_limit_urls;
|
||||||
|
|
||||||
if (!limit || urls_by_this_user <= limit) {
|
if (!limit || urls_by_this_user <= limit) {
|
||||||
return true;
|
return false;
|
||||||
} else throw error(401, { message: EN['api:error:too:many:shorturl'] });
|
} else true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,13 @@ import { error } from 'console';
|
|||||||
import type { Database } from '..';
|
import type { Database } from '..';
|
||||||
|
|
||||||
export default async function trackRPDandRPM(this: Database, apiKey: DBAPIKey, _EN?: Translation) {
|
export default async function trackRPDandRPM(this: Database, apiKey: DBAPIKey, _EN?: Translation) {
|
||||||
if (apiKey.roles.includes('admin') || apiKey.roles.includes('superadmin')) return true;
|
if (apiKey.roles.includes('admin') || apiKey.roles.includes('superadmin')) return false;
|
||||||
|
|
||||||
const is_limited = await this.getSetting('settings:app:limits:enabled').then(
|
const is_limited = await this.getSetting('settings:app:limits:enabled').then(
|
||||||
(res) => res === 'true' || false
|
(res) => res === 'true' || false
|
||||||
);
|
);
|
||||||
if (!is_limited) return true;
|
if (!is_limited) return false;
|
||||||
|
|
||||||
const EN = _EN ? _EN : await getLanguage();
|
|
||||||
const global_limit_rpm = await parseNumber(this.getSetting('settings:app:limits:max:rpm'));
|
const global_limit_rpm = await parseNumber(this.getSetting('settings:app:limits:max:rpm'));
|
||||||
const global_limit_rpd = await parseNumber(this.getSetting('settings:app:limits:max:rpd'));
|
const global_limit_rpd = await parseNumber(this.getSetting('settings:app:limits:max:rpd'));
|
||||||
const user_limit = (
|
const user_limit = (
|
||||||
@@ -23,5 +22,6 @@ export default async function trackRPDandRPM(this: Database, apiKey: DBAPIKey, _
|
|||||||
|
|
||||||
const under_the_max_request_limit =
|
const under_the_max_request_limit =
|
||||||
(await this.rpm(apiKey.user_id, rpm)) && (await this.rpd(apiKey.user_id, rpd));
|
(await this.rpm(apiKey.user_id, rpm)) && (await this.rpd(apiKey.user_id, rpd));
|
||||||
if (!under_the_max_request_limit) throw error(429, { message: EN['api:error:too:many:request'] });
|
if (!under_the_max_request_limit) return true;
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
import type { SubmitFunction } from './$types';
|
import type { SubmitFunction } from './$types';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import { signIn } from '@auth/sveltekit/client';
|
import { signIn } from '@auth/sveltekit/client';
|
||||||
import { invalidateAll } from '$app/navigation';
|
import { goto, invalidateAll } from '$app/navigation';
|
||||||
import EyeIcon from 'lucide-svelte/icons/eye';
|
import EyeIcon from 'lucide-svelte/icons/eye';
|
||||||
import EyeOffIcon from 'lucide-svelte/icons/eye-off';
|
import EyeOffIcon from 'lucide-svelte/icons/eye-off';
|
||||||
let show_password = false;
|
let show_password = false;
|
||||||
@@ -51,6 +51,7 @@
|
|||||||
callbackUrl: '/'
|
callbackUrl: '/'
|
||||||
});
|
});
|
||||||
await invalidateAll();
|
await invalidateAll();
|
||||||
|
goto('/');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -59,7 +60,6 @@
|
|||||||
show_password = !show_password;
|
show_password = !show_password;
|
||||||
}
|
}
|
||||||
function handle_submit_enter(e: KeyboardEvent) {
|
function handle_submit_enter(e: KeyboardEvent) {
|
||||||
|
|
||||||
const keyEvent = e as KeyboardEvent;
|
const keyEvent = e as KeyboardEvent;
|
||||||
if (keyEvent.code !== 'Enter' && keyEvent.code !== 'NumpadEnter') return;
|
if (keyEvent.code !== 'Enter' && keyEvent.code !== 'NumpadEnter') return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const actions = {
|
|||||||
emailForm.set('email_footer', EMAIL_FOOTER);
|
emailForm.set('email_footer', EMAIL_FOOTER);
|
||||||
emailForm.set('outer_text', OUT_TEXT);
|
emailForm.set('outer_text', OUT_TEXT);
|
||||||
|
|
||||||
const send_mail = await fetch('/api/smtp/send', {
|
await fetch('/api/smtp/send', {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: 'Bearer ' + token.id
|
authorization: 'Bearer ' + token.id
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import type SnappError from '$lib/db/utils/snappError.js';
|
|||||||
import { error, fail, redirect, type NumericRange } from '@sveltejs/kit';
|
import { error, fail, redirect, type NumericRange } from '@sveltejs/kit';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import type { Actions } from './$types';
|
import type { Actions } from './$types';
|
||||||
import { generateRandomString } from '$lib/utils/randomString';
|
|
||||||
import { createTransport, type TransportOptions } from 'nodemailer';
|
|
||||||
import { env } from '$env/dynamic/public';
|
import { env } from '$env/dynamic/public';
|
||||||
import getLanguage from '$lib/api/utils/getLanguage';
|
import getLanguage from '$lib/api/utils/getLanguage';
|
||||||
|
|
||||||
@@ -124,7 +122,7 @@ export const actions: Actions = {
|
|||||||
const APP_NAME = EN['global:appname'];
|
const APP_NAME = EN['global:appname'];
|
||||||
const EMAIL_OBJECT = EN['emails:invited:object'].replace('{app_name}', APP_NAME);
|
const EMAIL_OBJECT = EN['emails:invited:object'].replace('{app_name}', APP_NAME);
|
||||||
const EMAIL_DESCRIPTION = EN['emails:invited:text']
|
const EMAIL_DESCRIPTION = EN['emails:invited:text']
|
||||||
.replace('{url}', `${env.PUBLIC_URL}/auth/recover-password?token=${token}`)
|
.replace('{url}', `${env.PUBLIC_URL}/auth/recover-password?token=${token.id}`)
|
||||||
.replace('{username}', user.username);
|
.replace('{username}', user.username);
|
||||||
const EMAIL_FOOTER = EN['emails:invited:footer'].replace('{url}', url.origin);
|
const EMAIL_FOOTER = EN['emails:invited:footer'].replace('{url}', url.origin);
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import { extractDomain } from '$lib/db/snapps/shorten.js';
|
|||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
|
|
||||||
export async function GET({ request, url }) {
|
export async function GET({ request, url }) {
|
||||||
console.log('get');
|
|
||||||
const EN = await getLanguage();
|
const EN = await getLanguage();
|
||||||
const apiKey = await authenticate(request, EN);
|
const apiKey = await authenticate(request, EN);
|
||||||
|
|
||||||
await db.trackRPDandRPM(apiKey, EN);
|
const isLimited = await db.trackRPDandRPM(apiKey, EN);
|
||||||
|
if (isLimited) throw error(429, { message: EN['api:error:too:many:request'] });
|
||||||
|
|
||||||
const { page, limit, search, sort, sortDir, offset } = getUrlParams(url);
|
const { page, limit, search, sort, sortDir, offset } = getUrlParams(url);
|
||||||
let query = db.snapps.search();
|
let query = db.snapps.search();
|
||||||
@@ -40,7 +40,7 @@ export async function GET({ request, url }) {
|
|||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'cache-control': 'max-age=' + 60 * 60,
|
'cache-control': 'max-age=' + 60 * 60,
|
||||||
'Access-Control-Allowed-Origins':"*"
|
'Access-Control-Allowed-Origins': '*'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -53,8 +53,10 @@ export async function POST({ request, fetch }) {
|
|||||||
const EN = await getLanguage();
|
const EN = await getLanguage();
|
||||||
const apiKey = await authenticate(request, EN);
|
const apiKey = await authenticate(request, EN);
|
||||||
|
|
||||||
await db.trackRPDandRPM(apiKey, EN);
|
const isLimited = await db.trackRPDandRPM(apiKey, EN);
|
||||||
await db.trackMaxURLs(apiKey, EN);
|
if (isLimited) throw error(429, { message: EN['api:error:too:many:request'] });
|
||||||
|
const hasReachedMaxLimit = await db.trackMaxURLs(apiKey, EN);
|
||||||
|
if (hasReachedMaxLimit) throw error(401, { message: EN['api:error:too:many:shorturl'] });
|
||||||
|
|
||||||
const form = await request.formData();
|
const form = await request.formData();
|
||||||
const { user_id } = apiKey;
|
const { user_id } = apiKey;
|
||||||
@@ -107,7 +109,8 @@ export async function PATCH({ request, fetch }) {
|
|||||||
const EN = await getLanguage();
|
const EN = await getLanguage();
|
||||||
const apiKey = await authenticate(request, EN);
|
const apiKey = await authenticate(request, EN);
|
||||||
|
|
||||||
await db.trackRPDandRPM(apiKey, EN);
|
const isLimited = await db.trackRPDandRPM(apiKey, EN);
|
||||||
|
if (isLimited) throw error(429, { message: EN['api:error:too:many:request'] });
|
||||||
|
|
||||||
const form = await request.formData();
|
const form = await request.formData();
|
||||||
const id = form.get('id')?.toString();
|
const id = form.get('id')?.toString();
|
||||||
@@ -171,7 +174,8 @@ export async function DELETE({ request, url }) {
|
|||||||
const EN = await getLanguage();
|
const EN = await getLanguage();
|
||||||
const apiKey = await authenticate(request, EN);
|
const apiKey = await authenticate(request, EN);
|
||||||
|
|
||||||
await db.trackRPDandRPM(apiKey, EN);
|
const isLimited = await db.trackRPDandRPM(apiKey, EN);
|
||||||
|
if (isLimited) throw error(429, { message: EN['api:error:too:many:request'] });
|
||||||
|
|
||||||
const { user_id } = apiKey;
|
const { user_id } = apiKey;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user