Skip to content

Commit

Permalink
remove support for github repos
Browse files Browse the repository at this point in the history
  • Loading branch information
phiresky committed Oct 19, 2015
1 parent 12f162d commit 5fdd0aa
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 101 deletions.
5 changes: 2 additions & 3 deletions README.md
@@ -1,6 +1,6 @@
# encrypted-gist

Store any type of files by uploading them to GitHub as encrypted text files.
Store any type of file by uploading them to GitHub as encrypted text files.

#### Hosted version:

Expand Down Expand Up @@ -48,8 +48,7 @@ Starting with only image uploads, I expanded it to work for any file type.
3. Decrypt file
3. View file according its metadata

It's also possible to use a public github repository instead by supplying a repo name and access token.
This has the advantage of allowing deletions and more efficient storage, but needs setup. *Do not* upload your access token anywhere. Instead set it once with `localStorage.setItem("accessToken", ...)`. Giving others a special URL to allow uploading is then possible via `Upload.getAllowUploadURL()`
This tool also had support for uploading the files to github repositories, but I removed that because it was a hassle.

## Limitations

Expand Down
75 changes: 9 additions & 66 deletions src/github.ts
@@ -1,85 +1,28 @@
declare var fetch: typeof window.fetch;
class Github {
constructor(public access_token: string = null, public apiUrl = `https://api.github.com/`) { }
getRepo(repo: string) {
return new GithubRepo(this, repo);
}
async fetch(path: string, data?: RequestInit, authenticate = false) {
if (!data) data = { headers: new Headers() };
if (!data.headers) data.headers = new Headers();
const h: Headers = data.headers as Headers;
if (authenticate)
if (this.access_token) h.append("Authorization", "token " + this.access_token);
else throw Error(`can't ${data.method} ${path} without access token`);
constructor(public apiUrl = `https://api.github.com/`) { }
async fetch(path: string, data?: RequestInit) {
log("fetching " + (data.method||"") + " " +this.apiUrl + path);
return await fetch(this.apiUrl + path, data);
}
async fetchJSON(path: string, data?: RequestInit, authenticate = false) {
return await (await this.fetch(path, data, authenticate)).json();
async fetchJSON(path: string, data?: RequestInit) {
return await (await this.fetch(path, data)).json();
}
async fetchRaw(path: string, data?: RequestInit, authenticate = false) {
async fetchRaw(path: string, data?: RequestInit) {
const headers = new Headers();
headers.append("Accept", "application/vnd.github.v3.raw");
const r = await this.fetch(path, { headers }, authenticate);
const r = await this.fetch(path, { headers });
return await r.arrayBuffer();
}
async postJSON(path: string, data: any, method = "POST", authenticate = false) {
async postJSON(path: string, data: any, method = "POST") {
const headers = new Headers();
headers.append("Content-Type", "application/json;charset=UTF-8");
return await this.fetchJSON(path, { method, headers, body: JSON.stringify(data) }, authenticate);
return await this.fetchJSON(path, { method, headers, body: JSON.stringify(data) });
}
async createGist(description: string, files: { [filename: string]: { content: string } }, is_public = true, authenticate = false) {
return await this.postJSON("gists", { description, public: is_public, files: files }, "POST", authenticate);
return await this.postJSON("gists", { description, public: is_public, files: files }, "POST");
}
async getGist(id: string) {
return await this.fetchJSON("gists/" + id);
}
}
class GithubRepo {
constructor(public github: Github, public repo: string) { }

async getRefs() {
return await this.github.fetchJSON(this.repo + "/git/refs");
}
async getRef(ref = "heads/master") {
return await this.github.fetchJSON(this.repo + "/git/refs/" + ref);
}
async updateRef(sha: string, ref = "heads/master") {
return await this.github.postJSON(this.repo + "/git/refs/" + ref, { sha }, "PATCH");
}
async getHead() {
const branch = await this.getRef();
return branch.object.sha as string;
}
async getTree(sha: string, recursive = false) {
return await this.github.fetchJSON(this.repo + "/git/trees/" + sha + (recursive ? "?recursive=1" : ""));
}
async createBlob(data: ArrayBuffer) {
const resp = await this.github.postJSON(this.repo + "/git/blobs", {
encoding: "base64", content: base64.encode(data, false, true)
});
return resp.sha;
}
async getBlob(sha) {
return await this.github.fetchRaw(this.repo + "/git/blobs/" + sha);
}
async createTree(base_tree: string, path: string, sha: string) {
const resp = await this.github.postJSON(this.repo + "/git/trees", {
base_tree, tree: [{ path, mode: "100644", type: "blob", sha }]
});
return resp;
}
async createCommit(parent: string, tree: string, message: string) {
return await this.github.postJSON(this.repo + "/git/commits", { message, parents: [parent], tree });
}
async pushFileToMaster(path: string, content: Uint8Array, commitMessage: string) {
const head = await this.getHead();
const newtree = await this.createTree(head, path, await this.createBlob(content.buffer));
const newsha = newtree.sha;
const files = newtree.tree;
const filesha = files.filter(file => file.path == path)[0].sha;
const commit = await this.createCommit(head, newsha, commitMessage);
await this.updateRef(commit.sha);
return filesha;
}
}
42 changes: 10 additions & 32 deletions src/main.ts
@@ -1,7 +1,4 @@
const repoName = null; // "phire-store/testing";

let github = new Github(typeof localStorage != "undefined" && localStorage.getItem("accessToken"));
let repo = github.getRepo(repoName);
let github = new Github();

const $ = s => [].slice.call(document.querySelectorAll(s)) as HTMLElement[];
function log(info: any) {
Expand Down Expand Up @@ -39,43 +36,33 @@ interface UploadMetadata {
name: string, type: string
}
module Upload {
type UploadMethod = (data: Uint8Array) => Promise<string>;
const gistUploadMethod: UploadMethod = async function(d) {
async function uploadToGist(d) {
const f = Util.randomString(1, 16);
if (d.byteLength >= 1000 * 3 / 4 * 1000) console.warn("Data should be < 700 kB to avoid calling api twice");
if (d.byteLength >= 2e6) throw "Data must be < 2 MB"; // more should be possible
return (await github.createGist(Util.randomString(0, 10), {
[f]: { content: base64.encode(d.buffer, true, false) }
})).id;
}
const repoUploadMethod = (d) => repo.pushFileToMaster(Util.randomString(1, 16), d, "add");

let uploadMethod: UploadMethod = repoName ? repoUploadMethod : gistUploadMethod;

let downloadMethod: (sha: string) => Promise<ArrayBuffer>;
if (repoName) downloadMethod = (sha) => repo.getBlob(sha);
else downloadMethod = async function(sha) {
async function downloadFromGist(sha) {
const gist = await github.getGist(sha);
const file = gist.files[Object.keys(gist.files)[0]];
if (file.truncated) {
return base64.decode(await (await fetch(file.raw_url)).text(), true);
} else
return base64.decode(file.content, true);
}
export async function getAllowUploadURL() {
location.hash = "#allowupload!" + github.access_token;
}
export async function uploadEncrypted(meta: UploadMetadata, raw_data:Uint8Array) {
log("Uploading...");
const nullByte = new Uint8Array(1);
const inputData = await Util.joinBuffers(new TextEncoder().encode(JSON.stringify(meta)), nullByte, raw_data);
const {data, key} = await SimpleCrypto.encrypt(inputData);
// TODO: don't copy all data twice (via Util.joinBuffers)
return { data, key, sha: await uploadMethod(await Util.joinBuffers(...data)) };
return { data, key, sha: await uploadToGist(await Util.joinBuffers(...data)) };
}
export async function downloadEncrypted(sha: string, key: string) {
sha = Util.arrToHex(new Uint8Array(base64.decode(sha, true)));
const buf = await SimpleCrypto.decrypt(new Uint8Array(await downloadMethod(sha)), key);
const buf = await SimpleCrypto.decrypt(new Uint8Array(await downloadFromGist(sha)), key);
const sep = new Uint8Array(buf).indexOf(0);
const meta = new TextDecoder().decode(new Uint8Array(buf, 0, sep));
log("Decoded metadata: " + meta);
Expand Down Expand Up @@ -202,21 +189,12 @@ module GUI {
if (typeof process !== "undefined") {
initializeNode();
} else if (location.hash) {
if (location.hash.startsWith("#allowupload!")) {
const token = location.hash.substr(1).split("!")[1];
localStorage.setItem("accessToken", token);
location.hash = "";
location.reload();
} else {
const [filename, key] = location.hash.substr(1).split("!");
log("Loading...");
container.innerHTML = "<h3>Loading...</h3>";
Upload.downloadEncrypted(filename, key).then(displayFile);
}
} else if (github.access_token || !repoName) {
initializeUploader();
const [filename, key] = location.hash.substr(1).split("!");
log("Loading...");
container.innerHTML = "<h3>Loading...</h3>";
Upload.downloadEncrypted(filename, key).then(displayFile);
} else {
log("No image given and upload key missing");
initializeUploader();
}
});
}

0 comments on commit 5fdd0aa

Please sign in to comment.