diff --git a/.env.example b/.env.example index 9a9d0dc..448d63f 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,8 @@ APP_DEBUG=true APP_KEY=SomeRandomString APP_URL=http://localhost +PNS_DEFAULT_APP=id-or-slug + DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 diff --git a/app/Application.php b/app/Application.php index 7638cd8..9f255e6 100644 --- a/app/Application.php +++ b/app/Application.php @@ -31,6 +31,11 @@ public function notifications() return $this->hasMany('App\Notification'); } + public function fake_pages() + { + return $this->hasMany('App\FakePage'); + } + public function getDevicesCountAttribute() { return Device::onlyActive()->where('application_id', $this->getKey())->count(); diff --git a/app/FakePage.php b/app/FakePage.php index 225b6c1..14e9686 100644 --- a/app/FakePage.php +++ b/app/FakePage.php @@ -16,4 +16,9 @@ public function scopeOnlyActive() { return $this->where('is_active', true); } + + public function application() + { + return $this->belongsTo('App\Application'); + } } diff --git a/app/Http/Controllers/Api/V1/FakePagesController.php b/app/Http/Controllers/Api/V1/FakePagesController.php index 5160cc5..2d98547 100644 --- a/app/Http/Controllers/Api/V1/FakePagesController.php +++ b/app/Http/Controllers/Api/V1/FakePagesController.php @@ -11,6 +11,7 @@ use App\FakePage; use App\User; +use App\Application; use App\Http\Controllers\Api\ApiBaseController; @@ -23,16 +24,19 @@ public function __construct(FakePagesRepositoryContract $fakePagesRepo) $this->fakePagesRepo = $fakePagesRepo; } - public function index(Request $request) + public function index(Request $request, Application $application = null) { if ( ($perPage = $request->get('per_page', 20)) > 100 ) { $perPage = 100; } + if (!$application->exists) + $application = null; + $user = app('Dingo\Api\Auth\Auth')->user(); return $this->response->paginator( - $this->fakePagesRepo->all($user, $perPage), + $this->fakePagesRepo->all($user, $application, $perPage), $this->getBasicTransformer() ); } @@ -44,7 +48,7 @@ public function show(FakePage $fakePage, Request $request) public function store(CreateFakePageRequest $request) { - $data = $request->only('name', 'content_url', 'is_active'); + $data = $request->only('name', 'content_url', 'application_id', 'is_active'); $fakePage = $this->fakePagesRepo->store($data); return $this->response->item($fakePage, $this->getBasicTransformer()); @@ -52,7 +56,7 @@ public function store(CreateFakePageRequest $request) public function update(FakePage $fakePage, UpdateFakePageRequest $request) { - $data = $request->only('name', 'content_url', 'is_active'); + $data = $request->only('name', 'content_url', 'application_id', 'is_active'); $fakePage = $this->fakePagesRepo->update($fakePage, $data); diff --git a/app/Http/Requests/FakePages/CreateFakePageRequest.php b/app/Http/Requests/FakePages/CreateFakePageRequest.php index 6e701db..5a7fb80 100644 --- a/app/Http/Requests/FakePages/CreateFakePageRequest.php +++ b/app/Http/Requests/FakePages/CreateFakePageRequest.php @@ -27,7 +27,8 @@ public function rules() return [ 'name' => 'required', 'content_url' => 'required', - 'is_active' => 'required|boolean' + 'is_active' => 'required|boolean', + 'application_id' => 'required|exists:applications,id' ]; } } diff --git a/app/Http/Requests/FakePages/UpdateFakePageRequest.php b/app/Http/Requests/FakePages/UpdateFakePageRequest.php index 79e76b7..dd3224f 100644 --- a/app/Http/Requests/FakePages/UpdateFakePageRequest.php +++ b/app/Http/Requests/FakePages/UpdateFakePageRequest.php @@ -27,7 +27,8 @@ public function rules() return [ 'name' => 'required', 'content_url' => 'required', - 'is_active' => 'required|boolean' + 'is_active' => 'required|boolean', + 'application_id' => 'required|exists:applications,id' ]; } } diff --git a/app/Http/routes.php b/app/Http/routes.php index a82b9ab..11eb081 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -22,7 +22,11 @@ $api->get('cities', ['as' => 'cities.all', 'uses' => 'CitiesController@all']); $api->post('cities/zipcodes', ['as' => 'cities.zipcodes', 'uses' => 'CitiesController@zipcode']); - $api->post('applications/{application}/devices', ['as' => 'devices.create', 'uses' => 'DeviceController@create']); + $api->group(['prefix' => 'applications/{application}'], function($api) { + $api->post('devices', ['as' => 'devices.create', 'uses' => 'DeviceController@create']); + $api->get('fake-pages', ['as' => 'fakePages.indexPublic', 'uses' => 'FakePagesController@index']); + }); + $api->group(['prefix' => 'fake-pages'], function($api) { $api->get('/', ['as' => 'fakePages.index', 'uses' => 'FakePagesController@index']); diff --git a/app/Repositories/FakePagesRepository.php b/app/Repositories/FakePagesRepository.php index a1f0688..c2d4e6b 100644 --- a/app/Repositories/FakePagesRepository.php +++ b/app/Repositories/FakePagesRepository.php @@ -4,17 +4,32 @@ use App\Repositories\Contracts\FakePagesRepositoryContract; use App\FakePage; +use App\User; +use App\Application; class FakePagesRepository implements FakePagesRepositoryContract { - public function all($user, $perPage = 20) + public function all($user, Application $application = null, $perPage = 20) { $fakePages = null; + if (!$user) { $fakePages = FakePage::onlyActive()->orderBy('name'); + + if (!is_null($application)) + $fakePages->where('application_id', $application->getKey()); + else { + $application = Application::where('id', env('PNS_DEFAULT_APP', -1)) + ->orWhere('slug', env('PNS_DEFAULT_APP', -1))->first(); + + $fakePages->whereNull('application_id'); + if ($application) + $fakePages->orWhere('application_id', $application->getKey()); + } } else { - $fakePages = FakePage::orderBy('name'); + $fakePages = FakePage::orderBy('name')->with(['application']); } + return $fakePages->paginate($perPage); } diff --git a/composer.json b/composer.json index 585dac5..5261aea 100644 --- a/composer.json +++ b/composer.json @@ -5,10 +5,6 @@ "license": "MIT", "type": "project", "repositories":[ - { - "url":"https://github.com/eduardostuart/feedreader.git", - "type":"git" - }, { "url":"https://github.com/eduardostuart/api.git", "type":"git", diff --git a/database/migrations/2017_10_16_214313_add_application_id_to_fake_pages.php b/database/migrations/2017_10_16_214313_add_application_id_to_fake_pages.php new file mode 100644 index 0000000..7871e12 --- /dev/null +++ b/database/migrations/2017_10_16_214313_add_application_id_to_fake_pages.php @@ -0,0 +1,31 @@ +integer('application_id')->unsigned()->index()->nullable(); + + $table->foreign('application_id')->references('id')->on('applications')->onDelete('set null'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/readme.md b/readme.md index 7f8816d..5e37ad2 100644 --- a/readme.md +++ b/readme.md @@ -1,27 +1,118 @@ -# Laravel PHP Framework +# Push Notification Service -[![Build Status](https://travis-ci.org/laravel/framework.svg)](https://travis-ci.org/laravel/framework) -[![Total Downloads](https://poser.pugx.org/laravel/framework/d/total.svg)](https://packagist.org/packages/laravel/framework) -[![Latest Stable Version](https://poser.pugx.org/laravel/framework/v/stable.svg)](https://packagist.org/packages/laravel/framework) -[![Latest Unstable Version](https://poser.pugx.org/laravel/framework/v/unstable.svg)](https://packagist.org/packages/laravel/framework) -[![License](https://poser.pugx.org/laravel/framework/license.svg)](https://packagist.org/packages/laravel/framework) +Simple and functional API service to store device tokens of your app and dispatch notifications to them. -Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable, creative experience to be truly fulfilling. Laravel attempts to take the pain out of development by easing common tasks used in the majority of web projects, such as authentication, routing, sessions, queueing, and caching. +If you want to get it running, follow the instructions here. It will take half an hour, at least it is free. -Laravel is accessible, yet powerful, providing tools needed for large, robust applications. A superb inversion of control container, expressive migration system, and tightly integrated unit testing support give you the tools you need to build any application with which you are tasked. +## Installing -## Official Documentation +* You can clone this repo, or [download it here](https://github.com/gabfr/pns/archive/master.zip) (development version - master branch). +* After getting the files, you should configure your `.env` file following the example in `.env.example` +* Finally you run `composer install` +* And then create the database with the tables running `php artisan migrate` +* With the database created, you should run `php artisan db:seed`, so you will get the default records. -Documentation for the framework can be found on the [Laravel website](http://laravel.com/docs). +# Configuring the applications -## Contributing +There is no UI to manage the applications yet, so you have to do it yourself through the database or you can use something like a Postman (which I think it's easier - but this is totally up to you). -Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](http://laravel.com/docs/contributions). +# Configuring the applications - using Postman -## Security Vulnerabilities +## Authenticating -If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell at taylor@laravel.com. All security vulnerabilities will be promptly addressed. +First of all, you should get a access token so you can POST/GET/DELETE/PUT critical resources such as applications registries. +You do it in the route `/access-token` as the following request: +``` +POST /access-token HTTP/1.1 +Content-Type: application/json +Cache-Control: no-cache + +{ + "email": "eu@gabrielf.com", + "password": "secret" +} +``` + +When you got the token successfully, you have to add in the headers of all your further requests: +``` +Content-Type: application/json +Authorization: Bearer PASTE_HERE_THE_TOKEN_YOU_GOT +``` + +## Configuring applications + +With the token, it is simple to create an application on the system, you just send a `POST` to `/applications`: +``` +Content-Type: application/json +Authorization: Bearer PASTE_HERE_THE_TOKEN_YOU_GOT + +{ + "name": "My Fancy App Name", + "slug": "my-fancy-app-name" +} +``` + +### Configuring applications - notification service certificates (Android and Apple) + +With your apps created you should now configure the credentials/certificates of the notifications APIs: + +### Android - Google Cloud Messaging/Firebase + +`POST /applications/*your-app-slug*/gcm` + +Field | Description | Type | Mandatory | Default value +----- | ----------- | ---- | --------- | ------------- +`gcm_api_key` | The private key to send notifications on the Google Cloud Messaging. You can get it on the [Firebase Control Panel](https://console.firebase.google.com) (Select Project > Go to Settings > Click on the Cloud Messaging Tab) | `string` | [X] | `null` +`gcm_mode` | The mode you want to operate on the GCM network. `sandbox` or `production` | `string` | [X] | `sandbox` + +### Apple Push Notification Service + +`POST /applications/*your-app-slug*/apns` + +**Important:** When sending the files, you should select the **`form-data`** option on the body tab of the postman. Which will result in a content-type header like this one: +`Content-Type: multipart/form-data; boundary=...` + +Field | Description | Type | Mandatory | Default value +----- | ----------- | ---- | --------- | ------------- +`apns_certificate_sandbox` | The private key to send notifications on the Apple Push Notification Service, the sandbox one. The format of the certificate file is **`*.pem`**, check out how to generate it in the next section. | `file` | [ ] | `null` +`apns_certificate_production` | The production key to send notifications on the Apple Push Notification Service, the production one. | `file` | [X] | `null` +`apns_mode` | The mode you want to operate on the APNS network. `sandbox` or `production` | `string` | [X] | `sandbox` + +### Apple Push Notification Service - Preparing the certificate files + +This guide will help you to convert the certificates you already generated on the Apple Developer Center to the format that the API will work with. + +First of all, make sure you have the `*.cer` file, before you start, follow these steps below: +* You have to go to the **Keychain Access** tool, selected the category **My Certificates** +* Drag and drop the `*.cer` file on the certificate list +* Localize your certificate that should be called something like `Apple Development IOS Push Services: com.mycoolapp.code` or `Apple Push Services: com.mycoolapp.code` +* Toggle the marker right before the name, and you will see a private key registry, click with the right button of your magic mouse or trackpad (whatever), and **Export** it. +* Save it on the same folder of your `*.cer` file. Make sure it is on the format `*.p12` (Personal Information Exchange) + +I recommend that you use the same name for your certificates, i.e., if you generate the `*.p12` file of your `push_certificate_sandbox.cer`, you should name your `*.p12` file as: `push_certificate_sandbox.p12`. That way, we will not get lost in the middle of the many files we will generate. + +Now you just have to run the bash script below to generate the correct `*.pem` file to the API. + +**Important:** Remember to check the files in bold below and change it to the correct name that you want. + +```shell +#!/bin/sh + +cd **folder/of/your/certificates** +openssl x509 -in **aps_production.cer** -inform der -out aps_production.pem + +openssl pkcs12 -nocerts -out Certificates_production.pem -in **Certificates_production.p12** +openssl rsa -in Certificates_production.pem -out Certificates_production_NOPWD.pem + +cat aps_production.pem Certificates_production_NOPWD.pem > apns_production.pem +``` + +In this case, the file that you will use on the endpoint above is the **apns_production.pem**. + +## Ready? Nice! + +After you are ready, you can start sendind notifications with our [simple web app, that was made with love and needs attention](https://github.com/gabfr/pns-app). ## License -The Laravel framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT). +This software is made on top of the Laravel framework that is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).