diff --git a/package-lock.json b/package-lock.json index a057956..893a8e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "bullmq": "^2.4.0", "compression": "^1.7.4", "connect-redis": "^7.0.1", + "crypto-js": "^4.2.0", "decompress-stream-to-s3": "^2.1.1", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -49,11 +50,12 @@ "unzip-stream": "^0.3.1" }, "bin": { - "anonymous_github": "build/cli.js" + "anonymous_github": "build/cli/index.js" }, "devDependencies": { "@types/archiver": "^5.3.4", "@types/compression": "^1.7.5", + "@types/crypto-js": "^4.2.2", "@types/express": "^4.17.21", "@types/express-session": "^1.18.0", "@types/got": "^9.6.12", @@ -4160,6 +4162,12 @@ "@types/node": "*" } }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -5600,6 +5608,11 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "node_modules/crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -13494,6 +13507,12 @@ "@types/node": "*" } }, + "@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, "@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", @@ -14654,6 +14673,11 @@ } } }, + "crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", diff --git a/package.json b/package.json index 3410b66..de30e4e 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "bullmq": "^2.4.0", "compression": "^1.7.4", "connect-redis": "^7.0.1", + "crypto-js": "^4.2.0", "decompress-stream-to-s3": "^2.1.1", "dotenv": "^16.4.5", "express": "^4.19.2", @@ -72,6 +73,7 @@ "devDependencies": { "@types/archiver": "^5.3.4", "@types/compression": "^1.7.5", + "@types/crypto-js": "^4.2.2", "@types/express": "^4.17.21", "@types/express-session": "^1.18.0", "@types/got": "^9.6.12", diff --git a/public/partials/explorer.htm b/public/partials/explorer.htm index 010fb95..9177e1c 100644 --- a/public/partials/explorer.htm +++ b/public/partials/explorer.htm @@ -34,7 +34,7 @@ >View raw Download file= thresh && u < units.length - 1); - - return bytes.toFixed(dp) + "" + units[u]; - }; + return humanFileSize; }) .filter("humanTime", function () { return function humanTime(seconds) { @@ -325,7 +304,7 @@ angular for (let f of afiles) { let dir = isDir(f.child); let name = f.name; - let size = f.size; + let size = f.size || 0; if (dir) { let test = name; current = toArray(f.child); @@ -339,10 +318,15 @@ angular dir = false; } } + if (size) { + size = `Size: ${humanFileSize(size || 0)}`; + } else { + size = ""; + } const path = `${parentPath}/${name}`; output += `
  • `; + }" ng-class="{active: isActive('${path}'), open: opens['${path}']}" title="${size}">`; if (dir) { output += `${name}`; } else { @@ -1561,6 +1545,18 @@ angular ); } + function getSelectedFile() { + let currentFolder = $scope.files; + for (const p of $scope.paths) { + if (currentFolder[p]) { + currentFolder = currentFolder[p]; + } else { + return null; + } + } + return currentFolder; + } + async function getOptions(callback) { $http.get(`/api/repo/${$scope.repoId}/options`).then( (res) => { @@ -1614,7 +1610,7 @@ angular return "code"; } - function getContent(path) { + function getContent(path, fileInfo) { if (!path) { $scope.type = "error"; $scope.content = "no_file_selected"; @@ -1624,7 +1620,7 @@ angular $scope.type = "loading"; $scope.content = "loading"; $http - .get(`/api/repo/${$scope.repoId}/file/${path}`, { + .get(`/api/repo/${$scope.repoId}/file/${path}?v=` + fileInfo.sha, { transformResponse: (data) => { return data; }, @@ -1686,7 +1682,10 @@ angular function updateContent() { $scope.content = ""; - $scope.url = `/api/repo/${$scope.repoId}/file/${$scope.filePath}`; + $scope.file = getSelectedFile(); + $scope.url = + `/api/repo/${$scope.repoId}/file/${$scope.filePath}?v=` + + $scope.file.sha; let extension = $scope.filePath.toLowerCase(); const extensionIndex = extension.lastIndexOf("."); @@ -1784,7 +1783,7 @@ angular } $scope.type = getType(extension); - getContent($scope.filePath); + getContent($scope.filePath, $scope.file); } function init() { @@ -2431,3 +2430,28 @@ angular getConference(); }, ]); +function humanFileSize(bytes, si = false, dp = 1) { + const thresh = si ? 1000 : 1024; + + bytes = bytes / 8; + + if (Math.abs(bytes) < thresh) { + return bytes + "B"; + } + + const units = si + ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; + let u = -1; + const r = 10 ** dp; + + do { + bytes /= thresh; + ++u; + } while ( + Math.round(Math.abs(bytes) * r) / r >= thresh && + u < units.length - 1 + ); + + return bytes.toFixed(dp) + "" + units[u]; +} diff --git a/src/core/Repository.ts b/src/core/Repository.ts index 614f9ca..c369215 100644 --- a/src/core/Repository.ts +++ b/src/core/Repository.ts @@ -1,6 +1,7 @@ import storage from "./storage"; import { RepositoryStatus, Tree, TreeElement, TreeFile } from "./types"; import { Readable } from "stream"; +import * as sha1 from "crypto-js/sha1"; import User from "./User"; import GitHubStream from "./source/GitHubStream"; import GitHubDownload from "./source/GitHubDownload"; @@ -37,7 +38,10 @@ function anonymizeTreeRecursive( ): TreeElement { if (typeof tree.size !== "object" && tree.sha !== undefined) { if (opt?.includeSha) return tree as TreeFile; - return { size: tree.size } as TreeFile; + return { + size: tree.size, + sha: sha1(tree.sha as string).toString(), + } as TreeFile; } const output: Tree = {}; Object.getOwnPropertyNames(tree).forEach((file) => {