This commit is contained in:
retoor 2025-12-04 20:44:39 +01:00
parent 8e6feefdaf
commit b9ca88702c
3 changed files with 44 additions and 5 deletions

View File

@ -5,7 +5,7 @@
* @keywords api, client, devrant, http, fetch * @keywords api, client, devrant, http, fetch
*/ */
const API_BASE_URL = window.location.hostname === 'localhost' ? '/api/' : 'https://dr.molodetz.nl/api/'; const API_BASE_URL = '/api/';
const APP_ID = 3; const APP_ID = 3;
class ApiClient { class ApiClient {

View File

@ -24,7 +24,7 @@ function getYoutubeVideoId(url) {
} }
function getYoutubeThumbnail(videoId) { function getYoutubeThumbnail(videoId) {
return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`; return `/api/proxy-image?url=${encodeURIComponent(`https://img.youtube.com/vi/${videoId}/hqdefault.jpg`)}`;
} }
function getYoutubeEmbedUrl(videoId) { function getYoutubeEmbedUrl(videoId) {
@ -86,17 +86,18 @@ function isDevrantImageUrl(url) {
} }
function buildDevrantImageUrl(imagePath) { function buildDevrantImageUrl(imagePath) {
if (!imagePath) return null;
if (imagePath.startsWith('http')) { if (imagePath.startsWith('http')) {
return imagePath; return `/api/proxy-image?url=${encodeURIComponent(imagePath)}`;
} }
return `https://img.devrant.com/${imagePath}`; return `/api/proxy-image?url=${encodeURIComponent(`https://img.devrant.com/${imagePath}`)}`;
} }
function buildAvatarUrl(avatar) { function buildAvatarUrl(avatar) {
if (!avatar || !avatar.i) { if (!avatar || !avatar.i) {
return null; return null;
} }
return `https://avatars.devrant.com/${avatar.i}`; return `/api/proxy-image?url=${encodeURIComponent(`https://avatars.devrant.com/${avatar.i}`)}`;
} }
export { export {

View File

@ -70,7 +70,45 @@ async def proxy_request(request, method, max_retries=10, retry_delay=2):
response = web.json_response({'success': False, 'error': 'Connection failed'}, status=503) response = web.json_response({'success': False, 'error': 'Connection failed'}, status=503)
return add_cors_headers(response) return add_cors_headers(response)
ALLOWED_IMAGE_HOSTS = [
'img.devrant.com',
'avatars.devrant.com',
'img.youtube.com',
'i.ytimg.com',
]
async def handle_image_proxy(request):
url = request.query.get('url')
if not url:
return add_cors_headers(web.Response(status=400, text='Missing url parameter'))
try:
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_IMAGE_HOSTS:
return add_cors_headers(web.Response(status=403, text='Host not allowed'))
except Exception:
return add_cors_headers(web.Response(status=400, text='Invalid URL'))
for attempt in range(3):
try:
async with ClientSession() as session:
async with session.get(url) as resp:
if resp.status != 200:
return add_cors_headers(web.Response(status=resp.status))
data = await resp.read()
content_type = resp.content_type or 'image/png'
response = web.Response(body=data, content_type=content_type)
response.headers['Cache-Control'] = 'public, max-age=86400'
return add_cors_headers(response)
except Exception:
if attempt < 2:
await asyncio.sleep(1)
return add_cors_headers(web.Response(status=503, text='Failed to fetch image'))
async def handle_api_get(request): async def handle_api_get(request):
if request.path == '/api/proxy-image':
return await handle_image_proxy(request)
return await proxy_request(request, 'GET') return await proxy_request(request, 'GET')
async def handle_api_post(request): async def handle_api_post(request):