fix(registry): align registry integrations with updated auth, storage, repository, and audit models
This commit is contained in:
167
ts/registry.ts
167
ts/registry.ts
@@ -86,6 +86,7 @@ export class StackGalleryRegistry {
|
||||
// Initialize storage hooks
|
||||
this.storageHooks = new StackGalleryStorageHooks({
|
||||
bucket: this.smartBucket,
|
||||
bucketName: this.config.s3Bucket,
|
||||
basePath: this.config.storagePath!,
|
||||
});
|
||||
|
||||
@@ -95,16 +96,22 @@ export class StackGalleryRegistry {
|
||||
authProvider: this.authProvider,
|
||||
storageHooks: this.storageHooks,
|
||||
storage: {
|
||||
type: 's3',
|
||||
bucket: this.smartBucket,
|
||||
basePath: this.config.storagePath,
|
||||
endpoint: this.config.s3Endpoint,
|
||||
accessKey: this.config.s3AccessKey,
|
||||
accessSecret: this.config.s3SecretKey,
|
||||
bucketName: this.config.s3Bucket,
|
||||
region: this.config.s3Region,
|
||||
},
|
||||
auth: {
|
||||
jwtSecret: this.config.jwtSecret || 'change-me-in-production',
|
||||
tokenStore: 'database',
|
||||
npmTokens: { enabled: true },
|
||||
ociTokens: {
|
||||
enabled: true,
|
||||
realm: 'stack.gallery',
|
||||
service: 'registry',
|
||||
},
|
||||
},
|
||||
upstreamCache: this.config.enableUpstreamCache
|
||||
? {
|
||||
enabled: true,
|
||||
expiryHours: this.config.upstreamCacheExpiry,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
console.log('[StackGalleryRegistry] smartregistry initialized');
|
||||
|
||||
@@ -161,30 +168,34 @@ export class StackGalleryRegistry {
|
||||
}
|
||||
|
||||
// Registry protocol endpoints (handled by smartregistry)
|
||||
// NPM: /-/..., /@scope/package (but not /packages which is UI route)
|
||||
// OCI: /v2/...
|
||||
// Maven: /maven2/...
|
||||
// PyPI: /simple/..., /pypi/...
|
||||
// Cargo: /api/v1/crates/...
|
||||
// Composer: /packages.json, /p/...
|
||||
// RubyGems: /api/v1/gems/..., /gems/...
|
||||
const registryPaths = ['/-/', '/v2/', '/maven2/', '/simple/', '/pypi/', '/api/v1/crates/', '/packages.json', '/p/', '/api/v1/gems/', '/gems/'];
|
||||
const isRegistryPath = registryPaths.some(p => path.startsWith(p)) ||
|
||||
(path.startsWith('/@') && !path.startsWith('/@stack'));
|
||||
const registryPaths = [
|
||||
'/-/',
|
||||
'/v2/',
|
||||
'/maven2/',
|
||||
'/simple/',
|
||||
'/pypi/',
|
||||
'/api/v1/crates/',
|
||||
'/packages.json',
|
||||
'/p/',
|
||||
'/api/v1/gems/',
|
||||
'/gems/',
|
||||
];
|
||||
const isRegistryPath =
|
||||
registryPaths.some((p) => path.startsWith(p)) ||
|
||||
(path.startsWith('/@') && !path.startsWith('/@stack'));
|
||||
|
||||
if (this.smartRegistry && isRegistryPath) {
|
||||
try {
|
||||
const response = await this.smartRegistry.handleRequest(request);
|
||||
if (response) return response;
|
||||
// Convert Request to IRequestContext
|
||||
const requestContext = await this.requestToContext(request);
|
||||
const response = await this.smartRegistry.handleRequest(requestContext);
|
||||
if (response) return this.contextResponseToResponse(response);
|
||||
} catch (error) {
|
||||
console.error('[StackGalleryRegistry] Request error:', error);
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'Internal server error' }),
|
||||
{
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
return new Response(JSON.stringify({ error: 'Internal server error' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,6 +208,82 @@ export class StackGalleryRegistry {
|
||||
return this.serveStaticFile(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Deno Request to smartregistry IRequestContext
|
||||
*/
|
||||
private async requestToContext(
|
||||
request: Request
|
||||
): Promise<plugins.smartregistry.IRequestContext> {
|
||||
const url = new URL(request.url);
|
||||
const headers: Record<string, string> = {};
|
||||
request.headers.forEach((value, key) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
const query: Record<string, string> = {};
|
||||
url.searchParams.forEach((value, key) => {
|
||||
query[key] = value;
|
||||
});
|
||||
|
||||
let body: unknown = undefined;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
let rawBody: any = undefined;
|
||||
if (request.body && request.method !== 'GET' && request.method !== 'HEAD') {
|
||||
try {
|
||||
const bytes = new Uint8Array(await request.arrayBuffer());
|
||||
rawBody = bytes;
|
||||
const contentType = request.headers.get('content-type') || '';
|
||||
if (contentType.includes('json')) {
|
||||
body = JSON.parse(new TextDecoder().decode(bytes));
|
||||
}
|
||||
} catch {
|
||||
// Body parsing failed, continue with undefined body
|
||||
}
|
||||
}
|
||||
|
||||
// Extract token from Authorization header
|
||||
let token: string | undefined;
|
||||
const authHeader = headers['authorization'];
|
||||
if (authHeader?.startsWith('Bearer ')) {
|
||||
token = authHeader.substring(7);
|
||||
}
|
||||
|
||||
return {
|
||||
method: request.method,
|
||||
path: url.pathname,
|
||||
headers,
|
||||
query,
|
||||
body,
|
||||
rawBody,
|
||||
token,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert smartregistry IResponse to Deno Response
|
||||
*/
|
||||
private contextResponseToResponse(response: plugins.smartregistry.IResponse): Response {
|
||||
const headers = new Headers(response.headers || {});
|
||||
let body: BodyInit | null = null;
|
||||
|
||||
if (response.body !== undefined) {
|
||||
if (typeof response.body === 'string') {
|
||||
body = response.body;
|
||||
} else if (response.body instanceof Uint8Array) {
|
||||
body = response.body as unknown as BodyInit;
|
||||
} else {
|
||||
body = JSON.stringify(response.body);
|
||||
if (!headers.has('content-type')) {
|
||||
headers.set('content-type', 'application/json');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(body, {
|
||||
status: response.status,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve static files from embedded UI
|
||||
*/
|
||||
@@ -206,7 +293,7 @@ export class StackGalleryRegistry {
|
||||
// Get embedded file
|
||||
const embeddedFile = getEmbeddedFile(filePath);
|
||||
if (embeddedFile) {
|
||||
return new Response(embeddedFile.data, {
|
||||
return new Response(embeddedFile.data as unknown as BodyInit, {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': embeddedFile.contentType },
|
||||
});
|
||||
@@ -215,7 +302,7 @@ export class StackGalleryRegistry {
|
||||
// SPA fallback: serve index.html for unknown paths
|
||||
const indexFile = getEmbeddedFile('/index.html');
|
||||
if (indexFile) {
|
||||
return new Response(indexFile.data, {
|
||||
return new Response(indexFile.data as unknown as BodyInit, {
|
||||
status: 200,
|
||||
headers: { 'Content-Type': 'text/html' },
|
||||
});
|
||||
@@ -229,13 +316,10 @@ export class StackGalleryRegistry {
|
||||
*/
|
||||
private async handleApiRequest(request: Request): Promise<Response> {
|
||||
if (!this.apiRouter) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'API router not initialized' }),
|
||||
{
|
||||
status: 503,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
}
|
||||
);
|
||||
return new Response(JSON.stringify({ error: 'API router not initialized' }), {
|
||||
status: 503,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
return await this.apiRouter.handle(request);
|
||||
@@ -336,7 +420,9 @@ export async function createRegistryFromEnvFile(): Promise<StackGalleryRegistry>
|
||||
const s3Endpoint = `${s3Protocol}://${env.S3_HOST || 'localhost'}:${env.S3_PORT || '9000'}`;
|
||||
|
||||
const config: IRegistryConfig = {
|
||||
mongoUrl: env.MONGODB_URL || `mongodb://${env.MONGODB_USER}:${env.MONGODB_PASS}@${env.MONGODB_HOST || 'localhost'}:${env.MONGODB_PORT || '27017'}/${env.MONGODB_NAME}?authSource=admin`,
|
||||
mongoUrl:
|
||||
env.MONGODB_URL ||
|
||||
`mongodb://${env.MONGODB_USER}:${env.MONGODB_PASS}@${env.MONGODB_HOST || 'localhost'}:${env.MONGODB_PORT || '27017'}/${env.MONGODB_NAME}?authSource=admin`,
|
||||
mongoDb: env.MONGODB_NAME || 'stackgallery',
|
||||
s3Endpoint: s3Endpoint,
|
||||
s3AccessKey: env.S3_ACCESSKEY || env.S3_ACCESS_KEY || 'minioadmin',
|
||||
@@ -356,7 +442,10 @@ export async function createRegistryFromEnvFile(): Promise<StackGalleryRegistry>
|
||||
if (error instanceof Deno.errors.NotFound) {
|
||||
console.log('[StackGalleryRegistry] No .nogit/env.json found, using environment variables');
|
||||
} else {
|
||||
console.warn('[StackGalleryRegistry] Error reading .nogit/env.json, falling back to env vars:', error);
|
||||
console.warn(
|
||||
'[StackGalleryRegistry] Error reading .nogit/env.json, falling back to env vars:',
|
||||
error
|
||||
);
|
||||
}
|
||||
return createRegistryFromEnv();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user