mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-07-06 13:47:59 +02:00
chore: use strict compilation mode
This commit is contained in:
@@ -10,6 +10,7 @@ import { tmpdir } from "os";
|
|||||||
import * as gh from "parse-github-url";
|
import * as gh from "parse-github-url";
|
||||||
import * as inquirer from "inquirer";
|
import * as inquirer from "inquirer";
|
||||||
|
|
||||||
|
import server from "./src/server";
|
||||||
import config from "./config";
|
import config from "./config";
|
||||||
import GitHubDownload from "./src/source/GitHubDownload";
|
import GitHubDownload from "./src/source/GitHubDownload";
|
||||||
import Repository from "./src/Repository";
|
import Repository from "./src/Repository";
|
||||||
@@ -91,7 +92,7 @@ async function main() {
|
|||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
if (process.argv[2] == "server") {
|
if (process.argv[2] == "server") {
|
||||||
// start the server
|
// start the server
|
||||||
require("./src/server").default();
|
server();
|
||||||
} else {
|
} else {
|
||||||
// use the cli interface
|
// use the cli interface
|
||||||
main();
|
main();
|
||||||
|
|||||||
@@ -68,11 +68,11 @@ const config: Config = {
|
|||||||
"in",
|
"in",
|
||||||
],
|
],
|
||||||
STORAGE: "filesystem",
|
STORAGE: "filesystem",
|
||||||
S3_BUCKET: null,
|
S3_BUCKET: process.env.S3_BUCKET,
|
||||||
S3_CLIENT_ID: null,
|
S3_CLIENT_ID: process.env.S3_CLIENT_ID,
|
||||||
S3_CLIENT_SECRET: null,
|
S3_CLIENT_SECRET: process.env.S3_CLIENT_SECRET,
|
||||||
S3_ENDPOINT: null,
|
S3_ENDPOINT: process.env.S3_ENDPOINT,
|
||||||
S3_REGION: null,
|
S3_REGION: process.env.S3_REGION,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let conf in process.env) {
|
for (let conf in process.env) {
|
||||||
|
|||||||
Generated
+3701
-1972
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -10,7 +10,7 @@
|
|||||||
"start": "node --inspect=5858 -r ts-node/register ./index.ts",
|
"start": "node --inspect=5858 -r ts-node/register ./index.ts",
|
||||||
"dev": "nodemon --transpile-only index.ts",
|
"dev": "nodemon --transpile-only index.ts",
|
||||||
"migrateDB": "ts-node --transpile-only migrateDB.ts",
|
"migrateDB": "ts-node --transpile-only migrateDB.ts",
|
||||||
"build": "tsc"
|
"build": "rm -rf build && tsc"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -64,7 +64,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/archiver": "^5.3.1",
|
"@types/archiver": "^5.3.1",
|
||||||
"@types/compression": "^1.7.1",
|
"@types/compression": "^1.7.1",
|
||||||
"@types/connect-redis": "^0.0.18",
|
"@types/connect-redis": "^0.0.20",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
"@types/express-rate-limit": "^6.0.0",
|
"@types/express-rate-limit": "^6.0.0",
|
||||||
"@types/express-session": "^1.17.5",
|
"@types/express-session": "^1.17.5",
|
||||||
@@ -73,10 +73,10 @@
|
|||||||
"@types/inquirer": "^8.0.0",
|
"@types/inquirer": "^8.0.0",
|
||||||
"@types/marked": "^4.0.7",
|
"@types/marked": "^4.0.7",
|
||||||
"@types/mime-types": "^2.1.0",
|
"@types/mime-types": "^2.1.0",
|
||||||
|
"@types/node-schedule": "^2.1.0",
|
||||||
"@types/parse-github-url": "^1.0.0",
|
"@types/parse-github-url": "^1.0.0",
|
||||||
"@types/passport": "^1.0.11",
|
"@types/passport": "^1.0.11",
|
||||||
"@types/passport-github2": "^1.2.5",
|
"@types/passport-github2": "^1.2.5",
|
||||||
"@types/rate-limit-redis": "^1.7.4",
|
|
||||||
"@types/tar-fs": "^2.0.1",
|
"@types/tar-fs": "^2.0.1",
|
||||||
"@types/unzip-stream": "^0.3.1",
|
"@types/unzip-stream": "^0.3.1",
|
||||||
"@types/xml-flow": "^1.0.1",
|
"@types/xml-flow": "^1.0.1",
|
||||||
|
|||||||
+11
-11
@@ -2,7 +2,7 @@ import { join, basename } from "path";
|
|||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import Repository from "./Repository";
|
import Repository from "./Repository";
|
||||||
import { TreeElement, TreeFile } from "./types";
|
import { Tree, TreeElement, TreeFile } from "./types";
|
||||||
import storage from "./storage";
|
import storage from "./storage";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import { anonymizePath, anonymizeStream } from "./anonymize-utils";
|
import { anonymizePath, anonymizeStream } from "./anonymize-utils";
|
||||||
@@ -13,7 +13,7 @@ import { handleError } from "./routes/route-utils";
|
|||||||
* Represent a file in a anonymized repository
|
* Represent a file in a anonymized repository
|
||||||
*/
|
*/
|
||||||
export default class AnonymizedFile {
|
export default class AnonymizedFile {
|
||||||
private _originalPath: string;
|
private _originalPath: string | undefined;
|
||||||
private fileSize?: number;
|
private fileSize?: number;
|
||||||
|
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
@@ -59,7 +59,7 @@ export default class AnonymizedFile {
|
|||||||
if (fileName == "") {
|
if (fileName == "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!currentOriginal[fileName]) {
|
if (!(currentOriginal as Tree)[fileName]) {
|
||||||
// anonymize all the file in the folder and check if there is one that match the current filename
|
// anonymize all the file in the folder and check if there is one that match the current filename
|
||||||
const options = [];
|
const options = [];
|
||||||
for (let originalFileName in currentOriginal) {
|
for (let originalFileName in currentOriginal) {
|
||||||
@@ -74,7 +74,7 @@ export default class AnonymizedFile {
|
|||||||
// if only one option we found the original filename
|
// if only one option we found the original filename
|
||||||
if (options.length == 1) {
|
if (options.length == 1) {
|
||||||
currentOriginalPath = join(currentOriginalPath, options[0]);
|
currentOriginalPath = join(currentOriginalPath, options[0]);
|
||||||
currentOriginal = currentOriginal[options[0]];
|
currentOriginal = (currentOriginal as Tree)[options[0]];
|
||||||
} else if (options.length == 0) {
|
} else if (options.length == 0) {
|
||||||
throw new AnonymousError("file_not_found", {
|
throw new AnonymousError("file_not_found", {
|
||||||
object: this,
|
object: this,
|
||||||
@@ -85,14 +85,14 @@ export default class AnonymizedFile {
|
|||||||
if (!nextName) {
|
if (!nextName) {
|
||||||
// if there is no next name we can't find the file and we return the first option
|
// if there is no next name we can't find the file and we return the first option
|
||||||
currentOriginalPath = join(currentOriginalPath, options[0]);
|
currentOriginalPath = join(currentOriginalPath, options[0]);
|
||||||
currentOriginal = currentOriginal[options[0]];
|
currentOriginal = (currentOriginal as Tree)[options[0]];
|
||||||
}
|
}
|
||||||
let found = false;
|
let found = false;
|
||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
const optionTree = currentOriginal[option];
|
const optionTree = (currentOriginal as Tree)[option];
|
||||||
if (optionTree.child) {
|
if ((optionTree as Tree).child) {
|
||||||
const optionTreeChild = optionTree.child;
|
const optionTreeChild = (optionTree as Tree).child;
|
||||||
if (optionTreeChild[nextName]) {
|
if ((optionTreeChild as Tree)[nextName]) {
|
||||||
currentOriginalPath = join(currentOriginalPath, option);
|
currentOriginalPath = join(currentOriginalPath, option);
|
||||||
currentOriginal = optionTreeChild;
|
currentOriginal = optionTreeChild;
|
||||||
found = true;
|
found = true;
|
||||||
@@ -103,12 +103,12 @@ export default class AnonymizedFile {
|
|||||||
if (!found) {
|
if (!found) {
|
||||||
// if we didn't find the next name we return the first option
|
// if we didn't find the next name we return the first option
|
||||||
currentOriginalPath = join(currentOriginalPath, options[0]);
|
currentOriginalPath = join(currentOriginalPath, options[0]);
|
||||||
currentOriginal = currentOriginal[options[0]];
|
currentOriginal = (currentOriginal as Tree)[options[0]];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentOriginalPath = join(currentOriginalPath, fileName);
|
currentOriginalPath = join(currentOriginalPath, fileName);
|
||||||
currentOriginal = currentOriginal[fileName];
|
currentOriginal = (currentOriginal as Tree)[fileName];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import { ConferenceStatus } from "./types";
|
|||||||
|
|
||||||
export default class Conference {
|
export default class Conference {
|
||||||
private _data: IConferenceDocument;
|
private _data: IConferenceDocument;
|
||||||
private _repositories: Repository[] = null;
|
private _repositories: Repository[] = [];
|
||||||
|
|
||||||
constructor(data: IConferenceDocument) {
|
constructor(data: IConferenceDocument) {
|
||||||
this._data = data;
|
this._data = data;
|
||||||
|
|||||||
+18
-8
@@ -56,22 +56,22 @@ export default class PullRequest {
|
|||||||
this._model.pullRequest = {
|
this._model.pullRequest = {
|
||||||
diff: diff.body,
|
diff: diff.body,
|
||||||
title: prInfo.data.title,
|
title: prInfo.data.title,
|
||||||
body: prInfo.data.body,
|
body: prInfo.data.body || "",
|
||||||
creationDate: new Date(prInfo.data.created_at),
|
creationDate: new Date(prInfo.data.created_at),
|
||||||
updatedDate: new Date(prInfo.data.updated_at),
|
updatedDate: new Date(prInfo.data.updated_at),
|
||||||
draft: prInfo.data.draft,
|
draft: prInfo.data.draft,
|
||||||
merged: prInfo.data.merged,
|
merged: prInfo.data.merged,
|
||||||
mergedDate: prInfo.data.merged_at
|
mergedDate: prInfo.data.merged_at
|
||||||
? new Date(prInfo.data.merged_at)
|
? new Date(prInfo.data.merged_at)
|
||||||
: null,
|
: undefined,
|
||||||
state: prInfo.data.state,
|
state: prInfo.data.state,
|
||||||
baseRepositoryFullName: prInfo.data.base.repo.full_name,
|
baseRepositoryFullName: prInfo.data.base.repo.full_name,
|
||||||
headRepositoryFullName: prInfo.data.head.repo.full_name,
|
headRepositoryFullName: prInfo.data.head.repo?.full_name,
|
||||||
comments: comments.data.map((comment) => ({
|
comments: comments.data.map((comment) => ({
|
||||||
body: comment.body,
|
body: comment.body || "",
|
||||||
creationDate: new Date(comment.created_at),
|
creationDate: new Date(comment.created_at),
|
||||||
updatedDate: new Date(comment.updated_at),
|
updatedDate: new Date(comment.updated_at),
|
||||||
author: comment.user.login,
|
author: comment.user?.login || "",
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -82,7 +82,8 @@ export default class PullRequest {
|
|||||||
check() {
|
check() {
|
||||||
if (
|
if (
|
||||||
this._model.options.expirationMode !== "never" &&
|
this._model.options.expirationMode !== "never" &&
|
||||||
this.status == "ready"
|
this.status == "ready" &&
|
||||||
|
this._model.options.expirationDate
|
||||||
) {
|
) {
|
||||||
if (this._model.options.expirationDate <= new Date()) {
|
if (this._model.options.expirationDate <= new Date()) {
|
||||||
this.expire();
|
this.expire();
|
||||||
@@ -187,7 +188,16 @@ export default class PullRequest {
|
|||||||
if (status) this._model.status = status;
|
if (status) this._model.status = status;
|
||||||
if (statusMessage) this._model.statusMessage = statusMessage;
|
if (statusMessage) this._model.statusMessage = statusMessage;
|
||||||
// remove cache
|
// remove cache
|
||||||
this._model.pullRequest = null;
|
this._model.pullRequest.comments = [];
|
||||||
|
this._model.pullRequest.body = "";
|
||||||
|
this._model.pullRequest.title = "";
|
||||||
|
this._model.pullRequest.diff = "";
|
||||||
|
this._model.pullRequest.baseRepositoryFullName = "";
|
||||||
|
this._model.pullRequest.headRepositoryFullName = "";
|
||||||
|
this._model.pullRequest.merged = false;
|
||||||
|
this._model.pullRequest.mergedDate = undefined;
|
||||||
|
this._model.pullRequest.state = "closed";
|
||||||
|
this._model.pullRequest.draft = false;
|
||||||
return Promise.all([this._model.save()]);
|
return Promise.all([this._model.save()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +232,7 @@ export default class PullRequest {
|
|||||||
output.body = anonymizeContent(this._model.pullRequest.body, this);
|
output.body = anonymizeContent(this._model.pullRequest.body, this);
|
||||||
}
|
}
|
||||||
if (this.options.comments) {
|
if (this.options.comments) {
|
||||||
output.comments = this._model.pullRequest.comments.map((comment) => {
|
output.comments = this._model.pullRequest.comments?.map((comment) => {
|
||||||
const o: any = {};
|
const o: any = {};
|
||||||
if (this.options.body) o.body = anonymizeContent(comment.body, this);
|
if (this.options.body) o.body = anonymizeContent(comment.body, this);
|
||||||
if (this.options.username)
|
if (this.options.username)
|
||||||
|
|||||||
+16
-8
@@ -1,7 +1,7 @@
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import storage from "./storage";
|
import storage from "./storage";
|
||||||
import { RepositoryStatus, Source, Tree, TreeElement, TreeFile } from "./types";
|
import { RepositoryStatus, Source, Tree, TreeElement, TreeFile } from "./types";
|
||||||
import { Readable } from "stream";
|
import { Readable, Transform } from "stream";
|
||||||
import User from "./User";
|
import User from "./User";
|
||||||
import GitHubStream from "./source/GitHubStream";
|
import GitHubStream from "./source/GitHubStream";
|
||||||
import GitHubDownload from "./source/GitHubDownload";
|
import GitHubDownload from "./source/GitHubDownload";
|
||||||
@@ -14,7 +14,7 @@ import GitHubBase from "./source/GitHubBase";
|
|||||||
import Conference from "./Conference";
|
import Conference from "./Conference";
|
||||||
import ConferenceModel from "./database/conference/conferences.model";
|
import ConferenceModel from "./database/conference/conferences.model";
|
||||||
import AnonymousError from "./AnonymousError";
|
import AnonymousError from "./AnonymousError";
|
||||||
import { downloadQueue } from "./queue";
|
import { downloadQueue, removeQueue } from "./queue";
|
||||||
import { isConnected } from "./database/database";
|
import { isConnected } from "./database/database";
|
||||||
import AnonymizedFile from "./AnonymizedFile";
|
import AnonymizedFile from "./AnonymizedFile";
|
||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
@@ -36,7 +36,11 @@ function anonymizeTreeRecursive(
|
|||||||
const output: Tree = {};
|
const output: Tree = {};
|
||||||
Object.getOwnPropertyNames(tree).forEach((file) => {
|
Object.getOwnPropertyNames(tree).forEach((file) => {
|
||||||
const anonymizedPath = anonymizePath(file, terms);
|
const anonymizedPath = anonymizePath(file, terms);
|
||||||
output[anonymizedPath] = anonymizeTreeRecursive(tree[file], terms, opt);
|
output[anonymizedPath] = anonymizeTreeRecursive(
|
||||||
|
(tree as Tree)[file],
|
||||||
|
terms,
|
||||||
|
opt
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -98,6 +102,7 @@ export default class Repository {
|
|||||||
const res = await AnonymizedRepositoryModel.findById(this._model._id, {
|
const res = await AnonymizedRepositoryModel.findById(this._model._id, {
|
||||||
originalFiles: 1,
|
originalFiles: 1,
|
||||||
});
|
});
|
||||||
|
if (!res) throw new AnonymousError("repository_not_found");
|
||||||
this.model.originalFiles = res.originalFiles;
|
this.model.originalFiles = res.originalFiles;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@@ -120,7 +125,8 @@ export default class Repository {
|
|||||||
check() {
|
check() {
|
||||||
if (
|
if (
|
||||||
this._model.options.expirationMode !== "never" &&
|
this._model.options.expirationMode !== "never" &&
|
||||||
this.status == "ready"
|
this.status == "ready" &&
|
||||||
|
this._model.options.expirationDate
|
||||||
) {
|
) {
|
||||||
if (this._model.options.expirationDate <= new Date()) {
|
if (this._model.options.expirationDate <= new Date()) {
|
||||||
this.expire();
|
this.expire();
|
||||||
@@ -164,7 +170,7 @@ export default class Repository {
|
|||||||
repository: this,
|
repository: this,
|
||||||
anonymizedPath: filename,
|
anonymizedPath: filename,
|
||||||
})
|
})
|
||||||
) as Transformer,
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,12 +280,14 @@ export default class Repository {
|
|||||||
* Reset/delete the state of the repository
|
* Reset/delete the state of the repository
|
||||||
*/
|
*/
|
||||||
async resetSate(status?: RepositoryStatus, statusMessage?: string) {
|
async resetSate(status?: RepositoryStatus, statusMessage?: string) {
|
||||||
const p = this.updateStatus(status, statusMessage);
|
if (status) {
|
||||||
|
await this.updateStatus(status, statusMessage);
|
||||||
|
}
|
||||||
// remove attribute
|
// remove attribute
|
||||||
this._model.size = { storage: 0, file: 0 };
|
this._model.size = { storage: 0, file: 0 };
|
||||||
this._model.originalFiles = null;
|
this._model.originalFiles = undefined;
|
||||||
// remove cache
|
// remove cache
|
||||||
return Promise.all([p, this.removeCache()]);
|
await this.removeCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -32,7 +32,7 @@ export default class User {
|
|||||||
return this._model.accessTokens.github;
|
return this._model.accessTokens.github;
|
||||||
}
|
}
|
||||||
|
|
||||||
get photo(): string {
|
get photo(): string | undefined {
|
||||||
return this._model.photo;
|
return this._model.photo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const urlRegex =
|
|||||||
/<?\b((https?|ftp|file):\/\/)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]\b\/?>?/g;
|
/<?\b((https?|ftp|file):\/\/)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]\b\/?>?/g;
|
||||||
|
|
||||||
export function streamToString(stream: Readable): Promise<string> {
|
export function streamToString(stream: Readable): Promise<string> {
|
||||||
const chunks = [];
|
const chunks: Buffer[] = [];
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
||||||
stream.on("error", (err) => reject(err));
|
stream.on("error", (err) => reject(err));
|
||||||
@@ -33,7 +33,7 @@ export function isTextFile(filePath: string, content: Buffer) {
|
|||||||
|
|
||||||
export function anonymizeStream(file: AnonymizedFile) {
|
export function anonymizeStream(file: AnonymizedFile) {
|
||||||
const ts = new Transform();
|
const ts = new Transform();
|
||||||
var chunks = [],
|
var chunks: Buffer[] = [],
|
||||||
len = 0,
|
len = 0,
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export interface IAnonymizedPullRequest {
|
|||||||
anonymizeDate: Date;
|
anonymizeDate: Date;
|
||||||
source: {
|
source: {
|
||||||
pullRequestId: number;
|
pullRequestId: number;
|
||||||
repositoryFullName?: string;
|
repositoryFullName: string;
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
};
|
};
|
||||||
owner: string;
|
owner: string;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface IAnonymizedRepository {
|
|||||||
};
|
};
|
||||||
owner: string;
|
owner: string;
|
||||||
truckedFileList: boolean;
|
truckedFileList: boolean;
|
||||||
originalFiles: Tree;
|
originalFiles?: Tree;
|
||||||
conference: string;
|
conference: string;
|
||||||
options: {
|
options: {
|
||||||
terms: string[];
|
terms: string[];
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const database = mongoose.connection;
|
|||||||
export let isConnected = false;
|
export let isConnected = false;
|
||||||
|
|
||||||
export async function connect() {
|
export async function connect() {
|
||||||
|
mongoose.set("strictQuery", false);
|
||||||
await mongoose.connect(MONGO_URL + "production", {
|
await mongoose.connect(MONGO_URL + "production", {
|
||||||
authSource: "admin",
|
authSource: "admin",
|
||||||
appName: "Anonymous GitHub Server",
|
appName: "Anonymous GitHub Server",
|
||||||
|
|||||||
@@ -23,7 +23,11 @@ export default async function (job: SandboxedJob<Repository, void>) {
|
|||||||
try {
|
try {
|
||||||
await repo.anonymize();
|
await repo.anonymize();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await repo.updateStatus(RepositoryStatus.ERROR, error.message);
|
if (error instanceof Error) {
|
||||||
|
await repo.updateStatus(RepositoryStatus.ERROR, error.message);
|
||||||
|
} else if (typeof error === "string") {
|
||||||
|
await repo.updateStatus(RepositoryStatus.ERROR, error);
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ export default async function (job: SandboxedJob<Repository, void>) {
|
|||||||
try {
|
try {
|
||||||
await repo.remove();
|
await repo.remove();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await repo.updateStatus(RepositoryStatus.ERROR, error.message);
|
if (error instanceof Error) {
|
||||||
|
await repo.updateStatus(RepositoryStatus.ERROR, error.message);
|
||||||
|
} else if (typeof error === "string") {
|
||||||
|
await repo.updateStatus(RepositoryStatus.ERROR, error);
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
+3
-15
@@ -48,9 +48,6 @@ export function startWorker() {
|
|||||||
autorun: true,
|
autorun: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
cacheWorker.on("error", async (error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
cacheWorker.on("completed", async (job) => {
|
cacheWorker.on("completed", async (job) => {
|
||||||
await job.remove();
|
await job.remove();
|
||||||
});
|
});
|
||||||
@@ -66,9 +63,6 @@ export function startWorker() {
|
|||||||
autorun: true,
|
autorun: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
removeWorker.on("error", async (error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
|
||||||
removeWorker.on("completed", async (job) => {
|
removeWorker.on("completed", async (job) => {
|
||||||
await job.remove();
|
await job.remove();
|
||||||
});
|
});
|
||||||
@@ -88,18 +82,12 @@ export function startWorker() {
|
|||||||
if (!downloadWorker.isRunning) downloadWorker.run();
|
if (!downloadWorker.isRunning) downloadWorker.run();
|
||||||
|
|
||||||
downloadWorker.on("active", async (job) => {
|
downloadWorker.on("active", async (job) => {
|
||||||
console.log("active", job.data.repoId);
|
console.log("[QUEUE] download repository start", job.data.repoId);
|
||||||
});
|
});
|
||||||
downloadWorker.on("completed", async (job) => {
|
downloadWorker.on("completed", async (job) => {
|
||||||
console.log("completed", job.data.repoId);
|
console.log("[QUEUE] download repository completed", job.data.repoId);
|
||||||
});
|
});
|
||||||
downloadWorker.on("failed", async (job) => {
|
downloadWorker.on("failed", async (job) => {
|
||||||
console.log("failed", job.data.repoId);
|
console.log("download repository failed", job.data.repoId);
|
||||||
});
|
|
||||||
downloadWorker.on("closing", async (error) => {
|
|
||||||
console.log("closing", error);
|
|
||||||
});
|
|
||||||
downloadWorker.on("error", async (error) => {
|
|
||||||
console.log(error);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -118,7 +118,7 @@ router.get("/repos", async (req, res) => {
|
|||||||
if (req.query.search) {
|
if (req.query.search) {
|
||||||
query.push({ repoId: { $regex: req.query.search } });
|
query.push({ repoId: { $regex: req.query.search } });
|
||||||
}
|
}
|
||||||
let status = [];
|
const status: { status: string }[] = [];
|
||||||
query.push({ $or: status });
|
query.push({ $or: status });
|
||||||
if (ready) {
|
if (ready) {
|
||||||
status.push({ status: "ready" });
|
status.push({ status: "ready" });
|
||||||
|
|||||||
+22
-10
@@ -4,6 +4,7 @@ import Conference from "../Conference";
|
|||||||
import ConferenceModel from "../database/conference/conferences.model";
|
import ConferenceModel from "../database/conference/conferences.model";
|
||||||
import { ensureAuthenticated } from "./connection";
|
import { ensureAuthenticated } from "./connection";
|
||||||
import { handleError, getUser, isOwnerOrAdmin } from "./route-utils";
|
import { handleError, getUser, isOwnerOrAdmin } from "./route-utils";
|
||||||
|
import { IConferenceDocument } from "../database/conference/conferences.types";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ router.get("/", async (req: express.Request, res: express.Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function validateConferenceForm(conf) {
|
function validateConferenceForm(conf: any) {
|
||||||
if (!conf.name)
|
if (!conf.name)
|
||||||
throw new AnonymousError("conf_name_missing", {
|
throw new AnonymousError("conf_name_missing", {
|
||||||
object: conf,
|
object: conf,
|
||||||
@@ -148,11 +149,17 @@ router.post(
|
|||||||
async (req: express.Request, res: express.Response) => {
|
async (req: express.Request, res: express.Response) => {
|
||||||
try {
|
try {
|
||||||
const user = await getUser(req);
|
const user = await getUser(req);
|
||||||
let model = new ConferenceModel();
|
let model: IConferenceDocument = new ConferenceModel();
|
||||||
if (req.params.conferenceID) {
|
if (req.params.conferenceID) {
|
||||||
model = await ConferenceModel.findOne({
|
const queryModel = await ConferenceModel.findOne({
|
||||||
conferenceID: req.params.conferenceID,
|
conferenceID: req.params.conferenceID,
|
||||||
});
|
});
|
||||||
|
if (!queryModel) {
|
||||||
|
throw new AnonymousError("conference_not_found", {
|
||||||
|
httpStatus: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
model = queryModel;
|
||||||
isOwnerOrAdmin(model.owners, user);
|
isOwnerOrAdmin(model.owners, user);
|
||||||
}
|
}
|
||||||
validateConferenceForm(req.body);
|
validateConferenceForm(req.body);
|
||||||
@@ -197,7 +204,10 @@ router.post(
|
|||||||
|
|
||||||
res.send("ok");
|
res.send("ok");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message?.indexOf(" duplicate key") > -1) {
|
if (
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message?.indexOf(" duplicate key") > -1
|
||||||
|
) {
|
||||||
return handleError(
|
return handleError(
|
||||||
new AnonymousError("conf_id_used", {
|
new AnonymousError("conf_id_used", {
|
||||||
object: req.params.conferenceID,
|
object: req.params.conferenceID,
|
||||||
@@ -219,16 +229,18 @@ router.get(
|
|||||||
conferenceID: req.params.conferenceID,
|
conferenceID: req.params.conferenceID,
|
||||||
});
|
});
|
||||||
if (!data)
|
if (!data)
|
||||||
throw new AnonymousError("conf_not_found", {
|
throw new AnonymousError("conf_not_found", {
|
||||||
object: req.params.conferenceID,
|
object: req.params.conferenceID,
|
||||||
httpStatus: 404,
|
httpStatus: 404,
|
||||||
});
|
});
|
||||||
const user = await getUser(req);
|
const user = await getUser(req);
|
||||||
const conference = new Conference(data);
|
const conference = new Conference(data);
|
||||||
try {
|
try {
|
||||||
isOwnerOrAdmin(conference.ownerIDs, user);
|
isOwnerOrAdmin(conference.ownerIDs, user);
|
||||||
const o: any = conference.toJSON();
|
const o: any = conference.toJSON();
|
||||||
o.repositories = (await conference.repositories()).map((r) => r.toJSON());
|
o.repositories = (await conference.repositories()).map((r) =>
|
||||||
|
r.toJSON()
|
||||||
|
);
|
||||||
res.json(o);
|
res.json(o);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.json({
|
return res.json({
|
||||||
@@ -238,7 +250,7 @@ router.get(
|
|||||||
startDate: conference.startDate,
|
startDate: conference.startDate,
|
||||||
endDate: conference.endDate,
|
endDate: conference.endDate,
|
||||||
options: conference.options,
|
options: conference.options,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, res, req);
|
handleError(error, res, req);
|
||||||
|
|||||||
+22
-20
@@ -10,8 +10,6 @@ import config from "../../config";
|
|||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../database/users/users.model";
|
||||||
import { IUserDocument } from "../database/users/users.types";
|
import { IUserDocument } from "../database/users/users.types";
|
||||||
|
|
||||||
const RedisStore = connectRedis(session);
|
|
||||||
|
|
||||||
export function ensureAuthenticated(
|
export function ensureAuthenticated(
|
||||||
req: express.Request,
|
req: express.Request,
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
@@ -29,7 +27,7 @@ const verify = async (
|
|||||||
profile: Profile,
|
profile: Profile,
|
||||||
done: OAuth2Strategy.VerifyCallback
|
done: OAuth2Strategy.VerifyCallback
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
let user: IUserDocument;
|
let user: IUserDocument | null = null;
|
||||||
try {
|
try {
|
||||||
user = await UserModel.findOne({ "externalIDs.github": profile.id });
|
user = await UserModel.findOne({ "externalIDs.github": profile.id });
|
||||||
if (user) {
|
if (user) {
|
||||||
@@ -84,23 +82,27 @@ passport.deserializeUser((user: Express.User, done) => {
|
|||||||
done(null, user);
|
done(null, user);
|
||||||
});
|
});
|
||||||
|
|
||||||
const redisClient = createClient({
|
export function initSession() {
|
||||||
legacyMode: true,
|
const RedisStore = connectRedis(session);
|
||||||
socket: {
|
const redisClient = createClient({
|
||||||
port: config.REDIS_PORT,
|
legacyMode: true,
|
||||||
host: config.REDIS_HOSTNAME,
|
socket: {
|
||||||
},
|
port: config.REDIS_PORT,
|
||||||
});
|
host: config.REDIS_HOSTNAME,
|
||||||
redisClient.on("error", (err) => console.log("Redis Client Error", err));
|
},
|
||||||
redisClient.connect();
|
});
|
||||||
export const appSession = session({
|
redisClient.on("error", (err) => console.log("Redis Client Error", err));
|
||||||
secret: "keyboard cat",
|
redisClient.connect();
|
||||||
store: new RedisStore({
|
|
||||||
client: redisClient,
|
return session({
|
||||||
}),
|
secret: "keyboard cat",
|
||||||
saveUninitialized: false,
|
store: new RedisStore({
|
||||||
resave: false,
|
client: redisClient,
|
||||||
});
|
}),
|
||||||
|
saveUninitialized: false,
|
||||||
|
resave: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export const router = express.Router();
|
export const router = express.Router();
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ router.get(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
function validateNewPullRequest(pullRequestUpdate): void {
|
function validateNewPullRequest(pullRequestUpdate: any): void {
|
||||||
const validCharacters = /^[0-9a-zA-Z\-\_]+$/;
|
const validCharacters = /^[0-9a-zA-Z\-\_]+$/;
|
||||||
if (
|
if (
|
||||||
!pullRequestUpdate.pullRequestId.match(validCharacters) ||
|
!pullRequestUpdate.pullRequestId.match(validCharacters) ||
|
||||||
@@ -151,7 +151,7 @@ function updatePullRequestModel(
|
|||||||
expirationMode: pullRequestUpdate.options.expirationMode,
|
expirationMode: pullRequestUpdate.options.expirationMode,
|
||||||
expirationDate: pullRequestUpdate.options.expirationDate
|
expirationDate: pullRequestUpdate.options.expirationDate
|
||||||
? new Date(pullRequestUpdate.options.expirationDate)
|
? new Date(pullRequestUpdate.options.expirationDate)
|
||||||
: null,
|
: undefined,
|
||||||
update: pullRequestUpdate.options.update,
|
update: pullRequestUpdate.options.update,
|
||||||
image: pullRequestUpdate.options.image,
|
image: pullRequestUpdate.options.image,
|
||||||
link: pullRequestUpdate.options.link,
|
link: pullRequestUpdate.options.link,
|
||||||
@@ -220,7 +220,10 @@ router.post("/", async (req: express.Request, res: express.Response) => {
|
|||||||
await pullRequest.anonymize();
|
await pullRequest.anonymize();
|
||||||
res.send(pullRequest.toJSON());
|
res.send(pullRequest.toJSON());
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message?.indexOf(" duplicate key") > -1) {
|
if (
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message.indexOf(" duplicate key") > -1
|
||||||
|
) {
|
||||||
return handleError(
|
return handleError(
|
||||||
new AnonymousError("pullRequestId_already_used", {
|
new AnonymousError("pullRequestId_already_used", {
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
|
|||||||
@@ -70,6 +70,12 @@ router.post("/claim", async (req: express.Request, res: express.Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const r = gh(req.body.repoUrl);
|
const r = gh(req.body.repoUrl);
|
||||||
|
if (!r?.owner || !r?.name) {
|
||||||
|
throw new AnonymousError("repo_not_found", {
|
||||||
|
object: req.body,
|
||||||
|
httpStatus: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
const repo = await getRepositoryFromGitHub({
|
const repo = await getRepositoryFromGitHub({
|
||||||
owner: r.owner,
|
owner: r.owner,
|
||||||
repo: r.name,
|
repo: r.name,
|
||||||
@@ -259,7 +265,7 @@ router.get("/:repoId/", async (req: express.Request, res: express.Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function validateNewRepo(repoUpdate): void {
|
function validateNewRepo(repoUpdate: any): void {
|
||||||
const validCharacters = /^[0-9a-zA-Z\-\_]+$/;
|
const validCharacters = /^[0-9a-zA-Z\-\_]+$/;
|
||||||
if (
|
if (
|
||||||
!repoUpdate.repoId.match(validCharacters) ||
|
!repoUpdate.repoId.match(validCharacters) ||
|
||||||
@@ -322,7 +328,7 @@ function updateRepoModel(
|
|||||||
expirationMode: repoUpdate.options.expirationMode,
|
expirationMode: repoUpdate.options.expirationMode,
|
||||||
expirationDate: repoUpdate.options.expirationDate
|
expirationDate: repoUpdate.options.expirationDate
|
||||||
? new Date(repoUpdate.options.expirationDate)
|
? new Date(repoUpdate.options.expirationDate)
|
||||||
: null,
|
: undefined,
|
||||||
update: repoUpdate.options.update,
|
update: repoUpdate.options.update,
|
||||||
image: repoUpdate.options.image,
|
image: repoUpdate.options.image,
|
||||||
pdf: repoUpdate.options.pdf,
|
pdf: repoUpdate.options.pdf,
|
||||||
@@ -359,7 +365,7 @@ router.post(
|
|||||||
|
|
||||||
updateRepoModel(repo.model, repoUpdate);
|
updateRepoModel(repo.model, repoUpdate);
|
||||||
|
|
||||||
async function removeRepoFromConference(conferenceID) {
|
const removeRepoFromConference = async (conferenceID: string) => {
|
||||||
const conf = await ConferenceModel.findOne({
|
const conf = await ConferenceModel.findOne({
|
||||||
conferenceID,
|
conferenceID,
|
||||||
});
|
});
|
||||||
@@ -368,7 +374,7 @@ router.post(
|
|||||||
if (r.length == 1) r[0].removeDate = new Date();
|
if (r.length == 1) r[0].removeDate = new Date();
|
||||||
await conf.save();
|
await conf.save();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
if (!repoUpdate.conference) {
|
if (!repoUpdate.conference) {
|
||||||
// remove conference
|
// remove conference
|
||||||
if (repo.model.conference) {
|
if (repo.model.conference) {
|
||||||
@@ -394,7 +400,7 @@ router.post(
|
|||||||
if (f.length) {
|
if (f.length) {
|
||||||
// the repository already referenced the conference
|
// the repository already referenced the conference
|
||||||
f[0].addDate = new Date();
|
f[0].addDate = new Date();
|
||||||
f[0].removeDate = null;
|
f[0].removeDate = undefined;
|
||||||
} else {
|
} else {
|
||||||
conf.repositories.push({
|
conf.repositories.push({
|
||||||
id: repo.model.id,
|
id: repo.model.id,
|
||||||
@@ -426,12 +432,25 @@ router.post("/", async (req: express.Request, res: express.Response) => {
|
|||||||
validateNewRepo(repoUpdate);
|
validateNewRepo(repoUpdate);
|
||||||
|
|
||||||
const r = gh(repoUpdate.fullName);
|
const r = gh(repoUpdate.fullName);
|
||||||
|
if (!r?.owner || !r?.name) {
|
||||||
|
throw new AnonymousError("repo_not_found", {
|
||||||
|
object: req.body,
|
||||||
|
httpStatus: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
const repository = await getRepositoryFromGitHub({
|
const repository = await getRepositoryFromGitHub({
|
||||||
accessToken: user.accessToken,
|
accessToken: user.accessToken,
|
||||||
owner: r.owner,
|
owner: r.owner,
|
||||||
repo: r.name,
|
repo: r.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!repository) {
|
||||||
|
throw new AnonymousError("repo_not_found", {
|
||||||
|
object: req.body,
|
||||||
|
httpStatus: 404,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const repo = new AnonymizedRepositoryModel();
|
const repo = new AnonymizedRepositoryModel();
|
||||||
repo.repoId = repoUpdate.repoId;
|
repo.repoId = repoUpdate.repoId;
|
||||||
repo.anonymizeDate = new Date();
|
repo.anonymizeDate = new Date();
|
||||||
@@ -444,14 +463,20 @@ router.post("/", async (req: express.Request, res: express.Response) => {
|
|||||||
|
|
||||||
if (repo.source.type == "GitHubDownload") {
|
if (repo.source.type == "GitHubDownload") {
|
||||||
// details.size is in kilobytes
|
// details.size is in kilobytes
|
||||||
if (repository.size > config.MAX_REPO_SIZE) {
|
if (
|
||||||
|
repository.size === undefined ||
|
||||||
|
repository.size > config.MAX_REPO_SIZE
|
||||||
|
) {
|
||||||
throw new AnonymousError("invalid_mode", {
|
throw new AnonymousError("invalid_mode", {
|
||||||
object: repository,
|
object: repository,
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (repository.size < config.AUTO_DOWNLOAD_REPO_SIZE) {
|
if (
|
||||||
|
repository.size !== undefined &&
|
||||||
|
repository.size < config.AUTO_DOWNLOAD_REPO_SIZE
|
||||||
|
) {
|
||||||
repo.source.type = "GitHubDownload";
|
repo.source.type = "GitHubDownload";
|
||||||
}
|
}
|
||||||
repo.conference = repoUpdate.conference;
|
repo.conference = repoUpdate.conference;
|
||||||
@@ -488,7 +513,10 @@ router.post("/", async (req: express.Request, res: express.Response) => {
|
|||||||
attempts: 3,
|
attempts: 3,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.message?.indexOf(" duplicate key") > -1) {
|
if (
|
||||||
|
error instanceof Error &&
|
||||||
|
error.message?.indexOf(" duplicate key") > -1
|
||||||
|
) {
|
||||||
return handleError(
|
return handleError(
|
||||||
new AnonymousError("repoId_already_used", {
|
new AnonymousError("repoId_already_used", {
|
||||||
httpStatus: 400,
|
httpStatus: 400,
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export async function getRepo(
|
|||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const repo = await db.getRepository(req.params.repoId, {
|
const repo = await db.getRepository(req.params.repoId, {
|
||||||
includeFiles: opt.includeFiles,
|
includeFiles: opt.includeFiles === true,
|
||||||
});
|
});
|
||||||
if (opt.nocheck == true) {
|
if (opt.nocheck == true) {
|
||||||
} else {
|
} else {
|
||||||
@@ -79,7 +79,7 @@ function printError(error: any, req?: express.Request) {
|
|||||||
io.notifyError(error, error.value);
|
io.notifyError(error, error.value);
|
||||||
if (error instanceof AnonymousError) {
|
if (error instanceof AnonymousError) {
|
||||||
let message = `[ERROR] ${error.toString()} ${error.stack
|
let message = `[ERROR] ${error.toString()} ${error.stack
|
||||||
.split("\n")[1]
|
?.split("\n")[1]
|
||||||
.trim()}`;
|
.trim()}`;
|
||||||
if (req) {
|
if (req) {
|
||||||
message += ` ${req.originalUrl}`;
|
message += ` ${req.originalUrl}`;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import * as path from "path";
|
|||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
import GitHubDownload from "../source/GitHubDownload";
|
import GitHubDownload from "../source/GitHubDownload";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../AnonymousError";
|
||||||
import { TreeElement } from "../types";
|
import { Tree, TreeElement } from "../types";
|
||||||
import * as marked from "marked";
|
import * as marked from "marked";
|
||||||
import { streamToString } from "../anonymize-utils";
|
import { streamToString } from "../anonymize-utils";
|
||||||
|
|
||||||
@@ -68,13 +68,13 @@ async function webView(req: express.Request, res: express.Response) {
|
|||||||
if (fileName == "") {
|
if (fileName == "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!currentAnonymized[fileName]) {
|
if (!(currentAnonymized as Tree)[fileName]) {
|
||||||
throw new AnonymousError("file_not_found", {
|
throw new AnonymousError("file_not_found", {
|
||||||
object: this,
|
object: repo,
|
||||||
httpStatus: 404,
|
httpStatus: 404,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
currentAnonymized = currentAnonymized[fileName];
|
currentAnonymized = (currentAnonymized as Tree)[fileName];
|
||||||
}
|
}
|
||||||
|
|
||||||
let best_match = null;
|
let best_match = null;
|
||||||
|
|||||||
+12
-12
@@ -1,16 +1,16 @@
|
|||||||
import * as path from "path";
|
|
||||||
import * as ofs from "fs";
|
|
||||||
import { createClient } from "redis";
|
import { createClient } from "redis";
|
||||||
|
import { resolve, join } from "path";
|
||||||
|
import { existsSync } from "fs";
|
||||||
import rateLimit from "express-rate-limit";
|
import rateLimit from "express-rate-limit";
|
||||||
import * as slowDown from "express-slow-down";
|
import * as slowDown from "express-slow-down";
|
||||||
import RedisStore from "rate-limit-redis";
|
import RedisStore from "rate-limit-redis";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import * as compression from "compression";
|
import * as compression from "compression";
|
||||||
import * as db from "./database/database";
|
|
||||||
import config from "../config";
|
|
||||||
import * as passport from "passport";
|
import * as passport from "passport";
|
||||||
|
|
||||||
import * as connection from "./routes/connection";
|
import config from "../config";
|
||||||
|
import { connect } from "./database/database";
|
||||||
|
import { initSession, router as connectionRouter } from "./routes/connection";
|
||||||
import router from "./routes";
|
import router from "./routes";
|
||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { conferenceStatusCheck, repositoryStatusCheck } from "./schedule";
|
import { conferenceStatusCheck, repositoryStatusCheck } from "./schedule";
|
||||||
@@ -31,7 +31,7 @@ function indexResponse(req: express.Request, res: express.Response) {
|
|||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
res.sendFile(path.resolve(__dirname, "..", "public", "index.html"));
|
res.sendFile(resolve("public", "index.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function start() {
|
export default async function start() {
|
||||||
@@ -45,7 +45,7 @@ export default async function start() {
|
|||||||
app.get("/ip", (request, response) => response.send(request.ip));
|
app.get("/ip", (request, response) => response.send(request.ip));
|
||||||
|
|
||||||
// handle session and connection
|
// handle session and connection
|
||||||
app.use(connection.appSession);
|
app.use(initSession());
|
||||||
app.use(passport.initialize());
|
app.use(passport.initialize());
|
||||||
app.use(passport.session());
|
app.use(passport.session());
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ export default async function start() {
|
|||||||
headers: true,
|
headers: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use("/github", rate, speedLimiter, connection.router);
|
app.use("/github", rate, speedLimiter, connectionRouter);
|
||||||
|
|
||||||
// api routes
|
// api routes
|
||||||
const apiRouter = express.Router();
|
const apiRouter = express.Router();
|
||||||
@@ -102,8 +102,8 @@ export default async function start() {
|
|||||||
apiRouter.use("/pr", speedLimiter, router.pullRequestPrivate);
|
apiRouter.use("/pr", speedLimiter, router.pullRequestPrivate);
|
||||||
|
|
||||||
apiRouter.get("/message", async (_, res) => {
|
apiRouter.get("/message", async (_, res) => {
|
||||||
if (ofs.existsSync("./message.txt")) {
|
if (existsSync("./message.txt")) {
|
||||||
return res.sendFile(path.resolve(__dirname, "..", "message.txt"));
|
return res.sendFile(resolve("message.txt"));
|
||||||
}
|
}
|
||||||
res.sendStatus(404);
|
res.sendStatus(404);
|
||||||
});
|
});
|
||||||
@@ -142,7 +142,7 @@ export default async function start() {
|
|||||||
.get("/repository/:repoId/?*", indexResponse);
|
.get("/repository/:repoId/?*", indexResponse);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
express.static(path.join(__dirname, "..", "public"), {
|
express.static(join("public"), {
|
||||||
etag: true,
|
etag: true,
|
||||||
lastModified: true,
|
lastModified: true,
|
||||||
maxAge: 3600, // 1h
|
maxAge: 3600, // 1h
|
||||||
@@ -155,7 +155,7 @@ export default async function start() {
|
|||||||
conferenceStatusCheck();
|
conferenceStatusCheck();
|
||||||
repositoryStatusCheck();
|
repositoryStatusCheck();
|
||||||
|
|
||||||
await db.connect();
|
await connect();
|
||||||
app.listen(config.PORT);
|
app.listen(config.PORT);
|
||||||
console.log("Database connected and Server started on port: " + config.PORT);
|
console.log("Database connected and Server started on port: " + config.PORT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default abstract class GitHubBase {
|
|||||||
type: "GitHubDownload" | "GitHubStream" | "Zip";
|
type: "GitHubDownload" | "GitHubStream" | "Zip";
|
||||||
githubRepository: GitHubRepository;
|
githubRepository: GitHubRepository;
|
||||||
branch: Branch;
|
branch: Branch;
|
||||||
accessToken: string;
|
accessToken: string | undefined;
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -27,13 +27,17 @@ export default abstract class GitHubBase {
|
|||||||
) {
|
) {
|
||||||
this.type = data.type;
|
this.type = data.type;
|
||||||
this.accessToken = data.accessToken;
|
this.accessToken = data.accessToken;
|
||||||
|
const branches = [];
|
||||||
|
if (data.branch && data.commit) {
|
||||||
|
branches.push({ commit: data.commit, name: data.branch });
|
||||||
|
}
|
||||||
this.githubRepository = new GitHubRepository({
|
this.githubRepository = new GitHubRepository({
|
||||||
name: data.repositoryName,
|
name: data.repositoryName,
|
||||||
externalId: data.repositoryId,
|
externalId: data.repositoryId,
|
||||||
branches: [{ commit: data.commit, name: data.branch }],
|
branches,
|
||||||
});
|
});
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.branch = { commit: data.commit, name: data.branch };
|
this.branch = branches[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getFileContent(file: AnonymizedFile): Promise<Readable> {
|
async getFileContent(file: AnonymizedFile): Promise<Readable> {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import config from "../../config";
|
|
||||||
import storage from "../storage";
|
|
||||||
import Repository from "../Repository";
|
|
||||||
|
|
||||||
import GitHubBase from "./GitHubBase";
|
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
|
||||||
import { RepositoryStatus, SourceBase } from "../types";
|
|
||||||
import got from "got";
|
import got from "got";
|
||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import { OctokitResponse } from "@octokit/types";
|
import { OctokitResponse } from "@octokit/types";
|
||||||
|
|
||||||
|
import config from "../../config";
|
||||||
|
import storage from "../storage";
|
||||||
|
import Repository from "../Repository";
|
||||||
|
import GitHubBase from "./GitHubBase";
|
||||||
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
|
import { RepositoryStatus, SourceBase } from "../types";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../AnonymousError";
|
||||||
|
|
||||||
export default class GitHubDownload extends GitHubBase implements SourceBase {
|
export default class GitHubDownload extends GitHubBase implements SourceBase {
|
||||||
@@ -56,7 +56,7 @@ export default class GitHubDownload extends GitHubBase implements SourceBase {
|
|||||||
}
|
}
|
||||||
response = await this._getZipUrl(token);
|
response = await this._getZipUrl(token);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.status == 401 && config.GITHUB_TOKEN) {
|
if ((error as any).status == 401 && config.GITHUB_TOKEN) {
|
||||||
try {
|
try {
|
||||||
response = await this._getZipUrl(config.GITHUB_TOKEN);
|
response = await this._getZipUrl(config.GITHUB_TOKEN);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -66,7 +66,7 @@ export default class GitHubDownload extends GitHubBase implements SourceBase {
|
|||||||
);
|
);
|
||||||
throw new AnonymousError("repo_not_accessible", {
|
throw new AnonymousError("repo_not_accessible", {
|
||||||
httpStatus: 404,
|
httpStatus: 404,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: this.repository,
|
object: this.repository,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -78,23 +78,23 @@ export default class GitHubDownload extends GitHubBase implements SourceBase {
|
|||||||
throw new AnonymousError("repo_not_accessible", {
|
throw new AnonymousError("repo_not_accessible", {
|
||||||
httpStatus: 404,
|
httpStatus: 404,
|
||||||
object: this.repository,
|
object: this.repository,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.repository.updateStatus(RepositoryStatus.DOWNLOAD);
|
await this.repository.updateStatus(RepositoryStatus.DOWNLOAD);
|
||||||
const originalPath = this.repository.originalCachePath;
|
const originalPath = this.repository.originalCachePath;
|
||||||
await storage.mk(originalPath);
|
await storage.mk(originalPath);
|
||||||
let progress = null;
|
let progress: { transferred: number } | undefined = undefined;
|
||||||
let progressTimeout;
|
let progressTimeout;
|
||||||
let inDownload = true;
|
let inDownload = true;
|
||||||
|
|
||||||
const that = this;
|
const that = this;
|
||||||
async function updateProgress() {
|
async function updateProgress() {
|
||||||
if (progress) {
|
if (progress && that.repository.status) {
|
||||||
await that.repository.updateStatus(
|
await that.repository.updateStatus(
|
||||||
that.repository.status,
|
that.repository.status,
|
||||||
progress.transferred
|
progress.transferred.toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (inDownload) {
|
if (inDownload) {
|
||||||
@@ -106,7 +106,7 @@ export default class GitHubDownload extends GitHubBase implements SourceBase {
|
|||||||
try {
|
try {
|
||||||
const downloadStream = got.stream(response.url);
|
const downloadStream = got.stream(response.url);
|
||||||
downloadStream.addListener("downloadProgress", (p) => (progress = p));
|
downloadStream.addListener("downloadProgress", (p) => (progress = p));
|
||||||
await storage.extractZip(originalPath, downloadStream, null, this);
|
await storage.extractZip(originalPath, downloadStream, undefined, this);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.repository.updateStatus(
|
await this.repository.updateStatus(
|
||||||
RepositoryStatus.ERROR,
|
RepositoryStatus.ERROR,
|
||||||
@@ -114,7 +114,7 @@ export default class GitHubDownload extends GitHubBase implements SourceBase {
|
|||||||
);
|
);
|
||||||
throw new AnonymousError("unable_to_download", {
|
throw new AnonymousError("unable_to_download", {
|
||||||
httpStatus: 500,
|
httpStatus: 500,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: this.repository,
|
object: this.repository,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -31,15 +31,15 @@ export class GitHubRepository {
|
|||||||
return this._data;
|
return this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get fullName(): string {
|
public get fullName(): string | undefined {
|
||||||
return this._data.name;
|
return this._data.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get id(): string {
|
public get id(): string | undefined {
|
||||||
return this._data.externalId;
|
return this._data.externalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get size(): number {
|
public get size(): number | undefined {
|
||||||
return this._data.size;
|
return this._data.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,27 +76,30 @@ export class GitHubRepository {
|
|||||||
{ $set: { branches } }
|
{ $set: { branches } }
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this._data.branches = (
|
const q = await RepositoryModel.findOne({ externalId: this.id }).select(
|
||||||
await RepositoryModel.findOne({ externalId: this.id }).select(
|
"branches"
|
||||||
"branches"
|
);
|
||||||
)
|
this._data.branches = q?.branches;
|
||||||
).branches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._data.branches;
|
return this._data.branches || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async readme(opt: {
|
async readme(opt: {
|
||||||
branch?: string;
|
branch?: string;
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
accessToken?: string;
|
accessToken?: string;
|
||||||
}): Promise<string> {
|
}): Promise<string | undefined> {
|
||||||
if (!opt.branch) opt.branch = this._data.defaultBranch || "master";
|
if (!opt.branch) opt.branch = this._data.defaultBranch || "master";
|
||||||
|
|
||||||
const model = await RepositoryModel.findOne({
|
const model = await RepositoryModel.findOne({
|
||||||
externalId: this.id,
|
externalId: this.id,
|
||||||
}).select("branches");
|
}).select("branches");
|
||||||
|
|
||||||
|
if (!model) {
|
||||||
|
throw new AnonymousError("repo_not_found", { httpStatus: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
this._data.branches = await this.branches(opt);
|
this._data.branches = await this.branches(opt);
|
||||||
model.branches = this._data.branches;
|
model.branches = this._data.branches;
|
||||||
|
|
||||||
@@ -119,7 +122,7 @@ export class GitHubRepository {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new AnonymousError("readme_not_available", {
|
throw new AnonymousError("readme_not_available", {
|
||||||
httpStatus: 404,
|
httpStatus: 404,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: this,
|
object: this,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,12 @@ export class GitHubRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get owner(): string {
|
public get owner(): string {
|
||||||
|
if (!this.fullName) {
|
||||||
|
throw new AnonymousError("invalid_repo", {
|
||||||
|
httpStatus: 400,
|
||||||
|
object: this,
|
||||||
|
});
|
||||||
|
}
|
||||||
const repo = gh(this.fullName);
|
const repo = gh(this.fullName);
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
throw new AnonymousError("invalid_repo", {
|
throw new AnonymousError("invalid_repo", {
|
||||||
@@ -147,6 +156,12 @@ export class GitHubRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get repo(): string {
|
public get repo(): string {
|
||||||
|
if (!this.fullName) {
|
||||||
|
throw new AnonymousError("invalid_repo", {
|
||||||
|
httpStatus: 400,
|
||||||
|
object: this,
|
||||||
|
});
|
||||||
|
}
|
||||||
const repo = gh(this.fullName);
|
const repo = gh(this.fullName);
|
||||||
if (!repo) {
|
if (!repo) {
|
||||||
throw new AnonymousError("invalid_repo", {
|
throw new AnonymousError("invalid_repo", {
|
||||||
@@ -177,12 +192,12 @@ export async function getRepositoryFromGitHub(opt: {
|
|||||||
).data;
|
).data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new AnonymousError("repo_not_found", {
|
throw new AnonymousError("repo_not_found", {
|
||||||
httpStatus: error.status,
|
httpStatus: (error as any).status,
|
||||||
object: {
|
object: {
|
||||||
owner: opt.owner,
|
owner: opt.owner,
|
||||||
repo: opt.repo,
|
repo: opt.repo,
|
||||||
},
|
},
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!r)
|
if (!r)
|
||||||
|
|||||||
+21
-12
@@ -30,11 +30,18 @@ export default class GitHubStream extends GitHubBase implements SourceBase {
|
|||||||
auth: await this.getToken(),
|
auth: await this.getToken(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const file_sha = await file.sha();
|
||||||
|
if (!file_sha) {
|
||||||
|
throw new AnonymousError("file_not_accessible", {
|
||||||
|
httpStatus: 404,
|
||||||
|
object: file,
|
||||||
|
});
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const ghRes = await octokit.rest.git.getBlob({
|
const ghRes = await octokit.rest.git.getBlob({
|
||||||
owner: this.githubRepository.owner,
|
owner: this.githubRepository.owner,
|
||||||
repo: this.githubRepository.repo,
|
repo: this.githubRepository.repo,
|
||||||
file_sha: await file.sha(),
|
file_sha,
|
||||||
});
|
});
|
||||||
if (!ghRes.data.content && ghRes.data.size != 0) {
|
if (!ghRes.data.content && ghRes.data.size != 0) {
|
||||||
throw new AnonymousError("file_not_accessible", {
|
throw new AnonymousError("file_not_accessible", {
|
||||||
@@ -57,16 +64,16 @@ export default class GitHubStream extends GitHubBase implements SourceBase {
|
|||||||
await storage.write(file.originalCachePath, content, file, this);
|
await storage.write(file.originalCachePath, content, file, this);
|
||||||
return stream.Readable.from(content);
|
return stream.Readable.from(content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.status === 404 || error.httpStatus === 404) {
|
if ((error as any).status === 404 || (error as any).httpStatus === 404) {
|
||||||
throw new AnonymousError("file_not_found", {
|
throw new AnonymousError("file_not_found", {
|
||||||
httpStatus: error.status,
|
httpStatus: (error as any).status || (error as any).httpStatus,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: file,
|
object: file,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
throw new AnonymousError("file_too_big", {
|
throw new AnonymousError("file_too_big", {
|
||||||
httpStatus: error.status,
|
httpStatus: (error as any).status || (error as any).httpStatus,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: file,
|
object: file,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -92,7 +99,7 @@ export default class GitHubStream extends GitHubBase implements SourceBase {
|
|||||||
count.request++;
|
count.request++;
|
||||||
ghRes = await this.getGHTree(sha, { recursive: true });
|
ghRes = await this.getGHTree(sha, { recursive: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.status == 409) {
|
if ((error as any).status == 409) {
|
||||||
// empty tree
|
// empty tree
|
||||||
if (this.repository.status != RepositoryStatus.READY)
|
if (this.repository.status != RepositoryStatus.READY)
|
||||||
await this.repository.updateStatus(RepositoryStatus.READY);
|
await this.repository.updateStatus(RepositoryStatus.READY);
|
||||||
@@ -100,15 +107,17 @@ export default class GitHubStream extends GitHubBase implements SourceBase {
|
|||||||
return { __: {} };
|
return { __: {} };
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
`[ERROR] getTree ${this.repository.repoId}@${sha}: ${error.message}`
|
`[ERROR] getTree ${this.repository.repoId}@${sha}: ${
|
||||||
|
(error as Error).message
|
||||||
|
}`
|
||||||
);
|
);
|
||||||
await this.repository.resetSate(
|
await this.repository.resetSate(
|
||||||
RepositoryStatus.ERROR,
|
RepositoryStatus.ERROR,
|
||||||
"repo_not_accessible"
|
"repo_not_accessible"
|
||||||
);
|
);
|
||||||
throw new AnonymousError("repo_not_accessible", {
|
throw new AnonymousError("repo_not_accessible", {
|
||||||
httpStatus: error.status,
|
httpStatus: (error as any).status,
|
||||||
cause: error,
|
cause: error as Error,
|
||||||
object: {
|
object: {
|
||||||
owner: this.githubRepository.owner,
|
owner: this.githubRepository.owner,
|
||||||
repo: this.githubRepository.repo,
|
repo: this.githubRepository.repo,
|
||||||
@@ -161,8 +170,8 @@ export default class GitHubStream extends GitHubBase implements SourceBase {
|
|||||||
if (data.tree.length < 100 && count.request < 200) {
|
if (data.tree.length < 100 && count.request < 200) {
|
||||||
const promises: Promise<any>[] = [];
|
const promises: Promise<any>[] = [];
|
||||||
for (const file of data.tree) {
|
for (const file of data.tree) {
|
||||||
const elementPath = path.join(parentPath, file.path);
|
if (file.type == "tree" && file.path && file.sha) {
|
||||||
if (file.type == "tree") {
|
const elementPath = path.join(parentPath, file.path);
|
||||||
promises.push(
|
promises.push(
|
||||||
this.getTruncatedTree(
|
this.getTruncatedTree(
|
||||||
file.sha,
|
file.sha,
|
||||||
|
|||||||
+3
-4
@@ -1,8 +1,7 @@
|
|||||||
import config from "../config";
|
import config from "../config";
|
||||||
import FileSystem from "./storage/FileSystem";
|
import FileSystem from "./storage/FileSystem";
|
||||||
import S3Storage from "./storage/S3";
|
import S3Storage from "./storage/S3";
|
||||||
import { StorageBase } from "./types";
|
|
||||||
|
|
||||||
const storage = config.STORAGE == "s3" ? new S3Storage() : new FileSystem();
|
export default (() => {
|
||||||
|
return config.STORAGE == "s3" ? new S3Storage() : new FileSystem();
|
||||||
export default storage as StorageBase;
|
})();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as fs from "fs";
|
|||||||
import { Extract } from "unzip-stream";
|
import { Extract } from "unzip-stream";
|
||||||
import { join, basename, dirname } from "path";
|
import { join, basename, dirname } from "path";
|
||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
import { Readable, pipeline } from "stream";
|
import { Readable, pipeline, Transform } from "stream";
|
||||||
import * as archiver from "archiver";
|
import * as archiver from "archiver";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
@@ -31,7 +31,12 @@ export default class FileSystem implements StorageBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async write(p: string, data: Buffer, file?: AnonymizedFile, source?: SourceBase): Promise<void> {
|
async write(
|
||||||
|
p: string,
|
||||||
|
data: Buffer,
|
||||||
|
file?: AnonymizedFile,
|
||||||
|
source?: SourceBase
|
||||||
|
): Promise<void> {
|
||||||
if (!(await this.exists(dirname(p)))) {
|
if (!(await this.exists(dirname(p)))) {
|
||||||
await fs.promises.mkdir(dirname(join(config.FOLDER, p)), {
|
await fs.promises.mkdir(dirname(join(config.FOLDER, p)), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
@@ -93,7 +98,12 @@ export default class FileSystem implements StorageBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async extractZip(p: string, data: Readable, file?: AnonymizedFile, source?: SourceBase): Promise<void> {
|
async extractZip(
|
||||||
|
p: string,
|
||||||
|
data: Readable,
|
||||||
|
file?: AnonymizedFile,
|
||||||
|
source?: SourceBase
|
||||||
|
): Promise<void> {
|
||||||
const pipe = promisify(pipeline);
|
const pipe = promisify(pipeline);
|
||||||
return pipe(
|
return pipe(
|
||||||
data,
|
data,
|
||||||
@@ -114,10 +124,10 @@ export default class FileSystem implements StorageBase {
|
|||||||
dir: string,
|
dir: string,
|
||||||
opt?: {
|
opt?: {
|
||||||
format?: "zip" | "tar";
|
format?: "zip" | "tar";
|
||||||
fileTransformer?;
|
fileTransformer? (path: string): Transform;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const archive = archiver(opt?.format, {});
|
const archive = archiver(opt?.format || "zip", {});
|
||||||
|
|
||||||
this.listFiles(dir, {
|
this.listFiles(dir, {
|
||||||
onEntry: (file) => {
|
onEntry: (file) => {
|
||||||
|
|||||||
+26
-10
@@ -1,7 +1,7 @@
|
|||||||
import { SourceBase, StorageBase, Tree, TreeFile } from "../types";
|
import { SourceBase, StorageBase, Tree, TreeFile } from "../types";
|
||||||
import { S3 } from "aws-sdk";
|
import { S3 } from "aws-sdk";
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
import { pipeline, Readable } from "stream";
|
import { pipeline, Readable, Transform } from "stream";
|
||||||
import ArchiveStreamToS3 from "decompress-stream-to-s3";
|
import ArchiveStreamToS3 from "decompress-stream-to-s3";
|
||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
import { lookup } from "mime-types";
|
import { lookup } from "mime-types";
|
||||||
@@ -35,6 +35,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async exists(path: string): Promise<boolean> {
|
async exists(path: string): Promise<boolean> {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
try {
|
try {
|
||||||
await this.client
|
await this.client
|
||||||
.headObject({
|
.headObject({
|
||||||
@@ -50,6 +51,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async mk(dir: string): Promise<void> {
|
async mk(dir: string): Promise<void> {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
||||||
|
|
||||||
await this.client
|
await this.client
|
||||||
@@ -62,6 +64,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async rm(dir: string): Promise<void> {
|
async rm(dir: string): Promise<void> {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
const data = await this.client
|
const data = await this.client
|
||||||
.listObjectsV2({
|
.listObjectsV2({
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
@@ -69,10 +72,15 @@ export default class S3Storage implements StorageBase {
|
|||||||
})
|
})
|
||||||
.promise();
|
.promise();
|
||||||
|
|
||||||
const params = { Bucket: config.S3_BUCKET, Delete: { Objects: [] } };
|
const params = {
|
||||||
|
Bucket: config.S3_BUCKET,
|
||||||
|
Delete: { Objects: new Array<{ Key: string }>() },
|
||||||
|
};
|
||||||
|
|
||||||
data.Contents.forEach(function (content) {
|
data.Contents?.forEach(function (content) {
|
||||||
params.Delete.Objects.push({ Key: content.Key });
|
if (content.Key) {
|
||||||
|
params.Delete.Objects.push({ Key: content.Key });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (params.Delete.Objects.length == 0) {
|
if (params.Delete.Objects.length == 0) {
|
||||||
@@ -88,6 +96,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
send(p: string, res: Response) {
|
send(p: string, res: Response) {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
const s = this.client
|
const s = this.client
|
||||||
.getObject({
|
.getObject({
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
@@ -95,7 +104,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
})
|
})
|
||||||
.on("error", (error) => {
|
.on("error", (error) => {
|
||||||
try {
|
try {
|
||||||
res.status(error.statusCode);
|
res.status(error.statusCode || 500);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@@ -114,6 +123,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
read(path: string): Readable {
|
read(path: string): Readable {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
return this.client
|
return this.client
|
||||||
.getObject({
|
.getObject({
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
@@ -129,6 +139,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
file?: AnonymizedFile,
|
file?: AnonymizedFile,
|
||||||
source?: SourceBase
|
source?: SourceBase
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
const params: S3.PutObjectRequest = {
|
const params: S3.PutObjectRequest = {
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
Key: path,
|
Key: path,
|
||||||
@@ -144,6 +155,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
async listFiles(dir: string): Promise<Tree> {
|
async listFiles(dir: string): Promise<Tree> {
|
||||||
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
||||||
const out: Tree = {};
|
const out: Tree = {};
|
||||||
const req = await this.client
|
const req = await this.client
|
||||||
@@ -168,9 +180,11 @@ export default class S3Storage implements StorageBase {
|
|||||||
current = current[p] as Tree;
|
current = current[p] as Tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileInfo: TreeFile = { size: f.Size || 0, sha: f.ETag };
|
if (f.ETag) {
|
||||||
const fileName = paths[paths.length - 1];
|
const fileInfo: TreeFile = { size: f.Size || 0, sha: f.ETag };
|
||||||
if (fileName) current[fileName] = fileInfo;
|
const fileName = paths[paths.length - 1];
|
||||||
|
if (fileName) current[fileName] = fileInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -185,6 +199,7 @@ export default class S3Storage implements StorageBase {
|
|||||||
let toS3: ArchiveStreamToS3;
|
let toS3: ArchiveStreamToS3;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!config.S3_BUCKET) return reject("S3_BUCKET not set");
|
||||||
toS3 = new ArchiveStreamToS3({
|
toS3 = new ArchiveStreamToS3({
|
||||||
bucket: config.S3_BUCKET,
|
bucket: config.S3_BUCKET,
|
||||||
prefix: p,
|
prefix: p,
|
||||||
@@ -208,10 +223,11 @@ export default class S3Storage implements StorageBase {
|
|||||||
dir: string,
|
dir: string,
|
||||||
opt?: {
|
opt?: {
|
||||||
format?: "zip" | "tar";
|
format?: "zip" | "tar";
|
||||||
fileTransformer?;
|
fileTransformer?: (p: string) => Transform;
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
const archive = archiver(opt?.format, {});
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
|
const archive = archiver(opt?.format || "zip", {});
|
||||||
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
if (dir && dir[dir.length - 1] != "/") dir = dir + "/";
|
||||||
const req = this.client.listObjectsV2({
|
const req = this.client.listObjectsV2({
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
|
|||||||
+5
-5
@@ -4,7 +4,7 @@ import Zip from "./source/Zip";
|
|||||||
import S3Storage from "./storage/S3";
|
import S3Storage from "./storage/S3";
|
||||||
import FileSystem from "./storage/FileSystem";
|
import FileSystem from "./storage/FileSystem";
|
||||||
import AnonymizedFile from "./AnonymizedFile";
|
import AnonymizedFile from "./AnonymizedFile";
|
||||||
import * as stream from "stream";
|
import { Transform, Readable } from "stream";
|
||||||
import * as archiver from "archiver";
|
import * as archiver from "archiver";
|
||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export interface SourceBase {
|
|||||||
* Retrieve the fie content
|
* Retrieve the fie content
|
||||||
* @param file the file of the content to retrieve
|
* @param file the file of the content to retrieve
|
||||||
*/
|
*/
|
||||||
getFileContent(file: AnonymizedFile): Promise<stream.Readable>;
|
getFileContent(file: AnonymizedFile): Promise<Readable>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the files from a specific source
|
* Get all the files from a specific source
|
||||||
@@ -50,7 +50,7 @@ export interface StorageBase {
|
|||||||
* Read the content of a file
|
* Read the content of a file
|
||||||
* @param path the path to the file
|
* @param path the path to the file
|
||||||
*/
|
*/
|
||||||
read(path: string): stream.Readable;
|
read(path: string): Readable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write data to a file
|
* Write data to a file
|
||||||
@@ -81,7 +81,7 @@ export interface StorageBase {
|
|||||||
*/
|
*/
|
||||||
extractZip(
|
extractZip(
|
||||||
dir: string,
|
dir: string,
|
||||||
tar: stream.Readable,
|
tar: Readable,
|
||||||
file?: AnonymizedFile,
|
file?: AnonymizedFile,
|
||||||
source?: SourceBase
|
source?: SourceBase
|
||||||
): Promise<void>;
|
): Promise<void>;
|
||||||
@@ -107,7 +107,7 @@ export interface StorageBase {
|
|||||||
/**
|
/**
|
||||||
* Transformer to apply on the content of the file
|
* Transformer to apply on the content of the file
|
||||||
*/
|
*/
|
||||||
fileTransformer?: (p: any) => Transformer;
|
fileTransformer?: (p: string) => Transform;
|
||||||
}
|
}
|
||||||
): archiver.Archiver;
|
): archiver.Archiver;
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -3,14 +3,18 @@
|
|||||||
"transpileOnly": true
|
"transpileOnly": true
|
||||||
},
|
},
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"types": ["node"],
|
||||||
|
"lib": ["es6"],
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"module": "commonjs",
|
"module": "CommonJS",
|
||||||
"outDir": "build",
|
"outDir": "build",
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"preserveConstEnums": true,
|
"preserveConstEnums": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"sourceMap": false,
|
"sourceMap": false,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"esModuleInterop": false
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "index.ts", "cli.ts"],
|
"include": ["src/**/*.ts", "index.ts", "cli.ts"],
|
||||||
"exclude": ["node_modules", ".vscode"]
|
"exclude": ["node_modules", ".vscode"]
|
||||||
|
|||||||
Reference in New Issue
Block a user