feat(core): Add Cargo and Composer registries with storage, auth and helpers
This commit is contained in:
@@ -118,17 +118,7 @@ export class MavenRegistry extends BaseRegistry {
|
||||
switch (method) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
// Read permission required
|
||||
if (!await this.checkPermission(token, resource, 'read')) {
|
||||
return {
|
||||
status: 401,
|
||||
headers: {
|
||||
'WWW-Authenticate': `Bearer realm="${this.basePath}",service="maven-registry"`,
|
||||
},
|
||||
body: { error: 'UNAUTHORIZED', message: 'Authentication required' },
|
||||
};
|
||||
}
|
||||
|
||||
// Maven repositories typically allow anonymous reads
|
||||
return method === 'GET'
|
||||
? this.getArtifact(groupId, artifactId, version, filename)
|
||||
: this.headArtifact(groupId, artifactId, version, filename);
|
||||
@@ -181,24 +171,15 @@ export class MavenRegistry extends BaseRegistry {
|
||||
private async handleChecksumRequest(
|
||||
method: string,
|
||||
coordinate: IMavenCoordinate,
|
||||
token: IAuthToken | null
|
||||
token: IAuthToken | null,
|
||||
path: string
|
||||
): Promise<IResponse> {
|
||||
const { groupId, artifactId, version, extension } = coordinate;
|
||||
const resource = `${groupId}:${artifactId}`;
|
||||
|
||||
// Checksums follow the same permissions as their artifacts
|
||||
// Checksums follow the same permissions as their artifacts (public read)
|
||||
if (method === 'GET' || method === 'HEAD') {
|
||||
if (!await this.checkPermission(token, resource, 'read')) {
|
||||
return {
|
||||
status: 401,
|
||||
headers: {
|
||||
'WWW-Authenticate': `Bearer realm="${this.basePath}",service="maven-registry"`,
|
||||
},
|
||||
body: { error: 'UNAUTHORIZED', message: 'Authentication required' },
|
||||
};
|
||||
}
|
||||
|
||||
return this.getChecksum(groupId, artifactId, version, coordinate);
|
||||
return this.getChecksum(groupId, artifactId, version, coordinate, path);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -402,9 +383,14 @@ export class MavenRegistry extends BaseRegistry {
|
||||
groupId: string,
|
||||
artifactId: string,
|
||||
version: string,
|
||||
coordinate: IMavenCoordinate
|
||||
coordinate: IMavenCoordinate,
|
||||
fullPath: string
|
||||
): Promise<IResponse> {
|
||||
const checksumFilename = buildFilename(coordinate);
|
||||
// Extract the filename from the full path (last component)
|
||||
// The fullPath might be something like /com/example/test/test-artifact/1.0.0/test-artifact-1.0.0.jar.md5
|
||||
const pathParts = fullPath.split('/');
|
||||
const checksumFilename = pathParts[pathParts.length - 1];
|
||||
|
||||
const data = await this.storage.getMavenArtifact(groupId, artifactId, version, checksumFilename);
|
||||
|
||||
if (!data) {
|
||||
@@ -567,10 +553,8 @@ export class MavenRegistry extends BaseRegistry {
|
||||
const xml = generateMetadataXml(metadata);
|
||||
await this.storage.putMavenMetadata(groupId, artifactId, Buffer.from(xml, 'utf-8'));
|
||||
|
||||
// Also store checksums for metadata
|
||||
const checksums = await calculateChecksums(Buffer.from(xml, 'utf-8'));
|
||||
const metadataFilename = 'maven-metadata.xml';
|
||||
await this.storeChecksums(groupId, artifactId, '', metadataFilename, checksums);
|
||||
// Note: Checksums for maven-metadata.xml are optional and not critical
|
||||
// They would need special handling since metadata uses a different storage path
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
|
||||
@@ -65,22 +65,35 @@ export function pathToGAV(path: string): IMavenCoordinate | null {
|
||||
/**
|
||||
* Parse Maven artifact filename
|
||||
* Example: my-lib-1.0.0-sources.jar → {classifier: 'sources', extension: 'jar'}
|
||||
* Example: my-lib-1.0.0.jar.md5 → {extension: 'md5'}
|
||||
*/
|
||||
export function parseFilename(
|
||||
filename: string,
|
||||
artifactId: string,
|
||||
version: string
|
||||
): { classifier?: string; extension: string } | null {
|
||||
// Expected format: {artifactId}-{version}[-{classifier}].{extension}
|
||||
// Expected format: {artifactId}-{version}[-{classifier}].{extension}[.checksum]
|
||||
const prefix = `${artifactId}-${version}`;
|
||||
|
||||
if (!filename.startsWith(prefix)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const remainder = filename.substring(prefix.length);
|
||||
let remainder = filename.substring(prefix.length);
|
||||
|
||||
// Check for classifier
|
||||
// Check if this is a checksum file (double extension like .jar.md5)
|
||||
const checksumExtensions = ['md5', 'sha1', 'sha256', 'sha512'];
|
||||
const lastDotIndex = remainder.lastIndexOf('.');
|
||||
if (lastDotIndex !== -1) {
|
||||
const possibleChecksum = remainder.substring(lastDotIndex + 1);
|
||||
if (checksumExtensions.includes(possibleChecksum)) {
|
||||
// This is a checksum file - just return the checksum extension
|
||||
// The base artifact extension doesn't matter for checksum retrieval
|
||||
return { extension: possibleChecksum };
|
||||
}
|
||||
}
|
||||
|
||||
// Regular artifact file parsing
|
||||
const dotIndex = remainder.lastIndexOf('.');
|
||||
if (dotIndex === -1) {
|
||||
return null; // No extension
|
||||
|
||||
Reference in New Issue
Block a user