first version of the exploration of repositories

This commit is contained in:
tdurieux
2020-06-05 13:17:10 +02:00
parent 322e14608d
commit 141d016aae
25 changed files with 12423 additions and 577 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
config.js
repositories/
# Created by https://www.gitignore.io/api/node
# Edit at https://www.gitignore.io/?templates=node

295
index.js
View File

@@ -1,26 +1,289 @@
const ofs = require("fs");
const fs = require("fs").promises;
const path = require("path");
const downloadGit = require("download-git-repo");
const { Octokit } = require("@octokit/rest");
const loc = require("@umijs/linguist");
const gh = require("parse-github-url");
const passport = require("passport");
const session = require("express-session");
const FileStore = require("session-file-store")(session);
const GitHubStrategy = require("passport-github2").Strategy;
const express = require("express");
const compression = require('compression');
const compression = require("compression");
const bodyParser = require("body-parser");
var app = express();
const config = require("./config");
const app = express();
app.use(bodyParser.json());
app.use(compression())
app.use(compression());
app.use(express.static(__dirname + "/publics"));
passport.serializeUser(function (user, done) {
done(null, user);
});
app.get("/myrepo", (req, res) => {
res.status(500).send("To implement!")
})
passport.deserializeUser(function (obj, done) {
done(null, obj);
});
app.get("/repository/:id/commit/:sha", (req, res) => {
res.status(500).send("To implement!")
})
app.get("/r/:id/:path", (req, res) => {
res.status(500).send("To implement!")
})
passport.use(
new GitHubStrategy(
{
clientID: config.clientId,
clientSecret: config.clientSecret,
callbackURL: config.authCallback,
},
(accessToken, refreshToken, profile, done) => {
// asynchronous verification, for effect...
console.log({ accessToken, refreshToken, profile });
done(null, { accessToken, refreshToken, profile });
// an example of how you might save a user
// new User({ username: profile.username }).fetch().then(user => {
// if (!user) {
// user = User.forge({ username: profile.username })
// }
//
// user.save({ profile: profile, access_token: accessToken }).then(() => {
// return done(null, user)
// })
// })
}
)
);
app.use(
session({
secret: "keyboard cat",
resave: true,
saveUninitialized: true,
store: new FileStore({
path: "./session-store",
}),
})
);
app.use(passport.initialize());
app.use(passport.session());
app.get(
"/github/login",
passport.authenticate("github", { scope: ["repo"] }), /// Note the scope here
function (req, res) {
console.log("/github/login");
}
);
app.get(
"/github/auth",
passport.authenticate("github", { failureRedirect: "/" }),
function (req, res) {
console.log("here");
res.redirect("/");
}
);
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/github/login");
}
app.get("/api/user", async (req, res) => {
if (req.user) {
res.json({ username: req.user.profile.username });
} else {
res.status(403).json({ error: "not_connected" });
}
});
app.get("/api/repos", ensureAuthenticated, async (req, res) => {
const octokit = new Octokit({ auth: req.user.accessToken });
const repos = await octokit.repos.listForAuthenticatedUser({
visibility: "all",
sort: "pushed",
per_page: 100,
});
res.json(repos);
});
app.get("/([r|repository])/:id/commit/:sha", (req, res) => {
res.status(500).send("To implement!");
});
function downloadRepoAndAnonymize(repoConfig) {
const cachePath = path.resolve(
__dirname,
"repositories",
repoConfig.id,
"cache"
);
return new Promise(async (resolve, reject) => {
fs.access(cachePath, ofs.constants.F_OK).then(
() => {},
(_) => {
try {
const opt = {
filter: (file) => {
return true;
},
map: (file) => {
if (file.path.indexOf(".md") > -1) {
let content = file.data.toString();
for (let term of repoConfig.terms) {
content = content.replace(new RegExp(term, "gi"), "XXX");
}
file.data = content;
let path = file.path;
for (let term of repoConfig.terms) {
path = path.replace(new RegExp(term, "gi"), "XXX");
}
file.path = path;
}
return file;
},
};
const gurl = gh(repoConfig.repository);
if (repoConfig.token) {
opt.headers = {
"Authorization": `token ${repoConfig.token}`,
"user-agent":
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36",
"accept": "application/vnd.github.3.raw",
};
opt.clone = false;
}
const url = `direct:https://api.github.com/repos/${gurl.repo}/tarball`;
downloadGit(url, cachePath, opt, (err) => {
console.log(err);
resolve();
});
} catch (error) {
console.log(error);
resolve();
}
}
);
});
}
async function walk(dir, root) {
if (root == null) {
root = dir;
}
let files = await fs.readdir(dir);
const output = {};
for (let file of files) {
let filePath = path.join(dir, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
output[file] = await walk(filePath, root);
} else if (stats.isFile()) {
output[file] = stats.size;
}
}
return output;
}
app.get("/api/files/:id/", (req, res) => {
const repo_id = req.params.id;
if (!repo_id) {
return res.status(404).json({ error: "invalid_repo_id" });
}
const repoPath = path.resolve(__dirname, "repositories", repo_id);
fs.access(repoPath, ofs.constants.F_OK).then(
(_) => {
fs.readFile(path.resolve(repoPath, "config.json")).then(
async (data) => {
data = JSON.parse(data);
const repoCache = path.join(repoPath, "cache");
if (!ofs.existsSync(repoCache)) {
await downloadRepoAndAnonymize(data, repo_id);
}
fs.access(repoCache, ofs.constants.F_OK).then(
async (_) => {
res.json(await walk(repoCache));
},
(_) => res.status(404).json({ error: "repo_not_found" })
);
},
(_) => res.status(404).json({ error: "config_error" })
);
},
(_) => res.status(404).json({ error: "repo_not_found" })
);
});
app.get("/api/repository/:id/:path*", (req, res) => {
const repo_id = req.params.id;
console.log(repo_id);
if (!repo_id) {
return res.status(404).json({ error: "invalid_repo_id" });
}
const repoPath = path.resolve(__dirname, "repositories", repo_id);
const repoConfig = path.join(repoPath, "config.json");
const repoCache = path.join(repoPath, "cache");
fs.access(repoConfig, ofs.constants.F_OK).then(
(_) => {
fs.readFile(repoConfig).then(
async (data) => {
data = JSON.parse(data);
if (!ofs.existsSync(repoCache)) {
await downloadRepoAndAnonymize(data, repo_id);
}
let requestPath = req.params.path;
if (req.params[0]) {
requestPath += req.params[0];
}
if (requestPath == null) {
requestPath = "README.md";
}
const ppath = path.join(repoCache, requestPath);
fs.access(ppath, ofs.constants.F_OK).then(
(ok) => res.sendFile(ppath, { dotfiles: "allow" }),
(ko) =>
res
.status(404)
.json({ error: "file_not_found", path: requestPath })
);
},
(_) => res.status(404).json({ error: "config_error" })
);
},
(_) => res.status(404).json({ error: "repo_not_found" })
);
});
app.get("/api/stat/:id/", (req, res) => {
const repo_id = req.params.id;
const repoPath = path.resolve(__dirname, "repositories", repo_id);
const repoCache = path.join(repoPath, "cache");
if (ofs.existsSync(repoCache)) {
res.json(loc(repoCache).languages);
} else {
res.status(404).json({ error: "repo_not_found" });
}
});
app.post("/", (req, res) => {
res.status(500).send("To implement!")
})
res.status(500).send("To implement!");
});
app.route("api")
app.use(express.static(__dirname + "/public"));
function homeAppResponse(req, res) {
res.sendFile(path.resolve(__dirname, "public", "index.html"));
}
function exploreAppResponse(req, res) {
res.sendFile(path.resolve(__dirname, "public", "explore.html"));
}
app
.get("/", homeAppResponse)
.get("/myrepo", homeAppResponse)
.get("/r/*", exploreAppResponse)
.get("/repository/*", exploreAppResponse);
app.listen(5000, () => {});

8486
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,9 @@
"description": "Anonymise github repositories for double blind reviews",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js",
"dev": "nodemon index.js"
},
"repository": {
"type": "git",
@@ -17,6 +19,27 @@
},
"homepage": "https://github.com/tdurieux/anonymous_github#readme",
"dependencies": {
"express": "^4.17.1"
"@octokit/rest": "^17.9.2",
"@umijs/linguist": "^1.0.1",
"compression": "^1.7.4",
"download-git-repo": "^3.0.2",
"express": "^4.17.1",
"express-session": "^1.17.1",
"jszip": "^3.4.0",
"parse-github-url": "^1.0.2",
"passport": "^0.4.1",
"passport-github2": "^0.1.12",
"path": "^0.12.7",
"request": "^2.88.2",
"session-file-store": "^1.4.0"
},
"devDependencies": {
"nodemon": "^2.0.4"
},
"nodemonConfig": {
"ignore": [
"public/*",
"session-store/*"
]
}
}

View File

@@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.8.0/github-markdown.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet" type="text/css" />
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
<style>
h1 {
font-size: 234px;
text-align: center;
left: 0;
line-height: 200px;
margin: auto;
margin-top: -100px;
position: absolute;
top: 50%;
width: 100%;
}
</style>
</head>
<body>
<h1>404</h1>
</body>
</html>

4
public/css/font-awesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,51 +1,56 @@
.fadeIn {
opacity: 1;
animation-name: fadeInOpacity;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: 0.6s;
opacity: 1;
animation-name: fadeInOpacity;
animation-iteration-count: 1;
animation-timing-function: ease-in;
animation-duration: 0.6s;
}
@keyframes fadeInOpacity {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
textarea, select, input, button { outline: none; }
textarea,
select,
input,
button {
outline: none;
}
html,
body{
body {
position: relative;
height: 100%;
}
header {
position: relative;
min-height: 100%;
position: relative;
min-height: 100%;
}
.view {
width: 100%;
color: #ffffff;
width: 100%;
color: #ffffff;
}
.top-nav-collapse {
background: #4a507b !important;
background: #4a507b !important;
}
.navbar {
box-shadow: 0 2px 5px 0 rgba(0,0,0,.16), 0 2px 10px 0 rgba(0,0,0,.12);
font-weight: 300;
transition: background-color 0.5s ease;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
font-weight: 300;
transition: background-color 0.5s ease;
}
.navbar .nav-link.nav-icon {
padding: 0 .5rem;
padding: 0 0.5rem;
}
.navbar .nav-link .fa {
font-size: 30px;
line-height: 40px;
font-size: 30px;
line-height: 40px;
}
.navbar:not(.top-nav-collapse) {
background: transparent !important;
@@ -58,141 +63,230 @@ header {
}
.rgba-gradient {
background: -moz-linear-gradient(45deg, rgba(51, 51, 51, 0.82), rgba(13, 17, 198, 0.69) 100%);
background: -webkit-linear-gradient(45deg, rgba(51, 51, 51, 0.82), rgba(13, 17, 198, 0.69) 100%);
background: linear-gradient(to 45deg, rgba(51, 51, 51, 0.82), rgba(13, 17, 198, 0.69) 100%);
background: -moz-linear-gradient(
45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%
);
background: -webkit-linear-gradient(
45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%
);
background: linear-gradient(
to 45deg,
rgba(51, 51, 51, 0.82),
rgba(13, 17, 198, 0.69) 100%
);
}
a:hover {
text-decoration: none;
text-decoration: none;
}
.white_border, .black_border {
border: 1px solid white;
padding: 7px;
background: transparent;
color: white;
min-width: 115px;
.white_border,
.black_border {
border: 1px solid white;
padding: 7px;
background: transparent;
color: white;
min-width: 115px;
}
a.white_border, a.white_border:hover, a.black_border, a.black_border:hover {
padding: 10px;
color: white;
a.white_border,
a.white_border:hover,
a.black_border,
a.black_border:hover {
padding: 10px;
color: white;
}
.white_border:focus, .black_border:focus {
background: #333333;
border: 1px solid #333333;
.white_border:focus,
.black_border:focus {
background: #333333;
border: 1px solid #333333;
}
.white_border::placeholder, .black_border::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
.white_border::placeholder,
.black_border::placeholder {
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: #cccccc;
opacity: 1; /* Firefox */
}
.black_border {
border: 1px solid #333333;
border: 1px solid #333333;
}
a.black_border, a.black_border:hover {
color: #333333;
a.black_border,
a.black_border:hover {
color: #333333;
}
.black_border::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */
.black_border::placeholder {
/* Chrome, Firefox, Opera, Safari 10.1+ */
color: #666666;
opacity: 1; /* Firefox */
}
.active .container {
transition: height 1s ease;
transition: height 1s ease;
}
.add_form {
display: none;
display: none;
}
.active .add_form {
display: block;
display: block;
}
.main {
width: 80%;
margin: auto;
width: 100%;
margin: auto;
height: 100%;
}
.hljs {
background: #FFFFFF;
background: #f8f8f8 !important;
}
pre {
margin: 0;
background: #f8f8f8;
-moz-tab-size: 4;
tab-size: 4;
}
/* for block of numbers */
.hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: right;
color: #d1d1d1;
vertical-align: top;
padding-right: 5px !important;
}
/* for block of code */
.hljs-ln-code {
padding-left: 5px !important;
}
.files {
border: 1px solid #DDDDDD;
border-bottom: initial;
margin: 15px;
border-radius: 5px;
position: sticky;
top: 0;
height: 100%;
overflow: scroll;
z-index: 9;
background: #ecedef;
}
.files a {
display: block;
padding: 9px 9px 9px 30px;
display: block;
padding: 4px 4px 4px 30px;
color: #6e6e6f;
}
.files .blob, .files .tree, .files .parent_folder {
position: relative;
border-bottom: 1px solid #dddddd;
.files .file,
.files .folder {
position: relative;
color: #6e6e6f;
cursor: pointer;
}
.files .blob.active, .files .blob:hover, .files .tree:hover, .files .parent_folder:hover {
background: #f0f0f0;
.files .file.active a,
.files .file a:hover {
background: #c7cbd2;
color: #77777a;
}
.files ul {
list-style: none;
margin: 0;
padding: 0;
}
.files li ul {
padding-left: 10px;
}
.files .folder tree {
display: none;
}
.files .folder.open > tree {
display: block;
}
.files .file::before {
content: "\f15b";
font-family: FontAwesome;
position: absolute;
left: 0px;
width: 7px;
padding: 4px;
padding-left: 8px;
color: #3ba3f8;
}
.files .tree::before, .files .blob::before {
content: "\f07b";
font-family: Font Awesome\ 5 Free;
position: absolute;
left: 0px;
width: 7px;
padding: 9px;
color: #007bff;
.files .folder::before {
content: "\f07b";
}
.files .blob::before {
content: "\f15b";
.files .folder.open::before {
content: "\f07c";
}
.paths {
margin: 15px;
border: 1px solid #dddddd;
border-radius: 5px;
padding: 8px
background-color: #ffffff;
padding: 8px 4px;
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 0;
border-radius: 0;
}
.paths a {
color: #000000;
color: #000000;
}
.paths .path::after {
content: '/';
color: #dddddd;
padding-left: 4px;
}
@media (max-width: 640px) {
.main {
width: 100%;
}
.container-fluid {
padding: 0;
}
.files {
margin: 0;
border-left: 0;
border-right: 0;
border-radius: 0;
}
.main {
width: 100%;
}
.container-fluid {
padding: 0;
}
.files {
margin: 0;
border-left: 0;
border-right: 0;
border-radius: 0;
}
}
.repos {
margin: 0;
padding: 0;
margin: 0;
padding: 0;
}
.repo {
list-style: none;
margin: 0;
padding: 3px 7px;
background: #fdfdfd;
border: 1px solid #ddd;
border-top: 0
list-style: none;
margin: 0;
padding: 3px 7px;
background: #fdfdfd;
border: 1px solid #ddd;
border-top: 0;
}
.repo .options {
float: right;
}
float: right;
}
.main > .container-fluid,
.main > .container-fluid > .row,
.body {
height: 100%;
position: relative;
}
.content {
height: 100%;
overflow: scroll;
padding-top: 42px;
background: #f8f8f8;
}
.file-content {
padding: 4px 7px;
}
.file-error {
font-weight: bold;
font-size: 50px;
text-align: center;
padding-top: 100px;
}

View File

@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.8.0/github-markdown.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet" type="text/css" />
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
</head>
<body>
<p>
The repository that you try to access is empty or is not accessible.
</p>
</body>
</html>

84
public/explore.html Normal file
View File

@@ -0,0 +1,84 @@
<!-- index.html -->
<!DOCTYPE html>
<html lang="en" ng-app="anonymous-github" ng-controller="mainController">
<head>
<meta charset="UTF-8" />
<title>{{title || "anonymous Github"}}</title>
<script>
var base = document.createElement("base");
base.href = `/r/${document.location.pathname.split("/")[2]}/`;
document.getElementsByTagName("head")[0].appendChild(base);
</script>
<!-- CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="/css/font-awesome.min.css" />
<link rel="stylesheet" href="/css/style.css" />
<!-- JS -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-touch.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-sanitize.js"></script>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"
></script>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.8.0/github-markdown.min.css"
rel="stylesheet"
type="text/css"
/>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css"
rel="stylesheet"
type="text/css"
/>
<script src="/script/exploreApp.js"></script>
</head>
<body keypress-events>
<div class="main">
<div class="container-fluid">
<div class="row">
<tree class="files col-3 shadow p-0" file="files" parent=""></tree>
<div class="col-9 p-0 body">
<nav aria-label="breadcrumb">
<ol class="breadcrumb shadow paths">
<li class="breadcrumb-item" ng-repeat="p in paths">{{p}}</li>
</ol>
</nav>
<div class="content" ng-view></div>
</div>
</div>
</div>
</div>
</body>
<script
src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"
crossorigin="anonymous"
></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/marked/1.1.0/marked.min.js"></script>
</html>

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,272 +1,50 @@
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Anonymous GitHub</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<html lang="en" ng-app="anonymous-github" ng-controller="mainController">
<head>
<meta charset="UTF-8" />
<title>{{title || "anonymous Github"}}</title>
<base href="/" />
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<!-- Main navigation -->
<header class="d-flex">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark fixed-top scrolling-navbar ">
<div class="container">
<a class="navbar-brand" href="#">Anonymous GitHub</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarTogglerDemo02" aria-controls="navbarTogglerDemo02" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTogglerDemo02">
<ul class="navbar-nav mr-auto smooth-scroll">
<li class="nav-item">
<a class="nav-link" href="#home">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#usage" data-offset="90">Usage</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#features" data-offset="90">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#about" data-offset="90">About</a>
</li>
<li class="nav-item">
<a class="nav-link nav-icon" href="https://github.com/tdurieux/anonymous_github/" data-offset="30"><i class="fa fa-github" aria-hidden="true"></i></a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Navbar -->
<!-- Full Page Intro -->
<div id="home" class="view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center">
<!-- Content -->
<div class="container px-md-3 px-sm-0">
<!--Grid row-->
<form action="" method="post">
<div class="row fadeIn main-options">
<!--Grid column-->
<div class="col-md-12 mb-4 white-text text-center fadeIn">
<h3 class="display-3 font-weight-bold white-text mb-0 pt-md-5 pt-5">Anonymous GitHub</h3>
<hr class="hr-light my-4 w-75">
<h4 class="subtext-header mt-2 mb-4">Double-blind your repository in 5 min!</h4>
<input id="url_input" type="text" placeholder="Repository URL..." name="githubRepository" class="white_border" value="{{ repo.repository }}" >
or
<a href="github/login" class="white_border">Login to GitHub</a>
or
<button id="edit-button" class="white_border">Edit your existing repository</button>
</div>
<!--Grid column-->
</div>
<div class="row add_form">
{% if not repo %}
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="alert alert-danger" role="alert">
The repository has to be public, <a href="github/login">login to GitHub</a> for private repositories!
</div>
</div>
{% endif %}
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="form-group">
<label for="ignoredTerms">The text to remove from the repository will be replaced by XXX.</label>
<small id="ignoredTermsHelp" class="form-text text-muted">One term per line (case insensitive).</small>
<textarea class="form-control .form-control-lg" name="terms" id="ignoredTerms" rows="5">{{ repo.terms|join('\n') }}</textarea>
</div>
<div class="form-group">
<label for="expiration">Expiration options</label>
<select class="form-control" id="expiration" name="expiration">
<option value="never" {% if repo.expiration=="never" %} selected="selected"{% endif %}>Never</option>
<option value="redirect" {% if repo.expiration=="redirect" %} selected="selected"{% endif %}>Redirect to the GitHub repository</option>
<option value="remove" {% if repo.expiration=="remove" %} selected="selected"{% endif %}>Remove anonymized repository</option>
</select>
</div>
<div class="form-group" id="expiration-date-form" style="display: none;">
<label for="date">Expiration date.</label>
<small class="form-text text-muted">When the anonymous repository.</small>
<input class="form-control .form-control-lg" type="date" name="expiration_date" id="date" value="{% if repo.expiration_date %}{{ repo.expiration_date.date().isoformat() }}{% endif %}" />
</div>
<button id="submit" type="submit" class="white_border">Submit</button>`
<!-- CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<button id="delete" class="white_border" style="color: red;">Delete</button>`
</div>
</div>
</form>
<form class="row edit_form" style="display: none;">
<h2>Edit Anonymized Repository</h2>
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="form-group">
<label>Repository ID</label>
<input class="form-control .form-control-lg" type="text" name="id" id="id"/>
</div>
<div class="form-group">
<label>Repository URL</label>
<input class="form-control .form-control-lg" type="url" name="githubRepository" id="url" value=""/>
</div>
<button type="submit" class="white_border">Submit</button>
<button id="edit_cancel" type="submit" class="white_border">Cancel</button>`
</div>
</form>
<!--Grid row-->
</div>
<!-- Content -->
</div>
<!-- Full Page Intro -->
</header>
<!-- Main navigation -->
<!--Main Layout-->
<main>
<div class="container">
<!--Grid row-->
<div class="row py-5">
<div class="col-md-12">
<h2 id="usage">Usage</h2>
<p class="card-text mb-auto">
<ol>
<li>
Fill the Github repo URL
</li>
<li>
Complete the list of terms that will be anonymized.
<span class="text-muted">The anonymization of the content is done by replacing all occurrences of words in a list by "XXX".The word list typically contains the institution name, author names, logins, etc...</span>
</li>
<li>
Define if you want an expiration date for your anonymized repository.
You can keep it for ever, remove the repository after a specific date or redirect the user to the GitHub repository.
</li>
</ol>
As result, a unique url is created with the content of your repository, for example, <a href="http://anonymous.4open.science/repository/840c8c57-3c32-451e-bf12-0e20be300389/">http://anonymous.4open.science/repository/840c8c57-3c32-451e-bf12-0e20be300389/</a>.
</p>
</div>
<!--Grid column-->
<div class="col-md-12">
<h2 id="features">Features</h2>
</div>
<div class="col-md-6">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Anonymized</h3>
<p class="card-text mb-auto">
Anonymous GitHub Anonymizes the content of the repository but also hide the issues, pull-requests, history. This way you are sure that your repository stays anonymized.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Always up-to-date</h3>
<p class="card-text mb-auto">
Anonymous GitHub follows to track of the changes on your repository and updates the anonymous version automatically.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Fast</h3>
<p class="card-text mb-auto">
With Anonymous GitHub, it requires only 5min to anonymize your repository. No more time lost to create a anonymized version of your repository.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Open-source</h3>
<p class="card-text mb-auto">
Anonymous GitHub is open-source, you can easily deploy it for your conference and simplify the life of your authors.
</p>
</div>
</div>
</div>
<div class="col-md-12">
<h2 id="about">Metrics</h2>
</div>
<div class="col-md-12">
<div class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">2609 Anonymized Repositories</h3>
<p class="card-text mb-auto">
<strong></strong>
</p>
</div>
</div>
</div>
<!--Grid column-->
</div>
<!--Grid row-->
</div>
</main>
<!--Main Layout-->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script>
if ($("#url_input")[0].value != "") {
$('.view').addClass('active')
}
$("#url_input").on('focus', e => {
$('.view').addClass('active')
})
if ($("#expiration")[0].value =='never') {
$("#expiration-date-form").hide();
} else {
$("#expiration-date-form").show();
}
$("#expiration").change(e => {
value = e.currentTarget.value;
if (value == 'never') {
$("#expiration-date-form").hide();
} else {
$("#expiration-date-form").show();
}
})
if ($(document).scrollTop() < 35) {
$('.navbar').removeClass('top-nav-collapse')
} else {
$('.navbar').addClass('top-nav-collapse')
}
$(document).scroll(e => {
if ($(document).scrollTop() < 35) {
$('.navbar').removeClass('top-nav-collapse')
} else {
$('.navbar').addClass('top-nav-collapse')
}
})
$('#edit_cancel').on('click', e=> {
e.preventDefault();
$(".edit_form").hide();
$('.main-options').show();
return false;
})
$('#delete').on('click', e => {
e.preventDefault();
if (confirm("Are you sure you want to delete the repository?")) {
$('#expiration')[0].value = 'remove';
var date = new Date();
date.setDate(date.getDate() - 1);
$('#date')[0].value = date.toISOString().split('T')[0];
$('#submit').click();
}
return false;
})
$('#edit-button').on('click', e=> {
e.preventDefault();
$('.view').removeClass('active')
$(".edit_form").show();
$('.main-options').hide();
$('#id').focus();
return false;
})
</script>
</body>
</html>
<link rel="stylesheet" href="/css/style.css" />
<!-- JS -->
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-touch.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular-route.js"></script>
<link rel="stylesheet" href="/css/font-awesome.min.css" />
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"
></script>
<script src="/script/homeApp.js"></script>
</head>
<body keypress-events ng-view></body>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"
crossorigin="anonymous"
></script>
</html>

View File

@@ -1,66 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>GitHub Anonymous</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark fixed-top scrolling-navbar top-nav-collapse">
<div class="container">
<a class="navbar-brand" href="#">Anonymous GitHub</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarTogglerDemo02" aria-controls="navbarTogglerDemo02" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTogglerDemo02">
<ul class="navbar-nav mr-auto smooth-scroll">
<li class="nav-item">
<a class="nav-link" href="/">Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link nav-icon" href="https://github.com/tdurieux/anonymous_github/" data-offset="30"><i class="fa fa-github" aria-hidden="true"></i></a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container" style="margin-top: 70px">
<div class="list-group">
{% for repo in repos %}
<div class="list-group-item list-group-item-action">
<div class="d-flex mb-12 justify-content-between">
<h5 class="mb-1">{{ repo.full_name }}</h5>
<small>{% if repo.private %} Private {% else %} Public {% endif %}</small>
</div>
{% if repo.description %}
<p class="mb-1">{{ repo.description }}</p>
{% endif %}
<div class="mb-1">
<a class="btn btn-primary anonymize-btn" href="/?githubRepository=https://github.com/{{ repo.full_name }}&id={{ repo.uuid }}">Anonymize</a>
</div>
</div>
{% endfor %}
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script>
$('.anonymize-btn').on('click', e => {
url = e.currentTarget.href;
$.post(url, url.substring(url.indexOf('?') + 1, url.length)+'&terms=', function () {
window.location.href =url;
});
return false;
});
</script>
</body>
</html>

1
public/partials/404.htm Normal file
View File

@@ -0,0 +1 @@
<h1>404</h1>

View File

@@ -0,0 +1,4 @@
<pre ng-if="type == 'text'" ng-bind="content" class="file-content"></pre>
<div ng-if="type == 'html'" ng-bind-html="content" class="file-content"></div>
<pre ng-if="type == 'code'"><code class="hljs" ng-bind="content"></code></pre>
<div ng-if="content == null" class="file-error">Empty file!</div>

352
public/partials/home.htm Normal file
View File

@@ -0,0 +1,352 @@
<!-- Main navigation -->
<header class="d-flex">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark fixed-top scrolling-navbar">
<div class="container">
<a class="navbar-brand" href="#">Anonymous GitHub</a>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarTogglerDemo02"
aria-controls="navbarTogglerDemo02"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTogglerDemo02">
<ul class="navbar-nav mr-auto smooth-scroll">
<li class="nav-item">
<a class="nav-link" href="#home"
>Home
<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#usage" data-offset="90">Usage</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#features" data-offset="90">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#about" data-offset="90">About</a>
</li>
<li class="nav-item">
<a
class="nav-link nav-icon"
href="https://github.com/tdurieux/anonymous_github/"
data-offset="30"
><i class="fa fa-github" aria-hidden="true"></i
></a>
</li>
<li class="nav-link">{{user.username}}</li>
</ul>
</div>
</div>
</nav>
<div
id="home"
class="view rgba-gradient d-flex align-self-stretch justify-content-center align-items-center"
>
<!-- Content -->
<div class="container px-md-3 px-sm-0">
<!--Grid row-->
<form action="" method="post">
<div class="row fadeIn main-options">
<!--Grid column-->
<div class="col-md-12 mb-4 white-text text-center fadeIn">
<h3 class="display-3 font-weight-bold white-text mb-0 pt-md-5 pt-5">
Anonymous GitHub
</h3>
<hr class="hr-light my-4 w-75" />
<h4 class="subtext-header mt-2 mb-4">
Double-blind your repository in 5 min!
</h4>
<input
id="url_input"
type="text"
placeholder="Repository URL..."
name="githubRepository"
class="white_border"
value=""
/>
or
<span ng-if="!user">
<a href="github/login" class="white_border">Login to GitHub</a>
</span>
<span ng-if="user">
<a href="/myrepo" class="white_border">List my repositories</a>
</span>
or
<button id="edit-button" class="white_border">
Edit your existing repository
</button>
</div>
<!--Grid column-->
</div>
<div class="row add_form">
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="alert alert-danger" role="alert">
The repository has to be public,
<a href="github/login">login to GitHub</a> for private
repositories!
</div>
</div>
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="form-group">
<label for="ignoredTerms"
>The text to remove from the repository will be replaced by
XXX.</label
>
<small id="ignoredTermsHelp" class="form-text text-muted"
>One term per line (case insensitive).</small
>
<textarea
class="form-control .form-control-lg"
name="terms"
id="ignoredTerms"
rows="5"
></textarea>
</div>
<div class="form-group">
<label for="expiration">Expiration options</label>
<select class="form-control" id="expiration" name="expiration">
<option value="never">Never</option>
<option value="redirect"
>Redirect to the GitHub repository</option
>
<option value="remove">Remove anonymized repository</option>
</select>
</div>
<div
class="form-group"
id="expiration-date-form"
style="display: none;"
>
<label for="date">Expiration date.</label>
<small class="form-text text-muted"
>When the anonymous repository.</small
>
<input
class="form-control .form-control-lg"
type="date"
name="expiration_date"
id="date"
value=""
/>
</div>
<button id="submit" type="submit" class="white_border">
Submit</button
>`
<button id="delete" class="white_border" style="color: red;">
Delete</button
>`
</div>
</div>
</form>
<form class="row edit_form" style="display: none;">
<h2>Edit Anonymized Repository</h2>
<div class="col-md-12 mb-4 white-text fadeIn">
<div class="form-group">
<label>Repository ID</label>
<input
class="form-control .form-control-lg"
type="text"
name="id"
id="id"
/>
</div>
<div class="form-group">
<label>Repository URL</label>
<input
class="form-control .form-control-lg"
type="url"
name="githubRepository"
id="url"
value=""
/>
</div>
<button type="submit" class="white_border">Submit</button>
<button id="edit_cancel" type="submit" class="white_border">
Cancel</button
>`
</div>
</form>
<!--Grid row-->
</div>
<!-- Content -->
</div>
</header>
<main>
<div class="container">
<!--Grid row-->
<div class="row py-5">
<div class="col-md-12">
<h2 id="usage">Usage</h2>
<div class="card-text mb-auto">
<ol>
<li>
Fill the Github repo URL
</li>
<li>
Complete the list of terms that will be anonymized.
<span class="text-muted"
>The anonymization of the content is done by replacing all
occurrences of words in a list by "XXX".The word list typically
contains the institution name, author names, logins,
etc...</span
>
</li>
<li>
Define if you want an expiration date for your anonymized
repository. You can keep it for ever, remove the repository after
a specific date or redirect the user to the GitHub repository.
</li>
</ol>
As result, a unique url is created with the content of your
repository, for example,
<a
href="http://anonymous.4open.science/repository/840c8c57-3c32-451e-bf12-0e20be300389/"
>http://anonymous.4open.science/repository/840c8c57-3c32-451e-bf12-0e20be300389/</a
>.
</div>
</div>
<!--Grid column-->
<div class="col-md-12">
<h2 id="features">Features</h2>
</div>
<div class="col-md-6">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Anonymized</h3>
<p class="card-text mb-auto">
Anonymous GitHub Anonymizes the content of the repository but also
hide the issues, pull-requests, history. This way you are sure
that your repository stays anonymized.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Always up-to-date</h3>
<p class="card-text mb-auto">
Anonymous GitHub follows to track of the changes on your
repository and updates the anonymous version automatically.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Fast</h3>
<p class="card-text mb-auto">
With Anonymous GitHub, it requires only 5min to anonymize your
repository. No more time lost to create a anonymized version of
your repository.
</p>
</div>
</div>
</div>
<div class="col-md-6">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-0">Open-source</h3>
<p class="card-text mb-auto">
Anonymous GitHub is open-source, you can easily deploy it for your
conference and simplify the life of your authors.
</p>
</div>
</div>
</div>
<div class="col-md-12">
<h2 id="about">Metrics</h2>
</div>
<div class="col-md-12">
<div
class="row no-gutters border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative"
>
<div class="col p-4 d-flex flex-column position-static">
<h3 class="mb-auto text-center">2609 Anonymized Repositories</h3>
<p class="card-text mb-auto">
<strong></strong>
</p>
</div>
</div>
</div>
<!--Grid column-->
</div>
<!--Grid row-->
</div>
</main>
<script>
if ($("#url_input")[0].value != "") {
$(".view").addClass("active");
}
$("#url_input").on("focus", (e) => {
$(".view").addClass("active");
});
if ($("#expiration")[0].value == "never") {
$("#expiration-date-form").hide();
} else {
$("#expiration-date-form").show();
}
$("#expiration").change((e) => {
value = e.currentTarget.value;
if (value == "never") {
$("#expiration-date-form").hide();
} else {
$("#expiration-date-form").show();
}
});
if ($(document).scrollTop() < 35) {
$(".navbar").removeClass("top-nav-collapse");
} else {
$(".navbar").addClass("top-nav-collapse");
}
$(document).scroll((e) => {
if ($(document).scrollTop() < 35) {
$(".navbar").removeClass("top-nav-collapse");
} else {
$(".navbar").addClass("top-nav-collapse");
}
});
$("#edit_cancel").on("click", (e) => {
e.preventDefault();
$(".edit_form").hide();
$(".main-options").show();
return false;
});
$("#delete").on("click", (e) => {
e.preventDefault();
if (confirm("Are you sure you want to delete the repository?")) {
$("#expiration")[0].value = "remove";
var date = new Date();
date.setDate(date.getDate() - 1);
$("#date")[0].value = date.toISOString().split("T")[0];
$("#submit").click();
}
return false;
});
$("#edit-button").on("click", (e) => {
e.preventDefault();
$(".view").removeClass("active");
$(".edit_form").show();
$(".main-options").hide();
$("#id").focus();
return false;
});
</script>

23
public/partials/repos.htm Normal file
View File

@@ -0,0 +1,23 @@
<div class="container" style="margin-top: 70px;">
<div class="list-group">
<div
class="list-group-item list-group-item-action"
ng-repeat="repo in repos"
>
<div class="d-flex mb-12 justify-content-between">
<h5 class="mb-1">{{ repo.full_name }}</h5>
<small>
{{ repo.private? "Private": "Public" }}
</small>
</div>
<p class="mb-1" ng-if="repo.description">{{ repo.description }}</p>
<div class="mb-1">
<a
class="btn btn-primary anonymize-btn"
href="/?githubRepository=https://github.com/{{ repo.full_name }}&id={{ repo.uuid }}"
>Anonymize</a
>
</div>
</div>
</div>
</div>

View File

@@ -1,6 +0,0 @@
{% for item in patch.files %}
<section>
<h3>{{ item.filename }}</h3>
<pre><code class="diff">{{ item.patch }}</code></pre>
</section>
{% endfor %}

View File

@@ -1,52 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/2.8.0/github-markdown.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/github.min.css" rel="stylesheet" type="text/css" />
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet">
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="main">
<div class="container-fluid">
<div class="paths">
{% set current_path = [] %}
{% do current_path.append('/repository/' + current_repository) %}
<span class="path"><a href="{{ current_path|join("/") }}">Root</a></span>
{% for item in path %}
{% do current_path.append(item) %}
<span class="path"><a href="{{ current_path|join("/") }}">{{ item|remove_terms(repository, False) }}</a></span>
{% endfor %}
</div>
<div class="files">
{% for item in files %}
<div class="{{ item.type }} {% if item.path == current_file.name %}active{% endif %}">
<a href="/repository/{{ current_repository }}/{{ path_directory }}{% if path_directory|length > 0%}/{% endif %}{{ item.path }}{% if item.type == 'tree'%}/{% endif %}">
{{ item.path|remove_terms(repository, False) }}
</a>
</div>
{% endfor %}
</div>
{% if current_file %}
{{ current_file|file_render(repository) }}
{% endif %}
</div>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js" crossorigin="anonymous"></script>
<script>hljs.initHighlightingOnLoad();</script>
</body>
</html>

182
public/script/exploreApp.js Normal file
View File

@@ -0,0 +1,182 @@
angular
.module("anonymous-github", ["ngRoute", "ngSanitize"])
.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when("/:path*", {
templateUrl: "/partials/explore.htm",
controller: "exploreController",
title: "Explore",
})
.when("/404", {
templateUrl: "/partials/404.htm",
title: "Not Found!",
});
//.otherwise("/error");
$locationProvider.html5Mode(true);
})
.factory("RecursionHelper", [
"$compile",
function ($compile) {
return {
/**
* Manually compiles the element, fixing the recursion loop.
* @param element
* @param [link] A post-link function, or an object with function(s) registered via pre and post properties.
* @returns An object containing the linking functions.
*/
compile: function (element, link) {
// Normalize the link parameter
if (angular.isFunction(link)) {
link = { post: link };
}
// Break the recursion loop by removing the contents
var contents = element.contents().remove();
var compiledContents;
return {
pre: link && link.pre ? link.pre : null,
/**
* Compiles and re-adds the contents
*/
post: function (scope, element) {
// Compile the contents
if (!compiledContents) {
compiledContents = $compile(contents);
}
// Re-add the compiled contents to the element
compiledContents(scope, function (clone) {
element.append(clone);
});
// Call the post-linking function, if any
if (link && link.post) {
link.post.apply(null, arguments);
}
},
};
},
};
},
])
.directive("tree", [
"RecursionHelper",
function (RecursionHelper) {
return {
restrict: "E",
scope: { file: "=", parent: "@" },
template:
"<ul>" +
'<li class="file" ng-repeat="(name, child) in file" ng-class="{folder: isDir(child), active: isActive(name), open: opens[name]}">' +
"<a href='/r/{{repoId}}/{{parent}}/{{name}}' ng-if='!isDir(child)'>{{name}}</a>" +
"<a ng-click='openFolder(name)' ng-if='isDir(child)'>{{name}}</a>" +
'<tree file="child" parent="{{parent}}/{{name}}" ng-if="isDir(child)""></tree>' +
"</li>" +
"</ul>",
compile: function (element) {
// Use the compile function from the RecursionHelper,
// And return the linking function(s) which it returns
return RecursionHelper.compile(element);
},
controller: function ($scope, $location) {
$scope.repoId = document.location.pathname.split("/")[2];
$scope.opens = {};
$scope.isActive = function (name) {
return $location.path() == $scope.parent + "/" + name;
};
$scope.openFolder = function (folder) {
$scope.opens[folder] = !$scope.opens[folder];
};
$scope.isDir = function (child) {
return !Number.isInteger(child);
};
},
};
},
])
.filter("filterObj", function () {
return function (input, search) {
if (!input) return input;
if (!search) return input;
var result = {};
angular.forEach(input, function (value, key) {
if (search(value)) {
result[key] = value;
}
});
return result;
};
})
.controller("mainController", function ($scope, $http, $location) {
$scope.files = [];
$scope.repoId = document.location.pathname.split("/")[2];
$scope.paths = $location.path().split("/");
$scope.$on("$routeChangeSuccess", function (event, current) {
console.log(event, current);
$scope.paths = $location.path().split("/");
});
function getFiles() {
$http.get(`/api/files/${$scope.repoId}`).then(
(res) => {
$scope.files = res.data;
},
(err) => {
console.log(err);
$scope.files = [];
}
);
}
getFiles();
$http.get(`/api/stat/${$scope.repoId}`).then(
(res) => {
console.log(res.data);
},
(err) => {
console.log(err);
}
);
})
.controller("exploreController", function ($scope, $http, $routeParams) {
console.log("here");
$scope.content = "";
$scope.type = "code";
const textFiles = ["LICENSE", ".txt"];
function getContent(path) {
$http.get(`/api/repository/${$scope.repoId}/${path}`).then(
(res) => {
$scope.content = res.data;
if ($scope.content == "") {
$scope.content = null;
}
$scope.type = "code";
for (let t of textFiles) {
if (path.toLowerCase().indexOf(t.toLowerCase()) > -1) {
$scope.type = "text";
break;
}
}
if ($scope.type == "code" && path.toLowerCase().indexOf(".md") > -1) {
$scope.content = marked(res.data);
$scope.type = "html";
}
setTimeout(() => {
document.querySelectorAll("pre code").forEach((block) => {
hljs.highlightBlock(block);
hljs.lineNumbersBlock(block);
});
}, 50);
},
(err) => {
console.log(err);
$scope.content = err.data;
// $location.url("/" + err.status);
}
);
}
getContent($routeParams.path ? $routeParams.path : "");
});

62
public/script/homeApp.js Normal file
View File

@@ -0,0 +1,62 @@
angular
.module("anonymous-github", ["ngRoute"])
.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when("/myrepo", {
templateUrl: "/partials/repos.htm",
controller: "reposController",
title: "Repo",
})
.when("/", {
templateUrl: "/partials/home.htm",
controller: "homeController",
title: "Home",
})
.when("/404", {
templateUrl: "/partials/404.htm",
title: "Not Found!",
});
//.otherwise("/error");
$locationProvider.html5Mode(true);
})
.filter("filterObj", function () {
return function (input, search) {
if (!input) return input;
if (!search) return input;
var result = {};
angular.forEach(input, function (value, key) {
if (search(value)) {
result[key] = value;
}
});
return result;
};
})
.controller("reposController", function ($scope, $http) {
$scope.repos = [];
$http.get("/api/repos").then((res) => {
$scope.repos = res.data.data;
});
})
.controller("mainController", function ($scope, $http) {
$scope.title = "Main";
$scope.user = null;
$http.get("/api/user").then(
(res) => {
if (res) $scope.user = res.data;
},
() => {
$scope.user = null;
}
);
$scope.$on("$routeChangeSuccess", function (event, current) {
if (current) {
$scope.title = current.title;
}
});
})
.controller("homeController", function ($scope) {
console.log("here");
});