mirror of
https://github.com/kvazar-network/geminiapp.git
synced 2025-08-26 05:22:02 +00:00
initial commit
This commit is contained in:
parent
a118237621
commit
a57e1029da
57
example/config.json
Normal file
57
example/config.json
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
host/.gitignore
vendored
Normal file
2
host/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
510
src/server.php
Normal file
510
src/server.php
Normal file
@ -0,0 +1,510 @@
|
||||
<?php
|
||||
|
||||
// Load dependencies
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// Check arguments
|
||||
if (empty($argv[1]))
|
||||
{
|
||||
exit(_('Configured hostname required as argument!') . PHP_EOL);
|
||||
}
|
||||
|
||||
// Check cert exists
|
||||
if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/cert.pem'))
|
||||
{
|
||||
exit(
|
||||
sprintf(
|
||||
_('Certificate for host "%s" not found!') . PHP_EOL,
|
||||
$argv[1]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check key exists
|
||||
if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/key.rsa'))
|
||||
{
|
||||
exit(
|
||||
sprintf(
|
||||
_('Key for host "%s" not found!') . PHP_EOL,
|
||||
$argv[1]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Check host configured
|
||||
if (!file_exists(__DIR__ . '/../host/' . $argv[1] . '/config.json'))
|
||||
{
|
||||
exit(
|
||||
sprintf(
|
||||
_('Host "%s" not configured!') . PHP_EOL,
|
||||
$argv[1]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Init config
|
||||
$config = json_decode(
|
||||
file_get_contents(
|
||||
__DIR__ . '/../host/' . $argv[1] . '/config.json'
|
||||
)
|
||||
);
|
||||
|
||||
// Init index
|
||||
$index = new \Kvazar\Index\Manticore(
|
||||
$config->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
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user