mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-11 18:39:04 +02:00
feat: introduce streamers that handle the stream and anonymization from github
This commit is contained in:
1 parent
73019c1b44
commit
4d12641c7e
64 files changed
+419
-257
No files matched your search
+1
-3
@@ -13,10 +13,8 @@ COPY healthcheck.js .
|
|||||||
|
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY public ./public
|
COPY public ./public
|
||||||
COPY index.ts .
|
|
||||||
COPY config.ts .
|
|
||||||
|
|
||||||
RUN npm install && npm run build && npm cache clean --force
|
RUN npm install && npm run build && npm cache clean --force
|
||||||
COPY opentelemetry.js .
|
COPY opentelemetry.js .
|
||||||
|
|
||||||
CMD [ "node", "--require", "./opentelemetry.js", "./build/index.js"]
|
CMD [ "node", "--require", "./opentelemetry.js", "./build/server/index.js"]
|
||||||
+31
-14
@@ -5,17 +5,17 @@ services:
|
|||||||
build: .
|
build: .
|
||||||
restart: always
|
restart: always
|
||||||
image: tdurieux/anonymous_github:v2
|
image: tdurieux/anonymous_github:v2
|
||||||
deploy:
|
ports:
|
||||||
mode: replicated
|
- $EXPOSED_PORT:5000
|
||||||
replicas: 4
|
|
||||||
endpoint_mode: vip
|
|
||||||
env_file:
|
env_file:
|
||||||
- ./.env
|
- ./.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./repositories:/app/build/repositories/
|
- ./repositories:/app/build/repositories/
|
||||||
environment:
|
environment:
|
||||||
|
- PORT=5000
|
||||||
- REDIS_HOSTNAME=redis
|
- REDIS_HOSTNAME=redis
|
||||||
- DB_HOSTNAME=mongodb
|
- DB_HOSTNAME=mongodb
|
||||||
|
- STREAMER_ENTRYPOINT=http://streamer:5000/
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
- CMD
|
- CMD
|
||||||
@@ -27,22 +27,37 @@ services:
|
|||||||
links:
|
links:
|
||||||
- mongodb
|
- mongodb
|
||||||
- redis
|
- redis
|
||||||
- opentelemetry
|
- streamer
|
||||||
|
|
||||||
nginx:
|
streamer:
|
||||||
image: nginx:stable-alpine
|
build: .
|
||||||
|
restart: always
|
||||||
|
image: tdurieux/anonymous_github:v2
|
||||||
|
deploy:
|
||||||
|
mode: replicated
|
||||||
|
replicas: 4
|
||||||
|
endpoint_mode: vip
|
||||||
|
entrypoint: ["node", "./build/streamer/index.js"]
|
||||||
env_file:
|
env_file:
|
||||||
- ./.env
|
- ./.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
|
- ./repositories:/app/build/repositories/
|
||||||
depends_on:
|
environment:
|
||||||
- anonymous_github
|
- PORT=5000
|
||||||
ports:
|
healthcheck:
|
||||||
- "$EXPOSED_PORT:4000"
|
test:
|
||||||
|
- CMD
|
||||||
|
- node
|
||||||
|
- healthcheck.js
|
||||||
|
interval: 10s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: "redis:alpine"
|
image: "redis:alpine"
|
||||||
restart: always
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:6379:6379
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test:
|
test:
|
||||||
- CMD
|
- CMD
|
||||||
@@ -101,13 +116,15 @@ services:
|
|||||||
image: tiredofit/db-backup
|
image: tiredofit/db-backup
|
||||||
links:
|
links:
|
||||||
- mongodb
|
- mongodb
|
||||||
|
env_file:
|
||||||
|
- ./.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./db_backups:/backup
|
- ./db_backups:/backup
|
||||||
environment:
|
environment:
|
||||||
- DB_TYPE=mongo
|
- DB_TYPE=mongo
|
||||||
- DB_HOST=mongodb
|
- DB_HOST=mongodb
|
||||||
- DB_DUMP_FREQ=60
|
- DB_DUMP_FREQ=120
|
||||||
- DB_CLEANUP_TIME=240
|
- DB_CLEANUP_TIME=500
|
||||||
- COMPRESSION=XZ
|
- COMPRESSION=XZ
|
||||||
- DB_USER=$DB_USERNAME
|
- DB_USER=$DB_USERNAME
|
||||||
- DB_PASS=$DB_PASSWORD
|
- DB_PASS=$DB_PASSWORD
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { config } from "dotenv";
|
|
||||||
config();
|
|
||||||
|
|
||||||
import server from "./src/server";
|
|
||||||
|
|
||||||
// start the server
|
|
||||||
server();
|
|
||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
upstream backend {
|
|
||||||
server anonymous_github:5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 4000;
|
|
||||||
|
|
||||||
resolver 127.0.0.11 valid=5s;
|
|
||||||
|
|
||||||
include /etc/nginx/mime.types;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_pass http://backend/;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+3
-4
@@ -3,13 +3,12 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"description": "Anonymise Github repositories for double-anonymous reviews",
|
"description": "Anonymise Github repositories for double-anonymous reviews",
|
||||||
"bin": {
|
"bin": {
|
||||||
"anonymous_github": "build/cli.js"
|
"anonymous_github": "build/cli/index.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --reporter spec",
|
"test": "mocha --reporter spec",
|
||||||
"start": "node --inspect=5858 -r ts-node/register ./index.ts",
|
"start": "node --inspect=5858 -r ts-node/register ./src/server/index.ts",
|
||||||
"dev": "nodemon --transpile-only index.ts",
|
"dev": "nodemon --transpile-only ./src/server/index.ts",
|
||||||
"migrateDB": "ts-node --transpile-only migrateDB.ts",
|
|
||||||
"build": "rm -rf build && tsc",
|
"build": "rm -rf build && tsc",
|
||||||
"knip": "knip"
|
"knip": "knip"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ import { join } from "path";
|
|||||||
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 server from "../server";
|
||||||
import config from "./config";
|
import config from "../config";
|
||||||
import GitHubDownload from "./src/source/GitHubDownload";
|
import GitHubDownload from "../core/source/GitHubDownload";
|
||||||
import Repository from "./src/Repository";
|
import Repository from "../core/Repository";
|
||||||
import AnonymizedRepositoryModel from "./src/database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { getRepositoryFromGitHub } from "./src/source/GitHubRepository";
|
import { getRepositoryFromGitHub } from "../core/source/GitHubRepository";
|
||||||
|
|
||||||
function generateRandomFileName(size: number) {
|
function generateRandomFileName(size: number) {
|
||||||
const characters =
|
const characters =
|
||||||
+13
-11
@@ -18,6 +18,7 @@ interface Config {
|
|||||||
* Allow to download repository and files
|
* Allow to download repository and files
|
||||||
*/
|
*/
|
||||||
ENABLE_DOWNLOAD: boolean;
|
ENABLE_DOWNLOAD: boolean;
|
||||||
|
STREAMER_ENTRYPOINT: string | null;
|
||||||
ANONYMIZATION_MASK: string;
|
ANONYMIZATION_MASK: string;
|
||||||
PORT: number;
|
PORT: number;
|
||||||
APP_HOSTNAME: string;
|
APP_HOSTNAME: string;
|
||||||
@@ -26,11 +27,11 @@ interface Config {
|
|||||||
DB_HOSTNAME: string;
|
DB_HOSTNAME: string;
|
||||||
FOLDER: string;
|
FOLDER: string;
|
||||||
additionalExtensions: string[];
|
additionalExtensions: string[];
|
||||||
S3_BUCKET?: string;
|
S3_BUCKET: string | null;
|
||||||
S3_CLIENT_ID?: string;
|
S3_CLIENT_ID: string | null;
|
||||||
S3_CLIENT_SECRET?: string;
|
S3_CLIENT_SECRET: string | null;
|
||||||
S3_ENDPOINT?: string;
|
S3_ENDPOINT: string | null;
|
||||||
S3_REGION?: string;
|
S3_REGION: string | null;
|
||||||
STORAGE: "filesystem" | "s3";
|
STORAGE: "filesystem" | "s3";
|
||||||
TRUST_PROXY: number;
|
TRUST_PROXY: number;
|
||||||
RATE_LIMIT: number;
|
RATE_LIMIT: number;
|
||||||
@@ -58,7 +59,7 @@ const config: Config = {
|
|||||||
DB_HOSTNAME: "mongodb",
|
DB_HOSTNAME: "mongodb",
|
||||||
REDIS_HOSTNAME: "redis",
|
REDIS_HOSTNAME: "redis",
|
||||||
REDIS_PORT: 6379,
|
REDIS_PORT: 6379,
|
||||||
FOLDER: resolve(__dirname, "repositories"),
|
FOLDER: resolve(__dirname, "..", "repositories"),
|
||||||
additionalExtensions: [
|
additionalExtensions: [
|
||||||
"license",
|
"license",
|
||||||
"dockerfile",
|
"dockerfile",
|
||||||
@@ -70,11 +71,12 @@ const config: Config = {
|
|||||||
"in",
|
"in",
|
||||||
],
|
],
|
||||||
STORAGE: "filesystem",
|
STORAGE: "filesystem",
|
||||||
S3_BUCKET: process.env.S3_BUCKET,
|
STREAMER_ENTRYPOINT: null,
|
||||||
S3_CLIENT_ID: process.env.S3_CLIENT_ID,
|
S3_BUCKET: null,
|
||||||
S3_CLIENT_SECRET: process.env.S3_CLIENT_SECRET,
|
S3_CLIENT_ID: null,
|
||||||
S3_ENDPOINT: process.env.S3_ENDPOINT,
|
S3_CLIENT_SECRET: null,
|
||||||
S3_REGION: process.env.S3_REGION,
|
S3_ENDPOINT: null,
|
||||||
|
S3_REGION: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let conf in process.env) {
|
for (let conf in process.env) {
|
||||||
@@ -2,13 +2,15 @@ import { join, basename } from "path";
|
|||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
import { Readable } from "stream";
|
import { Readable } from "stream";
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
|
import { lookup } from "mime-types";
|
||||||
|
|
||||||
import Repository from "./Repository";
|
import Repository from "./Repository";
|
||||||
import { RepositoryStatus, Tree, TreeElement, TreeFile } from "./types";
|
import { RepositoryStatus, Tree, TreeElement, TreeFile } from "./types";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import { anonymizePath, isTextFile } from "./anonymize-utils";
|
import { anonymizePath, isTextFile } from "./anonymize-utils";
|
||||||
import AnonymousError from "./AnonymousError";
|
import AnonymousError from "./AnonymousError";
|
||||||
import { handleError } from "./routes/route-utils";
|
import { handleError } from "../server/routes/route-utils";
|
||||||
import { lookup } from "mime-types";
|
import got from "got";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represent a file in a anonymized repository
|
* Represent a file in a anonymized repository
|
||||||
@@ -197,7 +199,7 @@ export default class AnonymizedFile {
|
|||||||
httpStatus: 403,
|
httpStatus: 403,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const out = await this.repository.source?.getFileContent(this);
|
const content = await this.repository.source?.getFileContent(this);
|
||||||
if (
|
if (
|
||||||
!this.repository.model.isReseted ||
|
!this.repository.model.isReseted ||
|
||||||
this.repository.status != RepositoryStatus.READY
|
this.repository.status != RepositoryStatus.READY
|
||||||
@@ -205,7 +207,7 @@ export default class AnonymizedFile {
|
|||||||
this.repository.model.isReseted = false;
|
this.repository.model.isReseted = false;
|
||||||
await this.repository.updateStatus(RepositoryStatus.READY);
|
await this.repository.updateStatus(RepositoryStatus.READY);
|
||||||
}
|
}
|
||||||
return out;
|
return content;
|
||||||
} finally {
|
} finally {
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
@@ -213,19 +215,32 @@ export default class AnonymizedFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async anonymizedContent() {
|
async anonymizedContent() {
|
||||||
return trace
|
const span = trace.getTracer("ano-file").startSpan("Repository.conference");
|
||||||
.getTracer("ano-file")
|
span.setAttribute("anonymizedPath", this.anonymizedPath);
|
||||||
.startActiveSpan("anonymizedContent", async (span) => {
|
const anonymizer = this.repository.generateAnonymizeTransformer(
|
||||||
span.setAttribute("anonymizedPath", this.anonymizedPath);
|
this.anonymizedPath
|
||||||
const content = await this.content();
|
);
|
||||||
return content
|
if (!config.STREAMER_ENTRYPOINT) {
|
||||||
.pipe(
|
// collect the content locally
|
||||||
this.repository.generateAnonymizeTransformer(this.anonymizedPath)
|
const content = await this.content();
|
||||||
)
|
return content.pipe(anonymizer).on("close", () => {
|
||||||
.on("close", () => {
|
span.end();
|
||||||
span.end();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
// use the streamer service
|
||||||
|
return got.stream(join(config.STREAMER_ENTRYPOINT, "api"), {
|
||||||
|
method: "POST",
|
||||||
|
json: {
|
||||||
|
token: await this.repository.getToken(),
|
||||||
|
repoFullName: this.repository.model.source.repositoryName,
|
||||||
|
commit: this.repository.model.source.commit,
|
||||||
|
branch: this.repository.model.source.branch,
|
||||||
|
repoId: this.repository.repoId,
|
||||||
|
filePath: this.filePath,
|
||||||
|
sha: await this.sha(),
|
||||||
|
anonymizerOptions: anonymizer.opt,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get filePath() {
|
get filePath() {
|
||||||
@@ -243,6 +258,9 @@ export default class AnonymizedFile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async send(res: Response): Promise<void> {
|
async send(res: Response): Promise<void> {
|
||||||
|
const anonymizer = this.repository.generateAnonymizeTransformer(
|
||||||
|
this.anonymizedPath
|
||||||
|
);
|
||||||
return trace
|
return trace
|
||||||
.getTracer("ano-file")
|
.getTracer("ano-file")
|
||||||
.startActiveSpan("AnonymizedFile.send", async (span) => {
|
.startActiveSpan("AnonymizedFile.send", async (span) => {
|
||||||
@@ -250,6 +268,39 @@ export default class AnonymizedFile {
|
|||||||
span.setAttribute("anonymizedPath", this.anonymizedPath);
|
span.setAttribute("anonymizedPath", this.anonymizedPath);
|
||||||
return new Promise<void>(async (resolve, reject) => {
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
|
if (config.STREAMER_ENTRYPOINT) {
|
||||||
|
// use the streamer service
|
||||||
|
got
|
||||||
|
.stream(join(config.STREAMER_ENTRYPOINT, "api"), {
|
||||||
|
method: "POST",
|
||||||
|
json: {
|
||||||
|
token: await this.repository.getToken(),
|
||||||
|
repoFullName: this.repository.model.source.repositoryName,
|
||||||
|
commit: this.repository.model.source.commit,
|
||||||
|
branch: this.repository.model.source.branch,
|
||||||
|
repoId: this.repository.repoId,
|
||||||
|
filePath: this.filePath,
|
||||||
|
sha: await this.sha(),
|
||||||
|
anonymizerOptions: anonymizer.opt,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.on("error", () => {
|
||||||
|
handleError(
|
||||||
|
new AnonymousError("file_not_found", {
|
||||||
|
object: this,
|
||||||
|
httpStatus: 404,
|
||||||
|
}),
|
||||||
|
res
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.pipe(res)
|
||||||
|
.on("close", () => {
|
||||||
|
span.end();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const mime = lookup(this.anonymizedPath);
|
const mime = lookup(this.anonymizedPath);
|
||||||
if (mime && this.extension() != "ts") {
|
if (mime && this.extension() != "ts") {
|
||||||
res.contentType(mime);
|
res.contentType(mime);
|
||||||
@@ -257,9 +308,6 @@ export default class AnonymizedFile {
|
|||||||
res.contentType("text/plain");
|
res.contentType("text/plain");
|
||||||
}
|
}
|
||||||
res.header("Accept-Ranges", "none");
|
res.header("Accept-Ranges", "none");
|
||||||
const anonymizer = this.repository.generateAnonymizeTransformer(
|
|
||||||
this.anonymizedPath
|
|
||||||
);
|
|
||||||
anonymizer.once("transform", (data) => {
|
anonymizer.once("transform", (data) => {
|
||||||
if (!mime && data.isText) {
|
if (!mime && data.isText) {
|
||||||
res.contentType("text/plain");
|
res.contentType("text/plain");
|
||||||
@@ -269,26 +317,27 @@ export default class AnonymizedFile {
|
|||||||
res.header("Content-Length", this.fileSize.toString());
|
res.header("Content-Length", this.fileSize.toString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const content = await this.content();
|
const content = await this.content();
|
||||||
|
function handleStreamError(error: Error) {
|
||||||
|
if (!content.closed && !content.destroyed) {
|
||||||
|
content.destroy();
|
||||||
|
}
|
||||||
|
span.recordException(error);
|
||||||
|
span.end();
|
||||||
|
reject(error);
|
||||||
|
// handleError(error, res);
|
||||||
|
}
|
||||||
content
|
content
|
||||||
|
.on("error", handleStreamError)
|
||||||
.pipe(anonymizer)
|
.pipe(anonymizer)
|
||||||
.pipe(res)
|
.pipe(res)
|
||||||
|
.on("error", handleStreamError)
|
||||||
.on("close", () => {
|
.on("close", () => {
|
||||||
if (!content.closed && !content.destroyed) {
|
if (!content.closed && !content.destroyed) {
|
||||||
content.destroy();
|
content.destroy();
|
||||||
}
|
}
|
||||||
span.end();
|
span.end();
|
||||||
resolve();
|
resolve();
|
||||||
})
|
|
||||||
.on("error", (error) => {
|
|
||||||
if (!content.closed && !content.destroyed) {
|
|
||||||
content.destroy();
|
|
||||||
}
|
|
||||||
span.recordException(error);
|
|
||||||
span.end();
|
|
||||||
reject(error);
|
|
||||||
handleError(error, res);
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleError(error, res);
|
handleError(error, res);
|
||||||
File renamed without changes.
@@ -1,5 +1,5 @@
|
|||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "./model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { IConferenceDocument } from "./database/conference/conferences.types";
|
import { IConferenceDocument } from "./model/conference/conferences.types";
|
||||||
import Repository from "./Repository";
|
import Repository from "./Repository";
|
||||||
import { ConferenceStatus } from "./types";
|
import { ConferenceStatus } from "./types";
|
||||||
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
import Repository from "./Repository";
|
|
||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import UserModel from "./database/users/users.model";
|
|
||||||
|
import Repository from "./Repository";
|
||||||
|
import UserModel from "./model/users/users.model";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
|
|
||||||
export function octokit(token: string) {
|
export function octokit(token: string) {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { RepositoryStatus } from "./types";
|
import { RepositoryStatus } from "./types";
|
||||||
import User from "./User";
|
import User from "./User";
|
||||||
import UserModel from "./database/users/users.model";
|
import UserModel from "./model/users/users.model";
|
||||||
import Conference from "./Conference";
|
import Conference from "./Conference";
|
||||||
import ConferenceModel from "./database/conference/conferences.model";
|
import ConferenceModel from "./model/conference/conferences.model";
|
||||||
import AnonymousError from "./AnonymousError";
|
import AnonymousError from "./AnonymousError";
|
||||||
import { IAnonymizedPullRequestDocument } from "./database/anonymizedPullRequests/anonymizedPullRequests.types";
|
import { IAnonymizedPullRequestDocument } from "./model/anonymizedPullRequests/anonymizedPullRequests.types";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import got from "got";
|
import got from "got";
|
||||||
import { octokit } from "./GitHubUtils";
|
import { octokit } from "./GitHubUtils";
|
||||||
@@ -6,16 +6,16 @@ import GitHubStream from "./source/GitHubStream";
|
|||||||
import GitHubDownload from "./source/GitHubDownload";
|
import GitHubDownload from "./source/GitHubDownload";
|
||||||
import Zip from "./source/Zip";
|
import Zip from "./source/Zip";
|
||||||
import { anonymizePath } from "./anonymize-utils";
|
import { anonymizePath } from "./anonymize-utils";
|
||||||
import UserModel from "./database/users/users.model";
|
import UserModel from "./model/users/users.model";
|
||||||
import { IAnonymizedRepositoryDocument } from "./database/anonymizedRepositories/anonymizedRepositories.types";
|
import { IAnonymizedRepositoryDocument } from "./model/anonymizedRepositories/anonymizedRepositories.types";
|
||||||
import { AnonymizeTransformer } from "./anonymize-utils";
|
import { AnonymizeTransformer } from "./anonymize-utils";
|
||||||
import GitHubBase from "./source/GitHubBase";
|
import GitHubBase from "./source/GitHubBase";
|
||||||
import Conference from "./Conference";
|
import Conference from "./Conference";
|
||||||
import ConferenceModel from "./database/conference/conferences.model";
|
import ConferenceModel from "./model/conference/conferences.model";
|
||||||
import AnonymousError from "./AnonymousError";
|
import AnonymousError from "./AnonymousError";
|
||||||
import { downloadQueue } from "./queue";
|
import { downloadQueue } from "../queue";
|
||||||
import { isConnected } from "./database/database";
|
import { isConnected } from "../server/database";
|
||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "./model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { GitHubRepository } from "./source/GitHubRepository";
|
import { GitHubRepository } from "./source/GitHubRepository";
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
import { getToken } from "./GitHubUtils";
|
import { getToken } from "./GitHubUtils";
|
||||||
@@ -59,7 +59,7 @@ export default class Repository {
|
|||||||
|
|
||||||
private checkedToken: boolean = false;
|
private checkedToken: boolean = false;
|
||||||
|
|
||||||
private async getToken() {
|
async getToken() {
|
||||||
if (this.checkedToken) return this._model.source.accessToken as string;
|
if (this.checkedToken) return this._model.source.accessToken as string;
|
||||||
const originalToken = this._model.source.accessToken;
|
const originalToken = this._model.source.accessToken;
|
||||||
const token = await getToken(this);
|
const token = await getToken(this);
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import { trace } from "@opentelemetry/api";
|
||||||
import RepositoryModel from "./database/repositories/repositories.model";
|
|
||||||
import { IUserDocument } from "./database/users/users.types";
|
import AnonymizedRepositoryModel from "./model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
|
import RepositoryModel from "./model/repositories/repositories.model";
|
||||||
|
import { IUserDocument } from "./model/users/users.types";
|
||||||
import Repository from "./Repository";
|
import Repository from "./Repository";
|
||||||
import { GitHubRepository } from "./source/GitHubRepository";
|
import { GitHubRepository } from "./source/GitHubRepository";
|
||||||
import PullRequest from "./PullRequest";
|
import PullRequest from "./PullRequest";
|
||||||
import AnonymizedPullRequestModel from "./database/anonymizedPullRequests/anonymizedPullRequests.model";
|
import AnonymizedPullRequestModel from "./model/anonymizedPullRequests/anonymizedPullRequests.model";
|
||||||
import { trace } from "@opentelemetry/api";
|
|
||||||
import { octokit } from "./GitHubUtils";
|
import { octokit } from "./GitHubUtils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import config from "../config";
|
|
||||||
import { isText } from "istextorbinary";
|
|
||||||
import { basename } from "path";
|
import { basename } from "path";
|
||||||
import { Transform } from "stream";
|
import { Transform, Readable } from "stream";
|
||||||
import { Readable } from "stream";
|
import { isText } from "istextorbinary";
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
|
|
||||||
|
import config from "../config";
|
||||||
|
|
||||||
const urlRegex =
|
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;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export class AnonymizeTransformer extends Transform {
|
|||||||
anonimizer: ContentAnonimizer;
|
anonimizer: ContentAnonimizer;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly opt: {
|
readonly opt: {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
} & ConstructorParameters<typeof ContentAnonimizer>[0]
|
} & ConstructorParameters<typeof ContentAnonimizer>[0]
|
||||||
) {
|
) {
|
||||||
@@ -56,7 +56,6 @@ export class AnonymizeTransformer extends Transform {
|
|||||||
if (this.isText === null) {
|
if (this.isText === null) {
|
||||||
this.isText = isTextFile(this.opt.filePath, chunk);
|
this.isText = isTextFile(this.opt.filePath, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isText) {
|
if (this.isText) {
|
||||||
const content = this.anonimizer.anonymize(chunk.toString());
|
const content = this.anonimizer.anonymize(chunk.toString());
|
||||||
if (this.anonimizer.wasAnonymized) {
|
if (this.anonimizer.wasAnonymized) {
|
||||||
@@ -171,7 +170,7 @@ export class ContentAnonimizer {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// remove the term in the text
|
// remove the term in the text
|
||||||
return content.replace(new RegExp(`\\b${term}\\b`, "gi"), () => {
|
content = content.replace(new RegExp(`\\b${term}\\b`, "gi"), () => {
|
||||||
this.wasAnonymized = true;
|
this.wasAnonymized = true;
|
||||||
return mask;
|
return mask;
|
||||||
});
|
});
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,6 +2,7 @@ import { Readable } from "stream";
|
|||||||
|
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
import { Tree } from "../types";
|
import { Tree } from "../types";
|
||||||
|
import { SourceBase } from "./Source";
|
||||||
|
|
||||||
export interface GitHubBaseData {
|
export interface GitHubBaseData {
|
||||||
getToken: () => string | Promise<string>;
|
getToken: () => string | Promise<string>;
|
||||||
@@ -11,21 +12,6 @@ export interface GitHubBaseData {
|
|||||||
commit: string;
|
commit: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SourceBase {
|
|
||||||
readonly type: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the fie content
|
|
||||||
* @param file the file of the content to retrieve
|
|
||||||
*/
|
|
||||||
getFileContent(file: AnonymizedFile): Promise<Readable>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all the files from a specific source
|
|
||||||
*/
|
|
||||||
getFiles(progress?: (status: string) => void): Promise<Tree>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default abstract class GitHubBase implements SourceBase {
|
export default abstract class GitHubBase implements SourceBase {
|
||||||
abstract type: "GitHubDownload" | "GitHubStream" | "Zip";
|
abstract type: "GitHubDownload" | "GitHubStream" | "Zip";
|
||||||
accessToken: string | undefined;
|
accessToken: string | undefined;
|
||||||
@@ -4,11 +4,11 @@ import { OctokitResponse } from "@octokit/types";
|
|||||||
|
|
||||||
import storage from "../storage";
|
import storage from "../storage";
|
||||||
import GitHubBase, { GitHubBaseData } from "./GitHubBase";
|
import GitHubBase, { GitHubBaseData } from "./GitHubBase";
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
|
||||||
import AnonymousError from "../AnonymousError";
|
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
import { FILE_TYPE } from "../storage/Storage";
|
import { FILE_TYPE } from "../storage/Storage";
|
||||||
import { octokit } from "../GitHubUtils";
|
import { octokit } from "../GitHubUtils";
|
||||||
|
import AnonymousError from "../AnonymousError";
|
||||||
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
|
|
||||||
export default class GitHubDownload extends GitHubBase {
|
export default class GitHubDownload extends GitHubBase {
|
||||||
type: "GitHubDownload" | "GitHubStream" | "Zip" = "GitHubDownload";
|
type: "GitHubDownload" | "GitHubStream" | "Zip" = "GitHubDownload";
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Branch } from "../types";
|
import { Branch } from "../types";
|
||||||
import * as gh from "parse-github-url";
|
import * as gh from "parse-github-url";
|
||||||
import { IRepositoryDocument } from "../database/repositories/repositories.types";
|
|
||||||
import { RestEndpointMethodTypes } from "@octokit/rest";
|
import { RestEndpointMethodTypes } from "@octokit/rest";
|
||||||
import RepositoryModel from "../database/repositories/repositories.model";
|
|
||||||
import AnonymousError from "../AnonymousError";
|
|
||||||
import { isConnected } from "../database/database";
|
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
|
|
||||||
|
import AnonymousError from "../AnonymousError";
|
||||||
|
import { isConnected } from "../../server/database";
|
||||||
import { octokit } from "../GitHubUtils";
|
import { octokit } from "../GitHubUtils";
|
||||||
|
import { IRepositoryDocument } from "../model/repositories/repositories.types";
|
||||||
|
import RepositoryModel from "../model/repositories/repositories.model";
|
||||||
|
|
||||||
export class GitHubRepository {
|
export class GitHubRepository {
|
||||||
private _data: Partial<{
|
private _data: Partial<{
|
||||||
@@ -82,36 +82,24 @@ export default class GitHubStream extends GitHubBase {
|
|||||||
object: file,
|
object: file,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
const content = this.downloadFile(await this.data.getToken(), file_sha);
|
||||||
const content = this.downloadFile(await this.data.getToken(), file_sha);
|
|
||||||
|
|
||||||
// duplicate the stream to write it to the storage
|
// duplicate the stream to write it to the storage
|
||||||
const stream1 = content.pipe(new stream.PassThrough());
|
const stream1 = content.pipe(new stream.PassThrough());
|
||||||
const stream2 = content.pipe(new stream.PassThrough());
|
const stream2 = content.pipe(new stream.PassThrough());
|
||||||
storage.write(
|
|
||||||
file.repository.repoId,
|
content.on("error", (error) => {
|
||||||
file.filePath,
|
error = new AnonymousError("file_not_found", {
|
||||||
stream1,
|
|
||||||
this.type
|
|
||||||
);
|
|
||||||
return stream2;
|
|
||||||
} catch (error) {
|
|
||||||
if (
|
|
||||||
(error as any).status === 404 ||
|
|
||||||
(error as any).httpStatus === 404
|
|
||||||
) {
|
|
||||||
throw new AnonymousError("file_not_found", {
|
|
||||||
httpStatus: (error as any).status || (error as any).httpStatus,
|
|
||||||
cause: error as Error,
|
|
||||||
object: file,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
throw new AnonymousError("file_too_big", {
|
|
||||||
httpStatus: (error as any).status || (error as any).httpStatus,
|
httpStatus: (error as any).status || (error as any).httpStatus,
|
||||||
cause: error as Error,
|
cause: error as Error,
|
||||||
object: file,
|
object: file,
|
||||||
});
|
});
|
||||||
}
|
stream1.emit("error", error);
|
||||||
|
stream2.emit("error", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
storage.write(file.repository.repoId, file.filePath, stream1, this.type);
|
||||||
|
return stream2;
|
||||||
} finally {
|
} finally {
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Readable } from "stream";
|
||||||
|
|
||||||
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
|
import { Tree } from "../types";
|
||||||
|
import GitHubDownload from "./GitHubDownload";
|
||||||
|
import GitHubStream from "./GitHubStream";
|
||||||
|
import Zip from "./Zip";
|
||||||
|
|
||||||
|
export type Source = GitHubDownload | GitHubStream | Zip;
|
||||||
|
|
||||||
|
export interface SourceBase {
|
||||||
|
readonly type: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the fie content
|
||||||
|
* @param file the file of the content to retrieve
|
||||||
|
*/
|
||||||
|
getFileContent(file: AnonymizedFile): Promise<Readable>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all the files from a specific source
|
||||||
|
*/
|
||||||
|
getFiles(progress?: (status: string) => void): Promise<Tree>;
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import * as stream from "stream";
|
||||||
|
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../AnonymizedFile";
|
||||||
import storage from "../storage";
|
import storage from "../storage";
|
||||||
import * as stream from "stream";
|
import { SourceBase } from "./Source";
|
||||||
import { SourceBase } from "./GitHubBase";
|
|
||||||
|
|
||||||
export default class Zip implements SourceBase {
|
export default class Zip implements SourceBase {
|
||||||
type = "Zip";
|
type = "Zip";
|
||||||
File renamed without changes.
@@ -83,7 +83,15 @@ export default class FileSystem extends StorageBase {
|
|||||||
span.setAttribute("path", fullPath);
|
span.setAttribute("path", fullPath);
|
||||||
try {
|
try {
|
||||||
await this.mk(repoId, dirname(p));
|
await this.mk(repoId, dirname(p));
|
||||||
|
if (data instanceof Readable) {
|
||||||
|
data.on("error", (err) => {
|
||||||
|
this.rm(repoId, p);
|
||||||
|
});
|
||||||
|
}
|
||||||
return await fs.promises.writeFile(fullPath, data, "utf-8");
|
return await fs.promises.writeFile(fullPath, data, "utf-8");
|
||||||
|
} catch (err: any) {
|
||||||
|
span.recordException(err);
|
||||||
|
// throw err;
|
||||||
} finally {
|
} finally {
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
@@ -32,6 +32,8 @@ export default class S3Storage extends StorageBase {
|
|||||||
private client(timeout = 10000) {
|
private client(timeout = 10000) {
|
||||||
if (!config.S3_CLIENT_ID) throw new Error("S3_CLIENT_ID not set");
|
if (!config.S3_CLIENT_ID) throw new Error("S3_CLIENT_ID not set");
|
||||||
if (!config.S3_CLIENT_SECRET) throw new Error("S3_CLIENT_SECRET not set");
|
if (!config.S3_CLIENT_SECRET) throw new Error("S3_CLIENT_SECRET not set");
|
||||||
|
if (!config.S3_REGION) throw new Error("S3_REGION not set");
|
||||||
|
if (!config.S3_ENDPOINT) throw new Error("S3_ENDPOINT not set");
|
||||||
return new S3({
|
return new S3({
|
||||||
credentials: {
|
credentials: {
|
||||||
accessKeyId: config.S3_CLIENT_ID,
|
accessKeyId: config.S3_CLIENT_ID,
|
||||||
@@ -212,6 +214,14 @@ export default class S3Storage extends StorageBase {
|
|||||||
try {
|
try {
|
||||||
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
if (!config.S3_BUCKET) throw new Error("S3_BUCKET not set");
|
||||||
|
|
||||||
|
if (data instanceof Readable) {
|
||||||
|
data.on("error", (err) => {
|
||||||
|
console.error(`[ERROR] S3 write ${path}`, err);
|
||||||
|
span.recordException(err as Error);
|
||||||
|
this.rm(repoId, path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const params: PutObjectCommandInput = {
|
const params: PutObjectCommandInput = {
|
||||||
Bucket: config.S3_BUCKET,
|
Bucket: config.S3_BUCKET,
|
||||||
Key: join(this.repoPath(repoId), path),
|
Key: join(this.repoPath(repoId), path),
|
||||||
@@ -229,7 +239,6 @@ export default class S3Storage extends StorageBase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await parallelUploads3.done();
|
await parallelUploads3.done();
|
||||||
return;
|
|
||||||
} finally {
|
} finally {
|
||||||
span.end();
|
span.end();
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,10 @@ import * as archiver from "archiver";
|
|||||||
import { Response } from "express";
|
import { Response } from "express";
|
||||||
|
|
||||||
import { Tree } from "../types";
|
import { Tree } from "../types";
|
||||||
|
import S3Storage from "./S3";
|
||||||
|
import FileSystem from "./FileSystem";
|
||||||
|
|
||||||
|
export type Storage = S3Storage | FileSystem;
|
||||||
|
|
||||||
export enum FILE_TYPE {
|
export enum FILE_TYPE {
|
||||||
FILE = "file",
|
FILE = "file",
|
||||||
@@ -1,13 +1,3 @@
|
|||||||
import GitHubDownload from "./source/GitHubDownload";
|
|
||||||
import GitHubStream from "./source/GitHubStream";
|
|
||||||
import Zip from "./source/Zip";
|
|
||||||
import S3Storage from "./storage/S3";
|
|
||||||
import FileSystem from "./storage/FileSystem";
|
|
||||||
|
|
||||||
export type Source = GitHubDownload | GitHubStream | Zip;
|
|
||||||
|
|
||||||
export type Storage = S3Storage | FileSystem;
|
|
||||||
|
|
||||||
export interface Branch {
|
export interface Branch {
|
||||||
name: string;
|
name: string;
|
||||||
commit: string;
|
commit: string;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Queue, Worker } from "bullmq";
|
import { Queue, Worker } from "bullmq";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import Repository from "./Repository";
|
import Repository from "../core/Repository";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
export let cacheQueue: Queue<Repository>;
|
export let cacheQueue: Queue<Repository>;
|
||||||
@@ -37,7 +37,7 @@ export function startWorker() {
|
|||||||
});
|
});
|
||||||
const cacheWorker = new Worker<Repository>(
|
const cacheWorker = new Worker<Repository>(
|
||||||
cacheQueue.name,
|
cacheQueue.name,
|
||||||
path.resolve("build/src/processes/removeCache.js"),
|
path.resolve("build/queue/processes/removeCache.js"),
|
||||||
{
|
{
|
||||||
concurrency: 5,
|
concurrency: 5,
|
||||||
connection,
|
connection,
|
||||||
@@ -49,7 +49,7 @@ export function startWorker() {
|
|||||||
});
|
});
|
||||||
const removeWorker = new Worker<Repository>(
|
const removeWorker = new Worker<Repository>(
|
||||||
removeQueue.name,
|
removeQueue.name,
|
||||||
path.resolve("build/src/processes/removeRepository.js"),
|
path.resolve("build/queue/processes/removeRepository.js"),
|
||||||
{
|
{
|
||||||
concurrency: 5,
|
concurrency: 5,
|
||||||
connection,
|
connection,
|
||||||
@@ -62,7 +62,7 @@ export function startWorker() {
|
|||||||
|
|
||||||
const downloadWorker = new Worker<Repository>(
|
const downloadWorker = new Worker<Repository>(
|
||||||
downloadQueue.name,
|
downloadQueue.name,
|
||||||
path.resolve("build/src/processes/downloadRepository.js"),
|
path.resolve("build/queue/processes/downloadRepository.js"),
|
||||||
{
|
{
|
||||||
concurrency: 3,
|
concurrency: 3,
|
||||||
connection,
|
connection,
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { Exception, trace } from "@opentelemetry/api";
|
||||||
import { SandboxedJob } from "bullmq";
|
import { SandboxedJob } from "bullmq";
|
||||||
import { config } from "dotenv";
|
import { config } from "dotenv";
|
||||||
config();
|
config();
|
||||||
import Repository from "../Repository";
|
import Repository from "../../core/Repository";
|
||||||
import { getRepository as getRepositoryImport } from "../database/database";
|
import { getRepository as getRepositoryImport } from "../../server/database";
|
||||||
import { RepositoryStatus } from "../types";
|
import { RepositoryStatus } from "../../core/types";
|
||||||
import { Exception, trace } from "@opentelemetry/api";
|
|
||||||
|
|
||||||
export default async function (job: SandboxedJob<Repository, void>) {
|
export default async function (job: SandboxedJob<Repository, void>) {
|
||||||
const {
|
const {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { SandboxedJob } from "bullmq";
|
|
||||||
import Repository from "../Repository";
|
|
||||||
import { getRepository as getRepositoryImport } from "../database/database";
|
|
||||||
import { Exception, trace } from "@opentelemetry/api";
|
import { Exception, trace } from "@opentelemetry/api";
|
||||||
|
import { SandboxedJob } from "bullmq";
|
||||||
|
import Repository from "../../core/Repository";
|
||||||
|
import { getRepository as getRepositoryImport } from "../../server/database";
|
||||||
|
|
||||||
export default async function (job: SandboxedJob<Repository, void>) {
|
export default async function (job: SandboxedJob<Repository, void>) {
|
||||||
const {
|
const {
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { SandboxedJob } from "bullmq";
|
|
||||||
import Repository from "../Repository";
|
|
||||||
import { getRepository as getRepositoryImport } from "../database/database";
|
|
||||||
import { RepositoryStatus } from "../types";
|
|
||||||
import { trace } from "@opentelemetry/api";
|
import { trace } from "@opentelemetry/api";
|
||||||
|
import { SandboxedJob } from "bullmq";
|
||||||
|
import Repository from "../../core/Repository";
|
||||||
|
import { getRepository as getRepositoryImport } from "../../server/database";
|
||||||
|
import { RepositoryStatus } from "../../core/types";
|
||||||
|
|
||||||
export default async function (job: SandboxedJob<Repository, void>) {
|
export default async function (job: SandboxedJob<Repository, void>) {
|
||||||
const {
|
const {
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import mongoose, { ConnectOptions } from "mongoose";
|
import mongoose, { ConnectOptions } from "mongoose";
|
||||||
import Repository from "../Repository";
|
import Repository from "../core/Repository";
|
||||||
import config from "../../config";
|
import config from "../config";
|
||||||
import AnonymizedRepositoryModel from "./anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../core/AnonymousError";
|
||||||
import AnonymizedPullRequestModel from "./anonymizedPullRequests/anonymizedPullRequests.model";
|
import AnonymizedPullRequestModel from "../core/model/anonymizedPullRequests/anonymizedPullRequests.model";
|
||||||
import PullRequest from "../PullRequest";
|
import PullRequest from "../core/PullRequest";
|
||||||
|
|
||||||
const MONGO_URL = `mongodb://${config.DB_USERNAME}:${config.DB_PASSWORD}@${config.DB_HOSTNAME}:27017/`;
|
const MONGO_URL = `mongodb://${config.DB_USERNAME}:${config.DB_PASSWORD}@${config.DB_HOSTNAME}:27017/`;
|
||||||
|
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { config as dotenv } from "dotenv";
|
||||||
|
dotenv();
|
||||||
|
|
||||||
import { createClient } from "redis";
|
import { createClient } from "redis";
|
||||||
import { resolve, join } from "path";
|
import { resolve, join } from "path";
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
@@ -7,16 +10,15 @@ 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 passport from "passport";
|
import * as passport from "passport";
|
||||||
|
import { connect } from "./database";
|
||||||
import config from "../config";
|
|
||||||
import { connect } from "./database/database";
|
|
||||||
import { initSession, router as connectionRouter } from "./routes/connection";
|
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 "../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { conferenceStatusCheck, repositoryStatusCheck } from "./schedule";
|
import { conferenceStatusCheck, repositoryStatusCheck } from "./schedule";
|
||||||
import { startWorker } from "./queue";
|
import { startWorker } from "../queue";
|
||||||
import AnonymizedPullRequestModel from "./database/anonymizedPullRequests/anonymizedPullRequests.model";
|
import AnonymizedPullRequestModel from "../core/model/anonymizedPullRequests/anonymizedPullRequests.model";
|
||||||
import { getUser } from "./routes/route-utils";
|
import { getUser } from "./routes/route-utils";
|
||||||
|
import config from "../config";
|
||||||
|
|
||||||
function indexResponse(req: express.Request, res: express.Response) {
|
function indexResponse(req: express.Request, res: express.Response) {
|
||||||
if (
|
if (
|
||||||
@@ -214,3 +216,5 @@ export default async function start() {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start();
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Queue } from "bullmq";
|
import { Queue } from "bullmq";
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import AnonymizedRepositoryModel from "../database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "../../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import ConferenceModel from "../database/conference/conferences.model";
|
import ConferenceModel from "../../core/model/conference/conferences.model";
|
||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../../core/model/users/users.model";
|
||||||
import { cacheQueue, downloadQueue, removeQueue } from "../queue";
|
import { cacheQueue, downloadQueue, removeQueue } from "../../queue";
|
||||||
import Repository from "../Repository";
|
import Repository from "../../core/Repository";
|
||||||
import User from "../User";
|
import User from "../../core/User";
|
||||||
import { ensureAuthenticated } from "./connection";
|
import { ensureAuthenticated } from "./connection";
|
||||||
import { handleError, getUser, isOwnerOrAdmin, getRepo } from "./route-utils";
|
import { handleError, getUser, isOwnerOrAdmin, getRepo } from "./route-utils";
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import Conference from "../Conference";
|
import Conference from "../../core/Conference";
|
||||||
import ConferenceModel from "../database/conference/conferences.model";
|
import ConferenceModel from "../../core/model/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";
|
import { IConferenceDocument } from "../../core/model/conference/conferences.types";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -7,9 +7,9 @@ import { Profile, Strategy } from "passport-github2";
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
|
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../../core/model/users/users.model";
|
||||||
import { IUserDocument } from "../database/users/users.types";
|
import { IUserDocument } from "../../core/model/users/users.types";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
|
|
||||||
export function ensureAuthenticated(
|
export function ensureAuthenticated(
|
||||||
req: express.Request,
|
req: express.Request,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../../core/AnonymizedFile";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import { getRepo, handleError } from "./route-utils";
|
import { getRepo, handleError } from "./route-utils";
|
||||||
|
|
||||||
export const router = express.Router();
|
export const router = express.Router();
|
||||||
@@ -49,7 +49,7 @@ router.get(
|
|||||||
// cache the file for 5min
|
// cache the file for 5min
|
||||||
res.header("Cache-Control", "max-age=300");
|
res.header("Cache-Control", "max-age=300");
|
||||||
await repo.countView();
|
await repo.countView();
|
||||||
f.send(res);
|
await f.send(res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return handleError(error, res, req);
|
return handleError(error, res, req);
|
||||||
}
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -7,11 +7,11 @@ import {
|
|||||||
handleError,
|
handleError,
|
||||||
isOwnerOrAdmin,
|
isOwnerOrAdmin,
|
||||||
} from "./route-utils";
|
} from "./route-utils";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import { IAnonymizedPullRequestDocument } from "../database/anonymizedPullRequests/anonymizedPullRequests.types";
|
import { IAnonymizedPullRequestDocument } from "../../core/model/anonymizedPullRequests/anonymizedPullRequests.types";
|
||||||
import PullRequest from "../PullRequest";
|
import PullRequest from "../../core/PullRequest";
|
||||||
import AnonymizedPullRequestModel from "../database/anonymizedPullRequests/anonymizedPullRequests.model";
|
import AnonymizedPullRequestModel from "../../core/model/anonymizedPullRequests/anonymizedPullRequests.model";
|
||||||
import { RepositoryStatus } from "../types";
|
import { RepositoryStatus } from "../../core/types";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
|
|
||||||
import { getPullRequest, handleError } from "./route-utils";
|
import { getPullRequest, handleError } from "./route-utils";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import { ensureAuthenticated } from "./connection";
|
import { ensureAuthenticated } from "./connection";
|
||||||
|
|
||||||
import * as db from "../database/database";
|
import * as db from "../database";
|
||||||
import { getRepo, getUser, handleError, isOwnerOrAdmin } from "./route-utils";
|
import { getRepo, getUser, handleError, isOwnerOrAdmin } from "./route-utils";
|
||||||
import { getRepositoryFromGitHub } from "../source/GitHubRepository";
|
import { getRepositoryFromGitHub } from "../../core/source/GitHubRepository";
|
||||||
import gh = require("parse-github-url");
|
import gh = require("parse-github-url");
|
||||||
import AnonymizedRepositoryModel from "../database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "../../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import { IAnonymizedRepositoryDocument } from "../database/anonymizedRepositories/anonymizedRepositories.types";
|
import { IAnonymizedRepositoryDocument } from "../../core/model/anonymizedRepositories/anonymizedRepositories.types";
|
||||||
import Repository from "../Repository";
|
import Repository from "../../core/Repository";
|
||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../../core/model/users/users.model";
|
||||||
import ConferenceModel from "../database/conference/conferences.model";
|
import ConferenceModel from "../../core/model/conference/conferences.model";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import { downloadQueue, removeQueue } from "../queue";
|
import { downloadQueue, removeQueue } from "../../queue";
|
||||||
import RepositoryModel from "../database/repositories/repositories.model";
|
import RepositoryModel from "../../core/model/repositories/repositories.model";
|
||||||
import User from "../User";
|
import User from "../../core/User";
|
||||||
import { RepositoryStatus } from "../types";
|
import { RepositoryStatus } from "../../core/types";
|
||||||
import { IUserDocument } from "../database/users/users.types";
|
import { IUserDocument } from "../../core/model/users/users.types";
|
||||||
import { checkToken } from "../GitHubUtils";
|
import { checkToken } from "../../core/GitHubUtils";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -4,9 +4,9 @@ import * as stream from "stream";
|
|||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
|
|
||||||
import { getRepo, handleError } from "./route-utils";
|
import { getRepo, handleError } from "./route-utils";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import { downloadQueue } from "../queue";
|
import { downloadQueue } from "../../queue";
|
||||||
import { RepositoryStatus } from "../types";
|
import { RepositoryStatus } from "../../core/types";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import * as db from "../database/database";
|
import * as db from "../database";
|
||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../../core/model/users/users.model";
|
||||||
import User from "../User";
|
import User from "../../core/User";
|
||||||
|
import { HTTPError } from "got";
|
||||||
|
|
||||||
export async function getPullRequest(
|
export async function getPullRequest(
|
||||||
req: express.Request,
|
req: express.Request,
|
||||||
@@ -85,6 +86,11 @@ function printError(error: any, req?: express.Request) {
|
|||||||
if (req.originalUrl === "/api/repo/undefined/options") return;
|
if (req.originalUrl === "/api/repo/undefined/options") return;
|
||||||
}
|
}
|
||||||
console.error(message);
|
console.error(message);
|
||||||
|
} else if (error instanceof HTTPError) {
|
||||||
|
let message = `[ERROR] HTTP.${
|
||||||
|
error.code
|
||||||
|
} ${error.message.toString()} ${error.stack?.split("\n")[1].trim()}`;
|
||||||
|
console.error(message);
|
||||||
} else if (error instanceof Error) {
|
} else if (error instanceof Error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
} else {
|
} else {
|
||||||
@@ -107,7 +113,10 @@ export function handleError(
|
|||||||
status = error.httpStatus;
|
status = error.httpStatus;
|
||||||
} else if (error.$metadata?.httpStatusCode) {
|
} else if (error.$metadata?.httpStatusCode) {
|
||||||
status = error.$metadata.httpStatusCode;
|
status = error.$metadata.httpStatusCode;
|
||||||
} else if (message && message.indexOf("not_found") > -1) {
|
} else if (
|
||||||
|
message &&
|
||||||
|
(message.indexOf("not_found") > -1 || message.indexOf("(Not Found)") > -1)
|
||||||
|
) {
|
||||||
status = 404;
|
status = 404;
|
||||||
} else if (message && message.indexOf("not_connected") > -1) {
|
} else if (message && message.indexOf("not_connected") > -1) {
|
||||||
status = 401;
|
status = 401;
|
||||||
@@ -2,8 +2,8 @@ import * as express from "express";
|
|||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
import { ensureAuthenticated } from "./connection";
|
import { ensureAuthenticated } from "./connection";
|
||||||
import { handleError, getUser, isOwnerOrAdmin } from "./route-utils";
|
import { handleError, getUser, isOwnerOrAdmin } from "./route-utils";
|
||||||
import UserModel from "../database/users/users.model";
|
import UserModel from "../../core/model/users/users.model";
|
||||||
import User from "../User";
|
import User from "../../core/User";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import { getRepo, handleError } from "./route-utils";
|
import { getRepo, handleError } from "./route-utils";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import AnonymizedFile from "../AnonymizedFile";
|
import AnonymizedFile from "../../core/AnonymizedFile";
|
||||||
import AnonymousError from "../AnonymousError";
|
import AnonymousError from "../../core/AnonymousError";
|
||||||
import { Tree, TreeElement } from "../types";
|
import { Tree, TreeElement } from "../../core/types";
|
||||||
import * as marked from "marked";
|
import * as marked from "marked";
|
||||||
import { streamToString } from "../anonymize-utils";
|
import { streamToString } from "../../core/anonymize-utils";
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as schedule from "node-schedule";
|
import * as schedule from "node-schedule";
|
||||||
import Conference from "./Conference";
|
import Conference from "../core/Conference";
|
||||||
import AnonymizedRepositoryModel from "./database/anonymizedRepositories/anonymizedRepositories.model";
|
import AnonymizedRepositoryModel from "../core/model/anonymizedRepositories/anonymizedRepositories.model";
|
||||||
import ConferenceModel from "./database/conference/conferences.model";
|
import ConferenceModel from "../core/model/conference/conferences.model";
|
||||||
import Repository from "./Repository";
|
import Repository from "../core/Repository";
|
||||||
|
|
||||||
export function conferenceStatusCheck() {
|
export function conferenceStatusCheck() {
|
||||||
// check every 6 hours the status of the conferences
|
// check every 6 hours the status of the conferences
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { config as dotenv } from "dotenv";
|
||||||
|
dotenv();
|
||||||
|
|
||||||
|
import * as express from "express";
|
||||||
|
import * as compression from "compression";
|
||||||
|
|
||||||
|
import config from "../config";
|
||||||
|
import router from "./route";
|
||||||
|
import { handleError } from "../server/routes/route-utils";
|
||||||
|
import AnonymousError from "../core/AnonymousError";
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.use(compression());
|
||||||
|
|
||||||
|
app.use("/api", router);
|
||||||
|
|
||||||
|
app.all("*", (req, res) => {
|
||||||
|
handleError(
|
||||||
|
new AnonymousError("file_not_found", {
|
||||||
|
httpStatus: 404,
|
||||||
|
object: req.originalUrl,
|
||||||
|
}),
|
||||||
|
res,
|
||||||
|
req
|
||||||
|
);
|
||||||
|
});
|
||||||
|
app.listen(config.PORT, () => {
|
||||||
|
console.log(`Server started on http://streamer:${config.PORT}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import * as express from "express";
|
||||||
|
import GitHubStream from "../core/source/GitHubStream";
|
||||||
|
import { AnonymizeTransformer, isTextFile } from "../core/anonymize-utils";
|
||||||
|
import { handleError } from "../server/routes/route-utils";
|
||||||
|
import { contentType } from "mime-types";
|
||||||
|
|
||||||
|
export const router = express.Router();
|
||||||
|
|
||||||
|
router.post("/", async (req: express.Request, res: express.Response) => {
|
||||||
|
req.body = req.body || {};
|
||||||
|
const token: string = req.body.token;
|
||||||
|
const repoFullName = req.body.repoFullName.split("/");
|
||||||
|
const repoId = req.body.repoId;
|
||||||
|
const branch = req.body.branch;
|
||||||
|
const fileSha = req.body.sha;
|
||||||
|
const commit = req.body.commit;
|
||||||
|
const filePath = req.body.filePath;
|
||||||
|
const anonymizerOptions = req.body.anonymizerOptions;
|
||||||
|
const anonymizer = new AnonymizeTransformer(anonymizerOptions);
|
||||||
|
|
||||||
|
const source = new GitHubStream({
|
||||||
|
repoId,
|
||||||
|
organization: repoFullName[0],
|
||||||
|
repoName: repoFullName[1],
|
||||||
|
commit: commit,
|
||||||
|
getToken: () => token,
|
||||||
|
});
|
||||||
|
const content = source.downloadFile(token, fileSha);
|
||||||
|
try {
|
||||||
|
const mime = contentType(filePath);
|
||||||
|
if (mime && !filePath.endsWith(".ts")) {
|
||||||
|
res.contentType(mime);
|
||||||
|
} else if (isTextFile(filePath)) {
|
||||||
|
res.contentType("text/plain");
|
||||||
|
}
|
||||||
|
res.header("Accept-Ranges", "none");
|
||||||
|
anonymizer.once("transform", (data) => {
|
||||||
|
if (!mime && data.isText) {
|
||||||
|
res.contentType("text/plain");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
function handleStreamError(error: Error) {
|
||||||
|
if (!content.closed && !content.destroyed) {
|
||||||
|
content.destroy();
|
||||||
|
}
|
||||||
|
handleError(error, res);
|
||||||
|
}
|
||||||
|
content
|
||||||
|
.on("error", handleStreamError)
|
||||||
|
.pipe(anonymizer)
|
||||||
|
.pipe(res)
|
||||||
|
.on("error", handleStreamError)
|
||||||
|
.on("close", () => {
|
||||||
|
if (!content.closed && !content.destroyed) {
|
||||||
|
content.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, res);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
+1
-1
@@ -17,6 +17,6 @@
|
|||||||
"esModuleInterop": false,
|
"esModuleInterop": false,
|
||||||
"incremental": true
|
"incremental": true
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "index.ts", "cli.ts"],
|
"include": ["src/**/*.ts", "index.ts", "src/cli/index.ts"],
|
||||||
"exclude": ["node_modules", ".vscode"]
|
"exclude": ["node_modules", ".vscode"]
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user