diff --git a/composer.json b/composer.json index 260b647..e5b33b1 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,14 @@ { "name": "yggverse/dokuwiki-gemini-server", "description": "DokuWiki bridge for Gemini Protocol", - "keywords": [ "yggverse", "gemini", "gemini-protocol", "gemini-server", "wiki", "dokuwiki", "bridge", "server" ], + "keywords": [ "yggverse", "gemini", "gemini-protocol", "gemini-server", "wiki", "dokuwiki", "manticore", "bridge", "server" ], "homepage": "https://github.com/yggverse/dokuwiki-to-gemini", "type": "project", "require": { "yggverse/titan-ii": "^1.0", "yggverse/gemini": "dev-main", - "yggverse/cache": "^0.4.0" + "yggverse/cache": "^0.4.0", + "manticoresoftware/manticoresearch-php": "^3.1" }, "license": "MIT", "autoload": { diff --git a/example/config.json b/example/config.json index f580cfa..bad0051 100644 --- a/example/config.json +++ b/example/config.json @@ -16,6 +16,29 @@ "timeout":3600 } }, + "manticore": + { + "server": + { + "host":"127.0.0.1", + "port":9308 + }, + "index": + { + "document":{ + "name":"dokuwiki_gemini_server", + "settings": + { + "morphology":"stem_cz,stem_enru", + "index_exact_words":1, + "html_strip":1, + "min_word_len":3, + "min_prefix_len":3 + } + }, + "extension":"txt" + } + }, "dokuwiki": { "uri": @@ -34,6 +57,10 @@ "sections":"Sections", "pages":"Pages", "actions":"Actions", + "search":"Search", + "results":"Request results", + "found":"Found", + "nothing":"Nothing found by this request", "main":"Main page", "source":"Source", "welcome":"About", diff --git a/src/server.php b/src/server.php index ccafb55..d2b9f03 100644 --- a/src/server.php +++ b/src/server.php @@ -69,6 +69,8 @@ $memory = new \Yggverse\Cache\Memory( $config->memcached->server->timeout ); +$memory->flush(); + // Init filesystem $filesystem = new \Yggverse\Gemini\Dokuwiki\Filesystem( sprintf( @@ -85,6 +87,71 @@ $helper = new \Yggverse\Gemini\Dokuwiki\Helper( $reader ); +// Init search server +$manticore = new \Manticoresearch\Client( + [ + 'host' => $config->manticore->server->host, + 'port' => $config->manticore->server->port, + ] +); + +// Init search index +$index = $manticore->index( + $config->manticore->index->document->name +); + +$index->drop(true); +$index->create( + [ + 'uri' => + [ + 'type' => 'text' + ], + 'name' => + [ + 'type' => 'text' + ], + 'data' => + [ + 'type' => 'text' + ] + ], + (array) $config->manticore->index->document->settings +); + +foreach ($filesystem->getList() as $path) +{ + if (!str_ends_with($path, $config->manticore->index->extension)) + { + continue; + } + + if ($uri = $filesystem->getPageUriByPath($path)) + { + if ($data = file_get_contents($path)) + { + $gemini = $reader->toGemini( + $data + ); + + $index->addDocument( + [ + 'uri' => $uri, + 'name' => (string) $reader->getH1( + $gemini + ), + 'data' => (string) $reader->toGemini( + $gemini + ) + ], + crc32( + $uri + ) + ); + } + } +} + // Init server $server = new \Yggverse\TitanII\Server(); @@ -101,6 +168,7 @@ $server->setHandler( { global $config; global $memory; + global $index; global $filesystem; global $reader; global $helper; @@ -118,18 +186,135 @@ $server->setHandler( // Route begin switch ($request->getPath()) { - // Static route here + // Static routes case null: case false: case '': - // @TODO redirect to document root (/) + $response->setCode( + 30 + ); + + $response->setMeta( + sprintf( + 'gemini://%s%s/', + $config->gemini->server->host, + $config->gemini->server->port == 1965 ? null : ':' . $config->gemini->server->port + ) + ); + + return $response; break; case '/search': - // @TODO implement search feature + // Search query request + if (empty($request->getQuery())) + { + $response->setMeta( + 'text/plain' + ); + + $response->setCode( + 10 + ); + + return $response; + } + + // Prepare query + $query = trim( + urldecode( + $request->getQuery() + ) + ); + + // Do search + $results = $index->search( + @\Manticoresearch\Utils::escape( + $query + ) + )->get(); + + // Init page content + $lines = [ + PHP_EOL + ]; + + // Append page title + $lines[] = sprintf( + '# %s - %s', + $config->string->search, + $query + ); + + // Append search results + if ($total = $results->getTotal()) + { + $lines[] = sprintf( + '%s: %d', + $config->string->found, + $total + ); + + $lines[] = sprintf( + '## %s', + $config->string->results + ); + + foreach($results as $result) + { + $lines[] = sprintf( + '=> /%s %s', + $result->get('uri'), + $result->get('name') + ); + } + } + + // Nothing found + else + { + $lines[] = $config->string->nothing; + } + + // Append actions + $lines[] = sprintf( + '## %s', + $config->string->actions + ); + + // Append search link + $lines[] = sprintf( + '=> /search %s', + $config->string->search + ); + + // Append homepage link + $lines[] = sprintf( + '=> / %s', + $config->string->main + ); + + // Append source link + $lines[] = sprintf( + '=> %s %s', + $config->dokuwiki->url->source, + $config->string->source + ); + + // Append about info + $lines[] = $config->string->about; + + $response->setContent( + implode( + PHP_EOL, + $lines + ) + ); + + return $response; break; @@ -165,13 +350,13 @@ $server->setHandler( ); // Define index menu - $index = []; + $menu = []; // Append index sections if ($sections = $helper->getChildrenSectionLinksByUri($_uri)) { // Append header - $index[] = sprintf( + $menu[] = sprintf( '### %s', $config->string->sections ); @@ -179,7 +364,7 @@ $server->setHandler( // Append sections foreach ($sections as $section) { - $index[] = $section; + $menu[] = $section; } } @@ -187,7 +372,7 @@ $server->setHandler( if ($pages = $helper->getChildrenPageLinksByUri($_uri)) { // Append header - $index[] = sprintf( + $menu[] = sprintf( '### %s', $config->string->pages ); @@ -195,18 +380,18 @@ $server->setHandler( // Append pages foreach ($pages as $page) { - $index[] = $page; + $menu[] = $page; } } // Set macros value - if ($index) + if ($menu) { $reader->setRule( '/\{\{indexmenu>:([^\}]+)\}\}/i', implode( PHP_EOL, - $index + $menu ) ); @@ -214,7 +399,7 @@ $server->setHandler( '/\{\{indexmenu_n>[\d]+\}\}/i', implode( PHP_EOL, - $index + $menu ) ); } @@ -253,11 +438,15 @@ $server->setHandler( $config->string->actions ); - // Append source and homepage link + // Append search link $lines[] = sprintf( - '=> gemini://%s%s %s', - $config->gemini->server->host, - $config->gemini->server->port == 1965 ? null : ':' . $config->gemini->server->port, + '=> /search %s', + $config->string->search + ); + + // Append homepage link + $lines[] = sprintf( + '=> / %s', $config->string->main ); @@ -397,6 +586,12 @@ $server->setHandler( $config->string->resources ); + // Append search link + $lines[] = sprintf( + '=> /search %s', + $config->string->search + ); + // Append source link $lines[] = sprintf( '=> %s %s',