aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2021-05-27 21:46:44 +0200
committerHampusM <hampus@hampusmat.com>2021-05-27 21:46:44 +0200
commitbdfc5d2d092fdce8fb4149ae8acb86b63c14c642 (patch)
tree64fd18534f7f6cfb0f204065c2d40b9bf6146d85 /src
parentdf806dda764ff722742301042ab88b3d91f472c5 (diff)
Implemented cloning in backend
Diffstat (limited to 'src')
-rw-r--r--src/api/git.js74
-rw-r--r--src/app.js47
2 files changed, 113 insertions, 8 deletions
diff --git a/src/api/git.js b/src/api/git.js
index 756136b..3697f98 100644
--- a/src/api/git.js
+++ b/src/api/git.js
@@ -1,6 +1,10 @@
const { formatDistance } = require('date-fns');
const fs = require('fs');
const git = require("nodegit");
+const zlib = require("zlib");
+const { spawn } = require('child_process');
+const whatwg = require("whatwg-url");
+const path = require("path");
function addRepoDirSuffix(repo_name)
{
@@ -244,8 +248,76 @@ async function doesCommitExist(base_dir, repo_name, commit_oid)
}
}
+function connectToGitHTTPBackend(base_dir, req, reply)
+{
+ const url_path = req.url.replace(req.params.repo, req.params.repo + ".git");
+ const repo = req.params.repo + ".git";
+ const repo_path = path.join(base_dir, repo);
+
+ req = req.headers['Content-Encoding'] == 'gzip' ? req.pipe(zlib.createGunzip()) : req;
+
+ const parsed_url = new whatwg.URL(`${req.protocol}://${req.hostname}${url_path}`);
+ const url_path_parts = parsed_url.pathname.split('/');
+
+ let service;
+ let info = false;
+
+ if(/\/info\/refs$/.test(parsed_url.pathname)) {
+ service = parsed_url.searchParams.get("service");
+ info = true;
+ }
+ else {
+ service = url_path_parts[url_path_parts.length-1];
+ }
+
+ const content_type = `application/x-${service}-${info ? "advertisement" : "result"}`;
+
+ if(/\.\/|\.\./.test(parsed_url.pathname)) {
+ reply.header("Content-Type", content_type);
+ reply.code(404).send("Git repository not found!\n");
+ return;
+ }
+
+ if(service !== 'git-upload-pack') {
+ reply.header("Content-Type", content_type);
+ reply.code(403).send("Access denied!\n");
+ return;
+ }
+
+ reply.raw.writeHead(200, { "Content-Type": content_type });
+
+ const spawn_args = [ "--stateless-rpc", repo_path ];
+
+ if(info) {
+ spawn_args.push("--advertise-refs");
+ }
+
+ const git_pack = spawn(service, spawn_args);
+
+ if(info) {
+ const s = '# service=' + service + '\n';
+ const n = (4 + s.length).toString(16);
+ reply.raw.write(Buffer.from((Array(4 - n.length + 1).join('0') + n + s) + '0000'));
+ }
+ else {
+ req.body.on("data", (data) => git_pack.stdin.write(data));
+ req.body.on("close", () => git_pack.stdin.end());
+ }
+
+ git_pack.on("error", (err) => console.log(err))
+ git_pack.stderr.on("data", (stderr) => console.log(stderr));
+
+ git_pack.stdout.on("data", (data) =>
+ {
+ reply.raw.write(data);
+ });
+
+ git_pack.on("close", () => reply.raw.end());
+}
+
module.exports.getLog = getLog;
module.exports.getRepos = getRepos;
module.exports.getRepoFile = getRepoFile;
module.exports.getCommit = getCommit;
-module.exports.doesCommitExist = doesCommitExist; \ No newline at end of file
+module.exports.doesCommitExist = doesCommitExist;
+module.exports.connectToGitHTTPBackend = connectToGitHTTPBackend; \ No newline at end of file
diff --git a/src/app.js b/src/app.js
index 14b8188..57f0ca1 100644
--- a/src/app.js
+++ b/src/app.js
@@ -6,6 +6,7 @@ const fs = require('fs');
const { exit } = require("process");
const path = require("path");
const util = require("./api/util");
+const git = require("./api/git");
const settings = yaml.load(fs.readFileSync(__dirname + "/../settings.yml", 'utf8'));
const settings_keys = Object.keys(settings);
@@ -44,12 +45,9 @@ fastify.setNotFoundHandler({
reply.send("404: Not found");
});
-fastify.route({
- method: "GET",
- path: "/app.html",
- handler: (req, reply) => reply.redirect("/")
-});
+fastify.addContentTypeParser("application/x-git-upload-pack-request", (req, payload, done) => done(null, payload));
+
fastify.register(fastify_static, { root: dist_dir })
fastify.register(api, { prefix: "/api/v1", config: { settings: settings } });
@@ -64,6 +62,12 @@ fastify.route({
}
});
+fastify.route({
+ method: "GET",
+ path: "/app.html",
+ handler: (req, reply) => reply.redirect("/")
+});
+
fastify.register((fastify_repo, opts, done) =>
{
fastify_repo.setNotFoundHandler({
@@ -79,10 +83,10 @@ fastify.register((fastify_repo, opts, done) =>
const repo_verification = await util.verifyRepoName(req.params.repo, settings.base_dir);
if(repo_verification !== true) {
if(repo_verification === "ERR_REPO_REGEX") {
- reply.code(400).send("Error: Unacceptable git repository name!");
+ reply.code(400).send("Unacceptable git repository name!\n");
}
else if(repo_verification === "ERR_REPO_NOT_FOUND") {
- reply.code(404).send("Error: Git repository not found!");
+ reply.code(404).send("Git repository not found!\n");
}
}
});
@@ -113,6 +117,35 @@ fastify.register((fastify_repo, opts, done) =>
}
});
+ fastify_repo.route({
+ method: "GET",
+ path: "/info/refs",
+ handler: (req, reply) =>
+ {
+ if(!req.query.service) {
+ reply.code(403).send("Missing service query parameter\n");
+ return
+ }
+ else if(req.query.service !== "git-upload-pack") {
+ reply.code(403).send("Access denied!\n");
+ return;
+ }
+ else if(Object.keys(req.query).length !== 1) {
+ reply.header("Content-Type", "application/x-git-upload-pack-advertisement");
+ reply.code(403).send("Too many query parameters!\n");
+ return;
+ }
+
+ git.connectToGitHTTPBackend(settings["base_dir"], req, reply);
+ }
+ });
+
+ fastify_repo.route({
+ method: "POST",
+ path: "/git-upload-pack",
+ handler: (req, reply) => git.connectToGitHTTPBackend(settings["base_dir"], req, reply)
+ });
+
done();
}, { prefix: "/:repo" });