s3.1080p.hk 已永久停用且文件不可恢复。首页查询排除旧源并统一分页统计,历史图册、聊天和画布保留记录并显示失效说明;下载和代理返回 410,停止旧源上传与归档。使用本地黑白 Logo 适配主题。 提供保存原配置后停用旧存储、取消失效精选、替换 Logo 的整理脚本。七牛 10 月 14 日上传核查记录见 docs/retired-storage.md。 验证:7 项旧存储检查、41 项下载和归档回归测试、TypeScript 检查通过。
77 lines
4.3 KiB
TypeScript
77 lines
4.3 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import { serverLoader } from '../helpers/canvas-download-server-loader';
|
|
import { isRetiredStorageUrl, RETIRED_STORAGE_MESSAGE } from '@/lib/storage/retired-storage';
|
|
import { resolveSiteBranding, DEFAULT_SITE_LOGO, DEFAULT_SITE_LOGO_DARK } from '@/lib/site-branding';
|
|
|
|
const retired = 'https://s3.1080p.hk/jsxiangche/2025/01/image.png';
|
|
const current = 'https://scqnycdn.jiansun.vip/uploads/current.png';
|
|
|
|
test('retired URLs include stored proxy URLs but do not include live storage', () => {
|
|
for (const url of [retired, retired.replace('https:', 'http:'), '/api/image-proxy?url=' + encodeURIComponent(retired)]) {
|
|
expect(isRetiredStorageUrl(url)).toBe(true);
|
|
}
|
|
for (const url of [current, '/brand/logo-black.png', 'https://s3.jiansun.vip/gongxiangpan/a.png', 'https://example.com/s3.1080p.hk/a.png']) {
|
|
expect(isRetiredStorageUrl(url)).toBe(false);
|
|
}
|
|
});
|
|
|
|
test('original image proxy returns permanent failure without redirecting or fetching', async () => {
|
|
let fetches = 0;
|
|
const route = serverLoader({ 'next/server': { NextResponse }, fetch: () => { fetches++; throw Error('Unexpected network'); } })('app/api/image-proxy/route.ts');
|
|
const response = await route.GET(new NextRequest('https://ai.yydd.ai/api/image-proxy?original=1&url=' + encodeURIComponent(retired), {headers:{'sec-fetch-dest':'image'}}));
|
|
expect(response.status).toBe(410);
|
|
expect(await response.json()).toMatchObject({code:'storage_retired',error:RETIRED_STORAGE_MESSAGE});
|
|
expect(fetches).toBe(0);
|
|
});
|
|
|
|
test('byte cache never fetches retired resources', async () => {
|
|
let fetches = 0;
|
|
const module = serverLoader({ fetch: () => { fetches++; throw Error('Unexpected network'); } })('lib/storage/image-byte-cache.ts');
|
|
await expect(module.fetchImageBytes(retired)).rejects.toThrow('HTTP 410');
|
|
expect(fetches).toBe(0);
|
|
});
|
|
|
|
test('retired sources cannot enter the archive queue', async () => {
|
|
let inserts = 0;
|
|
const module = serverLoader({
|
|
'@/lib/db': {db:{insert:()=>{inserts++;throw Error('Unexpected insert');}}},
|
|
'@/lib/db/schema': {},
|
|
})('lib/storage/image-archive-db.ts');
|
|
expect(await module.enqueueImageArchiveTask({sourceUrl:retired})).toBeNull();
|
|
expect(inserts).toBe(0);
|
|
});
|
|
|
|
test('single downloads reject retired sources before async preparation, while live candidates remain usable', async () => {
|
|
const fetched: string[] = [];
|
|
const route = serverLoader({
|
|
'@/app/(auth)/auth':{auth:async()=>({user:{id:'owner'}})},
|
|
'next/server':{NextResponse,after:()=>{}},
|
|
'@/lib/storage/canvas-cdn-download':{},
|
|
'@/lib/storage/image-download-preparation':{},
|
|
'@/lib/canvas/photoshop-safe-download':{
|
|
fetchCanvasDownloadAsset:async({file}:any)=>{fetched.push(file.url);return {ok:true,bytes:Buffer.from('original'),filename:'original.png',contentType:'image/png'};},
|
|
sanitizeCanvasDownloadFileName:()=> 'original', buildAttachmentContentDisposition:()=> 'attachment',
|
|
},
|
|
})('app/api/canvas/download-image/route.ts');
|
|
const request = (body:unknown) => new NextRequest('https://ai.yydd.ai/api/canvas/download-image',{method:'POST',body:JSON.stringify(body)});
|
|
const failure = await route.POST(request({url:retired,preparation:'async',preferDirect:true}));
|
|
expect(failure.status).toBe(410);
|
|
expect(fetched).toEqual([]);
|
|
const success = await route.POST(request({urls:[retired,current]}));
|
|
expect(success.status).toBe(200);
|
|
expect(fetched).toEqual([current]);
|
|
expect(await success.text()).toBe('original');
|
|
});
|
|
|
|
test('ZIP asset preparation reports permanent failure without retrying the source', async () => {
|
|
const module = serverLoader({})( 'lib/canvas/photoshop-safe-download.ts');
|
|
const result = await module.fetchCanvasDownloadAsset({file:{url:retired},index:0,requestOrigin:'https://ai.yydd.ai',cookie:''});
|
|
expect(result).toMatchObject({ok:false,error:RETIRED_STORAGE_MESSAGE});
|
|
});
|
|
|
|
test('branding replaces unavailable storage with theme variants and preserves custom live branding', () => {
|
|
expect(resolveSiteBranding({siteLogo:retired,siteLogoDark:retired,favicon:retired})).toEqual({siteLogo:DEFAULT_SITE_LOGO,siteLogoDark:DEFAULT_SITE_LOGO_DARK,favicon:DEFAULT_SITE_LOGO});
|
|
expect(resolveSiteBranding({siteLogo:current,siteLogoDark:current}).siteLogo).toBe(current);
|
|
});
|