vendor
nbproject
app/credentials.ini
+config/credentials.ini
namespace app;
-use merry\Config;
use pharext\Cli\Args;
class Cli
{
/**
- * @var \merry\Config
+ * @var \app\Config
*/
private $config;
+ /**
+ * @var \pharext\Cli\Args
+ */
+ private $args;
+
function __construct(Config $config, Args $args) {
$this->config = $config;
$this->args = $args;
}
- function __invoke($argc, array $argv) {
+ function __invoke($argc, array $argv, callable $exec) {
$prog = array_shift($argv);
foreach ($this->args->parse(--$argc, $argv) as $error) {
$errs[] = $error;
}
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"
- ])));
+ $exec(Cli\Ngrok::class);
+ }
+ if ($this->args["initdb"]) {
+ $exec(Cli\Initdb::class);
+ }
+ if ($this->args["gen-models"]) {
+ $exec(Cli\GenModels::class);
}
}
--- /dev/null
+<?php
+
+namespace app\Cli;
+
+use app\Controller;
+use app\Model;
+use pq\Connection;
+
+class GenModels implements Controller
+{
+ private $pq;
+
+ function __construct(Connection $pq) {
+ $this->pq = $pq;
+ }
+
+ function __invoke(array $args = null) {
+ $tables = $this->pq->exec("SELECT tablename FROM pg_tables WHERE schemaname='public'");
+ /* @var $tables \pq\Result */
+ foreach ($tables->fetchAllCols("tablename") as $table) {
+ $this->genModel($table);
+ }
+ }
+
+ function genModel($entity) {
+ $title = ucwords($entity);
+ $single = substr($title, -3) == "ies"
+ ? substr($title, 0, -3)."y"
+ : rtrim($title, "s");
+ $this->genTable($entity, $title, $single."Collection");
+ $this->genRowset($single."Collection", $single);
+ $this->genRow($single);
+ }
+
+ function genTable($name, $class, $collection) {
+ $ns = explode("\\", $class);
+
+ if (count($ns) > 1) {
+ $class = array_pop($ns);
+ $dir = implode("/", $ns);
+ $ns = "\\".implode("\\", $ns);
+ } else {
+ $ns = "";
+ $dir = "";
+ }
+
+ $file = __DIR__."/../Model/$dir/$class.php";
+
+ if (!file_exists($file)) {
+ file_put_contents($file, <<<EOD
+<?php
+
+namespace app\\Model$ns;
+
+use pq\\Gateway\\Table;
+
+class $class extends Table
+{
+ protected \$name = "$name";
+ protected \$rowset = "app\\\\Model$ns\\\\$collection";
+}
+
+EOD
+ );
+ }
+ }
+
+ function genRowset($class, $row) {
+ $ns = explode("\\", $class);
+
+ if (count($ns) > 1) {
+ $class = array_pop($ns);
+ $dir = implode("/", $ns);
+ $ns = "\\".implode("\\", $ns);
+ } else {
+ $ns = "";
+ $dir = "";
+ }
+
+ $file = __DIR__."/../Model/$dir/$class.php";
+
+ if (!file_exists($file)) {
+ file_put_contents($file, <<<EOD
+<?php
+
+namespace app\\Model$ns;
+
+use pq\\Gateway\\Rowset;
+
+class $class extends Rowset
+{
+ protected \$row = "app\\\\Model$ns\\\\$row";
+}
+
+EOD
+ );
+ }
+ }
+ function genRow($class) {
+ $ns = explode("\\", $class);
+
+ if (count($ns) > 1) {
+ $class = array_pop($ns);
+ $dir = implode("/", $ns);
+ $ns = "\\".implode("\\", $ns);
+ } else {
+ $ns = "";
+ $dir = "";
+ }
+
+ $file = __DIR__."/../Model/$dir/$class.php";
+
+ if (!file_exists($file)) {
+ file_put_contents($file, <<<EOD
+<?php
+
+namespace app\\Model$ns;
+
+use pq\\Gateway\\Row;
+
+class $class extends Row
+{
+}
+
+EOD
+ );
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace app\Cli;
+
+use app\Controller;
+use pq\Connection;
+
+class Initdb implements Controller
+{
+ private $pq;
+
+ function __construct(Connection $pq) {
+ $this->pq = $pq;
+ }
+
+ function __invoke(array $args = null) {
+ foreach (glob(__DIR__."/../../config/sql/*.sql") as $sql) {
+ $xa = $this->pq->startTransaction();
+ $this->pq->exec(file_get_contents($sql));
+ $xa->commit();
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Cli;
+
+use app\Config;
+use app\Controller;
+
+class Ngrok implements Controller
+{
+ private $config;
+
+ function __construct(Config $config) {
+ $this->config = $config;
+ }
+
+ function __invoke(array $args = null) {
+ system($this->config->ngrok->command . " ". implode(" ", array_map("escapeshellarg", [
+ "http",
+ "--subdomain=pharext",
+ "--log=stderr",
+ "--authtoken",
+ $this->config->ngrok->auth->token,
+ "--auth",
+ $this->config->ngrok->auth->user .":". $this->config->ngrok->auth->pass,
+ "80"
+ ])));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace app;
+
+use merry\Config as Container;
+
+class Config extends Container
+{
+ function addConfig(Config $config) {
+ foreach ($config as $sub => $conf) {
+ /* one level down should suffice, i.e. $config->github->client = {id,secret,scope} */
+ if ($conf instanceof Config) {
+ foreach ($conf as $key => $val) {
+ $this->$sub->$key = $val;
+ }
+ } else {
+ $this->$sub = $conf;
+ }
+ }
+ }
+}
* @var \app\Session
*/
protected $session;
-
+
function __construct(Web $app, API $github, Session $session) {
$this->app = $app;
$this->github = $github;
namespace app\Controller\Github;
use app\Controller\Github;
+use app\Github\API;
+use app\Github\Exception;
+use app\Model\Accounts;
+use app\Session;
+use app\Web;
class Callback extends Github
{
+
+ /**
+ * @var Accounts
+ */
+ private $accounts;
+
+ function __construct(Web $app, API $github, Session $session, Accounts $accounts) {
+ parent::__construct($app, $github, $session);
+ $this->accounts = $accounts;
+ }
+
function __invoke(array $args = null) {
if ($this->app->getRequest()->getQuery("error")) {
$this->app->getView()->addData([
$this->github->fetchToken(
$this->app->getRequest()->getQuery("code"),
$this->app->getRequest()->getQuery("state"),
- function($json) {
- $this->github->setToken($json->access_token);
- $this->github->fetchUser(function($user) {
- $this->session->github = $user;
- });
+ function($token) {
+ $this->github->setToken($token->access_token);
+ $this->github->fetchUser($this->createUserCallback($token));
})->send();
if (isset($this->session->returnto)) {
- $this->app->redirect($this->session->returnto);
+ $returnto = $this->session->returnto;
+ unset($this->session->returnto);
+ $this->app->redirect($returnto);
} else {
$this->app->redirect(
$this->app->getBaseUrl()->mod("./github"));
}
- } catch (\app\Github\Exception $exception) {
+ } catch (Exception $exception) {
$this->app->getView()->addData(compact("exception"));
}
}
$this->app->display("github/callback");
}
+
+ function createUserCallback($token) {
+ return function($user) use($token) {
+ $tx = $this->accounts->getConnection()->startTransaction();
+
+ if (!($account = $this->accounts->byOAuth("github", $token->access_token, $user->login))) {
+ $account = $this->accounts->createOAuthAccount("github", $token->access_token, $user->login);
+ }
+ $account->updateToken("github", $token->access_token, $token);
+ $owner = $account->updateOwner("github", $user->login, $user);
+
+ $tx->commit();
+
+ $this->session->account = $account->account->get();
+ $this->session->github = (object) $owner->export();
+ };
+ }
}
<?php
-namespace app\Github\API;
+namespace app\Controller\Github;
use app\Controller;
use app\Github\API;
+use app\Model\Accounts;
use app\Web;
+use http\Params;
+use pharext\Task;
+use pharext\SourceDir\Git;
+
+require_once __DIR__."/../../../vendor/m6w6/pharext/src/pharext/Version.php";
class Hook implements Controller
{
private $app;
private $github;
+ private $accounts;
- function __construct(Web $app, API $github) {
+ function __construct(Web $app, API $github, Accounts $accounts) {
$this->app = $app;
$this->github = $github;
+ $this->accounts = $accounts;
}
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"))) {
+ if (!($sig = $request->getHeader("X-Hub-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);
+ $response->setContentType("message/http");
+ $response->getBody()->append($request);
} else {
- $push = json_decode($request->getBody());
+ $key = $this->github->getConfig()->client->secret;
+ foreach ((new Params($sig))->params as $algo => $mac) {
+ if ($mac["value"] !== hash_hmac($algo, $request->getBody(), $key)) {
+ $response->setResponseCode(403);
+ $response->getBody()->append("Invalid signature");
+ return;
+ }
+ }
+ }
+
+ switch ($evt) {
+ default:
+ $response->setResponseCode(202);
+ $response->getBody()->append("Not a configured event");
+ break;
+ case "ping";
+ $response->setResponseCode(204);
+ $response->setResponseStatus("PONG");
+ break;
+ case "create":
+ case "release":
+ if (($json = json_decode($request->getBody()))) {
+ $this->$evt($json);
+ } else {
+ $response->setResponseCode(415);
+ $response->setContentType($request->getHeader("Content-Type"));
+ $response->getBody()->append($request->getBody());
+ }
+ break;
+ }
+ }
+
+ function release($release) {
+ if ($release->action !== "published") {
+ $response = $this->app->getResponse();
+
+ $response->setResponseCode(202);
+ $response->getBody()->append("Not published");
+ return;
}
+
+ $this->uploadAssetForRelease($release->release, $release->repository);
+ }
+
+ private function uploadAssetForRelease($release, $repo) {
+ $this->setTokenForUser($repo->owner->login);
+ $asset = $this->createReleaseAsset($release, $repo);
+ $this->github->createReleaseAsset($release->upload_url, $asset, "application/phar", function($json) {
+ $response = $this->app->getResponse();
+ $response->setResponseCode(201);
+ $response->setHeader("Location", $json->url);
+ })->send();
+ }
+
+ private function createReleaseAsset($release, $repo) {
+ define("STDERR", fopen("/var/log/apache2/php_errors.log", "a"));
+ $source = (new Task\GitClone($repo->clone_url, $release->tag_name))->run();
+ $iterator = new Git($source);
+ $meta = [
+ "header" => sprintf("pharext v%s (c) Michael Wallner <mike@php.net>", \pharext\VERSION),
+ "version" => \pharext\VERSION,
+ "date" => date("Y-m-d"),
+ "name" => $repo->name,
+ "release" => $release->tag_name,
+ "license" => @file_get_contents(current(glob($iterator->getBaseDir()."/LICENSE*"))),
+ "stub" => "pharext_installer.php",
+ "type" => false ? "zend_extension" : "extension",
+ ];
+ $file = (new Task\PharBuild($iterator, $meta))->run();
+ return $file;
+ }
+
+ function create($create) {
+ if ($create->ref_type !== "tag") {
+ $response = $this->app->getResponse();
+
+ $response->setResponseCode(202);
+ $response->getBody()->append("Not a tag");
+ return;
+ }
+
+ $this->createReleaseFromTag($create->ref, $create->repository);
+ }
+
+ private function setTokenForUser($login) {
+ $relations = [
+ $this->accounts->getTokens()->getRelation("accounts"),
+ $this->accounts->getOwners()->getRelation("accounts")
+ ];
+ $tokens = $this->accounts->getTokens()->with($relations, [
+ "login=" => $login,
+ "tokens.authority=" => "github",
+ ]);
+
+ if (count($tokens)) {
+ $this->github->setToken($tokens->current()->token->get());
+ }
+ }
+
+ private function createReleaseFromTag($tag, $repo) {
+ $this->setTokenForUser($repo->owner->login);
+ $this->github->createRelease($repo->full_name, $tag, function($json) {
+ $response = $this->app->getResponse();
+ $response->setResponseCode(201);
+ $response->setHeader("Location", $json->url);
+ })->send();
}
}
"query" => "modal=hook&hook=" . $args["action"]
]));
} else switch ($args["action"]) {
+ case "upd":
+ $this->updateHook($args["owner"], $args["name"]);
+ break;
+
case "add":
$this->addHook($args["owner"], $args["name"]);
break;
}
function addHook($owner, $repo) {
- $this->github->createRepoHook("$owner/$repo", function($hook) use($owner, $repo) {
+ $hook_conf = $this->app->getRequest()->getForm();
+ $this->github->createRepoHook("$owner/$repo", $hook_conf, function($hook) use($owner, $repo) {
if (($cache = $this->github->getCacheStorage())) {
$cache->del($this->github->getCacheKey("hooks:$owner/$repo"));
}
$callback = $this->app->getBaseUrl()->mod("./github/callback");
$location = $this->github->getAuthUrl($callback);
$this->app->redirect($location);
- if ($returnto = $this->app->getRequest()->getQuery("returnto")) {
+ if (($returnto = $this->app->getRequest()->getQuery("returnto"))) {
$this->session->returnto = $returnto;
}
}
return $fetch($callback);
}
- function createRepoHook($repo, callable $callback) {
- $create = new Create\Webhook($this, compact("repo"));
+ function createRepoHook($repo, $conf, callable $callback) {
+ $create = new Create\Webhook($this, compact("repo", "conf"));
return $create($callback);
}
+ function updateRepoHook($repo, $id, $conf, callable $callback) {
+ $update = new Update\Webhook($this, compact("repo", "id", "conf"));
+ return $update($callback);
+ }
+
function deleteRepoHook($repo, $id, callable $callback) {
$delete = new Delete\Webhook($this, compact("repo", "id"));
return $delete($callback);
}
+
+ function createRelease($repo, $tag, callable $callback) {
+ $create = new Create\Release($this, compact("repo", "tag"));
+ return $create($callback);
+ }
+
+ function createReleaseAsset($url, $asset, $type, callable $callback) {
+ $create = new Create\ReleaseAsset($this, compact("url", "asset", "type"));
+ return $create($callback);
+ }
}
--- /dev/null
+<?php
+
+namespace app\Github\Create;
+
+use app\Github\Create;
+use app\Github\Exception\ReleaseCreateFailed;
+use http\Client\Request;
+
+class Release extends Create
+{
+ function getRequest() {
+ $url = $this->url->mod("/repos/". $this->args["repo"] ."/releases");
+ $request = new Request("POST", $url, [
+ "Accept" => "application/vnd.github.v3+json",
+ "Content-Type" => "application/json",
+ "Authorization" => "token ". $this->api->getToken()
+ ]);
+ $request->getBody()->append(json_encode([
+ "tag_name" => $this->args["tag"]
+ ]));
+ return $request;
+ }
+
+ function getException($message, $code, $previous = null) {
+ return new ReleaseCreateFailed($message, $code, $previous);
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\Create;
+
+use app\Github\Create;
+use app\Github\Exception\ReleaseAssetCreateFailed;
+use http\Client\Request;
+
+class ReleaseAsset extends Create
+{
+ function getRequest() {
+ // FIXME: use uri_template extension
+ $url = str_replace("{?name}", "?name=".urlencode(basename($this->args["asset"])), $this->args["url"]);
+ $body = new \http\Message\Body(fopen($this->args["asset"], "rb"));
+ $request = new Request("POST", $url, [
+ "Accept" => "application/vnd.github.v3+json",
+ "Content-Type" => $this->args["type"],
+ "Authorization" => "token ". $this->api->getToken()
+ ], $body);
+ return $request;
+ }
+
+ function getException($message, $code, $previous = null) {
+ return new ReleaseAssetCreateFailed($message, $code, $previous);
+ }
+}
"Authorization" => "token " . $this->api->getToken(),
]);
+ if (!empty($this->args["conf"]["tag"])) {
+ $events[] = "create";
+ }
+ if (!empty($this->args["conf"]["release"])) {
+ $events[] = "release";
+ }
+
$request->getBody()->append(json_encode([
"name" => "web",
+ "events" => $events,
"config" => [
"url" => $this->config->hook->url,
"content_type" => $this->config->hook->content_type,
--- /dev/null
+<?php
+
+namespace app\Github\Exception;
+
+use app\Github\Exception\RequestException;
+
+class ReleaseAssetCreateFailed extends \Exception implements RequestException
+{
+ function __construct($message, $code, $previous = null)
+ {
+ parent::__construct($message ?: "ReleaseAsset create request failed", $code, $previous);
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Github\Exception;
+
+use app\Github\Exception\RequestException;
+
+class ReleaseCreateFailed extends \Exception implements RequestException
+{
+ function __construct($message, $code, $previous = null)
+ {
+ parent::__construct($message ?: "Release create request failed", $code, $previous);
+ }
+}
$this->args = $args;
$this->url = new Url("https://api.github.com/", null, 0);
if (isset($this->config->fetch->{$this}->per_page)) {
- header("X-Fetch-{$this}: ".$this->config->fetch->{$this}->per_page, false);
$this->url->query = "per_page=" . $this->config->fetch->{$this}->per_page;
}
}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Row;
+
+class Account extends Row
+{
+ function updateToken($authority, $token, $json) {
+ $tokens = $this->table->getTokens();
+
+ $existing = $tokens->find([
+ "authority=" => $authority,
+ "account=" => $this->account,
+ ]);
+
+ if (count($existing)) {
+ $found = $existing->current();
+ $found->token = $token;
+ $found->oauth = $json;
+ $found->update();
+ return $found;
+ }
+
+ return $tokens->create([
+ "authority" => $authority,
+ "account" => $this->account,
+ "token" => $token,
+ "oauth" => $json
+ ])->current();
+ }
+
+ function updateOwner($authority, $user, $json) {
+ $owners = $this->table->getOwners();
+
+ $existing = $owners->find([
+ "authority=" => $authority,
+ "account=" => $this->account,
+ ]);
+
+ if (count($existing)) {
+ $found = $existing->current();
+ $found->login = $user;
+ $found->owner = $json;
+ $found->update();
+ return $found;
+ }
+
+ return $owners->create([
+ "authority" => $authority,
+ "login" => $user,
+ "owner" => $json
+ ])->current();
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Rowset;
+
+class AccountCollection extends Rowset
+{
+ protected $row = "app\\Model\\Account";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Connection;
+use pq\Gateway\Table;
+use pq\Query\Expr;
+
+class Accounts extends Table
+{
+ protected $rowset = "app\\Model\\AccountCollection";
+
+ /**
+ * @var \app\Model\Tokens
+ */
+ private $tokens;
+
+ /**
+ * @var \app\Model\Owners
+ */
+ private $owners;
+
+ function __construct(Connection $conn, Tokens $tokens, Owners $owners) {
+ parent::__construct("accounts", $conn);
+ $this->tokens = $tokens;
+ $this->owners = $owners;
+ }
+
+ function getTokens() {
+ return $this->tokens;
+ }
+
+ function getOwners() {
+ return $this->owners;
+ }
+
+ function byOAuth($authority, $token, $user) {
+ if (!($account = $this->byOAuthToken($authority, $token))) {
+ $account = $this->byOAuthOwner($authority, $user);
+ }
+ return $account;
+ }
+
+ function byOAuthToken($authority, $access_token, &$token = null) {
+ $tokens = $this->tokens->find([
+ "token=" => $access_token,
+ "authority=" => $authority,
+ ]);
+
+ if (count($tokens)) {
+ $token = $tokens->current();
+ $accounts = $this->by($token);
+ if (count($accounts)) {
+ return $accounts->current();
+ }
+ }
+ }
+
+ function byOAuthOwner($authority, $login, &$owner = null) {
+ $owners = $this->owners->find([
+ "authority=" => $authority,
+ "login=" => $login,
+ ]);
+
+ if (count($owners)) {
+ $owner = $owners->current();
+ $accounts = $this->by($owner);
+ if (count($accounts)) {
+ return $accounts->current();
+ }
+ }
+ }
+
+ function createOAuthAccount($authority, $token, $user) {
+ $account = $this->create([
+ "account" => new Expr("DEFAULT")
+ ])->current();
+ $this->owners->create([
+ "account" => $account->account,
+ "authority" => $authority,
+ "login" => $user,
+ ]);
+ $this->tokens->create([
+ "account" => $account->account,
+ "authority" => "github",
+ "token" => $token
+ ]);
+ return $account;
+ }
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Table;
+
+class Authorities extends Table
+{
+ protected $name = "authorities";
+ protected $rowset = "app\\Model\\AuthorityCollection";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Row;
+
+class Authority extends Row
+{
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Rowset;
+
+class AuthorityCollection extends Rowset
+{
+ protected $row = "app\\Model\\Authority";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Row;
+
+class Owner extends Row
+{
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Rowset;
+
+class OwnerCollection extends Rowset
+{
+ protected $row = "app\\Model\\Owner";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Table;
+
+class Owners extends Table
+{
+ protected $name = "owners";
+ protected $rowset = "app\\Model\\OwnerCollection";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Row;
+
+class Token extends Row
+{
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Rowset;
+
+class TokenCollection extends Rowset
+{
+ protected $row = "app\\Model\\Token";
+}
--- /dev/null
+<?php
+
+namespace app\Model;
+
+use pq\Gateway\Table;
+
+class Tokens extends Table
+{
+ protected $name = "tokens";
+ protected $rowset = "app\\Model\\TokenCollection";
+}
namespace app;
use ArrayAccess;
-use merry\Config;
+use http\Env\Response;
+use http\Params;
class Session implements ArrayAccess
{
- function __construct(Config $config) {
+ function __construct(Config $config, Response $response) {
foreach ($config->session as $key => $val) {
ini_set("session.$key", $val);
}
+ if (ini_get("session.use_cookies")) {
+ $response->addHeader("Vary", "cookie");
+ }
+ $response->addHeader("Cache-Control",
+ new Params([
+ "private" => true,
+ "must-revalidate" => true,
+ "max-age" => ini_get("session.cache_expire") * 60
+ ])
+ );
session_start();
}
->define(Args::class, [
":spec" => [
[null, "ngrok", "Run ngrok", Args::SINGLE],
+ [null, "initdb", "Create database", Args::SINGLE],
+ [null, "gen-models", "Generate pq\\Gateway models", Args::SINGLE],
["h", "help", "Show this help", Args::HALT],
]
]);
namespace app;
-use merry\Config;
-
define("APP_ENVIRONMENT", getenv("APP_ENVIRONMENT") ?: "localhost");
$injector->share(Config::class)
->define(Config::class, [
- ":array" => parse_ini_file(__DIR__."/../config.ini", true),
+ "+array" => function($key, $injector) {
+ return parse_ini_file(__DIR__."/../../config/app.ini", true);
+ },
":section" => APP_ENVIRONMENT
])
- ->prepare(Config::class, function(Config $config) {
- $credentials = parse_ini_file(__DIR__."/../credentials.ini", true);
- foreach (new Config($credentials, APP_ENVIRONMENT) as $app => $creds) {
- /* one level down should suffice, i.e. $config->github->client = {id,secret,scope} */
- if ($creds instanceof Config) {
- foreach ($creds as $key => $val) {
- $config->$app->$key = $val;
- }
- } else {
- $config->$app = $creds;
- }
- }
+ ->prepare(Config::class, function($config, $injector) {
+ $credentials = parse_ini_file(__DIR__."/../../config/credentials.ini", true);
+ $config->addConfig(new Config($credentials, APP_ENVIRONMENT));
});
require_once __DIR__."/config.php";
-use merry\Config;
use http\Url;
$injector->share(Github\API::class)
--- /dev/null
+<?php
+
+namespace app;
+
+use http\Env\Request;
+use http\Env\Response;
+
+$injector->share(Request::class);
+$injector->share(Response::class);
--- /dev/null
+<?php
+
+namespace app;
+
+require_once __DIR__."/config.php";
+require_once __DIR__."/pq.php";
+
+use pq\Connection;
+
+/* @var $injector \Auryn\Injector */
+
+$injector->define(Model\Accounts::class, [
+ "conn" => Connection::class,
+ ])
+ ->define(Model\Tokens::class, [
+ "conn" => Connection::class,
+ ])
+ ->define(Model\Authorities::class, [
+ "conn" => Connection::class,
+ ])
+ ->define(Model\Owners::class, [
+ "conn" => Connection::class,
+ ]);
+
+//$modelconf = function($key, $injector) {
+// return new Table($key, $injector->make(Connection::class));
+//};
+//
+//$injector->define(Model\Account::class, [
+// "+accounts" => $modelconf,
+// "+owners" => $modelconf,
+// "+tokens" => $modelconf
+//]);
use http\Env\Request;
use http\Env\Response;
-use merry\Config;
-
$injector->share(Plates\Engine::class)
->define(Plates\Engine::class, [
":directory" => __DIR__."/../views",
--- /dev/null
+<?php
+
+namespace app;
+
+require_once __DIR__."/config.php";
+
+use pq\Connection;
+
+/* @var $injector \Auryn\Injector */
+
+$pqconfig = function($key, $injector) {
+ return $injector->make(Config::class)->pq->$key;
+};
+
+$injector->share(Connection::class)
+ ->define(Connection::class, [
+ "+dsn" => $pqconfig,
+ "+flags" => $pqconfig
+ ]);
--- /dev/null
+<?php
+
+namespace app;
+
+use Auryn\Injector;
+
+use FastRoute\DataGenerator;
+use FastRoute\Dispatcher;
+use FastRoute\RouteCollector;
+use FastRoute\RouteParser;
+
+use http\Env\Response;
+
+$injector->share(RouteCollector::class)
+ ->prepare(RouteCollector::class, function($routes) use($injector) {
+ $routes->addRoute("GET", "/reset", function(array $args = null) use($injector) {
+ $injector->make(Session::class)->reset()->regenerateId();
+ $injector->make(Web::class)->redirect($injector->make(BaseUrl::class));
+ });
+ $routes->addRoute("GET", "/session", function(array $args = null) use($injector) {
+ $session = $injector->make(Session::class);
+ $response = $injector->make(Response::class);
+ if (!(extension_loaded("xdebug") && ini_get("xdebug.overload_var_dump") && ini_get("html_errors"))) {
+ $response->setContentType("text/plain");
+ }
+ ob_start($response);
+ var_dump($_SESSION, $session);
+ });
+ $routes->addRoute("GET", "/info", function(array $args = null) {
+ phpinfo();
+ });
+
+ foreach (parse_ini_file(__DIR__."/../../config/routes.ini", true) as $controller => $definition) {
+ $factory = function(array $args = null) use($injector, $controller) {
+ $handler = $injector->make("app\\Controller\\$controller");
+ $handler($args);
+ };
+ foreach ($definition as $method => $locations) {
+ foreach ($locations as $location) {
+ $routes->addRoute($method, $location, $factory);
+ }
+ }
+ }
+ })
+ ->alias(RouteParser::class, RouteParser\Std::class)
+ ->alias(DataGenerator::class, DataGenerator\GroupCountBased::class);
+
+$injector->share(Dispatcher::class)
+ ->alias(Dispatcher::class, Dispatcher\GroupCountBased::class)
+ ->delegate(Dispatcher\GroupCountBased::class, function($class, Injector $injector) {
+ return new $class($injector->make(RouteCollector::class)->getData());
+ });
+
--- /dev/null
+<?php
+
+namespace app;
+
+require_once __DIR__."/http.php";
+
+use Auryn\Injector;
+use http\Env\Request;
+
+$injector->share(Session::class)
+ ->prepare(Session::class, function(Session $session, Injector $injector) {
+ if (isset($session->current) && (!isset($session->previous) || strcmp($session->current, $session->previous))) {
+ $session->previous = $session->current;
+ $session->current = $injector->make(Request::class)->getRequestUrl();
+ }
+ $session->current = $injector->make(Request::class)->getRequestUrl();
+ });
namespace app;
require_once __DIR__."/config.php";
+require_once __DIR__."/http.php";
require_once __DIR__."/github.php";
+require_once __DIR__."/router.php";
+require_once __DIR__."/session.php";
use Auryn\Injector;
-use FastRoute\DataGenerator;
-use FastRoute\Dispatcher;
-use FastRoute\RouteCollector;
-use FastRoute\RouteParser;
-
-use http\Env\Request;
-use http\Env\Response;
-
-$injector->share(Request::class);
-$injector->share(Response::class);
-
-$injector->share(RouteCollector::class)
- ->prepare(RouteCollector::class, function($routes) use($injector) {
- $routes->addRoute("GET", "/reset", function(array $args = null) use($injector) {
- $injector->make(Session::class)->reset()->regenerateId();
- $injector->make(Web::class)->redirect($injector->make(BaseUrl::class));
- });
- $routes->addRoute("GET", "/session", function(array $args = null) use($injector) {
- $session = $injector->make(Session::class);
- $response = $injector->make(Response::class);
- if (!(extension_loaded("xdebug") && ini_get("xdebug.overload_var_dump") && ini_get("html_errors"))) {
- $response->setContentType("text/plain");
- }
- ob_start($response);
- var_dump($_SESSION, $session);
- });
- $routes->addRoute("GET", "/info", function(array $args = null) {
- phpinfo();
- });
-
- foreach (parse_ini_file(__DIR__."/../routes.ini", true) as $controller => $definition) {
- $factory = function(array $args = null) use($injector, $controller) {
- $handler = $injector->make("app\\Controller\\$controller");
- $handler($args);
- };
- foreach ($definition as $method => $locations) {
- foreach ($locations as $location) {
- $routes->addRoute($method, $location, $factory);
- }
- }
- }
- })
- ->alias(RouteParser::class, RouteParser\Std::class)
- ->alias(DataGenerator::class, DataGenerator\GroupCountBased::class);
-
-$injector->share(Dispatcher::class)
- ->alias(Dispatcher::class, Dispatcher\GroupCountBased::class)
- ->delegate(Dispatcher\GroupCountBased::class, function($class, Injector $injector) {
- return new $class($injector->make(RouteCollector::class)->getData());
- });
-
$injector->prepare(Controller::class, function(Controller $controller, Injector $injector) {
if (method_exists($controller, "setSession")) {
$controller->setSession($injector->make(Session::class));
}
+ if (method_exists($controller, "setDatabase")) {
+ $controller->setDatabase($injector->make(Connection::class));
+ }
});
-$injector->share(Session::class)
- ->prepare(Session::class, function(Session $session, Injector $injector) {
- if (isset($session->current) && (!isset($session->previous) || strcmp($session->current, $session->previous))) {
- $session->previous = $session->current;
- $session->current = $injector->make(Request::class)->getRequestUrl();
- }
- $session->current = $injector->make(Request::class)->getRequestUrl();
- });
-
$injector->share(BaseUrl::class);
$injector->share(Web::class);
+++ /dev/null
-[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
-
-github.storage.token.ttl = 3600
-github.storage.cache.repo.ttl = 3600
-github.storage.cache.repos.ttl = 3600
-github.storage.cache.hooks.ttl = 3600
-github.storage.cache.tags.ttl = 3600
-github.storage.cache.releases.ttl = 3600
-
-session.use_cookies = 1
-session.use_only_cookies = 1
-session.use_strict_mode = 1
-session.cookie_httponly = 1
-session.cookie_secure = 1
-session.cache_limiter = private_no_expire
-; minutes
-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
-github.hook.use_basic_auth = ngrok
-
-ngrok.command = ngrok
\ No newline at end of file
+++ /dev/null
-[Homepage]
-GET[] = /
-
-[Wikipage]
-GET[] = /pages/{page}
-
-[Github\Callback]
-GET[] = /github/callback
-
-[Github\Hook]
-POST[] = /github/hook
-
-[Github\Index]
-GET[] = /github
-
-[Github\Repo]
-GET[] = /github/repo/{owner}/{name}
-GET[] = /github/repo/{owner}/{name}/{page}
-
-[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
-
</div>
<div class="col-md-6">
-
- <div class="row text-center">
- <div class="col-md-6">
- <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/add") ?>">
- <button type="submit" class="btn btn-lg btn-block btn-success <?= $this->check($repo) ? "disabled":"" ?>">Enable Hook</button>
- </form>
- </div>
+ <div class="row">
+ <form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/" . ($this->check($repo) ? "upd" : "add")) ?>">
+ <div class="col-md-12">
+ <div class="checkbox">
+ <label for="hook-tag">
+ <input id="hook-tag" type="checkbox" name="tag" value="1">
+ Automatically create a release when I push a tag.
+ </label>
+ </div>
+ <div class="checkbox">
+ <label for="hook-release">
+ <input id="hook-release" type="checkbox" name="release" value="1">
+ Automatically upload a PHARext package as an asset to a release.
+ </label>
+ </div>
+ </div>
+ <?php if ($this->check($repo)) : ?>
+ <div class="col-md-6">
+ <button type="submit" class="btn btn-lg btn-block btn-info">
+ <span class="glyphicon glyphicon-ok-circle"></span>
+ Update Hook
+ </button>
+ </div>
+ <?php else : ?>
+ <div class="col-md-12">
+ <button type="submit" class="btn btn-lg btn-block btn-success">
+ <span class="octicon octicon-plug"></span>
+ Enable Hook
+ </button>
+ </div>
+ <?php endif; ?>
+ </form>
+ <!-- column wrapping! -->
+ <?php if ($this->check($repo)) : ?>
<div class="col-md-6">
<form method="post" action="<?= $baseUrl->mod("./github/repo/". $repo->full_name ."/hook/del") ?>">
- <button class="btn btn-lg btn-block btn-danger <?= $this->check($repo) ? "":"disabled" ?>">Disable Hook</button>
+ <button class="btn btn-lg btn-block btn-danger">
+ <span class="glyphicon glyphicon-remove-circle"></span>
+ Remove Hook
+ </button>
</form>
</div>
+ <?php endif; ?>
</div>
- </form>
</div>
</div>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
- <img src="<?= $session->github->avatar_url ?>"
+ <img src="<?= $session->github->owner["avatar_url"] ?>"
class="img-circle"
alt="avatar"
width="32"
style="margin:-15px 0px">
- <abbr title="<?= $this->e($session->github->name) ?>">
- <?= $this->e($session->github->login) ?>
+ <abbr title="<?= $this->e($session->github->owner["name"]) ?>">
+ <?= $this->e($session->github->owner["login"]) ?>
</abbr>
<span class="caret"></span>
</a>
namespace app;
$bootstrap = require __DIR__."/../app/bootstrap.php";
-$injector = $bootstrap(["config", "github", "cli"]);
-$injector->execute(Cli::class, [$argc, $argv]);
+$injector = $bootstrap(["config", "github", "pq", "cli"]);
+$injector->execute(Cli::class, [$argc, $argv, function($command, array $args = []) use($injector) {
+ $injector->execute($injector->make($command), $args);
+}]);
}
},
"require": {
- "m6w6/pharext": "~3.0",
+ "m6w6/pharext": "dev-master",
"m6w6/merry": "~1.1",
"nikic/fast-route": "0.4.*",
"league/plates": "~3.1",
"rdlowrey/auryn": "dev-master",
- "m6w6/pharext.wiki": "dev-master"
+ "m6w6/pharext.wiki": "dev-master",
+ "m6w6/pq-gateway": "^2.0"
}
}
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "c13d01568dea02e2afe4b9060478178f",
+ "hash": "a2c6e7fb3b26571277d242842331c3cb",
"packages": [
{
"name": "league/plates",
},
{
"name": "m6w6/pharext",
- "version": "v3.0.1",
+ "version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/m6w6/pharext.git",
- "reference": "3dcc326aa922ce4b70d07049572c6f15a744869c"
+ "reference": "e458434f1a5539f1905e61835bed0123f1635067"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/m6w6/pharext/zipball/3dcc326aa922ce4b70d07049572c6f15a744869c",
- "reference": "3dcc326aa922ce4b70d07049572c6f15a744869c",
+ "url": "https://api.github.com/repos/m6w6/pharext/zipball/e458434f1a5539f1905e61835bed0123f1635067",
+ "reference": "e458434f1a5539f1905e61835bed0123f1635067",
"shasum": ""
},
"bin": [
"package",
"phar"
],
- "time": "2015-04-08 10:17:40"
+ "time": "2015-05-08 08:54:09"
},
{
"name": "m6w6/pharext.wiki",
"type": "library",
"time": "2015-04-30 17:39:40"
},
+ {
+ "name": "m6w6/pq-gateway",
+ "version": "v2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/m6w6/pq-gateway.git",
+ "reference": "19590cfee428909a9ad2195b02a30ae176980992"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/m6w6/pq-gateway/zipball/19590cfee428909a9ad2195b02a30ae176980992",
+ "reference": "19590cfee428909a9ad2195b02a30ae176980992",
+ "shasum": ""
+ },
+ "suggest": {
+ "react/promise": "1.0.*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "pq\\Gateway": "lib",
+ "pq\\Query": "lib"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Michael Wallner",
+ "email": "mike@php.net"
+ }
+ ],
+ "description": "Table/row gateway for ext-pq",
+ "homepage": "http://github.com/m6w6/pq-gateway",
+ "keywords": [
+ "gateway",
+ "orm",
+ "postgres",
+ "postgresql",
+ "pq"
+ ],
+ "time": "2014-10-15 15:30:26"
+ },
{
"name": "nikic/fast-route",
"version": "v0.4.0",
"source": {
"type": "git",
"url": "https://github.com/rdlowrey/Auryn.git",
- "reference": "500b08ec9942eb8b647b128fa13942d45754e3d4"
+ "reference": "2e03bf93ddcd855e962d314cce74ce2a01a49228"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/rdlowrey/Auryn/zipball/500b08ec9942eb8b647b128fa13942d45754e3d4",
- "reference": "500b08ec9942eb8b647b128fa13942d45754e3d4",
+ "url": "https://api.github.com/repos/rdlowrey/Auryn/zipball/be3807b62a6991e61151b55b6974dc1289afabd5",
+ "reference": "2e03bf93ddcd855e962d314cce74ce2a01a49228",
"shasum": ""
},
"require": {
"dic",
"ioc"
],
- "time": "2015-03-12 19:52:27"
+ "time": "2015-05-01 14:20:14"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
+ "m6w6/pharext": 20,
"rdlowrey/auryn": 20,
"m6w6/pharext.wiki": 20
},
--- /dev/null
+[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
+
+github.storage.token.ttl = 3600
+github.storage.cache.repo.ttl = 3600
+github.storage.cache.repos.ttl = 3600
+github.storage.cache.hooks.ttl = 3600
+github.storage.cache.tags.ttl = 3600
+github.storage.cache.releases.ttl = 3600
+
+session.use_cookies = 1
+session.use_only_cookies = 1
+session.use_strict_mode = 1
+session.cookie_httponly = 1
+session.cookie_secure = 1
+session.cache_limiter = ""
+;private_no_expire
+; minutes
+session.cache_expire = 0
+
+pq.flags = 0
+pq.dsn = "user=pharext host=localhost"
+
+[localhost : production]
+
+github.hook.url = https://pharext.ngrok.io/src/pharext.org.git/public/github/hook
+github.hook.insecure_ssl = 1
+github.hook.use_basic_auth = ngrok
+
+ngrok.command = ngrok
\ No newline at end of file
--- /dev/null
+[Homepage]
+GET[] = /
+
+[Wikipage]
+GET[] = /pages/{page}
+
+[Github\Callback]
+GET[] = /github/callback
+
+[Github\Hook]
+POST[] = /github/hook
+
+[Github\Index]
+GET[] = /github
+
+[Github\Repo]
+GET[] = /github/repo/{owner}/{name}
+GET[] = /github/repo/{owner}/{name}/{page}
+
+[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
+
--- /dev/null
+drop table if exists authorities cascade;
+drop table if exists accounts cascade;
+drop table if exists tokens cascade;
+drop table if exists owners cascade;
+
+create table authorities (
+ authority text not null primary key
+);
+
+insert into authorities values('github');
+
+create table accounts (
+ account uuid not null default uuid_generate_v4() primary key
+);
+
+create table tokens (
+ token text not null primary key
+ ,account uuid not null references accounts on update cascade on delete cascade
+ ,authority text not null references authorities on update cascade on delete cascade
+ ,oauth jsonb
+);
+
+create table owners (
+ account uuid not null references accounts on update cascade on delete cascade
+ ,authority text not null references authorities on update cascade on delete cascade
+ ,login text not null
+ ,owner jsonb
+ ,primary key (account,authority)
+ ,unique key (login,authority)
+);
namespace app;
$bootstrap = require "../app/bootstrap.php";
-$injector = $bootstrap(["config", "github", "plates", "web"]);
+$injector = $bootstrap(["config", "github", "plates", "model", "web"]);
$injector->execute(Web::class);