--- /dev/null
+<?php
+
+namespace app;
+
+use merry\Config;
+use pharext\Cli\Args;
+
+class Cli
+{
+ /**
+ * @var \merry\Config
+ */
+ private $config;
+
+ function __construct(Config $config, Args $args) {
+ $this->config = $config;
+ $this->args = $args;
+ }
+
+ function __invoke($argc, array $argv) {
+ $prog = array_shift($argv);
+ foreach ($this->args->parse(--$argc, $argv) as $error) {
+ $errs[] = $error;
+ }
+
+ if ($this->args["help"] || !array_filter($this->args->toArray())) {
+ $this->help($prog);
+ exit;
+ }
+ if (!empty($errs)) {
+ foreach ($errs as $err) {
+ fprintf(STDERR, "ERROR: %s\n", $err);
+ exit(-1);
+ }
+ }
+
+ if ($this->args["ngrok"]) {
+ system($this->config->ngrok->command . " ". implode(" ", array_map("escapeshellarg", [
+ "http",
+ "--subdomain=pharext",
+ "--authtoken",
+ $this->config->ngrok->auth->token,
+ "--auth",
+ $this->config->ngrok->auth->user .":". $this->config->ngrok->auth->pass,
+ "80"
+ ])));
+ }
+ }
+
+ function getConfig() {
+ return $this->config;
+ }
+
+ /**
+ * Output command line help message
+ * @param string $prog
+ */
+ public function help($prog) {
+ printf("Usage:\n\n \$ %s", $prog);
+
+ $flags = [];
+ $required = [];
+ $optional = [];
+ foreach ($this->args->getSpec() as $spec) {
+ if ($spec[3] & Args::REQARG) {
+ if ($spec[3] & Args::REQUIRED) {
+ $required[] = $spec;
+ } else {
+ $optional[] = $spec;
+ }
+ } else {
+ $flags[] = $spec;
+ }
+ }
+
+ if ($flags) {
+ printf(" [-%s]", implode("", array_column($flags, 0)));
+ }
+ foreach ($required as $req) {
+ printf(" -%s <arg>", $req[0]);
+ }
+ if ($optional) {
+ printf(" [-%s <arg>]", implode("|-", array_column($optional, 0)));
+ }
+ printf("\n\n");
+ $spc = $this->args->getSpec();
+ $max = $spc ? max(array_map("strlen", array_column($spc, 1))) : 0;
+ $max += $max % 8 + 2;
+ foreach ($spc as $spec) {
+ if (isset($spec[0])) {
+ printf(" -%s|", $spec[0]);
+ } else {
+ printf(" ");
+ }
+ printf("--%s ", $spec[1]);
+ if ($spec[3] & Args::REQARG) {
+ printf("<arg> ");
+ } elseif ($spec[3] & Args::OPTARG) {
+ printf("[<arg>]");
+ } else {
+ printf(" ");
+ }
+ printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]);
+ if ($spec[3] & Args::REQUIRED) {
+ printf(" (REQUIRED)");
+ }
+ if (isset($spec[4])) {
+ printf(" [%s]", $spec[4]);
+ }
+ printf("\n");
+ }
+ printf("\n");
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\API;
+
+use app\Controller;
+use app\Github\API;
+use app\Web;
+
+class Hook implements Controller
+{
+ private $app;
+ private $github;
+
+ function __construct(Web $app, API $github) {
+ $this->app = $app;
+ $this->github = $github;
+ }
+
+ function __invoke(array $args = []) {
+ $request = $this->app->getRequest();
+ $response = $this->app->getResponse();
+
+ if (!($sig = $request->getHeader("X-Github-Signature")) || !($evt = $request->getHeader("X-Github-Event"))) {
+ $response->setResponseCode(400);
+ } elseif ($sig !== hash_hmac("sha1", $request->getBody(), $this->app->getConfig()->github->client->secret)) {
+ $response->setResponseCode(403);
+ } elseif ($evt === "ping") {
+ $response->setReponseStatus("PONG");
+ } elseif ($evt !== "push") {
+ $this->app->getResponse()->setResponseCode(204);
+ } else {
+ $push = json_decode($request->getBody());
+ }
+ }
+}
} catch (\app\Github\Exception $exception) {
$this->app->getView()->addData(compact("exception", "owner", "name"));
}
+
+ if (($modal = $this->app->getRequest()->getQuery("modal"))) {
+ $this->app->getView()->addData(compact("modal") + [
+ "action" => $this->app->getRequest()->getQuery($modal)
+ ]);
+ }
+
$this->app->display("github/repo");
}
}
class Hook extends Github
{
function __invoke(array $args = null) {
- switch ($args["action"]) {
- case "add":
- $this->addHook($args["owner"], $args["name"]);
- break;
-
- case "del":
- $this->delHook($args["owner"], $args["name"]);
- break;
+ if ($this->checkToken()) {
+ if ($this->app->getRequest()->getRequestMethod() != "POST") {
+ // user had to re-authenticate, and was redirected here
+ $this->app->redirect($this->app->getBaseUrl()->mod([
+ "path" => "./github/repo/" . $args["owner"] ."/". $args["name"],
+ "query" => "modal=hook&hook=" . $args["action"]
+ ]));
+ } else switch ($args["action"]) {
+ case "add":
+ $this->addHook($args["owner"], $args["name"]);
+ break;
+
+ case "del":
+ $this->delHook($args["owner"], $args["name"]);
+ break;
+ }
}
}
"config" => [
"url" => $this->config->hook->url,
"content_type" => $this->config->hook->content_type,
+ "insecure_ssl" => $this->config->hook->insecure_ssl,
"secret" => $this->config->client->secret, // FIXME: bad idea?
- "insecure_ssl" => false,
]
]));
return $request;
namespace app;
-use http\Url;
use http\Env\Request;
use http\Env\Response;
--- /dev/null
+<?php
+
+namespace app;
+
+use pharext\Cli\Args;
+
+require_once __DIR__."/config.php";
+require_once __DIR__."/github.php";
+
+/* @var $injector \Auryn\Injector */
+
+$injector->share(Cli::class);
+
+$injector->share(Args::class)
+ ->define(Args::class, [
+ ":spec" => [
+ [null, "ngrok", "Run ngrok", Args::SINGLE],
+ ["h", "help", "Show this help", Args::HALT],
+ ]
+ ]);
[production]
github.hook.url = https://pharext.org/github/hook
+github.hook.content_type = json
+github.hook.insecure_ssl = 0
github.fetch.repos.per_page = 10
github.fetch.hooks.per_page = 100
session.cache_expire = 0
[localhost : production]
+
+github.hook.url = https://pharext.ngrok.io/src/pharext.org.git/public/github/hook
+github.hook.insecure_ssl = 1
+
+ngrok.command = ngrok
\ No newline at end of file
[Github\Callback]
GET[] = /github/callback
+[Github\Hook]
+POST[] = /github/hook
+
[Github\Index]
GET[] = /github
[Github\Repo\Hook]
POST[] = /github/repo/{owner}/{name}/hook/{action}
+; if the user has to re-authenticate, we'll receive a GET because of a redirect
+GET[] = /github/repo/{owner}/{name}/hook/{action}
[Github\Signin]
GET[] = /github/signin
<?= $this->section("content") ?>
</div>
+ <?php if (isset($modal)) : ?>
+ <?= $this->fetch("modal/$modal") ?>
+ <?php endif; ?>
<footer class="footer">
<div class="container">
<p>
</footer>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
-
+ <script>$(".modal").modal("show")</script>
<?php if (!empty($scripts)) : ?>
<?php foreach ($scripts as $script) : ?>
<?php if ($script instanceof \http\Url) : ?>
--- /dev/null
+<div class="modal fade">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title">Complete Webhook Action</h4>
+ </div>
+ <div class="modal-body">
+ <p>You tried to <?= $action == "del" ? "remove" : "add" ?> the PHARext webhook,
+ but you had to re-authenticate.</p>
+ <p>Please complete the desired action by submitting this form.</p>
+ </div>
+ <div class="modal-footer">
+ <?php if ($action === "add") : ?>
+ <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/add") ?>">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="submit" class="btn btn-primary">Enable Hook</button>
+ </form>
+ <?php elseif ($action === "del") : ?>
+ <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/del") ?>">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+ <button type="submit" class="btn btn-primary">Disable Hook</button>
+ </form>
+ <?php endif; ?>
+ </div>
+ </div><!-- /.modal-content -->
+ </div><!-- /.modal-dialog -->
+</div><!-- /.modal -->
\ No newline at end of file
--- /dev/null
+#!/usr/bin/php
+<?php
+
+namespace app;
+
+$bootstrap = require __DIR__."/../app/bootstrap.php";
+$injector = $bootstrap(["config", "github", "cli"]);
+$injector->execute(Cli::class, [$argc, $argv]);
}],
"autoload": {
"psr-0": {
- "app": ""
+ "app": "",
+ "pharext": "vendor/m6w6/pharext/src"
}
},
"require": {