...
 
Commits (25)
......@@ -8,7 +8,9 @@ stages:
- test
- prepare
- review
- deploy
- deploy:staging
- deploy:canary
- deploy:production
cache:
paths:
......@@ -94,44 +96,100 @@ review:stop:
- master
- test/gitlab-ci
deploy:fpm:
stage: deploy
staging:fpm:
stage: deploy:staging
image: minds/ci:latest
script:
- IMAGE_LABEL="staging"
- $(aws ecr get-login --no-include-email --region us-east-1)
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker pull $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF
# Push to production registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $REPOSITORY_URL_FPM
- docker push $REPOSITORY_URL_FPM
# Push to gitlab register
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $CI_REGISTRY_IMAGE/fpm:latest
- docker push $CI_REGISTRY_IMAGE/fpm:latest
- aws ecs update-service --service=$SERVICE_FPM --force-new-deployment --region us-east-1 --cluster=$CLUSTER
# Push to AWS registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
- docker push $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
# Push to Gitlab registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
- docker push $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
# Deploy to staging
- aws ecs update-service --service=$ECS_APP_STAGING_SERVICE --force-new-deployment --region us-east-1 --cluster=$ECS_CLUSTER
environment:
name: staging
url: https://www.minds.com/?staging=1
only:
refs:
- master
- test/gitlab-ci
canary:fpm:
stage: deploy:canary
image: minds/ci:latest
script:
- IMAGE_LABEL="canary"
- $(aws ecr get-login --no-include-email --region us-east-1)
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker pull $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF
# Push to AWS registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
- docker push $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
# Push to Gitlab registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
- docker push $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
# Deploy to ECS
- aws ecs update-service --service=$ECS_APP_CANARY_SERVICE --force-new-deployment --region us-east-1 --cluster=$ECS_CLUSTER
only:
refs:
- master
- test/gitlab-ci
environment:
name: canary
url: https://www.minds.com/?canary=1 # requires canary cookie
when: manual
allow_failure: false # prevents auto deploy to full production
production:fpm:
stage: deploy:production
image: minds/ci:latest
script:
- IMAGE_LABEL="production"
- $(aws ecr get-login --no-include-email --region us-east-1)
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker pull $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF
# Push to AWS registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
- docker push $ECR_REPOSITORY_URL_FPM:$IMAGE_LABEL
# Push to Gitlab registry
- docker tag $CI_REGISTRY_IMAGE/fpm:$CI_BUILD_REF $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
- docker push $CI_REGISTRY_IMAGE/fpm:$IMAGE_LABEL
# Delpoy to ECS
- aws ecs update-service --service=$ECS_APP_PRODUCTION_SERVICE --force-new-deployment --region us-east-1 --cluster=$ECS_CLUSTER
only:
refs:
- master
- test/gitlab-ci
environment:
name: production
url: https://www.minds.com/
when: delayed
start_in: 2 hours # reduce? can always be deployed manually earlier too
deploy:runners:
stage: deploy
production:runners:
stage: deploy:production
image: minds/ci:latest
script:
- $(aws ecr get-login --no-include-email --region us-east-1)
- docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
- docker pull $CI_REGISTRY_IMAGE/runners:$CI_BUILD_REF
# Push to production register
- docker tag $CI_REGISTRY_IMAGE/runners:$CI_BUILD_REF $REPOSITORY_URL_RUNNERS
- docker push $REPOSITORY_URL_RUNNERS
- docker tag $CI_REGISTRY_IMAGE/runners:$CI_BUILD_REF $ECR_REPOSITORY_URL_RUNNERS
- docker push $ECR_REPOSITORY_URL_RUNNERS:production
# Push gitlab registry
- docker tag $CI_REGISTRY_IMAGE/runners:$CI_BUILD_REF $CI_REGISTRY_IMAGE/runners:latest
- docker push $CI_REGISTRY_IMAGE/runners:latest
- aws ecs update-service --service=$SERVICE_RUNNERS --force-new-deployment --region us-east-1 --cluster=$CLUSTER
- aws ecs update-service --service=$ECS_RUNNERS_PRODUCTION_SERVICE --force-new-deployment --region us-east-1 --cluster=$ECS_CLUSTER
only:
refs:
- master
- test/gitlab-ci
environment:
name: production
url: https://www.minds.com/
......@@ -10,6 +10,7 @@ namespace Minds\Controllers\api\v1;
use Minds\Core;
use Minds\Core\Security;
use Minds\Core\Session;
use Minds\Core\Features;
use Minds\Core\Di\Di;
use Minds\Entities;
use Minds\Interfaces;
......@@ -99,6 +100,10 @@ class authenticate implements Interfaces\Api, Interfaces\ApiIgnorePam
Session::generateJWTCookie($sessions->getSession());
Security\XSRF::setCookie(true);
// Set the canary cookie
Di::_()->get('Features\Manager')
->setCanaryCookie($user->isCanary());
$response['status'] = 'success';
$response['user'] = $user->export();
......
......@@ -118,7 +118,7 @@ class feed implements Interfaces\Api
$ttl = 1800; // 30 minutes
if (($next / 1000) < strtotime('48 hours ago')) {
$ttl = 30; // 0.5 minutes;
$ttl = 300; // 5 minutes;
}
$cacher->set(Core\Session::getLoggedinUser()->guid . ':boost-offset-rotator', $next, $ttl);
......
......@@ -23,6 +23,11 @@ class canary implements Interfaces\Api
'message' => 'You are not logged in'
]);
}
// Refresh the canary cookie
Di::_()->get('Features\Manager')
->setCanaryCookie($user->isCanary());
return Factory::response([
'enabled' => (bool) $user->isCanary(),
]);
......@@ -51,6 +56,10 @@ class canary implements Interfaces\Api
],
'message' => $message,
]);
// Set the canary cookie
Di::_()->get('Features\Manager')
->setCanaryCookie($user->isCanary());
return Factory::response([]);
}
......
<?php
namespace Minds\Core\Email\Batches;
use Minds\Core\Di\Di;
use Minds\Core\Security\ACL;
use Minds\Core\Email\Campaigns;
use Minds\Core\Email\EmailSubscribersIterator;
use Minds\Traits\MagicAttributes;
class WithBlogs implements EmailBatchInterface
{
use MagicAttributes;
/** @var Manager */
protected $manager;
/** @var Repository */
protected $repository;
/** @var EntitiesBuilder */
protected $builder;
/** @var string $offset */
protected $offset;
/** @var string $offset */
protected $templatePath;
/** @var string $subject */
protected $subject;
public function __construct($manager = null, $trendingRepository = null, $builder = null)
{
$this->manager = $manager ?: Di::_()->get('Email\Manager');
$this->repository = $trendingRepository ?: Di::_()->get('Trending\Repository');
$this->builder = $builder ?: Di::_()->get('EntitiesBuilder');
}
public function setDryRun($dry)
{
return $this;
}
/**
* @param string $offset
*
* @return Catchup
*/
public function setOffset($offset)
{
$this->offset = $offset;
return $this;
}
/**
* @param string $templatePath
*
* @return Catchup
*/
public function setTemplateKey($template)
{
$this->templatePath = $template;
return $this;
}
/**
* @param string $subject
*
* @return Catchup
*/
public function setSubject($subject)
{
$this->subject = $subject;
return $this;
}
/**
* @throws \Exception
*/
public function run()
{
if (!$this->templatePath || $this->templatePath == '') {
// throw new \Exception('You must set the templatePath');
}
if (!$this->subject || $this->subject == '') {
// throw new \Exception('You must set the subject');
}
$iterator = new EmailSubscribersIterator();
$iterator->setCampaign('with')
->setTopic('posts_missed_since_login')
->setValue(true)
->setOffset($this->offset);
$blogs = $this->getTrendingBlogs();
$i = 0;
foreach ($iterator as $user) {
$user = new \Minds\Entities\User('mark');
++$i;
echo "\n[$i]: $user->guid ($iterator->offset)";
//if ($user->getTimeCreated() > strtotime('-28 days ago')) {
// echo "[done]";
// return true;
//}
$campaign = new Campaigns\WithBlogs();
$campaign
->setUser($user)
->setTemplateKey($this->templatePath)
->setSubject($this->subject)
->setBlogs($blogs)
->send();
echo ' sent';
exit;
}
}
private function getTrendingBlogs()
{
ACL::$ignore = true;
/*$result = $this->repository->getList([
'type' => 'blogs',
'limit' => 10
]);
if (!$result || !$result['guids'] || count($result['guids']) === 0) {
return [];
}
ksort($result['guids']);
$options['guids'] = $result['guids'];*/
$options['guids'] = [
'998738105104236544',
'999467832663445504',
'966409629405708288',
'993006058717818880',
'973946575102472192',
'996468067362422784',
'993621806187380736',
'971156445331410944',
'983472046539116544',
'952732651199864832',
];
$blogs = $this->builder->get(array_merge([
'subtype' => 'blog',
], $options));
return $blogs;
}
}
......@@ -17,5 +17,8 @@ class FeaturesProvider extends Provider
$this->di->bind('Features', function ($di) {
return new Manager();
}, [ 'useFactory'=> true ]);
$this->di->bind('Features\Manager', function ($di) {
return new Manager();
}, [ 'useFactory'=> true ]);
}
}
......@@ -9,6 +9,7 @@
namespace Minds\Core\Features;
use Minds\Core\Di\Di;
use Minds\Common\Cookie;
use Minds\Core\Session;
class Manager
......@@ -18,10 +19,14 @@ class Manager
/** @var Config $config */
private $config;
/** @var Cookie $cookie */
private $cookie;
public function __construct($config = null)
public function __construct($config = null, $cookie = null)
{
$this->config = $config ?: Di::_()->get('Config');
$this->cookie = $cookie ?: new Cookie;
}
/**
......@@ -65,4 +70,21 @@ class Manager
{
return $this->config->get('features') ?: [];
}
/**
* Set the canary cookie
* @param bool $enabled
* @return void
*/
public function setCanaryCookie(bool $enabled = true) : void
{
$this->cookie
->setName('canary')
->setValue((int) $enabled)
->setExpire(0)
->setSecure(true) //only via ssl
->setHttpOnly(true) //never by browser
->setPath('/')
->create();
}
}
......@@ -34,4 +34,18 @@ class SendNotificationDelegate
}
public function onMaxSubscriptions($subscription)
{
$message = "You are unable to subscribe to new channels as you have over 5000 subscriptions.";
$this->eventsDispatcher->trigger('notification', 'all', [
'to' => [ $subscription->getSubscriberGuid() ],
'entity' => $subscription->getPublisherGuid(),
'notification_view' => 'custom_message',
'from' => 100000000000000519,
'message' => $message,
'params' => [ 'message' => $message],
]);
}
}
......@@ -14,6 +14,7 @@ use Minds\Entities\User;
class Manager
{
const MAX_SUBSCRIPTIONS = 5000;
/** @var Repository $repository */
private $repository;
......@@ -102,10 +103,15 @@ class Manager
$subscription->setSubscriberGuid($this->subscriber->getGuid())
->setPublisherGuid($publisher->getGuid());
if ($this->getSubscriptionsCount() >= static::MAX_SUBSCRIPTIONS) {
$this->sendNotificationDelegate->onMaxSubscriptions($subscription);
throw new TooManySubscriptionsException();
}
$subscription = $this->repository->add($subscription);
$this->eventsDelegate->trigger($subscription);
$this->feedsDelegate->copy($subscription);
//$this->feedsDelegate->copy($subscription);
$this->copyToElasticSearchDelegate->copy($subscription);
$this->cacheDelegate->cache($subscription);
$this->checkRateLimitDelegate->incrementCache($this->subscriber->guid);
......@@ -145,7 +151,7 @@ class Manager
*/
public function getSubscriptionsCount()
{
return $this->subscriber->getSubscriptonsCount(); //TODO: Refactor so we are the source of truth
return $this->subscriber->getSubscriptionsCount(); //TODO: Refactor so we are the source of truth
}
}
<?php
namespace Minds\Core\Subscriptions;
class TooManySubscriptionsException extends \Exception
{
protected $message = "Subscribe to over 5000 channels";
}
\ No newline at end of file
......@@ -98,10 +98,6 @@ class ManagerSpec extends ObjectBehavior
$this->eventsDelegate->trigger($subscription)
->shouldBeCalled();
// Call the feeds delegate
$this->feedsDelegate->copy($subscription)
->shouldBeCalled();
// Call the es delegate
$this->copyToElasticSearchDelegate->copy($subscription)
->shouldBeCalled();
......@@ -119,6 +115,20 @@ class ManagerSpec extends ObjectBehavior
->shouldBe(true);
}
function it_should_not_allow_if_over_5000_subscriptions(User $subscriber)
{
$publisher = (new User)->set('guid', 456);
$subscriber->getSubscriptionsCount()
->willReturn(5000);
$subscriber->getGUID()
->willReturn(123);
$this->setSubscriber($subscriber);
$this->shouldThrow('Minds\Core\Subscriptions\TooManySubscriptionsException')
->duringSubscribe($publisher);
}
function it_should_unsubscribe()
{
// Confusing.. but this is the returned subscription
......
FROM minds/php:latest
# Install opcache (move to php image)
RUN docker-php-ext-install -j$(nproc) opcache
# Additional folders
RUN mkdir --parents --mode=0777 /tmp/minds-cache/ \
......
opcache.enable=1
opcache.memory_consumption=2046
opcache.memory_consumption=2048
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=65407
opcache.validate_timestamps=0
......
......@@ -8,8 +8,8 @@ listen.backlog = -1
pm = static
pm.max_children = 500
pm.max_requests = 2000
pm.max_children = 250
pm.max_requests = 1000
pm.status_path = /status
chdir = /
......
log_errors = On
error_log = /dev/stderr
upload_max_filesize = 4G
post_max_size = 4G
\ No newline at end of file
upload_max_filesize = 1G
post_max_size = 1G
\ No newline at end of file
......@@ -351,6 +351,8 @@ $CONFIG->set('blockchain', [
'wallet_pkey' => '',
],
'wire' => [
'plus_address' => '',
'plus_guid' => '', // Your plus user's guid.
'contract_address' => '0x4b637bba81d24657d4c6acc173275f3e11a8d5d7',
'wallet_address' => '0x4CDc1C1fd1A3F4DD63231afF8c16501BcC11Df95',
'wallet_pkey' => '',
......@@ -458,7 +460,7 @@ $CONFIG->set('tags', [
$CONFIG->set('steward_guid', '');
$CONFIG->set('steward_autoconfirm', false);
$CONFIG->set('development_mode', '{{development-mode}}');
$CONFIG->set('development_mode', '{{development_mode}}');
$CONFIG->set('max_video_length', 900);
......