Ver código fonte

Merge pull request #132 from Seldaek/notification-api

Notification api
Nils Adermann 13 anos atrás
pai
commit
2b68759cb1

+ 1 - 0
app/AppKernel.php

@@ -20,6 +20,7 @@ class AppKernel extends Kernel
             new Symfony\Bundle\AsseticBundle\AsseticBundle(),
             new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
             new FOS\UserBundle\FOSUserBundle(),
+            new Snc\RedisBundle\SncRedisBundle(),
             new Packagist\WebBundle\PackagistWebBundle(),
             new WhiteOctober\PagerfantaBundle\WhiteOctoberPagerfantaBundle(),
             new Nelmio\SolariumBundle\NelmioSolariumBundle(),

+ 2 - 0
app/autoload.php

@@ -15,6 +15,8 @@ $loader->registerNamespaces(array(
     'Assetic'          => __DIR__.'/../vendor/assetic/src',
     'Metadata'         => __DIR__.'/../vendor/metadata/src',
     'FOS'              => __DIR__.'/../vendor/bundles',
+    'Snc'              => __DIR__.'/../vendor/bundles',
+    'Predis'           => __DIR__.'/../vendor/predis/lib',
     'Composer'         => __DIR__.'/../vendor/composer/src',
     'Packagist'        => __DIR__.'/../src',
     'WhiteOctober\PagerfantaBundle' => __DIR__.'/../vendor/bundles',

+ 7 - 0
app/config/config.yml

@@ -48,6 +48,13 @@ doctrine:
         auto_generate_proxy_classes: %kernel.debug%
         auto_mapping: true
 
+snc_redis:
+    clients:
+        default:
+            type: predis
+            alias: default
+            dsn: %redis_dsn%
+
 # Swiftmailer Configuration
 swiftmailer:
     transport: %mailer_transport%

+ 2 - 0
app/config/parameters.yml.dist

@@ -12,6 +12,8 @@ parameters:
     mailer_from_email: admin@example.org
     mailer_from_name: Admin Team
 
+    redis_dsn: redis://localhost/1
+
     locale: en
 
     secret: CHANGE_ME_IN_PROD

+ 9 - 0
deps

@@ -85,3 +85,12 @@
 
 [jsonlint]
     git=https://github.com/Seldaek/jsonlint.git
+
+[SncRedisBundle]
+    git=git://github.com/snc/SncRedisBundle.git
+    target=/bundles/Snc/RedisBundle
+    version=origin/master
+
+[predis]
+    git=git://github.com/nrk/predis.git
+    version=origin/v0.7

+ 8 - 5
deps.lock

@@ -6,15 +6,18 @@ doctrine-dbal 2.1.6
 doctrine 2.1.6
 swiftmailer v4.1.5
 assetic v1.0.2
-twig-extensions 1dfff8e793f50f651c4f74f796c2c68a4aee3147
+twig-extensions 4c831657215e51568fcb31bb9142e827f9a69bd5
 metadata 1.0.0
-composer bfd48b06bd85619a36f660c43dda4fabfe980a67
+composer 470153989284bc41d13a8e279e2b9cb607e96d34
 SensioFrameworkExtraBundle 638f545b7020b9e9d5944a7e3167f60ed848250d
 SensioDistributionBundle 20b66a408084ad8752f98e50f10533f5245310bf
-SensioGeneratorBundle dd37fc4487bc09ac01bdcf89e0ff4ee4484b7fab
+SensioGeneratorBundle b1ccb78c1743f30817b0fce9bb5c6baff6ed7bf8
 AsseticBundle v1.0.1
 FOSUserBundle 1.1.0
 WhiteOctoberPagerfanta c490684def33e992241e7fad33bcbd03d9d18643
-WhiteOctoberPagerfantaBundle 3fad72a0916d12f50b7a56da470175f560991724
+WhiteOctoberPagerfantaBundle 40209ef994fa2f342660d96bf6bea3f88171cd2d
 solarium 2.3.0-RC1
-NelmioSolariumBundle f1f0c436e727e28acd209c5c9e1176a8ae306ea6
+NelmioSolariumBundle 75c1c8481b3e6be50f1509ab8290181c48b1169e
+jsonlint 869e5d011fe1c82501ae0a3b427a686c21fd5baf
+SncRedisBundle 23c1814a179598d62f6c50290472234e8e4faedd
+predis v0.7.2

+ 50 - 2
src/Packagist/WebBundle/Controller/ApiController.php

@@ -15,6 +15,7 @@ namespace Packagist\WebBundle\Controller;
 use Composer\IO\NullIO;
 use Composer\Repository\VcsRepository;
 use Packagist\WebBundle\Package\Updater;
+use Packagist\WebBundle\Entity\Package;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpFoundation\Request;
@@ -40,14 +41,19 @@ class ApiController extends Controller
         $packages = $em->getRepository('Packagist\WebBundle\Entity\Package')
             ->getFullPackages();
 
-        $data = array();
+        $notifyUrl = $this->generateUrl('track_download', array('name' => 'VND/PKG'));
+
+        $data = array(
+            'notify' => str_replace('VND/PKG', '%package%', $notifyUrl),
+            'packages' => array(),
+        );
         foreach ($packages as $package) {
             $versions = array();
             foreach ($package->getVersions() as $version) {
                 $versions[$version->getVersion()] = $version->toArray();
                 $em->detach($version);
             }
-            $data[$package->getName()] = array('versions' => $versions);
+            $data['packages'][$package->getName()] = array($versions);
             $em->detach($package);
         }
         unset($versions, $package, $packages);
@@ -106,4 +112,46 @@ class ApiController extends Controller
 
         return new Response(json_encode(array('status' => 'error', 'message' => 'Could not find a package that matches this request (does user maintain the package?)',)), 404);
     }
+
+    /**
+     * @Route("/downloads/{name}", name="track_download", requirements={"name"="[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+"}, defaults={"_format" = "json"})
+     * @Method({"POST"})
+     */
+    public function trackDownloadAction(Request $request, $name)
+    {
+        $result = $this->getDoctrine()->getConnection()->fetchAssoc(
+            'SELECT p.id, v.id vid
+            FROM package p
+            LEFT JOIN package_version v ON p.id = v.package_id
+            WHERE p.name = ?
+            AND v.normalizedVersion = ?
+            LIMIT 1',
+            array($name, $request->request->get('version_normalized'))
+        );
+
+        if (!$result) {
+            return new Response('{"status": "error", "message": "Package not found"}', 200);
+        }
+
+        $redis = $this->get('snc_redis.default');
+        $id = $result['id'];
+        $version = $result['vid'];
+
+        $throttleKey = 'dl:'.$id.':'.$request->getClientIp().':'.date('Ymd');
+        $requests = $redis->incr($throttleKey);
+        if (1 === $requests) {
+            $redis->expire($throttleKey, 86400);
+        }
+        if ($requests <= 10) {
+            $redis->incr('dl:'.$id.':'.date('Ymd'));
+            $redis->incr('dl:'.$id.':'.date('Ym'));
+            $redis->incr('dl:'.$id);
+
+            $redis->incr('dl:'.$id.'-'.$version.':'.date('Ymd'));
+            $redis->incr('dl:'.$id.'-'.$version.':'.date('Ym'));
+            $redis->incr('dl:'.$id.'-'.$version);
+        }
+
+        return new Response('{"status": "success"}', 201);
+    }
 }

+ 1 - 0
src/Packagist/WebBundle/Entity/Version.php

@@ -212,6 +212,7 @@ class Version
             'keywords' => $tags,
             'homepage' => $this->getHomepage(),
             'version' => $this->getVersion(),
+            'version_normalized' => $this->getNormalizedVersion(),
             'license' => $this->getLicense(),
             'authors' => $authors,
             'source' => $this->getSource(),

+ 10 - 1
src/Packagist/WebBundle/Package/Dumper.php

@@ -14,6 +14,7 @@ namespace Packagist\WebBundle\Package;
 
 use Symfony\Component\Filesystem\Filesystem;
 use Symfony\Bridge\Doctrine\RegistryInterface;
+use Symfony\Component\Routing\RouterInterface;
 use Packagist\WebBundle\Entity\Version;
 
 /**
@@ -42,6 +43,11 @@ class Dumper
      */
     protected $buildDir;
 
+    /**
+     * @var RouterInterface
+     */
+    protected $router;
+
     /**
      * Data cache
      * @var array
@@ -55,10 +61,11 @@ class Dumper
      * @param string $webDir web root
      * @param string $cacheDir cache dir
      */
-    public function __construct(RegistryInterface $doctrine, Filesystem $filesystem, $webDir, $cacheDir)
+    public function __construct(RegistryInterface $doctrine, Filesystem $filesystem, RouterInterface $router, $webDir, $cacheDir)
     {
         $this->doctrine = $doctrine;
         $this->fs = $filesystem;
+        $this->router = $router;
         $this->webDir = realpath($webDir);
         $this->buildDir = $cacheDir . '/composer-packages-build';
     }
@@ -112,6 +119,8 @@ class Dumper
         if (!isset($this->files['packages.json']['packages'])) {
             $this->files['packages.json']['packages'] = array();
         }
+        $url = $this->router->generate('track_download', array('name' => 'VND/PKG'));
+        $this->files['packages.json']['notify'] = str_replace('VND/PKG', '%package%', $url);
 
         // dump files to build dir
         foreach ($modifiedFiles as $file => $dummy) {

+ 1 - 1
src/Packagist/WebBundle/Resources/config/services.yml

@@ -7,4 +7,4 @@ services:
 
     packagist.package_dumper:
         class: Packagist\WebBundle\Package\Dumper
-        arguments: [ @doctrine, @filesystem, %kernel.root_dir%/../web/, %kernel.cache_dir% ]
+        arguments: [ @doctrine, @filesystem, @router, %kernel.root_dir%/../web/, %kernel.cache_dir% ]