Skip to content

Commit

Permalink
Move functions into proper folder
Browse files Browse the repository at this point in the history
  • Loading branch information
MNThomson committed Nov 17, 2023
1 parent 1e7da44 commit 532beff
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 14 deletions.
18 changes: 13 additions & 5 deletions src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
/// <reference types="astro/client" />
import type { KVNamespace, R2Bucket } from "@cloudflare/workers-types";
import type { DirectoryRuntime } from "@astrojs/cloudflare";

interface ImportMetaEnv {
readonly PUBLIC_GIT_SHA: string;
}
type ENV = {
kv: KVNamespace;
r2: R2Bucket;
};

interface ImportMeta {
readonly env: ImportMetaEnv;
declare namespace App {
interface Locals extends DirectoryRuntime {
runtime: DirectoryRuntime & { env: {
kv: KVNamespace;
r2: R2Bucket;
} };
}
}
4 changes: 1 addition & 3 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ export interface Props {
}
const { title } = Astro.props;
const PUBLIC_GIT_SHA = import.meta.env.PUBLIC_GIT_SHA;
---

<!DOCTYPE html>
Expand All @@ -19,7 +17,7 @@ const PUBLIC_GIT_SHA = import.meta.env.PUBLIC_GIT_SHA;
name="description"
content="The Globally Distributed, Edge Container Registry"
/>
<meta name="version" content={PUBLIC_GIT_SHA} />
<!-- <meta name="version" content={PUBLIC_GIT_SHA} /> -->
<meta name="application-name" content="cfcr.dev" />
<meta name="author" content="MNThomson" />

Expand Down
82 changes: 82 additions & 0 deletions src/lib/server/miniflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// ABSOLUTE LEGEND: https://github.com/sveltejs/kit/issues/4292#issuecomment-1550596497
type StorageOptionsMemory = {
type: 'memory';
};

type StorageOptionsFile = {
type: 'file';
path: string;
};

export type StorageOptions = StorageOptionsMemory | StorageOptionsFile;

export const createCache = async (storageOptions: StorageOptions) => {
const { Cache } = await import('@miniflare/cache');

if (storageOptions.type === 'memory') {
const { MemoryStorage } = await import('@miniflare/storage-memory');
return new Cache(new MemoryStorage());
} else if (storageOptions.type === 'file') {
const { FileStorage } = await import('@miniflare/storage-file');
return new Cache(new FileStorage(storageOptions.path));
}

throw new Error('StorageType not found');
};

export const createD1 = async (storageOptions: StorageOptions) => {
const { createSQLiteDB } = await import('@miniflare/shared');
const { D1Database, D1DatabaseAPI } = await import('@miniflare/d1');

if (storageOptions.type === 'memory') {
const sqliteDb = await createSQLiteDB(':memory:');
return new D1Database(new D1DatabaseAPI(sqliteDb));
} else if (storageOptions.type === 'file') {
const sqliteDb = await createSQLiteDB(storageOptions.path);
return new D1Database(new D1DatabaseAPI(sqliteDb));
}

throw new Error('StorageType not found');
};

export const createR2 = async (storageOptions: StorageOptions) => {
const { R2Bucket } = await import('@miniflare/r2');

if (storageOptions.type === 'memory') {
const { MemoryStorage } = await import('@miniflare/storage-memory');
return new R2Bucket(new MemoryStorage());
} else if (storageOptions.type === 'file') {
const { FileStorage } = await import('@miniflare/storage-file');
return new R2Bucket(new FileStorage(storageOptions.path));
}

throw new Error('StorageType not found');
};

export const createKV = async (storageOptions: StorageOptions) => {
const { KVNamespace } = await import('@miniflare/kv');

if (storageOptions.type === 'memory') {
const { MemoryStorage } = await import('@miniflare/storage-memory');
return new KVNamespace(new MemoryStorage());
} else if (storageOptions.type === 'file') {
const { FileStorage } = await import('@miniflare/storage-file');
return new KVNamespace(new FileStorage(storageOptions.path));
}

throw new Error('StorageType not found');
};

export const createDOStorage = async (storageOptions: StorageOptions) => {
const { DurableObjectStorage } = await import('@miniflare/durable-objects');

if (storageOptions.type === 'memory') {
const { MemoryStorage } = await import('@miniflare/storage-memory');
return new DurableObjectStorage(new MemoryStorage());
} else if (storageOptions.type === 'file') {
const { FileStorage } = await import('@miniflare/storage-file');
return new DurableObjectStorage(new FileStorage(storageOptions.path));
}

throw new Error('StorageType not found');
};
15 changes: 15 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createKV, createR2 } from './lib/server/miniflare';
import { defineMiddleware } from "astro:middleware";

export const onRequest = defineMiddleware(async (context, next) => {
if (import.meta.env.DEV) {
context.locals.runtime = {
env: {
kv: await createKV({ type: 'file', path: '.mf/kv-counter' }),
r2: await createR2({ type: 'file', path: '.mf/r2-bucket' }),
},
};
}

return next();
});
23 changes: 23 additions & 0 deletions src/pages/v2/[...name]/blobs/[reference]/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { errorNoData } from "@utils/response";
import { parseParams } from "@utils/url";

import type { APIContext } from "astro";

export async function GET(context: APIContext) {
const { reference, error } = parseParams(context.params);
if (error != null) {
return error;
}

let response: typeof Response;

// DB Query
const data = await context.locals?.runtime?.env.r2.get(reference);
if (data == null) {
response = errorNoData();
} else {
response = new Response(data.body);
}

return response;
};
49 changes: 49 additions & 0 deletions src/pages/v2/[...name]/manifests/[reference]/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { errorNoData } from "@utils/response";
import { parseParams } from "@utils/url";
import type { APIContext } from "astro";

export async function GET(context: APIContext) {
const { name, reference } = parseParams(context.params);

// DB Query
let dbKey: string;
if (reference.includes("sha256")) {
dbKey = reference;
} else {
dbKey = name + "/" + reference;
}

// Potential for readable stream and no waiting
const data = await context.locals?.runtime?.env.kv.get(dbKey);
if (!data) {
return errorNoData();
}

// Set body of response
let body = "";
if (context.request.method !== "HEAD") {
body = data;
}

const resp = new Response(body);

// Set docker-content-digest header
let shaTag = reference;
if (!reference.includes("sha256")) {
shaTag = data;
}
resp.headers.set("docker-content-digest", shaTag);

// Set Content-Type header
let contentType = "";
if (data.startsWith("{")) {
contentType = JSON.parse(data)?.mediaType;
}
if (!contentType) {
contentType = "application/vnd.docker.distribution.manifest.list.v2+json";
}
resp.headers.set("Content-Type", contentType);
resp.headers.set("Content-Length", data.length.toString());

return resp;
};
121 changes: 121 additions & 0 deletions src/pages/v2/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// import type { Env} from "../../../env.d.ts"

let authHeader: string;

async function getAuth() {
const resp = await fetch(
"https://auth.docker.io/token?scope=repository%3Alibrary%2Fhello-world%3Apull&service=registry.docker.io"
);
return "Bearer " + ((await resp.json()) as Record<string, string>).token;
}

async function seedTag(env: Env, tag: string) {
// Get hello-world:latest
const label_req = await fetch(
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest",
{
method: "HEAD",
headers: {
Authorization: authHeader,
Accept: "application/vnd.docker.distribution.manifest.list.v2+json",
},
}
);

if (label_req.status != 200) {
console.error("Tag does not exist", label_req.statusText);
return;
}

console.log(await label_req.text());

const sha_hash = await label_req.headers.get("docker-content-digest")!;
await env.kv.put(tag, sha_hash);
console.log(sha_hash);
seedOS(env, sha_hash);
}

async function seedOS(env: Env, hash: string) {
// Get sha256:
const label_req = await fetch(
`https://registry-1.docker.io/v2/library/hello-world/manifests/${hash}`,
{
method: "GET",
headers: {
Authorization: authHeader,
Accept: "application/vnd.docker.distribution.manifest.list.v2+json",
},
}
);

if (label_req.status != 200) {
console.error("OS list does not exist", label_req.statusText);
return;
}

const os_list = await label_req.text();
await env.kv.put(hash, os_list);
const os_json = JSON.parse(os_list) as Record<any, any>;

for (var key in os_json.manifests) {
seedImage(env, os_json.manifests[key].digest);
}
}

async function seedImage(env: Env, hash: string) {
// Get sha256:
const label_req = await fetch(
`https://registry-1.docker.io/v2/library/hello-world/manifests/${hash}`,
{
method: "GET",
headers: {
Authorization: authHeader,
Accept: "application/vnd.docker.distribution.manifest.v2+json",
},
}
);

if (label_req.status != 200) {
console.error("OS list does not exist", label_req.statusText);
return;
}

const os_list = await label_req.text();
await env.kv.put(hash, os_list);
const image_json = JSON.parse(os_list) as Record<any, any>;

seedBlob(env, image_json.config.digest);
for (var key in image_json.layers) {
seedBlob(env, image_json.layers[key].digest);
}
}

async function seedBlob(env: Env, hash: string) {
const req = await fetch(
`https://registry-1.docker.io/v2/library/hello-world/blobs/${hash}`,
{
headers: {
Authorization: authHeader,
},
}
);

if (req.status != 200) {
console.error("Seed not 200", req.statusText);
}

await env.r2.put(hash, await req.arrayBuffer());
}

export const GET: APIRoute = async (context) => {
if (!import.meta.env.DEV) {
return new Response("", { status: 404 });
}

console.log("Seeding...");
authHeader = await getAuth();
await seedTag(context.locals?.runtime?.env, "hello-world:latest");
console.log("DONE Seeding");

return new Response("DONE");
}
7 changes: 3 additions & 4 deletions src/pages/version.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export const prerender = true;

export function get(): Record<string, string> {
return {
body: import.meta.env.PUBLIC_GIT_SHA,
};
export const GET: APIRoute = async () => {
// return new Response(import.meta.env.PUBLIC_GIT_SHA);
return new Response("todo!()");
}
5 changes: 3 additions & 2 deletions src/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ const nameRegex = new RegExp(
);
const referenceRegex = new RegExp("^(sha256:)?(\\w[\\w.-]{0,127})$");

function parseParams(params: Record<string, string | string[]>): {
function parseParams(params: Record<string, string | undefined>): {
name: string;
reference: string;
error: Response | null;
} {
try {
const name = (params.name as string[]).join("/");
if (!params) throw new Error;
const name = params.name
const reference = params.reference as string;

let error = "";
Expand Down

0 comments on commit 532beff

Please sign in to comment.