fix(oci): remove /v2/ from internal route patterns and make upstream apiPrefix configurable
The OCI handler had /v2/ baked into all regex patterns and Location headers. When basePath was set to /v2 (as in stack.gallery), stripping it removed the prefix that patterns expected, causing all OCI endpoints to 404. Now patterns match on bare paths after basePath stripping, working correctly regardless of the basePath value. Also adds configurable apiPrefix to OCI upstream class (default /v2) for registries behind reverse proxies with custom path prefixes.
This commit is contained in:
@@ -24,13 +24,18 @@ export class OciUpstream extends BaseUpstream {
|
||||
/** Local registry base path for URL building */
|
||||
private readonly localBasePath: string;
|
||||
|
||||
/** API prefix for outbound OCI requests (default: /v2) */
|
||||
private readonly apiPrefix: string;
|
||||
|
||||
constructor(
|
||||
config: IProtocolUpstreamConfig,
|
||||
localBasePath: string = '/oci',
|
||||
logger?: plugins.smartlog.Smartlog,
|
||||
apiPrefix: string = '/v2',
|
||||
) {
|
||||
super(config, logger);
|
||||
this.localBasePath = localBasePath;
|
||||
this.apiPrefix = apiPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +49,7 @@ export class OciUpstream extends BaseUpstream {
|
||||
protocol: 'oci',
|
||||
resource: repository,
|
||||
resourceType: 'manifest',
|
||||
path: `/v2/${repository}/manifests/${reference}`,
|
||||
path: `${this.apiPrefix}/${repository}/manifests/${reference}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': [
|
||||
@@ -88,7 +93,7 @@ export class OciUpstream extends BaseUpstream {
|
||||
protocol: 'oci',
|
||||
resource: repository,
|
||||
resourceType: 'manifest',
|
||||
path: `/v2/${repository}/manifests/${reference}`,
|
||||
path: `${this.apiPrefix}/${repository}/manifests/${reference}`,
|
||||
method: 'HEAD',
|
||||
headers: {
|
||||
'accept': [
|
||||
@@ -127,7 +132,7 @@ export class OciUpstream extends BaseUpstream {
|
||||
protocol: 'oci',
|
||||
resource: repository,
|
||||
resourceType: 'blob',
|
||||
path: `/v2/${repository}/blobs/${digest}`,
|
||||
path: `${this.apiPrefix}/${repository}/blobs/${digest}`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/octet-stream',
|
||||
@@ -155,7 +160,7 @@ export class OciUpstream extends BaseUpstream {
|
||||
protocol: 'oci',
|
||||
resource: repository,
|
||||
resourceType: 'blob',
|
||||
path: `/v2/${repository}/blobs/${digest}`,
|
||||
path: `${this.apiPrefix}/${repository}/blobs/${digest}`,
|
||||
method: 'HEAD',
|
||||
headers: {},
|
||||
query: {},
|
||||
@@ -189,7 +194,7 @@ export class OciUpstream extends BaseUpstream {
|
||||
protocol: 'oci',
|
||||
resource: repository,
|
||||
resourceType: 'tags',
|
||||
path: `/v2/${repository}/tags/list`,
|
||||
path: `${this.apiPrefix}/${repository}/tags/list`,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
@@ -215,7 +220,8 @@ export class OciUpstream extends BaseUpstream {
|
||||
|
||||
/**
|
||||
* Override URL building for OCI-specific handling.
|
||||
* OCI registries use /v2/ prefix and may require special handling for Docker Hub.
|
||||
* OCI registries use a configurable API prefix (default /v2/) and may require
|
||||
* special handling for Docker Hub.
|
||||
*/
|
||||
protected buildUpstreamUrl(
|
||||
upstream: IUpstreamRegistryConfig,
|
||||
@@ -228,16 +234,20 @@ export class OciUpstream extends BaseUpstream {
|
||||
baseUrl = baseUrl.slice(0, -1);
|
||||
}
|
||||
|
||||
// Use per-upstream apiPrefix if configured, otherwise use the instance default
|
||||
const prefix = upstream.apiPrefix || this.apiPrefix;
|
||||
|
||||
// Handle Docker Hub special case
|
||||
// Docker Hub uses registry-1.docker.io but library images need special handling
|
||||
if (baseUrl.includes('docker.io') || baseUrl.includes('registry-1.docker.io')) {
|
||||
// For library images (e.g., "nginx" -> "library/nginx")
|
||||
const pathParts = context.path.match(/^\/v2\/([^\/]+)\/(.+)$/);
|
||||
const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pathParts = context.path.match(new RegExp(`^${escapedPrefix}\\/([^\\/]+)\\/(.+)$`));
|
||||
if (pathParts) {
|
||||
const [, repository, rest] = pathParts;
|
||||
// If repository doesn't contain a slash, it's a library image
|
||||
if (!repository.includes('/')) {
|
||||
return `${baseUrl}/v2/library/${repository}/${rest}`;
|
||||
return `${baseUrl}${prefix}/library/${repository}/${rest}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user