Compare commits
2 Commits
073fba5312
...
ebf3d05620
Author | SHA1 | Date |
---|---|---|
Sascha Nitsch | ebf3d05620 | |
Sascha Nitsch | 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:
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
@ -94,6 +94,10 @@ class Api extends Main
|
|||
}
|
||||
}
|
||||
switch ($this->paths[0]) {
|
||||
case '.well-known':
|
||||
case 'nodeinfo':
|
||||
$handler = new Api\WellKnown($this);
|
||||
break;
|
||||
case 'v1':
|
||||
switch ($this->paths[1]) {
|
||||
case 'dummy':
|
||||
|
@ -101,9 +105,6 @@ class Api extends Main
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case '.well-known':
|
||||
$handler = new Api\WellKnown($this);
|
||||
break;
|
||||
}
|
||||
$printresponse = true;
|
||||
if ($handler !== null) {
|
||||
|
@ -126,14 +127,13 @@ class Api extends Main
|
|||
header($name . ': ' . $value);
|
||||
}
|
||||
}
|
||||
if ($this->responseCode != 200) {
|
||||
http_response_code($this->responseCode);
|
||||
}
|
||||
if ($printresponse) {
|
||||
if ($this->redirect !== null) {
|
||||
header("Location: $this->redirect");
|
||||
}
|
||||
// @phan-suppress-next-line PhanSuspiciousValueComparison
|
||||
if ($this->responseCode != 200) {
|
||||
http_response_code($this->responseCode);
|
||||
}
|
||||
if ($this->responseCode != 404) {
|
||||
header("Content-type: " . $this->contentType);
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
|
|
@ -37,6 +37,19 @@ class WellKnown implements APIInterface
|
|||
$this->main = $main;
|
||||
}
|
||||
|
||||
/**
|
||||
* return host-meta information
|
||||
*
|
||||
* @return true
|
||||
*/
|
||||
private function hostMeta()
|
||||
{
|
||||
$data = [
|
||||
'fqdn' => $_SERVER['SERVER_NAME']
|
||||
];
|
||||
$this->response = $this->main->renderTemplate('host-meta.xml', $data);
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* run given url path
|
||||
*
|
||||
|
@ -51,9 +64,19 @@ class WellKnown implements APIInterface
|
|||
case 'GET':
|
||||
switch (sizeof($paths)) {
|
||||
case 2:
|
||||
if ($paths[1] === 'webfinger') {
|
||||
$wf = new WellKnown\WebFinger($this, $this->main);
|
||||
return $wf->exec();
|
||||
if ($paths[0] === 'nodeinfo') {
|
||||
$ni = new WellKnown\NodeInfo($this, $this->main);
|
||||
return $ni->exec($paths);
|
||||
}
|
||||
switch ($paths[1]) {
|
||||
case 'host-meta':
|
||||
return $this->hostMeta();
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
/**
|
||||
* save remote stats
|
||||
*
|
||||
* @param \Federator\Data\Stats $stats stats to save
|
||||
* @return void
|
||||
*/
|
||||
public function saveRemoteStats($stats);
|
||||
|
||||
/**
|
||||
* save remote user by given name
|
||||
*
|
||||
|
|
|
@ -29,4 +29,11 @@ interface Connector
|
|||
* @return \Federator\Data\User | false
|
||||
*/
|
||||
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'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
|
@ -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
|
||||
* @param string $_name user or profile name
|
||||
|
|
|
@ -77,6 +77,25 @@ class RedisCache implements Cache
|
|||
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
|
||||
*
|
||||
|
@ -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 \Federator\Data\User $user user data
|
||||
|
@ -129,7 +163,7 @@ class RedisCache implements Cache
|
|||
{
|
||||
$key = self::createKey('u', $_name);
|
||||
$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
|
||||
password = redis*change*password
|
||||
userttl = 10
|
||||
statsttl = 60
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
version="1.0" encoding="UTF-8"?>
|
||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
||||
<Link rel="lrdd" template="https://{$fqdn}/.well-known/webfinger?resource={ldelim}uri{rdelim}"/>
|
||||
</XRD>
|
|
@ -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