From 8a6aef743009d3ef69160cf68a83f8e328a30e6b Mon Sep 17 00:00:00 2001 From: TZGyn Date: Sat, 3 Aug 2024 18:03:56 +0800 Subject: [PATCH] added rate limiter + click limiter to redirect --- redirect/bun.lockb | Bin 30726 -> 33229 bytes redirect/package.json | 3 +- redirect/src/index.ts | 76 ++++++++++++++++++++++++------------------ 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/redirect/bun.lockb b/redirect/bun.lockb index 0983070a445038c273edc3bc53958bb5ccae8fd5..b9abd06e3d64666dc3067b84d212645fc74dcc90 100755 GIT binary patch delta 5227 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 D5N&@M delta 3879 zcmeHKdvH`&89(RdwKvHoOA;WL&7)=Hk)(M)c9Tu^rU|s*vOx-A6ChzXVUsL`JWL2s zun9`FjM8c5b85?o4l^C;Mw~XTr80=3)v+4eVh1{fnVQnt8DfnMG96Qgg1_(Xu0_Sp z_=o=G+4;`5zw`QD=X~d$SH7ftc}_l2yfSpqbISkSrNtknf4ijf=Z#0i2iJb_XwLD@ zo5!xTT~B#!B-v%10npq`-cKPAs+7EVKlX&P8MQppgTN5A3u>h>Q#hT zfX02p{yrYI5!ej88axHu7U2Wkg@L|dyQq*tBpHho4)pi;ihF=FfYZTwUVMc2`TF_; zSa3r`=$^iT-aXxcz7_F8EXKG@@Khm4m)eG4f)Fa&3cy)rV5qRWx4%PlnS@9L&W^Ot z0kf`Yz^p^Z=Y5_2AtBCTGLMTv#q*mfD04?&iwlq5-P^mL4SoSrSjvEJxW^AX56td( z3FDZ*h{_6n8kjBVM#X&B{vmks6L+qI2ySl#=k;nLJRjTw+yb*SA(SzV4k@`cm(Yfu zv8bc}*}?rCd%9S?sH3Mao{f3TLRS>$l14a};ZbnbG8UX|cV~pkVopn%i?xSZ;w(}I zC=jp9rznW}GR>fFro04QeuDz2rzn`9tG5vxJ0>eMnP5`~5!%lJNk;N41x&h}rJzYy zi(`aXW%QJ)HaSFjsxDuo0BZE3E}$9IKFUkf)yY^P8lq$4ZZM)k)uA@PjTLB*qd}}O zLU~ELe47GEy6QyeH={?;V3JLKoo3K;h4PYhSxtdtT^&StS<4uj#LVy04BGyPHr6PH zSfdgOn00jk+o2Y13Y9|tQ#6A%iHNXGhK15affQXmf;QG9h6YVG^_M_AD}nV=7r?i? zuA!yc4pd|Gu@v|+)$k?iWXiMXvW5bvAEls0SAUEs@K{NMW?Nh=VpB$i2@bUi|LV~W z?V;5O1sCY@b(&eAtIM!!*i+C0Glqd2v|Dw^=O~z_E5A2We!5d_#a?N|pk$g%wW;3( z3JbYzwkdC<&>t|wf(^DNEIGv%Hwd(a*HNZYX(Gd^BwJ|bBB!#+LKBOeDq%aZGh+>N z-vwfG5pt})0*8s6WjIwG1Y-3RV`K>E?+#c00#stm8%)8xv}hkYbQcgi6pjEo3B>X; z=sMgx4fG&T9EVL=o=!V8r_z>A6PiiT<7@{qgJDk#4@S9@bN?0)+tUtW zNj?ylPvN76`#QqoM3gi9-)seB3NFT8p|`S%{~!Ik?t=eP^i%#Z`l0Hj&n%$I3Ky-c zFw@Zrm#n1A6&`vMsIJl_ZFHp4LyuLOX%?uOs;fL?uQJp3>@Mk`kL(`02GmyVl64fU z_Rv!{Gns2#Qm57$53RAA=^W7gq&hq_5479il8tl*=-FyBW!JjoY6{ePsHMhCuK+zr zTAhbX4l{kB&L!8!+ltGV%=*+OX=NByW5y)vq#{F5r4Yk{d&7MNG)DCdra`;i2%}Ox{Qb zTkBT?+`1hu-0lYP*~L7gL@`GSnce7X0_XE+--rrjY!lsT-JHkAycxua0(XpXff_)# z+xT7)Eb0MJ4jtOE*htB^>iCP0zo7)K2L7rv4u2~+Cj}3P(+VrWLf3;n2jaZNR&c7i zA5;(GB#87c9H3>O<)9THPU~Ai+dyp~))|RW5i3dCn(O53z8!=MjBg=P2I8!XJ4jT4 z*yc=-23i8b=c0H%lF;m%VfXTdtO;wxnz4+RJ4;&9-aS=;wUPefhIzci$LNWNA@J5#L1j(7oHs zWd${FcUyIA4l_bNar)-^=bQx}$IAB7BK#nf&TV(&EG2n0YxF6x_Cl7r?Z6w4DDsiW zVjD^KYSK%sUbi*++5NN8rmOwaXWJFoUJj91>m~ZBw_LtPA9@d2BhQAhlm6N9RhR$T zYK$r?Do1!}+~<~QH0{%5HhttP$5(1$hg$~eK!=tUeM%%Azk20x!n@^|R~FWE&P305 zlv$&1h`Jl+Cx30J?7;wASbPk9+@V>UBWEYSc+JiEtr>rg^ur?sRNARQjH}ZvU!bQu z%a!>gdbM*MzL>2$G;8!3;cQoabnyezG$ux@*XTIjw&P?tP`q(%G}xsf*?hH2 zvqm2s?=}4?`1-f53WT66Y@L^W(xoYv@Yv`&ro?8@NOxIP^o_B6={wJy|4!ZEPl6wP zja0ldG!Zv4@8=#w3DVpd^t (set.redirect = fallback_url + '/landing')) app.get('/invalid', () => 'Invalid Shortener') @@ -17,7 +31,7 @@ app.get('/robots.txt', () => Bun.file('public/robots.txt')) app.get( '/:shortenerCode', - async ({ params: { shortenerCode }, set, request }) => { + async ({ params: { shortenerCode }, set, request, cookie }) => { try { const request_domain = request.headers.get('host') const domain = request_domain !== app_url ? request_domain : null @@ -28,21 +42,6 @@ app.get( : 'x-forwarded-for' ) - const WebServiceClient = - require('@maxmind/geoip2-node').WebServiceClient - - const client = new WebServiceClient( - geoipupdate_account_id, - geoipupdate_license_key, - { host: 'geolite.info' } - ) - - const geolocation = await client.city(ip) - - const user_agent = request.headers.get('User-Agent') - - const ua_parser = new UAParser(user_agent ?? '') - const query = db .selectFrom('shortener') .selectAll('shortener') @@ -60,26 +59,15 @@ app.get( const shortener = await query.execute() - console.log('shortener', shortener) + const user_agent = request.headers.get('User-Agent') + + const ua_parser = new UAParser(user_agent ?? '') if (!shortener.length || !shortener[0].active) { set.redirect = '/invalid' return } - const visitor_data = { - shortener_id: shortener[0].id, - country: geolocation.country.names.en as string, - country_code: geolocation.country.isoCode as string, - city: geolocation.city.names.en as string, - device_type: ua_parser.getDevice().type || 'desktop', - device_vendor: ua_parser.getDevice().vendor, - browser: ua_parser.getBrowser().name, - os: ua_parser.getOS().name, - } - - await db.insertInto('visitor').values(visitor_data).execute() - if ( ua_parser.getOS().name === 'iOS' && shortener[0].ios && @@ -95,6 +83,28 @@ app.get( } else { set.redirect = shortener[0].link } + + const clickKey = `${ip}_${shortener[0].id}` + const clickLimited = clickLimiter.has(clickKey) + + if (clickLimited) return + + clickLimiter.set(clickKey, 1) + + const geolocation = await client.city(ip || '') + + const visitor_data = { + shortener_id: shortener[0].id, + country: geolocation.country!.names.en, + country_code: geolocation.country!.isoCode, + city: geolocation.city!.names.en, + device_type: ua_parser.getDevice().type || 'desktop', + device_vendor: ua_parser.getDevice().vendor, + browser: ua_parser.getBrowser().name, + os: ua_parser.getOS().name, + } + + await db.insertInto('visitor').values(visitor_data).execute() } catch (error) { console.error(error) set.redirect = fallback_url