mirror of
https://github.com/daledavies/jump.git
synced 2026-01-11 01:42:09 +01:00
Refactor APIs to use routing
This commit is contained in:
@@ -1 +1 @@
|
||||
v1.2.3 (1657207353)
|
||||
v1.2.3 (1658150865)
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Return icon image data for a given site from sites.json
|
||||
*
|
||||
* @author Dale Davies <dale@daledavies.co.uk>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Provided by composer for psr-4 style autoloading.
|
||||
require __DIR__ .'/../vendor/autoload.php';
|
||||
|
||||
$config = new Jump\Config();
|
||||
$cache = new Jump\Cache($config);
|
||||
$sites = new Jump\Sites($config, $cache);
|
||||
|
||||
$siteurl = isset($_GET['siteurl']) ? filter_var($_GET['siteurl'], FILTER_SANITIZE_URL) : (throw new Exception('siteurl param not provided'));
|
||||
|
||||
$site = $sites->get_site_by_url($siteurl);
|
||||
|
||||
$imagedata = $site->get_favicon_image_data();
|
||||
|
||||
// We made it here so output the API response as json.
|
||||
header('Content-Type: '.$imagedata->mimetype);
|
||||
echo $imagedata->data;
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Proxy requests to Unsplash API and cache response.
|
||||
*
|
||||
* @author Dale Davies <dale@daledavies.co.uk>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Provided by composer for psr-4 style autoloading.
|
||||
require __DIR__ .'/../vendor/autoload.php';
|
||||
|
||||
$config = new Jump\Config();
|
||||
$cache = new Jump\Cache($config);
|
||||
|
||||
// If this script is run via CLI then clear the cache and repopulate it,
|
||||
// otherwise if run via web then get image data from cache and run this
|
||||
// script asynchronously to refresh the cache for next time.
|
||||
if (http_response_code() === false) {
|
||||
$unsplashdata = load_cache_unsplash_data($config);
|
||||
$cache->save(cachename: 'unsplash', data: $unsplashdata);
|
||||
die('Cached data from Unsplash');
|
||||
}
|
||||
|
||||
// Output header here so we can return early with a json response if there is a curl error.
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Initialise a new session using the request object.
|
||||
$session = new Nette\Http\Session((new Nette\Http\RequestFactory)->fromGlobals(), new Nette\Http\Response);
|
||||
$session->setName($config->get('sessionname'));
|
||||
$session->setExpiration($config->get('sessiontimeout'));
|
||||
|
||||
// Get a Nette session section for CSRF data.
|
||||
$csrfsection = $session->getSection('csrf');
|
||||
|
||||
// Has a CSRF token been set up for the session yet?
|
||||
if (!$csrfsection->offsetExists('token')){
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'Session not fully set up']));
|
||||
}
|
||||
|
||||
// Check CSRF token saved in session against token provided via request.
|
||||
$token = isset($_GET['token']) ? $_GET['token'] : false;
|
||||
if (!$token || !hash_equals($csrfsection->get('token'), $token)) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'API token is incorrect or missing']));
|
||||
}
|
||||
|
||||
$unsplashdata = $cache->load(cachename: 'unsplash');
|
||||
if ($unsplashdata == null) {
|
||||
$unsplashdata = load_cache_unsplash_data($config);
|
||||
$cache->save(cachename: 'unsplash', data: $unsplashdata);
|
||||
}
|
||||
|
||||
echo json_encode($unsplashdata);
|
||||
|
||||
shell_exec('/usr/bin/nohup /usr/bin/php -f unsplashdata.php >/dev/null 2>&1 &');
|
||||
|
||||
function load_cache_unsplash_data($config) {
|
||||
Crew\Unsplash\HttpClient::init([
|
||||
'utmSource' => 'jump_startpage',
|
||||
'applicationId' => $config->get('unsplashapikey'),
|
||||
]);
|
||||
// Try to get a random image via the API.
|
||||
try {
|
||||
$photo = Crew\Unsplash\Photo::random([
|
||||
'collections' => $config->get('unsplashcollections', false),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => json_decode($e->getMessage())]));
|
||||
}
|
||||
// Download the image data from Unsplash.
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_URL, $photo->urls['raw'].'&auto=compress&w=1920');
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, true);
|
||||
$response = curl_exec($ch);
|
||||
// Create the response and return it.
|
||||
$description = 'Photo';
|
||||
if ($photo->description !== null &&
|
||||
strlen($photo->description) <= 45) {
|
||||
$description = $photo->description;
|
||||
}
|
||||
$unsplashdata = new stdClass();
|
||||
$unsplashdata->color = $photo->color;
|
||||
$unsplashdata->attribution = '<a target="_blank" rel="noopener" href="'.$photo->links['html'].'">'.$description.' by '.$photo->user['name'].'</a>';
|
||||
$unsplashdata->imagedatauri = 'data: '.(new finfo(FILEINFO_MIME_TYPE))->buffer($response).';base64,'.base64_encode($response);
|
||||
return $unsplashdata;
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Proxy requests to OpenWeather API and cache response.
|
||||
*
|
||||
* @author Dale Davies <dale@daledavies.co.uk>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Provided by composer for psr-4 style autoloading.
|
||||
require __DIR__ .'/../vendor/autoload.php';
|
||||
|
||||
$config = new Jump\Config();
|
||||
$cache = new Jump\Cache($config);
|
||||
|
||||
// Output header here so we can return early with a json response if there is a curl error.
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
// Initialise a new session using the request object.
|
||||
$session = new \Nette\Http\Session((new \Nette\Http\RequestFactory)->fromGlobals(), new \Nette\Http\Response);
|
||||
$session->setName($config->get('sessionname'));
|
||||
$session->setExpiration($config->get('sessiontimeout'));
|
||||
|
||||
// Get a Nette session section for CSRF data.
|
||||
$csrfsection = $session->getSection('csrf');
|
||||
|
||||
// Has a CSRF token been set up for the session yet?
|
||||
if (!$csrfsection->offsetExists('token')){
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'Session not fully set up']));
|
||||
}
|
||||
|
||||
// Check CSRF token saved in session against token provided via request.
|
||||
$token = isset($_GET['token']) ? $_GET['token'] : false;
|
||||
if (!$token || !hash_equals($csrfsection->get('token'), $token)) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'API token is incorrect or missing']));
|
||||
}
|
||||
|
||||
// Start of variables we want to use.
|
||||
$owmapiurlbase = 'https://api.openweathermap.org/data/2.5/weather';
|
||||
$units = $config->parse_bool($config->get('metrictemp')) ? 'metric' : 'imperial';
|
||||
|
||||
// If we have either lat or lon query params then cast them to a float, if not then
|
||||
// set the values to zero.
|
||||
$lat = isset($_GET['lat']) ? (float) $_GET['lat'] : 0;
|
||||
$lon = isset($_GET['lon']) ? (float) $_GET['lon'] : 0;
|
||||
|
||||
// Use the lat and lon values provided unless they are zero, this might mean that
|
||||
// either they werent provided as query params or they couldn't be cast to a float.
|
||||
// If they are zero then use the default latlong from config.
|
||||
$latlong = [$lat, $lon];
|
||||
if ($lat === 0 || $lon === 0) {
|
||||
$latlong = explode(',', $config->get('latlong', false));
|
||||
}
|
||||
|
||||
// This is the API endpoint and params we are using for the query,
|
||||
$url = $owmapiurlbase
|
||||
.'?units=' . $units
|
||||
.'&lat=' . $latlong[0]
|
||||
.'&lon=' . $latlong[1]
|
||||
.'&appid=' . $config->get('owmapikey', false);
|
||||
|
||||
// Use the cache to store/retrieve data, make an md5 hash of latlong so it is not possible
|
||||
// to track location history form the stored cache.
|
||||
$weatherdata = $cache->load(cachename: 'weatherdata', key: md5(json_encode($latlong)), callback: function() use ($url) {
|
||||
// Ask the API for some data.
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, true);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Just in case something went wrong with the request we'll capture the error.
|
||||
if (curl_errno($ch)) {
|
||||
$curlerror = curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
// If we had an error then return the error message and exit, otherwise return the API response.
|
||||
if (isset($curlerror)) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => $curlerror]));
|
||||
}
|
||||
return $response;
|
||||
});
|
||||
|
||||
// We made it here so output the API response as json.
|
||||
echo $weatherdata;
|
||||
File diff suppressed because one or more lines are too long
@@ -55,7 +55,7 @@ export default class Main {
|
||||
if (JUMP.unsplashcolor) {
|
||||
backgroundelm.style.backgroundColor = JUMP.unsplashcolor;
|
||||
}
|
||||
fetch(JUMP.wwwurl + '/api/unsplashdata.php?token=' + JUMP.token)
|
||||
fetch(JUMP.wwwurl + '/api/unsplash/' + JUMP.token + '/')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.error) {
|
||||
|
||||
@@ -16,9 +16,9 @@ export default class Weather {
|
||||
fetch_owm_data(latlong) {
|
||||
// If we are provided with a latlong then the user must have cliecked on the location
|
||||
// button at some point, so let's use this in the api url...
|
||||
let apiurl = JUMP.wwwurl + '/api/weatherdata.php?token=' + JUMP.token;
|
||||
let apiurl = JUMP.wwwurl + '/api/weather/' + JUMP.token + '/';
|
||||
if (latlong.length) {
|
||||
apiurl += ('&lat=' + latlong[0] + '&lon=' + latlong[1]);
|
||||
apiurl += (latlong[0] + '/' + latlong[1] + '/');
|
||||
}
|
||||
// Get some data from the weather api...
|
||||
fetch(apiurl)
|
||||
|
||||
39
jumpapp/classes/API/AbstractAPI.php
Normal file
39
jumpapp/classes/API/AbstractAPI.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Jump\API;
|
||||
|
||||
abstract class AbstractAPI {
|
||||
|
||||
public function __construct(
|
||||
protected \Jump\Config $config,
|
||||
protected \Jump\Cache $cache,
|
||||
protected \Nette\Http\Session $session,
|
||||
protected ?array $routeparams
|
||||
){}
|
||||
|
||||
protected function send_json_header(): void {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
}
|
||||
|
||||
protected function validate_token(): void {
|
||||
$this->send_json_header();
|
||||
|
||||
// Get a Nette session section for CSRF data.
|
||||
$csrfsection = $this->session->getSection('csrf');
|
||||
|
||||
// Has a CSRF token been set up for the session yet?
|
||||
if (!$csrfsection->offsetExists('token')){
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'Session not fully set up']));
|
||||
}
|
||||
|
||||
// Check CSRF token saved in session against token provided via request.
|
||||
if (!isset($this->routeparams['token']) || !hash_equals($csrfsection->get('token'), $this->routeparams['token'])) {
|
||||
http_response_code(401);
|
||||
die(json_encode(['error' => 'API token is incorrect or missing']));
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function get_output(): string;
|
||||
|
||||
}
|
||||
24
jumpapp/classes/API/Icon.php
Normal file
24
jumpapp/classes/API/Icon.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Jump\API;
|
||||
|
||||
class Icon extends AbstractAPI {
|
||||
|
||||
public function get_output(): string {
|
||||
if (!isset($this->routeparams['siteurl']) || empty($this->routeparams['siteurl'])) {
|
||||
throw new \Exception('The siteurl query parameter is not provided or empty');
|
||||
}
|
||||
|
||||
$sites = new \Jump\Sites($this->config, $this->cache);
|
||||
|
||||
$siteurl = filter_var($this->routeparams['siteurl'], FILTER_SANITIZE_URL);
|
||||
$site = $sites->get_site_by_url($siteurl);
|
||||
|
||||
$imagedata = $site->get_favicon_image_data();
|
||||
|
||||
// We made it here so output the API response as json.
|
||||
header('Content-Type: '.$imagedata->mimetype);
|
||||
return $imagedata->data;
|
||||
}
|
||||
|
||||
}
|
||||
23
jumpapp/classes/API/Unsplash.php
Normal file
23
jumpapp/classes/API/Unsplash.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Jump\API;
|
||||
|
||||
class Unsplash extends AbstractAPI {
|
||||
|
||||
public function get_output(): string {
|
||||
|
||||
$this->validate_token();
|
||||
|
||||
$unsplashdata = $this->cache->load(cachename: 'unsplash');
|
||||
|
||||
if ($unsplashdata == null) {
|
||||
$unsplashdata = \Jump\Unsplash::load_cache_unsplash_data($this->config);
|
||||
$this->cache->save(cachename: 'unsplash', data: $unsplashdata);
|
||||
}
|
||||
|
||||
$toexec = '/usr/bin/nohup /usr/bin/php -f ' . $this->config->get('wwwroot') . '/cli/cacheunsplash.php >/dev/null 2>&1 &';
|
||||
shell_exec($toexec);
|
||||
|
||||
return json_encode($unsplashdata);
|
||||
}
|
||||
}
|
||||
63
jumpapp/classes/API/Weather.php
Normal file
63
jumpapp/classes/API/Weather.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Jump\API;
|
||||
|
||||
class Weather extends AbstractAPI {
|
||||
|
||||
public function get_output(): string {
|
||||
|
||||
$this->validate_token();
|
||||
|
||||
// Start of variables we want to use.
|
||||
$owmapiurlbase = 'https://api.openweathermap.org/data/2.5/weather';
|
||||
$units = $this->config->parse_bool($this->config->get('metrictemp')) ? 'metric' : 'imperial';
|
||||
|
||||
// If we have either lat or lon query params then cast them to a float, if not then
|
||||
// set the values to zero.
|
||||
$lat = isset($this->routeparams['lat']) ? (float) $this->routeparams['lat'] : 0;
|
||||
$lon = isset($this->routeparams['lat']) ? (float) $this->routeparams['lat'] : 0;
|
||||
|
||||
// Use the lat and lon values provided unless they are zero, this might mean that
|
||||
// either they werent provided as query params or they couldn't be cast to a float.
|
||||
// If they are zero then use the default latlong from config.
|
||||
$latlong = [$lat, $lon];
|
||||
if ($lat === 0 || $lon === 0) {
|
||||
$latlong = explode(',', $this->config->get('latlong', false));
|
||||
}
|
||||
|
||||
// This is the API endpoint and params we are using for the query,
|
||||
$url = $owmapiurlbase
|
||||
.'?units=' . $units
|
||||
.'&lat=' . $latlong[0]
|
||||
.'&lon=' . $latlong[1]
|
||||
.'&appid=' . $this->config->get('owmapikey', false);
|
||||
|
||||
// Use the cache to store/retrieve data, make an md5 hash of latlong so it is not possible
|
||||
// to track location history form the stored cache.
|
||||
$weatherdata = $this->cache->load(cachename: 'weatherdata', key: md5(json_encode($latlong)), callback: function() use ($url) {
|
||||
// Ask the API for some data.
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, true);
|
||||
$response = curl_exec($ch);
|
||||
|
||||
// Just in case something went wrong with the request we'll capture the error.
|
||||
if (curl_errno($ch)) {
|
||||
$curlerror = curl_error($ch);
|
||||
}
|
||||
curl_close($ch);
|
||||
// If we had an error then return the error message and exit, otherwise return the API response.
|
||||
if (isset($curlerror)) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => $curlerror]));
|
||||
}
|
||||
return $response;
|
||||
});
|
||||
|
||||
// We made it here so return the API response as a json string.
|
||||
return $weatherdata;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,6 +26,15 @@ class Main {
|
||||
$this->router->addRoute('/tag/<tag>', [
|
||||
'class' => 'Jump\Pages\TagPage'
|
||||
]);
|
||||
$this->router->addRoute('/api/icon?siteurl=<siteurl>', [
|
||||
'class' => 'Jump\API\Icon'
|
||||
]);
|
||||
$this->router->addRoute('/api/unsplash[/<token>]', [
|
||||
'class' => 'Jump\API\Unsplash'
|
||||
]);
|
||||
$this->router->addRoute('/api/weather[/<token>[/<lat>[/<lon>]]]', [
|
||||
'class' => 'Jump\API\Weather'
|
||||
]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
40
jumpapp/classes/Unsplash.php
Normal file
40
jumpapp/classes/Unsplash.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Jump;
|
||||
|
||||
class Unsplash {
|
||||
|
||||
public static function load_cache_unsplash_data($config) {
|
||||
\Crew\Unsplash\HttpClient::init([
|
||||
'utmSource' => 'jump_startpage',
|
||||
'applicationId' => $config->get('unsplashapikey'),
|
||||
]);
|
||||
// Try to get a random image via the API.
|
||||
try {
|
||||
$photo = \Crew\Unsplash\Photo::random([
|
||||
'collections' => $config->get('unsplashcollections', false),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
http_response_code(500);
|
||||
die(json_encode(['error' => json_decode($e->getMessage())]));
|
||||
}
|
||||
// Download the image data from Unsplash.
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_URL, $photo->urls['raw'].'&auto=compress&w=1920');
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_setopt($ch, CURLOPT_FAILONERROR, true);
|
||||
$response = curl_exec($ch);
|
||||
// Create the response and return it.
|
||||
$description = 'Photo';
|
||||
if ($photo->description !== null &&
|
||||
strlen($photo->description) <= 45) {
|
||||
$description = $photo->description;
|
||||
}
|
||||
$unsplashdata = new \stdClass();
|
||||
$unsplashdata->color = $photo->color;
|
||||
$unsplashdata->attribution = '<a target="_blank" rel="noopener" href="'.$photo->links['html'].'">'.$description.' by '.$photo->user['name'].'</a>';
|
||||
$unsplashdata->imagedatauri = 'data: '.(new \finfo(FILEINFO_MIME_TYPE))->buffer($response).';base64,'.base64_encode($response);
|
||||
return $unsplashdata;
|
||||
}
|
||||
}
|
||||
22
jumpapp/cli/cacheunsplash.php
Normal file
22
jumpapp/cli/cacheunsplash.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* Proxy requests to Unsplash API and cache response.
|
||||
*
|
||||
* @author Dale Davies <dale@daledavies.co.uk>
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// Provided by composer for psr-4 style autoloading.
|
||||
require __DIR__ .'/../vendor/autoload.php';
|
||||
|
||||
$config = new Jump\Config();
|
||||
$cache = new Jump\Cache($config);
|
||||
|
||||
// If this script is run via CLI then clear the cache and repopulate it,
|
||||
// otherwise if run via web then get image data from cache and run this
|
||||
// script asynchronously to refresh the cache for next time.
|
||||
if (http_response_code() === false) {
|
||||
$unsplashdata = Jump\Unsplash::load_cache_unsplash_data($config);
|
||||
$cache->save(cachename: 'unsplash', data: $unsplashdata);
|
||||
die('Cached data from Unsplash');
|
||||
}
|
||||
@@ -33,6 +33,6 @@
|
||||
{{/ hastags}}
|
||||
<span class="unsplash"></span>
|
||||
<div class="background fixed"></div>
|
||||
<script defer src="{{{wwwurl}}}/assets/js/index.01967507a63ce4e80097.min.js"></script>
|
||||
<script defer src="{{{wwwurl}}}/assets/js/index.068ef33aa2ef2fcb48cf.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<ul class="sites {{# altlayout}}alternate{{/ altlayout}}">
|
||||
{{# sites}}<li><a {{# newtab}}target="_blank"{{/ newtab}} rel="{{# nofollow}}nofollow {{/ nofollow}}{{# newtab}}noopener{{/ newtab}}" title="{{description}}" href="{{url}}">
|
||||
<span class="icon">
|
||||
<img src="{{{wwwurl}}}/api/icon.php?siteurl={{#urlencode}}{{url}}{{/urlencode}}">
|
||||
<img src="{{{wwwurl}}}/api/icon?siteurl={{#urlencode}}{{url}}{{/urlencode}}">
|
||||
</span>
|
||||
<span class="name">{{name}}</span>
|
||||
</a></li>{{/ sites}}
|
||||
|
||||
Reference in New Issue
Block a user