diff --git a/src/registry/r2.ts b/src/registry/r2.ts index acadcee..d2acc43 100644 --- a/src/registry/r2.ts +++ b/src/registry/r2.ts @@ -19,6 +19,7 @@ import { FinishedUploadObject, GetLayerResponse, GetManifestResponse, + ListRepositoriesResponse, PutManifestResponse, Registry, RegistryError, @@ -130,6 +131,34 @@ export class R2Registry implements Registry { return checkManifestResponse; } + async listRepositories(limit?: number, last?: string): Promise { + const env = this.env; + const options = { + limit: limit ? limit : 1000, + delimiter: "/", + startAfter: last, + } + const r2Objects = (await env.REGISTRY.list(options)); + + let truncated = r2Objects.truncated; + let cursor = truncated ? r2Objects.cursor : undefined; + + while (truncated) { + const next = await env.REGISTRY.list({ + ...options, + cursor: cursor, + }); + r2Objects.objects.push(...next.objects); + + truncated = next.truncated; + cursor = next.cursor + } + + return { + repositories: r2Objects.delimitedPrefixes.map((name)=> name.endsWith('/') ? name.slice(0, -1) : name) + }; + } + async putManifest( name: string, reference: string, diff --git a/src/registry/registry.ts b/src/registry/registry.ts index 15fdd2f..e115bd6 100644 --- a/src/registry/registry.ts +++ b/src/registry/registry.ts @@ -43,6 +43,12 @@ export type CheckManifestResponse = exists: false; }; +export type ListRepositoriesResponse = + { + repositories: string[]; + } + + // Response layerExists call export type CheckLayerResponse = | { @@ -102,6 +108,9 @@ export interface Registry { // checks whether the manifest exists in the registry manifestExists(namespace: string, tag: string): Promise; + // listing repositories in the registry + listRepositories(limit?: number, last?: string): Promise; + // gets the manifest by namespace + digest getManifest(namespace: string, digest: string): Promise; diff --git a/src/router.ts b/src/router.ts index ed88a1b..2eabac6 100644 --- a/src/router.ts +++ b/src/router.ts @@ -25,6 +25,17 @@ v2Router.get("/", async (_req, _env: Env) => { return new Response(); }); +v2Router.get("/_catalog", async (req, env: Env) => { + const { n, last } = req.query; + const res = await env.REGISTRY_CLIENT.listRepositories( + n ? parseInt(n?.toString()) : undefined, + last?.toString() + ); + + return new Response(JSON.stringify(res)); +}); + + v2Router.delete("/:name+/manifests/:reference", async (req, env: Env) => { // deleting a manifest works by retrieving the """main""" manifest that its key is a sha, // and then going through every tag and removing it