From bb3048dae7c064b04b683eed4ea5755b8bae3f8c Mon Sep 17 00:00:00 2001 From: Kutalo Alexey Date: Mon, 13 Aug 2018 20:01:17 -0300 Subject: [PATCH] Add prerender service for SEO and Open Graph support --- .env.example | 4 + app/Http/Controllers/PostController.php | 2 +- composer.json | 1 + config/app.php | 1 + config/prerender.php | 145 ++++++++++++++++++++++++ resources/assets/js/views/post/List.vue | 21 ++++ resources/assets/js/views/post/View.vue | 7 +- 7 files changed, 177 insertions(+), 4 deletions(-) create mode 100644 config/prerender.php diff --git a/.env.example b/.env.example index a83a631..681cb6f 100644 --- a/.env.example +++ b/.env.example @@ -30,6 +30,10 @@ MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null +JWT_SECRET= + +PRERENDER_TOKEN= + PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= diff --git a/app/Http/Controllers/PostController.php b/app/Http/Controllers/PostController.php index 37b5746..2b55fdd 100755 --- a/app/Http/Controllers/PostController.php +++ b/app/Http/Controllers/PostController.php @@ -6,7 +6,6 @@ use App\Repositories\CategoryRepository; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\JsonResponse; -use Illuminate\Support\Collection; use Illuminate\Validation\ValidationException; use Illuminate\Http\Request; use App\Repositories\UserRepository; @@ -54,6 +53,7 @@ class PostController extends Controller * @param PostRepository $repo * @param ActivityLogRepository $activity * @param UserRepository $user + * @param CategoryRepository $category */ public function __construct(Request $request, PostRepository $repo, ActivityLogRepository $activity, UserRepository $user, CategoryRepository $category) { diff --git a/composer.json b/composer.json index db2d637..63660bb 100644 --- a/composer.json +++ b/composer.json @@ -15,6 +15,7 @@ "laravel/framework": "5.6.*", "laravel/tinker": "^1.0", "mews/purifier": "^2.0", + "nutsweb/laravel-prerender": "^2.0", "spatie/laravel-permission": "^2.7", "tymon/jwt-auth": "^1.0.0-rc.2" }, diff --git a/config/app.php b/config/app.php index 4180bbf..89be686 100755 --- a/config/app.php +++ b/config/app.php @@ -151,6 +151,7 @@ * Package Service Providers... */ Tymon\JWTAuth\Providers\LaravelServiceProvider::class, + Nutsweb\LaravelPrerender\LaravelPrerenderServiceProvider::class, /* * Application Service Providers... diff --git a/config/prerender.php b/config/prerender.php new file mode 100644 index 0000000..c27063a --- /dev/null +++ b/config/prerender.php @@ -0,0 +1,145 @@ + env('PRERENDER_ENABLE', true), + /* + |-------------------------------------------------------------------------- + | Prerender URL + |-------------------------------------------------------------------------- + | + | This is the prerender URL to the service that prerenders the pages. + | By default, Prerender's hosted service on prerender.io is used + | (https://service.prerender.io). But you can also set it to your + | own server address. + | + */ + 'prerender_url' => env('PRERENDER_URL', 'https://service.prerender.io'), + /* + |-------------------------------------------------------------------------- + | Return soft HTTP status codes + |-------------------------------------------------------------------------- + | + | By default Prerender returns soft HTTP codes. If you would like it to + | return the real ones in case of Redirection (3xx) or status Not Found (404), + | set this parameter to false. + | Keep in mind that returning real HTTP codes requires appropriate meta tags + | to be set. For more details, see github.com/prerender/prerender#httpheaders + | + */ + 'prerender_soft_http_codes' => env('PRERENDER_SOFT_HTTP_STATUS_CODES', true), + /* + |-------------------------------------------------------------------------- + | Prerender Token + |-------------------------------------------------------------------------- + | + | If you use prerender.io as service, you need to set your prerender.io + | token here. It will be sent via the X-Prerender-Token header. If + | you do not provide a token, the header will not be added. + | + */ + 'prerender_token' => env('PRERENDER_TOKEN'), + /* + |-------------------------------------------------------------------------- + | Prerender Whitelist + |-------------------------------------------------------------------------- + | + | Whitelist paths or patterns. You can use asterix syntax, or regular + | expressions (without start and end markers). If a whitelist is supplied, + | only url's containing a whitelist path will be prerendered. An empty + | array means that all URIs will pass this filter. Note that this is the + | full request URI, so including starting slash and query parameter string. + | See github.com/JeroenNoten/Laravel-Prerender for an example. + | + */ + 'whitelist' => [], + /* + |-------------------------------------------------------------------------- + | Prerender Blacklist + |-------------------------------------------------------------------------- + | + | Blacklist paths to exclude. You can use asterix syntax, or regular + | expressions (without start and end markers). If a blacklist is supplied, + | all url's will be prerendered except ones containing a blacklist path. + | By default, a set of asset extentions are included (this is actually only + | necessary when you dynamically provide assets via routes). Note that this + | is the full request URI, so including starting slash and query parameter + | string. See github.com/JeroenNoten/Laravel-Prerender for an example. + | + */ + 'blacklist' => [ + '*.js', + '*.css', + '*.xml', + '*.less', + '*.png', + '*.jpg', + '*.jpeg', + '*.gif', + '*.pdf', + '*.doc', + '*.txt', + '*.ico', + '*.rss', + '*.zip', + '*.mp3', + '*.rar', + '*.exe', + '*.wmv', + '*.doc', + '*.avi', + '*.ppt', + '*.mpg', + '*.mpeg', + '*.tif', + '*.wav', + '*.mov', + '*.psd', + '*.ai', + '*.xls', + '*.mp4', + '*.m4a', + '*.swf', + '*.dat', + '*.dmg', + '*.iso', + '*.flv', + '*.m4v', + '*.torrent' + ], + /* + |-------------------------------------------------------------------------- + | Crawler User Agents + |-------------------------------------------------------------------------- + | + | Requests from crawlers that do not support _escaped_fragment_ will + | nevertheless be served with prerendered pages. You can customize + | the list of crawlers here. + | + */ + 'crawler_user_agents' => [ + 'googlebot', + 'yahoo', + 'bingbot', + 'yandex', + 'baiduspider', + 'facebookexternalhit', + 'twitterbot', + 'rogerbot', + 'linkedinbot', + 'embedly', + 'quora link preview', + 'showyoubot', + 'outbrain', + 'pinterest', + 'developers.google.com/+/web/snippet', + 'slackbot', + ], +]; \ No newline at end of file diff --git a/resources/assets/js/views/post/List.vue b/resources/assets/js/views/post/List.vue index 0bd700c..4dbae42 100644 --- a/resources/assets/js/views/post/List.vue +++ b/resources/assets/js/views/post/List.vue @@ -30,6 +30,24 @@ import postCard from './PostCard' export default { + metaInfo() { + return { + title: `${this.getConfig('company_name')}`, + meta: [ + {name: 'description', content: ''}, + {name: 'twitter:card', content: 'summary_large_image'}, + {name: 'twitter:title', content: this.getConfig('company_name')}, + {name: 'twitter:description', content: ''}, + {name: 'twitter:image', content: `${this.getConfig('app_url')}/uploads/images/cover-default.png`}, + {property: 'og:type', content: 'website'}, + {property: 'og:site_name', content: this.getConfig('company_name')}, + {property: 'og:url', content: `${this.getConfig('app_url')}`}, + {property: 'og:title', content: this.getConfig('company_name')}, + {property: 'og:description', content: ''}, + {property: 'og:image', content: `${this.getConfig('app_url')}/uploads/images/cover-default.png`} + ] + } + }, data() { return { posts: { @@ -72,6 +90,9 @@ chunks.push(arr.slice(i, i += len)); } return chunks; + }, + getConfig(name) { + return helper.getConfig(name); } }, watch: { diff --git a/resources/assets/js/views/post/View.vue b/resources/assets/js/views/post/View.vue index bea48b6..7f70534 100755 --- a/resources/assets/js/views/post/View.vue +++ b/resources/assets/js/views/post/View.vue @@ -41,13 +41,13 @@ {name: 'twitter:title', content: this.post ? this.post.title : ''}, {name: 'twitter:description', content: this.post ? this.limitWords(this.post.stripped_body) : ''}, {name: 'twitter:image', content: this.post ? `${this.getConfig('app_url')}/${this.post.cover}` : ''}, - {property: 'og:locale', content: this.getConfig('locale')}, + {property: 'og:type', content: 'website'}, {property: 'og:site_name', content: this.getConfig('company_name')}, - {property: 'og:url', content: this.getConfig('app_url')}, + {property: 'og:url', content: `${this.getConfig('app_url')}/${this.categorySlug}/${this.post.slug}`}, {property: 'og:title', content: this.post ? this.post.title : ''}, {property: 'og:description', content: this.post ? this.limitWords(this.post.stripped_body) : ''}, {property: 'og:image', content: this.post ? `${this.getConfig('app_url')}/${this.post.cover}` : ''} - ] + ] } }, components: { @@ -70,6 +70,7 @@ .then(response => { this.post = response.post; this.categoryName = this.post ? response.post.category.name : ''; + this.categorySlug = this.post ? response.post.category.slug : ''; if (this.post) { this.documentTitle = `${helper.getConfig('company_name')} | ${this.post.title}`; } else {