Adding basic file browsing support
Some checks failed
continuous-integration/drone/push Build is failing
5
.vscode/settings.json
vendored
@ -1,5 +1,8 @@
|
||||
{
|
||||
"deno.enable": true,
|
||||
"deno.unstable": true,
|
||||
"debug.javascript.usePreview": false
|
||||
"debug.javascript.usePreview": false,
|
||||
"deno.import_intellisense_origins": {
|
||||
"https://deno.land": true
|
||||
}
|
||||
}
|
||||
|
BIN
registry/favicon.png
Normal file
After Width: | Height: | Size: 44 KiB |
11
registry/favicon.svg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/android-icon-192x192.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
registry/public/apple-icon-114x114.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/apple-icon-120x120.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
registry/public/apple-icon-144x144.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/apple-icon-152x152.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/apple-icon-180x180.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
registry/public/apple-icon-57x57.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
registry/public/apple-icon-60x60.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
registry/public/apple-icon-72x72.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
registry/public/apple-icon-76x76.png
Normal file
After Width: | Height: | Size: 7.5 KiB |
2
registry/public/browserconfig.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig><msapplication><tile><square70x70logo src="/public/ms-icon-70x70.png"/><square150x150logo src="/public/ms-icon-150x150.png"/><square310x310logo src="/public/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>
|
BIN
registry/public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 930 B |
BIN
registry/public/favicon-256x256.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
registry/public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
registry/public/favicon-96x96.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
registry/public/favicon.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
1
registry/public/file.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
|
After Width: | Height: | Size: 337 B |
1
registry/public/folder.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-folder"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
After Width: | Height: | Size: 311 B |
41
registry/public/manifest.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "Deno package Registry",
|
||||
"short_name": "DenReg",
|
||||
"description": "A deno package registry, that can be self hosted easily",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"theme_color": "#ffffff",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/public/favicon-32x32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png",
|
||||
"density": "0.75"
|
||||
},
|
||||
{
|
||||
"src": "/public/apple-icon-72x72.png",
|
||||
"sizes": "72x72",
|
||||
"type": "image/png",
|
||||
"density": "1.5"
|
||||
},
|
||||
{
|
||||
"src": "/public/favicon-96x96.png",
|
||||
"sizes": "96x96",
|
||||
"type": "image/png",
|
||||
"density": "2.0"
|
||||
},
|
||||
{
|
||||
"src": "/public/apple-icon-144x144.png",
|
||||
"sizes": "144x144",
|
||||
"type": "image/png",
|
||||
"density": "3.0"
|
||||
},
|
||||
{
|
||||
"src": "/public/android-icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"density": "4.0"
|
||||
}
|
||||
]
|
||||
}
|
BIN
registry/public/ms-icon-144x144.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/ms-icon-150x150.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
registry/public/ms-icon-310x310.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
registry/public/ms-icon-70x70.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
2
registry/public/paper.min.css
vendored
Normal file
6
registry/scripts/getPaperCSS.ts
Normal file
@ -0,0 +1,6 @@
|
||||
await fetch("https://unpkg.com/papercss/dist/paper.min.css")
|
||||
.then((res) => res.arrayBuffer())
|
||||
.then((data) =>
|
||||
Deno.writeFile("./public/paper.min.css", new Uint8Array(data))
|
||||
)
|
||||
.catch(console.error);
|
@ -1,29 +1,33 @@
|
||||
// export { MongoClient, ObjectId } from "https://deno.land/x/mongo@v0.9.1/mod.ts";
|
||||
export * as S3 from "https://deno.land/x/s3@0.2.0/mod.ts";
|
||||
|
||||
export * as S3 from "https://deno.land/x/s3/mod.ts";
|
||||
|
||||
export * as Ini from "https://deno.land/x/ini/mod.ts";
|
||||
export * as Ini from "https://deno.hibas123.de/raw/ini@0.0.1/mod.ts";
|
||||
|
||||
export * as ABC from "https://deno.land/x/abc@v1/mod.ts";
|
||||
export * as CorsMW from "https://deno.land/x/abc@v1/middleware/cors.ts";
|
||||
export * as LoggerMW from "https://deno.land/x/abc@v1/middleware/logger.ts";
|
||||
|
||||
export * as Path from "https://deno.land/std@0.63.0/path/mod.ts";
|
||||
export * as FS from "https://deno.land/std@0.63.0/fs/mod.ts";
|
||||
export * as Base64 from "https://deno.land/std@0.63.0/encoding/base64.ts";
|
||||
export * as Hash from "https://deno.land/std@0.63.0/hash/mod.ts";
|
||||
export * as Path from "https://deno.land/std@0.69.0/path/mod.ts";
|
||||
export * as FS from "https://deno.land/std@0.69.0/fs/mod.ts";
|
||||
export * as Base64 from "https://deno.land/std@0.69.0/encoding/base64.ts";
|
||||
export * as Hash from "https://deno.land/std@0.69.0/hash/mod.ts";
|
||||
export * as Colors from "https://deno.land/std@0.69.0/fmt/colors.ts";
|
||||
|
||||
export * as Compress from "https://git.stamm.me/Deno/DenReg/raw/branch/master/tar/mod.ts";
|
||||
|
||||
export { Marked } from "https://deno.land/x/markdown/mod.ts";
|
||||
export { default as Prism } from "https://cdn.skypack.dev/prismjs";
|
||||
|
||||
// export { Marked } from "https://deno.land/x/markdown/mod.ts";
|
||||
|
||||
export { Marked } from "../../markdown/mod.ts";
|
||||
|
||||
import DS from "https://raw.githubusercontent.com/hibas123/dndb/master/mod.ts";
|
||||
|
||||
/// <reference path="./types/jsx.d.ts" />
|
||||
export {
|
||||
React,
|
||||
jsx,
|
||||
Fragment,
|
||||
} from "https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts";
|
||||
export { React, jsx, Fragment } from "../../jsx-html/mod.ts";
|
||||
// export {
|
||||
// React,
|
||||
// jsx,
|
||||
// Fragment,
|
||||
// } from "https://raw.githubusercontent.com/apiel/jsx-html/master/mod.ts";
|
||||
|
||||
export const Datastore = DS;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/// <reference path="./types/jsx.d.ts" />
|
||||
import { ABC, CorsMW, LoggerMW } from "./deps.ts";
|
||||
import { ABC, CorsMW, LoggerMW, Path } from "./deps.ts";
|
||||
import config from "./config.ts";
|
||||
|
||||
const port = config?.api?.port || 8000;
|
||||
@ -8,6 +8,30 @@ const app = new ABC.Application();
|
||||
app.use(LoggerMW.logger({}));
|
||||
app.use(CorsMW.cors({}));
|
||||
|
||||
// app.static("/public", "./public");
|
||||
|
||||
const MIMEDB = {
|
||||
".css": "text/css",
|
||||
".json": "application/json",
|
||||
".js": "application/javascript",
|
||||
".gz": "applcation/gzip",
|
||||
".ico": "image/x-icon",
|
||||
".png": "image/png",
|
||||
".svg": "image/svg+xml",
|
||||
".xml": "application/xml",
|
||||
};
|
||||
|
||||
app.get("/public/*path", async (ctx) => {
|
||||
const filePath = Path.join("./public", ctx.params.path);
|
||||
|
||||
console.log(filePath);
|
||||
|
||||
ctx.blob(
|
||||
await Deno.open(filePath, { read: true }),
|
||||
(MIMEDB as any)[Path.extname(filePath)]
|
||||
);
|
||||
});
|
||||
|
||||
import api from "./http/api.ts";
|
||||
api(app.group("/api"));
|
||||
|
||||
@ -17,7 +41,15 @@ raw(app.group("/raw"));
|
||||
import view from "./http/views.ts";
|
||||
view(app);
|
||||
|
||||
console.log(app.router.trees.GET);
|
||||
// function logNode(router: Node, indent = 0) {
|
||||
// trees.map((tree) => {
|
||||
// console.log("Path:", tree.path);
|
||||
// tree.children?.forEach((node) => logNode(node, indent + 3));
|
||||
// });
|
||||
// }
|
||||
|
||||
// const trees = Object.values(app.router) as Node[];
|
||||
// trees.forEach(logNode);
|
||||
|
||||
import render from "./renderer.tsx";
|
||||
app.renderer = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ABC, Path, Compress, FS } from "../deps.ts";
|
||||
import { ABC, Path, Compress, FS, Colors } from "../deps.ts";
|
||||
|
||||
import bucket from "../s3.ts";
|
||||
import { isValidPackageName, basicauth, isValidFullVersion } from "../utils.ts";
|
||||
@ -138,16 +138,14 @@ async function uploadPackage(ctx: ABC.Context) {
|
||||
});
|
||||
|
||||
for await (const file of walker) {
|
||||
const relative = Path.relative(folder, file.path);
|
||||
const relative = Path.relative(folder, file.path).replace("\\", "/");
|
||||
|
||||
console.log("Normalised path:", relative.replace("\\", "/"));
|
||||
|
||||
const bucketPath = (bucketBase + relative).replace(/@/g, "§");
|
||||
|
||||
console.log("Uploading file:", file.path, bucketPath, bucketBase);
|
||||
await bucket.putObject(
|
||||
bucketPath,
|
||||
await Deno.readAll(await Deno.open(file.path)),
|
||||
{}
|
||||
);
|
||||
const body = await Deno.readAll(await Deno.open(file.path));
|
||||
await bucket.putObject(bucketPath, body, {});
|
||||
}
|
||||
console.log("Setting new live version");
|
||||
|
||||
@ -168,7 +166,9 @@ async function uploadPackage(ctx: ABC.Context) {
|
||||
success: true,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error("Error while processing newly uploaded package");
|
||||
console.error(
|
||||
Colors.red("Error while processing newly uploaded package")
|
||||
);
|
||||
console.error(err);
|
||||
return {
|
||||
success: false,
|
||||
|
@ -1,5 +1,11 @@
|
||||
import { ABC } from "../deps.ts";
|
||||
import { extractPackagePath, getFile } from "../utils.ts";
|
||||
import { Path } from "../deps.ts";
|
||||
import type { ABC } from "../deps.ts";
|
||||
import {
|
||||
extractPackagePath,
|
||||
getAbsolutePackageVersion,
|
||||
getFile,
|
||||
} from "../utils.ts";
|
||||
import db from "../db.ts";
|
||||
|
||||
const MAX_FIXED_CACHE_AGE = 60 * 60 * 24 * 365;
|
||||
const MAX_FLOATING_CACHE_AGE = 60 * 30;
|
||||
@ -11,16 +17,27 @@ export default function raw(g: ABC.Group) {
|
||||
ctx.params.package
|
||||
);
|
||||
|
||||
const pkg = await db.package.findOne({ name: packageName });
|
||||
packageVersion = getAbsolutePackageVersion(pkg, packageVersion);
|
||||
|
||||
const E404 = () => {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = "// Not found!";
|
||||
};
|
||||
|
||||
const result = await getFile(
|
||||
packageName,
|
||||
packageVersion,
|
||||
ctx.params.path
|
||||
);
|
||||
const filepath = ctx.params.path;
|
||||
const result = await getFile(packageName, packageVersion, filepath);
|
||||
|
||||
if (filepath.endsWith(".js")) {
|
||||
const tsFile = filepath.substr(0, filepath.length - 3) + ".d.ts";
|
||||
const tsResult = await getFile(packageName, packageVersion, tsFile);
|
||||
if (tsResult) {
|
||||
ctx.response.headers.set(
|
||||
"X-TypeScript-Types",
|
||||
"./" + Path.posix.basename(tsFile)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (packageVersion && result) {
|
||||
ctx.response.headers.set(
|
||||
|
@ -1,8 +1,16 @@
|
||||
import { ABC } from "../deps.ts";
|
||||
import { basicauth, extractPackagePath, sortVersions } from "../utils.ts";
|
||||
import type { ABC } from "../deps.ts";
|
||||
import {
|
||||
basicauth,
|
||||
extractPackagePath,
|
||||
getBucketFilePath,
|
||||
getFile,
|
||||
getAbsolutePackageVersion,
|
||||
sortVersions,
|
||||
} from "../utils.ts";
|
||||
|
||||
import { Hash } from "../deps.ts";
|
||||
import { Hash, Path } from "../deps.ts";
|
||||
import db, { IPackage } from "../db.ts";
|
||||
import bucket from "../s3.ts";
|
||||
|
||||
const MAX_CACHE_AGE = 60 * 30; // 30 Minutes
|
||||
|
||||
@ -44,6 +52,7 @@ export default function views(g: ABC.Application) {
|
||||
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
||||
ctx.response.headers.set("E-Tag", etag);
|
||||
});
|
||||
|
||||
g.get("/package/:package", async (ctx) => {
|
||||
let [packageName, packageVersion] = extractPackagePath(
|
||||
ctx.params.package
|
||||
@ -61,4 +70,84 @@ export default function views(g: ABC.Application) {
|
||||
ctx.response.headers.set("cache-control", CACHE_CONTROL);
|
||||
ctx.response.headers.set("E-Tag", etag);
|
||||
});
|
||||
|
||||
async function handleBrowse(ctx: ABC.Context) {
|
||||
const E404 = () => {
|
||||
ctx.response.status = 404;
|
||||
ctx.response.body = "// Not found!";
|
||||
};
|
||||
|
||||
let [packageName, packageVersion] = extractPackagePath(
|
||||
ctx.params.package
|
||||
);
|
||||
|
||||
const pkg = await db.package.findOne({ name: packageName });
|
||||
packageVersion = getAbsolutePackageVersion(pkg, packageVersion);
|
||||
|
||||
if (!packageVersion) return E404();
|
||||
|
||||
const path = ctx.params.path || "";
|
||||
|
||||
const fileContent = await getFile(packageName, packageVersion, path);
|
||||
if (fileContent) {
|
||||
await ctx.render("browse_file", {
|
||||
pkg,
|
||||
version: packageVersion,
|
||||
content: new TextDecoder().decode(fileContent.data),
|
||||
ext: Path.extname(path),
|
||||
path: `${packageName}@${packageVersion}/${path}`,
|
||||
});
|
||||
} else {
|
||||
const bucketPath = await getBucketFilePath(
|
||||
packageName,
|
||||
packageVersion,
|
||||
path
|
||||
);
|
||||
if (!bucketPath) return E404();
|
||||
|
||||
console.log(bucketPath);
|
||||
|
||||
const filesItr = bucket.listAllObjects({
|
||||
batchSize: 100,
|
||||
prefix: bucketPath,
|
||||
// delimiter: "/",
|
||||
});
|
||||
|
||||
let files: { name: string; size: number }[] = [];
|
||||
let directories: Set<string> = new Set();
|
||||
let readme: string | null = null;
|
||||
for await (let file of filesItr) {
|
||||
const relPath = Path.posix.relative(bucketPath, file.key || "");
|
||||
if (relPath.indexOf("/") >= 0) {
|
||||
directories.add(relPath.split("/")[0]);
|
||||
} else {
|
||||
files.push({ name: relPath, size: file.size || -1 });
|
||||
if (relPath.toLowerCase() === "readme.md") {
|
||||
let readmeCont = await getFile(
|
||||
packageName,
|
||||
packageVersion,
|
||||
Path.posix.join(path, relPath)
|
||||
);
|
||||
if (readmeCont) {
|
||||
readme = new TextDecoder().decode(readmeCont?.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.render("browse_folder", {
|
||||
pkg,
|
||||
version: packageVersion,
|
||||
readme,
|
||||
files,
|
||||
directories: Array.from(directories.values()).map((e) => ({
|
||||
name: e,
|
||||
})),
|
||||
path: `${packageName}@${packageVersion}/${path}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
g.get("/browse/:package", handleBrowse);
|
||||
g.get("/browse/:package/*path", handleBrowse);
|
||||
}
|
||||
|
@ -5,6 +5,18 @@ if (!config.s3) {
|
||||
throw new Error("Config is missing [s3] section!");
|
||||
}
|
||||
|
||||
if (!config.s3.endpoint) {
|
||||
throw new Error("Config is missing s3.endpoint!");
|
||||
}
|
||||
|
||||
if (!config.s3.accessKey) {
|
||||
throw new Error("Config is missing s3.accessKey!");
|
||||
}
|
||||
|
||||
if (!config.s3.secretKey) {
|
||||
throw new Error("Config is missing s3.secretKey!");
|
||||
}
|
||||
|
||||
const bucket = new S3.S3Bucket({
|
||||
bucket: config.s3.bucket || "deno-registry",
|
||||
endpointURL: config.s3.endpoint,
|
||||
|
3
registry/src/test.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import * as Prism from "./vendor/prism/prism.js";
|
||||
|
||||
console.log((window as any).Prism);
|
8
registry/src/types/jsx.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
declare namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
[elemName: string]: any;
|
||||
}
|
||||
}
|
||||
interface IntrinsicElements {
|
||||
[elemName: string]: any;
|
||||
}
|
||||
}
|
||||
|
@ -87,30 +87,38 @@ export function extractPackagePath(path: string): [string, string | undefined] {
|
||||
return [packageName, packageVersion];
|
||||
}
|
||||
|
||||
import db from "./db.ts";
|
||||
import type { IPackage } from "./db.ts";
|
||||
|
||||
import bucket from "./s3.ts";
|
||||
|
||||
export async function getFile(
|
||||
pkgName: string,
|
||||
version: string | null | undefined,
|
||||
file: string
|
||||
): Promise<{ etag: string; data: Uint8Array } | null | undefined> {
|
||||
console.log("Searching for file: %s/%s@%s", pkgName, file, version);
|
||||
const meta = await db.package.findOne({ name: pkgName });
|
||||
export function getAbsolutePackageVersion(
|
||||
pkg?: IPackage | null,
|
||||
version?: string
|
||||
) {
|
||||
if (!pkg || pkg.versions.length < 1) return undefined;
|
||||
|
||||
if (!meta || meta.versions.length < 1) return null;
|
||||
|
||||
const versions = meta.versions.sort(sortVersions).reverse();
|
||||
const versions = pkg.versions.sort(sortVersions).reverse();
|
||||
|
||||
if (!version) {
|
||||
version = versions[0];
|
||||
} else {
|
||||
const v = versions.filter((e) => e.startsWith(version as string));
|
||||
if (v.length < 1) return null;
|
||||
if (v.length < 1) return undefined;
|
||||
version = v[0];
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
export async function getBucketFilePath(
|
||||
pkgName: string,
|
||||
version: string,
|
||||
file: string
|
||||
) {
|
||||
if (file.startsWith("/")) {
|
||||
file = file.substr(1);
|
||||
}
|
||||
|
||||
const bucketPath = (
|
||||
"packages/" +
|
||||
pkgName +
|
||||
@ -120,11 +128,24 @@ export async function getFile(
|
||||
file
|
||||
).replace(/@/g, "§");
|
||||
|
||||
return bucketPath;
|
||||
}
|
||||
|
||||
export async function getFile(
|
||||
pkgName: string,
|
||||
version: string | undefined,
|
||||
file: string
|
||||
): Promise<{ etag: string; data: Uint8Array } | null | undefined> {
|
||||
if (!version) return undefined;
|
||||
const bucketPath = await getBucketFilePath(pkgName, version, file);
|
||||
if (!bucketPath) return null;
|
||||
|
||||
console.log("Getting file from:", bucketPath);
|
||||
|
||||
try {
|
||||
const res = await bucket.getObject(bucketPath);
|
||||
if (!res) return undefined;
|
||||
if (!res || res.body.byteLength === 0) return undefined;
|
||||
|
||||
return {
|
||||
etag: res.etag,
|
||||
data: res.body,
|
||||
|
7
registry/src/vendor/prism.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import "./prism/prism.js";
|
||||
|
||||
const Prism = (window as any).Prism;
|
||||
|
||||
delete (window as any).Prism;
|
||||
|
||||
export default Prism;
|
218
registry/src/vendor/prism/prism.css
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
/* PrismJS 1.22.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+agda+al+antlr4+apacheconf+apl+applescript+aql+arduino+arff+asciidoc+aspnet+asm6502+autohotkey+autoit+bash+basic+batch+bbcode+birb+bison+bnf+brainfuck+brightscript+bro+bsl+c+csharp+cpp+cil+clojure+cmake+coffeescript+concurnas+csp+crystal+css-extras+cypher+d+dart+dax+dhall+diff+django+dns-zone-file+docker+ebnf+editorconfig+eiffel+ejs+elixir+elm+etlua+erb+erlang+excel-formula+fsharp+factor+firestore-security-rules+flow+fortran+ftl+gml+gcode+gdscript+gedcom+gherkin+git+glsl+go+graphql+groovy+haml+handlebars+haskell+haxe+hcl+hlsl+http+hpkp+hsts+ichigojam+icon+ignore+inform7+ini+io+j+java+javadoc+javadoclike+javastacktrace+jolie+jq+jsdoc+js-extras+json+json5+jsonp+jsstacktrace+js-templates+julia+keyman+kotlin+latex+latte+less+lilypond+liquid+lisp+livescript+llvm+lolcode+lua+makefile+markdown+markup-templating+matlab+mel+mizar+mongodb+monkey+moonscript+n1ql+n4js+nand2tetris-hdl+naniscript+nasm+neon+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+peoplecode+perl+php+phpdoc+php-extras+plsql+powerquery+powershell+processing+prolog+properties+protobuf+pug+puppet+pure+purebasic+purescript+python+q+qml+qore+r+racket+jsx+tsx+reason+regex+renpy+rest+rip+roboconf+robotframework+ruby+rust+sas+sass+scss+scala+scheme+shell-session+smali+smalltalk+smarty+sml+solidity+solution-file+soy+sparql+splunk-spl+sqf+sql+stan+iecst+stylus+swift+t4-templating+t4-cs+t4-vb+tap+tcl+tt2+textile+toml+turtle+twig+typescript+typoscript+unrealscript+vala+vbnet+velocity+verilog+vhdl+vim+visual-basic+warpscript+wasm+wiki+xeora+xml-doc+xojo+xquery+yaml+yang+zig&plugins=line-numbers+inline-color */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
font-size: 1em;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection, code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.token.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
/* This background color was intended by the author of this theme. */
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers {
|
||||
position: relative;
|
||||
padding-left: 3.8em;
|
||||
counter-reset: linenumber;
|
||||
}
|
||||
|
||||
pre[class*="language-"].line-numbers > code {
|
||||
position: relative;
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.line-numbers .line-numbers-rows {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
font-size: 100%;
|
||||
left: -3.8em;
|
||||
width: 3em; /* works for line-numbers below 1000 lines */
|
||||
letter-spacing: -1px;
|
||||
border-right: 1px solid #999;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
}
|
||||
|
||||
.line-numbers-rows > span {
|
||||
display: block;
|
||||
counter-increment: linenumber;
|
||||
}
|
||||
|
||||
.line-numbers-rows > span:before {
|
||||
content: counter(linenumber);
|
||||
color: #999;
|
||||
display: block;
|
||||
padding-right: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
span.inline-color-wrapper {
|
||||
/*
|
||||
* The background image is the following SVG inline in base 64:
|
||||
*
|
||||
* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2 2">
|
||||
* <path fill="gray" d="M0 0h2v2H0z"/>
|
||||
* <path fill="white" d="M0 0h1v1H0zM1 1h1v1H1z"/>
|
||||
* </svg>
|
||||
*
|
||||
* SVG-inlining explained:
|
||||
* https://stackoverflow.com/a/21626701/7595472
|
||||
*/
|
||||
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyIDIiPjxwYXRoIGZpbGw9ImdyYXkiIGQ9Ik0wIDBoMnYySDB6Ii8+PHBhdGggZmlsbD0id2hpdGUiIGQ9Ik0wIDBoMXYxSDB6TTEgMWgxdjFIMXoiLz48L3N2Zz4=");
|
||||
/* This is to prevent visual glitches where one pixel from the repeating pattern could be seen. */
|
||||
background-position: center;
|
||||
background-size: 110%;
|
||||
|
||||
display: inline-block;
|
||||
height: 1.333ch;
|
||||
width: 1.333ch;
|
||||
margin: 0 .333ch;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid white;
|
||||
outline: 1px solid rgba(0,0,0,.5);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
span.inline-color {
|
||||
display: block;
|
||||
/* To prevent visual glitches again */
|
||||
height: 120%;
|
||||
width: 120%;
|
||||
}
|
||||
|
239
registry/src/vendor/prism/prism.js
vendored
Normal file
@ -5,6 +5,7 @@ const styles = new TextDecoder().decode(
|
||||
Deno.readFileSync("src/views/styles.css")
|
||||
);
|
||||
|
||||
// href="https://unpkg.com/papercss@1.6.1/dist/paper.min.css"
|
||||
export default function Base(p: any, children: any[]) {
|
||||
const title = p.title || "DenReg";
|
||||
return (
|
||||
@ -14,9 +15,86 @@ export default function Base(p: any, children: any[]) {
|
||||
rel="stylesheet"
|
||||
href="https://deno.hibas123.de/raw/@hibas123-theme@2.0.2/out/base.css"
|
||||
/> */}
|
||||
<link rel="stylesheet" href="/public/paper.min.css" />
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="57x57"
|
||||
href="/public/apple-icon-57x57.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="60x60"
|
||||
href="/public/apple-icon-60x60.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="72x72"
|
||||
href="/public/apple-icon-72x72.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="76x76"
|
||||
href="/public/apple-icon-76x76.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="114x114"
|
||||
href="/public/apple-icon-114x114.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="120x120"
|
||||
href="/public/apple-icon-120x120.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="144x144"
|
||||
href="/public/apple-icon-144x144.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="152x152"
|
||||
href="/public/apple-icon-152x152.png"
|
||||
/>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="/public/apple-icon-180x180.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="192x192"
|
||||
href="/public/android-icon-192x192.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="/public/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="96x96"
|
||||
href="/public/favicon-96x96.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="/public/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/public/manifest.json" />
|
||||
<meta name="msapplication-TileColor" content="#ffffff" />
|
||||
<meta
|
||||
name="msapplication-TileImage"
|
||||
content="/public/ms-icon-144x144.png"
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff"></meta>
|
||||
<link
|
||||
href="https://unpkg.com/prismjs/themes/prism.css"
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/papercss@1.6.1/dist/paper.min.css"
|
||||
/>
|
||||
<style innerHTML={styles}></style>
|
||||
<title>{title}</title>
|
||||
|
113
registry/src/views/_browse.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React, Marked } from "../deps.ts";
|
||||
import type { IPackage } from "../db.ts";
|
||||
import { sortVersions } from "../utils.ts";
|
||||
|
||||
import Prism from "../vendor/prism.ts";
|
||||
|
||||
interface IEntryParams {
|
||||
name: string;
|
||||
size?: number;
|
||||
directory?: true;
|
||||
}
|
||||
|
||||
export function Entry({ name, size, directory }: IEntryParams) {
|
||||
return (
|
||||
<a class="browse-list-item" href={"./" + name + (directory ? "/" : "")}>
|
||||
<img src={directory ? "/public/folder.svg" : "/public/file.svg"} />
|
||||
<span>{name}</span>
|
||||
{size && <span class="browse-list-item-size">{size}</span>}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
interface IEntryListParams {
|
||||
files: { name: string; size: number }[];
|
||||
directories: { name: string }[];
|
||||
}
|
||||
|
||||
export function EntryList({ directories, files }: IEntryListParams) {
|
||||
return (
|
||||
<div class="card" style="padding: 1rem;">
|
||||
<div>
|
||||
{directories.map((e) => (
|
||||
<Entry name={e.name} directory />
|
||||
))}
|
||||
{files.map((e) => (
|
||||
<Entry name={e.name} size={e.size} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface IRenderFileInterface {
|
||||
content: string;
|
||||
ext: string;
|
||||
}
|
||||
|
||||
const languages: { [key: string]: string } = {
|
||||
js: "javascript",
|
||||
cjs: "javascript",
|
||||
mjs: "javascript",
|
||||
ts: "typescript",
|
||||
c: "clike",
|
||||
svelte: "html",
|
||||
cs: "csharp",
|
||||
hb: "handlebars",
|
||||
ps: "powershell",
|
||||
sh: "bash",
|
||||
bat: "batch",
|
||||
yml: "yaml",
|
||||
};
|
||||
|
||||
export function RenderFile({ content, ext }: IRenderFileInterface) {
|
||||
if (ext === ".md") {
|
||||
content = Marked.parse(content).content;
|
||||
return (
|
||||
<div
|
||||
class="card browse-code-block"
|
||||
style="margin-top: 1rem; padding: 1rem;"
|
||||
innerHTML={content}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
let lang = languages[ext.replace(".", "")] || ext.replace(".", "");
|
||||
|
||||
if (Prism.languages[lang]) {
|
||||
content = Prism.highlight(content, Prism.languages[lang], lang);
|
||||
}
|
||||
|
||||
return <pre innerHTML={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
interface IBrowseHeaderParams {
|
||||
pkg: IPackage;
|
||||
version?: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export function BrowseHeader({ pkg, version, path }: IBrowseHeaderParams) {
|
||||
return (
|
||||
<>
|
||||
<div style="display: flex;">
|
||||
<div>
|
||||
<h2 style="margin-bottom: 0">Browse: {pkg.name}</h2>
|
||||
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
||||
By {pkg.owner}
|
||||
</h4>
|
||||
</div>
|
||||
<div>
|
||||
Version:
|
||||
<select>
|
||||
{pkg.versions.sort(sortVersions).map((v) => (
|
||||
<option selected={version === v}>{v}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="browse-path">{path}</div>
|
||||
</>
|
||||
);
|
||||
}
|
20
registry/src/views/_pkgheader.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React, Fragment, Marked } from "../deps.ts";
|
||||
import type { IPackage } from "../db.ts";
|
||||
|
||||
export default async function index({
|
||||
pkg,
|
||||
version,
|
||||
}: {
|
||||
pkg: IPackage;
|
||||
version?: string;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<h2 style="margin-bottom: 0">Package: {pkg.name}</h2>
|
||||
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
||||
By {pkg.owner}
|
||||
</h4>
|
||||
</>
|
||||
);
|
||||
}
|
38
registry/src/views/browse_file.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React } from "../deps.ts";
|
||||
import Base from "./_base.tsx";
|
||||
import type { IPackage } from "../db.ts";
|
||||
|
||||
import { Main, Menu } from "./_default.tsx";
|
||||
import { RenderFile, EntryList, BrowseHeader } from "./_browse.tsx";
|
||||
|
||||
export default async function index({
|
||||
pkg,
|
||||
version,
|
||||
content,
|
||||
ext,
|
||||
path,
|
||||
}: {
|
||||
pkg: IPackage;
|
||||
version?: string;
|
||||
content: string;
|
||||
ext: string;
|
||||
path: string;
|
||||
}) {
|
||||
if (!pkg)
|
||||
return (
|
||||
<Base>
|
||||
<h1>Not found</h1>
|
||||
</Base>
|
||||
);
|
||||
|
||||
return (
|
||||
<Base title={"DenReg - " + pkg.name}>
|
||||
<Main>
|
||||
<BrowseHeader pkg={pkg} version={version} path={path} />
|
||||
<RenderFile content={content} ext={ext} />
|
||||
</Main>
|
||||
<Menu></Menu>
|
||||
</Base>
|
||||
);
|
||||
}
|
43
registry/src/views/browse_folder.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React } from "../deps.ts";
|
||||
import Base from "./_base.tsx";
|
||||
import type { IPackage } from "../db.ts";
|
||||
|
||||
import { Main, Menu } from "./_default.tsx";
|
||||
import { RenderFile, EntryList, BrowseHeader } from "./_browse.tsx";
|
||||
|
||||
export default async function index({
|
||||
pkg,
|
||||
version,
|
||||
files,
|
||||
directories,
|
||||
readme,
|
||||
path,
|
||||
}: {
|
||||
pkg: IPackage;
|
||||
version?: string;
|
||||
files: { name: string; size: number }[];
|
||||
directories: { name: string }[];
|
||||
readme?: string;
|
||||
path: string;
|
||||
}) {
|
||||
if (!pkg)
|
||||
return (
|
||||
<Base>
|
||||
<h1>Not found</h1>
|
||||
</Base>
|
||||
);
|
||||
|
||||
return (
|
||||
<Base title={"DenReg - " + pkg.name}>
|
||||
<Main>
|
||||
<BrowseHeader pkg={pkg} version={version} path={path} />
|
||||
|
||||
<EntryList directories={directories} files={files} />
|
||||
|
||||
{readme && <RenderFile content={readme} ext={".md"} />}
|
||||
</Main>
|
||||
<Menu></Menu>
|
||||
</Base>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React, Fragment } from "../deps.ts";
|
||||
import Base from "./_base.tsx";
|
||||
import DB, { IPackage } from "../db.ts";
|
||||
import type { IPackage } from "../db.ts";
|
||||
import { sortVersions } from "../utils.ts";
|
||||
|
||||
function Package({ pkg }: { pkg: IPackage }) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
/// <reference path="../types/jsx.d.ts" />
|
||||
import { React, Fragment, Marked } from "../deps.ts";
|
||||
import Base from "./_base.tsx";
|
||||
import DB, { IPackage } from "../db.ts";
|
||||
import { sortVersions, getFile } from "../utils.ts";
|
||||
|
||||
import type { IPackage } from "../db.ts";
|
||||
import { sortVersions, getFile, getAbsolutePackageVersion } from "../utils.ts";
|
||||
import PkgHeader from "./_pkgheader.tsx";
|
||||
// function Package({ pkg }: { pkg: IPackage }) {
|
||||
// const { name, versions, author } = pkg;
|
||||
|
||||
@ -44,6 +44,7 @@ export default async function index({
|
||||
</Base>
|
||||
);
|
||||
|
||||
version = getAbsolutePackageVersion(pkg, version);
|
||||
const readmeContent = await getFile(pkg.name, version, "README.md").then(
|
||||
(res) => {
|
||||
if (res)
|
||||
@ -56,23 +57,13 @@ export default async function index({
|
||||
return (
|
||||
<Base title={"DenReg - " + pkg.name}>
|
||||
<Main>
|
||||
<h2 style="margin-bottom: 0">Package: {pkg.name}</h2>
|
||||
<h4 class="text-muted" style="margin-top: 0; margin-left: .5rem">
|
||||
By {pkg.owner}
|
||||
</h4>
|
||||
|
||||
<PkgHeader pkg={pkg} version={version} />
|
||||
<div class="tabs">
|
||||
<input id="tab1" type="radio" name="tabs" checked />
|
||||
<label for="tab1">Readme</label>
|
||||
|
||||
<input id="tab2" type="radio" name="tabs" />
|
||||
<label for="tab2">Versions</label>
|
||||
{/*
|
||||
<input id="tab3" type="radio" name="tabs" />
|
||||
<label for="tab3">Tab 3</label>
|
||||
|
||||
<input id="tab4" type="radio" name="tabs" />
|
||||
<label for="tab4">Tab 4</label> */}
|
||||
<div class="content" id="content1">
|
||||
{readmeContent !== undefined ? (
|
||||
<div
|
||||
@ -95,7 +86,11 @@ export default async function index({
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
<Menu></Menu>
|
||||
<Menu>
|
||||
<a href={`/browse/${pkg.name}${version ? "@" + version : ""}/`}>
|
||||
Browse Files
|
||||
</a>
|
||||
</Menu>
|
||||
</Base>
|
||||
);
|
||||
}
|
||||
|
@ -27,3 +27,34 @@ code {
|
||||
margin: 1rem 0;
|
||||
}
|
||||
}
|
||||
|
||||
.browse-list-item {
|
||||
display: flex;
|
||||
margin: 0.2rem;
|
||||
text-decoration: none;
|
||||
color: unset;
|
||||
background-image: unset;
|
||||
|
||||
/* flex */
|
||||
}
|
||||
|
||||
.browse-list-item:hover {
|
||||
background-color: var(--muted-light);
|
||||
}
|
||||
|
||||
.browse-list-item > img {
|
||||
height: 1rem;
|
||||
width: auto;
|
||||
border: none !important;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.browse-list-item-size {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.browse-path {
|
||||
margin: 1rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|