Browse Source

implement build-in gemini server

fs
yggverse 7 months ago
parent
commit
7275ecdacd
  1. 4
      .gitignore
  2. 34
      README.md
  3. 4
      composer.json
  4. 4
      example/crawler.json
  5. 12
      example/host.json
  6. 2
      src/crawler.php
  7. 249
      src/server.php

4
.gitignore vendored

@ -1,4 +1,4 @@
/composer.lock /composer.lock
/config.json /crawler.json
/data/ /server/
/vendor/ /vendor/

34
README.md

@ -7,8 +7,8 @@ Simple RSS feed converter to static Gemtext format, useful for news portals or l
## Usage ## Usage
1. `git clone https://github.com/YGGverse/Pulsar.git` 1. `git clone https://github.com/YGGverse/Pulsar.git`
2. `cp example/config.json config.json` - setup your feeds 2. `cp example/crawler.json crawler.json` - setup your feed locations
3. `php src/crawler.php` - create crontab schedule 3. `php src/crawler.php` - grab feeds manually or create the crontab task
## Config ## Config
@ -26,4 +26,32 @@ Configuration file supports multiple feed channels with custom settings:
* `{title}` - item title * `{title}` - item title
* `{description}` - item description * `{description}` - item description
Resulting files could be generated to the any folder for personal reading on localhost, or shared with others using [gmid](https://github.com/omar-polo/gmid), [twins](https://code.rocket9labs.com/tslocum/twins) or any other [Gemini server](https://github.com/kr1sp1n/awesome-gemini#servers). Resulting files could be generated to the any folder for personal reading on localhost, or shared with others using [gmid](https://github.com/omar-polo/gmid), [twins](https://code.rocket9labs.com/tslocum/twins) or any other [Gemini server](https://github.com/kr1sp1n/awesome-gemini#servers).
## Server
Pulsar comes with build-in [Titan-II](https://github.com/YGGverse/titan-II) server implementation.
It's especially useful for [Yggdrasil](https://github.com/yggdrasil-network/yggdrasil-go) users, who wish to host their feeds using plain IPv6 `0200::/7` addresses as the `CN` record. Build-in server contain this feature implemented from the box.
### Setup
* `cd Pulsar` - navigate to the project folder
* `composer update` - download server dependencies with Composer
* `mkdir server/127.0.0.1` - init server location (you can define any other destination, but `server` one is just git ignored)
* `cp example/host.json server/127.0.0.1/host.json` - copy configuration example to the destination folder
* `cd server/127.0.0.1` - navigate to server folder created and generate new self-signed certificate
On example above, certificate could be generated with following command:
```
openssl req -x509 -newkey rsa:4096 -keyout key.rsa -out cert.pem -days 365 -nodes -subj "/CN=127.0.0.1"
```
* _tip: for IPv6 address, just skip square brackets from `CN` value_
### Launch
* `php src/server.php server/127.0.0.1` - supported relative or absolute paths for systemd service
Open `gemini://127.0.0.1` in [Gemini browser](https://github.com/kr1sp1n/awesome-gemini#clients)!

4
composer.json

@ -15,5 +15,7 @@
"name": "YGGverse" "name": "YGGverse"
} }
], ],
"require": {} "require": {
"yggverse/titan-ii": "^1.0"
}
} }

4
example/config.json → example/crawler.json

@ -3,7 +3,7 @@
[ [
{ {
"source":"https://www.omglinux.com/feed", "source":"https://www.omglinux.com/feed",
"target":"data/omglinux.com/feed.gmi", "target":"server/127.0.0.1/public/omglinux/feed.gmi",
"item": "item":
{ {
"template":"=> {link} {title}{nl}{nl}{description}", "template":"=> {link} {title}{nl}{nl}{description}",
@ -12,7 +12,7 @@
}, },
{ {
"source":"https://omgubuntu.co.uk/feed", "source":"https://omgubuntu.co.uk/feed",
"target":"data/omgubuntu.co.uk/feed.gmi", "target":"server/127.0.0.1/public/omgubuntu/feed.gmi",
"item": "item":
{ {
"template":"=> {link} {title}{nl}{nl}{description}", "template":"=> {link} {title}{nl}{nl}{description}",

12
example/host.json

@ -0,0 +1,12 @@
{
"host":"127.0.0.1",
"port":1965,
"cert":"cert.pem",
"key":"key.rsa",
"data":
{
"directory":"public",
"index":"index.gmi",
"listing":true
}
}

2
src/crawler.php

@ -15,7 +15,7 @@ if (false === sem_acquire($semaphore, true))
// Init config // Init config
$config = json_decode( $config = json_decode(
file_get_contents( file_get_contents(
__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'config.json' __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'crawler.json'
) )
); );

249
src/server.php

@ -0,0 +1,249 @@
<?php
// Load dependencies
require_once __DIR__ .
DIRECTORY_SEPARATOR . '..'.
DIRECTORY_SEPARATOR . 'vendor' .
DIRECTORY_SEPARATOR . 'autoload.php';
// Init required arguments
if (empty($argv[1]))
{
throw new \Exception(
_('Configured hostname required as argument')
);
}
// Init server path
define(
'PULSAR_SERVER_DIRECTORY',
str_starts_with($argv[1], DIRECTORY_SEPARATOR) ? $argv[1] :
realpath(
__DIR__ .
DIRECTORY_SEPARATOR . '..' .
DIRECTORY_SEPARATOR . $argv[1]
) . DIRECTORY_SEPARATOR
);
// Init server config
if (!file_exists(PULSAR_SERVER_DIRECTORY . 'host.json'))
{
throw new \Exception(
_('Host not configured')
);
}
$config = json_decode(
file_get_contents(
PULSAR_SERVER_DIRECTORY . 'host.json'
)
);
// Init server certificate
define(
'PULSAR_SERVER_CERT',
str_starts_with(
$config->cert,
DIRECTORY_SEPARATOR
) ? $config->cert : PULSAR_SERVER_DIRECTORY . $config->cert
);
if (!file_exists(PULSAR_SERVER_CERT))
{
throw new \Exception(
_('Certificate file not found')
);
}
// Init server key
define(
'PULSAR_SERVER_KEY',
str_starts_with(
$config->key,
DIRECTORY_SEPARATOR
) ? $config->key : PULSAR_SERVER_DIRECTORY . $config->key
);
if (!file_exists(PULSAR_SERVER_KEY))
{
throw new \Exception(
_('Key file not found')
);
}
// Init data directory
define(
'PULSAR_SERVER_DATA_DIRECTORY',
str_starts_with(
$config->data->directory,
DIRECTORY_SEPARATOR
) ? $config->data->directory : PULSAR_SERVER_DIRECTORY . $config->data->directory
);
if (!is_dir(PULSAR_SERVER_DATA_DIRECTORY))
{
throw new \Exception(
_('Data directory not found')
);
}
// Init server
$server = new \Yggverse\TitanII\Server();
$server->setCert(
PULSAR_SERVER_CERT
);
$server->setKey(
PULSAR_SERVER_KEY
);
$server->setHandler(
function (\Yggverse\TitanII\Request $request): \Yggverse\TitanII\Response
{
global $config;
$response = new \Yggverse\TitanII\Response;
// Filter path request
$path = trim(
preg_replace(
[
'/\/[\.]+\//',
'/\/[\/]+\//',
],
'/',
$request->getPath()
)
);
if ($path != $request->getPath() || in_array($path, ['', null, false]))
{
$response->setCode(
30
);
$response->setMeta(
sprintf(
'gemini://%s%s/%s',
$config->host,
$config->port == 1965 ? null : ':' . $config->port,
trim(
(string) $path,
'/'
)
)
);
return $response;
}
// Directory request
if (is_dir(PULSAR_SERVER_DATA_DIRECTORY . $path))
{
// Try index
if (file_exists(PULSAR_SERVER_DATA_DIRECTORY . $path . $config->data->index))
{
$response->setContent(
file_get_contents(
PULSAR_SERVER_DATA_DIRECTORY . $path . $config->data->index
)
);
$response->setCode(
20
);
$response->setMeta(
'text/gemini; charset=utf-8'
);
return $response;
}
// Build listing
if ($config->data->listing)
{
$response->setCode(
20
);
$response->setMeta(
'text/gemini; charset=utf-8'
);
$links = [];
foreach ((array) scandir(PULSAR_SERVER_DATA_DIRECTORY . $path) as $link)
{
if (!str_starts_with($link, '.'))
{
if (is_dir(PULSAR_SERVER_DATA_DIRECTORY . $path . $link))
{
$links[] = sprintf(
'=> %s/',
$link
);
}
else
{
$links[] = sprintf(
'=> %s',
$link
);
}
}
}
$response->setContent(
implode(
PHP_EOL,
$links
)
);
return $response;
}
}
// File request
if (file_exists(PULSAR_SERVER_DATA_DIRECTORY . $path))
{
$response->setCode(
20
);
$response->setMeta(
'text/gemini; charset=utf-8'
);
$response->setContent(
file_get_contents(
PULSAR_SERVER_DATA_DIRECTORY . $path
)
);
return $response;
}
// Noting found
$response->setCode(
51
);
return $response;
}
);
// Start server
echo sprintf(
_('Server started on %s:%d'),
$config->host,
$config->port
);
$server->start(
$config->host,
$config->port
);
Loading…
Cancel
Save