1
+ from typing import Annotated , Optional
2
+ from fastapi import Request , Depends
3
+ from fastapi .security .utils import get_authorization_scheme_param
4
+ from sqlalchemy import select
5
+ from slowapi import Limiter
6
+ from slowapi .util import get_remote_address
7
+
8
+ from core .database import DBConnect
9
+ from core .models import Config , Member
10
+ from api .settings import api_settings
11
+ from api .v1 .auth import oauth2_scheme
12
+ from api .v1 .auth .jwt import JWT
13
+ from api .v1 .service .member import MemberServiceAPI
14
+ from api .v1 .models .auth import TokenPayload
15
+
16
+
17
+ def get_request_member (
18
+ token : Annotated [str , Depends (oauth2_scheme )],
19
+ member_service : Annotated [MemberServiceAPI , Depends ()]
20
+ ) -> Optional [Member ]:
21
+ """
22
+ REST_API 요청 시 JWT 토큰을 통해 사용자 정보를 가져오는 함수
23
+
24
+ Args:
25
+ token (Annotated[str, Depends(oauth2_scheme)]): JWT 토큰
26
+ member_service (Annotated[MemberServiceAPI, Depends()]): 사용자 정보 서비스
27
+
28
+ Returns:
29
+ Member: 사용자 정보 객체 또는 None
30
+ """
31
+ payload : TokenPayload = JWT .decode_token (
32
+ token ,
33
+ api_settings .ACCESS_TOKEN_SECRET_KEY
34
+ )
35
+
36
+ mb_id : str = payload .sub
37
+ if mb_id is None :
38
+ return None
39
+
40
+ member = member_service .get_member (mb_id )
41
+ return member
42
+
43
+
44
+ def limiter_key_func (request : Request ) -> Optional [str ]:
45
+ """
46
+ Limiter 인스턴스 생성시 key_func 인자에 제공될 함수.
47
+ None으로 반환되는 IP 주소(관리자 IP)는 요청 제한을 하지 않는다.
48
+
49
+ Args:
50
+ request (Request): FastAPI Request 객체
51
+
52
+ Returns:
53
+ Optional[str]: 요청 제한 IP 주소 또는 None
54
+ """
55
+ authorization = request .headers .get ("Authorization" )
56
+ scheme , token = get_authorization_scheme_param (authorization )
57
+
58
+ if not authorization or scheme .lower () != "bearer" :
59
+ return get_remote_address (request )
60
+
61
+ with DBConnect ().sessionLocal () as db :
62
+ member_service = MemberServiceAPI (request , db )
63
+ cf_admin = db .scalar (select (Config )).cf_admin
64
+
65
+ member = get_request_member (token , member_service )
66
+ if member .mb_id == cf_admin :
67
+ return None
68
+
69
+ return get_remote_address (request )
70
+
71
+
72
+ def get_cf_delay_sec_from_db ():
73
+ """
74
+ 데이터베이스에서 cf_delay_sec 값을 가져와서
75
+ Limiter 인스턴스 생성시 사용할 제한 표현식을 반환하는 함수
76
+ "n/t time" 형식으로 반환
77
+ - t시간 (시간 단위는 time) 동안 n번의 요청을 허용
78
+ - time: second, minute, hour, day, month, year
79
+ - documentation: https://limits.readthedocs.io/en/stable/quickstart.html#rate-limit-string-notation
80
+ """
81
+ with DBConnect ().sessionLocal () as db :
82
+ cf_delay_sec = db .scalar (select (Config )).cf_delay_sec
83
+ limiter_expr = f"1/{ cf_delay_sec } second"
84
+ return limiter_expr
85
+
86
+
87
+ # 요청 제한 limiter 인스턴스 생성
88
+ limiter = Limiter (key_func = limiter_key_func )
89
+
90
+
91
+ @limiter .limit (
92
+ get_cf_delay_sec_from_db ,
93
+ error_message = "너무 빠른 시간내에 게시글을 연속해서 올릴 수 없습니다." ,
94
+ )
95
+ def validate_slowapi_create_post (request : Request ):
96
+ """
97
+ slowapi의 Limiter를 통해 게시글 생성 API 요청 제한 시간을 검증하는 함수
98
+
99
+ Args:
100
+ request (Request): FastAPI Request 객체
101
+ """
102
+ pass
0 commit comments