diff --git a/example/config.json b/example/config.json new file mode 100644 index 0000000..92c17c3 --- /dev/null +++ b/example/config.json @@ -0,0 +1,57 @@ +{ + "geminiapp": + { + "server": + { + "host":"127.0.0.1", + "port":1965 + }, + "pagination": + { + "limit":10 + }, + "links": + [ + "https://github.com/kvazar-network/geminiapp GitHub" + ], + "string": + { + "title":"Kvazar", + "description":"Observe Kevacoin Universe", + "navigation":"Navigation", + "main":"Main", + "latest":"Latest", + "search":"Search", + "part":"Part", + "namespace":"Namespace", + "key":"Key", + "value":"Value", + "results":"Results", + "older":"Older", + "newer":"Newer", + "nothing":"Nothing" + } + }, + "manticore": + { + "server": + { + "host":"127.0.0.1", + "port":9308 + }, + "index": + { + "document":{ + "name":"kvazar", + "meta": + { + "morphology":"stem_cz,stem_enru", + "index_exact_words":1, + "html_strip":1, + "min_word_len":3, + "min_prefix_len":3 + } + } + } + } +} \ No newline at end of file diff --git a/host/.gitignore b/host/.gitignore new file mode 100644 index 0000000..c96a04f --- /dev/null +++ b/host/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore \ No newline at end of file diff --git a/src/server.php b/src/server.php new file mode 100644 index 0000000..b973bf1 --- /dev/null +++ b/src/server.php @@ -0,0 +1,510 @@ +manticore->index->document->name, + (array) $config->manticore->index->document->meta, + $config->manticore->server->host, + $config->manticore->server->port +); + +// Init server +$server = new \Yggverse\TitanII\Server(); + +$server->setCert( + __DIR__ . '/../host/' . $argv[1] . '/cert.pem' +); + +$server->setKey( + __DIR__ . '/../host/' . $argv[1] . '/key.rsa' +); + +$server->setHandler( + function (\Yggverse\TitanII\Request $request): \Yggverse\TitanII\Response + { + global $config; + global $index; + + $response = new \Yggverse\TitanII\Response(); + + $response->setCode( + 20 + ); + + $response->setMeta( + 'text/gemini; charset=utf-8' + ); + + // Route begin + switch (true) + { + case null == $request->getPath(): + case false == $request->getPath(): + case '' == $request->getPath(): + + $response->setCode( + 30 + ); + + $response->setMeta( + sprintf( + 'gemini://%s%s/', + $config->geminiapp->server->host, + $config->geminiapp->server->port == 1965 ? null : ':' . $config->geminiapp->server->port + ) + ); + + break; + + // Main + case '/' == $request->getPath(): + case preg_match('/\/(N[A-z0-9]{33})/', $request->getPath(), $ns): + + $part = 1; + $search = ''; + $namespace = null; + $h1 = []; + $filter = []; + $result = []; + + // Parse request + parse_str( + $request->getQuery(), + $attribute + ); + + if (isset($attribute['search'])) + { + $search = urldecode( + (string) $attribute['search'] + ); + } + + if (isset($attribute['part'])) + { + $part = (int) $attribute['part']; + } + + if (isset($ns[1])) + { + $namespace = $ns[1]; + } + + // Build header + if ($search) + { + $h1[] = $search; + $h1[] = $config->geminiapp->string->search; + + if ($part > 1) + { + $h1[] = sprintf( + '%s %d', + $config->geminiapp->string->part, + $part + ); + } + + $h1[] = $config->geminiapp->string->title; + } + + else if ($namespace) + { + // Find namespace alias + if ($aliases = $index->get('_KEVA_NS', $filter)) + { + foreach ($aliases as $alias) + { + if ($alias['value']) + { + $h1[] = $alias['value']; + + break; + } + } + } + + // Not found, use hash + else + { + $h1[] = $namespace; + } + + $h1[] = $config->geminiapp->string->title; + } + + else + { + if ($part > 1) + { + $h1[] = sprintf( + '%s %d', + $config->geminiapp->string->part, + $part + ); + + $h1[] = $config->geminiapp->string->title; + } + + else + { + $h1[] = $config->geminiapp->string->title; + $h1[] = $config->geminiapp->string->description; + } + } + + $result[] = sprintf( + '# %s', + implode( + ' ยท ', + $h1 + ) + ); + + // Menu + $result[] = sprintf( + '=> /search %s', + $config->geminiapp->string->search + ); + + // Append links + if ($namespace || $search || $part > 1) + { + $result[] = sprintf( + '=> / %s', + $config->geminiapp->string->main + ); + } + + else + { + foreach ($config->geminiapp->links as $link) + { + $result[] = sprintf( + '=> %s', + $link + ); + } + } + + // Get records + if ($namespace) + { + $filter = + [ + 'crc32_namespace' => crc32( + $namespace + ) + ]; + } + + $records = $index->get( + $search, + $filter, + [ + 'time' => 'desc' + ], + $part > 1 ? ($part - 1) * $config->geminiapp->pagination->limit : 0, + $config->geminiapp->pagination->limit + ); + + // Append h2 + $result[] = sprintf( + '## %s', + $search ? $config->geminiapp->string->results : $config->geminiapp->string->latest + ); + + if ($records) + { + // Append latest records + foreach ($records as $record) + { + $result[] = sprintf( + '### %s', + $record['key'] + ); + + $result[] = $record['value']; + + $result[] = sprintf( + '=> /%s %s in %d', + $record['transaction'], + date( + 'Y-m-d', + $record['time'] + ), + $record['block'] + ); + } + + // Append navigation + $result[] = sprintf( + '## %s', + $config->geminiapp->string->navigation + ); + + // Pagination + $older = []; + $newer = []; + + if ($search) + { + $older[] = sprintf( + 'search=%s', + urlencode( + $search + ) + ); + + $newer[] = sprintf( + 'search=%s', + urlencode( + $search + ) + ); + } + + $older[] = sprintf( + 'part=%d', + $part + 1 + ); + + if ($part > 1) + { + $newer[] = sprintf( + 'part=%d', + $part - 1 + ); + } + + // Links + if ($older) + { + $result[] = sprintf( + '=> /%s?%s %s', + $namespace, + implode( + '&', + $older, + ), + $config->geminiapp->string->older + ); + } + + if ($newer) + { + $result[] = sprintf( + '=> /%s?%s %s', + $namespace, + implode( + '&', + $newer, + ), + $config->geminiapp->string->newer + ); + } + } + + else + { + $result[] = $config->geminiapp->string->nothing; + } + + $response->setContent( + implode( + PHP_EOL, + $result + ) + ); + + break; + + // Transaction page + case preg_match('/\/([A-f0-9]{64})/', $request->getPath(), $attribute): + + $result = []; + + foreach ( + $index->get( + $attribute[1], + [ + 'crc32_transaction' => crc32( + $attribute[1] + ) + ], + [ + 'time' => 'desc' + ], + 0, 1 + ) as $record + ) { + if ($record['transaction'] == $attribute[1]) + { + // Header + $result[] = sprintf( + '# %s', + $record['key'] + ); + + // Body + $result[] = sprintf( + '``` %s', + $config->geminiapp->string->value + ); + + $result[] = $record['value']; + $result[] = '```'; + + $result[] = sprintf( + '%s in %d', + date( + 'Y-m-d', + $record['time'] + ), + $record['block'] + ); + + // Footer + $result[] = sprintf( + '## %s', + $config->geminiapp->string->navigation + ); + + $result[] = sprintf( + '=> /%s %s', + $record['namespace'], + $config->geminiapp->string->namespace + ); + + $result[] = sprintf( + '=> /search %s', + $config->geminiapp->string->search + ); + + $result[] = sprintf( + '=> / %s', + $config->geminiapp->string->main + ); + + break; + } + } + + $response->setContent( + implode( + PHP_EOL, + $result + ) + ); + + break; + + // Search page + case '/search' == $request->getPath(): + + if (empty($request->getQuery())) + { + $response->setCode( + 10 + ); + + $response->setMeta( + $config->geminiapp->string->search + ); + } + + else + { + $response->setCode( + 30 + ); + + $response->setMeta( + sprintf( + 'gemini://%s%s/?search=%s', + $config->geminiapp->server->host, + $config->geminiapp->server->port == 1965 ? null : ':' . $config->geminiapp->server->port, + $request->getQuery() + ) + ); + } + + break; + + // Not found + default: + + $response->setCode( + 51 + ); + + $response->setMeta( + $config->geminiapp->string->nothing + ); + } + + return $response; + } +); + +// Start server +printf( + _('Server "%s" started on %s:%d') . PHP_EOL, + $argv[1], + $config->geminiapp->server->host, + $config->geminiapp->server->port +); + +$server->start( + $config->geminiapp->server->host, + $config->geminiapp->server->port +); \ No newline at end of file