-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
release version 1
- Loading branch information
Showing
9 changed files
with
634 additions
and
244 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
composer.phar | ||
composer.lock | ||
config.php | ||
secret.txt | ||
vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,11 @@ | ||
# micropub | ||
a minimal PHP micropub endpoint, with media support | ||
a minimal PHP micropub endpoint for static sites, with media support | ||
|
||
This is a micropub solution for static sites generated with [Hugo](https://gohugo.io/). It will accept most actions as defined in the [Micropub standard](https://www.w3.org/TR/micropub/), and upon completion will invoke Hugo and rebuild your site. | ||
|
||
This is based heavily off of the following projects: | ||
* [rhiaro's MVP micropub](https://rhiaro.co.uk/2015/04/minimum-viable-micropub) | ||
* [dgold's Nanopub](https://github.com/dg01d/nanopub/) | ||
* [aaronpk's MVP Media Endpoint](https://gist.github.com/aaronpk/4bee1753688ca9f3036d6f31377edf14) | ||
|
||
My personal setup is a little convoluted. I run a variety of sites on my server, all with different document roots. I run PHP in a container, which mounts my host's `/var/www/html` into the container. On my host, `/var/www/html` holds a WordPress multi-site setup. My other sites are all static sites generated with [Hugo](https://gohugo.io/). | ||
|
||
I use [Caddy](https://caddyserver.com/) as my web server. In my static sites, I have the following directive to make all PHP requests work with the PHP container: | ||
``` | ||
fastcgi / 127.0.0.1:9000 php { root /var/www/html } | ||
``` | ||
But because PHP is running in a container, it does not have access to anything outside of `/var/www/html`. In order to get my micropub-published files into my static sites, I use [incron](http://inotify.aiken.cz/?section=incron&page=about&lang=en). I create a new `incrontab` entry for each static site that should be micropub-enabled, to watch a directory that corresponds with the site's domain name. When a new file is written, the `micropub.sh` script in this repository will execute, copying and moving the files as necessary. If it's a Markdown file, `hugo` is invoked to rebuild the site. | ||
|
||
The `micropub.sh` script copies AND moves images. This is so that micropub endpoints can see and access uploaded images without requiring a full site rebuild. The image is copied into the `/images/` directory of the site's docroot, and moved to the `/static/images` directory of the source of my Hugo site. | ||
|
||
The `is.php` script in this repo is an example of how to use most of this functionality **without** a full micropub setup. I use it to power [https://skippy.is/](https://skippy.is/) for easy uploading from my phone. It builds the Markdown file in exactly the way I want with minimal input from me. | ||
This works **for me**, following the principles of [self dog fooding](https://indieweb.org/selfdogfood). Rather then develop a universal widget that might work for all possible implementation, I built what I needed. Hopefully this serves as an inspiration for others, in the same way that those projects linked above heavily inspired me. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"require": { | ||
"symfony/yaml": "4.0.*", | ||
"p3k/micropub": "*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?php | ||
|
||
/** | ||
this is the configuration file for my micropub implementation. Rather than | ||
jump through a bunch of hoops trying to parse the host name, and figure out | ||
the current working directory, just capture those values as needed. | ||
**/ | ||
$config = array( | ||
# the URL of our site, with trailing slash. | ||
'base_url' => 'https://' . $_SERVER['HTTP_HOST'] .'/', | ||
|
||
# the base path of the site's docroot, with trailing slash | ||
'base_path' => '/var/www/html/', | ||
|
||
# the name of the sub-directory for images, with trailing slash. | ||
# we'll create sub-directories of the form 'year/month/'. | ||
'upload_path' => 'images/', | ||
|
||
# the max pixel width of uploaded images. | ||
'max_image_width' => 800, | ||
|
||
# the path to the Hugo site. DO NOT include "content/", we'll handle that. | ||
# trailing slash required. | ||
'source_path' => '/var/www/skippy/', | ||
|
||
# different types of content may have different paths. | ||
# by default, entries are in the root of the /content/ directory, so | ||
# are not included here. Notes are in the /note/ directory. | ||
'content_paths' => array( | ||
'note' => 'note/' . date('Y/m/d/'), | ||
), | ||
|
||
# whether or not to copy uploaded files to the source /static/ directory. | ||
'copy_uploads_to_source' => true, | ||
|
||
# an array of syndication targets; each of which should contain the | ||
# necessary credentials. | ||
'syndication' => array( | ||
'twitter' => 'smerrill', | ||
), | ||
|
||
# the timezone to use for all times | ||
'tz' => 'America/New_York', | ||
|
||
# the command used to build the site | ||
'command' => '/var/www/bin/hugo --quiet --config /var/www/skippy/config.yaml -s /var/www/skippy/ -d /var/www/html/', | ||
); | ||
|
||
return $config; | ||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
<?php | ||
function http_status ($code) { | ||
$http_codes = array( | ||
'200' => '200 OK', | ||
'201' => '201 Created', | ||
'202' => '202 Accepted', | ||
'400' => '400 Bad Request', | ||
'401' => '401 Unauthorized', | ||
'403' => '403 Forbidden', | ||
'409' => '409 Conflict', | ||
'413' => '413 Payload Too Large', | ||
'415' => '415 Unsupported Media Type', | ||
'502' => '502 Bad Gateway', | ||
); | ||
return $http_codes[$code]; | ||
} | ||
|
||
function quit ($code = 400, $error = '', $description = 'An error occurred.', $location = '') { | ||
$code = (int) $code; | ||
$status = 'success'; | ||
header("HTTP/1.1 " . http_status($code)); | ||
if ( $code >= 400 ) { | ||
echo json_encode(['error' => $error, 'error_description' => $description]); | ||
} elseif ($code == 200 || $code == 201 || $code == 202) { | ||
if (!empty($location)) { | ||
header('Location: ' . $location); | ||
} | ||
} | ||
die(); | ||
} | ||
|
||
function show_info() { | ||
echo '<p>This is a <a href="https://www.w3.org/TR/micropub/">micropub</a> endpoint.</p>'; | ||
die(); | ||
} | ||
|
||
function parse_request() { | ||
if ( strtolower($_SERVER['CONTENT_TYPE']) == 'application/json' || strtolower($_SERVER['HTTP_CONTENT_TYPE']) == 'application/json' ) { | ||
$request = \p3k\Micropub\Request::createFromJSONObject(json_decode(file_get_contents('php://input'), true)); | ||
} else { | ||
$request = \p3k\Micropub\Request::createFromPostArray($_POST); | ||
} | ||
if($request->error) { | ||
quit(400, $request->error_property, $request->error_description); | ||
} | ||
return $request; | ||
} | ||
|
||
/** | ||
* getallheaders() replacement for nginx | ||
* | ||
* Replaces the getallheaders function which relies on Apache | ||
* | ||
* @return array incoming headers from _POST | ||
*/ | ||
if (!function_exists('getallheaders')) { | ||
function getallheaders() { | ||
$headers = []; | ||
foreach ($_SERVER as $name => $value) { | ||
if (substr($name, 0, 5) == 'HTTP_') { | ||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; | ||
} | ||
} | ||
return $headers; | ||
} | ||
} | ||
|
||
/** | ||
* Validate incoming requests, using IndieAuth | ||
* | ||
* This section largely adopted from rhiaro | ||
* | ||
* @param array $token the authorization token to check | ||
* @param string $me the site to authorize | ||
* | ||
* @return boolean true if authorised | ||
*/ | ||
function indieAuth($token, $me = '') { | ||
/** | ||
* Check token is valid | ||
*/ | ||
if ( $me == '' ) { $me = $_SERVER['HTTP_HOST']; } | ||
$ch = curl_init("https://tokens.indieauth.com/token"); | ||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | ||
curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Authorization: $token")); | ||
$response = Array(); | ||
$curl_response = curl_exec($ch); | ||
if (false === $curl_response) { | ||
quit(502, 'connection_problem', 'Unable to connect to indieauth service'); | ||
} | ||
parse_str($curl_response, $response); | ||
curl_close($ch); | ||
if (empty($response) || ! isset($response['me']) || ! isset($response['scope']) ) { | ||
quit(401, 'insufficient_scope', 'The request lacks authentication credentials'); | ||
} elseif ($response['me'] != $me) { | ||
quit(401, 'insufficient_scope', 'The request lacks valid authentication credentials'); | ||
} elseif (is_array($response['scope']) && !in_array('create', $response['scope']) && !in_array('post', $response['scope'])) { | ||
quit(403, 'forbidden', 'Client does not have access to this resource'); | ||
} elseif (FALSE === stripos($response['scope'], 'create')) { | ||
quit(403, 'Forbidden', 'Client does not have access to this resource'); | ||
} | ||
// we got here, so all checks passed. return true. | ||
return true; | ||
} | ||
|
||
# respond to queries about config and/or syndication | ||
function show_config($show = 'all') { | ||
global $config; | ||
$syndicate_to = array(); | ||
if ( ! empty($config['syndication'])) { | ||
foreach ($config['syndication'] as $k => $v) { | ||
$syndicate_to[] = array('uid' => $k, 'name' => $k); | ||
} | ||
} | ||
|
||
$conf = array("media-endpoint" => $config['base_url'] . 'micropub/index.php'); | ||
if ( ! empty($syndicate_to) ) { | ||
$conf['syndicate_to'] = $syndicate_to; | ||
} | ||
|
||
header('Content-Type: application/json'); | ||
if ($show == "syndicate-to") { | ||
echo json_encode(array('syndicate-to' => $syndicate_to), 32 | 64 | 128 | 256); | ||
} else { | ||
echo json_encode($conf, 32 | 64 | 128 | 256); | ||
} | ||
exit; | ||
} | ||
|
||
function build_site() { | ||
global $config; | ||
exec( $config['command']); | ||
} | ||
?> |
Oops, something went wrong.