diff --git a/migrateDB.ts b/migrateDB.ts index b5924f9..aaab23f 100644 --- a/migrateDB.ts +++ b/migrateDB.ts @@ -164,18 +164,21 @@ async function connect(db) { console.error(`Repository ${r.fullName} is not found (renamed)`); } } - let size = 0; + let size = { storage: 0, file: 0 }; function recursiveCount(files) { - let total = 0; + const out = { storage: 0, file: 0 }; for (const name in files) { const file = files[name]; if (file.size && file.sha && parseInt(file.size) == file.size) { - total += file.size as number; + out.storage += file.size as number; + out.file++; } else if (typeof file == "object") { - total += recursiveCount(file); + const r = recursiveCount(file); + out.storage += r.storage; + out.file += r.file; } } - return total; + return out; } if (r.originalFiles) { diff --git a/public/i18n/locale-en.json b/public/i18n/locale-en.json index a84e234..9bf6769 100644 --- a/public/i18n/locale-en.json +++ b/public/i18n/locale-en.json @@ -9,6 +9,7 @@ "repoId_already_used": "The repository id is already used.", "invalid_repoId": "The format of the repository id is invalid.", "branch_not_specified": "The branch is not specified.", + "branch_not_found": "The branch of the repository cannot be found.", "options_not_provided": "Anonymization options are mandatory.", "invalid_terms_format": "Terms are in an invalid format.", "unable_to_anonymize": "An error happened during the anonymization process. Please try later or report the issue.", diff --git a/public/partials/dashboard.htm b/public/partials/dashboard.htm index fd901c2..638eafe 100644 --- a/public/partials/dashboard.htm +++ b/public/partials/dashboard.htm @@ -152,6 +152,67 @@ + +
+ Repository +
+
+ {{quota.repository.used | + number}}/{{quota.repository.total}} +
+
+
+ +
+ Storage +
+
+ {{quota.storage.used | + humanFileSize}}/{{quota.storage.total| + humanFileSize}} +
+
+
+ +
+ File +
+
+ {{quota.file.used | number}}/{{quota.file.total || + "∞"}} +
+
+
@@ -235,7 +296,7 @@ data-toggle="tooltip" data-placement="bottom" > - {{::repo.size | + {{::repo.size.storage | humanFileSize}} +

Quota

+

Quota

+
Repository
+
+
+ {{quota.repository.used | number}}/{{quota.repository.total}} +
+
+
Storage
+
+
+ {{quota.storage.used | humanFileSize}}/{{quota.storage.total| + humanFileSize}} +
+
+
File
+
+
+ {{quota.file.used | number}}/{{quota.file.total || "∞"}} +
+
+

Default anonymization options

diff --git a/public/script/app.js b/public/script/app.js index 0a4df51..df12c08 100644 --- a/public/script/app.js +++ b/public/script/app.js @@ -406,6 +406,7 @@ angular $http.get("/api/user").then( (res) => { if (res) $scope.user = res.data; + getQuota(); }, () => { $scope.user = null; @@ -425,6 +426,22 @@ angular ); } getOptions(); + function getQuota() { + $http.get("/api/user/quota").then((res) => { + $scope.quota = res.data; + $scope.quota.storage.percent = $scope.quota.storage.total + ? ($scope.quota.storage.used * 100) / $scope.quota.storage.total + : 100; + $scope.quota.file.percent = $scope.quota.file.total + ? ($scope.quota.file.used * 100) / $scope.quota.file.total + : 100; + $scope.quota.repository.percent = $scope.quota.repository.total + ? ($scope.quota.repository.used * 100) / + $scope.quota.repository.total + : 100; + }, console.error); + } + getQuota(); function getMessage() { $http.get("/api/message").then( @@ -605,13 +622,6 @@ angular } getRepositories(); - function getQuota() { - $http.get("/api/user/quota").then((res) => { - $scope.quota = res.data; - }, console.error); - } - // getQuota(); - $scope.removeRepository = (repo) => { if ( confirm( diff --git a/src/Repository.ts b/src/Repository.ts index 3b6ef20..5f0263d 100644 --- a/src/Repository.ts +++ b/src/Repository.ts @@ -82,7 +82,7 @@ export default class Repository { } const files = await this.source.getFiles(); this._model.originalFiles = files; - this._model.size = 0; + this._model.size = { storage: 0, file: 0 }; await this.computeSize(); await this._model.save(); @@ -149,14 +149,21 @@ export default class Repository { const branch = this.source.branch; if ( branch.commit == - branches.filter((f) => f.name == branch.name)[0].commit + branches.filter((f) => f.name == branch.name)[0]?.commit ) { console.log(`${this._model.repoId} is up to date`); return; } this._model.source.commit = branches.filter( (f) => f.name == branch.name - )[0].commit; + )[0]?.commit; + + if (!this._model.source.commit) { + console.error( + `${branch.name} for ${this.source.githubRepository.fullName} is not found` + ); + throw new Error("branch_not_found"); + } this._model.anonymizeDate = new Date(); console.log( `${this._model.repoId} will be updated to ${this._model.source.commit}` @@ -218,7 +225,7 @@ export default class Repository { */ private async resetSate(status?: RepositoryStatus) { if (status) this._model.status = status; - this._model.size = 0; + this._model.size = { storage: 0, file: 0 }; this._model.originalFiles = null; return Promise.all([ this._model.save(), @@ -227,27 +234,39 @@ export default class Repository { } /** - * Compute the size of the repository in bite. + * Compute the size of the repository in term of storage and number of files. * * @returns The size of the repository in bite */ - async computeSize(): Promise { - if (this._model.status != "ready") return 0; - if (this._model.size) return this._model.size; + async computeSize(): Promise<{ + /** + * Size of the repository in bit + */ + storage: number; + /** + * The number of files + */ + file: number; + }> { + if (this._model.status != "ready") return { storage: 0, file: 0 }; + if (this._model.size.file) return this._model.size; function recursiveCount(files) { - let total = 0; + const out = { storage: 0, file: 0 }; for (const name in files) { const file = files[name]; if (file.size) { - total += file.size as number; + out.storage += file.size as number; + out.file++; } else if (typeof file == "object") { - total += recursiveCount(file); + const r = recursiveCount(file); + out.storage += r.storage; + out.file += r.file; } } - return total; + return out; } - const files = await this.files({ force: false }); + const files = await this.files(); this._model.size = recursiveCount(files); await this._model.save(); return this._model.size; @@ -291,7 +310,7 @@ export default class Repository { } get size() { - if (this._model.status != "ready") return 0; + if (this._model.status != "ready") return { storage: 0, file: 0 }; return this._model.size; } diff --git a/src/database/anonymizedRepositories/anonymizedRepositories.schema.ts b/src/database/anonymizedRepositories/anonymizedRepositories.schema.ts index fe72ec9..0fb65c9 100644 --- a/src/database/anonymizedRepositories/anonymizedRepositories.schema.ts +++ b/src/database/anonymizedRepositories/anonymizedRepositories.schema.ts @@ -46,8 +46,14 @@ const AnonymizedRepositorySchema = new Schema({ default: new Date(), }, size: { - type: Number, - default: 0, + storage: { + type: Number, + default: 0, + }, + file: { + type: Number, + default: 0, + }, }, }); diff --git a/src/database/anonymizedRepositories/anonymizedRepositories.types.ts b/src/database/anonymizedRepositories/anonymizedRepositories.types.ts index 9953fb0..1d038b7 100644 --- a/src/database/anonymizedRepositories/anonymizedRepositories.types.ts +++ b/src/database/anonymizedRepositories/anonymizedRepositories.types.ts @@ -34,7 +34,10 @@ export interface IAnonymizedRepository { }; pageView: number; lastView: Date; - size: number; + size: { + storage: number; + file: number; + }; } export interface IAnonymizedRepositoryDocument diff --git a/src/routes/user.ts b/src/routes/user.ts index 43ad669..da6771b 100644 --- a/src/routes/user.ts +++ b/src/routes/user.ts @@ -29,14 +29,25 @@ router.get("/", async (req: express.Request, res: express.Response) => { router.get("/quota", async (req: express.Request, res: express.Response) => { try { const user = await getUser(req); + const repositories = await user.getRepositories(); const sizes = await Promise.all( - (await user.getRepositories()) + repositories .filter((r) => r.status == "ready") .map((r) => r.computeSize()) ); res.json({ - used: sizes.reduce((sum, i) => sum + i, 0), - total: config.DEFAULT_QUOTA, + storage: { + used: sizes.reduce((sum, i) => sum + i.storage, 0), + total: config.DEFAULT_QUOTA, + }, + file: { + used: sizes.reduce((sum, i) => sum + i.file, 0), + total: 0, + }, + repository: { + used: repositories.length, + total: 20, + }, }); } catch (error) { handleError(error, res);