support for nodeinfo
parent
073fba5312
commit
fed5224f46
|
@ -29,7 +29,7 @@ This will be changed, but works for the current develop verison.
|
||||||
|
|
||||||
If the include redis cache is enabled, create a users.acl for redis with the content:
|
If the include redis cache is enabled, create a users.acl for redis with the content:
|
||||||
|
|
||||||
user federator on ~u_* +get +set >redis*change*password
|
user federator on ~u_* +get +set ~s_* +get +setex ~m_* +get +setex >redis*change*password
|
||||||
|
|
||||||
change password in the rediscache.ini to match your given password.
|
change password in the rediscache.ini to match your given password.
|
||||||
|
|
||||||
|
|
|
@ -94,6 +94,10 @@ class Api extends Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch ($this->paths[0]) {
|
switch ($this->paths[0]) {
|
||||||
|
case '.well-known':
|
||||||
|
case 'nodeinfo':
|
||||||
|
$handler = new Api\WellKnown($this);
|
||||||
|
break;
|
||||||
case 'v1':
|
case 'v1':
|
||||||
switch ($this->paths[1]) {
|
switch ($this->paths[1]) {
|
||||||
case 'dummy':
|
case 'dummy':
|
||||||
|
@ -101,9 +105,6 @@ class Api extends Main
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '.well-known':
|
|
||||||
$handler = new Api\WellKnown($this);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
$printresponse = true;
|
$printresponse = true;
|
||||||
if ($handler !== null) {
|
if ($handler !== null) {
|
||||||
|
@ -126,14 +127,13 @@ class Api extends Main
|
||||||
header($name . ': ' . $value);
|
header($name . ': ' . $value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($this->responseCode != 200) {
|
||||||
|
http_response_code($this->responseCode);
|
||||||
|
}
|
||||||
if ($printresponse) {
|
if ($printresponse) {
|
||||||
if ($this->redirect !== null) {
|
if ($this->redirect !== null) {
|
||||||
header("Location: $this->redirect");
|
header("Location: $this->redirect");
|
||||||
}
|
}
|
||||||
// @phan-suppress-next-line PhanSuspiciousValueComparison
|
|
||||||
if ($this->responseCode != 200) {
|
|
||||||
http_response_code($this->responseCode);
|
|
||||||
}
|
|
||||||
if ($this->responseCode != 404) {
|
if ($this->responseCode != 404) {
|
||||||
header("Content-type: " . $this->contentType);
|
header("Content-type: " . $this->contentType);
|
||||||
header("Access-Control-Allow-Origin: *");
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
|
|
@ -51,9 +51,17 @@ class WellKnown implements APIInterface
|
||||||
case 'GET':
|
case 'GET':
|
||||||
switch (sizeof($paths)) {
|
switch (sizeof($paths)) {
|
||||||
case 2:
|
case 2:
|
||||||
if ($paths[1] === 'webfinger') {
|
if ($paths[0] === 'nodeinfo') {
|
||||||
$wf = new WellKnown\WebFinger($this, $this->main);
|
$ni = new WellKnown\NodeInfo($this, $this->main);
|
||||||
return $wf->exec();
|
return $ni->exec($paths);
|
||||||
|
}
|
||||||
|
switch ($paths[1]) {
|
||||||
|
case 'nodeinfo':
|
||||||
|
$ni = new WellKnown\NodeInfo($this, $this->main);
|
||||||
|
return $ni->exec($paths);
|
||||||
|
case 'webfinger':
|
||||||
|
$wf = new WellKnown\WebFinger($this, $this->main);
|
||||||
|
return $wf->exec();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\Api\WellKnown;
|
||||||
|
|
||||||
|
class NodeInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* parent instance
|
||||||
|
*
|
||||||
|
* @var \Federator\Api\WellKnown $wellKnown
|
||||||
|
*/
|
||||||
|
private $wellKnown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main instance
|
||||||
|
*
|
||||||
|
* @var \Federator\Main $main
|
||||||
|
*/
|
||||||
|
private $main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @param \Federator\Api\WellKnown $wellKnown parent instance
|
||||||
|
* @param \Federator\Main $main main instance
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($wellKnown, $main)
|
||||||
|
{
|
||||||
|
$this->wellKnown = $wellKnown;
|
||||||
|
$this->main = $main;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle nodeinfo request
|
||||||
|
*
|
||||||
|
* @param string[] $paths path of us
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
public function exec($paths)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'fqdn' => $_SERVER['SERVER_NAME']
|
||||||
|
];
|
||||||
|
$template = null;
|
||||||
|
if (sizeof($paths) == 2 && $paths[0] === '.well-known' && $paths[1] === 'nodeinfo') {
|
||||||
|
$template = 'nodeinfo.json';
|
||||||
|
} else {
|
||||||
|
if ($paths[0] !== 'nodeinfo') {
|
||||||
|
throw new \Federator\Exceptions\FileNotFound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($template === null) {
|
||||||
|
switch ($paths[1]) {
|
||||||
|
case '2.1':
|
||||||
|
$template = 'nodeinfo2.1.json';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$template = 'nodeinfo2.0.json';
|
||||||
|
}
|
||||||
|
$stats = \Federator\DIO\Stats::getStats($this->main);
|
||||||
|
echo "fetch usercount via connector\n";
|
||||||
|
$data['usercount'] = $stats->userCount;
|
||||||
|
$data['postcount'] = $stats->postCount;
|
||||||
|
$data['commentcount'] = $stats->commentCount;
|
||||||
|
}
|
||||||
|
$tpl = $this->main->renderTemplate($template, $data);
|
||||||
|
$this->wellKnown->setResponse($tpl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,14 @@ namespace Federator\Cache;
|
||||||
*/
|
*/
|
||||||
interface Cache extends \Federator\Connector\Connector
|
interface Cache extends \Federator\Connector\Connector
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* save remote stats
|
||||||
|
*
|
||||||
|
* @param \Federator\Data\Stats $stats stats to save
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function saveRemoteStats($stats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save remote user by given name
|
* save remote user by given name
|
||||||
*
|
*
|
||||||
|
|
|
@ -29,4 +29,11 @@ interface Connector
|
||||||
* @return \Federator\Data\User | false
|
* @return \Federator\Data\User | false
|
||||||
*/
|
*/
|
||||||
public function getRemoteUserBySession(string $_session, string $_user);
|
public function getRemoteUserBySession(string $_session, string $_user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get statistics from remote system
|
||||||
|
*
|
||||||
|
* @return \Federator\Data\Stats|false
|
||||||
|
*/
|
||||||
|
public function getRemoteStats();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* storage class for statistics
|
||||||
|
*/
|
||||||
|
class Stats
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* user count on the external system
|
||||||
|
*
|
||||||
|
* @var int $userCount
|
||||||
|
*/
|
||||||
|
public $userCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post count on the external system
|
||||||
|
*
|
||||||
|
* @var int $postCount
|
||||||
|
*/
|
||||||
|
public $postCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* comment count on the external system
|
||||||
|
*
|
||||||
|
* @var int $commentCount
|
||||||
|
*/
|
||||||
|
public $commentCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create new user object from json string
|
||||||
|
*
|
||||||
|
* @param string $input input string
|
||||||
|
* @return Stats
|
||||||
|
*/
|
||||||
|
public static function createFromJson($input)
|
||||||
|
{
|
||||||
|
$stats = new Stats();
|
||||||
|
$data = json_decode($input, true);
|
||||||
|
if ($data === null) {
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
$stats->userCount = $data['userCount'];
|
||||||
|
$stats->postCount = $data['postCount'];
|
||||||
|
$stats->commentCount = $data['commentCount'];
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* convert internal data to json string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function toJson()
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'userCount' => $this->userCount,
|
||||||
|
'postCount' => $this->postCount,
|
||||||
|
'commentCount' => $this->commentCount
|
||||||
|
];
|
||||||
|
return json_encode($data) | '';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\DIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IO functions related to stats
|
||||||
|
*/
|
||||||
|
class Stats
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get remote stats
|
||||||
|
*
|
||||||
|
* @param \Federator\Main $main
|
||||||
|
* main instance
|
||||||
|
* @return \Federator\Data\Stats
|
||||||
|
*/
|
||||||
|
public static function getStats($main)
|
||||||
|
{
|
||||||
|
$cache = $main->getCache();
|
||||||
|
// ask cache
|
||||||
|
if ($cache !== null) {
|
||||||
|
$stats = $cache->getRemoteStats();
|
||||||
|
if ($stats !== false) {
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$connector = $main->getConnector();
|
||||||
|
// ask connector for stats
|
||||||
|
$stats = $connector->getRemoteStats();
|
||||||
|
if ($cache !== null && $stats !== false) {
|
||||||
|
$cache->saveRemoteStats($stats);
|
||||||
|
}
|
||||||
|
if ($stats === false) {
|
||||||
|
$stats = new \Federator\Data\Stats();
|
||||||
|
}
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,6 +37,30 @@ class ContentNation implements Connector
|
||||||
$this->service = $config['service-uri'];
|
$this->service = $config['service-uri'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get statistics from remote system
|
||||||
|
*
|
||||||
|
* @return \Federator\Data\Stats|false
|
||||||
|
*/
|
||||||
|
public function getRemoteStats()
|
||||||
|
{
|
||||||
|
$remoteURL = $this->service . '/api/stats';
|
||||||
|
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, []);
|
||||||
|
if ($info['http_code'] != 200) {
|
||||||
|
print_r($info);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$r = json_decode($response, true);
|
||||||
|
if ($r === false || $r === null || !is_array($r)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stats = new \Federator\Data\Stats();
|
||||||
|
$stats->userCount = array_key_exists('userCount', $r) ? $r['userCount'] : 0;
|
||||||
|
$stats->postCount = array_key_exists('pageCount', $r) ? $r['pageCount'] : 0;
|
||||||
|
$stats->commentCount = array_key_exists('commentCount', $r) ? $r['commentCount'] : 0;
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get remote user by given name
|
* get remote user by given name
|
||||||
*
|
*
|
||||||
|
|
|
@ -19,6 +19,20 @@ class DummyConnector implements Connector
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get statistics from remote system
|
||||||
|
*
|
||||||
|
* @return \Federator\Data\Stats|false
|
||||||
|
*/
|
||||||
|
public function getRemoteStats()
|
||||||
|
{
|
||||||
|
$stats = new \Federator\Data\Stats();
|
||||||
|
$stats->userCount = 9;
|
||||||
|
$stats->postCount = 11;
|
||||||
|
$stats->commentCount = 13;
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get remote user by name
|
* get remote user by name
|
||||||
* @param string $_name user or profile name
|
* @param string $_name user or profile name
|
||||||
|
|
|
@ -77,6 +77,25 @@ class RedisCache implements Cache
|
||||||
return $prefix . '_' . md5($input);
|
return $prefix . '_' . md5($input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get statistics from remote system
|
||||||
|
*
|
||||||
|
* @return \Federator\Data\Stats|false
|
||||||
|
*/
|
||||||
|
public function getRemoteStats()
|
||||||
|
{
|
||||||
|
if (!$this->connected) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
$key = 'm_stats';
|
||||||
|
$data = $this->redis->get($key);
|
||||||
|
if ($data === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stats = \Federator\Data\Stats::createFromJson($data);
|
||||||
|
return $stats;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get remote user by given name
|
* get remote user by given name
|
||||||
*
|
*
|
||||||
|
@ -119,7 +138,22 @@ class RedisCache implements Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* save remote user by namr
|
* save remote stats
|
||||||
|
*
|
||||||
|
* @param \Federator\Data\Stats $stats stats to save
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function saveRemoteStats($stats)
|
||||||
|
{
|
||||||
|
if (!$this->connected) {
|
||||||
|
$this->connect();
|
||||||
|
}
|
||||||
|
$key = 'm_stats';
|
||||||
|
$serialized = $stats->toJson();
|
||||||
|
$this->redis->setEx($key, $this->config['statsttl'], $serialized);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* save remote user by name
|
||||||
*
|
*
|
||||||
* @param string $_name user/profile name
|
* @param string $_name user/profile name
|
||||||
* @param \Federator\Data\User $user user data
|
* @param \Federator\Data\User $user user data
|
||||||
|
@ -129,7 +163,7 @@ class RedisCache implements Cache
|
||||||
{
|
{
|
||||||
$key = self::createKey('u', $_name);
|
$key = self::createKey('u', $_name);
|
||||||
$serialized = $user->toJson();
|
$serialized = $user->toJson();
|
||||||
$this->redis->setEx($key, $this->userTTL, $serialized,);
|
$this->redis->setEx($key, $this->userTTL, $serialized);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,3 +4,4 @@ port = 6379
|
||||||
username = federator
|
username = federator
|
||||||
password = redis*change*password
|
password = redis*change*password
|
||||||
userttl = 10
|
userttl = 10
|
||||||
|
statsttl = 60
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
{ldelim}
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||||
|
"href": "https://{$fqdn}/nodeinfo/2.1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
{rdelim}}
|
|
@ -0,0 +1,29 @@
|
||||||
|
{ldelim}
|
||||||
|
"version": "2.0",
|
||||||
|
"software": {ldelim}
|
||||||
|
"name": "federator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
{rdelim},
|
||||||
|
"protocols": [
|
||||||
|
"activitypub"
|
||||||
|
],
|
||||||
|
"relay": "none",
|
||||||
|
"services": {ldelim}
|
||||||
|
"inbound": [],
|
||||||
|
"outbound": [
|
||||||
|
"atom1.0",
|
||||||
|
"rss2.0"
|
||||||
|
]
|
||||||
|
{rdelim},
|
||||||
|
"openRegistrations": true,
|
||||||
|
"usage": {ldelim}
|
||||||
|
"users": {ldelim}
|
||||||
|
"total": {$usercount}
|
||||||
|
{* "activeMonth": activemonth,
|
||||||
|
"activeHalfyear": activehalfyear *}
|
||||||
|
{rdelim},
|
||||||
|
"localPosts": {$postcount},
|
||||||
|
"localComments": {$commentcount}
|
||||||
|
{rdelim},
|
||||||
|
"metadata": {ldelim}{rdelim}
|
||||||
|
{rdelim}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{ldelim}
|
||||||
|
"version": "2.1",
|
||||||
|
"software": {ldelim}
|
||||||
|
"name": "federator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"repository": "https://git.contentnation.net/grumpydevelop/federator"
|
||||||
|
{rdelim},
|
||||||
|
"protocols": [
|
||||||
|
"activitypub"
|
||||||
|
],
|
||||||
|
"relay": "none",
|
||||||
|
"services": {ldelim}
|
||||||
|
"inbound": [],
|
||||||
|
"outbound": [
|
||||||
|
"atom1.0",
|
||||||
|
"rss2.0"
|
||||||
|
]
|
||||||
|
{rdelim},
|
||||||
|
"openRegistrations": true,
|
||||||
|
"usage": {ldelim}
|
||||||
|
"users": {ldelim}
|
||||||
|
"total": {$usercount}
|
||||||
|
{* "activeMonth": activemonth,
|
||||||
|
"activeHalfyear": activehalfyear *}
|
||||||
|
{rdelim},
|
||||||
|
"localPosts": {$postcount},
|
||||||
|
"localComments": {$commentcount}
|
||||||
|
{rdelim},
|
||||||
|
"metadata": {ldelim}{rdelim}
|
||||||
|
{rdelim}
|
Loading…
Reference in New Issue