diff --git a/.gitignore b/.gitignore index 51ec49a..afb4696 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ vendor/ documentation/ tests/coverage arsse.db* +config.php # Windows image file caches Thumbs.db diff --git a/README.md b/README.md index e158696..ee7e720 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,7 @@ sudo systemctl start arsse ### Configuring the Web server -A sample configuration file for Nginx can be found in `arsse/dist/nginx.conf` - -FIXME: stub +Sample configuration parameters for Nginx can be found in `arsse/dist/nginx.conf` and `arsse/dist/nginx-fcgi.conf`. ## Installation from source diff --git a/arsse.php b/arsse.php index 0765874..5ff4623 100644 --- a/arsse.php +++ b/arsse.php @@ -5,12 +5,7 @@ require_once __DIR__.DIRECTORY_SEPARATOR."bootstrap.php"; if(\PHP_SAPI=="cli") { // initialize the CLI; this automatically handles --help and --version $cli = new CLI; - // load configuration - Arsse::load(new Conf()); - if(file_exists(BASE."config.php")) { - Arsse::$conf->importFile(BASE."config.php"); - } - // handle CLI requests + // handle other CLI requests; some do not require configuration $cli->dispatch(); } else { // load configuration diff --git a/dist/nginx-fcgi.conf b/dist/nginx-fcgi.conf new file mode 100644 index 0000000..fc76d31 --- /dev/null +++ b/dist/nginx-fcgi.conf @@ -0,0 +1,12 @@ +fastcgi_pass php; # PHP is assumed to already be configured for FastCGI operation +fastcgi_pass_header Authorization; # required if the Arsse is to perform its own HTTP authentication +fastcgi_pass_request_body on; +fastcgi_pass_request_headers on; +fastcgi_intercept_errors off; +fastcgi_buffering off; +fastcgi_param SCRIPT_FILENAME /usr/share/arsse/arsse.php; +fastcgi_param REQUEST_METHOD $request_method; +fastcgi_param CONTENT_TYPE $content_type; +fastcgi_param CONTENT_LENGTH $content_length; +fastcgi_param REQUEST_URI $request_uri; +fastcgi_param HTTPS $https if_not_empty; \ No newline at end of file diff --git a/dist/nginx.conf b/dist/nginx.conf new file mode 100644 index 0000000..f1aaca4 --- /dev/null +++ b/dist/nginx.conf @@ -0,0 +1,24 @@ +server { + server_name news.example.com; + listen 80; # adding HTTPS configuration is highly recommended + # redirect to HTTPS, if desired + #if ($https != "on") {rewrite ^ https://$host$request_uri;} + # the userPreAuth setting should be enabled if the Web server is handling authentication + #auth_basic "Advanced RSS Environment"; + root /usr/share/arsse/www; + index index.html; + + location / { + try_files $uri $uri/ =404; + } + + location /index.php/apps/news/api { + include /usr/share/arsse/dist/nginx-fcgi.conf; + + location ~ ^/index\.php/apps/news/api/?$ { + auth_basic off; # the NextCloud News API version enumerator does not require authentication + include /usr/share/arsse/dist/nginx-fcgi.conf; + } + } + +} \ No newline at end of file diff --git a/lib/CLI.php b/lib/CLI.php index f765ac7..7422c85 100644 --- a/lib/CLI.php +++ b/lib/CLI.php @@ -11,11 +11,13 @@ class CLI { Usage: $prog daemon $prog feed refresh + $prog conf save-defaults $prog --version $prog --help | -h The Arsse command-line interface currently allows you to start the refresh -daemon or refresh a specific feed by numeric ID. +daemon, refresh a specific feed by numeric ID, or save default configuration +to a sample file. USAGE_TEXT; } @@ -30,15 +32,28 @@ USAGE_TEXT; ]); } + protected function loadConf(): bool { + // FIXME: this should be a method of the Conf class + Arsse::load(new Conf()); + if(file_exists(BASE."config.php")) { + Arsse::$conf->importFile(BASE."config.php"); + } + return true; + } + function dispatch(array $args = null): int { // act on command line if(is_null($args)) { $args = $this->args; } if($args['daemon']) { + $this->loadConf(); return $this->daemon(); - } elseif($args['feed'] && $args['refresh']) { + } else if($args['feed'] && $args['refresh']) { + $this->loadConf(); return $this->feedRefresh((int) $args['']); + } else if($args['conf'] && $args['save-defaults']) { + return $this->confSaveDefaults($args['']); } } @@ -50,4 +65,8 @@ USAGE_TEXT; protected function feedRefresh(int $id): int { return (int) !Arsse::$db->feedUpdate($id); // FIXME: exception error codes should be returned here } + + protected function confSaveDefaults($file): int { + return (int) !(new Conf)->exportFile($file, true); + } } \ No newline at end of file diff --git a/lib/Conf.php b/lib/Conf.php index 67fb30f..bd6a620 100644 --- a/lib/Conf.php +++ b/lib/Conf.php @@ -13,8 +13,6 @@ class Conf { /** @var string Class of the database driver in use (SQLite3 by default) */ public $dbDriver = Db\SQLite3\Driver::class; - /** @var string Base path to database schema files */ - public $dbSchemaBase = BASE.'sql'; /** @var boolean Whether to attempt to automatically update the database when updated to a new version with schema changes */ public $dbAutoUpdate = true; /** @var string Full path and file name of SQLite database (if using SQLite) */ diff --git a/lib/Db/SQLite3/Driver.php b/lib/Db/SQLite3/Driver.php index bdf3b4d..b6fb02d 100644 --- a/lib/Db/SQLite3/Driver.php +++ b/lib/Db/SQLite3/Driver.php @@ -73,7 +73,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { return $this->query("PRAGMA user_version")->getValue(); } - public function schemaUpdate(int $to): bool { + public function schemaUpdate(int $to, string $basePath = null): bool { $ver = $this->schemaVersion(); if(!Arsse::$conf->dbAutoUpdate) { throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]); @@ -81,7 +81,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver { throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]); } $sep = \DIRECTORY_SEPARATOR; - $path = Arsse::$conf->dbSchemaBase.$sep."SQLite3".$sep; + $path = ($basePath ?? \JKingWeb\Arsse\BASE."sql").$sep."SQLite3".$sep; // lock the database $this->savepointCreate(true); for($a = $this->schemaVersion(); $a < $to; $a++) { diff --git a/lib/REST/NextCloudNews/V1_2.php b/lib/REST/NextCloudNews/V1_2.php index a9fa745..cf1d686 100644 --- a/lib/REST/NextCloudNews/V1_2.php +++ b/lib/REST/NextCloudNews/V1_2.php @@ -634,7 +634,7 @@ class V1_2 extends \JKingWeb\Arsse\REST\AbstractHandler { } protected function userStatus(array $url, array $data): Response { - $data = Arsse::$user::propertiesGet(Arsse::$user->id, true); + $data = Arsse::$user->propertiesGet(Arsse::$user->id, true); // construct the avatar structure, if an image is available if(isset($data['avatar'])) { $avatar = [ diff --git a/tests/Conf/TestConf.php b/tests/Conf/TestConf.php index 8890d52..5f99d02 100644 --- a/tests/Conf/TestConf.php +++ b/tests/Conf/TestConf.php @@ -93,10 +93,10 @@ class TestConf extends Test\AbstractTest { function testExportToArray() { $conf = new Conf(); $conf->lang = ["en", "fr"]; // should not be exported: not scalar - $conf->dbSchemaBase = "schema"; // should be exported: value changed + $conf->dbSQLite3File = "test.db"; // should be exported: value changed $conf->someCustomProperty = "Look at me!"; // should be exported: unknown property $exp = [ - 'dbSchemaBase' => "schema", + 'dbSQLite3File' => "test.db", 'someCustomProperty' => "Look at me!", ]; $this->assertSame($exp, $conf->export()); @@ -110,12 +110,12 @@ class TestConf extends Test\AbstractTest { function testExportToFile() { $conf = new Conf(); $conf->lang = ["en", "fr"]; // should not be exported: not scalar - $conf->dbSchemaBase = "schema"; // should be exported: value changed + $conf->dbSQLite3File = "test.db"; // should be exported: value changed $conf->someCustomProperty = "Look at me!"; // should be exported: unknown property $conf->exportFile(self::$path."confNotArray"); $arr = (include self::$path."confNotArray"); $exp = [ - 'dbSchemaBase' => "schema", + 'dbSQLite3File' => "test.db", 'someCustomProperty' => "Look at me!", ]; $this->assertSame($exp, $arr); diff --git a/tests/Db/SQLite3/TestDbUpdateSQLite3.php b/tests/Db/SQLite3/TestDbUpdateSQLite3.php index f8a99ce..8946588 100644 --- a/tests/Db/SQLite3/TestDbUpdateSQLite3.php +++ b/tests/Db/SQLite3/TestDbUpdateSQLite3.php @@ -26,10 +26,10 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { $conf = new Conf(); } $conf->dbDriver = Db\SQLite3\Driver::class; - $conf->dbSchemaBase = $this->vfs->url(); $conf->dbSQLite3File = ":memory:"; Arsse::$conf = $conf; - $this->base = $this->vfs->url()."/SQLite3/"; + $this->base = $this->vfs->url(); + $this->path = $this->base."/SQLite3/"; $this->drv = new Db\SQLite3\Driver(true); } @@ -42,40 +42,40 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { function testLoadMissingFile() { $this->assertException("updateFileMissing", "Db"); - $this->drv->schemaUpdate(1); + $this->drv->schemaUpdate(1, $this->base); } function testLoadUnreadableFile() { - touch($this->base."0.sql"); - chmod($this->base."0.sql", 0000); + touch($this->path."0.sql"); + chmod($this->path."0.sql", 0000); $this->assertException("updateFileUnreadable", "Db"); - $this->drv->schemaUpdate(1); + $this->drv->schemaUpdate(1, $this->base); } function testLoadCorruptFile() { - file_put_contents($this->base."0.sql", "This is a corrupt file"); + file_put_contents($this->path."0.sql", "This is a corrupt file"); $this->assertException("updateFileError", "Db"); - $this->drv->schemaUpdate(1); + $this->drv->schemaUpdate(1, $this->base); } function testLoadIncompleteFile() { - file_put_contents($this->base."0.sql", "create table arsse_meta(key text primary key not null, value text);"); + file_put_contents($this->path."0.sql", "create table arsse_meta(key text primary key not null, value text);"); $this->assertException("updateFileIncomplete", "Db"); - $this->drv->schemaUpdate(1); + $this->drv->schemaUpdate(1, $this->base); } function testLoadCorrectFile() { - file_put_contents($this->base."0.sql", self::MINIMAL1); - $this->drv->schemaUpdate(1); + file_put_contents($this->path."0.sql", self::MINIMAL1); + $this->drv->schemaUpdate(1, $this->base); $this->assertEquals(1, $this->drv->schemaVersion()); } function testPerformPartialUpdate() { - file_put_contents($this->base."0.sql", self::MINIMAL1); - file_put_contents($this->base."1.sql", ""); + file_put_contents($this->path."0.sql", self::MINIMAL1); + file_put_contents($this->path."1.sql", ""); $this->assertException("updateFileIncomplete", "Db"); try { - $this->drv->schemaUpdate(2); + $this->drv->schemaUpdate(2, $this->base); } catch(Exception $e) { $this->assertEquals(1, $this->drv->schemaVersion()); throw $e; @@ -83,14 +83,13 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { } function testPerformSequentialUpdate() { - file_put_contents($this->base."0.sql", self::MINIMAL1); - file_put_contents($this->base."1.sql", self::MINIMAL2); - $this->drv->schemaUpdate(2); + file_put_contents($this->path."0.sql", self::MINIMAL1); + file_put_contents($this->path."1.sql", self::MINIMAL2); + $this->drv->schemaUpdate(2, $this->base); $this->assertEquals(2, $this->drv->schemaVersion()); } function testPerformActualUpdate() { - Arsse::$conf->dbSchemaBase = (new Conf())->dbSchemaBase; $this->drv->schemaUpdate(Database::SCHEMA_VERSION); $this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion()); } @@ -106,6 +105,6 @@ class TestDbUpdateSQLite3 extends Test\AbstractTest { function testDeclineDowngrade() { $this->assertException("updateTooNew", "Db"); - $this->drv->schemaUpdate(-1); + $this->drv->schemaUpdate(-1, $this->base); } } \ No newline at end of file diff --git a/www/arsse.php b/www/arsse.php deleted file mode 100644 index 78c2824..0000000 --- a/www/arsse.php +++ /dev/null @@ -1,2 +0,0 @@ -