mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-02-13 02:42:45 +00:00
339 lines
9.6 KiB
JavaScript
339 lines
9.6 KiB
JavaScript
const ofs = require("fs");
|
|
const fs = require("fs").promises;
|
|
|
|
const express = require("express");
|
|
const gh = require("parse-github-url");
|
|
const arrayEquals = require("array-equal");
|
|
|
|
const connection = require("./connection");
|
|
const githubUtils = require("../utils/github");
|
|
const db = require("../utils/database");
|
|
const repoUtils = require("../utils/repository");
|
|
const config = require("../config");
|
|
|
|
const router = express.Router();
|
|
|
|
// user needs to be connected for all user API
|
|
router.use(connection.ensureAuthenticated);
|
|
|
|
router.get("/:repoId/", async (req, res) => {
|
|
try {
|
|
const repository = await repoUtils.getAnonymizedRepoDetails(
|
|
req.params.repoId,
|
|
req.user
|
|
);
|
|
if (repository) {
|
|
return res.json(repository);
|
|
}
|
|
res.status(404).send("repo_not_found");
|
|
} catch (error) {
|
|
res.status(500).send(error);
|
|
}
|
|
});
|
|
|
|
// update a repository
|
|
router.post("/:repoId/", async (req, res) => {
|
|
const repoUpdate = req.body;
|
|
|
|
let repoConfig = await repoUtils.getConfig(req.params.repoId);
|
|
if (repoConfig == null) {
|
|
return res.status(500).json({ error: "repo_not_found" });
|
|
}
|
|
if (repoConfig.owner != req.user.username) {
|
|
return res.status(401).json({ error: "not_authorized" });
|
|
}
|
|
if (!repoUpdate.branch) {
|
|
return res.status(500).json({ error: "branch_not_specified" });
|
|
}
|
|
if (!repoUpdate.options) {
|
|
return res.status(500).json({ error: "options_not_provided" });
|
|
}
|
|
if (!Array.isArray(repoUpdate.terms)) {
|
|
return res.status(500).send({ error: "invalid_terms_format" });
|
|
}
|
|
|
|
const details = await repoUtils.getRepoDetails({
|
|
repoConfig,
|
|
force: true,
|
|
token: req.user.accessToken,
|
|
});
|
|
if (repoUpdate.options.mode == "download") {
|
|
// details.size is in kilobytes
|
|
if (details.size > config.MAX_REPO_SIZE) {
|
|
return res.status(500).send({ error: "invalid_mode" });
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (repoUpdate.commit != repoConfig.commit) {
|
|
repoUpdate.anonymizeDate = new Date();
|
|
await repoUtils.removeRepository(repoConfig);
|
|
}
|
|
if (
|
|
!arrayEquals(repoUpdate.terms, repoConfig.terms) ||
|
|
repoUpdate.options.link != repoConfig.options.link ||
|
|
repoUpdate.options.image != repoConfig.options.image
|
|
) {
|
|
repoUpdate.anonymizeDate = new Date();
|
|
if (ofs.existsSync(repoUtils.getAnonymizedPath(repoConfig.repoId))) {
|
|
await fs.rm(repoUtils.getAnonymizedPath(repoConfig.repoId), {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
}
|
|
}
|
|
const data = {
|
|
terms: repoUpdate.terms,
|
|
branch: repoUpdate.branch,
|
|
commit: repoUpdate.commit,
|
|
options: {
|
|
expirationMode: repoUpdate.options.expirationMode,
|
|
expirationDate: repoUpdate.options.expirationDate,
|
|
update: repoUpdate.options.update,
|
|
image: repoUpdate.options.image,
|
|
pdf: repoUpdate.options.pdf,
|
|
notebook: repoUpdate.options.notebook,
|
|
loc: repoUpdate.options.loc,
|
|
link: repoUpdate.options.link,
|
|
mode: repoUpdate.options.mode,
|
|
page: repoUpdate.options.page,
|
|
},
|
|
};
|
|
if (repoUpdate.options.page) {
|
|
data.options.pageSource = details.pageSource;
|
|
}
|
|
await db.get("anonymized_repositories").updateOne(
|
|
{
|
|
repoId: repoConfig.repoId,
|
|
},
|
|
{
|
|
$set: data,
|
|
}
|
|
);
|
|
|
|
repoConfig = await repoUtils.getConfig(repoUpdate.repoId);
|
|
await repoUtils.updateStatus(repoConfig, "preparing");
|
|
|
|
res.send("ok");
|
|
|
|
await githubUtils.downloadRepoAndAnonymize(repoConfig);
|
|
await repoUtils.updateStatus(repoConfig, "ready");
|
|
} catch (error) {
|
|
console.error(error);
|
|
await repoUtils.updateStatus(repoConfig, "error", error);
|
|
return res.status(500).json({ error });
|
|
}
|
|
});
|
|
|
|
// refresh a repository
|
|
router.post("/:repoId/refresh", async (req, res) => {
|
|
const repoConfig = await repoUtils.getConfig(req.params.repoId);
|
|
if (repoConfig == null) {
|
|
return res.status(500).json({ error: "repo_not_found" });
|
|
}
|
|
if (repoConfig.owner != req.user.username) {
|
|
return res.status(401).json({ error: "not_authorized" });
|
|
}
|
|
try {
|
|
await repoUtils.updateAnonymizedRepository(repoConfig);
|
|
return res.send("ok");
|
|
} catch (error) {
|
|
return res.status(500).json({ error });
|
|
}
|
|
});
|
|
|
|
// delete a repository
|
|
router.delete("/:repoId/", async (req, res) => {
|
|
const repoConfig = await repoUtils.getConfig(req.params.repoId);
|
|
if (repoConfig == null) {
|
|
return res.status(500).json({ error: "repo_not_found" });
|
|
}
|
|
if (repoConfig.owner != req.user.username) {
|
|
return res.status(401).json({ error: "not_authorized" });
|
|
}
|
|
try {
|
|
await repoUtils.updateStatus(repoConfig, "removed");
|
|
await repoUtils.removeRepository(repoConfig);
|
|
console.log(`${req.params.repoId} is removed`);
|
|
return res.json("ok");
|
|
} catch (error) {
|
|
return res.status(500).json({ error });
|
|
}
|
|
});
|
|
|
|
// claim a repository
|
|
router.post("/claim", async (req, res) => {
|
|
if (!req.body.repoId) {
|
|
return res.status(500).json({ error: "repoId_not_defined" });
|
|
}
|
|
if (!req.body.repoUrl) {
|
|
return res.status(500).json({ error: "repoUrl_not_defined" });
|
|
}
|
|
|
|
const repoConfig = await repoUtils.getConfig(req.body.repoId);
|
|
if (repoConfig == null) {
|
|
return res.status(500).json({ error: "repo_not_found" });
|
|
}
|
|
|
|
const repo = gh(req.body.repoUrl);
|
|
if (repoConfig.fullName != repo.repository) {
|
|
return res.status(500).json({ error: "repo_not_found" });
|
|
}
|
|
|
|
console.log(`${req.user.username} claims ${repoConfig.fullName}.`);
|
|
try {
|
|
await db
|
|
.get("anonymized_repositories")
|
|
.updateOne(
|
|
{ repoId: repoConfig.repoId },
|
|
{ $set: { owner: req.user.username } }
|
|
);
|
|
return res.send("Ok");
|
|
} catch (error) {
|
|
return res.status(500).json({ error });
|
|
}
|
|
});
|
|
|
|
router.get("/:owner/:repo/", async (req, res) => {
|
|
try {
|
|
const repository = await repoUtils.getRepoDetails({
|
|
owner: req.params.owner,
|
|
repo: req.params.repo,
|
|
token: req.user.accessToken,
|
|
force: req.query.force === "1",
|
|
});
|
|
if (repository) {
|
|
return res.json(repository);
|
|
}
|
|
res.status(404).send("repo_not_found");
|
|
} catch (error) {
|
|
res.status(500).send(error);
|
|
}
|
|
});
|
|
|
|
router.get("/:owner/:repo/branches", async (req, res) => {
|
|
try {
|
|
const repository = await repoUtils.getRepoBranches({
|
|
owner: req.params.owner,
|
|
repo: req.params.repo,
|
|
token: req.user.accessToken,
|
|
force: req.query.force === "1",
|
|
});
|
|
if (repository) {
|
|
return res.json(repository);
|
|
}
|
|
res.status(404).send("repo_not_found");
|
|
} catch (error) {
|
|
res.status(500).send(error);
|
|
}
|
|
});
|
|
|
|
router.get("/:owner/:repo/readme", async (req, res) => {
|
|
try {
|
|
const readme = await repoUtils.getRepoReadme({
|
|
owner: req.params.owner,
|
|
repo: req.params.repo,
|
|
token: req.user.accessToken,
|
|
force: req.query.force === "1",
|
|
});
|
|
if (readme) {
|
|
return res.send(readme);
|
|
}
|
|
res.status(404).send("repo_not_found");
|
|
} catch (error) {
|
|
res.status(500).send(error);
|
|
}
|
|
});
|
|
|
|
router.post("/", async (req, res) => {
|
|
const repoConfig = req.body;
|
|
|
|
const repository = await repoUtils.getConfig(repoConfig.repoId);
|
|
const cacheExist = ofs.existsSync(
|
|
repoUtils.getOriginalPath(repoConfig.repoId)
|
|
);
|
|
if (repository && cacheExist) {
|
|
return res.status(500).send({ error: "repoId_already_used" });
|
|
}
|
|
var validCharacters = /^[0-9a-zA-Z\-\_]+$/;
|
|
if (
|
|
!repoConfig.repoId.match(validCharacters) ||
|
|
repoConfig.repoId.length < 3
|
|
) {
|
|
return res.status(500).send({ error: "invalid_repoId" });
|
|
}
|
|
if (!repoConfig.branch) {
|
|
return res.status(500).json({ error: "branch_not_specified" });
|
|
}
|
|
if (!repoConfig.options) {
|
|
return res.status(500).json({ error: "options_not_provided" });
|
|
}
|
|
if (!Array.isArray(repoConfig.terms)) {
|
|
return res.status(500).send({ error: "invalid_terms_format" });
|
|
}
|
|
|
|
await repoUtils.getRepoBranches({ repoConfig, token: req.user.accessToken });
|
|
const details = await repoUtils.getRepoDetails({
|
|
repoConfig,
|
|
token: req.user.accessToken,
|
|
});
|
|
if (repoConfig.options.mode == "download") {
|
|
// details.size is in kilobytes
|
|
if (details.size > config.MAX_REPO_SIZE) {
|
|
return res.status(500).send({ error: "non_supported_mode" });
|
|
}
|
|
}
|
|
|
|
const data = {
|
|
repoId: repoConfig.repoId,
|
|
fullName: repoConfig.fullName,
|
|
status: "preparing",
|
|
terms: repoConfig.terms,
|
|
owner: req.user.profile.username,
|
|
token: req.user.accessToken,
|
|
branch: repoConfig.branch,
|
|
conference: repoConfig.conference,
|
|
commit: repoConfig.commit
|
|
? repoConfig.commit
|
|
: details.branches[repoConfig.branch].commit.sha,
|
|
anonymizeDate: new Date(),
|
|
options: {
|
|
expirationMode: repoConfig.options.expirationMode,
|
|
expirationDate: repoConfig.options.expirationDate,
|
|
update: repoConfig.options.update,
|
|
image: repoConfig.options.image,
|
|
pdf: repoConfig.options.pdf,
|
|
notebook: repoConfig.options.notebook,
|
|
loc: repoConfig.options.loc,
|
|
link: repoConfig.options.link,
|
|
mode: repoConfig.options.mode,
|
|
page: repoConfig.options.page,
|
|
},
|
|
};
|
|
if (repoConfig.options.page) {
|
|
data.options.pageSource = details.pageSource;
|
|
}
|
|
await db.get("anonymized_repositories").updateOne(
|
|
{
|
|
repoId: data.repoId,
|
|
},
|
|
{
|
|
$set: data,
|
|
},
|
|
{ upsert: true }
|
|
);
|
|
res.send("ok");
|
|
try {
|
|
await githubUtils.downloadRepoAndAnonymize(data);
|
|
await repoUtils.updateStatus(repoConfig, "ready");
|
|
} catch (error) {
|
|
console.error(error);
|
|
await repoUtils.updateStatus(repoConfig, "error", "unable_to_anonymize");
|
|
return res
|
|
.status(500)
|
|
.json({ error: "unable_to_anonymize", message: error.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|