From de07e950f274d7f656533b4af92f8572551f471c Mon Sep 17 00:00:00 2001 From: shibafu Date: Sun, 19 Jul 2020 23:04:10 +0900 Subject: [PATCH] add POST /api/webhooks/checkin/{id} --- app/CheckinWebhook.php | 5 + app/Ejaculation.php | 1 + .../Controllers/Api/WebhookController.php | 91 ++++++++++++++++++- app/Http/Kernel.php | 10 +- database/seeds/EjaculationSourcesSeeder.php | 4 +- routes/api.php | 5 +- 6 files changed, 106 insertions(+), 10 deletions(-) diff --git a/app/CheckinWebhook.php b/app/CheckinWebhook.php index 185322c..62c18fb 100644 --- a/app/CheckinWebhook.php +++ b/app/CheckinWebhook.php @@ -25,4 +25,9 @@ class CheckinWebhook extends Model { return $this->belongsTo(User::class); } + + public function isAvailable() + { + return $this->user() !== null; + } } diff --git a/app/Ejaculation.php b/app/Ejaculation.php index a0e571a..074d41a 100644 --- a/app/Ejaculation.php +++ b/app/Ejaculation.php @@ -14,6 +14,7 @@ class Ejaculation extends Model const SOURCE_WEB = 'web'; const SOURCE_CSV = 'csv'; + const SOURCE_WEBHOOK = 'webhook'; protected $fillable = [ 'user_id', 'ejaculated_date', diff --git a/app/Http/Controllers/Api/WebhookController.php b/app/Http/Controllers/Api/WebhookController.php index be1238d..734e861 100644 --- a/app/Http/Controllers/Api/WebhookController.php +++ b/app/Http/Controllers/Api/WebhookController.php @@ -2,13 +2,100 @@ namespace App\Http\Controllers\Api; +use App\CheckinWebhook; +use App\Ejaculation; +use App\Events\LinkDiscovered; use App\Http\Controllers\Controller; +use App\Tag; +use Carbon\Carbon; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Validator; class WebhookController extends Controller { - public function checkin(Request $request) + public function checkin(CheckinWebhook $webhook, Request $request) { - // TODO + if (!$webhook->isAvailable()) { + return response()->json([ + 'status' => 404, + 'error' => [ + 'message' => 'The webhook is unavailable' + ] + ], 404); + } + + $inputs = $request->all(); + + $validator = Validator::make($inputs, [ + 'date' => 'nullable|date_format:Y/m/d', + 'time' => 'nullable|date_format:H:i', + 'note' => 'nullable|string|max:500', + 'link' => 'nullable|url|max:2000', + 'tags' => 'nullable|array', + 'tags.*' => ['string', 'not_regex:/\s/u'], + 'is_private' => 'nullable|boolean', + 'is_too_sensitive' => 'nullable|boolean', + ], [ + 'tags.*.not_regex' => 'The :attribute cannot contain spaces.' + ]); + + if ($validator->fails()) { + return response()->json([ + 'status' => 422, + 'error' => [ + 'message' => 'Validation failed', + 'violations' => $validator->errors()->all(), + ] + ]); + } + + $ejaculatedDate = now()->startOfMinute(); + if (!empty($inputs['date'])) { + $ejaculatedDate = $ejaculatedDate->setDateFrom(Carbon::createFromFormat('Y/m/d', $inputs['date'])); + } + if (!empty($inputs['time'])) { + $ejaculatedDate = $ejaculatedDate->setTimeFrom(Carbon::createFromFormat('H:i', $inputs['time'])); + } + if (Ejaculation::where(['user_id' => $webhook->user_id, 'ejaculated_date' => $ejaculatedDate])->count()) { + return response()->json([ + 'status' => 422, + 'error' => [ + 'message' => 'Checkin already exists in this time', + ] + ]); + } + + $ejaculation = Ejaculation::create([ + 'user_id' => $webhook->user_id, + 'ejaculated_date' => $ejaculatedDate, + 'note' => $inputs['note'] ?? '', + 'link' => $inputs['link'] ?? '', + 'source' => Ejaculation::SOURCE_WEBHOOK, + 'is_private' => $request->has('is_private') ?? false, + 'is_too_sensitive' => $request->has('is_too_sensitive') ?? false + ]); + + $tagIds = []; + if (!empty($inputs['tags'])) { + foreach ($inputs['tags'] as $tag) { + $tag = trim($tag); + if ($tag === '') { + continue; + } + + $tag = Tag::firstOrCreate(['name' => $tag]); + $tagIds[] = $tag->id; + } + } + $ejaculation->tags()->sync($tagIds); + + if (!empty($ejaculation->link)) { + event(new LinkDiscovered($ejaculation->link)); + } + + return response()->json([ + 'status' => 200, + 'checkin' => $ejaculation + ]); } } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index fd3fc57..1804b56 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -38,15 +38,17 @@ class Kernel extends HttpKernel \App\Http\Middleware\NormalizeLineEnding::class, ], - // 現時点では内部APIしかないので、認証の手間を省くためにステートフルにしている。 'api' => [ + 'throttle:60,1', + \Illuminate\Routing\Middleware\SubstituteBindings::class, + ], + + 'stateful' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \App\Http\Middleware\VerifyCsrfToken::class, - 'throttle:60,1', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], + ] ]; /** diff --git a/database/seeds/EjaculationSourcesSeeder.php b/database/seeds/EjaculationSourcesSeeder.php index a762a08..dcec6ef 100644 --- a/database/seeds/EjaculationSourcesSeeder.php +++ b/database/seeds/EjaculationSourcesSeeder.php @@ -11,9 +11,9 @@ class EjaculationSourcesSeeder extends Seeder */ public function run() { - $sources = ['web', 'csv']; + $sources = ['web', 'csv', 'webhook']; foreach ($sources as $source) { - DB::table('ejaculation_sources')->insert(['name' => $source]); + DB::table('ejaculation_sources')->insertOrIgnore(['name' => $source]); } } } diff --git a/routes/api.php b/routes/api.php index f4f2764..d8ee613 100644 --- a/routes/api.php +++ b/routes/api.php @@ -17,9 +17,10 @@ Route::get('/checkin/card', 'Api\\CardController@show'); -Route::middleware('auth')->group(function () { +Route::middleware(['stateful', 'auth'])->group(function () { Route::post('/likes', 'Api\\LikeController@store'); Route::delete('/likes/{id}', 'Api\\LikeController@destroy'); }); -Route::post('/webhooks/checkin/{webhook}', 'Api\\WebhookController@checkin'); +Route::post('/webhooks/checkin/{webhook}', 'Api\\WebhookController@checkin') + ->middleware('throttle:15,15,checkin_webhook');