Skip to content

Commit

Permalink
feat: Add stats for api keys and a page to view them.
Browse files Browse the repository at this point in the history
  • Loading branch information
3m1n3nc3 committed Jan 11, 2025
1 parent 91a0d16 commit 0b73cd5
Show file tree
Hide file tree
Showing 154 changed files with 783 additions and 463 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<?php

namespace App\Http\Controllers;
namespace App\Http\Controllers\Account;

use App\Enums\HttpStatus;
use App\Helpers\Providers;
use App\Http\Controllers\Controller;
use App\Http\Resources\ApiKeyCollection;
use App\Http\Resources\ApiKeyResource;
use App\Models\ApiKey;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<?php

namespace App\Http\Controllers;
namespace App\Http\Controllers\Account;

use App\Helpers\Providers;
use App\Http\Controllers\Controller;
use App\Models\Log;
use App\Services\Trend;
use Carbon\Carbon;
Expand Down
11 changes: 10 additions & 1 deletion app/Http/Resources/ApiKeyResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ApiKeyResource extends JsonResource
*/
public function toArray(Request $request): array
{
$with = str($request->input('with'))->replace(', ', ',')->explode(',');

return [
'id' => $this->id,
'key' => $request->isMethod('POST') ? $this->key : str($this->key)->mask('*', strlen($this->key) / 5),
Expand All @@ -22,6 +24,13 @@ public function toArray(Request $request): array
'createdAt' => $this->created_at,
'createDate' => $this->created_at?->format('Y-m-d'),
'rateLimited' => $this->rate_limited,
'stats' => $this->when($with->contains('stats'), fn() => [
'totalCalls' => $this->calls['total'] ?? 0,
'dailyCalls' => $this->calls['daily'] ?? 0,
'monthlyCalls' => $this->calls['monthly'] ?? 0,
'topEndpoint' => $this->calls['top_endpoint'] ?? '',
'dailyTopEndpoint' => $this->calls['daily_top_endpoint'] ?? '',
])
];
}
}
}
41 changes: 40 additions & 1 deletion app/Models/ApiKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
namespace App\Models;

use App\Helpers\Providers;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;
use Valorin\Random\Random;

class ApiKey extends Model
Expand Down Expand Up @@ -60,6 +63,42 @@ public static function boot(): void
});
}

public function calls(): Attribute
{
return Attribute::make(
get: fn() => [
'total' => $this->log()->count(),
'daily' => $this->log()->whereBetween('created_at', [
now()->startOfDay(),
now()->endOfDay(),
])->count(),
'monthly' => $this->log()->whereBetween('created_at', [
now()->startOfMonth(),
now()->endOfMonth(),
])->count(),
'top_endpoint' => $this->log()
->select('endpoint', DB::raw('COUNT(*) as total_calls'))
->groupBy('endpoint')
->orderByDesc('total_calls')
->first(),
'daily_top_endpoint' => $this->log()
->select('endpoint', DB::raw('COUNT(*) as total_calls'))
->groupBy('endpoint')
->orderByDesc('total_calls')
->whereBetween('created_at', [
now()->startOfDay(),
now()->endOfDay(),
])
->first(),
],
);
}

public function log(): HasMany
{
return $this->hasMany(Log::class);
}

/**
* Get the user that owns the ApiKey
*
Expand All @@ -69,4 +108,4 @@ public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}
}
11 changes: 10 additions & 1 deletion database/factories/LogFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Database\Factories;

use App\Models\ApiKey;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
Expand All @@ -16,8 +17,16 @@ class LogFactory extends Factory
*/
public function definition(): array
{
$routeCollection = \Illuminate\Support\Facades\Route::getRoutes();
$uri = collect($routeCollection->getRoutes())
->shuffle()
->filter(fn($r) => str($r->uri())->contains('countries'))
->first();

return [
//
'endpoint' => route($uri->getName(), collect($uri->parameterNames())->mapWithKeys(fn($n) => [$n => rand()])),
'ip_address' => $this->faker->ipv4(),
'api_key_id' => ApiKey::inRandomOrder()->first('id')?->id,
];
}
}
11 changes: 9 additions & 2 deletions database/seeders/LogSeeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Database\Seeders;

use App\Models\Log;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

Expand All @@ -12,6 +13,12 @@ class LogSeeder extends Seeder
*/
public function run(): void
{
//
$time = now()->subDays(rand(2, 10));

Log::factory(rand(5, 10))->create();
Log::factory(4, [
'created_at' => $time,
'updated_at' => $time->addHours(10),
])->create();
}
}
}
6 changes: 3 additions & 3 deletions public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<title>404 | Toneflix Places API</title>

<meta name="generator" content="VitePress v1.3.2">
<link rel="preload stylesheet" href="/doc-assets/style.EL1P2gZo.css" as="style">
<link rel="preload stylesheet" href="/doc-assets/style.DYutow-A.css" as="style">

<script type="module" src="/doc-assets/app.FSZg1qt4.js"></script>
<script type="module" src="/doc-assets/app.BgeGFxHw.js"></script>
<link rel="preload" href="/doc-assets/inter-roman-latin.Di8DUHzh.woff2" as="font" type="font/woff2" crossorigin="">
<link rel="icon" href="/docs/logo.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/remixicon/4.2.0/remixicon.css" integrity="sha512-OQDNdI5rpnZ0BRhhJc+btbbtnxaj+LdQFeh0V9/igiEPDiWE2fG+ZsXl0JEH+bjXKPJ3zcXqNyP4/F/NegVdZg==" crossorigin="anonymous" referrerpolicy="no-referrer">
Expand All @@ -22,7 +22,7 @@
</head>
<body>
<div id="app"></div>
<script>window.__VP_HASH_MAP__=JSON.parse("{\"api-documentation.md\":\"D2s_N2G_\",\"demo.md\":\"D99vP8Nb\",\"docs_cars.md\":\"Ckb2hpOW\",\"docs_cars_countries.md\":\"CGTWMGp8\",\"docs_cars_demo.md\":\"DuSCi_vk\",\"docs_cars_derivatives.md\":\"D9ZOdPDC\",\"docs_cars_engines.md\":\"CSjUdmvi\",\"docs_cars_manufacturers.md\":\"rzCHWYco\",\"docs_cars_mileages.md\":\"oeOxT-JV\",\"docs_cars_models.md\":\"DYOmkxOr\",\"docs_cars_vehicles.md\":\"D_06LCoc\",\"docs_cars_years.md\":\"5cCNRZ0y\",\"docs_cities.md\":\"B9-ZOAxY\",\"docs_lgas.md\":\"Taan7sev\",\"docs_states.md\":\"LUXpUf_G\",\"docs_units.md\":\"CfN56dAK\",\"docs_wards.md\":\"CKCWMPXq\",\"docs_world_cities.md\":\"DfemAqOv\",\"docs_world_countries.md\":\"Du5jXSJK\",\"docs_world_demo.md\":\"KxBMlcPq\",\"docs_world_regions.md\":\"Dol5TwHY\",\"docs_world_states.md\":\"BHxRHhxm\",\"docs_world_subregions.md\":\"BhcZJo4T\",\"get-started.md\":\"UyRU3NQH\",\"index.md\":\"3lLRA69S\",\"portal_api-keys.md\":\"BpIhLm6a\",\"portal_home.md\":\"YnzIn0tO\",\"portal_login.md\":\"Dt8S5zUo\",\"portal_register.md\":\"BAHkw1ZR\",\"portal_reset-password.md\":\"DePf6j4I\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Toneflix Places API\",\"description\":\"One api to rule them all, query all states, Local government areas, Wards, Polling Units and Towns in Nigeria + all world regions, countries, states and cities.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/docs/logo.png\",\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Demo\",\"link\":\"/demo\"},{\"text\":\"Portal\",\"link\":\"/portal/home\"}],\"sidebar\":[{\"text\":\"Usage\",\"items\":[{\"text\":\"Api Documentation\",\"link\":\"/api-documentation\"}]},{\"text\":\"Naija Geolocation\",\"items\":[{\"text\":\"States\",\"link\":\"/docs/states\"},{\"text\":\"LGAs\",\"link\":\"/docs/lgas\"},{\"text\":\"Wards\",\"link\":\"/docs/wards\"},{\"text\":\"Polling Units\",\"link\":\"/docs/units\"},{\"text\":\"Towns and Cities\",\"link\":\"/docs/cities\"}]},{\"text\":\"World Geolocation\",\"items\":[{\"text\":\"Regions\",\"link\":\"/docs/world/regions\"},{\"text\":\"Subregions\",\"link\":\"/docs/world/subregions\"},{\"text\":\"Countries\",\"link\":\"/docs/world/countries\"},{\"text\":\"States\",\"link\":\"/docs/world/states\"},{\"text\":\"Cities\",\"link\":\"/docs/world/cities\"}]},{\"text\":\"Car API\",\"link\":\"/docs/cars\",\"items\":[{\"text\":\"Countries/Origins\",\"link\":\"/docs/cars/countries\"},{\"text\":\"Make Years\",\"link\":\"/docs/cars/years\"},{\"text\":\"Manufacturers\",\"link\":\"/docs/cars/manufacturers\"},{\"text\":\"Vehicles\",\"link\":\"/docs/cars/vehicles\"},{\"text\":\"Derivatives\",\"link\":\"/docs/cars/derivatives\"},{\"text\":\"Mileages\",\"link\":\"/docs/cars/mileages\"},{\"text\":\"Engines\",\"link\":\"/docs/cars/engines\"},{\"text\":\"Models\",\"link\":\"/docs/cars/models\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/toneflix/naija-places\"}],\"footer\":{\"message\":\"Released under the MIT License.\",\"copyright\":\"Copyright © 2025 Toneflix\"},\"lastUpdated\":{\"text\":\"Last updated\",\"formatOptions\":{\"dateStyle\":\"full\",\"timeStyle\":\"medium\"}}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>
<script>window.__VP_HASH_MAP__=JSON.parse("{\"api-documentation.md\":\"CEEkJfzn\",\"demo.md\":\"wfvwDRu5\",\"docs_cars.md\":\"Df_kLrpg\",\"docs_cars_countries.md\":\"BYbVQRIu\",\"docs_cars_demo.md\":\"DzDd7Bhv\",\"docs_cars_derivatives.md\":\"DHZJeyN4\",\"docs_cars_engines.md\":\"BiTndVZC\",\"docs_cars_manufacturers.md\":\"CT_hzqMx\",\"docs_cars_mileages.md\":\"CH4tW-mo\",\"docs_cars_models.md\":\"Bx2m1FZi\",\"docs_cars_vehicles.md\":\"DWbK3QYk\",\"docs_cars_years.md\":\"DWNiAslX\",\"docs_cities.md\":\"ozRUQclA\",\"docs_lgas.md\":\"Chu_29tg\",\"docs_states.md\":\"CA18y45c\",\"docs_units.md\":\"Dw1DqmP7\",\"docs_wards.md\":\"DcjA-D77\",\"docs_world_cities.md\":\"DX0p81dn\",\"docs_world_countries.md\":\"rdN32XUE\",\"docs_world_demo.md\":\"C_d4gnF5\",\"docs_world_regions.md\":\"_ZRYXW2G\",\"docs_world_states.md\":\"CkbEz_wk\",\"docs_world_subregions.md\":\"c0FZvxKe\",\"get-started.md\":\"DGvNDmi0\",\"index.md\":\"DDsuk9lT\",\"portal_api-key.md\":\"DCYOTvIL\",\"portal_api-keys.md\":\"8eR_ppbv\",\"portal_home.md\":\"4gIexFq8\",\"portal_login.md\":\"CHjmis0K\",\"portal_register.md\":\"D5CBve8j\",\"portal_reset-password.md\":\"CZMi9D65\"}");window.__VP_SITE_DATA__=JSON.parse("{\"lang\":\"en-US\",\"dir\":\"ltr\",\"title\":\"Toneflix Places API\",\"description\":\"One api to rule them all, query all states, Local government areas, Wards, Polling Units and Towns in Nigeria + all world regions, countries, states and cities.\",\"base\":\"/\",\"head\":[],\"router\":{\"prefetchLinks\":true},\"appearance\":true,\"themeConfig\":{\"logo\":\"/docs/logo.png\",\"nav\":[{\"text\":\"Home\",\"link\":\"/\"},{\"text\":\"Demo\",\"link\":\"/demo\"},{\"text\":\"Portal\",\"link\":\"/portal/home\"}],\"sidebar\":[{\"text\":\"Usage\",\"items\":[{\"text\":\"Api Documentation\",\"link\":\"/api-documentation\"}]},{\"text\":\"Naija Geolocation\",\"items\":[{\"text\":\"States\",\"link\":\"/docs/states\"},{\"text\":\"LGAs\",\"link\":\"/docs/lgas\"},{\"text\":\"Wards\",\"link\":\"/docs/wards\"},{\"text\":\"Polling Units\",\"link\":\"/docs/units\"},{\"text\":\"Towns and Cities\",\"link\":\"/docs/cities\"}]},{\"text\":\"World Geolocation\",\"items\":[{\"text\":\"Regions\",\"link\":\"/docs/world/regions\"},{\"text\":\"Subregions\",\"link\":\"/docs/world/subregions\"},{\"text\":\"Countries\",\"link\":\"/docs/world/countries\"},{\"text\":\"States\",\"link\":\"/docs/world/states\"},{\"text\":\"Cities\",\"link\":\"/docs/world/cities\"}]},{\"text\":\"Car API\",\"link\":\"/docs/cars\",\"items\":[{\"text\":\"Countries/Origins\",\"link\":\"/docs/cars/countries\"},{\"text\":\"Make Years\",\"link\":\"/docs/cars/years\"},{\"text\":\"Manufacturers\",\"link\":\"/docs/cars/manufacturers\"},{\"text\":\"Vehicles\",\"link\":\"/docs/cars/vehicles\"},{\"text\":\"Derivatives\",\"link\":\"/docs/cars/derivatives\"},{\"text\":\"Mileages\",\"link\":\"/docs/cars/mileages\"},{\"text\":\"Engines\",\"link\":\"/docs/cars/engines\"},{\"text\":\"Models\",\"link\":\"/docs/cars/models\"}]}],\"socialLinks\":[{\"icon\":\"github\",\"link\":\"https://github.com/toneflix/naija-places\"}],\"footer\":{\"message\":\"Released under the MIT License.\",\"copyright\":\"Copyright © 2025 Toneflix\"},\"lastUpdated\":{\"text\":\"Last updated\",\"formatOptions\":{\"dateStyle\":\"full\",\"timeStyle\":\"medium\"}}},\"locales\":{},\"scrollOffset\":134,\"cleanUrls\":true}");</script>

</body>
</html>
Loading

0 comments on commit 0b73cd5

Please sign in to comment.