+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+
+namespace OCA\Social\Cron;
+
+
+use Exception;
+use OC\BackgroundJob\TimedJob;
+use OCA\Social\AppInfo\Application;
+use OCA\Social\Exceptions\ActorDoesNotExistException;
+use OCA\Social\Exceptions\RequestException;
+use OCA\Social\Exceptions\SocialAppConfigException;
+use OCA\Social\Service\ActivityPub\DocumentService;
+use OCA\Social\Service\ActivityPub\PersonService;
+use OCA\Social\Service\ActivityService;
+use OCA\Social\Service\ActorService;
+use OCA\Social\Service\CacheService;
+use OCA\Social\Service\ConfigService;
+use OCA\Social\Service\MiscService;
+use OCA\Social\Service\QueueService;
+use OCP\AppFramework\QueryException;
+
+
+/**
+ * Class Queue
+ *
+ * @package OCA\Social\Cron
+ */
+class Queue extends TimedJob {
+
+
+ /** @var ActivityService */
+ private $activityService;
+
+ /** @var QueueService */
+ private $queueService;
+
+ /** @var MiscService */
+ private $miscService;
+
+
+ /**
+ * Cache constructor.
+ */
+ public function __construct() {
+ $this->setInterval(12 * 60); // 12 minutes
+ }
+
+
+ /**
+ * @param mixed $argument
+ *
+ * @throws QueryException
+ */
+ protected function run($argument) {
+ $app = new Application();
+ $c = $app->getContainer();
+
+ $this->queueService = $c->query(QueueService::class);
+ $this->activityService = $c->query(ActivityService::class);
+ $this->miscService = $c->query(MiscService::class);
+
+ $this->manageQueue();
+ }
+
+
+ private function manageQueue() {
+ $requests = $this->queueService->getRequestStandby($total = 0);
+ $this->activityService->manageInit();
+
+ foreach ($requests as $request) {
+ try {
+ $this->activityService->manageRequest($request);
+ } catch (RequestException $e) {
+ } catch (SocialAppConfigException $e) {
+ }
+ }
+
+ }
+
+}
+
diff --git a/lib/Db/ActorsRequest.php b/lib/Db/ActorsRequest.php
index 09b37e4b..00572296 100644
--- a/lib/Db/ActorsRequest.php
+++ b/lib/Db/ActorsRequest.php
@@ -60,32 +60,28 @@ class ActorsRequest extends ActorsRequestBuilder {
* @param Person $actor
*
* @return string
- * @throws \Exception
+ * @throws SocialAppConfigException
*/
public function create(Person $actor): string {
$id = $this->configService->getUrlSocial() . '@' . $actor->getPreferredUsername();
- try {
- $qb = $this->getActorsInsertSql();
+ $qb = $this->getActorsInsertSql();
- $qb->setValue('id', $qb->createNamedParameter($id))
+ $qb->setValue('id', $qb->createNamedParameter($id))
// ->setValue('type', $qb->createNamedParameter($actor->getType()))
- ->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
- ->setValue('name', $qb->createNamedParameter($actor->getName()))
- ->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
- ->setValue(
- 'preferred_username', $qb->createNamedParameter($actor->getPreferredUsername())
- )
- ->setValue('public_key', $qb->createNamedParameter($actor->getPublicKey()))
- ->setValue('private_key', $qb->createNamedParameter($actor->getPrivateKey()));
+ ->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
+ ->setValue('name', $qb->createNamedParameter($actor->getName()))
+ ->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
+ ->setValue(
+ 'preferred_username', $qb->createNamedParameter($actor->getPreferredUsername())
+ )
+ ->setValue('public_key', $qb->createNamedParameter($actor->getPublicKey()))
+ ->setValue('private_key', $qb->createNamedParameter($actor->getPrivateKey()));
- $qb->execute();
+ $qb->execute();
- return $id;
- } catch (\Exception $e) {
- throw $e;
- }
+ return $id;
}
diff --git a/lib/Db/CacheDocumentsRequest.php b/lib/Db/CacheDocumentsRequest.php
index 46cdfd31..8241120c 100644
--- a/lib/Db/CacheDocumentsRequest.php
+++ b/lib/Db/CacheDocumentsRequest.php
@@ -150,6 +150,7 @@ class CacheDocumentsRequest extends CacheDocumentsRequestBuilder {
$qb = $this->getCacheDocumentsSelectSql();
$this->limitToDBFieldEmpty($qb, 'local_copy');
$this->limitToCaching($qb, self::CACHING_TIMEOUT);
+ $this->limitToDBFieldInt($qb, 'error', 0);
$documents = [];
$cursor = $qb->execute();
diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php
index e62d72fe..a87672d6 100644
--- a/lib/Db/CoreRequestBuilder.php
+++ b/lib/Db/CoreRequestBuilder.php
@@ -361,14 +361,6 @@ class CoreRequestBuilder {
}
- /**
- * @param IQueryBuilder $qb
- */
- protected function orderByPriority(IQueryBuilder &$qb) {
- $qb->orderBy('priority', 'desc');
- }
-
-
/**
* @param IQueryBuilder $qb
* @param string $field
diff --git a/lib/Db/RequestQueueRequest.php b/lib/Db/RequestQueueRequest.php
index 33ca298c..4e07ffba 100644
--- a/lib/Db/RequestQueueRequest.php
+++ b/lib/Db/RequestQueueRequest.php
@@ -57,18 +57,6 @@ class RequestQueueRequest extends RequestQueueRequestBuilder {
public function multiple(array $queues) {
foreach ($queues as $queue) {
$this->create($queue);
-// $qb->values(
-// [
-// 'source' => $qb->createNamedParameter($queue->getSource()),
-// 'activity' => $qb->createNamedParameter($queue->getActivity()),
-// 'instance' => $qb->createNamedParameter(
-// json_encode($queue->getInstance(), JSON_UNESCAPED_SLASHES)
-// ),
-// 'status' => $qb->createNamedParameter($queue->getStatus()),
-// 'tries' => $qb->createNamedParameter($queue->getTries()),
-// 'last' => $qb->createNamedParameter($queue->getLast())
-// ]
-// );
}
}
@@ -92,14 +80,34 @@ class RequestQueueRequest extends RequestQueueRequestBuilder {
)
->setValue('priority', $qb->createNamedParameter($queue->getPriority()))
->setValue('status', $qb->createNamedParameter($queue->getStatus()))
- ->setValue('tries', $qb->createNamedParameter($queue->getTries()))
- ->setValue('last', $qb->createNamedParameter($queue->getLast()));
+ ->setValue('tries', $qb->createNamedParameter($queue->getTries()));
$qb->execute();
}
/**
- * return Actor from database based on the username
+ * return Queue from database based on the status=0
+ *
+ * @return RequestQueue[]
+ */
+ public function getStandby(): array {
+ $qb = $this->getQueueSelectSql();
+ $this->limitToStatus($qb, RequestQueue::STATUS_STANDBY);
+ $qb->orderBy('id', 'asc');
+
+ $requests = [];
+ $cursor = $qb->execute();
+ while ($data = $cursor->fetch()) {
+ $requests[] = $this->parseQueueSelectSql($data);
+ }
+ $cursor->closeCursor();
+
+ return $requests;
+ }
+
+
+ /**
+ * return Queue from database based on the token
*
* @param string $token
* @param int $status
@@ -114,7 +122,7 @@ class RequestQueueRequest extends RequestQueueRequestBuilder {
$this->limitToStatus($qb, $status);
}
- $this->orderByPriority($qb);
+ $qb->orderBy('priority', 'desc');
$requests = [];
$cursor = $qb->execute();
@@ -180,9 +188,11 @@ class RequestQueueRequest extends RequestQueueRequestBuilder {
*/
public function setAsFailure(RequestQueue &$queue) {
$qb = $this->getQueueUpdateSql();
- $qb->set('status', $qb->createNamedParameter(RequestQueue::STATUS_STANDBY));
- // TODO - increment tries++
-// ->set('tries', 'tries+1');
+ $func = $qb->func();
+ $expr = $qb->expr();
+
+ $qb->set('status', $qb->createNamedParameter(RequestQueue::STATUS_STANDBY))
+ ->set('tries', $func->add('tries', $expr->literal(1)));
$this->limitToId($qb, $queue->getId());
$this->limitToStatus($qb, RequestQueue::STATUS_RUNNING);
@@ -195,5 +205,13 @@ class RequestQueueRequest extends RequestQueueRequestBuilder {
$queue->setStatus(RequestQueue::STATUS_SUCCESS);
}
+
+ public function delete(RequestQueue $queue) {
+ $qb = $this->getQueueDeleteSql();
+ $this->limitToId($qb, $queue->getId());
+
+ $qb->execute();
+ }
+
}
diff --git a/lib/Exceptions/Request410Exception.php b/lib/Exceptions/Request410Exception.php
new file mode 100644
index 00000000..85bc8405
--- /dev/null
+++ b/lib/Exceptions/Request410Exception.php
@@ -0,0 +1,8 @@
+setActivity($this->get('activity', $data, ''));
$this->setStatus($this->getInt('status', $data, 0));
$this->setTries($this->getInt('tries', $data, 0));
- $this->setLast($this->getInt('last', $data, 0));
- }
+ $last = $this->get('last', $data, '');
+ if ($last === '') {
+ $this->setLast(0);
+ } else {
+ $dTime = new DateTime($last);
+ $this->setLast($dTime->getTimestamp());
+ }
+ }
/**
* @return array
diff --git a/lib/Service/ActivityPub/PersonService.php b/lib/Service/ActivityPub/PersonService.php
index 31ac4aaa..6d2e8d87 100644
--- a/lib/Service/ActivityPub/PersonService.php
+++ b/lib/Service/ActivityPub/PersonService.php
@@ -38,6 +38,7 @@ use OCA\Social\Db\CacheDocumentsRequest;
use OCA\Social\Exceptions\CacheActorDoesNotExistException;
use OCA\Social\Exceptions\CacheDocumentDoesNotExistException;
use OCA\Social\Exceptions\InvalidResourceException;
+use OCA\Social\Exceptions\Request410Exception;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
@@ -124,6 +125,7 @@ class PersonService implements ICoreService {
* @throws RequestException
* @throws SocialAppConfigException
* @throws UrlCloudException
+ * @throws Request410Exception
*/
public function getFromId(string $id, bool $refresh = false): Person {
diff --git a/lib/Service/ActivityService.php b/lib/Service/ActivityService.php
index 03cd437f..37bf5606 100644
--- a/lib/Service/ActivityService.php
+++ b/lib/Service/ActivityService.php
@@ -43,8 +43,10 @@ use OCA\Social\Exceptions\EmptyQueueException;
use OCA\Social\Exceptions\InvalidResourceException;
use OCA\Social\Exceptions\NoHighPriorityRequestException;
use OCA\Social\Exceptions\QueueStatusException;
+use OCA\Social\Exceptions\Request410Exception;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SignatureException;
+use OCA\Social\Exceptions\SignatureIsGoneException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UrlCloudException;
use OCA\Social\Model\ActivityPub\ACore;
@@ -105,6 +107,10 @@ class ActivityService {
private $miscService;
+ /** @var array */
+ private $failInstances;
+
+
/**
* ActivityService constructor.
*
@@ -231,10 +237,12 @@ class ActivityService {
$author = $this->getAuthorFromItem($activity);
$instancePaths = $this->generateInstancePaths($activity);
$token = $this->queueService->generateRequestQueue($instancePaths, $activity, $author);
+ $this->manageInit();
try {
$directRequest = $this->queueService->getPriorityRequest($token);
$this->manageRequest($directRequest);
+ } catch (RequestException $e) {
} catch (NoHighPriorityRequestException $e) {
} catch (EmptyQueueException $e) {
return '';
@@ -246,14 +254,23 @@ class ActivityService {
}
+ public function manageInit() {
+ $this->failInstances = [];
+ }
+
+
/**
* @param RequestQueue $queue
*
- * @throws ActorDoesNotExistException
* @throws RequestException
* @throws SocialAppConfigException
*/
public function manageRequest(RequestQueue $queue) {
+ $host = $queue->getInstance()
+ ->getAddress();
+ if (in_array($host, $this->failInstances)) {
+ throw new RequestException();
+ }
try {
$this->queueService->initRequest($queue);
@@ -261,15 +278,22 @@ class ActivityService {
return;
}
- $result = $this->generateRequest(
- $queue->getInstance(), $queue->getActivity(), $queue->getAuthor()
- );
+ try {
+ $result = $this->generateRequest(
+ $queue->getInstance(), $queue->getActivity(), $queue->getAuthor()
+ );
+ } catch (ActorDoesNotExistException $e) {
+ $this->queueService->deleteRequest($queue);
+ } catch (Request410Exception $e) {
+ $this->queueService->deleteRequest($queue);
+ }
try {
if ($this->getint('_code', $result, 500) === 202) {
$this->queueService->endRequest($queue, true);
} else {
$this->queueService->endRequest($queue, false);
+ $this->failInstances[] = $host;
}
} catch (QueueStatusException $e) {
}
@@ -339,6 +363,7 @@ class ActivityService {
*
* @return Request[]
* @throws ActorDoesNotExistException
+ * @throws Request410Exception
* @throws RequestException
* @throws SocialAppConfigException
*/
@@ -385,6 +410,9 @@ class ActivityService {
* @throws MalformedArrayException
* @throws RequestException
* @throws SignatureException
+ * @throws SocialAppConfigException
+ * @throws UrlCloudException
+ * @throws SignatureIsGoneException
*/
public function checkRequest(IRequest $request) {
$dTime = new DateTime($request->getHeader('date'));
@@ -394,7 +422,12 @@ class ActivityService {
throw new SignatureException('object is too old');
}
- $this->checkSignature($request);
+ try {
+ $this->checkSignature($request);
+ } catch (Request410Exception $e) {
+ throw new SignatureIsGoneException();
+ }
+
}
@@ -429,9 +462,12 @@ class ActivityService {
* @param IRequest $request
*
* @throws InvalidResourceException
+ * @throws MalformedArrayException
+ * @throws Request410Exception
* @throws RequestException
* @throws SignatureException
- * @throws MalformedArrayException
+ * @throws SocialAppConfigException
+ * @throws UrlCloudException
* @throws Exception
*/
private function checkSignature(IRequest $request) {
@@ -508,6 +544,7 @@ class ActivityService {
* @throws RequestException
* @throws SocialAppConfigException
* @throws UrlCloudException
+ * @throws Request410Exception
*/
private function retrieveKey($keyId): string {
$actor = $this->personService->getFromId($keyId);
diff --git a/lib/Service/ActorService.php b/lib/Service/ActorService.php
index 348d1a1f..70fb824e 100644
--- a/lib/Service/ActorService.php
+++ b/lib/Service/ActorService.php
@@ -142,7 +142,7 @@ class ActorService {
*
* @throws AccountAlreadyExistsException
* @throws NoUserException
- * @throws Exception
+ * @throws SocialAppConfigException
*/
public function createActor(string $userId, string $username) {
diff --git a/lib/Service/CurlService.php b/lib/Service/CurlService.php
index 1aa94cc3..2218a52d 100644
--- a/lib/Service/CurlService.php
+++ b/lib/Service/CurlService.php
@@ -33,6 +33,8 @@ namespace OCA\Social\Service;
use daita\MySmallPhpTools\Model\Request;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
+use Exception;
+use OCA\Social\Exceptions\Request410Exception;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Exceptions\SocialAppConfigException;
@@ -71,6 +73,7 @@ class CurlService {
*
* @return array
* @throws RequestException
+ * @throws Request410Exception
*/
public function request(Request $request): array {
$curl = $this->initRequest($request);
@@ -85,6 +88,7 @@ class CurlService {
$this->parseRequestResultCode301($code);
// $this->parseRequestResultCode401($code);
$this->parseRequestResultCode404($code, $request);
+ $this->parseRequestResultCode410($code);
// $this->parseRequestResultCode503($code);
// $this->parseRequestResultCode500($code);
// $this->parseRequestResult($result);
@@ -130,7 +134,7 @@ class CurlService {
$request->setAddress($host);
try {
$this->request($request);
- } catch (RequestException $e) {
+ } catch (Exception $e) {
}
}
@@ -234,11 +238,24 @@ class CurlService {
*
* @param Request $request
*
- * @throws RequestException
+ * @throws Request410Exception
*/
private function parseRequestResultCode404(int $code, Request $request) {
if ($code === 404) {
- throw new RequestException('404 Not Found - ' . json_encode($request));
+ throw new Request410Exception('404 Not Found - ' . json_encode($request));
+ }
+ }
+
+ /**
+ * @param int $code
+ *
+ * @param Request $request
+ *
+ * @throws Request410Exception
+ */
+ private function parseRequestResultCode410(int $code) {
+ if ($code === 410) {
+ throw new Request410Exception();
}
}
diff --git a/lib/Service/InstanceService.php b/lib/Service/InstanceService.php
index 1eafcc33..911a86c9 100644
--- a/lib/Service/InstanceService.php
+++ b/lib/Service/InstanceService.php
@@ -35,6 +35,7 @@ use daita\MySmallPhpTools\Model\Request;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use OCA\Social\Exceptions\InvalidResourceException;
+use OCA\Social\Exceptions\Request410Exception;
use OCA\Social\Exceptions\RequestException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Model\Instance;
@@ -79,12 +80,12 @@ class InstanceService {
* @return mixed
* @throws RequestException
* @throws InvalidResourceException
+ * @throws Request410Exception
*/
public function retrieveAccount(string $account) {
$account = $this->withoutBeginAt($account);
- if (strstr(substr($account, 0, -3), '@') === false)
- {
+ if (strstr(substr($account, 0, -3), '@') === false) {
throw new InvalidResourceException();
}
list($username, $host) = explode('@', $account);
@@ -113,6 +114,7 @@ class InstanceService {
*
* @return mixed
* @throws RequestException
+ * @throws Request410Exception
*/
public function retrieveObject($id) {
$url = parse_url($id);
diff --git a/lib/Service/QueueService.php b/lib/Service/QueueService.php
index 6fc3b8f3..36cc46dd 100644
--- a/lib/Service/QueueService.php
+++ b/lib/Service/QueueService.php
@@ -147,6 +147,22 @@ class QueueService {
}
+ public function getRequestStandby(int &$total = 0): array {
+ $requests = $this->requestQueueRequest->getStandby();
+ $total = sizeof($requests);
+
+ $result = [];
+ foreach ($requests as $request) {
+ $delay = floor(pow($request->getTries(), 4) / 3);
+ if ($request->getLast() < (time() - $delay)) {
+ $result[] = $request;
+ }
+ }
+
+ return $result;
+ }
+
+
/**
* @param string $token
* @param int $status
@@ -186,6 +202,12 @@ class QueueService {
}
}
+ /**
+ * @param RequestQueue $queue
+ */
+ public function deleteRequest(RequestQueue $queue) {
+ $this->requestQueueRequest->delete($queue);
+ }
}
diff --git a/src/App.vue b/src/App.vue
index ef25ee57..d27f5c4d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,7 +4,8 @@
-
+
+
@@ -30,6 +31,7 @@
.app-social {
width: 100%;
}
+
.setup {
margin: auto;
width: 700px;
@@ -38,6 +40,16 @@
.setup input[type=url] {
width: 300px;
}
+
+ #app-content .social__wrapper {
+ margin: 15px calc(50% - 350px - 75px);
+ }
+
+ #social-spacer a:hover,
+ #social-spacer a:focus {
+ border: none !important;
+ }
+
diff --git a/src/components/TimelineEntry.vue b/src/components/TimelineEntry.vue
index 98d651db..bb00ec0e 100644
--- a/src/components/TimelineEntry.vue
+++ b/src/components/TimelineEntry.vue
@@ -11,6 +11,10 @@
{{ item.actor_info.preferredUsername }}
{{ item.actor_info.account }}
+
+ {{ item.actor_info.preferredUsername }}
+ {{ item.actor_info.account }}
+
{{ item.actor_info.preferredUsername }}
{{ item.actor_info.account }}
@@ -44,7 +48,13 @@ export default {
formatedMessage: function() {
let message = this.item.content
message = message.replace(/(?:\r\n|\r|\n)/g, '
')
- message = message.linkify()
+ message = message.linkify({
+ formatHref: {
+ email: function(href) {
+ return OC.generateUrl('/apps/social/@' + (href.indexOf('mailto:') === 0 ? href.substring(7) : href))
+ }
+ }
+ })
message = this.$twemoji.parse(message)
return message
}
diff --git a/src/components/UserEntry.vue b/src/components/UserEntry.vue
new file mode 100644
index 00000000..5c37c774
--- /dev/null
+++ b/src/components/UserEntry.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/router.js b/src/router.js
index 69833e83..88ed9b95 100644
--- a/src/router.js
+++ b/src/router.js
@@ -53,29 +53,32 @@ export default new Router({
},
{
path: '/:index(index.php/)?apps/social/account/@:account',
+ alias: './timeline',
components: {
- default: Profile
+ default: Profile,
+ details: ProfileTimeline
},
props: true,
- name: 'profile',
children: [
{
path: '',
+ name: 'profile',
components: {
details: ProfileTimeline
}
},
{
path: 'followers',
+ name: 'profile.followers',
components: {
- default: Profile,
details: ProfileFollowers
}
},
{
path: 'following',
+ name: 'profile.following',
+
components: {
- default: Profile,
details: ProfileFollowers
}
}
diff --git a/src/store/timeline.js b/src/store/timeline.js
index 411c1343..a77fcb4e 100644
--- a/src/store/timeline.js
+++ b/src/store/timeline.js
@@ -58,7 +58,8 @@ const actions = {
},
post(context, post) {
return axios.post(OC.generateUrl('apps/social/api/v1/post'), { data: post }).then((response) => {
- context.commit('addPost', { data: response.data })
+ // eslint-disable-next-line no-console
+ console.log('Post created with token ' + response.data.result.token)
}).catch((error) => {
OC.Notification.showTemporary('Failed to create a post')
console.error('Failed to create a post', error)
diff --git a/src/views/ProfileFollowers.vue b/src/views/ProfileFollowers.vue
index a84a63a1..862163df 100644
--- a/src/views/ProfileFollowers.vue
+++ b/src/views/ProfileFollowers.vue
@@ -21,31 +21,49 @@
-->
-