CDN Santillana para aplicaciones Basecentral.
Para distribuir el contenido estático utilizando Amazon CloudFront dentro de las aplicaciones desarrolladas en Yii se ha tomado como base el trabajo de la librería CDN Asset Management Library for Yii 1 realizada por 2amigos.
Esta librería implementa el SDK de Amazon AWS y extiende el assetManager del core de Yii (CAssetManager).
CAssetManager is a Web application component that manages private files (called assets) and makes them accessible by Web clients.
It achieves this goal by copying assets to a Web-accessible directory and returns the corresponding URL for accessing them.
En el esquema funcional CAssetManager copia los archivos al directorio ./assets de la aplicación y genera una url accesible para el cliente web, yii-cdn-asset-management-library utiliza el mismo patrón para publicar los archivos al bucket S3 de Amazon.
Implementación en aplicaciones basecentral.
Fue necesario realizar algunos ajustes para utilizar la librería dentro de las aplicaciones desarrolladas por Consucorp.
Originalmente la librería está pensada para instalarse con composer por lo que fue necesario incluir las librería de forma standalone dentro de un submodulo.
El submodulo cdn-manager está disponible en el stash de Santillana.
Instalación.
Para instalar las librerías es necesario incluir el repositorio cdn-manager como un submodulo git dentro de la aplicación.
Desde la consola /protectec/vendor/
$ git submodule add ssh://git@stash.project-tools.santillanatn.com:7999/psa/cdn-manager.git
Desde SourceTree
- Menu -> Repository -> Add Submodule
- -
Dentro del repositorio se incluyen archivos de configuración de ejemplo.
- aliasCdn.php, continene los alias de los namespaces utilizados por las librerías (autoload).
- assetManager.php, es un ejemplo de la configuración de los componentes
cdnManager
yassetManager
. - console.php, incluye la configuración para ejecutar la publicación de assets desde la consola de
yiic.hp
. - Controler.php, incorpora el método
getAssetsUrl
para obtener la url del contenido publicado. - main.php, ejemplo de como incluir los archivos de configuración en la aplicación.
Configuración en Acuerdos V3
El primer requisito es haber integrado las variables de entorno dentro de la aplicación e instalar la librería.
Archivo de configuración para namespaces (alias)
// protected/config/aliasCdn.php /** * Definir los espacios de nombres compatibles con el autoload de Yii */ Yii::setPathOfAlias('dosamigos', dirname(__FILE__) .'/../vendor/cdn-manager/dosamigos/'); Yii::setPathOfAlias('Guzzle', dirname(__FILE__) . '/../vendor/cdn-manager/guzzle/src/Guzzle/'); Yii::setPathOfAlias('Symfony', dirname(__FILE__) . '/../vendor/cdn-manager/symfony/event-dispatcher/Symfony/'); Yii::setPathOfAlias('Aws', dirname(__FILE__) . '/../vendor/cdn-manager/aws/aws-sdk-php/src/Aws/'); // path para acceder a / de la aplicación Yii::setPathOfAlias('base', realpath(dirname(__FILE__) .'/../../'));
Configuración de los parámetros de la CDN
// protected/config/assetManager.php return (ENVIRONMENT != 'dev')? array( 'components'=>array( 'cdnManager' => array( // make sure you set this name to the one that you are going to use wit S3Command 'class' => 'dosamigos\cdn\Manager', 'assetManagerComponentId' => 'assetManager', 'startVersion' => 1, // the starting version of assets when it has no records in file cache 'assetsPaths' => array( // the 'static' assets you wish to publish. They must be "aliases" //'application.assets', 'zii.widgets.assets', 'system.web.js.source', //'base.themes.unoi.assets', //'base.themes.comp.assets', ) ), 'assetManager' => array( 'class' => 'dosamigos\cdn\S3AssetManager', 'key' => AWS_ACCESS_KEY, 'secret' => AWS_SECRET_KEY, 'host' => AWS_BUCKET, 'bucket' =>AWS_BUCKET, 'path' => 'CONTRATOV3/assets', // aplicacion/assets 'region' => AWS_REGION, 'assetsVersion' => false // Your 'dynamic' assets version ) ), ) : array();
Dentro del componente cdnManager
se configuran los directorios que contienen los archivos estáticos.
zii.widgets.assets
y system.web.js.source
corresponden a los archivos css, js e imágenes que utiliza el framework.
En el componente assetManager
se define la ruta donde se publicarán los archivos de la aplicación. Esta debe contener el nombre de la aplicación. Por ejemplo:
'path' => 'CONTRATOV3/assets'
Configuración de los comandos ejecutables desde yiic.php
// protected/config/console.php // ./protected/yiic S3 --manager=cdnManager publish --useVersionCache=0 // set error reporting ini_set('display_errors', 1); error_reporting(E_ALL); require_once '/basecentral/apache/conf/xx_contrato3_db.php'; defined('AWS_REGION') or define('AWS_REGION', $AWS_REGION); defined('ENVIRONMENT') or define('ENVIRONMENT', $ENVIRONMENT); defined('AWS_ACCESS_KEY') or define('AWS_ACCESS_KEY', $AWS_ACCESS_KEY); defined('AWS_SECRET_KEY') or define('AWS_SECRET_KEY', $AWS_SECRET_KEY); defined('AWS_BUCKET') or define('AWS_BUCKET', $AWS_BUCKET); require_once dirname(__FILE__) . '/aliasCdn.php'; $assets = require_once dirname(__FILE__) . '/assetManager.php'; // This is the configuration for yiic console application. // Any writable CConsoleApplication properties can be configured here. return array_merge_recursive($assets, array( 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name'=>'Acuerdos v3', 'commandMap' => array( 'S3' => array( // alias of the path where you extracted the DocsCommand.php 'class' => 'dosamigos\cdn\S3Command', ) ), // application components 'components'=>array( 'cache'=>array( 'class'=>'system.caching.CDbCache' ), 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'trace, error, warning', ), ), ), ), ) );
La configuración de los comandos de la consola es necesaria para actualizar las versiones publicadas de los archivos.
./yiic S3 --manager=cdnManager publish --useVersionCache=0
Generar la url pública de los archivos Para obtener la url dónde fueron publicados los archivos es necesario agregar el siguiente método a Controller.php
// protected/components/Controller.php public function getAssetsUrl($asset) { return (Yii::app()->params['ambiente'] != 'dev')? Yii::app()->assetManager->publish( Yii::getPathOfAlias($asset)) : Yii::app()->assetManager->publish( Yii::getPathOfAlias($asset), false, -1, true); }
Ejemplo de uso en una vista de $this->getAssetsUrl()
<link rel="stylesheet" type="text/css" href="<?php echo $this->getAssetsUrl('application.assets'); ?>/css/layout.css" > <script src="<?php echo $this->getAssetsUrl('application.assets'); ?>/js/jquery-1.8.2.min.js" type="text/javascript"></script>
Forzar el protocolo https En los ambientes de PRE y PRO el https es forzado por los balanceadores, sin embargo Yii no puede detectar que se trata de una conexión https ya que internamente el tráfico se envía por http.
Por lo que es necesari forzar dentro de la aplicación a generar las urls para la CDN con https utilizando el siguiente componente.
// components/EHttpRequest.php /** * Extiende la clase CHttpRequest para modificar la validación de conexión segura */ class EHttpRequest extends CHttpRequest { private $_hostInfo; public function getIsSecureConnection() { /** * forzar https */ return true; } public function getHostInfo($schema='') { if($this->_hostInfo===null) { if($secure=$this->getIsSecureConnection()) $http='https'; else $http='http'; if(isset($_SERVER['HTTP_X_FORWARDED_HOST'])) $this->_hostInfo=$http.'://'.$_SERVER['HTTP_X_FORWARDED_HOST']; elseif(isset($_SERVER['HTTP_HOST'])) $this->_hostInfo=$http.'://'.$_SERVER['HTTP_HOST']; else { $this->_hostInfo=$http.'://'.$_SERVER['SERVER_NAME']; $port=$secure ? $this->getSecurePort() : $this->getPort(); if(($port!==80 && !$secure) || ($port!==443 && $secure)) $this->_hostInfo.=':'.$port; } } if($schema!=='') { $secure=$this->getIsSecureConnection(); if($secure && $schema==='https' || !$secure && $schema==='http') return $this->_hostInfo; $port=$schema==='https' ? $this->getSecurePort() : $this->getPort(); if($port!==80 && $schema==='http' || $port!==443 && $schema==='https') $port=':'.$port; else $port=''; $pos=strpos($this->_hostInfo,':'); return $schema.substr($this->_hostInfo,$pos,strcspn($this->_hostInfo,':',$pos+1)+1).$port; } else return $this->_hostInfo; } /** * [getUserHostAddress description] * @return {string} Retorna la ip del usuario */ public function getUserHostAddress() { if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != '') { $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } elseif ($_SERVER['REMOTE_ADDR'] != '') { $ip = $_SERVER['REMOTE_ADDR']; } else { $ip = '127.0.0.1'; } $_ips = explode(',', $ip); if (sizeof($_ips) > 1) { $ip = $_ips[0]; } return $ip; } }
Integrar la configuración dentro de main.php
// protected/config/main.php require_once dirname(__FILE__) . '/aliasCdn.php'; return CMap::mergeArray( require_once dirname(__FILE__) . '/assetManager.php', require_once dirname(__FILE__).'/debug_toolbar.php', require_once dirname(__FILE__).'/db.php', array( 'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..', 'name'=>'CONTRATO', 'language' => 'es', 'sourceLanguage'=>'en', 'charset'=>'utf-8', ... // application components 'components'=>array( 'request'=>array( 'class'=>'EHttpRequest', ), 'cache'=>array( 'class'=>'system.caching.CDbCache' ),
El componente cache
es necesario para el funcionamiento de la integración.
- -
En este punto, de acuerdo al ambiente de ejecución el contenido estático de la aplicación sera publicado dentro /assets o en el bucket S3 correspondiente.
Organizando los archivos del proyecto
Para la aplicación Acuerdos v3 se han identificados dos puntos en los que existen directorios con javascript, css e imágenes utilizadas por la aplicación.
El primero es todos los directorios que se encuentran en la "raíz" de la aplicación.
- contrato/js
- contrato/css
- contrato/img
Es importante validar que los archivos estáticos a pulbicar en la CDN deben ser los de librerías de terceros. Los componentes propios de la aplicación y que intervengan dentro de las funcionalidades se manatendrán dentro del directorio /js en el directorio raíz o en cada uno de los temas.
Para organizarlos de mejor forma se ha creado el directorio contrato/protected/assets
y dentro de han copiado cada uno de los directorios con los mismos nombres. Nota de Tania: no confundir con contrato/assets
En el caso de los temas en acuerdos existe un directorio para UNOi uno
y otro para SCompartir comp
en el cual se ha realizado el mismo proceso.
Se crea una carpeta llamada assets
y se copian los directorios con los archivos.
themes/uno/assets
themes/comp/assets
Una vez realizada la organización de los archivos solo será necesario incluirlos dentro de los parametros de la CDN.
// protected/config/assetManager.php 'assetsPaths' => array( // the 'static' assets you wish to publish. They must be "aliases" 'zii.widgets.assets', 'system.web.js.source', 'application.assets', 'base.themes.unoi.assets', 'base.themes.comp.assets', ),
El alias base
hace referencia al directorio raíz de la aplicación.
El último paso es indicar en las vistas que incluyen los archivos la url desde la cual estará disponible el recurso, utilizando el alias que le corresponde al directorio.
<?php Yii::app()->clientScript->registerCoreScript('jquery'); ?> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="Content-type" content="text/html; <?php echo Yii::app()->charset; ?>"> <meta name="language" content="<?php echo Yii::app()->language; ?>"> <title><?php echo Traductor::t('Acuerdo de Implementación'); ?></title> <meta name="robots" content="noindex,nofollow" /> <link rel="stylesheet" href="<?php echo $this->getAssetsUrl('application.assets'); ?>/css/bootstrap/css/bootstrap.min.css" > <link rel="stylesheet" href="<?php echo $this->getAssetsUrl('application.assets'); ?>/js/datepicker/css/datepicker.css" /> <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->theme->baseUrl ?>/css/layout.css" > <script type="text/javascript" src="<?php echo $this->getAssetsUrl('application.assets'); ?>/js/site.js"></script> <?php $separador_decimal = Yii::app()->params['formato']->separador_decimal; $formato_numero = Yii::app()->params['formato']->formato_numero; $decimales = explode($separador_decimal,$formato_numero); $decimales = end($decimales); $decimales = strlen($decimales); ?> ...
-
Despligue
Para realizar el despligue de la aplicación cuando se incluyan cambios en los archivos estáticos se deberá solicitar al equipo de Soporte Hosting ejecutar los siguientes comandos en cada una de las máquinas depués que se realice la copia de archivos.
./protected/yiic S3 resetCache ./protected/yiic S3 --manager=cdnManager publish --useVersionCache=0
EOF!