11import * as ipaddr from "ipaddr.js" ;
2- import { unstable_cache } from "next/cache " ;
2+ import { CLOUDFLARE_IPS_URL , TTL_CLOUDFLARE_IPS } from "@/lib/constants " ;
33
44export interface CloudflareIpRanges {
55 ipv4Cidrs : string [ ] ;
@@ -9,76 +9,80 @@ export interface CloudflareIpRanges {
99let lastLoadedIpv4Parsed : Array < [ ipaddr . IPv4 , number ] > | undefined ;
1010let lastLoadedIpv6Parsed : Array < [ ipaddr . IPv6 , number ] > | undefined ;
1111
12- export const getCloudflareIpRanges = unstable_cache (
13- async ( ) : Promise < CloudflareIpRanges > => {
14- const res = await fetch ( "https://api.cloudflare.com/client/v4/ips" ) ;
15- if ( ! res . ok ) {
16- throw new Error ( `Failed to fetch Cloudflare IPs: ${ res . status } ` ) ;
17- }
18- const data = await res . json ( ) ;
12+ async function getCloudflareIpRanges ( ) : Promise < CloudflareIpRanges > {
13+ const res = await fetch ( CLOUDFLARE_IPS_URL , {
14+ next : { revalidate : TTL_CLOUDFLARE_IPS } ,
15+ } ) ;
1916
20- const ranges : CloudflareIpRanges = {
21- ipv4Cidrs : data . result ?. ipv4_cidrs || [ ] ,
22- ipv6Cidrs : data . result ?. ipv6_cidrs || [ ] ,
23- } ;
17+ if ( ! res . ok ) {
18+ throw new Error ( `Failed to fetch Cloudflare IPs: ${ res . status } ` ) ;
19+ }
2420
25- // Pre-parse IPv4 CIDRs for fast sync/async checks
26- try {
27- lastLoadedIpv4Parsed = ranges . ipv4Cidrs
28- . map ( ( cidr ) => {
29- try {
30- const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
31- if ( net . kind ( ) !== "ipv4" ) return undefined ;
32- return [ net as ipaddr . IPv4 , prefix ] as [ ipaddr . IPv4 , number ] ;
33- } catch {
34- return undefined ;
35- }
36- } )
37- . filter ( Boolean ) as Array < [ ipaddr . IPv4 , number ] > ;
38- } catch {
39- lastLoadedIpv4Parsed = undefined ;
40- }
21+ const data = await res . json ( ) ;
4122
42- // Pre-parse IPv6 CIDRs for fast sync/async checks
43- try {
44- lastLoadedIpv6Parsed = ranges . ipv6Cidrs
45- . map ( ( cidr ) => {
46- try {
47- const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
48- if ( net . kind ( ) !== "ipv6" ) return undefined ;
49- return [ net as ipaddr . IPv6 , prefix ] as [ ipaddr . IPv6 , number ] ;
50- } catch {
51- return undefined ;
52- }
53- } )
54- . filter ( Boolean ) as Array < [ ipaddr . IPv6 , number ] > ;
55- } catch {
56- lastLoadedIpv6Parsed = undefined ;
57- }
58- return ranges ;
59- } ,
60- [ "cloudflare-ip-ranges" ] ,
61- // Cache for a very long time (30 days)
62- { revalidate : 30 * 24 * 60 * 60 } ,
63- ) ;
23+ const ranges : CloudflareIpRanges = {
24+ ipv4Cidrs : data . result ?. ipv4_cidrs || [ ] ,
25+ ipv6Cidrs : data . result ?. ipv6_cidrs || [ ] ,
26+ } ;
6427
65- export function isCloudflareIp ( ip : string ) : boolean {
66- if ( ipaddr . IPv4 . isValid ( ip ) ) {
67- if ( ! lastLoadedIpv4Parsed ) return false ;
68- const v4 = ipaddr . IPv4 . parse ( ip ) ;
69- return lastLoadedIpv4Parsed . some ( ( range ) => v4 . match ( range ) ) ;
28+ // Pre-parse IPv4 CIDRs for fast sync/async checks
29+ try {
30+ lastLoadedIpv4Parsed = ranges . ipv4Cidrs
31+ . map ( ( cidr ) => {
32+ try {
33+ const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
34+ if ( net . kind ( ) !== "ipv4" ) return undefined ;
35+ return [ net as ipaddr . IPv4 , prefix ] as [ ipaddr . IPv4 , number ] ;
36+ } catch {
37+ return undefined ;
38+ }
39+ } )
40+ . filter ( Boolean ) as Array < [ ipaddr . IPv4 , number ] > ;
41+ } catch {
42+ lastLoadedIpv4Parsed = undefined ;
7043 }
7144
72- if ( ipaddr . IPv6 . isValid ( ip ) ) {
73- if ( ! lastLoadedIpv6Parsed ) return false ;
74- const v6 = ipaddr . IPv6 . parse ( ip ) ;
75- return lastLoadedIpv6Parsed . some ( ( range ) => v6 . match ( range ) ) ;
45+ // Pre-parse IPv6 CIDRs for fast sync/async checks
46+ try {
47+ lastLoadedIpv6Parsed = ranges . ipv6Cidrs
48+ . map ( ( cidr ) => {
49+ try {
50+ const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
51+ if ( net . kind ( ) !== "ipv6" ) return undefined ;
52+ return [ net as ipaddr . IPv6 , prefix ] as [ ipaddr . IPv6 , number ] ;
53+ } catch {
54+ return undefined ;
55+ }
56+ } )
57+ . filter ( Boolean ) as Array < [ ipaddr . IPv6 , number ] > ;
58+ } catch {
59+ lastLoadedIpv6Parsed = undefined ;
7660 }
7761
78- return false ;
62+ return ranges ;
7963}
8064
81- export async function isCloudflareIpAsync ( ip : string ) : Promise < boolean > {
65+ function ipV4InCidr ( addr : ipaddr . IPv4 , cidr : string ) : boolean {
66+ try {
67+ const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
68+ if ( net . kind ( ) !== "ipv4" ) return false ;
69+ return addr . match ( [ net as ipaddr . IPv4 , prefix ] ) ;
70+ } catch {
71+ return false ;
72+ }
73+ }
74+
75+ function ipV6InCidr ( addr : ipaddr . IPv6 , cidr : string ) : boolean {
76+ try {
77+ const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
78+ if ( net . kind ( ) !== "ipv6" ) return false ;
79+ return addr . match ( [ net as ipaddr . IPv6 , prefix ] ) ;
80+ } catch {
81+ return false ;
82+ }
83+ }
84+
85+ export async function isCloudflareIp ( ip : string ) : Promise < boolean > {
8286 const ranges = await getCloudflareIpRanges ( ) ;
8387
8488 if ( ipaddr . IPv4 . isValid ( ip ) ) {
@@ -101,23 +105,3 @@ export async function isCloudflareIpAsync(ip: string): Promise<boolean> {
101105
102106 return false ;
103107}
104-
105- function ipV4InCidr ( addr : ipaddr . IPv4 , cidr : string ) : boolean {
106- try {
107- const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
108- if ( net . kind ( ) !== "ipv4" ) return false ;
109- return addr . match ( [ net as ipaddr . IPv4 , prefix ] ) ;
110- } catch {
111- return false ;
112- }
113- }
114-
115- function ipV6InCidr ( addr : ipaddr . IPv6 , cidr : string ) : boolean {
116- try {
117- const [ net , prefix ] = ipaddr . parseCIDR ( cidr ) ;
118- if ( net . kind ( ) !== "ipv6" ) return false ;
119- return addr . match ( [ net as ipaddr . IPv6 , prefix ] ) ;
120- } catch {
121- return false ;
122- }
123- }
0 commit comments