feat(upstream): Add upstream proxy/cache subsystem and integrate per-protocol upstreams
This commit is contained in:
@@ -7,6 +7,7 @@ import { BaseRegistry } from '../core/classes.baseregistry.js';
|
||||
import type { RegistryStorage } from '../core/classes.registrystorage.js';
|
||||
import type { AuthManager } from '../core/classes.authmanager.js';
|
||||
import type { IRequestContext, IResponse, IAuthToken } from '../core/interfaces.core.js';
|
||||
import type { IProtocolUpstreamConfig } from '../upstream/interfaces.upstream.js';
|
||||
import { toBuffer } from '../core/helpers.buffer.js';
|
||||
import type { IMavenCoordinate, IMavenMetadata, IChecksums } from './interfaces.maven.js';
|
||||
import {
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
extractGAVFromPom,
|
||||
gavToPath,
|
||||
} from './helpers.maven.js';
|
||||
import { MavenUpstream } from './classes.mavenupstream.js';
|
||||
|
||||
/**
|
||||
* Maven Registry class
|
||||
@@ -31,18 +33,34 @@ export class MavenRegistry extends BaseRegistry {
|
||||
private authManager: AuthManager;
|
||||
private basePath: string = '/maven';
|
||||
private registryUrl: string;
|
||||
private upstream: MavenUpstream | null = null;
|
||||
|
||||
constructor(
|
||||
storage: RegistryStorage,
|
||||
authManager: AuthManager,
|
||||
basePath: string,
|
||||
registryUrl: string
|
||||
registryUrl: string,
|
||||
upstreamConfig?: IProtocolUpstreamConfig
|
||||
) {
|
||||
super();
|
||||
this.storage = storage;
|
||||
this.authManager = authManager;
|
||||
this.basePath = basePath;
|
||||
this.registryUrl = registryUrl;
|
||||
|
||||
// Initialize upstream if configured
|
||||
if (upstreamConfig?.enabled) {
|
||||
this.upstream = new MavenUpstream(upstreamConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources (timers, connections, etc.)
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this.upstream) {
|
||||
this.upstream.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
@@ -234,7 +252,23 @@ export class MavenRegistry extends BaseRegistry {
|
||||
version: string,
|
||||
filename: string
|
||||
): Promise<IResponse> {
|
||||
const data = await this.storage.getMavenArtifact(groupId, artifactId, version, filename);
|
||||
let data = await this.storage.getMavenArtifact(groupId, artifactId, version, filename);
|
||||
|
||||
// Try upstream if not found locally
|
||||
if (!data && this.upstream) {
|
||||
// Parse the filename to extract extension and classifier
|
||||
const { extension, classifier } = this.parseFilename(filename, artifactId, version);
|
||||
if (extension) {
|
||||
data = await this.upstream.fetchArtifact(groupId, artifactId, version, extension, classifier);
|
||||
if (data) {
|
||||
// Cache the artifact locally
|
||||
await this.storage.putMavenArtifact(groupId, artifactId, version, filename, data);
|
||||
// Generate and store checksums
|
||||
const checksums = await calculateChecksums(data);
|
||||
await this.storeChecksums(groupId, artifactId, version, filename, checksums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
@@ -462,7 +496,17 @@ export class MavenRegistry extends BaseRegistry {
|
||||
// ========================================================================
|
||||
|
||||
private async getMetadata(groupId: string, artifactId: string): Promise<IResponse> {
|
||||
const metadataBuffer = await this.storage.getMavenMetadata(groupId, artifactId);
|
||||
let metadataBuffer = await this.storage.getMavenMetadata(groupId, artifactId);
|
||||
|
||||
// Try upstream if not found locally
|
||||
if (!metadataBuffer && this.upstream) {
|
||||
const upstreamMetadata = await this.upstream.fetchMetadata(groupId, artifactId);
|
||||
if (upstreamMetadata) {
|
||||
metadataBuffer = Buffer.from(upstreamMetadata, 'utf-8');
|
||||
// Cache the metadata locally
|
||||
await this.storage.putMavenMetadata(groupId, artifactId, metadataBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!metadataBuffer) {
|
||||
// Generate empty metadata if none exists
|
||||
@@ -578,4 +622,41 @@ export class MavenRegistry extends BaseRegistry {
|
||||
|
||||
return contentTypes[extension] || 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a Maven filename to extract extension and classifier.
|
||||
* Filename format: {artifactId}-{version}[-{classifier}].{extension}
|
||||
*/
|
||||
private parseFilename(
|
||||
filename: string,
|
||||
artifactId: string,
|
||||
version: string
|
||||
): { extension: string; classifier?: string } {
|
||||
const prefix = `${artifactId}-${version}`;
|
||||
|
||||
if (!filename.startsWith(prefix)) {
|
||||
// Fallback: just get the extension
|
||||
const lastDot = filename.lastIndexOf('.');
|
||||
return { extension: lastDot > 0 ? filename.slice(lastDot + 1) : '' };
|
||||
}
|
||||
|
||||
const remainder = filename.slice(prefix.length);
|
||||
// remainder is either ".extension" or "-classifier.extension"
|
||||
|
||||
if (remainder.startsWith('.')) {
|
||||
return { extension: remainder.slice(1) };
|
||||
}
|
||||
|
||||
if (remainder.startsWith('-')) {
|
||||
const lastDot = remainder.lastIndexOf('.');
|
||||
if (lastDot > 1) {
|
||||
return {
|
||||
classifier: remainder.slice(1, lastDot),
|
||||
extension: remainder.slice(lastDot + 1),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return { extension: '' };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user