diff --git a/arsse.php b/arsse.php new file mode 100644 index 00000000..b5507834 --- /dev/null +++ b/arsse.php @@ -0,0 +1,10 @@ +watch(); +} else { + (new REST)->dispatch(); +} \ No newline at end of file diff --git a/autoload.php b/autoload.php deleted file mode 100644 index 260e06ac..00000000 --- a/autoload.php +++ /dev/null @@ -1,3 +0,0 @@ -apis[$api]['class']; $drv = new $class(); $out = $drv->dispatch($req); - echo "Status: ".$out->code."\n"; - echo json_encode($out->payload,\JSON_PRETTY_PRINT); + header("Status: ".$out->code." ".Data::$lang->msg("HTTP.Status.".$out->code)); + if(!is_null($out->payload)) { + header("Content-Type: ".$out->type); + switch($out->type) { + case REST\Response::T_JSON: + $body = json_encode($out->payload,\JSON_PRETTY_PRINT); + break; + default: + $body = (string) $out->payload; + break; + } + } + foreach($out->fields as $field) { + header($field); + } + echo $body; return true; } @@ -49,6 +65,6 @@ class REST { if(strpos($url, $api['match'])===0) return $id; } // or throw an exception otherwise - throw new REST\ExceptionURL("apiNotSupported", $url); + throw new REST\Exception501(); } } \ No newline at end of file diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index 8cca370d..966cc48f 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -13,6 +13,7 @@ use JKingWeb\Arsse\REST\Exception405; class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { const REALM = "NextCloud News API v1-2"; + const VERSION = "11.0.5"; protected $dateFormat = "unix"; @@ -110,12 +111,19 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { 'star/multiple' => ['PUT' => "articleMarkStarredMulti"], 'unstar/multiple' => ['PUT' => "articleMarkStarredMulti"], ], - 'cleanup' => [], - 'version' => [ - '' => ['GET' => "versionReport"], + 'cleanup' => [ + 'before-update' => ['GET' => "cleanupBefore"], + 'after-update' => ['GET' => "cleanupAfter"], + ], + 'version' => [ + '' => ['GET' => "serverVersion"], + ], + 'status' => [ + '' => ['GET' => "serverStatus"], + ], + 'user' => [ + '' => ['GET' => "userStatus"], ], - 'status' => [], - 'user' => [], ]; // the first path element is the overall scope of the request $scope = $url[0]; @@ -284,11 +292,6 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } return new Response(204); } - - // return the server version - protected function versionReport(array $url, array $data): Response { - return new Response(200, ['version' => \JKingWeb\Arsse\VERSION]); - } // return list of feeds which should be refreshed protected function feedListStale(array $url, array $data): Response { @@ -461,7 +464,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $c->reverse(true); } // set the edition mark-off; the database uses an or-equal comparison for internal consistency, but the protocol does not, so we must adjust by one - if(isset($data['offset'])) { + if(isset($data['offset']) && $data['offset'] > 0) { if($c->reverse) { $c->latestEdition($data['offset'] - 1); } else { @@ -590,4 +593,49 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { $t->commit(); return new Response(204); } + + protected function userStatus(array $url, array $data): Response { + // FIXME: stub + $data = Data::$db->userPropertiesGet(Data::$user->id); + $out = [ + 'userId' => Data::$user->id, + 'displayName' => $data['name'] ?? Data::$user->id, + 'lastLoginTimestamp' => time(), + 'avatar' => null, + ]; + return new Response(200, $out); + } + + protected function cleanupBefore(array $url, array $data): Response { + // function requires admin rights per spec + if(Data::$user->rightsGet(Data::$user->id)==User::RIGHTS_NONE) return new Response(403); + // FIXME: stub + return new Response(204); + } + + protected function cleanupAfter(array $url, array $data): Response { + // function requires admin rights per spec + if(Data::$user->rightsGet(Data::$user->id)==User::RIGHTS_NONE) return new Response(403); + // FIXME: stub + return new Response(204); + } + + // return the server version + protected function serverVersion(array $url, array $data): Response { + return new Response(200, [ + 'version' => self::VERSION, + 'arsse_version' => \JKingWeb\Arsse\VERSION, + ]); + } + + protected function serverStatus(array $url, array $data): Response { + // FIXME: stub + return new Response(200, [ + 'version' => self::VERSION, + 'arsse_version' => \JKingWeb\Arsse\VERSION, + 'warnings' => [ + 'improperlyConfiguredCron' => false, + ] + ]); + } } \ No newline at end of file diff --git a/lib/Service.php b/lib/Service.php new file mode 100644 index 00000000..3cb838f5 --- /dev/null +++ b/lib/Service.php @@ -0,0 +1,42 @@ +serviceDriver; + $this->drv = new $driver(); + $this->interval = new \DateInterval(Data::$conf->serviceFrequency); // FIXME: this needs to fall back in case of incorrect input + } + + function watch() { + while(true) { + $t = new \DateTime(); + $list = Data::$db->feedListStale(); + if($list) { + echo date("H:i:s")." Updating feeds ".json_encode($list)."\n"; + // TODO: pre-cleanup + $this->drv->queue(...$list); + $this->drv->exec(); + $this->drv->clean(); + // TODO: post-cleanup + } else { + echo date("H:i:s")." No feeds to update; sleeping\n"; + } + $t->add($this->interval); + do { + @time_sleep_until($t->getTimestamp()); + } while($t->getTimestamp() > time()); + } + } +} \ No newline at end of file diff --git a/lib/Service/Driver.php b/lib/Service/Driver.php new file mode 100644 index 00000000..fe8f0102 --- /dev/null +++ b/lib/Service/Driver.php @@ -0,0 +1,11 @@ +msg("Driver.Service.Internal.Name"); + } + + static function requirementsMet(): bool { + // this driver has no requirements + return true; + } + + function __construct() { + } + + function queue(int ...$feeds): int { + $this->queue = array_merge($this->queue, $feeds); + return sizeof($this->queue); + } + + function exec(): int { + while(sizeof($this->queue)) { + $id = array_shift($this->queue); + Data::$db->feedUpdate($id); + } + return Data::$conf->serviceQueueWidth - sizeof($this->queue); + } + + function clean(): bool { + $this->queue = []; + return true; + } +} \ No newline at end of file diff --git a/lib/User/Internal/Driver.php b/lib/User/Internal/Driver.php index d6b3167a..98eabc72 100644 --- a/lib/User/Internal/Driver.php +++ b/lib/User/Internal/Driver.php @@ -1,23 +1,22 @@ Iface::FUNC_INTERNAL, - "userList" => Iface::FUNC_INTERNAL, - "userExists" => Iface::FUNC_INTERNAL, - "userAdd" => Iface::FUNC_INTERNAL, - "userRemove" => Iface::FUNC_INTERNAL, - "userPasswordSet" => Iface::FUNC_INTERNAL, - "userPropertiesGet" => Iface::FUNC_INTERNAL, - "userPropertiesSet" => Iface::FUNC_INTERNAL, - "userRightsGet" => Iface::FUNC_INTERNAL, - "userRightsSet" => Iface::FUNC_INTERNAL, + "auth" => self::FUNC_INTERNAL, + "userList" => self::FUNC_INTERNAL, + "userExists" => self::FUNC_INTERNAL, + "userAdd" => self::FUNC_INTERNAL, + "userRemove" => self::FUNC_INTERNAL, + "userPasswordSet" => self::FUNC_INTERNAL, + "userPropertiesGet" => self::FUNC_INTERNAL, + "userPropertiesSet" => self::FUNC_INTERNAL, + "userRightsGet" => self::FUNC_INTERNAL, + "userRightsSet" => self::FUNC_INTERNAL, ]; static public function driverName(): string { @@ -29,7 +28,7 @@ final class Driver implements Iface { if(array_key_exists($function, $this->functions)) { return $this->functions[$function]; } else { - return Iface::FUNC_NOT_IMPLEMENTED; + return self::FUNC_NOT_IMPLEMENTED; } } diff --git a/locale/en.php b/locale/en.php index 3fe20191..5b6ce454 100644 --- a/locale/en.php +++ b/locale/en.php @@ -1,8 +1,9 @@ 'Internal', - 'Driver.Db.SQLite3.Name' => 'SQLite 3', + 'Driver.Service.Curl.Name' => 'HTTP (curl)', + 'Driver.Service.Internal.Name' => 'Internal', + 'Driver.User.Internal.Name' => 'Internal', 'HTTP.Status.200' => 'OK', 'HTTP.Status.204' => 'No Content',