@@ -21,81 +21,98 @@ class ImagesController < BaseController
2121 # @return [Binary] The image data with appropriate content-type
2222 def proxy
2323 url = params [ :url ]
24+ return render_invalid_url unless valid_image_url? ( url )
2425
25- # Validate URL
26- unless valid_image_url? ( url )
27- render json : { error : 'Invalid or unauthorized URL' } , status : :bad_request
28- return
29- end
30-
31- # Try to get from cache first
32- cache_key = "image_proxy:#{ Digest ::MD5 . hexdigest ( url ) } "
33- cached_data = Rails . cache . fetch ( cache_key , expires_in : 7 . days ) do
34- fetch_external_image ( url )
35- end
36-
37- if cached_data [ :error ]
38- render json : { error : cached_data [ :error ] } , status : :bad_gateway
39- return
40- end
26+ cached_data = fetch_cached_image ( url )
27+ return render_fetch_error ( cached_data [ :error ] ) if cached_data [ :error ]
4128
42- # Send the cached image
43- send_data cached_data [ :body ] ,
44- type : cached_data [ :content_type ] ,
45- disposition : 'inline' ,
46- filename : File . basename ( URI . parse ( url ) . path )
29+ send_image_data ( cached_data , url )
4730 rescue StandardError => e
48- Rails . logger . error ( "Image proxy error: #{ e . message } " )
49- render json : { error : 'Failed to fetch image' } , status : :internal_server_error
31+ handle_proxy_error ( e )
5032 end
5133
5234 private
5335
36+ ALLOWED_DOMAINS = [
37+ 'upload.wikimedia.org' ,
38+ 'ddragon.leagueoflegends.com' ,
39+ 'raw.communitydragon.org' ,
40+ 'static.wikia.nocookie.net' ,
41+ 'commons.wikimedia.org'
42+ ] . freeze
43+
44+ HTTP_TIMEOUT_OPTIONS = { open_timeout : 5 , read_timeout : 10 } . freeze
45+
5446 # Validates if the URL is from an allowed domain
5547 def valid_image_url? ( url )
5648 return false if url . blank?
5749
5850 uri = URI . parse ( url )
59- allowed_domains = [
60- 'upload.wikimedia.org' ,
61- 'ddragon.leagueoflegends.com' ,
62- 'raw.communitydragon.org' ,
63- 'static.wikia.nocookie.net' ,
64- 'commons.wikimedia.org'
65- ]
66-
67- allowed_domains . any? { |domain | uri . host &.include? ( domain ) }
51+ ALLOWED_DOMAINS . any? { |domain | uri . host &.include? ( domain ) }
6852 rescue URI ::InvalidURIError
6953 false
7054 end
7155
56+ # Fetches image from cache or external source
57+ def fetch_cached_image ( url )
58+ cache_key = "image_proxy:#{ Digest ::SHA256 . hexdigest ( url ) } "
59+ Rails . cache . fetch ( cache_key , expires_in : 7 . days ) do
60+ fetch_external_image ( url )
61+ end
62+ end
63+
7264 # Fetches image from external URL
7365 def fetch_external_image ( url )
7466 uri = URI . parse ( url )
67+ response = perform_http_request ( uri )
68+ process_http_response ( response )
69+ rescue StandardError => e
70+ Rails . logger . error ( "Failed to fetch image from #{ url } : #{ e . message } " )
71+ { error : e . message }
72+ end
7573
76- Net ::HTTP . start ( uri . host , uri . port , use_ssl : uri . scheme == 'https' ,
77- open_timeout : 5 , read_timeout : 10 ) do |http |
74+ # Performs HTTP request to fetch image
75+ def perform_http_request ( uri )
76+ Net ::HTTP . start ( uri . host , uri . port ,
77+ use_ssl : uri . scheme == 'https' ,
78+ **HTTP_TIMEOUT_OPTIONS ) do |http |
7879 request = Net ::HTTP ::Get . new ( uri . request_uri )
7980 request [ 'User-Agent' ] = 'ProStaff-API/1.0 (Image Proxy)'
81+ http . request ( request )
82+ end
83+ end
8084
81- response = http . request ( request )
82-
83- if response . is_a? ( Net ::HTTPSuccess )
84- {
85- body : response . body ,
86- content_type : response [ 'content-type' ] || 'image/png'
87- }
88- else
89- {
90- error : "External service returned #{ response . code } " ,
91- content_type : 'text/plain' ,
92- body : ''
93- }
94- end
85+ # Processes HTTP response
86+ def process_http_response ( response )
87+ if response . is_a? ( Net ::HTTPSuccess )
88+ { body : response . body , content_type : response [ 'content-type' ] || 'image/png' }
89+ else
90+ { error : "External service returned #{ response . code } " , content_type : 'text/plain' , body : '' }
9591 end
96- rescue StandardError => e
97- Rails . logger . error ( "Failed to fetch image from #{ url } : #{ e . message } " )
98- { error : e . message }
92+ end
93+
94+ # Renders invalid URL error
95+ def render_invalid_url
96+ render json : { error : 'Invalid or unauthorized URL' } , status : :bad_request
97+ end
98+
99+ # Renders fetch error
100+ def render_fetch_error ( error )
101+ render json : { error : error } , status : :bad_gateway
102+ end
103+
104+ # Sends image data to client
105+ def send_image_data ( cached_data , url )
106+ send_data cached_data [ :body ] ,
107+ type : cached_data [ :content_type ] ,
108+ disposition : 'inline' ,
109+ filename : File . basename ( URI . parse ( url ) . path )
110+ end
111+
112+ # Handles proxy errors
113+ def handle_proxy_error ( error )
114+ Rails . logger . error ( "Image proxy error: #{ error . message } " )
115+ render json : { error : 'Failed to fetch image' } , status : :internal_server_error
99116 end
100117 end
101118 end
0 commit comments