Compare commits
3 Commits
37870b42d4
...
b9910fd1e4
| Author | SHA1 | Date | |
|---|---|---|---|
| b9910fd1e4 | |||
| 1ead110a21 | |||
| d38dba711d |
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
.env
|
|
||||||
474
backup.sql
@ -1,474 +0,0 @@
|
|||||||
--
|
|
||||||
-- PostgreSQL database dump
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Dumped from database version 15.12 (Debian 15.12-1.pgdg120+1)
|
|
||||||
-- Dumped by pg_dump version 15.12 (Debian 15.12-1.pgdg120+1)
|
|
||||||
|
|
||||||
SET statement_timeout = 0;
|
|
||||||
SET lock_timeout = 0;
|
|
||||||
SET idle_in_transaction_session_timeout = 0;
|
|
||||||
SET client_encoding = 'UTF8';
|
|
||||||
SET standard_conforming_strings = on;
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
SET check_function_bodies = false;
|
|
||||||
SET xmloption = content;
|
|
||||||
SET client_min_messages = warning;
|
|
||||||
SET row_security = off;
|
|
||||||
|
|
||||||
SET default_tablespace = '';
|
|
||||||
|
|
||||||
SET default_table_access_method = heap;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.gallery_images (
|
|
||||||
id integer NOT NULL,
|
|
||||||
image_url text NOT NULL,
|
|
||||||
title text,
|
|
||||||
uploaded_at timestamp without time zone DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.gallery_images OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images_id_seq; Type: SEQUENCE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.gallery_images_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.gallery_images_id_seq OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.gallery_images_id_seq OWNED BY public.gallery_images.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.news (
|
|
||||||
id integer NOT NULL,
|
|
||||||
title character varying(255) NOT NULL,
|
|
||||||
description text NOT NULL,
|
|
||||||
image_url text,
|
|
||||||
team character varying(255),
|
|
||||||
created_at timestamp without time zone DEFAULT now()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.news OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news_id_seq; Type: SEQUENCE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.news_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.news_id_seq OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.news_id_seq OWNED BY public.news.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: player_teams; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.player_teams (
|
|
||||||
player_id integer NOT NULL,
|
|
||||||
team_id integer NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.player_teams OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.players (
|
|
||||||
id integer NOT NULL,
|
|
||||||
name character varying(255) NOT NULL,
|
|
||||||
nickname character varying(255),
|
|
||||||
birthdate date,
|
|
||||||
"position" character varying(255) NOT NULL,
|
|
||||||
jersey_number integer,
|
|
||||||
favorite_food character varying(255),
|
|
||||||
image_url text,
|
|
||||||
status character varying(50) DEFAULT 'aktiv'::character varying
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.players OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players_id_seq; Type: SEQUENCE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.players_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.players_id_seq OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.players_id_seq OWNED BY public.players.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.teams (
|
|
||||||
id integer NOT NULL,
|
|
||||||
name character varying(255) NOT NULL,
|
|
||||||
liga text,
|
|
||||||
sucht_spieler boolean DEFAULT false,
|
|
||||||
social_media text,
|
|
||||||
karussell_bilder text[],
|
|
||||||
trainer_name text,
|
|
||||||
trainingszeiten text,
|
|
||||||
trainingsort text,
|
|
||||||
kontakt_name text,
|
|
||||||
kontakt_email text,
|
|
||||||
teamfarben text,
|
|
||||||
beschreibung text,
|
|
||||||
tabellenlink text
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.teams OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams_id_seq; Type: SEQUENCE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.teams_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.teams_id_seq OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.teams_id_seq OWNED BY public.teams.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users; Type: TABLE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE public.users (
|
|
||||||
id integer NOT NULL,
|
|
||||||
username character varying(255) NOT NULL,
|
|
||||||
password character varying(255) NOT NULL,
|
|
||||||
role character varying(50),
|
|
||||||
email character varying(255),
|
|
||||||
created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
last_logged timestamp without time zone
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.users OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE SEQUENCE public.users_id_seq
|
|
||||||
AS integer
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE public.users_id_seq OWNER TO tgadmin;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER SEQUENCE public.users_id_seq OWNED BY public.users.id;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images id; Type: DEFAULT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.gallery_images ALTER COLUMN id SET DEFAULT nextval('public.gallery_images_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news id; Type: DEFAULT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.news ALTER COLUMN id SET DEFAULT nextval('public.news_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players id; Type: DEFAULT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.players ALTER COLUMN id SET DEFAULT nextval('public.players_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams id; Type: DEFAULT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.teams ALTER COLUMN id SET DEFAULT nextval('public.teams_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users id; Type: DEFAULT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.users ALTER COLUMN id SET DEFAULT nextval('public.users_id_seq'::regclass);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: gallery_images; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.gallery_images (id, image_url, title, uploaded_at) FROM stdin;
|
|
||||||
1 /uploads/gallery/1745995571128.jpg \N 2025-04-30 06:46:11.135241
|
|
||||||
2 /uploads/gallery/1745995574092.jpg \N 2025-04-30 06:46:14.14359
|
|
||||||
3 /uploads/gallery/1745995576651.png \N 2025-04-30 06:46:16.693676
|
|
||||||
5 /uploads/gallery/1745995582496.jpg \N 2025-04-30 06:46:22.543915
|
|
||||||
7 /uploads/gallery/1745995795079.jpg \N 2025-04-30 06:49:55.087993
|
|
||||||
8 /uploads/gallery/1745995797723.jpg \N 2025-04-30 06:49:57.773579
|
|
||||||
9 /uploads/gallery/1745995800923.png \N 2025-04-30 06:50:00.973585
|
|
||||||
10 /uploads/gallery/1745995804788.jpg \N 2025-04-30 06:50:04.833931
|
|
||||||
11 /uploads/gallery/1745995810186.png \N 2025-04-30 06:50:10.23422
|
|
||||||
13 /uploads/gallery/1745997000203.png \N 2025-04-30 07:10:00.204411
|
|
||||||
14 /uploads/gallery/1745997004338.png \N 2025-04-30 07:10:04.383564
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: news; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.news (id, title, description, image_url, team, created_at) FROM stdin;
|
|
||||||
1 Neue News vorhanden <p>Diese hat sogar <strong>Rich Text Support </strong>enabled!</p> /uploads/news/1745994332500.jpg Herren 1 2025-04-29 07:39:47.266851
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: player_teams; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.player_teams (player_id, team_id) FROM stdin;
|
|
||||||
1 1
|
|
||||||
3 1
|
|
||||||
4 1
|
|
||||||
5 1
|
|
||||||
6 1
|
|
||||||
7 1
|
|
||||||
8 1
|
|
||||||
2 1
|
|
||||||
9 4
|
|
||||||
10 4
|
|
||||||
11 4
|
|
||||||
12 4
|
|
||||||
13 4
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: players; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.players (id, name, nickname, birthdate, "position", jersey_number, favorite_food, image_url, status) FROM stdin;
|
|
||||||
6 Lasse Höfer LassEs! \N Außen/Annahme 6 LasseRanja /uploads/players/1745913205538.jpg aktiv
|
|
||||||
1 Marc Wieland Marci \N Diagonal 20 Dönerfleisch /uploads/players/1745913211650.jpg aktiv
|
|
||||||
7 Peter Roller Brrm Brrm \N Zuspieler 4 Reis mit Hühnchen /uploads/players/1745913222839.jpg aktiv
|
|
||||||
5 Phillip Schaefer Hifi \N Libero 13 Dönerfleisch /uploads/players/1745913229017.jpg aktiv
|
|
||||||
3 Samuel Valentino Quintero Artigas (Be)samu \N Mitte 8 Pizza /uploads/players/1745913236094.jpg aktiv
|
|
||||||
4 Sten Grüner Stenislav \N Außen/Annahme 3 Borschtsch /uploads/players/1745913243174.jpg aktiv
|
|
||||||
8 Tony Fan Tony Mahoni \N Zuspieler 11 Asia-Wok /uploads/players/1745913254103.jpg aktiv
|
|
||||||
2 David Brockmüller Blocki \N Mitte/Diagonal 12 Keks /uploads/players/1745913167980.jpg aktiv
|
|
||||||
9 Ann-Kathrin Minden Annki \N Mitte 9 Cola /uploads/players/1745928485551.jpg aktiv
|
|
||||||
10 Luisa Trautmann Lulu/Lu \N Außen/Annahme 6 Kenneth /uploads/players/1745928513832.jpg aktiv
|
|
||||||
11 Paula Jüllich Pauli \N Mitte 10 Döner vom Özefe /uploads/players/1745928548483.jpg aktiv
|
|
||||||
12 Annika Braasch Anni \N Zuspiel 2 Sebastian /uploads/players/1745928569406.jpg aktiv
|
|
||||||
13 Sebastian Minden Basti \N Trainer \N Whisky /uploads/players/1745928620598.jpg aktiv
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: teams; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.teams (id, name, liga, sucht_spieler, social_media, karussell_bilder, trainer_name, trainingszeiten, trainingsort, kontakt_name, kontakt_email, teamfarben, beschreibung, tabellenlink) FROM stdin;
|
|
||||||
2 Herren 2 \N f \N \N \N \N \N \N \N \N \N \N
|
|
||||||
5 Damen 2 \N f \N \N \N \N \N \N \N \N \N \N
|
|
||||||
4 Damen 1 Landesliga t instagram.com/tgl_volleyball {} Sebastian Minden Montags & Freitags 20-22 Uhr Bergstraßenhalle Laudenbach Sebastian Minden sebastian@minden.de Grün Wir sind die Damen 1, eine super Truppe mit viel Zug zum Erfolg! https://www.volleyball-nordbaden.de/cms/home/halle/spielbetrieb/tabellen_vl_ll.xhtml?LeaguePresenter.view=resultTable&LeaguePresenter.matchSeriesId=70120527#samsCmsComponent_68828342
|
|
||||||
1 Herren 1 Landesliga f www.google.de {} Kahtrin Trübenbach Montags & Freitags, 19:30-22 Uhr Montags: Bergstraßenhalle Laudenbach, Freitags: BIZ Hemsbach Kathrin Trübenbach trübi@bach.de Grün Wir sind die H1 und wir machen Hackfleisch aus dir! https://www.volleyball-nordbaden.de/cms/home/halle/spielbetrieb/tabellen_vl_ll.xhtml?LeaguePresenter.view=resultTable&LeaguePresenter.matchSeriesId=70120580#samsCmsComponent_68828342
|
|
||||||
9 Mixed Bezirksklasse t {} Wolf Blecher Freitags: 20-22 Uhr Bergstraßenhalle Laudenbach Wolf Blecher wolf@blech.de Grün Spiel und Spaß für jung und alt!
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
COPY public.users (id, username, password, role, email, created_at, last_logged) FROM stdin;
|
|
||||||
2 kennyschrubber $2b$10$mHgcSphPt.WsNrEnYlM.i.wPVCeWnkd4bGCPpn2B50fdTOK9DvhxK user kennyschrubber@web.de 2025-04-23 13:19:15.946587 \N
|
|
||||||
4 hackerino $2b$10$DYv1lN3kivPCvdDnE4oSxOrXz0Bu.7K5EEz72RnjbgE9iemL3N/yS user hackerino@web.de 2025-04-23 13:29:57.510891 \N
|
|
||||||
1 marcwieland $2b$10$X0RgZYuVMBpIuGUM2PvGHew.siTgdaiJg0TbJ6sDZd0JPgXZII6IS admin wieland.marc@gmx.de 2025-04-23 12:00:02.440704 2025-04-30 06:45:53.31351
|
|
||||||
\.
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
SELECT pg_catalog.setval('public.gallery_images_id_seq', 14, true);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
SELECT pg_catalog.setval('public.news_id_seq', 1, true);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
SELECT pg_catalog.setval('public.players_id_seq', 13, true);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
SELECT pg_catalog.setval('public.teams_id_seq', 9, true);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users_id_seq; Type: SEQUENCE SET; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
SELECT pg_catalog.setval('public.users_id_seq', 4, true);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: gallery_images gallery_images_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.gallery_images
|
|
||||||
ADD CONSTRAINT gallery_images_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: news news_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.news
|
|
||||||
ADD CONSTRAINT news_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: player_teams player_teams_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.player_teams
|
|
||||||
ADD CONSTRAINT player_teams_pkey PRIMARY KEY (player_id, team_id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: players players_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.players
|
|
||||||
ADD CONSTRAINT players_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams teams_name_key; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.teams
|
|
||||||
ADD CONSTRAINT teams_name_key UNIQUE (name);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: teams teams_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.teams
|
|
||||||
ADD CONSTRAINT teams_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.users
|
|
||||||
ADD CONSTRAINT users_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: player_teams player_teams_player_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.player_teams
|
|
||||||
ADD CONSTRAINT player_teams_player_id_fkey FOREIGN KEY (player_id) REFERENCES public.players(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Name: player_teams player_teams_team_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: tgadmin
|
|
||||||
--
|
|
||||||
|
|
||||||
ALTER TABLE ONLY public.player_teams
|
|
||||||
ADD CONSTRAINT player_teams_team_id_fkey FOREIGN KEY (team_id) REFERENCES public.teams(id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- PostgreSQL database dump complete
|
|
||||||
--
|
|
||||||
|
|
||||||
210
index.js
@ -5,39 +5,21 @@ const multer = require('multer');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
|
//Rate Limit
|
||||||
|
const rateLimit = require('express-rate-limit');
|
||||||
|
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = process.env.PORT || 3000;
|
const port = process.env.PORT || 3000;
|
||||||
|
|
||||||
const pool = new Pool({
|
|
||||||
user: process.env.DB_USER,
|
|
||||||
host: process.env.DB_HOST,
|
|
||||||
database: process.env.DB_NAME,
|
|
||||||
password: process.env.DB_PASSWORD,
|
|
||||||
port: process.env.DB_PORT,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const bcrypt = require('bcryptjs');
|
const bcrypt = require('bcryptjs');
|
||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
const JWT_SECRET = process.env.JWT_SECRET;
|
const JWT_SECRET = "supergeheimes_tg_cms_secret";
|
||||||
//Rate Limiter fuer Logins
|
|
||||||
const rateLimit = require("express-rate-limit");
|
|
||||||
|
|
||||||
|
|
||||||
const loginLimiter = rateLimit({
|
|
||||||
windowMs: 15 * 60 * 1000,
|
|
||||||
max: 5,
|
|
||||||
message: "Zu viele Login-Versuche. Bitte in 15 Minuten erneut versuchen.",
|
|
||||||
standardHeaders: true,
|
|
||||||
legacyHeaders: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Bodyparser für JSON aktivieren
|
// Bodyparser für JSON aktivieren
|
||||||
app.use(cors({
|
app.use(cors({
|
||||||
origin: ["http://volleyball.marc-wieland.de", "https://volleyball.marc-wieland.de"],
|
origin: ["http://localhost:8080", "http://192.168.50.65:8080"],
|
||||||
methods: ["GET", "POST", "PUT", "DELETE"],
|
methods: ["GET", "POST", "PUT", "DELETE"],
|
||||||
credentials: true
|
credentials: true
|
||||||
}));
|
}));
|
||||||
@ -46,7 +28,14 @@ app.use(express.json());
|
|||||||
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));
|
||||||
|
|
||||||
|
|
||||||
|
// PostgreSQL Verbindung aufbauen
|
||||||
|
const pool = new Pool({
|
||||||
|
user: "tgadmin",
|
||||||
|
host: "db", // Docker Container Name!
|
||||||
|
database: "tg-cms",
|
||||||
|
password: "secretpass",
|
||||||
|
port: 5432,
|
||||||
|
});
|
||||||
|
|
||||||
// Testroute API-Check
|
// Testroute API-Check
|
||||||
app.get("/api/hello", (req, res) => {
|
app.get("/api/hello", (req, res) => {
|
||||||
@ -163,6 +152,18 @@ app.post("/api/users", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Rate Limiter fuer Login
|
||||||
|
const loginLimiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 9999,
|
||||||
|
standardHeaders: true,
|
||||||
|
legacyHeaders: false,
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Benutzer Login
|
// Benutzer Login
|
||||||
app.post("/api/login", loginLimiter, async (req, res) => {
|
app.post("/api/login", loginLimiter, async (req, res) => {
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
@ -223,7 +224,7 @@ app.get("/api/users", async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Benutzer l<EFBFBD>loeschen
|
// Benutzer loeschen
|
||||||
app.delete("/api/users/:id", async (req, res) => {
|
app.delete("/api/users/:id", async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
@ -321,7 +322,6 @@ app.post("/api/upload-news-image", uploadNewsImage.single("image"), (req, res) =
|
|||||||
|
|
||||||
|
|
||||||
//Teams abfragen
|
//Teams abfragen
|
||||||
|
|
||||||
app.get("/api/teams", async (req, res) => {
|
app.get("/api/teams", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const result = await pool.query(`
|
const result = await pool.query(`
|
||||||
@ -329,12 +329,10 @@ app.get("/api/teams", async (req, res) => {
|
|||||||
t.id,
|
t.id,
|
||||||
t.name,
|
t.name,
|
||||||
t.liga,
|
t.liga,
|
||||||
t.beschreibung,
|
|
||||||
t.trainingszeiten,
|
|
||||||
t.sucht_spieler,
|
t.sucht_spieler,
|
||||||
t.social_media,
|
t.social_media,
|
||||||
t.trainer_name,
|
t.trainer_name,
|
||||||
t.karussell_bilder,
|
t.karussell_bilder, -- 👉 Hier angepasst!
|
||||||
COUNT(pt.player_id) AS player_count
|
COUNT(pt.player_id) AS player_count
|
||||||
FROM teams t
|
FROM teams t
|
||||||
LEFT JOIN player_teams pt ON pt.team_id = t.id
|
LEFT JOIN player_teams pt ON pt.team_id = t.id
|
||||||
@ -342,12 +340,10 @@ app.get("/api/teams", async (req, res) => {
|
|||||||
t.id,
|
t.id,
|
||||||
t.name,
|
t.name,
|
||||||
t.liga,
|
t.liga,
|
||||||
t.beschreibung,
|
|
||||||
t.trainingszeiten,
|
|
||||||
t.sucht_spieler,
|
t.sucht_spieler,
|
||||||
t.social_media,
|
t.social_media,
|
||||||
t.trainer_name,
|
t.trainer_name,
|
||||||
t.karussell_bilder
|
t.karussell_bilder -- 👉 Hier auch
|
||||||
ORDER BY t.name ASC
|
ORDER BY t.name ASC
|
||||||
`);
|
`);
|
||||||
res.json(result.rows);
|
res.json(result.rows);
|
||||||
@ -358,9 +354,7 @@ app.get("/api/teams", async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Team aktualisieren
|
||||||
|
|
||||||
//Team aktualisieren
|
|
||||||
app.put("/api/teams/:id", async (req, res) => {
|
app.put("/api/teams/:id", async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const {
|
const {
|
||||||
@ -818,152 +812,6 @@ app.post("/api/players/:id/assign-team", async (req, res) => {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Multer Storage fuer Galleriebilder
|
|
||||||
const galleryStorage = multer.diskStorage({
|
|
||||||
destination: (req, file, cb) => {
|
|
||||||
const dir = "./uploads/gallery";
|
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
|
||||||
cb(null, dir);
|
|
||||||
},
|
|
||||||
filename: (req, file, cb) => {
|
|
||||||
const ext = path.extname(file.originalname);
|
|
||||||
const filename = Date.now() + ext;
|
|
||||||
cb(null, filename);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const uploadGalleryImage = multer({ storage: galleryStorage });
|
|
||||||
|
|
||||||
|
|
||||||
//Gallery Image Upload
|
|
||||||
app.post("/api/gallery", uploadGalleryImage.single("image"), async (req, res) => {
|
|
||||||
const { title } = req.body;
|
|
||||||
if (!req.file) return res.status(400).send("Kein Bild hochgeladen");
|
|
||||||
|
|
||||||
const imageUrl = `/uploads/gallery/${req.file.filename}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await pool.query(
|
|
||||||
"INSERT INTO gallery_images (image_url, title) VALUES ($1, $2) RETURNING *",
|
|
||||||
[imageUrl, title || null]
|
|
||||||
);
|
|
||||||
res.status(201).json(result.rows[0]);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Fehler beim Speichern des Galeriebilds:", err);
|
|
||||||
res.status(500).send("Fehler beim Speichern");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Get all gallery images
|
|
||||||
app.get("/api/gallery", async (req, res) => {
|
|
||||||
try {
|
|
||||||
const result = await pool.query("SELECT * FROM gallery_images ORDER BY uploaded_at DESC");
|
|
||||||
res.json(result.rows);
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Fehler beim Laden der Galerie:", err);
|
|
||||||
res.status(500).send("Fehler beim Laden");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Delete image
|
|
||||||
app.delete("/api/gallery/:id", async (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await pool.query("DELETE FROM gallery_images WHERE id = $1 RETURNING *", [id]);
|
|
||||||
|
|
||||||
if (result.rowCount === 0) return res.status(404).send("Bild nicht gefunden");
|
|
||||||
|
|
||||||
// Optional: Datei auch vom Dateisystem löschen
|
|
||||||
const filePath = path.join(__dirname, result.rows[0].image_url);
|
|
||||||
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
||||||
|
|
||||||
res.send("Bild erfolgreich gelöscht");
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Fehler beim Löschen des Bildes:", err);
|
|
||||||
res.status(500).send("Fehler beim Löschen");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Event laden
|
|
||||||
app.get("/api/events", async (req, res) => {
|
|
||||||
const { showPrivate } = req.query;
|
|
||||||
const query = showPrivate === "true"
|
|
||||||
? "SELECT * FROM events ORDER BY created_at DESC"
|
|
||||||
: "SELECT * FROM events WHERE is_private = FALSE ORDER BY created_at DESC";
|
|
||||||
|
|
||||||
const result = await pool.query(query);
|
|
||||||
res.json(result.rows);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Event erstellen
|
|
||||||
app.post("/api/events", async (req, res) => {
|
|
||||||
const { title, description, max_participants, fee, address, is_private } = req.body;
|
|
||||||
|
|
||||||
const result = await pool.query(
|
|
||||||
`INSERT INTO events (title, description, max_participants, fee, address, is_private)
|
|
||||||
VALUES ($1, $2, $3, $4, $5, $6) RETURNING *`,
|
|
||||||
[title, description, max_participants, fee, address, is_private]
|
|
||||||
);
|
|
||||||
|
|
||||||
res.status(201).json(result.rows[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Event editieren
|
|
||||||
app.put("/api/events/:id", async (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
const { title, description, max_participants, fee, address, is_private } = req.body;
|
|
||||||
|
|
||||||
const result = await pool.query(
|
|
||||||
`UPDATE events
|
|
||||||
SET title = $1,
|
|
||||||
description = $2,
|
|
||||||
max_participants = $3,
|
|
||||||
fee = $4,
|
|
||||||
address = $5,
|
|
||||||
is_private = $6
|
|
||||||
WHERE id = $7
|
|
||||||
RETURNING *`,
|
|
||||||
[title, description, max_participants, fee, address, is_private, id]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result.rowCount === 0) {
|
|
||||||
return res.status(404).json({ message: "Event not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json(result.rows[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//Event loeschen
|
|
||||||
app.delete("/api/events/:id", async (req, res) => {
|
|
||||||
const { id } = req.params;
|
|
||||||
|
|
||||||
const result = await pool.query(`DELETE FROM events WHERE id = $1`, [id]);
|
|
||||||
|
|
||||||
if (result.rowCount === 0) {
|
|
||||||
return res.status(404).json({ message: "Event not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(204).send(); // No Content
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
node_modules/.bin/bcrypt
generated
vendored
@ -1 +1,16 @@
|
|||||||
../bcryptjs/bin/bcrypt
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../bcryptjs/bin/bcrypt" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../bcryptjs/bin/bcrypt" "$@"
|
||||||
|
fi
|
||||||
|
|||||||
17
node_modules/.bin/bcrypt.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\bcryptjs\bin\bcrypt" %*
|
||||||
28
node_modules/.bin/bcrypt.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../bcryptjs/bin/bcrypt" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
17
node_modules/.bin/mkdirp
generated
vendored
@ -1 +1,16 @@
|
|||||||
../mkdirp/bin/cmd.js
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../mkdirp/bin/cmd.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../mkdirp/bin/cmd.js" "$@"
|
||||||
|
fi
|
||||||
|
|||||||
17
node_modules/.bin/mkdirp.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mkdirp\bin\cmd.js" %*
|
||||||
28
node_modules/.bin/mkdirp.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../mkdirp/bin/cmd.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../mkdirp/bin/cmd.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../mkdirp/bin/cmd.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../mkdirp/bin/cmd.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
17
node_modules/.bin/semver
generated
vendored
@ -1 +1,16 @@
|
|||||||
../semver/bin/semver.js
|
#!/bin/sh
|
||||||
|
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
|
||||||
|
|
||||||
|
case `uname` in
|
||||||
|
*CYGWIN*|*MINGW*|*MSYS*)
|
||||||
|
if command -v cygpath > /dev/null 2>&1; then
|
||||||
|
basedir=`cygpath -w "$basedir"`
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -x "$basedir/node" ]; then
|
||||||
|
exec "$basedir/node" "$basedir/../semver/bin/semver.js" "$@"
|
||||||
|
else
|
||||||
|
exec node "$basedir/../semver/bin/semver.js" "$@"
|
||||||
|
fi
|
||||||
|
|||||||
17
node_modules/.bin/semver.cmd
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@ECHO off
|
||||||
|
GOTO start
|
||||||
|
:find_dp0
|
||||||
|
SET dp0=%~dp0
|
||||||
|
EXIT /b
|
||||||
|
:start
|
||||||
|
SETLOCAL
|
||||||
|
CALL :find_dp0
|
||||||
|
|
||||||
|
IF EXIST "%dp0%\node.exe" (
|
||||||
|
SET "_prog=%dp0%\node.exe"
|
||||||
|
) ELSE (
|
||||||
|
SET "_prog=node"
|
||||||
|
SET PATHEXT=%PATHEXT:;.JS;=;%
|
||||||
|
)
|
||||||
|
|
||||||
|
endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\semver\bin\semver.js" %*
|
||||||
28
node_modules/.bin/semver.ps1
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env pwsh
|
||||||
|
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
|
||||||
|
|
||||||
|
$exe=""
|
||||||
|
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
|
||||||
|
# Fix case when both the Windows and Linux builds of Node
|
||||||
|
# are installed in the same directory
|
||||||
|
$exe=".exe"
|
||||||
|
}
|
||||||
|
$ret=0
|
||||||
|
if (Test-Path "$basedir/node$exe") {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||||
|
} else {
|
||||||
|
& "$basedir/node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
} else {
|
||||||
|
# Support pipeline input
|
||||||
|
if ($MyInvocation.ExpectingInput) {
|
||||||
|
$input | & "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||||
|
} else {
|
||||||
|
& "node$exe" "$basedir/../semver/bin/semver.js" $args
|
||||||
|
}
|
||||||
|
$ret=$LASTEXITCODE
|
||||||
|
}
|
||||||
|
exit $ret
|
||||||
15
node_modules/.package-lock.json
generated
vendored
@ -216,6 +216,7 @@
|
|||||||
"version": "16.5.0",
|
"version": "16.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -348,20 +349,6 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-rate-limit": {
|
|
||||||
"version": "7.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
|
|
||||||
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/express-rate-limit"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||||
|
|||||||
838
node_modules/express-rate-limit/dist/index.cjs
generated
vendored
@ -1,838 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
var __defProp = Object.defineProperty;
|
|
||||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
||||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
||||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
||||||
var __export = (target, all) => {
|
|
||||||
for (var name in all)
|
|
||||||
__defProp(target, name, { get: all[name], enumerable: true });
|
|
||||||
};
|
|
||||||
var __copyProps = (to, from, except, desc) => {
|
|
||||||
if (from && typeof from === "object" || typeof from === "function") {
|
|
||||||
for (let key of __getOwnPropNames(from))
|
|
||||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
||||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
||||||
}
|
|
||||||
return to;
|
|
||||||
};
|
|
||||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
||||||
|
|
||||||
// source/index.ts
|
|
||||||
var source_exports = {};
|
|
||||||
__export(source_exports, {
|
|
||||||
MemoryStore: () => MemoryStore,
|
|
||||||
default: () => lib_default,
|
|
||||||
rateLimit: () => lib_default
|
|
||||||
});
|
|
||||||
module.exports = __toCommonJS(source_exports);
|
|
||||||
|
|
||||||
// source/headers.ts
|
|
||||||
var import_node_buffer = require("buffer");
|
|
||||||
var import_node_crypto = require("crypto");
|
|
||||||
var SUPPORTED_DRAFT_VERSIONS = ["draft-6", "draft-7", "draft-8"];
|
|
||||||
var getResetSeconds = (resetTime, windowMs) => {
|
|
||||||
let resetSeconds = void 0;
|
|
||||||
if (resetTime) {
|
|
||||||
const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
|
|
||||||
resetSeconds = Math.max(0, deltaSeconds);
|
|
||||||
} else if (windowMs) {
|
|
||||||
resetSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
}
|
|
||||||
return resetSeconds;
|
|
||||||
};
|
|
||||||
var getPartitionKey = (key) => {
|
|
||||||
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
||||||
hash.update(key);
|
|
||||||
const partitionKey = hash.digest("hex").slice(0, 12);
|
|
||||||
return import_node_buffer.Buffer.from(partitionKey).toString("base64");
|
|
||||||
};
|
|
||||||
var setLegacyHeaders = (response, info) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
response.setHeader("X-RateLimit-Limit", info.limit.toString());
|
|
||||||
response.setHeader("X-RateLimit-Remaining", info.remaining.toString());
|
|
||||||
if (info.resetTime instanceof Date) {
|
|
||||||
response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
|
|
||||||
response.setHeader(
|
|
||||||
"X-RateLimit-Reset",
|
|
||||||
Math.ceil(info.resetTime.getTime() / 1e3).toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var setDraft6Headers = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime);
|
|
||||||
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
||||||
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
||||||
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
||||||
if (resetSeconds)
|
|
||||||
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
||||||
};
|
|
||||||
var setDraft7Headers = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
||||||
response.setHeader(
|
|
||||||
"RateLimit",
|
|
||||||
`limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
var setDraft8Headers = (response, info, windowMs, name, key) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
const partitionKey = getPartitionKey(key);
|
|
||||||
const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
|
|
||||||
const header = `r=${info.remaining}; t=${resetSeconds}`;
|
|
||||||
response.append("RateLimit-Policy", `"${name}"; ${policy}`);
|
|
||||||
response.append("RateLimit", `"${name}"; ${header}`);
|
|
||||||
};
|
|
||||||
var setRetryAfterHeader = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
response.setHeader("Retry-After", resetSeconds.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/validations.ts
|
|
||||||
var import_node_net = require("net");
|
|
||||||
var ValidationError = class extends Error {
|
|
||||||
/**
|
|
||||||
* The code must be a string, in snake case and all capital, that starts with
|
|
||||||
* the substring `ERR_ERL_`.
|
|
||||||
*
|
|
||||||
* The message must be a string, starting with an uppercase character,
|
|
||||||
* describing the issue in detail.
|
|
||||||
*/
|
|
||||||
constructor(code, message) {
|
|
||||||
const url = `https://express-rate-limit.github.io/${code}/`;
|
|
||||||
super(`${message} See ${url} for more information.`);
|
|
||||||
this.name = this.constructor.name;
|
|
||||||
this.code = code;
|
|
||||||
this.help = url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var ChangeWarning = class extends ValidationError {
|
|
||||||
};
|
|
||||||
var usedStores = /* @__PURE__ */ new Set();
|
|
||||||
var singleCountKeys = /* @__PURE__ */ new WeakMap();
|
|
||||||
var validations = {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
enabled: {
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// Should be EnabledValidations type, but that's a circular reference
|
|
||||||
disable() {
|
|
||||||
for (const k of Object.keys(this.enabled))
|
|
||||||
this.enabled[k] = false;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks whether the IP address is valid, and that it does not have a port
|
|
||||||
* number in it.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
|
|
||||||
*
|
|
||||||
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
ip(ip) {
|
|
||||||
if (ip === void 0) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNDEFINED_IP_ADDRESS",
|
|
||||||
`An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!(0, import_node_net.isIP)(ip)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_INVALID_IP_ADDRESS",
|
|
||||||
`An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is not set to `true`.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
trustProxy(request) {
|
|
||||||
if (request.app.get("trust proxy") === true) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_PERMISSIVE_TRUST_PROXY",
|
|
||||||
`The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
|
|
||||||
* header is present.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
xForwardedForHeader(request) {
|
|
||||||
if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
|
|
||||||
`The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures totalHits value from store is a positive integer.
|
|
||||||
*
|
|
||||||
* @param hits {any} - The `totalHits` returned by the store.
|
|
||||||
*/
|
|
||||||
positiveHits(hits) {
|
|
||||||
if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_INVALID_HITS",
|
|
||||||
`The totalHits value returned from the store must be a positive integer, got ${hits}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
||||||
*/
|
|
||||||
unsharedStore(store) {
|
|
||||||
if (usedStores.has(store)) {
|
|
||||||
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_STORE_REUSE",
|
|
||||||
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
usedStores.add(store);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures a given key is incremented only once per request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param store {Store} - The store class.
|
|
||||||
* @param key {string} - The key used to store the client's hit count.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
singleCount(request, store, key) {
|
|
||||||
let storeKeys = singleCountKeys.get(request);
|
|
||||||
if (!storeKeys) {
|
|
||||||
storeKeys = /* @__PURE__ */ new Map();
|
|
||||||
singleCountKeys.set(request, storeKeys);
|
|
||||||
}
|
|
||||||
const storeKey = store.localKeys ? store : store.constructor.name;
|
|
||||||
let keys = storeKeys.get(storeKey);
|
|
||||||
if (!keys) {
|
|
||||||
keys = [];
|
|
||||||
storeKeys.set(storeKey, keys);
|
|
||||||
}
|
|
||||||
const prefixedKey = `${store.prefix ?? ""}${key}`;
|
|
||||||
if (keys.includes(prefixedKey)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_DOUBLE_COUNT",
|
|
||||||
`The hit count for ${key} was incremented more than once for a single request.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
keys.push(prefixedKey);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
|
|
||||||
* changing in the next major release.
|
|
||||||
*
|
|
||||||
* @param limit {number} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
limit(limit) {
|
|
||||||
if (limit === 0) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_MAX_ZERO",
|
|
||||||
`Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
|
|
||||||
* and will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
draftPolliHeaders(draft_polli_ratelimit_headers) {
|
|
||||||
if (draft_polli_ratelimit_headers) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
|
|
||||||
`The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the `onLimitReached` option is deprecated and
|
|
||||||
* will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onLimitReached(onLimitReached) {
|
|
||||||
if (onLimitReached) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
|
|
||||||
`The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user when an invalid/unsupported version of the draft spec is passed.
|
|
||||||
*
|
|
||||||
* @param version {any | undefined} - The version passed by the user.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersDraftVersion(version) {
|
|
||||||
if (typeof version !== "string" || !SUPPORTED_DRAFT_VERSIONS.includes(version)) {
|
|
||||||
const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",
|
|
||||||
`standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user when the selected headers option requires a reset time but
|
|
||||||
* the store does not provide one.
|
|
||||||
*
|
|
||||||
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersResetTime(resetTime) {
|
|
||||||
if (!resetTime) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_HEADERS_NO_RESET",
|
|
||||||
`standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks the options.validate setting to ensure that only recognized
|
|
||||||
* validations are enabled or disabled.
|
|
||||||
*
|
|
||||||
* If any unrecognized values are found, an error is logged that
|
|
||||||
* includes the list of supported vaidations.
|
|
||||||
*/
|
|
||||||
validationsConfig() {
|
|
||||||
const supportedValidations = Object.keys(this).filter(
|
|
||||||
(k) => !["enabled", "disable"].includes(k)
|
|
||||||
);
|
|
||||||
supportedValidations.push("default");
|
|
||||||
for (const key of Object.keys(this.enabled)) {
|
|
||||||
if (!supportedValidations.includes(key)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNKNOWN_VALIDATION",
|
|
||||||
`options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
|
|
||||||
", "
|
|
||||||
)}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks to see if the instance was created inside of a request handler,
|
|
||||||
* which would prevent it from working correctly, with the default memory
|
|
||||||
* store (or any other store with localKeys.)
|
|
||||||
*/
|
|
||||||
creationStack(store) {
|
|
||||||
const { stack } = new Error(
|
|
||||||
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
|
|
||||||
);
|
|
||||||
if (stack?.includes("Layer.handle [as handle_request]")) {
|
|
||||||
if (!store.localKeys) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
||||||
"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
||||||
`express-rate-limit instance should be created at app initialization, not when responding to a request.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var getValidations = (_enabled) => {
|
|
||||||
let enabled;
|
|
||||||
if (typeof _enabled === "boolean") {
|
|
||||||
enabled = {
|
|
||||||
default: _enabled
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
enabled = {
|
|
||||||
default: true,
|
|
||||||
..._enabled
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const wrappedValidations = {
|
|
||||||
enabled
|
|
||||||
};
|
|
||||||
for (const [name, validation] of Object.entries(validations)) {
|
|
||||||
if (typeof validation === "function")
|
|
||||||
wrappedValidations[name] = (...args) => {
|
|
||||||
if (!(enabled[name] ?? enabled.default)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
;
|
|
||||||
validation.apply(
|
|
||||||
wrappedValidations,
|
|
||||||
args
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof ChangeWarning)
|
|
||||||
console.warn(error);
|
|
||||||
else
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return wrappedValidations;
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/memory-store.ts
|
|
||||||
var MemoryStore = class {
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
||||||
* addresses or API keys).
|
|
||||||
*
|
|
||||||
* They are split into two to avoid having to iterate through the entire set to
|
|
||||||
* determine which ones need reset. Instead, `Client`s are moved from `previous`
|
|
||||||
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
|
|
||||||
* left in `previous`, i.e., those that have not made any recent requests, are
|
|
||||||
* known to be expired and can be deleted in bulk.
|
|
||||||
*/
|
|
||||||
this.previous = /* @__PURE__ */ new Map();
|
|
||||||
this.current = /* @__PURE__ */ new Map();
|
|
||||||
/**
|
|
||||||
* Confirmation that the keys incremented in once instance of MemoryStore
|
|
||||||
* cannot affect other instances.
|
|
||||||
*/
|
|
||||||
this.localKeys = true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method that initializes the store.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init(options) {
|
|
||||||
this.windowMs = options.windowMs;
|
|
||||||
if (this.interval)
|
|
||||||
clearInterval(this.interval);
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
this.clearExpired();
|
|
||||||
}, this.windowMs);
|
|
||||||
if (this.interval.unref)
|
|
||||||
this.interval.unref();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async get(key) {
|
|
||||||
return this.current.get(key) ?? this.previous.get(key);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async increment(key) {
|
|
||||||
const client = this.getClient(key);
|
|
||||||
const now = Date.now();
|
|
||||||
if (client.resetTime.getTime() <= now) {
|
|
||||||
this.resetClient(client, now);
|
|
||||||
}
|
|
||||||
client.totalHits++;
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async decrement(key) {
|
|
||||||
const client = this.getClient(key);
|
|
||||||
if (client.totalHits > 0)
|
|
||||||
client.totalHits--;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async resetKey(key) {
|
|
||||||
this.current.delete(key);
|
|
||||||
this.previous.delete(key);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async resetAll() {
|
|
||||||
this.current.clear();
|
|
||||||
this.previous.clear();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to stop the timer (if currently running) and prevent any memory
|
|
||||||
* leaks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
shutdown() {
|
|
||||||
clearInterval(this.interval);
|
|
||||||
void this.resetAll();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Recycles a client by setting its hit count to zero, and reset time to
|
|
||||||
* `windowMs` milliseconds from now.
|
|
||||||
*
|
|
||||||
* NOT to be confused with `#resetKey()`, which removes a client from both the
|
|
||||||
* `current` and `previous` maps.
|
|
||||||
*
|
|
||||||
* @param client {Client} - The client to recycle.
|
|
||||||
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
|
|
||||||
*
|
|
||||||
* @return {Client} - The modified client that was passed in, to allow for chaining.
|
|
||||||
*/
|
|
||||||
resetClient(client, now = Date.now()) {
|
|
||||||
client.totalHits = 0;
|
|
||||||
client.resetTime.setTime(now + this.windowMs);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Retrieves or creates a client, given a key. Also ensures that the client being
|
|
||||||
* returned is in the `current` map.
|
|
||||||
*
|
|
||||||
* @param key {string} - The key under which the client is (or is to be) stored.
|
|
||||||
*
|
|
||||||
* @returns {Client} - The requested client.
|
|
||||||
*/
|
|
||||||
getClient(key) {
|
|
||||||
if (this.current.has(key))
|
|
||||||
return this.current.get(key);
|
|
||||||
let client;
|
|
||||||
if (this.previous.has(key)) {
|
|
||||||
client = this.previous.get(key);
|
|
||||||
this.previous.delete(key);
|
|
||||||
} else {
|
|
||||||
client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
|
|
||||||
this.resetClient(client);
|
|
||||||
}
|
|
||||||
this.current.set(key, client);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Move current clients to previous, create a new map for current.
|
|
||||||
*
|
|
||||||
* This function is called every `windowMs`.
|
|
||||||
*/
|
|
||||||
clearExpired() {
|
|
||||||
this.previous = this.current;
|
|
||||||
this.current = /* @__PURE__ */ new Map();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/lib.ts
|
|
||||||
var isLegacyStore = (store) => (
|
|
||||||
// Check that `incr` exists but `increment` does not - store authors might want
|
|
||||||
// to keep both around for backwards compatibility.
|
|
||||||
typeof store.incr === "function" && typeof store.increment !== "function"
|
|
||||||
);
|
|
||||||
var promisifyStore = (passedStore) => {
|
|
||||||
if (!isLegacyStore(passedStore)) {
|
|
||||||
return passedStore;
|
|
||||||
}
|
|
||||||
const legacyStore = passedStore;
|
|
||||||
class PromisifiedStore {
|
|
||||||
async increment(key) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
legacyStore.incr(
|
|
||||||
key,
|
|
||||||
(error, totalHits, resetTime) => {
|
|
||||||
if (error)
|
|
||||||
reject(error);
|
|
||||||
resolve({ totalHits, resetTime });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async decrement(key) {
|
|
||||||
return legacyStore.decrement(key);
|
|
||||||
}
|
|
||||||
async resetKey(key) {
|
|
||||||
return legacyStore.resetKey(key);
|
|
||||||
}
|
|
||||||
/* istanbul ignore next */
|
|
||||||
async resetAll() {
|
|
||||||
if (typeof legacyStore.resetAll === "function")
|
|
||||||
return legacyStore.resetAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new PromisifiedStore();
|
|
||||||
};
|
|
||||||
var getOptionsFromConfig = (config) => {
|
|
||||||
const { validations: validations2, ...directlyPassableEntries } = config;
|
|
||||||
return {
|
|
||||||
...directlyPassableEntries,
|
|
||||||
validate: validations2.enabled
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var omitUndefinedOptions = (passedOptions) => {
|
|
||||||
const omittedOptions = {};
|
|
||||||
for (const k of Object.keys(passedOptions)) {
|
|
||||||
const key = k;
|
|
||||||
if (passedOptions[key] !== void 0) {
|
|
||||||
omittedOptions[key] = passedOptions[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return omittedOptions;
|
|
||||||
};
|
|
||||||
var parseOptions = (passedOptions) => {
|
|
||||||
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
||||||
const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
|
|
||||||
validations2.validationsConfig();
|
|
||||||
validations2.draftPolliHeaders(
|
|
||||||
// @ts-expect-error see the note above.
|
|
||||||
notUndefinedOptions.draft_polli_ratelimit_headers
|
|
||||||
);
|
|
||||||
validations2.onLimitReached(notUndefinedOptions.onLimitReached);
|
|
||||||
let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
|
|
||||||
if (standardHeaders === true)
|
|
||||||
standardHeaders = "draft-6";
|
|
||||||
const config = {
|
|
||||||
windowMs: 60 * 1e3,
|
|
||||||
limit: passedOptions.max ?? 5,
|
|
||||||
// `max` is deprecated, but support it anyways.
|
|
||||||
message: "Too many requests, please try again later.",
|
|
||||||
statusCode: 429,
|
|
||||||
legacyHeaders: passedOptions.headers ?? true,
|
|
||||||
identifier(request, _response) {
|
|
||||||
let duration = "";
|
|
||||||
const property = config.requestPropertyName;
|
|
||||||
const { limit } = request[property];
|
|
||||||
const seconds = config.windowMs / 1e3;
|
|
||||||
const minutes = config.windowMs / (1e3 * 60);
|
|
||||||
const hours = config.windowMs / (1e3 * 60 * 60);
|
|
||||||
const days = config.windowMs / (1e3 * 60 * 60 * 24);
|
|
||||||
if (seconds < 60)
|
|
||||||
duration = `${seconds}sec`;
|
|
||||||
else if (minutes < 60)
|
|
||||||
duration = `${minutes}min`;
|
|
||||||
else if (hours < 24)
|
|
||||||
duration = `${hours}hr${hours > 1 ? "s" : ""}`;
|
|
||||||
else
|
|
||||||
duration = `${days}day${days > 1 ? "s" : ""}`;
|
|
||||||
return `${limit}-in-${duration}`;
|
|
||||||
},
|
|
||||||
requestPropertyName: "rateLimit",
|
|
||||||
skipFailedRequests: false,
|
|
||||||
skipSuccessfulRequests: false,
|
|
||||||
requestWasSuccessful: (_request, response) => response.statusCode < 400,
|
|
||||||
skip: (_request, _response) => false,
|
|
||||||
keyGenerator(request, _response) {
|
|
||||||
validations2.ip(request.ip);
|
|
||||||
validations2.trustProxy(request);
|
|
||||||
validations2.xForwardedForHeader(request);
|
|
||||||
return request.ip;
|
|
||||||
},
|
|
||||||
async handler(request, response, _next, _optionsUsed) {
|
|
||||||
response.status(config.statusCode);
|
|
||||||
const message = typeof config.message === "function" ? await config.message(
|
|
||||||
request,
|
|
||||||
response
|
|
||||||
) : config.message;
|
|
||||||
if (!response.writableEnded) {
|
|
||||||
response.send(message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
passOnStoreError: false,
|
|
||||||
// Allow the default options to be overriden by the passed options.
|
|
||||||
...notUndefinedOptions,
|
|
||||||
// `standardHeaders` is resolved into a draft version above, use that.
|
|
||||||
standardHeaders,
|
|
||||||
// Note that this field is declared after the user's options are spread in,
|
|
||||||
// so that this field doesn't get overriden with an un-promisified store!
|
|
||||||
store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
|
|
||||||
// Print an error to the console if a few known misconfigurations are detected.
|
|
||||||
validations: validations2
|
|
||||||
};
|
|
||||||
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
|
|
||||||
throw new TypeError(
|
|
||||||
"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
||||||
try {
|
|
||||||
await Promise.resolve(fn(request, response, next)).catch(next);
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var rateLimit = (passedOptions) => {
|
|
||||||
const config = parseOptions(passedOptions ?? {});
|
|
||||||
const options = getOptionsFromConfig(config);
|
|
||||||
config.validations.creationStack(config.store);
|
|
||||||
config.validations.unsharedStore(config.store);
|
|
||||||
if (typeof config.store.init === "function")
|
|
||||||
config.store.init(options);
|
|
||||||
const middleware = handleAsyncErrors(
|
|
||||||
async (request, response, next) => {
|
|
||||||
const skip = await config.skip(request, response);
|
|
||||||
if (skip) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const augmentedRequest = request;
|
|
||||||
const key = await config.keyGenerator(request, response);
|
|
||||||
let totalHits = 0;
|
|
||||||
let resetTime;
|
|
||||||
try {
|
|
||||||
const incrementResult = await config.store.increment(key);
|
|
||||||
totalHits = incrementResult.totalHits;
|
|
||||||
resetTime = incrementResult.resetTime;
|
|
||||||
} catch (error) {
|
|
||||||
if (config.passOnStoreError) {
|
|
||||||
console.error(
|
|
||||||
"express-rate-limit: error from store, allowing request without rate-limiting.",
|
|
||||||
error
|
|
||||||
);
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
config.validations.positiveHits(totalHits);
|
|
||||||
config.validations.singleCount(request, config.store, key);
|
|
||||||
const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
|
|
||||||
const limit = await retrieveLimit;
|
|
||||||
config.validations.limit(limit);
|
|
||||||
const info = {
|
|
||||||
limit,
|
|
||||||
used: totalHits,
|
|
||||||
remaining: Math.max(limit - totalHits, 0),
|
|
||||||
resetTime
|
|
||||||
};
|
|
||||||
Object.defineProperty(info, "current", {
|
|
||||||
configurable: false,
|
|
||||||
enumerable: false,
|
|
||||||
value: totalHits
|
|
||||||
});
|
|
||||||
augmentedRequest[config.requestPropertyName] = info;
|
|
||||||
if (config.legacyHeaders && !response.headersSent) {
|
|
||||||
setLegacyHeaders(response, info);
|
|
||||||
}
|
|
||||||
if (config.standardHeaders && !response.headersSent) {
|
|
||||||
switch (config.standardHeaders) {
|
|
||||||
case "draft-6": {
|
|
||||||
setDraft6Headers(response, info, config.windowMs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "draft-7": {
|
|
||||||
config.validations.headersResetTime(info.resetTime);
|
|
||||||
setDraft7Headers(response, info, config.windowMs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "draft-8": {
|
|
||||||
const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
|
|
||||||
const name = await retrieveName;
|
|
||||||
config.validations.headersResetTime(info.resetTime);
|
|
||||||
setDraft8Headers(response, info, config.windowMs, name, key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
config.validations.headersDraftVersion(config.standardHeaders);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (config.skipFailedRequests || config.skipSuccessfulRequests) {
|
|
||||||
let decremented = false;
|
|
||||||
const decrementKey = async () => {
|
|
||||||
if (!decremented) {
|
|
||||||
await config.store.decrement(key);
|
|
||||||
decremented = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (config.skipFailedRequests) {
|
|
||||||
response.on("finish", async () => {
|
|
||||||
if (!await config.requestWasSuccessful(request, response))
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
response.on("close", async () => {
|
|
||||||
if (!response.writableEnded)
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
response.on("error", async () => {
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.skipSuccessfulRequests) {
|
|
||||||
response.on("finish", async () => {
|
|
||||||
if (await config.requestWasSuccessful(request, response))
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.validations.disable();
|
|
||||||
if (totalHits > limit) {
|
|
||||||
if (config.legacyHeaders || config.standardHeaders) {
|
|
||||||
setRetryAfterHeader(response, info, config.windowMs);
|
|
||||||
}
|
|
||||||
config.handler(request, response, next, options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const getThrowFn = () => {
|
|
||||||
throw new Error("The current store does not support the get/getKey method");
|
|
||||||
};
|
|
||||||
middleware.resetKey = config.store.resetKey.bind(config.store);
|
|
||||||
middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
|
|
||||||
return middleware;
|
|
||||||
};
|
|
||||||
var lib_default = rateLimit;
|
|
||||||
// Annotate the CommonJS export names for ESM import in node:
|
|
||||||
0 && (module.exports = {
|
|
||||||
MemoryStore,
|
|
||||||
rateLimit
|
|
||||||
});
|
|
||||||
module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;
|
|
||||||
584
node_modules/express-rate-limit/dist/index.d.cts
generated
vendored
@ -1,584 +0,0 @@
|
|||||||
// Generated by dts-bundle-generator v8.0.1
|
|
||||||
|
|
||||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
||||||
|
|
||||||
declare const validations: {
|
|
||||||
enabled: {
|
|
||||||
[key: string]: boolean;
|
|
||||||
};
|
|
||||||
disable(): void;
|
|
||||||
/**
|
|
||||||
* Checks whether the IP address is valid, and that it does not have a port
|
|
||||||
* number in it.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
|
|
||||||
*
|
|
||||||
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
ip(ip: string | undefined): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is not set to `true`.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
trustProxy(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
|
|
||||||
* header is present.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
xForwardedForHeader(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Ensures totalHits value from store is a positive integer.
|
|
||||||
*
|
|
||||||
* @param hits {any} - The `totalHits` returned by the store.
|
|
||||||
*/
|
|
||||||
positiveHits(hits: any): void;
|
|
||||||
/**
|
|
||||||
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
||||||
*/
|
|
||||||
unsharedStore(store: Store): void;
|
|
||||||
/**
|
|
||||||
* Ensures a given key is incremented only once per request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param store {Store} - The store class.
|
|
||||||
* @param key {string} - The key used to store the client's hit count.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
singleCount(request: Request, store: Store, key: string): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
|
|
||||||
* changing in the next major release.
|
|
||||||
*
|
|
||||||
* @param limit {number} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
limit(limit: number): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
|
|
||||||
* and will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `onLimitReached` option is deprecated and
|
|
||||||
* will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onLimitReached(onLimitReached?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when an invalid/unsupported version of the draft spec is passed.
|
|
||||||
*
|
|
||||||
* @param version {any | undefined} - The version passed by the user.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersDraftVersion(version?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when the selected headers option requires a reset time but
|
|
||||||
* the store does not provide one.
|
|
||||||
*
|
|
||||||
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersResetTime(resetTime?: Date): void;
|
|
||||||
/**
|
|
||||||
* Checks the options.validate setting to ensure that only recognized
|
|
||||||
* validations are enabled or disabled.
|
|
||||||
*
|
|
||||||
* If any unrecognized values are found, an error is logged that
|
|
||||||
* includes the list of supported vaidations.
|
|
||||||
*/
|
|
||||||
validationsConfig(): void;
|
|
||||||
/**
|
|
||||||
* Checks to see if the instance was created inside of a request handler,
|
|
||||||
* which would prevent it from working correctly, with the default memory
|
|
||||||
* store (or any other store with localKeys.)
|
|
||||||
*/
|
|
||||||
creationStack(store: Store): void;
|
|
||||||
};
|
|
||||||
export type Validations = typeof validations;
|
|
||||||
declare const SUPPORTED_DRAFT_VERSIONS: string[];
|
|
||||||
/**
|
|
||||||
* Callback that fires when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @param error {Error | undefined} - The error that occurred, if any.
|
|
||||||
* @param totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @param resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to generate/retrieve a value based on the
|
|
||||||
* incoming request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
*
|
|
||||||
* @returns {T} - The value needed.
|
|
||||||
*/
|
|
||||||
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Event callback that is triggered on a client's first request that exceeds the limit
|
|
||||||
* but not for subsequent requests. May be used for logging, etc. Should *not*
|
|
||||||
* send a response.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Data returned from the `Store` when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @property totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @property resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type ClientRateLimitInfo = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
export type IncrementResponse = ClientRateLimitInfo;
|
|
||||||
/**
|
|
||||||
* A modified Express request handler with the rate limit functions.
|
|
||||||
*/
|
|
||||||
export type RateLimitRequestHandler = RequestHandler & {
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - Implement the `Store` interface instead.
|
|
||||||
*/
|
|
||||||
export type LegacyStore = {
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
|
|
||||||
*/
|
|
||||||
incr: (key: string, callback: IncrementCallback) => void;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => void;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*/
|
|
||||||
export type Store = {
|
|
||||||
/**
|
|
||||||
* Method that initializes the store, and has access to the options passed to
|
|
||||||
* the middleware too.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init?: (options: Options) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to shutdown the store, stop timers, and release all resources.
|
|
||||||
*/
|
|
||||||
shutdown?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Flag to indicate that keys incremented in one instance of this store can
|
|
||||||
* not affect other instances. Typically false if a database is used, true for
|
|
||||||
* MemoryStore.
|
|
||||||
*
|
|
||||||
* Used to help detect double-counting misconfigurations.
|
|
||||||
*/
|
|
||||||
localKeys?: boolean;
|
|
||||||
/**
|
|
||||||
* Optional value that the store prepends to keys
|
|
||||||
*
|
|
||||||
* Used by the double-count check to avoid false-positives when a key is counted twice, but with different prefixes
|
|
||||||
*/
|
|
||||||
prefix?: string;
|
|
||||||
};
|
|
||||||
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
|
|
||||||
/**
|
|
||||||
* Validate configuration object for enabling or disabling specific validations.
|
|
||||||
*
|
|
||||||
* The keys must also be keys in the validations object, except `enable`, `disable`,
|
|
||||||
* and `default`.
|
|
||||||
*/
|
|
||||||
export type EnabledValidations = {
|
|
||||||
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The configuration options for the rate limiter.
|
|
||||||
*/
|
|
||||||
export type Options = {
|
|
||||||
/**
|
|
||||||
* How long we should remember the requests.
|
|
||||||
*
|
|
||||||
* Defaults to `60000` ms (= 1 minute).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* Defaults to `5`.
|
|
||||||
*/
|
|
||||||
limit: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* The response body to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `'Too many requests, please try again later.'`
|
|
||||||
*/
|
|
||||||
message: any | ValueDeterminingMiddleware<any>;
|
|
||||||
/**
|
|
||||||
* The HTTP status code to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
|
|
||||||
*/
|
|
||||||
statusCode: number;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* Defaults to `true` (for backward compatibility).
|
|
||||||
*/
|
|
||||||
legacyHeaders: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
|
|
||||||
*
|
|
||||||
* Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
||||||
*/
|
|
||||||
standardHeaders: boolean | DraftHeadersVersion;
|
|
||||||
/**
|
|
||||||
* The name used to identify the quota policy in the `RateLimit` headers as per
|
|
||||||
* the 8th draft of the IETF specification.
|
|
||||||
*
|
|
||||||
* Defaults to `{limit}-in-{window}`.
|
|
||||||
*/
|
|
||||||
identifier: string | ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* The name of the property on the request object to store the rate limit info.
|
|
||||||
*
|
|
||||||
* Defaults to `rateLimit`.
|
|
||||||
*/
|
|
||||||
requestPropertyName: string;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a 4XX
|
|
||||||
* or 5XX status.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipFailedRequests: boolean;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a
|
|
||||||
* status code less than 400.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipSuccessfulRequests: boolean;
|
|
||||||
/**
|
|
||||||
* Method to generate custom identifiers for clients.
|
|
||||||
*
|
|
||||||
* By default, the client's IP address is used.
|
|
||||||
*/
|
|
||||||
keyGenerator: ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* By default, sends back the `statusCode` and `message` set via the options.
|
|
||||||
*/
|
|
||||||
handler: RateLimitExceededEventHandler;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to determine whether or not this request
|
|
||||||
* counts towards a client's quota.
|
|
||||||
*
|
|
||||||
* By default, skips no requests.
|
|
||||||
*/
|
|
||||||
skip: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* Method to determine whether or not the request counts as 'succesful'. Used
|
|
||||||
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
||||||
*
|
|
||||||
* By default, requests with a response status code less than 400 are considered
|
|
||||||
* successful.
|
|
||||||
*/
|
|
||||||
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* The `Store` to use to store the hit count for each client.
|
|
||||||
*
|
|
||||||
* By default, the built-in `MemoryStore` will be used.
|
|
||||||
*/
|
|
||||||
store: Store | LegacyStore;
|
|
||||||
/**
|
|
||||||
* The list of validation checks that should run.
|
|
||||||
*/
|
|
||||||
validate: boolean | EnabledValidations;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
|
|
||||||
*/
|
|
||||||
headers?: boolean;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
|
|
||||||
* be removed from the library in the foreseeable future.
|
|
||||||
*/
|
|
||||||
max?: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* If the Store generates an error, allow the request to pass.
|
|
||||||
*/
|
|
||||||
passOnStoreError: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The extended request object that includes information about the client's
|
|
||||||
* rate limit.
|
|
||||||
*/
|
|
||||||
export type AugmentedRequest = Request & {
|
|
||||||
[key: string]: RateLimitInfo;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The rate limit related information for each client included in the
|
|
||||||
* Express request object.
|
|
||||||
*/
|
|
||||||
export type RateLimitInfo = {
|
|
||||||
limit: number;
|
|
||||||
used: number;
|
|
||||||
remaining: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Create an instance of IP rate-limiting middleware for Express.
|
|
||||||
*
|
|
||||||
* @param passedOptions {Options} - Options to configure the rate limiter.
|
|
||||||
*
|
|
||||||
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
|
|
||||||
/**
|
|
||||||
* The record that stores information about a client - namely, how many times
|
|
||||||
* they have hit the endpoint, and when their hit count resets.
|
|
||||||
*
|
|
||||||
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
|
|
||||||
*/
|
|
||||||
export type Client = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* A `Store` that stores the hit count for each client in memory.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare class MemoryStore implements Store {
|
|
||||||
/**
|
|
||||||
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
||||||
* addresses or API keys).
|
|
||||||
*
|
|
||||||
* They are split into two to avoid having to iterate through the entire set to
|
|
||||||
* determine which ones need reset. Instead, `Client`s are moved from `previous`
|
|
||||||
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
|
|
||||||
* left in `previous`, i.e., those that have not made any recent requests, are
|
|
||||||
* known to be expired and can be deleted in bulk.
|
|
||||||
*/
|
|
||||||
previous: Map<string, Client>;
|
|
||||||
current: Map<string, Client>;
|
|
||||||
/**
|
|
||||||
* A reference to the active timer.
|
|
||||||
*/
|
|
||||||
interval?: NodeJS.Timeout;
|
|
||||||
/**
|
|
||||||
* Confirmation that the keys incremented in once instance of MemoryStore
|
|
||||||
* cannot affect other instances.
|
|
||||||
*/
|
|
||||||
localKeys: boolean;
|
|
||||||
/**
|
|
||||||
* Method that initializes the store.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init(options: Options): void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
get(key: string): Promise<ClientRateLimitInfo | undefined>;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
increment(key: string): Promise<ClientRateLimitInfo>;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
decrement(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetKey(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetAll(): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to stop the timer (if currently running) and prevent any memory
|
|
||||||
* leaks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
shutdown(): void;
|
|
||||||
/**
|
|
||||||
* Recycles a client by setting its hit count to zero, and reset time to
|
|
||||||
* `windowMs` milliseconds from now.
|
|
||||||
*
|
|
||||||
* NOT to be confused with `#resetKey()`, which removes a client from both the
|
|
||||||
* `current` and `previous` maps.
|
|
||||||
*
|
|
||||||
* @param client {Client} - The client to recycle.
|
|
||||||
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
|
|
||||||
*
|
|
||||||
* @return {Client} - The modified client that was passed in, to allow for chaining.
|
|
||||||
*/
|
|
||||||
private resetClient;
|
|
||||||
/**
|
|
||||||
* Retrieves or creates a client, given a key. Also ensures that the client being
|
|
||||||
* returned is in the `current` map.
|
|
||||||
*
|
|
||||||
* @param key {string} - The key under which the client is (or is to be) stored.
|
|
||||||
*
|
|
||||||
* @returns {Client} - The requested client.
|
|
||||||
*/
|
|
||||||
private getClient;
|
|
||||||
/**
|
|
||||||
* Move current clients to previous, create a new map for current.
|
|
||||||
*
|
|
||||||
* This function is called every `windowMs`.
|
|
||||||
*/
|
|
||||||
private clearExpired;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
rateLimit as default,
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
|
||||||
584
node_modules/express-rate-limit/dist/index.d.mts
generated
vendored
@ -1,584 +0,0 @@
|
|||||||
// Generated by dts-bundle-generator v8.0.1
|
|
||||||
|
|
||||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
||||||
|
|
||||||
declare const validations: {
|
|
||||||
enabled: {
|
|
||||||
[key: string]: boolean;
|
|
||||||
};
|
|
||||||
disable(): void;
|
|
||||||
/**
|
|
||||||
* Checks whether the IP address is valid, and that it does not have a port
|
|
||||||
* number in it.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
|
|
||||||
*
|
|
||||||
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
ip(ip: string | undefined): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is not set to `true`.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
trustProxy(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
|
|
||||||
* header is present.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
xForwardedForHeader(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Ensures totalHits value from store is a positive integer.
|
|
||||||
*
|
|
||||||
* @param hits {any} - The `totalHits` returned by the store.
|
|
||||||
*/
|
|
||||||
positiveHits(hits: any): void;
|
|
||||||
/**
|
|
||||||
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
||||||
*/
|
|
||||||
unsharedStore(store: Store): void;
|
|
||||||
/**
|
|
||||||
* Ensures a given key is incremented only once per request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param store {Store} - The store class.
|
|
||||||
* @param key {string} - The key used to store the client's hit count.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
singleCount(request: Request, store: Store, key: string): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
|
|
||||||
* changing in the next major release.
|
|
||||||
*
|
|
||||||
* @param limit {number} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
limit(limit: number): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
|
|
||||||
* and will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `onLimitReached` option is deprecated and
|
|
||||||
* will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onLimitReached(onLimitReached?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when an invalid/unsupported version of the draft spec is passed.
|
|
||||||
*
|
|
||||||
* @param version {any | undefined} - The version passed by the user.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersDraftVersion(version?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when the selected headers option requires a reset time but
|
|
||||||
* the store does not provide one.
|
|
||||||
*
|
|
||||||
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersResetTime(resetTime?: Date): void;
|
|
||||||
/**
|
|
||||||
* Checks the options.validate setting to ensure that only recognized
|
|
||||||
* validations are enabled or disabled.
|
|
||||||
*
|
|
||||||
* If any unrecognized values are found, an error is logged that
|
|
||||||
* includes the list of supported vaidations.
|
|
||||||
*/
|
|
||||||
validationsConfig(): void;
|
|
||||||
/**
|
|
||||||
* Checks to see if the instance was created inside of a request handler,
|
|
||||||
* which would prevent it from working correctly, with the default memory
|
|
||||||
* store (or any other store with localKeys.)
|
|
||||||
*/
|
|
||||||
creationStack(store: Store): void;
|
|
||||||
};
|
|
||||||
export type Validations = typeof validations;
|
|
||||||
declare const SUPPORTED_DRAFT_VERSIONS: string[];
|
|
||||||
/**
|
|
||||||
* Callback that fires when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @param error {Error | undefined} - The error that occurred, if any.
|
|
||||||
* @param totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @param resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to generate/retrieve a value based on the
|
|
||||||
* incoming request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
*
|
|
||||||
* @returns {T} - The value needed.
|
|
||||||
*/
|
|
||||||
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Event callback that is triggered on a client's first request that exceeds the limit
|
|
||||||
* but not for subsequent requests. May be used for logging, etc. Should *not*
|
|
||||||
* send a response.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Data returned from the `Store` when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @property totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @property resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type ClientRateLimitInfo = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
export type IncrementResponse = ClientRateLimitInfo;
|
|
||||||
/**
|
|
||||||
* A modified Express request handler with the rate limit functions.
|
|
||||||
*/
|
|
||||||
export type RateLimitRequestHandler = RequestHandler & {
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - Implement the `Store` interface instead.
|
|
||||||
*/
|
|
||||||
export type LegacyStore = {
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
|
|
||||||
*/
|
|
||||||
incr: (key: string, callback: IncrementCallback) => void;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => void;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*/
|
|
||||||
export type Store = {
|
|
||||||
/**
|
|
||||||
* Method that initializes the store, and has access to the options passed to
|
|
||||||
* the middleware too.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init?: (options: Options) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to shutdown the store, stop timers, and release all resources.
|
|
||||||
*/
|
|
||||||
shutdown?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Flag to indicate that keys incremented in one instance of this store can
|
|
||||||
* not affect other instances. Typically false if a database is used, true for
|
|
||||||
* MemoryStore.
|
|
||||||
*
|
|
||||||
* Used to help detect double-counting misconfigurations.
|
|
||||||
*/
|
|
||||||
localKeys?: boolean;
|
|
||||||
/**
|
|
||||||
* Optional value that the store prepends to keys
|
|
||||||
*
|
|
||||||
* Used by the double-count check to avoid false-positives when a key is counted twice, but with different prefixes
|
|
||||||
*/
|
|
||||||
prefix?: string;
|
|
||||||
};
|
|
||||||
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
|
|
||||||
/**
|
|
||||||
* Validate configuration object for enabling or disabling specific validations.
|
|
||||||
*
|
|
||||||
* The keys must also be keys in the validations object, except `enable`, `disable`,
|
|
||||||
* and `default`.
|
|
||||||
*/
|
|
||||||
export type EnabledValidations = {
|
|
||||||
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The configuration options for the rate limiter.
|
|
||||||
*/
|
|
||||||
export type Options = {
|
|
||||||
/**
|
|
||||||
* How long we should remember the requests.
|
|
||||||
*
|
|
||||||
* Defaults to `60000` ms (= 1 minute).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* Defaults to `5`.
|
|
||||||
*/
|
|
||||||
limit: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* The response body to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `'Too many requests, please try again later.'`
|
|
||||||
*/
|
|
||||||
message: any | ValueDeterminingMiddleware<any>;
|
|
||||||
/**
|
|
||||||
* The HTTP status code to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
|
|
||||||
*/
|
|
||||||
statusCode: number;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* Defaults to `true` (for backward compatibility).
|
|
||||||
*/
|
|
||||||
legacyHeaders: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
|
|
||||||
*
|
|
||||||
* Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
||||||
*/
|
|
||||||
standardHeaders: boolean | DraftHeadersVersion;
|
|
||||||
/**
|
|
||||||
* The name used to identify the quota policy in the `RateLimit` headers as per
|
|
||||||
* the 8th draft of the IETF specification.
|
|
||||||
*
|
|
||||||
* Defaults to `{limit}-in-{window}`.
|
|
||||||
*/
|
|
||||||
identifier: string | ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* The name of the property on the request object to store the rate limit info.
|
|
||||||
*
|
|
||||||
* Defaults to `rateLimit`.
|
|
||||||
*/
|
|
||||||
requestPropertyName: string;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a 4XX
|
|
||||||
* or 5XX status.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipFailedRequests: boolean;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a
|
|
||||||
* status code less than 400.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipSuccessfulRequests: boolean;
|
|
||||||
/**
|
|
||||||
* Method to generate custom identifiers for clients.
|
|
||||||
*
|
|
||||||
* By default, the client's IP address is used.
|
|
||||||
*/
|
|
||||||
keyGenerator: ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* By default, sends back the `statusCode` and `message` set via the options.
|
|
||||||
*/
|
|
||||||
handler: RateLimitExceededEventHandler;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to determine whether or not this request
|
|
||||||
* counts towards a client's quota.
|
|
||||||
*
|
|
||||||
* By default, skips no requests.
|
|
||||||
*/
|
|
||||||
skip: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* Method to determine whether or not the request counts as 'succesful'. Used
|
|
||||||
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
||||||
*
|
|
||||||
* By default, requests with a response status code less than 400 are considered
|
|
||||||
* successful.
|
|
||||||
*/
|
|
||||||
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* The `Store` to use to store the hit count for each client.
|
|
||||||
*
|
|
||||||
* By default, the built-in `MemoryStore` will be used.
|
|
||||||
*/
|
|
||||||
store: Store | LegacyStore;
|
|
||||||
/**
|
|
||||||
* The list of validation checks that should run.
|
|
||||||
*/
|
|
||||||
validate: boolean | EnabledValidations;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
|
|
||||||
*/
|
|
||||||
headers?: boolean;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
|
|
||||||
* be removed from the library in the foreseeable future.
|
|
||||||
*/
|
|
||||||
max?: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* If the Store generates an error, allow the request to pass.
|
|
||||||
*/
|
|
||||||
passOnStoreError: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The extended request object that includes information about the client's
|
|
||||||
* rate limit.
|
|
||||||
*/
|
|
||||||
export type AugmentedRequest = Request & {
|
|
||||||
[key: string]: RateLimitInfo;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The rate limit related information for each client included in the
|
|
||||||
* Express request object.
|
|
||||||
*/
|
|
||||||
export type RateLimitInfo = {
|
|
||||||
limit: number;
|
|
||||||
used: number;
|
|
||||||
remaining: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Create an instance of IP rate-limiting middleware for Express.
|
|
||||||
*
|
|
||||||
* @param passedOptions {Options} - Options to configure the rate limiter.
|
|
||||||
*
|
|
||||||
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
|
|
||||||
/**
|
|
||||||
* The record that stores information about a client - namely, how many times
|
|
||||||
* they have hit the endpoint, and when their hit count resets.
|
|
||||||
*
|
|
||||||
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
|
|
||||||
*/
|
|
||||||
export type Client = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* A `Store` that stores the hit count for each client in memory.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare class MemoryStore implements Store {
|
|
||||||
/**
|
|
||||||
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
||||||
* addresses or API keys).
|
|
||||||
*
|
|
||||||
* They are split into two to avoid having to iterate through the entire set to
|
|
||||||
* determine which ones need reset. Instead, `Client`s are moved from `previous`
|
|
||||||
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
|
|
||||||
* left in `previous`, i.e., those that have not made any recent requests, are
|
|
||||||
* known to be expired and can be deleted in bulk.
|
|
||||||
*/
|
|
||||||
previous: Map<string, Client>;
|
|
||||||
current: Map<string, Client>;
|
|
||||||
/**
|
|
||||||
* A reference to the active timer.
|
|
||||||
*/
|
|
||||||
interval?: NodeJS.Timeout;
|
|
||||||
/**
|
|
||||||
* Confirmation that the keys incremented in once instance of MemoryStore
|
|
||||||
* cannot affect other instances.
|
|
||||||
*/
|
|
||||||
localKeys: boolean;
|
|
||||||
/**
|
|
||||||
* Method that initializes the store.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init(options: Options): void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
get(key: string): Promise<ClientRateLimitInfo | undefined>;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
increment(key: string): Promise<ClientRateLimitInfo>;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
decrement(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetKey(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetAll(): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to stop the timer (if currently running) and prevent any memory
|
|
||||||
* leaks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
shutdown(): void;
|
|
||||||
/**
|
|
||||||
* Recycles a client by setting its hit count to zero, and reset time to
|
|
||||||
* `windowMs` milliseconds from now.
|
|
||||||
*
|
|
||||||
* NOT to be confused with `#resetKey()`, which removes a client from both the
|
|
||||||
* `current` and `previous` maps.
|
|
||||||
*
|
|
||||||
* @param client {Client} - The client to recycle.
|
|
||||||
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
|
|
||||||
*
|
|
||||||
* @return {Client} - The modified client that was passed in, to allow for chaining.
|
|
||||||
*/
|
|
||||||
private resetClient;
|
|
||||||
/**
|
|
||||||
* Retrieves or creates a client, given a key. Also ensures that the client being
|
|
||||||
* returned is in the `current` map.
|
|
||||||
*
|
|
||||||
* @param key {string} - The key under which the client is (or is to be) stored.
|
|
||||||
*
|
|
||||||
* @returns {Client} - The requested client.
|
|
||||||
*/
|
|
||||||
private getClient;
|
|
||||||
/**
|
|
||||||
* Move current clients to previous, create a new map for current.
|
|
||||||
*
|
|
||||||
* This function is called every `windowMs`.
|
|
||||||
*/
|
|
||||||
private clearExpired;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
rateLimit as default,
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
|
||||||
584
node_modules/express-rate-limit/dist/index.d.ts
generated
vendored
@ -1,584 +0,0 @@
|
|||||||
// Generated by dts-bundle-generator v8.0.1
|
|
||||||
|
|
||||||
import { NextFunction, Request, RequestHandler, Response } from 'express';
|
|
||||||
|
|
||||||
declare const validations: {
|
|
||||||
enabled: {
|
|
||||||
[key: string]: boolean;
|
|
||||||
};
|
|
||||||
disable(): void;
|
|
||||||
/**
|
|
||||||
* Checks whether the IP address is valid, and that it does not have a port
|
|
||||||
* number in it.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
|
|
||||||
*
|
|
||||||
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
ip(ip: string | undefined): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is not set to `true`.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
trustProxy(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
|
|
||||||
* header is present.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
xForwardedForHeader(request: Request): void;
|
|
||||||
/**
|
|
||||||
* Ensures totalHits value from store is a positive integer.
|
|
||||||
*
|
|
||||||
* @param hits {any} - The `totalHits` returned by the store.
|
|
||||||
*/
|
|
||||||
positiveHits(hits: any): void;
|
|
||||||
/**
|
|
||||||
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
||||||
*/
|
|
||||||
unsharedStore(store: Store): void;
|
|
||||||
/**
|
|
||||||
* Ensures a given key is incremented only once per request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param store {Store} - The store class.
|
|
||||||
* @param key {string} - The key used to store the client's hit count.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
singleCount(request: Request, store: Store, key: string): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
|
|
||||||
* changing in the next major release.
|
|
||||||
*
|
|
||||||
* @param limit {number} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
limit(limit: number): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
|
|
||||||
* and will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user that the `onLimitReached` option is deprecated and
|
|
||||||
* will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onLimitReached(onLimitReached?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when an invalid/unsupported version of the draft spec is passed.
|
|
||||||
*
|
|
||||||
* @param version {any | undefined} - The version passed by the user.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersDraftVersion(version?: any): void;
|
|
||||||
/**
|
|
||||||
* Warns the user when the selected headers option requires a reset time but
|
|
||||||
* the store does not provide one.
|
|
||||||
*
|
|
||||||
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersResetTime(resetTime?: Date): void;
|
|
||||||
/**
|
|
||||||
* Checks the options.validate setting to ensure that only recognized
|
|
||||||
* validations are enabled or disabled.
|
|
||||||
*
|
|
||||||
* If any unrecognized values are found, an error is logged that
|
|
||||||
* includes the list of supported vaidations.
|
|
||||||
*/
|
|
||||||
validationsConfig(): void;
|
|
||||||
/**
|
|
||||||
* Checks to see if the instance was created inside of a request handler,
|
|
||||||
* which would prevent it from working correctly, with the default memory
|
|
||||||
* store (or any other store with localKeys.)
|
|
||||||
*/
|
|
||||||
creationStack(store: Store): void;
|
|
||||||
};
|
|
||||||
export type Validations = typeof validations;
|
|
||||||
declare const SUPPORTED_DRAFT_VERSIONS: string[];
|
|
||||||
/**
|
|
||||||
* Callback that fires when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @param error {Error | undefined} - The error that occurred, if any.
|
|
||||||
* @param totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @param resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to generate/retrieve a value based on the
|
|
||||||
* incoming request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
*
|
|
||||||
* @returns {T} - The value needed.
|
|
||||||
*/
|
|
||||||
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Event callback that is triggered on a client's first request that exceeds the limit
|
|
||||||
* but not for subsequent requests. May be used for logging, etc. Should *not*
|
|
||||||
* send a response.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param response {Response} - The Express response object.
|
|
||||||
* @param optionsUsed {Options} - The options used to set up the middleware.
|
|
||||||
*/
|
|
||||||
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
|
|
||||||
/**
|
|
||||||
* Data returned from the `Store` when a client's hit counter is incremented.
|
|
||||||
*
|
|
||||||
* @property totalHits {number} - The number of hits for that client so far.
|
|
||||||
* @property resetTime {Date | undefined} - The time when the counter resets.
|
|
||||||
*/
|
|
||||||
export type ClientRateLimitInfo = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
export type IncrementResponse = ClientRateLimitInfo;
|
|
||||||
/**
|
|
||||||
* A modified Express request handler with the rate limit functions.
|
|
||||||
*/
|
|
||||||
export type RateLimitRequestHandler = RequestHandler & {
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - Implement the `Store` interface instead.
|
|
||||||
*/
|
|
||||||
export type LegacyStore = {
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
|
|
||||||
*/
|
|
||||||
incr: (key: string, callback: IncrementCallback) => void;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => void;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* An interface that all hit counter stores must implement.
|
|
||||||
*/
|
|
||||||
export type Store = {
|
|
||||||
/**
|
|
||||||
* Method that initializes the store, and has access to the options passed to
|
|
||||||
* the middleware too.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init?: (options: Options) => void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
|
|
||||||
*/
|
|
||||||
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
decrement: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*/
|
|
||||||
resetKey: (key: string) => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*/
|
|
||||||
resetAll?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Method to shutdown the store, stop timers, and release all resources.
|
|
||||||
*/
|
|
||||||
shutdown?: () => Promise<void> | void;
|
|
||||||
/**
|
|
||||||
* Flag to indicate that keys incremented in one instance of this store can
|
|
||||||
* not affect other instances. Typically false if a database is used, true for
|
|
||||||
* MemoryStore.
|
|
||||||
*
|
|
||||||
* Used to help detect double-counting misconfigurations.
|
|
||||||
*/
|
|
||||||
localKeys?: boolean;
|
|
||||||
/**
|
|
||||||
* Optional value that the store prepends to keys
|
|
||||||
*
|
|
||||||
* Used by the double-count check to avoid false-positives when a key is counted twice, but with different prefixes
|
|
||||||
*/
|
|
||||||
prefix?: string;
|
|
||||||
};
|
|
||||||
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
|
|
||||||
/**
|
|
||||||
* Validate configuration object for enabling or disabling specific validations.
|
|
||||||
*
|
|
||||||
* The keys must also be keys in the validations object, except `enable`, `disable`,
|
|
||||||
* and `default`.
|
|
||||||
*/
|
|
||||||
export type EnabledValidations = {
|
|
||||||
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The configuration options for the rate limiter.
|
|
||||||
*/
|
|
||||||
export type Options = {
|
|
||||||
/**
|
|
||||||
* How long we should remember the requests.
|
|
||||||
*
|
|
||||||
* Defaults to `60000` ms (= 1 minute).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* Defaults to `5`.
|
|
||||||
*/
|
|
||||||
limit: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* The response body to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `'Too many requests, please try again later.'`
|
|
||||||
*/
|
|
||||||
message: any | ValueDeterminingMiddleware<any>;
|
|
||||||
/**
|
|
||||||
* The HTTP status code to send back when a client is rate limited.
|
|
||||||
*
|
|
||||||
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
|
|
||||||
*/
|
|
||||||
statusCode: number;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* Defaults to `true` (for backward compatibility).
|
|
||||||
*/
|
|
||||||
legacyHeaders: boolean;
|
|
||||||
/**
|
|
||||||
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
|
|
||||||
*
|
|
||||||
* Defaults to `false` (for backward compatibility, but its use is recommended).
|
|
||||||
*/
|
|
||||||
standardHeaders: boolean | DraftHeadersVersion;
|
|
||||||
/**
|
|
||||||
* The name used to identify the quota policy in the `RateLimit` headers as per
|
|
||||||
* the 8th draft of the IETF specification.
|
|
||||||
*
|
|
||||||
* Defaults to `{limit}-in-{window}`.
|
|
||||||
*/
|
|
||||||
identifier: string | ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* The name of the property on the request object to store the rate limit info.
|
|
||||||
*
|
|
||||||
* Defaults to `rateLimit`.
|
|
||||||
*/
|
|
||||||
requestPropertyName: string;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a 4XX
|
|
||||||
* or 5XX status.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipFailedRequests: boolean;
|
|
||||||
/**
|
|
||||||
* If `true`, the library will (by default) skip all requests that have a
|
|
||||||
* status code less than 400.
|
|
||||||
*
|
|
||||||
* Defaults to `false`.
|
|
||||||
*/
|
|
||||||
skipSuccessfulRequests: boolean;
|
|
||||||
/**
|
|
||||||
* Method to generate custom identifiers for clients.
|
|
||||||
*
|
|
||||||
* By default, the client's IP address is used.
|
|
||||||
*/
|
|
||||||
keyGenerator: ValueDeterminingMiddleware<string>;
|
|
||||||
/**
|
|
||||||
* Express request handler that sends back a response when a client is
|
|
||||||
* rate-limited.
|
|
||||||
*
|
|
||||||
* By default, sends back the `statusCode` and `message` set via the options.
|
|
||||||
*/
|
|
||||||
handler: RateLimitExceededEventHandler;
|
|
||||||
/**
|
|
||||||
* Method (in the form of middleware) to determine whether or not this request
|
|
||||||
* counts towards a client's quota.
|
|
||||||
*
|
|
||||||
* By default, skips no requests.
|
|
||||||
*/
|
|
||||||
skip: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* Method to determine whether or not the request counts as 'succesful'. Used
|
|
||||||
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
||||||
*
|
|
||||||
* By default, requests with a response status code less than 400 are considered
|
|
||||||
* successful.
|
|
||||||
*/
|
|
||||||
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
|
|
||||||
/**
|
|
||||||
* The `Store` to use to store the hit count for each client.
|
|
||||||
*
|
|
||||||
* By default, the built-in `MemoryStore` will be used.
|
|
||||||
*/
|
|
||||||
store: Store | LegacyStore;
|
|
||||||
/**
|
|
||||||
* The list of validation checks that should run.
|
|
||||||
*/
|
|
||||||
validate: boolean | EnabledValidations;
|
|
||||||
/**
|
|
||||||
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
|
|
||||||
* of requests.
|
|
||||||
*
|
|
||||||
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
|
|
||||||
*/
|
|
||||||
headers?: boolean;
|
|
||||||
/**
|
|
||||||
* The maximum number of connections to allow during the `window` before
|
|
||||||
* rate limiting the client.
|
|
||||||
*
|
|
||||||
* Can be the limit itself as a number or express middleware that parses
|
|
||||||
* the request and then figures out the limit.
|
|
||||||
*
|
|
||||||
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
|
|
||||||
* be removed from the library in the foreseeable future.
|
|
||||||
*/
|
|
||||||
max?: number | ValueDeterminingMiddleware<number>;
|
|
||||||
/**
|
|
||||||
* If the Store generates an error, allow the request to pass.
|
|
||||||
*/
|
|
||||||
passOnStoreError: boolean;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The extended request object that includes information about the client's
|
|
||||||
* rate limit.
|
|
||||||
*/
|
|
||||||
export type AugmentedRequest = Request & {
|
|
||||||
[key: string]: RateLimitInfo;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* The rate limit related information for each client included in the
|
|
||||||
* Express request object.
|
|
||||||
*/
|
|
||||||
export type RateLimitInfo = {
|
|
||||||
limit: number;
|
|
||||||
used: number;
|
|
||||||
remaining: number;
|
|
||||||
resetTime: Date | undefined;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Create an instance of IP rate-limiting middleware for Express.
|
|
||||||
*
|
|
||||||
* @param passedOptions {Options} - Options to configure the rate limiter.
|
|
||||||
*
|
|
||||||
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
|
|
||||||
/**
|
|
||||||
* The record that stores information about a client - namely, how many times
|
|
||||||
* they have hit the endpoint, and when their hit count resets.
|
|
||||||
*
|
|
||||||
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
|
|
||||||
*/
|
|
||||||
export type Client = {
|
|
||||||
totalHits: number;
|
|
||||||
resetTime: Date;
|
|
||||||
};
|
|
||||||
/**
|
|
||||||
* A `Store` that stores the hit count for each client in memory.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export declare class MemoryStore implements Store {
|
|
||||||
/**
|
|
||||||
* The duration of time before which all hit counts are reset (in milliseconds).
|
|
||||||
*/
|
|
||||||
windowMs: number;
|
|
||||||
/**
|
|
||||||
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
||||||
* addresses or API keys).
|
|
||||||
*
|
|
||||||
* They are split into two to avoid having to iterate through the entire set to
|
|
||||||
* determine which ones need reset. Instead, `Client`s are moved from `previous`
|
|
||||||
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
|
|
||||||
* left in `previous`, i.e., those that have not made any recent requests, are
|
|
||||||
* known to be expired and can be deleted in bulk.
|
|
||||||
*/
|
|
||||||
previous: Map<string, Client>;
|
|
||||||
current: Map<string, Client>;
|
|
||||||
/**
|
|
||||||
* A reference to the active timer.
|
|
||||||
*/
|
|
||||||
interval?: NodeJS.Timeout;
|
|
||||||
/**
|
|
||||||
* Confirmation that the keys incremented in once instance of MemoryStore
|
|
||||||
* cannot affect other instances.
|
|
||||||
*/
|
|
||||||
localKeys: boolean;
|
|
||||||
/**
|
|
||||||
* Method that initializes the store.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init(options: Options): void;
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
get(key: string): Promise<ClientRateLimitInfo | undefined>;
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
increment(key: string): Promise<ClientRateLimitInfo>;
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
decrement(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetKey(key: string): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
resetAll(): Promise<void>;
|
|
||||||
/**
|
|
||||||
* Method to stop the timer (if currently running) and prevent any memory
|
|
||||||
* leaks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
shutdown(): void;
|
|
||||||
/**
|
|
||||||
* Recycles a client by setting its hit count to zero, and reset time to
|
|
||||||
* `windowMs` milliseconds from now.
|
|
||||||
*
|
|
||||||
* NOT to be confused with `#resetKey()`, which removes a client from both the
|
|
||||||
* `current` and `previous` maps.
|
|
||||||
*
|
|
||||||
* @param client {Client} - The client to recycle.
|
|
||||||
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
|
|
||||||
*
|
|
||||||
* @return {Client} - The modified client that was passed in, to allow for chaining.
|
|
||||||
*/
|
|
||||||
private resetClient;
|
|
||||||
/**
|
|
||||||
* Retrieves or creates a client, given a key. Also ensures that the client being
|
|
||||||
* returned is in the `current` map.
|
|
||||||
*
|
|
||||||
* @param key {string} - The key under which the client is (or is to be) stored.
|
|
||||||
*
|
|
||||||
* @returns {Client} - The requested client.
|
|
||||||
*/
|
|
||||||
private getClient;
|
|
||||||
/**
|
|
||||||
* Move current clients to previous, create a new map for current.
|
|
||||||
*
|
|
||||||
* This function is called every `windowMs`.
|
|
||||||
*/
|
|
||||||
private clearExpired;
|
|
||||||
}
|
|
||||||
|
|
||||||
export {
|
|
||||||
rateLimit as default,
|
|
||||||
};
|
|
||||||
|
|
||||||
export {};
|
|
||||||
809
node_modules/express-rate-limit/dist/index.mjs
generated
vendored
@ -1,809 +0,0 @@
|
|||||||
// source/headers.ts
|
|
||||||
import { Buffer } from "buffer";
|
|
||||||
import { createHash } from "crypto";
|
|
||||||
var SUPPORTED_DRAFT_VERSIONS = ["draft-6", "draft-7", "draft-8"];
|
|
||||||
var getResetSeconds = (resetTime, windowMs) => {
|
|
||||||
let resetSeconds = void 0;
|
|
||||||
if (resetTime) {
|
|
||||||
const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
|
|
||||||
resetSeconds = Math.max(0, deltaSeconds);
|
|
||||||
} else if (windowMs) {
|
|
||||||
resetSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
}
|
|
||||||
return resetSeconds;
|
|
||||||
};
|
|
||||||
var getPartitionKey = (key) => {
|
|
||||||
const hash = createHash("sha256");
|
|
||||||
hash.update(key);
|
|
||||||
const partitionKey = hash.digest("hex").slice(0, 12);
|
|
||||||
return Buffer.from(partitionKey).toString("base64");
|
|
||||||
};
|
|
||||||
var setLegacyHeaders = (response, info) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
response.setHeader("X-RateLimit-Limit", info.limit.toString());
|
|
||||||
response.setHeader("X-RateLimit-Remaining", info.remaining.toString());
|
|
||||||
if (info.resetTime instanceof Date) {
|
|
||||||
response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
|
|
||||||
response.setHeader(
|
|
||||||
"X-RateLimit-Reset",
|
|
||||||
Math.ceil(info.resetTime.getTime() / 1e3).toString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var setDraft6Headers = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime);
|
|
||||||
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
||||||
response.setHeader("RateLimit-Limit", info.limit.toString());
|
|
||||||
response.setHeader("RateLimit-Remaining", info.remaining.toString());
|
|
||||||
if (resetSeconds)
|
|
||||||
response.setHeader("RateLimit-Reset", resetSeconds.toString());
|
|
||||||
};
|
|
||||||
var setDraft7Headers = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
|
|
||||||
response.setHeader(
|
|
||||||
"RateLimit",
|
|
||||||
`limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`
|
|
||||||
);
|
|
||||||
};
|
|
||||||
var setDraft8Headers = (response, info, windowMs, name, key) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const windowSeconds = Math.ceil(windowMs / 1e3);
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
const partitionKey = getPartitionKey(key);
|
|
||||||
const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
|
|
||||||
const header = `r=${info.remaining}; t=${resetSeconds}`;
|
|
||||||
response.append("RateLimit-Policy", `"${name}"; ${policy}`);
|
|
||||||
response.append("RateLimit", `"${name}"; ${header}`);
|
|
||||||
};
|
|
||||||
var setRetryAfterHeader = (response, info, windowMs) => {
|
|
||||||
if (response.headersSent)
|
|
||||||
return;
|
|
||||||
const resetSeconds = getResetSeconds(info.resetTime, windowMs);
|
|
||||||
response.setHeader("Retry-After", resetSeconds.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/validations.ts
|
|
||||||
import { isIP } from "net";
|
|
||||||
var ValidationError = class extends Error {
|
|
||||||
/**
|
|
||||||
* The code must be a string, in snake case and all capital, that starts with
|
|
||||||
* the substring `ERR_ERL_`.
|
|
||||||
*
|
|
||||||
* The message must be a string, starting with an uppercase character,
|
|
||||||
* describing the issue in detail.
|
|
||||||
*/
|
|
||||||
constructor(code, message) {
|
|
||||||
const url = `https://express-rate-limit.github.io/${code}/`;
|
|
||||||
super(`${message} See ${url} for more information.`);
|
|
||||||
this.name = this.constructor.name;
|
|
||||||
this.code = code;
|
|
||||||
this.help = url;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var ChangeWarning = class extends ValidationError {
|
|
||||||
};
|
|
||||||
var usedStores = /* @__PURE__ */ new Set();
|
|
||||||
var singleCountKeys = /* @__PURE__ */ new WeakMap();
|
|
||||||
var validations = {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
enabled: {
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// Should be EnabledValidations type, but that's a circular reference
|
|
||||||
disable() {
|
|
||||||
for (const k of Object.keys(this.enabled))
|
|
||||||
this.enabled[k] = false;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks whether the IP address is valid, and that it does not have a port
|
|
||||||
* number in it.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
|
|
||||||
*
|
|
||||||
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
ip(ip) {
|
|
||||||
if (ip === void 0) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNDEFINED_IP_ADDRESS",
|
|
||||||
`An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!isIP(ip)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_INVALID_IP_ADDRESS",
|
|
||||||
`An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is not set to `true`.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
trustProxy(request) {
|
|
||||||
if (request.app.get("trust proxy") === true) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_PERMISSIVE_TRUST_PROXY",
|
|
||||||
`The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
|
|
||||||
* header is present.
|
|
||||||
*
|
|
||||||
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
xForwardedForHeader(request) {
|
|
||||||
if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
|
|
||||||
`The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures totalHits value from store is a positive integer.
|
|
||||||
*
|
|
||||||
* @param hits {any} - The `totalHits` returned by the store.
|
|
||||||
*/
|
|
||||||
positiveHits(hits) {
|
|
||||||
if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_INVALID_HITS",
|
|
||||||
`The totalHits value returned from the store must be a positive integer, got ${hits}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures a single store instance is not used with multiple express-rate-limit instances
|
|
||||||
*/
|
|
||||||
unsharedStore(store) {
|
|
||||||
if (usedStores.has(store)) {
|
|
||||||
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_STORE_REUSE",
|
|
||||||
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
usedStores.add(store);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Ensures a given key is incremented only once per request.
|
|
||||||
*
|
|
||||||
* @param request {Request} - The Express request object.
|
|
||||||
* @param store {Store} - The store class.
|
|
||||||
* @param key {string} - The key used to store the client's hit count.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
singleCount(request, store, key) {
|
|
||||||
let storeKeys = singleCountKeys.get(request);
|
|
||||||
if (!storeKeys) {
|
|
||||||
storeKeys = /* @__PURE__ */ new Map();
|
|
||||||
singleCountKeys.set(request, storeKeys);
|
|
||||||
}
|
|
||||||
const storeKey = store.localKeys ? store : store.constructor.name;
|
|
||||||
let keys = storeKeys.get(storeKey);
|
|
||||||
if (!keys) {
|
|
||||||
keys = [];
|
|
||||||
storeKeys.set(storeKey, keys);
|
|
||||||
}
|
|
||||||
const prefixedKey = `${store.prefix ?? ""}${key}`;
|
|
||||||
if (keys.includes(prefixedKey)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_DOUBLE_COUNT",
|
|
||||||
`The hit count for ${key} was incremented more than once for a single request.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
keys.push(prefixedKey);
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
|
|
||||||
* changing in the next major release.
|
|
||||||
*
|
|
||||||
* @param limit {number} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
limit(limit) {
|
|
||||||
if (limit === 0) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_MAX_ZERO",
|
|
||||||
`Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
|
|
||||||
* and will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
draftPolliHeaders(draft_polli_ratelimit_headers) {
|
|
||||||
if (draft_polli_ratelimit_headers) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
|
|
||||||
`The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user that the `onLimitReached` option is deprecated and
|
|
||||||
* will be removed in the next major release.
|
|
||||||
*
|
|
||||||
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
onLimitReached(onLimitReached) {
|
|
||||||
if (onLimitReached) {
|
|
||||||
throw new ChangeWarning(
|
|
||||||
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
|
|
||||||
`The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user when an invalid/unsupported version of the draft spec is passed.
|
|
||||||
*
|
|
||||||
* @param version {any | undefined} - The version passed by the user.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersDraftVersion(version) {
|
|
||||||
if (typeof version !== "string" || !SUPPORTED_DRAFT_VERSIONS.includes(version)) {
|
|
||||||
const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",
|
|
||||||
`standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Warns the user when the selected headers option requires a reset time but
|
|
||||||
* the store does not provide one.
|
|
||||||
*
|
|
||||||
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
|
|
||||||
*
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
headersResetTime(resetTime) {
|
|
||||||
if (!resetTime) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_HEADERS_NO_RESET",
|
|
||||||
`standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks the options.validate setting to ensure that only recognized
|
|
||||||
* validations are enabled or disabled.
|
|
||||||
*
|
|
||||||
* If any unrecognized values are found, an error is logged that
|
|
||||||
* includes the list of supported vaidations.
|
|
||||||
*/
|
|
||||||
validationsConfig() {
|
|
||||||
const supportedValidations = Object.keys(this).filter(
|
|
||||||
(k) => !["enabled", "disable"].includes(k)
|
|
||||||
);
|
|
||||||
supportedValidations.push("default");
|
|
||||||
for (const key of Object.keys(this.enabled)) {
|
|
||||||
if (!supportedValidations.includes(key)) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_UNKNOWN_VALIDATION",
|
|
||||||
`options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
|
|
||||||
", "
|
|
||||||
)}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Checks to see if the instance was created inside of a request handler,
|
|
||||||
* which would prevent it from working correctly, with the default memory
|
|
||||||
* store (or any other store with localKeys.)
|
|
||||||
*/
|
|
||||||
creationStack(store) {
|
|
||||||
const { stack } = new Error(
|
|
||||||
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
|
|
||||||
);
|
|
||||||
if (stack?.includes("Layer.handle [as handle_request]")) {
|
|
||||||
if (!store.localKeys) {
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
||||||
"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new ValidationError(
|
|
||||||
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
|
|
||||||
`express-rate-limit instance should be created at app initialization, not when responding to a request.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var getValidations = (_enabled) => {
|
|
||||||
let enabled;
|
|
||||||
if (typeof _enabled === "boolean") {
|
|
||||||
enabled = {
|
|
||||||
default: _enabled
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
enabled = {
|
|
||||||
default: true,
|
|
||||||
..._enabled
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const wrappedValidations = {
|
|
||||||
enabled
|
|
||||||
};
|
|
||||||
for (const [name, validation] of Object.entries(validations)) {
|
|
||||||
if (typeof validation === "function")
|
|
||||||
wrappedValidations[name] = (...args) => {
|
|
||||||
if (!(enabled[name] ?? enabled.default)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
;
|
|
||||||
validation.apply(
|
|
||||||
wrappedValidations,
|
|
||||||
args
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof ChangeWarning)
|
|
||||||
console.warn(error);
|
|
||||||
else
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return wrappedValidations;
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/memory-store.ts
|
|
||||||
var MemoryStore = class {
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* These two maps store usage (requests) and reset time by key (for example, IP
|
|
||||||
* addresses or API keys).
|
|
||||||
*
|
|
||||||
* They are split into two to avoid having to iterate through the entire set to
|
|
||||||
* determine which ones need reset. Instead, `Client`s are moved from `previous`
|
|
||||||
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
|
|
||||||
* left in `previous`, i.e., those that have not made any recent requests, are
|
|
||||||
* known to be expired and can be deleted in bulk.
|
|
||||||
*/
|
|
||||||
this.previous = /* @__PURE__ */ new Map();
|
|
||||||
this.current = /* @__PURE__ */ new Map();
|
|
||||||
/**
|
|
||||||
* Confirmation that the keys incremented in once instance of MemoryStore
|
|
||||||
* cannot affect other instances.
|
|
||||||
*/
|
|
||||||
this.localKeys = true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method that initializes the store.
|
|
||||||
*
|
|
||||||
* @param options {Options} - The options used to setup the middleware.
|
|
||||||
*/
|
|
||||||
init(options) {
|
|
||||||
this.windowMs = options.windowMs;
|
|
||||||
if (this.interval)
|
|
||||||
clearInterval(this.interval);
|
|
||||||
this.interval = setInterval(() => {
|
|
||||||
this.clearExpired();
|
|
||||||
}, this.windowMs);
|
|
||||||
if (this.interval.unref)
|
|
||||||
this.interval.unref();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to fetch a client's hit count and reset time.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async get(key) {
|
|
||||||
return this.current.get(key) ?? this.previous.get(key);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to increment a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async increment(key) {
|
|
||||||
const client = this.getClient(key);
|
|
||||||
const now = Date.now();
|
|
||||||
if (client.resetTime.getTime() <= now) {
|
|
||||||
this.resetClient(client, now);
|
|
||||||
}
|
|
||||||
client.totalHits++;
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to decrement a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async decrement(key) {
|
|
||||||
const client = this.getClient(key);
|
|
||||||
if (client.totalHits > 0)
|
|
||||||
client.totalHits--;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to reset a client's hit counter.
|
|
||||||
*
|
|
||||||
* @param key {string} - The identifier for a client.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async resetKey(key) {
|
|
||||||
this.current.delete(key);
|
|
||||||
this.previous.delete(key);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to reset everyone's hit counter.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
async resetAll() {
|
|
||||||
this.current.clear();
|
|
||||||
this.previous.clear();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Method to stop the timer (if currently running) and prevent any memory
|
|
||||||
* leaks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
shutdown() {
|
|
||||||
clearInterval(this.interval);
|
|
||||||
void this.resetAll();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Recycles a client by setting its hit count to zero, and reset time to
|
|
||||||
* `windowMs` milliseconds from now.
|
|
||||||
*
|
|
||||||
* NOT to be confused with `#resetKey()`, which removes a client from both the
|
|
||||||
* `current` and `previous` maps.
|
|
||||||
*
|
|
||||||
* @param client {Client} - The client to recycle.
|
|
||||||
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
|
|
||||||
*
|
|
||||||
* @return {Client} - The modified client that was passed in, to allow for chaining.
|
|
||||||
*/
|
|
||||||
resetClient(client, now = Date.now()) {
|
|
||||||
client.totalHits = 0;
|
|
||||||
client.resetTime.setTime(now + this.windowMs);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Retrieves or creates a client, given a key. Also ensures that the client being
|
|
||||||
* returned is in the `current` map.
|
|
||||||
*
|
|
||||||
* @param key {string} - The key under which the client is (or is to be) stored.
|
|
||||||
*
|
|
||||||
* @returns {Client} - The requested client.
|
|
||||||
*/
|
|
||||||
getClient(key) {
|
|
||||||
if (this.current.has(key))
|
|
||||||
return this.current.get(key);
|
|
||||||
let client;
|
|
||||||
if (this.previous.has(key)) {
|
|
||||||
client = this.previous.get(key);
|
|
||||||
this.previous.delete(key);
|
|
||||||
} else {
|
|
||||||
client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
|
|
||||||
this.resetClient(client);
|
|
||||||
}
|
|
||||||
this.current.set(key, client);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Move current clients to previous, create a new map for current.
|
|
||||||
*
|
|
||||||
* This function is called every `windowMs`.
|
|
||||||
*/
|
|
||||||
clearExpired() {
|
|
||||||
this.previous = this.current;
|
|
||||||
this.current = /* @__PURE__ */ new Map();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// source/lib.ts
|
|
||||||
var isLegacyStore = (store) => (
|
|
||||||
// Check that `incr` exists but `increment` does not - store authors might want
|
|
||||||
// to keep both around for backwards compatibility.
|
|
||||||
typeof store.incr === "function" && typeof store.increment !== "function"
|
|
||||||
);
|
|
||||||
var promisifyStore = (passedStore) => {
|
|
||||||
if (!isLegacyStore(passedStore)) {
|
|
||||||
return passedStore;
|
|
||||||
}
|
|
||||||
const legacyStore = passedStore;
|
|
||||||
class PromisifiedStore {
|
|
||||||
async increment(key) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
legacyStore.incr(
|
|
||||||
key,
|
|
||||||
(error, totalHits, resetTime) => {
|
|
||||||
if (error)
|
|
||||||
reject(error);
|
|
||||||
resolve({ totalHits, resetTime });
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
async decrement(key) {
|
|
||||||
return legacyStore.decrement(key);
|
|
||||||
}
|
|
||||||
async resetKey(key) {
|
|
||||||
return legacyStore.resetKey(key);
|
|
||||||
}
|
|
||||||
/* istanbul ignore next */
|
|
||||||
async resetAll() {
|
|
||||||
if (typeof legacyStore.resetAll === "function")
|
|
||||||
return legacyStore.resetAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new PromisifiedStore();
|
|
||||||
};
|
|
||||||
var getOptionsFromConfig = (config) => {
|
|
||||||
const { validations: validations2, ...directlyPassableEntries } = config;
|
|
||||||
return {
|
|
||||||
...directlyPassableEntries,
|
|
||||||
validate: validations2.enabled
|
|
||||||
};
|
|
||||||
};
|
|
||||||
var omitUndefinedOptions = (passedOptions) => {
|
|
||||||
const omittedOptions = {};
|
|
||||||
for (const k of Object.keys(passedOptions)) {
|
|
||||||
const key = k;
|
|
||||||
if (passedOptions[key] !== void 0) {
|
|
||||||
omittedOptions[key] = passedOptions[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return omittedOptions;
|
|
||||||
};
|
|
||||||
var parseOptions = (passedOptions) => {
|
|
||||||
const notUndefinedOptions = omitUndefinedOptions(passedOptions);
|
|
||||||
const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
|
|
||||||
validations2.validationsConfig();
|
|
||||||
validations2.draftPolliHeaders(
|
|
||||||
// @ts-expect-error see the note above.
|
|
||||||
notUndefinedOptions.draft_polli_ratelimit_headers
|
|
||||||
);
|
|
||||||
validations2.onLimitReached(notUndefinedOptions.onLimitReached);
|
|
||||||
let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
|
|
||||||
if (standardHeaders === true)
|
|
||||||
standardHeaders = "draft-6";
|
|
||||||
const config = {
|
|
||||||
windowMs: 60 * 1e3,
|
|
||||||
limit: passedOptions.max ?? 5,
|
|
||||||
// `max` is deprecated, but support it anyways.
|
|
||||||
message: "Too many requests, please try again later.",
|
|
||||||
statusCode: 429,
|
|
||||||
legacyHeaders: passedOptions.headers ?? true,
|
|
||||||
identifier(request, _response) {
|
|
||||||
let duration = "";
|
|
||||||
const property = config.requestPropertyName;
|
|
||||||
const { limit } = request[property];
|
|
||||||
const seconds = config.windowMs / 1e3;
|
|
||||||
const minutes = config.windowMs / (1e3 * 60);
|
|
||||||
const hours = config.windowMs / (1e3 * 60 * 60);
|
|
||||||
const days = config.windowMs / (1e3 * 60 * 60 * 24);
|
|
||||||
if (seconds < 60)
|
|
||||||
duration = `${seconds}sec`;
|
|
||||||
else if (minutes < 60)
|
|
||||||
duration = `${minutes}min`;
|
|
||||||
else if (hours < 24)
|
|
||||||
duration = `${hours}hr${hours > 1 ? "s" : ""}`;
|
|
||||||
else
|
|
||||||
duration = `${days}day${days > 1 ? "s" : ""}`;
|
|
||||||
return `${limit}-in-${duration}`;
|
|
||||||
},
|
|
||||||
requestPropertyName: "rateLimit",
|
|
||||||
skipFailedRequests: false,
|
|
||||||
skipSuccessfulRequests: false,
|
|
||||||
requestWasSuccessful: (_request, response) => response.statusCode < 400,
|
|
||||||
skip: (_request, _response) => false,
|
|
||||||
keyGenerator(request, _response) {
|
|
||||||
validations2.ip(request.ip);
|
|
||||||
validations2.trustProxy(request);
|
|
||||||
validations2.xForwardedForHeader(request);
|
|
||||||
return request.ip;
|
|
||||||
},
|
|
||||||
async handler(request, response, _next, _optionsUsed) {
|
|
||||||
response.status(config.statusCode);
|
|
||||||
const message = typeof config.message === "function" ? await config.message(
|
|
||||||
request,
|
|
||||||
response
|
|
||||||
) : config.message;
|
|
||||||
if (!response.writableEnded) {
|
|
||||||
response.send(message);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
passOnStoreError: false,
|
|
||||||
// Allow the default options to be overriden by the passed options.
|
|
||||||
...notUndefinedOptions,
|
|
||||||
// `standardHeaders` is resolved into a draft version above, use that.
|
|
||||||
standardHeaders,
|
|
||||||
// Note that this field is declared after the user's options are spread in,
|
|
||||||
// so that this field doesn't get overriden with an un-promisified store!
|
|
||||||
store: promisifyStore(notUndefinedOptions.store ?? new MemoryStore()),
|
|
||||||
// Print an error to the console if a few known misconfigurations are detected.
|
|
||||||
validations: validations2
|
|
||||||
};
|
|
||||||
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
|
|
||||||
throw new TypeError(
|
|
||||||
"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
var handleAsyncErrors = (fn) => async (request, response, next) => {
|
|
||||||
try {
|
|
||||||
await Promise.resolve(fn(request, response, next)).catch(next);
|
|
||||||
} catch (error) {
|
|
||||||
next(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var rateLimit = (passedOptions) => {
|
|
||||||
const config = parseOptions(passedOptions ?? {});
|
|
||||||
const options = getOptionsFromConfig(config);
|
|
||||||
config.validations.creationStack(config.store);
|
|
||||||
config.validations.unsharedStore(config.store);
|
|
||||||
if (typeof config.store.init === "function")
|
|
||||||
config.store.init(options);
|
|
||||||
const middleware = handleAsyncErrors(
|
|
||||||
async (request, response, next) => {
|
|
||||||
const skip = await config.skip(request, response);
|
|
||||||
if (skip) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const augmentedRequest = request;
|
|
||||||
const key = await config.keyGenerator(request, response);
|
|
||||||
let totalHits = 0;
|
|
||||||
let resetTime;
|
|
||||||
try {
|
|
||||||
const incrementResult = await config.store.increment(key);
|
|
||||||
totalHits = incrementResult.totalHits;
|
|
||||||
resetTime = incrementResult.resetTime;
|
|
||||||
} catch (error) {
|
|
||||||
if (config.passOnStoreError) {
|
|
||||||
console.error(
|
|
||||||
"express-rate-limit: error from store, allowing request without rate-limiting.",
|
|
||||||
error
|
|
||||||
);
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
config.validations.positiveHits(totalHits);
|
|
||||||
config.validations.singleCount(request, config.store, key);
|
|
||||||
const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
|
|
||||||
const limit = await retrieveLimit;
|
|
||||||
config.validations.limit(limit);
|
|
||||||
const info = {
|
|
||||||
limit,
|
|
||||||
used: totalHits,
|
|
||||||
remaining: Math.max(limit - totalHits, 0),
|
|
||||||
resetTime
|
|
||||||
};
|
|
||||||
Object.defineProperty(info, "current", {
|
|
||||||
configurable: false,
|
|
||||||
enumerable: false,
|
|
||||||
value: totalHits
|
|
||||||
});
|
|
||||||
augmentedRequest[config.requestPropertyName] = info;
|
|
||||||
if (config.legacyHeaders && !response.headersSent) {
|
|
||||||
setLegacyHeaders(response, info);
|
|
||||||
}
|
|
||||||
if (config.standardHeaders && !response.headersSent) {
|
|
||||||
switch (config.standardHeaders) {
|
|
||||||
case "draft-6": {
|
|
||||||
setDraft6Headers(response, info, config.windowMs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "draft-7": {
|
|
||||||
config.validations.headersResetTime(info.resetTime);
|
|
||||||
setDraft7Headers(response, info, config.windowMs);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "draft-8": {
|
|
||||||
const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
|
|
||||||
const name = await retrieveName;
|
|
||||||
config.validations.headersResetTime(info.resetTime);
|
|
||||||
setDraft8Headers(response, info, config.windowMs, name, key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
config.validations.headersDraftVersion(config.standardHeaders);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (config.skipFailedRequests || config.skipSuccessfulRequests) {
|
|
||||||
let decremented = false;
|
|
||||||
const decrementKey = async () => {
|
|
||||||
if (!decremented) {
|
|
||||||
await config.store.decrement(key);
|
|
||||||
decremented = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (config.skipFailedRequests) {
|
|
||||||
response.on("finish", async () => {
|
|
||||||
if (!await config.requestWasSuccessful(request, response))
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
response.on("close", async () => {
|
|
||||||
if (!response.writableEnded)
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
response.on("error", async () => {
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (config.skipSuccessfulRequests) {
|
|
||||||
response.on("finish", async () => {
|
|
||||||
if (await config.requestWasSuccessful(request, response))
|
|
||||||
await decrementKey();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.validations.disable();
|
|
||||||
if (totalHits > limit) {
|
|
||||||
if (config.legacyHeaders || config.standardHeaders) {
|
|
||||||
setRetryAfterHeader(response, info, config.windowMs);
|
|
||||||
}
|
|
||||||
config.handler(request, response, next, options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const getThrowFn = () => {
|
|
||||||
throw new Error("The current store does not support the get/getKey method");
|
|
||||||
};
|
|
||||||
middleware.resetKey = config.store.resetKey.bind(config.store);
|
|
||||||
middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
|
|
||||||
return middleware;
|
|
||||||
};
|
|
||||||
var lib_default = rateLimit;
|
|
||||||
export {
|
|
||||||
MemoryStore,
|
|
||||||
lib_default as default,
|
|
||||||
lib_default as rateLimit
|
|
||||||
};
|
|
||||||
20
node_modules/express-rate-limit/license.md
generated
vendored
@ -1,20 +0,0 @@
|
|||||||
# MIT License
|
|
||||||
|
|
||||||
Copyright 2023 Nathan Friedly, Vedant K
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
|
||||||
the Software without restriction, including without limitation the rights to
|
|
||||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
133
node_modules/express-rate-limit/package.json
generated
vendored
@ -1,133 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "express-rate-limit",
|
|
||||||
"version": "7.5.0",
|
|
||||||
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
|
|
||||||
"author": {
|
|
||||||
"name": "Nathan Friedly",
|
|
||||||
"url": "http://nfriedly.com/"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"homepage": "https://github.com/express-rate-limit/express-rate-limit",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/express-rate-limit/express-rate-limit.git"
|
|
||||||
},
|
|
||||||
"funding": "https://github.com/sponsors/express-rate-limit",
|
|
||||||
"keywords": [
|
|
||||||
"express-rate-limit",
|
|
||||||
"express",
|
|
||||||
"rate",
|
|
||||||
"limit",
|
|
||||||
"ratelimit",
|
|
||||||
"rate-limit",
|
|
||||||
"middleware",
|
|
||||||
"ip",
|
|
||||||
"auth",
|
|
||||||
"authorization",
|
|
||||||
"security",
|
|
||||||
"brute",
|
|
||||||
"force",
|
|
||||||
"bruteforce",
|
|
||||||
"brute-force",
|
|
||||||
"attack"
|
|
||||||
],
|
|
||||||
"type": "module",
|
|
||||||
"exports": {
|
|
||||||
".": {
|
|
||||||
"import": {
|
|
||||||
"types": "./dist/index.d.mts",
|
|
||||||
"default": "./dist/index.mjs"
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"types": "./dist/index.d.cts",
|
|
||||||
"default": "./dist/index.cjs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"main": "./dist/index.cjs",
|
|
||||||
"module": "./dist/index.mjs",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"files": [
|
|
||||||
"dist/",
|
|
||||||
"tsconfig.json"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
|
|
||||||
"build:cjs": "esbuild --platform=node --bundle --target=es2022 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = rateLimit; module.exports.default = rateLimit; module.exports.rateLimit = rateLimit; module.exports.MemoryStore = MemoryStore;\" source/index.ts",
|
|
||||||
"build:esm": "esbuild --platform=node --bundle --target=es2022 --format=esm --outfile=dist/index.mjs source/index.ts",
|
|
||||||
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
|
|
||||||
"compile": "run-s clean build:*",
|
|
||||||
"docs": "cd docs && mintlify dev",
|
|
||||||
"lint:code": "xo",
|
|
||||||
"lint:rest": "prettier --check .",
|
|
||||||
"lint": "run-s lint:*",
|
|
||||||
"format:code": "xo --fix",
|
|
||||||
"format:rest": "prettier --write .",
|
|
||||||
"format": "run-s format:*",
|
|
||||||
"test:lib": "jest",
|
|
||||||
"test:ext": "cd test/external/ && bash run-all-tests",
|
|
||||||
"test": "run-s lint test:lib",
|
|
||||||
"pre-commit": "lint-staged",
|
|
||||||
"prepare": "run-s compile && husky install config/husky"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@express-rate-limit/prettier": "1.1.1",
|
|
||||||
"@express-rate-limit/tsconfig": "1.0.2",
|
|
||||||
"@jest/globals": "29.7.0",
|
|
||||||
"@types/express": "4.17.20",
|
|
||||||
"@types/jest": "29.5.6",
|
|
||||||
"@types/node": "20.8.7",
|
|
||||||
"@types/supertest": "2.0.15",
|
|
||||||
"del-cli": "5.1.0",
|
|
||||||
"dts-bundle-generator": "8.0.1",
|
|
||||||
"esbuild": "0.19.5",
|
|
||||||
"express": "4.21.1",
|
|
||||||
"husky": "8.0.3",
|
|
||||||
"jest": "29.7.0",
|
|
||||||
"lint-staged": "15.0.2",
|
|
||||||
"mintlify": "4.0.63",
|
|
||||||
"npm-run-all": "4.1.5",
|
|
||||||
"ratelimit-header-parser": "0.1.0",
|
|
||||||
"supertest": "6.3.3",
|
|
||||||
"ts-jest": "29.1.1",
|
|
||||||
"ts-node": "10.9.1",
|
|
||||||
"typescript": "5.2.2",
|
|
||||||
"xo": "0.56.0"
|
|
||||||
},
|
|
||||||
"xo": {
|
|
||||||
"prettier": true,
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-empty-function": 0,
|
|
||||||
"@typescript-eslint/no-dynamic-delete": 0,
|
|
||||||
"@typescript-eslint/no-confusing-void-expression": 0,
|
|
||||||
"@typescript-eslint/consistent-indexed-object-style": [
|
|
||||||
"error",
|
|
||||||
"index-signature"
|
|
||||||
],
|
|
||||||
"n/no-unsupported-features/es-syntax": 0
|
|
||||||
},
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "test/library/*.ts",
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-unsafe-argument": 0,
|
|
||||||
"@typescript-eslint/no-unsafe-assignment": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ignore": [
|
|
||||||
"test/external"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"prettier": "@express-rate-limit/prettier",
|
|
||||||
"lint-staged": {
|
|
||||||
"{source,test}/**/*.ts": "xo --fix",
|
|
||||||
"**/*.{json,yaml,md}": "prettier --write "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
147
node_modules/express-rate-limit/readme.md
generated
vendored
@ -1,147 +0,0 @@
|
|||||||
<h1 align="center"> <code>express-rate-limit</code> </h1>
|
|
||||||
|
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
[](https://github.com/express-rate-limit/express-rate-limit/actions/workflows/ci.yaml)
|
|
||||||
[](https://npmjs.org/package/express-rate-limit 'View this project on NPM')
|
|
||||||
[](https://www.npmjs.com/package/express-rate-limit)
|
|
||||||
[](license.md)
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Basic rate-limiting middleware for [Express](http://expressjs.com/). Use to
|
|
||||||
limit repeated requests to public APIs and/or endpoints such as password reset.
|
|
||||||
Plays nice with
|
|
||||||
[express-slow-down](https://www.npmjs.com/package/express-slow-down) and
|
|
||||||
[ratelimit-header-parser](https://www.npmjs.com/package/ratelimit-header-parser).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The [full documentation](https://express-rate-limit.mintlify.app/overview) is
|
|
||||||
available on-line.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { rateLimit } from 'express-rate-limit'
|
|
||||||
|
|
||||||
const limiter = rateLimit({
|
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
||||||
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
|
|
||||||
standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
|
|
||||||
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
|
|
||||||
// store: ... , // Redis, Memcached, etc. See below.
|
|
||||||
})
|
|
||||||
|
|
||||||
// Apply the rate limiting middleware to all requests.
|
|
||||||
app.use(limiter)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Data Stores
|
|
||||||
|
|
||||||
The rate limiter comes with a built-in memory store, and supports a variety of
|
|
||||||
[external data stores](https://express-rate-limit.mintlify.app/reference/stores).
|
|
||||||
|
|
||||||
### Configuration
|
|
||||||
|
|
||||||
All function options may be async. Click the name for additional info and
|
|
||||||
default values.
|
|
||||||
|
|
||||||
| Option | Type | Remarks |
|
|
||||||
| -------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
||||||
| [`windowMs`] | `number` | How long to remember requests for, in milliseconds. |
|
|
||||||
| [`limit`] | `number` \| `function` | How many requests to allow. |
|
|
||||||
| [`message`] | `string` \| `json` \| `function` | Response to return after limit is reached. |
|
|
||||||
| [`statusCode`] | `number` | HTTP status code after limit is reached (default is 429). |
|
|
||||||
| [`handler`] | `function` | Function to run after limit is reached (overrides `message` and `statusCode` settings, if set). |
|
|
||||||
| [`legacyHeaders`] | `boolean` | Enable the `X-Rate-Limit` header. |
|
|
||||||
| [`standardHeaders`] | `'draft-6'` \| `'draft-7'` \| `'draft-8'` | Enable the `Ratelimit` header. |
|
|
||||||
| [`identifier`] | `string` \| `function` | Name associated with the quota policy enforced by this rate limiter. |
|
|
||||||
| [`store`] | `Store` | Use a custom store to share hit counts across multiple nodes. |
|
|
||||||
| [`passOnStoreError`] | `boolean` | Allow (`true`) or block (`false`, default) traffic if the store becomes unavailable. |
|
|
||||||
| [`keyGenerator`] | `function` | Identify users (defaults to IP address). |
|
|
||||||
| [`requestPropertyName`] | `string` | Add rate limit info to the `req` object. |
|
|
||||||
| [`skip`] | `function` | Return `true` to bypass the limiter for the given request. |
|
|
||||||
| [`skipSuccessfulRequests`] | `boolean` | Uncount 1xx/2xx/3xx responses. |
|
|
||||||
| [`skipFailedRequests`] | `boolean` | Uncount 4xx/5xx responses. |
|
|
||||||
| [`requestWasSuccessful`] | `function` | Used by `skipSuccessfulRequests` and `skipFailedRequests`. |
|
|
||||||
| [`validate`] | `boolean` \| `object` | Enable or disable built-in validation checks. |
|
|
||||||
|
|
||||||
## Thank You
|
|
||||||
|
|
||||||
Sponsored by [Zuplo](https://zuplo.link/express-rate-limit) a fully-managed API
|
|
||||||
Gateway for developers. Add
|
|
||||||
[dynamic rate-limiting](https://zuplo.link/dynamic-rate-limiting),
|
|
||||||
authentication and more to any API in minutes. Learn more at
|
|
||||||
[zuplo.com](https://zuplo.link/express-rate-limit)
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://zuplo.link/express-rate-limit">
|
|
||||||
<picture width="322">
|
|
||||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/express-rate-limit/express-rate-limit/assets/114976/cd2f6fa7-eae1-4fbb-be7d-b17df4c6f383">
|
|
||||||
<img alt="zuplo-logo" src="https://github.com/express-rate-limit/express-rate-limit/assets/114976/66fd75fa-b39e-4a8c-8d7a-52369bf244dc" width="322">
|
|
||||||
</picture>
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Thanks to Mintlify for hosting the documentation at
|
|
||||||
[express-rate-limit.mintlify.app](https://express-rate-limit.mintlify.app)
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<a href="https://mintlify.com/?utm_campaign=devmark&utm_medium=readme&utm_source=express-rate-limit">
|
|
||||||
<img height="75" src="https://devmark-public-assets.s3.us-west-2.amazonaws.com/sponsorships/mintlify.svg" alt="Create your docs today">
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Finally, thank you to everyone who's contributed to this project in any way! 🫶
|
|
||||||
|
|
||||||
## Issues and Contributing
|
|
||||||
|
|
||||||
If you encounter a bug or want to see something added/changed, please go ahead
|
|
||||||
and
|
|
||||||
[open an issue](https://github.com/express-rate-limit/express-rate-limit/issues/new)!
|
|
||||||
If you need help with something, feel free to
|
|
||||||
[start a discussion](https://github.com/express-rate-limit/express-rate-limit/discussions/new)!
|
|
||||||
|
|
||||||
If you wish to contribute to the library, thanks! First, please read
|
|
||||||
[the contributing guide](https://express-rate-limit.mintlify.app/docs/guides/contributing.mdx).
|
|
||||||
Then you can pick up any issue and fix/implement it!
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
MIT © [Nathan Friedly](http://nfriedly.com/),
|
|
||||||
[Vedant K](https://github.com/gamemaker1)
|
|
||||||
|
|
||||||
[`windowMs`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#windowms
|
|
||||||
[`limit`]: https://express-rate-limit.mintlify.app/reference/configuration#limit
|
|
||||||
[`message`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#message
|
|
||||||
[`statusCode`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#statuscode
|
|
||||||
[`handler`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#handler
|
|
||||||
[`legacyHeaders`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#legacyheaders
|
|
||||||
[`standardHeaders`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#standardheaders
|
|
||||||
[`identifier`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#identifier
|
|
||||||
[`store`]: https://express-rate-limit.mintlify.app/reference/configuration#store
|
|
||||||
[`passOnStoreError`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#passOnStoreError
|
|
||||||
[`keyGenerator`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#keygenerator
|
|
||||||
[`requestPropertyName`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#requestpropertyname
|
|
||||||
[`skip`]: https://express-rate-limit.mintlify.app/reference/configuration#skip
|
|
||||||
[`skipSuccessfulRequests`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#skipsuccessfulrequests
|
|
||||||
[`skipFailedRequests`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#skipfailedrequests
|
|
||||||
[`requestWasSuccessful`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#requestwassuccessful
|
|
||||||
[`validate`]:
|
|
||||||
https://express-rate-limit.mintlify.app/reference/configuration#validate
|
|
||||||
8
node_modules/express-rate-limit/tsconfig.json
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"include": ["source/"],
|
|
||||||
"exclude": ["node_modules/"],
|
|
||||||
"extends": "@express-rate-limit/tsconfig",
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2020"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
package-lock.json
generated
@ -13,7 +13,6 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-rate-limit": "^7.5.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"multer": "^1.4.5-lts.2",
|
"multer": "^1.4.5-lts.2",
|
||||||
"pg": "^8.15.1"
|
"pg": "^8.15.1"
|
||||||
@ -231,6 +230,7 @@
|
|||||||
"version": "16.5.0",
|
"version": "16.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||||
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
"integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
},
|
},
|
||||||
@ -363,20 +363,6 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/express-rate-limit": {
|
|
||||||
"version": "7.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
|
|
||||||
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/express-rate-limit"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"express": "^4.11 || 5 || ^5.0.0-beta.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/finalhandler": {
|
"node_modules/finalhandler": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.5.0",
|
"dotenv": "^16.5.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"express-rate-limit": "^7.5.0",
|
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"multer": "^1.4.5-lts.2",
|
"multer": "^1.4.5-lts.2",
|
||||||
"pg": "^8.15.1"
|
"pg": "^8.15.1"
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 307 KiB |
|
Before Width: | Height: | Size: 274 B |
|
Before Width: | Height: | Size: 282 KiB |
|
Before Width: | Height: | Size: 442 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |