1010from django .shortcuts import get_object_or_404
1111from django .utils .crypto import get_random_string
1212from django .core .serializers .json import DjangoJSONEncoder
13+ from django .contrib .auth import authenticate , login , logout
14+ from functools import wraps
1315
14- from .models import Url
16+ from .models import Url , User
1517
1618# 自定義 JSON 編碼器類別
1719class CustomJSONEncoder (DjangoJSONEncoder ):
@@ -41,63 +43,124 @@ class UrlSchema(Schema):
4143class ErrorSchema (Schema ):
4244 message : str
4345
44- # BASE62 編碼的函式
45- def base62_encode (num ):
46- base62 = string .digits + string .ascii_letters
47- if num == 0 :
48- return base62 [0 ]
49- array = []
50- while num :
51- num , rem = divmod (num , 62 )
52- array .append (base62 [rem ])
53- array .reverse ()
54- return '' .join (array )
46+ # 定義 User 的 Schema 類別
47+ class UserSchema (Schema ):
48+ username : str
49+ password : str
50+
51+ # 定義 User 註冊或登入回應的 Schema 類別
52+ class UserResponseSchema (Schema ):
53+ username : str
54+
55+ # 一般使用者的權限檢查裝飾器
56+ def user_is_authenticated (func ):
57+ @wraps (func )
58+ def wrapper (request , * args , ** kwargs ):
59+ if not request .user .is_authenticated :
60+ return api .create_response (request , {"message" : "您必須登錄才能執行此操作。" }, status = 403 )
61+ return func (request , * args , ** kwargs )
62+ return wrapper
63+
64+ # 管理員的權限檢查裝飾器
65+ def user_is_admin (func ):
66+ @wraps (func )
67+ def wrapper (request , * args , ** kwargs ):
68+ if not request .user .is_authenticated or request .user .user_type != 2 :
69+ return api .create_response (request , {"message" : "您必須是管理員才能執行此操作。" }, status = 403 )
70+ return func (request , * args , ** kwargs )
71+ return wrapper
72+
73+ # 使用者是否可以編輯短網址的裝飾器
74+ def user_can_edit_url (func ):
75+ @wraps (func )
76+ def wrapper (request , short_string , * args , ** kwargs ):
77+ url = Url .objects .filter (short_string = short_string ).first ()
78+ if not url :
79+ return api .create_response (request , {"message" : "找不到此短網址。" }, status = 404 )
80+ if url .user != request .user and request .user .user_type != 2 :
81+ return api .create_response (request , {"message" : "您沒有權限編輯此短網址。" }, status = 403 )
82+ return func (request , short_string , * args , ** kwargs )
83+ return wrapper
5584
5685# 產生隨機短網址的函式
57- def generator_short_url (orign_url : str , length = 6 ):
58- hash_value = abs (hash (orign_url )) # 取得原網址的 hash 值
59- encode = base62_encode (hash_value ) # 將 hash 值轉換為 BASE62 編碼
60- if len (encode ) < length :
61- encode += get_random_string (length - len (encode ), string .ascii_letters + string .digits )
62- return encode
63- return encode [:length ]
86+ def generator_short_url (length = 6 ):
87+ char = string .ascii_letters + string .digits
88+ while True :
89+ short_url = '' .join (random .choices (char , k = length ))
90+ if not Url .objects .filter (short_url = short_url ).exists ():
91+ return short_url # 如果短網址不存在 DB 中,則回傳此短網址
6492
6593# 處理短網址域名的函式
6694def handle_domain (request , short_string ):
6795 domain = request .build_absolute_uri ('/' )[:- 1 ].strip ('/' )
6896 return f'{ domain } /{ short_string } '
6997
7098# 建立短網址物件的函式
71- def create_url_entry (orign_url : HttpUrl , short_string : str , short_url : HttpUrl , expire_date : Optional [datetime .datetime ] = None ) -> Url :
99+ def create_url_entry (orign_url : HttpUrl , short_string : str , short_url : HttpUrl , expire_date : Optional [datetime .datetime ] = None , user = None ) -> Url :
72100 return Url .objects .create (
73101 orign_url = str (orign_url ),
74102 short_string = short_string ,
75103 short_url = str (short_url ),
76104 create_date = datetime .datetime .now (),
77- expire_date = expire_date
105+ expire_date = expire_date ,
106+ user = user
78107 )
79108
80109# GET : 首頁 API /
81110@api .get ("/" , response = {200 : ErrorSchema })
82111def index (request ):
83112 return 200 , {"message" : "已與 RyoURL 建立連線。" }
84113
114+ # POST : 註冊 API /register
115+ @api .post ("register" , response = {200 : UserResponseSchema , 400 : ErrorSchema })
116+ def register_user (request , user_data : UserSchema ):
117+ try :
118+ user = User .objects .create_user (
119+ username = user_data .username ,
120+ password = user_data .password
121+ )
122+ return 200 , {"username" : user .username }
123+ except :
124+ return 400 , {"message" : "註冊失敗" }
125+
126+ # POST : 登入 API /login
127+ @api .post ("login" , response = {200 : UserResponseSchema , 400 : ErrorSchema })
128+ def login_user (request , user_data : UserSchema ):
129+ user = authenticate (
130+ username = user_data .username ,
131+ password = user_data .password
132+ )
133+ if user :
134+ login (request , user )
135+ return 200 , {"username" : user .username }
136+ else :
137+ return 400 , {"message" : "登入失敗" }
138+
139+ # POST : 登出 API /logout
140+ @api .post ("logout" , response = {200 : ErrorSchema })
141+ @user_is_authenticated
142+ def logout_user (request ):
143+ logout (request )
144+ return 200 , {"message" : "登出成功" }
145+
85146# POST : 新增短網址 API /short_url
86147@api .post ("short-url" , response = {200 : UrlSchema , 404 : ErrorSchema })
87148def create_short_url (request , orign_url : HttpUrl , expire_date : Optional [datetime .datetime ] = None ):
88- short_string = generator_short_url (orign_url )
149+ short_string = generator_short_url ()
89150 short_url = HttpUrl (handle_domain (request , short_string ))
90- url = create_url_entry (orign_url , short_string , short_url , expire_date )
151+ user = request .user if request .user .is_authenticated else None
152+ url = create_url_entry (orign_url , short_string , short_url , expire_date , user = user )
91153 return 200 , url
92154
93155# POST : 新增自訂短網址 API /custom_url
94156@api .post ("custom-url" , response = {200 : UrlSchema , 403 : ErrorSchema })
157+ @user_is_authenticated
95158def create_custom_url (request , orign_url : HttpUrl , short_string : str , expire_date : Optional [datetime .datetime ] = None ):
96159 short_url = HttpUrl (handle_domain (request , short_string ))
97160 if Url .objects .filter (short_url = str (short_url )).exists ():
98161 return 403 , {"message" : "自訂短網址已存在,請更換其他短網址。" }
99162 else :
100- url = create_url_entry (orign_url , short_string , short_url , expire_date )
163+ url = create_url_entry (orign_url , short_string , short_url , expire_date , user = request . user )
101164 return 200 , url
102165
103166# GET : 以縮短網址字符查詢原網址 API /orign_url/{short_string}
@@ -106,21 +169,32 @@ def get_short_url(request, short_string: str):
106169 url = get_object_or_404 (Url , short_string = short_string )
107170 return 200 , url
108171
172+ # GET : 查詢自己所有短網址 API /all_myurl
173+ @api .get ('all-myurl' , response = List [UrlSchema ])
174+ @user_is_authenticated
175+ def get_all_myurl (request ):
176+ url = Url .objects .filter (user = request .user )
177+ return url
178+
109179# GET : 查詢所有短網址 API /all_url
110180@api .get ('all-url' , response = List [UrlSchema ])
181+ @user_is_admin
111182def get_all_url (request ):
112183 url = Url .objects .all ()
113184 return url
114185
115186# DELETE : 刪除短網址 API /short_url/{short_string}
116187@api .delete ('short-url/{short_string}' , response = {200 : ErrorSchema })
188+ @user_is_authenticated
189+ @user_can_edit_url
117190def delete_short_url (request , short_string : str ):
118191 url = get_object_or_404 (Url , short_string = short_string )
119192 url .delete ()
120193 return 200 , {"message" : "成功刪除!" }
121194
122195# DELETE : 刪除過期短網址 API /expire_url
123196@api .delete ('expire-url' , response = {200 : ErrorSchema })
197+ @user_is_admin
124198def delete_expire_url (request ):
125199 url = Url .objects .filter (expire_date__lt = datetime .datetime .now ())
126200 url .delete ()
0 commit comments