feat: add Flathub mirror support

This commit is contained in:
xixu-me committed 2026-03-14 16:23:38 +08:00
1 parent 6c7b15454d
commit 88c351a9ac
10 files changed
+474 -19

No files matched your search

+8
View File
@@ -93,6 +93,14 @@ describe('Xget Core Functionality', () => {
expect(response.status).not.toBe(400);
});
it('should handle Flathub URLs correctly', async () => {
const testUrl = 'https://example.com/flathub/repo/summary';
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
// Should attempt to proxy to Flathub
expect(response.status).not.toBe(400);
});
it('should not treat nested /v2/ path segments as container registry requests', async () => {
const testUrl = 'https://example.com/gh/microsoft/vscode/releases/download/v2/file.tar.gz';
const response = await SELF.fetch(testUrl, { method: 'HEAD' });
+56
View File
@@ -0,0 +1,56 @@
import { describe, expect, it } from 'vitest';
import { PLATFORMS, transformPath } from '../../src/config/platforms.js';
describe('Flathub Platform Configuration', () => {
it('should have Flathub platform configured', () => {
expect(PLATFORMS.flathub).toBe('https://dl.flathub.org');
});
it('should transform Flathub repository paths correctly', () => {
const testCases = [
{
input: '/flathub/repo/summary',
expected: '/repo/summary',
description: 'repository summary'
},
{
input: '/flathub/repo/summary.sig',
expected: '/repo/summary.sig',
description: 'repository summary signature'
},
{
input: '/flathub/repo/flathub.flatpakrepo',
expected: '/repo/flathub.flatpakrepo',
description: 'remote descriptor'
},
{
input: '/flathub/repo/appstream/org.gnome.gedit.flatpakref',
expected: '/repo/appstream/org.gnome.gedit.flatpakref',
description: 'application reference'
},
{
input: '/flathub/repo/objects/12/34567890abcdef.filez',
expected: '/repo/objects/12/34567890abcdef.filez',
description: 'content-addressed object'
},
{
input: '/flathub/repo/deltas/ABCD.superblock',
expected: '/repo/deltas/ABCD.superblock',
description: 'static delta'
}
];
testCases.forEach(({ input, expected, description }) => {
const result = transformPath(input, 'flathub');
expect(result, `Failed for ${description}: ${input}`).toBe(expected);
});
});
it('should handle root path correctly', () => {
expect(transformPath('/flathub/', 'flathub')).toBe('/');
});
it('should preserve already transformed Flathub repository paths', () => {
expect(transformPath('/repo/summary', 'flathub')).toBe('/repo/summary');
});
});
+114
View File
@@ -0,0 +1,114 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import worker from '../../src/index.js';
/** @type {ExecutionContext} */
const executionContext = {
waitUntil() {},
passThroughOnException() {}
};
/**
* Reads a response body as UTF-8 text without relying on the response MIME type.
* @param {Response} response
* @returns {Promise<string>} Decoded UTF-8 response text.
*/
async function readUtf8Text(response) {
return new TextDecoder().decode(await response.arrayBuffer());
}
describe('Flathub Response Rewriting', () => {
beforeEach(() => {
vi.stubGlobal('caches', {
default: {
match: vi.fn(async () => null),
put: vi.fn(async () => undefined)
}
});
});
afterEach(() => {
vi.unstubAllGlobals();
vi.restoreAllMocks();
});
it('rewrites .flatpakrepo URLs to stay on the Xget mirror', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
[
'[Flatpak Repo]',
'Url=https://dl.flathub.org/repo/',
'Icon=https://dl.flathub.org/repo/logo.svg',
'Homepage=https://flathub.org/'
].join('\n'),
{
status: 200,
headers: { 'Content-Type': 'application/octet-stream' }
}
)
);
const response = await worker.fetch(
new Request('https://example.com/flathub/repo/flathub.flatpakrepo'),
{},
executionContext
);
expect(response.status).toBe(200);
const body = await readUtf8Text(response);
expect(body).toContain('Url=https://example.com/flathub/repo/');
expect(body).toContain('Icon=https://example.com/flathub/repo/logo.svg');
expect(body).toContain('Homepage=https://flathub.org/');
expect(response.headers.get('Content-Length')).toBe(String(new TextEncoder().encode(body).length));
});
it('rewrites .flatpakref URLs to stay on the Xget mirror', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
[
'[Flatpak Ref]',
'Name=org.gnome.gedit',
'Url=https://dl.flathub.org/repo/',
'RuntimeRepo=https://dl.flathub.org/repo/flathub.flatpakrepo'
].join('\n'),
{
status: 200,
headers: { 'Content-Type': 'application/octet-stream' }
}
)
);
const response = await worker.fetch(
new Request('https://example.com/flathub/repo/appstream/org.gnome.gedit.flatpakref'),
{},
executionContext
);
expect(response.status).toBe(200);
const body = await readUtf8Text(response);
expect(body).toContain('Url=https://example.com/flathub/repo/');
expect(body).toContain(
'RuntimeRepo=https://example.com/flathub/repo/flathub.flatpakrepo'
);
});
it('does not rewrite binary repository metadata like summary files', async () => {
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response('summary-binary-payload', {
status: 200,
headers: { 'Content-Type': 'application/octet-stream' }
})
);
const response = await worker.fetch(
new Request('https://example.com/flathub/repo/summary'),
{},
executionContext
);
expect(response.status).toBe(200);
expect(await readUtf8Text(response)).toBe('summary-binary-payload');
});
});
+12
View File
@@ -16,6 +16,7 @@ describe('Platform Configuration', () => {
'npm',
'pypi',
'conda',
'flathub',
'homebrew'
];
@@ -134,6 +135,13 @@ describe('Platform Configuration', () => {
).toBe('/conda-forge/linux-64/repodata.json');
});
it('should transform Flathub paths correctly', () => {
expect(transformPath('/flathub/repo/summary', 'flathub')).toBe('/repo/summary');
expect(transformPath('/flathub/repo/flathub.flatpakrepo', 'flathub')).toBe(
'/repo/flathub.flatpakrepo'
);
});
it('should transform container registry paths correctly', () => {
expect(
transformPath('/cr/ghcr/v2/nginxinc/nginx-unprivileged/manifests/latest', 'cr-ghcr')
@@ -191,6 +199,10 @@ describe('Platform Configuration', () => {
expect(PLATFORMS['conda-community']).toBe('https://conda.anaconda.org');
});
it('should have correct Flathub base URL', () => {
expect(PLATFORMS.flathub).toBe('https://dl.flathub.org');
});
it('should have correct container registry base URLs', () => {
expect(PLATFORMS['cr-ghcr']).toBe('https://ghcr.io');
expect(PLATFORMS['cr-gcr']).toBe('https://gcr.io');