"use strict";
/*
api.github.com is very nice to use to get info, but

"Unauthenticated clients can make **60 requests per hour**."
https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api

So it's completely useless for our purposes without authentication.

"When authenticating, you should see your rate limit bumped to 5,000 requests an hour,"

which is also hopefully sufficient, but worrisome.

Thoughts:

- Since all rendering could be done client side, I could actually have
the client browser grab content instead of the server, then render there to
massively reduce api load, although even users could quickly hit "60 requests
per hour", so the api still wouldn't help.

- If we do hit the 5K/hour limit, maybe we can use more than one api key?

- Upgrading to enterprise doesn't increase this much.

- We could switch to mirroring and cloning files locally, and that might
  work around this problem in practice, but be a lot of work. We'll see.


Get at https://github.com/settings/tokens
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (_) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.fileInGist = exports.repos = exports.defaultBranch = exports.contents = exports.api = exports.rawText = exports.RAW_MAX_SIZE_BYTES = void 0;
var node_fetch_1 = __importStar(require("node-fetch"));
var base_64_1 = require("base-64");
var path_1 = require("path");
var pool_1 = __importDefault(require("@cocalc/database/pool"));
// We don't allow just fetching content that is arbitrarily large, since that could cause
// the server to just run out of memory.  However, we want this to reasonably big.
exports.RAW_MAX_SIZE_BYTES = 10000000; // 10MB
// TODO: we will also have a raw blob or stream or something for serving up images, etc.,
function rawText(githubOrg, githubRepo, segments) {
    return __awaiter(this, void 0, void 0, function () {
        var url;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    url = rawURL(githubOrg, githubRepo, segments);
                    return [4 /*yield*/, (0, node_fetch_1.default)(url, { size: exports.RAW_MAX_SIZE_BYTES })];
                case 1: return [4 /*yield*/, (_a.sent()).text()];
                case 2: 
                //console.log("raw:", { url });
                return [2 /*return*/, _a.sent()];
            }
        });
    });
}
exports.rawText = rawText;
function rawURL(githubOrg, githubRepo, segments) {
    return "https://raw.githubusercontent.com/".concat(githubOrg, "/").concat(githubRepo, "/").concat(path_1.join.apply(void 0, __spreadArray([], __read(segments.slice(1)), false)));
}
function credentials() {
    return __awaiter(this, void 0, void 0, function () {
        var pool, rows, result, rows_1, rows_1_1, row;
        var e_1, _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    pool = (0, pool_1.default)("long");
                    return [4 /*yield*/, pool.query("SELECT name, value FROM server_settings WHERE name='github_username' OR name='github_token'")];
                case 1:
                    rows = (_b.sent()).rows;
                    result = {};
                    try {
                        for (rows_1 = __values(rows), rows_1_1 = rows_1.next(); !rows_1_1.done; rows_1_1 = rows_1.next()) {
                            row = rows_1_1.value;
                            result[row.name] = row.value;
                        }
                    }
                    catch (e_1_1) { e_1 = { error: e_1_1 }; }
                    finally {
                        try {
                            if (rows_1_1 && !rows_1_1.done && (_a = rows_1.return)) _a.call(rows_1);
                        }
                        finally { if (e_1) throw e_1.error; }
                    }
                    return [2 /*return*/, result];
            }
        });
    });
}
function api(path) {
    return __awaiter(this, void 0, void 0, function () {
        var url, options, _a, github_username, github_token, response, data;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0:
                    url = "https://api.github.com/".concat(path);
                    options = {};
                    return [4 /*yield*/, credentials()];
                case 1:
                    _a = _b.sent(), github_username = _a.github_username, github_token = _a.github_token;
                    if (github_username && github_token) {
                        options.headers = new node_fetch_1.Headers({
                            Authorization: "Basic " + (0, base_64_1.encode)("".concat(github_username, ":").concat(github_token)),
                            "Content-Type": "application/json",
                        });
                    }
                    return [4 /*yield*/, (0, node_fetch_1.default)(url, options)];
                case 2:
                    response = _b.sent();
                    return [4 /*yield*/, response.json()];
                case 3:
                    data = _b.sent();
                    //console.log({ url, response });
                    if (data.message) {
                        throw Error("".concat(data.message, "  (see ").concat(data.documentation_url, ")"));
                    }
                    return [2 /*return*/, data];
            }
        });
    });
}
exports.api = api;
// Use the github api to get the contents of a path on github.
// We are planning to use this just to get directory listings,
// since individual files have their content base64 encoded, etc.,
// and that has to be much slower than just grabbing the
// file form raw (and also only works up to 1MB according to
// github docs).
// How to do auth + fetch with node: https://stackoverflow.com/questions/43842793/basic-authentication-with-fetch
function contents(githubOrg, githubRepo, segments) {
    return __awaiter(this, void 0, void 0, function () {
        var ref, path, result;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    if (segments.length == 0) {
                        ref = ""; // the default;
                        path = ""; // root
                    }
                    else {
                        // tree/[ref]/[path ...]
                        ref = segments[1];
                        path = path_1.join.apply(void 0, __spreadArray([], __read(segments.slice(2)), false));
                    }
                    return [4 /*yield*/, api("repos/".concat(githubOrg, "/").concat(githubRepo, "/contents/").concat(path).concat(ref ? "?ref=" + ref : ""))];
                case 1:
                    result = _a.sent();
                    if (result.name != null) {
                        throw Error("only use contents to get directory listing, not to get file contents");
                    }
                    return [2 /*return*/, result];
            }
        });
    });
}
exports.contents = contents;
function defaultBranch(githubOrg, githubRepo) {
    return __awaiter(this, void 0, void 0, function () {
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, api("repos/".concat(githubOrg, "/").concat(githubRepo))];
                case 1: return [2 /*return*/, (_a.sent()).default_branch];
            }
        });
    });
}
exports.defaultBranch = defaultBranch;
// Return all the repositories in a GitHub organization or user:
function repos(githubOrg) {
    return __awaiter(this, void 0, void 0, function () {
        var result, err_1;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0:
                    _a.trys.push([0, 2, , 4]);
                    return [4 /*yield*/, api("orgs/".concat(githubOrg, "/repos"))];
                case 1:
                    result = _a.sent();
                    return [3 /*break*/, 4];
                case 2:
                    err_1 = _a.sent();
                    return [4 /*yield*/, api("users/".concat(githubOrg, "/repos"))];
                case 3:
                    result = _a.sent();
                    return [3 /*break*/, 4];
                case 4: return [2 /*return*/, result
                        .filter(function (repo) { return !repo.private; })
                        .map(function (repo) {
                        return {
                            isdir: true,
                            name: repo.name,
                            mtime: new Date(repo.updated_at).valueOf(),
                            url: "/github/".concat(githubOrg, "/").concat(repo.name),
                        };
                    })];
            }
        });
    });
}
exports.repos = repos;
function fileInGist(gistId) {
    return __awaiter(this, void 0, void 0, function () {
        var info, filename;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, api("gists/".concat(gistId))];
                case 1:
                    info = _a.sent();
                    for (filename in info.files) {
                        return [2 /*return*/, filename];
                    }
                    throw Error("no files in the gist");
            }
        });
    });
}
exports.fileInGist = fileInGist;
//# sourceMappingURL=api.js.map