initial import: random background api
This commit is contained in:
22
functions/api/list.js
Normal file
22
functions/api/list.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { getVariantPayload, loadManifest } from '../lib/background.js';
|
||||
|
||||
export async function onRequestGet(context) {
|
||||
const { request } = context;
|
||||
const manifest = await loadManifest(request);
|
||||
|
||||
return Response.json(
|
||||
{
|
||||
generated_at: manifest?.generated_at || null,
|
||||
variants: {
|
||||
mobile: summarizeVariant(manifest, 'mobile'),
|
||||
desktop: summarizeVariant(manifest, 'desktop'),
|
||||
},
|
||||
},
|
||||
{ headers: { 'Cache-Control': 'no-store, max-age=0', 'Access-Control-Allow-Origin': '*' } },
|
||||
);
|
||||
}
|
||||
|
||||
function summarizeVariant(manifest, variant) {
|
||||
const { folder, images } = getVariantPayload(manifest, variant);
|
||||
return { folder, count: images.length, images };
|
||||
}
|
||||
36
functions/api/random.js
Normal file
36
functions/api/random.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import { buildAssetUrl, detectVariant, getVariantPayload, loadManifest, pickRandom } from '../lib/background.js';
|
||||
|
||||
export async function onRequestGet(context) {
|
||||
const { request } = context;
|
||||
const url = new URL(request.url);
|
||||
const format = (url.searchParams.get('format') || 'redirect').toLowerCase();
|
||||
const manifest = await loadManifest(request);
|
||||
const variant = detectVariant(request);
|
||||
const { folder, images } = getVariantPayload(manifest, variant);
|
||||
|
||||
if (!images.length) {
|
||||
return Response.json(
|
||||
{ error: 'no images available', variant, folder },
|
||||
{ status: 404, headers: { 'Cache-Control': 'no-store, max-age=0', 'Access-Control-Allow-Origin': '*' } },
|
||||
);
|
||||
}
|
||||
|
||||
const filename = pickRandom(images);
|
||||
const assetUrl = buildAssetUrl(request, folder, filename);
|
||||
|
||||
if (format === 'json') {
|
||||
return Response.json(
|
||||
{ variant, folder, filename, url: assetUrl, count: images.length },
|
||||
{ headers: { 'Cache-Control': 'no-store, max-age=0', 'Access-Control-Allow-Origin': '*' } },
|
||||
);
|
||||
}
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: assetUrl,
|
||||
'Cache-Control': 'no-store, max-age=0',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
});
|
||||
}
|
||||
52
functions/lib/background.js
Normal file
52
functions/lib/background.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const FALLBACK_MANIFEST = {
|
||||
variants: {
|
||||
mobile: { folder: 'mobile-image', images: [] },
|
||||
desktop: { folder: 'desketop-image', images: [] },
|
||||
},
|
||||
};
|
||||
|
||||
export async function loadManifest(request) {
|
||||
try {
|
||||
const response = await fetch(new URL('/manifest.json', request.url));
|
||||
if (!response.ok) return FALLBACK_MANIFEST;
|
||||
return await response.json();
|
||||
} catch {
|
||||
return FALLBACK_MANIFEST;
|
||||
}
|
||||
}
|
||||
|
||||
export function detectVariant(request) {
|
||||
const url = new URL(request.url);
|
||||
const params = url.searchParams;
|
||||
const mode = (params.get('mode') || 'auto').toLowerCase();
|
||||
const orientation = (params.get('orientation') || '').toLowerCase();
|
||||
const device = (params.get('device') || '').toLowerCase();
|
||||
const ua = (request.headers.get('user-agent') || '').toLowerCase();
|
||||
const chMobile = request.headers.get('sec-ch-ua-mobile');
|
||||
|
||||
if (mode === 'mobile' || mode === 'desktop') return mode;
|
||||
if (orientation === 'portrait' || orientation === 'vertical') return 'mobile';
|
||||
if (orientation === 'landscape' || orientation === 'horizontal') return 'desktop';
|
||||
if (device === 'mobile' || device === 'desktop') return device;
|
||||
if (chMobile === '?1') return 'mobile';
|
||||
if (chMobile === '?0') return 'desktop';
|
||||
if (['mobi', 'android', 'iphone', 'ipad', 'ipod', 'phone'].some((token) => ua.includes(token))) return 'mobile';
|
||||
return 'desktop';
|
||||
}
|
||||
|
||||
export function getVariantPayload(manifest, variant) {
|
||||
const data = manifest?.variants?.[variant] || {};
|
||||
const folder = data.folder || (variant === 'mobile' ? 'mobile-image' : 'desketop-image');
|
||||
const images = Array.isArray(data.images)
|
||||
? data.images.filter((name) => typeof name === 'string' && name.toLowerCase().endsWith('.webp'))
|
||||
: [];
|
||||
return { folder, images };
|
||||
}
|
||||
|
||||
export function buildAssetUrl(request, folder, filename) {
|
||||
return new URL(`/${folder}/${filename}`, request.url).toString();
|
||||
}
|
||||
|
||||
export function pickRandom(items) {
|
||||
return items[Math.floor(Math.random() * items.length)];
|
||||
}
|
||||
Reference in New Issue
Block a user