mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 21:22:40 +00:00
Initially mapping out of Miniflux API
This commit is contained in:
parent
16d2e01668
commit
8ad7fc81a8
2 changed files with 125 additions and 1 deletions
|
@ -40,6 +40,11 @@ class REST {
|
||||||
'strip' => '/fever/',
|
'strip' => '/fever/',
|
||||||
'class' => REST\Fever\API::class,
|
'class' => REST\Fever\API::class,
|
||||||
],
|
],
|
||||||
|
'miniflux' => [ // Miniflux https://miniflux.app/docs/api.html
|
||||||
|
'match' => '/v1/',
|
||||||
|
'strip' => '/v1',
|
||||||
|
'class' => REST\Miniflux\API::class,
|
||||||
|
],
|
||||||
// Other candidates:
|
// Other candidates:
|
||||||
// Microsub https://indieweb.org/Microsub
|
// Microsub https://indieweb.org/Microsub
|
||||||
// Google Reader http://feedhq.readthedocs.io/en/latest/api/index.html
|
// Google Reader http://feedhq.readthedocs.io/en/latest/api/index.html
|
||||||
|
@ -48,7 +53,6 @@ class REST {
|
||||||
// Selfoss https://github.com/SSilence/selfoss/wiki/Restful-API-for-Apps-or-any-other-external-access
|
// Selfoss https://github.com/SSilence/selfoss/wiki/Restful-API-for-Apps-or-any-other-external-access
|
||||||
// NewsBlur http://www.newsblur.com/api
|
// NewsBlur http://www.newsblur.com/api
|
||||||
// Unclear if clients exist:
|
// Unclear if clients exist:
|
||||||
// Miniflux https://docs.miniflux.app/en/latest/api.html#api-reference
|
|
||||||
// Nextcloud News v2 https://github.com/nextcloud/news/blob/master/docs/externalapi/External-Api.md
|
// Nextcloud News v2 https://github.com/nextcloud/news/blob/master/docs/externalapi/External-Api.md
|
||||||
// BirdReader https://github.com/glynnbird/birdreader/blob/master/API.md
|
// BirdReader https://github.com/glynnbird/birdreader/blob/master/API.md
|
||||||
// Feedbin v1 https://github.com/feedbin/feedbin-api/commit/86da10aac5f1a57531a6e17b08744e5f9e7db8a9
|
// Feedbin v1 https://github.com/feedbin/feedbin-api/commit/86da10aac5f1a57531a6e17b08744e5f9e7db8a9
|
||||||
|
|
120
lib/REST/Miniflux/V1.php
Normal file
120
lib/REST/Miniflux/V1.php
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
<?php
|
||||||
|
/** @license MIT
|
||||||
|
* Copyright 2017 J. King, Dustin Wilson et al.
|
||||||
|
* See LICENSE and AUTHORS files for details */
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\Arsse\REST\Miniflux;
|
||||||
|
|
||||||
|
use JKingWeb\Arsse\Arsse;
|
||||||
|
use JKingWeb\Arsse\Service;
|
||||||
|
use JKingWeb\Arsse\Context\Context;
|
||||||
|
use JKingWeb\Arsse\Misc\ValueInfo;
|
||||||
|
use JKingWeb\Arsse\AbstractException;
|
||||||
|
use JKingWeb\Arsse\Db\ExceptionInput;
|
||||||
|
use JKingWeb\Arsse\Feed\Exception as FeedException;
|
||||||
|
use JKingWeb\Arsse\Misc\HTTP;
|
||||||
|
use JKingWeb\Arsse\REST\Exception;
|
||||||
|
use JKingWeb\Arsse\REST\Exception404;
|
||||||
|
use JKingWeb\Arsse\REST\Exception405;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
use Laminas\Diactoros\Response\JsonResponse as Response;
|
||||||
|
use Laminas\Diactoros\Response\EmptyResponse;
|
||||||
|
|
||||||
|
class V1 extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||||
|
protected $paths = [
|
||||||
|
'/categories' => ['GET' => "getCategories", 'POST' => "createCategory"],
|
||||||
|
'/categories/1' => ['PUT' => "updateCategory", 'DELETE' => "deleteCategory"],
|
||||||
|
'/discover' => ['POST' => "discoverSubscriptions"],
|
||||||
|
'/entries' => ['GET' => "getEntries", 'PUT' => "updateEntries"],
|
||||||
|
'/entries/1' => ['GET' => "getEntry"],
|
||||||
|
'/entries/1/bookmark' => ['PUT' => "toggleEntryBookmark"],
|
||||||
|
'/export' => ['GET' => "opmlExport"],
|
||||||
|
'/feeds' => ['GET' => "getFeeds", 'POST' => "createFeed"],
|
||||||
|
'/feeds/1' => ['GET' => "getFeed", 'PUT' => "updateFeed", 'DELETE' => "removeFeed"],
|
||||||
|
'/feeds/1/entries/1' => ['GET' => "getFeedEntry"],
|
||||||
|
'/feeds/1/entries' => ['GET' => "getFeedEntries"],
|
||||||
|
'/feeds/1/icon' => ['GET' => "getFeedIcon"],
|
||||||
|
'/feeds/1/refresh' => ['PUT' => "refreshFeed"],
|
||||||
|
'/feeds/refresh' => ['PUT' => "refreshAllFeeds"],
|
||||||
|
'/healthcheck' => ['GET' => "healthCheck"],
|
||||||
|
'/import' => ['POST' => "opmlImport"],
|
||||||
|
'/me' => ['GET' => "getCurrentUser"],
|
||||||
|
'/users' => ['GET' => "getUsers", 'POST' => "createUser"],
|
||||||
|
'/users/1' => ['GET' => "getUser", 'PUT' => "updateUser", 'DELETE' => "deleteUser"],
|
||||||
|
'/users/*' => ['GET' => "getUser"],
|
||||||
|
'/version' => ['GET' => "getVersion"],
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dispatch(ServerRequestInterface $req): ResponseInterface {
|
||||||
|
// try to authenticate
|
||||||
|
if ($req->getAttribute("authenticated", false)) {
|
||||||
|
Arsse::$user->id = $req->getAttribute("authenticatedUser");
|
||||||
|
} else {
|
||||||
|
return new EmptyResponse(401);
|
||||||
|
}
|
||||||
|
// get the request path only; this is assumed to already be normalized
|
||||||
|
$target = parse_url($req->getRequestTarget())['path'] ?? "";
|
||||||
|
// handle HTTP OPTIONS requests
|
||||||
|
if ($req->getMethod() === "OPTIONS") {
|
||||||
|
return $this->handleHTTPOptions($target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function normalizePathIds(string $url): string {
|
||||||
|
$path = explode("/", $url);
|
||||||
|
// any path components which are database IDs (integers greater than zero) should be replaced with "1", for easier comparison (we don't care about the specific ID)
|
||||||
|
for ($a = 0; $a < sizeof($path); $a++) {
|
||||||
|
if (ValueInfo::id($path[$a])) {
|
||||||
|
$path[$a] = "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return implode("/", $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleHTTPOptions(string $url): ResponseInterface {
|
||||||
|
// normalize the URL path: change any IDs to 1 for easier comparison
|
||||||
|
$url = $this->normalizePathIDs($url);
|
||||||
|
if (isset($this->paths[$url])) {
|
||||||
|
// if the path is supported, respond with the allowed methods and other metadata
|
||||||
|
$allowed = array_keys($this->paths[$url]);
|
||||||
|
// if GET is allowed, so is HEAD
|
||||||
|
if (in_array("GET", $allowed)) {
|
||||||
|
array_unshift($allowed, "HEAD");
|
||||||
|
}
|
||||||
|
return new EmptyResponse(204, [
|
||||||
|
'Allow' => implode(",", $allowed),
|
||||||
|
'Accept' => self::ACCEPTED_TYPE,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// if the path is not supported, return 404
|
||||||
|
return new EmptyResponse(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function chooseCall(string $url, string $method): string {
|
||||||
|
// // normalize the URL path: change any IDs to 1 for easier comparison
|
||||||
|
$url = $this->normalizePathIds($url);
|
||||||
|
// normalize the HTTP method to uppercase
|
||||||
|
$method = strtoupper($method);
|
||||||
|
// we now evaluate the supplied URL against every supported path for the selected scope
|
||||||
|
// the URL is evaluated as an array so as to avoid decoded escapes turning invalid URLs into valid ones
|
||||||
|
if (isset($this->paths[$url])) {
|
||||||
|
// if the path is supported, make sure the method is allowed
|
||||||
|
if (isset($this->paths[$url][$method])) {
|
||||||
|
// if it is allowed, return the object method to run
|
||||||
|
return $this->paths[$url][$method];
|
||||||
|
} else {
|
||||||
|
// otherwise return 405
|
||||||
|
throw new Exception405(implode(", ", array_keys($this->paths[$url])));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the path is not supported, return 404
|
||||||
|
throw new Exception404();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue