Add reviews and support tutorial management

This commit is contained in:
Codex
2026-04-28 00:55:07 +08:00
parent 4e3fb17202
commit c8ccaa9925
78 changed files with 5231 additions and 858 deletions
+95
View File
@@ -0,0 +1,95 @@
import { NextRequest } from 'next/server'
import { eversoloPasteURLAllowList } from '@/payload/upload-paste-url-allow-list'
export const runtime = 'nodejs'
const retryDelays = [0, 300, 800, 1500]
const fetchTimeoutMs = 10000
function isAllowedPasteURL(value: string) {
try {
const url = new URL(value)
return eversoloPasteURLAllowList.some((allowItem) => {
return url.protocol === `${allowItem.protocol}:` && url.hostname === allowItem.hostname
})
} catch {
return false
}
}
function wait(delay: number) {
return new Promise((resolve) => {
setTimeout(resolve, delay)
})
}
async function fetchWithRetry(src: string) {
let lastError: unknown
for (const [index, delay] of retryDelays.entries()) {
if (delay > 0) {
await wait(delay)
}
const controller = new AbortController()
const timeout = setTimeout(() => {
controller.abort()
}, fetchTimeoutMs)
try {
const response = await fetch(src, {
cache: 'no-store',
headers: {
'user-agent':
'Mozilla/5.0 (compatible; EversoloWebPasteImporter/1.0; +https://www.eversolo.com)',
},
redirect: 'follow',
signal: controller.signal,
})
if (response.ok || response.status < 500 || index === retryDelays.length - 1) {
return response
}
} catch (error) {
lastError = error
} finally {
clearTimeout(timeout)
}
}
throw lastError
}
export async function GET(request: NextRequest) {
const src = request.nextUrl.searchParams.get('src')
if (!src || !isAllowedPasteURL(src)) {
return new Response('URL is not allowed.', { status: 400 })
}
let upstream: Response
try {
upstream = await fetchWithRetry(src)
} catch {
return new Response('Failed to fetch the file.', { status: 502 })
}
if (!upstream.ok) {
return new Response('Failed to fetch the file.', { status: upstream.status })
}
const headers = new Headers()
const contentType = upstream.headers.get('content-type')
const contentLength = upstream.headers.get('content-length')
if (contentType) headers.set('content-type', contentType)
if (contentLength) headers.set('content-length', contentLength)
return new Response(await upstream.arrayBuffer(), {
headers,
status: 200,
})
}