clean: remove views (#958)

This commit is contained in:
Kyrch
2025-09-05 14:16:44 -03:00
committed by GitHub
parent 8c62a8e892
commit 128a2bf9cf
34 changed files with 18 additions and 749 deletions
@@ -1,45 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Actions\Models\Aggregate;
use App\Actions\ActionResult;
use App\Enums\Actions\ActionStatus;
use Exception;
use Illuminate\Support\Facades\DB;
class SyncViewAggregatesAction
{
/**
* @throws Exception
*/
public function handle(): ActionResult
{
try {
DB::statement('
CREATE TEMPORARY TABLE tmp_view_aggregates AS
SELECT viewable_id, viewable_type, COUNT(*) as value
FROM views
GROUP BY viewable_type, viewable_id
');
DB::statement('
INSERT INTO view_aggregates (viewable_id, viewable_type, value)
SELECT viewable_id, viewable_type, value
FROM tmp_view_aggregates
ON DUPLICATE KEY UPDATE value = VALUES(value)
');
} catch (Exception $e) {
return new ActionResult(
ActionStatus::FAILED,
$e->getMessage()
);
}
return new ActionResult(
ActionStatus::PASSED,
'View aggregates synced successfully',
);
}
}
@@ -1,48 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Actions\Storage\Admin\Dump;
use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories;
use App\Models\Aggregate\ViewAggregate;
use App\Models\Service\View;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class DumpViewAction extends DumpAction
{
use ReconcilesDumpRepositories;
final public const FILENAME_PREFIX = 'animethemes-db-dump-view-';
/**
* The list of tables to include in the dump.
*
* @return array
*/
protected function allowedTables(): array
{
return [
ViewAggregate::TABLE,
View::TABLE,
];
}
/**
* The temporary path for the database dump.
* Note: The dumper library does not support writing to disk, so we have to write to the local filesystem first.
* Pattern: "animethemes-db-dump-view-{milliseconds from epoch}.sql".
*/
protected function getDumpFile(): string
{
$filesystem = Storage::disk('local');
return Str::of($filesystem->path(''))
->append(DumpViewAction::FILENAME_PREFIX)
->append(strval(Date::now()->valueOf()))
->append('.sql')
->__toString();
}
}
@@ -11,12 +11,9 @@ use App\Contracts\GraphQL\Fields\RequiredOnCreation;
use App\Contracts\GraphQL\Fields\RequiredOnUpdate;
use App\Contracts\GraphQL\Fields\UpdatableField;
use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Types\BaseType;
use App\GraphQL\Support\Argument\Argument;
use App\GraphQL\Support\Argument\BindableArgument;
use App\GraphQL\Support\Argument\SortArgument;
use App\GraphQL\Support\Filter\Filter;
use Illuminate\Support\Arr;
trait ResolvesArguments
{
@@ -58,16 +55,6 @@ trait ResolvesArguments
->toArray();
}
/**
* Resolve the fields into arguments that are used for sorting.
*
* @return SortArgument[]
*/
protected function resolveSortArguments(BaseType $type): array
{
return Arr::wrap(new SortArgument($type));
}
/**
* Resolve the fields into arguments that are used for mutations of type create.
*
@@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands\Models;
use App\Actions\Models\Aggregate\SyncViewAggregatesAction;
use App\Console\Commands\BaseCommand;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Facades\Validator as ValidatorFacade;
class SyncViewAggregatesCommand extends BaseCommand
{
protected $signature = 'view:sync-aggregates';
protected $description = 'Synchronizes views in the aggregates table';
public function handle(): int
{
$action = new SyncViewAggregatesAction();
$result = $action->handle();
$result->toLog();
$result->toConsole($this);
return $result->hasFailed() ? 1 : 0;
}
protected function validator(): Validator
{
return ValidatorFacade::make($this->options(), []);
}
}
@@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands\Storage\Admin;
use App\Actions\Storage\Admin\Dump\DumpViewAction;
class ViewDumpCommand extends DumpCommand
{
protected $signature = 'db:dump-view
{--comments : Write additional information in the MySQL dump such as program version, server version and host}
{--data-only : Dump only the data without the schema in PostgreSQL dump}
{--default-character-set=utf8 : Specify default character set in MySQL dump}
{--extended-insert : Use multiple-row insert syntax in MySQL dump}
{--inserts : Dump data as INSERT commands rather than COPY in PostgreSQL dump}
{--lock-tables : Lock all tables before dumping them to MySQL dump}
{--no-create-info : Turn off CREATE TABLE statements in MySQL dump}
{--quick : Retrieve rows for a table from the server one row at a time in MySQL dump}
{--set-gtid-purged=AUTO : Add SET GTID_PURGED to output in MySQL dump}
{--single-transaction=true : Issue a BEGIN SQL statement before dumping data from server for MySQL dump}
{--skip-column-statistics : Turn off ANALYZE table statements in the MySQL dump}
{--skip-comments : Do not write additional information in the MySQL dump}
{--skip-extended-insert : Turn off extended-insert in MySQL dump}
{--skip-lock-tables : Turn off locking tables before dumping to MySQL dump}
{--skip-quick : Do not retrieve rows for a table from the server one row at a time in MySQL dump}
';
protected $description = 'Produces sanitized database dump, targeting view-related tables for seeding purposes';
protected function action(): DumpViewAction
{
return new DumpViewAction($this->options());
}
}
-7
View File
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Console;
use App\Console\Commands\Models\SyncLikeAggregatesCommand;
use App\Console\Commands\Models\SyncViewAggregatesCommand;
use App\Console\Commands\Repositories\Storage\Admin\DumpReconcileCommand;
use App\Console\Commands\Storage\Admin\AdminDumpCommand;
use App\Console\Commands\Storage\Admin\AuthDumpCommand;
@@ -159,12 +158,6 @@ class Kernel extends ConsoleKernel
->storeOutput()
->daily();
$schedule->command(SyncViewAggregatesCommand::class)
->withoutOverlapping()
->runInBackground()
->storeOutput()
->weekly();
$schedule->command(UpdateDisposableDomainsCommand::class)
->withoutOverlapping()
->runInBackground()
-2
View File
@@ -8,8 +8,6 @@ class FeatureConstants
{
final public const ALLOW_DISCORD_NOTIFICATIONS = 'allow_discord_notifications';
final public const ALLOW_VIEW_RECORDING = 'allow_view_recording';
final public const AUDIO_BITRATE_RESTRICTION = 'audio_bitrate_restriction';
final public const ENABLED_ONLY_ON_LOCALHOST = 'enabled_only_on_localhost';
@@ -43,10 +43,10 @@ class GlobalSearchScoutProvider implements GlobalSearchProvider
}
return new GlobalSearchResult(
title: $resource::getGlobalSearchResultTitle($record),
url: $url,
details: $resource::getGlobalSearchResultDetails($record),
actions: $resource::getGlobalSearchResultActions($record),
$resource::getGlobalSearchResultTitle($record),
$url,
$resource::getGlobalSearchResultDetails($record),
$resource::getGlobalSearchResultActions($record),
);
})
->filter();
@@ -22,6 +22,6 @@ class AudioViewsCountField extends CountAggregateField implements DeprecatedFiel
public function deprecationReason(): string
{
return 'We will no longer track views.';
return 'We are no longer tracking views.';
}
}
@@ -22,6 +22,6 @@ class VideoViewsCountField extends CountAggregateField implements DeprecatedFiel
public function deprecationReason(): string
{
return 'We will no longer track views. Use likesCount instead';
return 'We are no longer tracking views. Use likesCount instead';
}
}
+2 -1
View File
@@ -12,6 +12,7 @@ use App\GraphQL\Schema\Types\BaseType;
use App\GraphQL\Support\Argument\Argument;
use App\GraphQL\Support\Argument\FirstArgument;
use App\GraphQL\Support\Argument\PageArgument;
use App\GraphQL\Support\Argument\SortArgument;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Rebing\GraphQL\Support\Facades\GraphQL;
@@ -69,7 +70,7 @@ abstract class BaseQuery extends Query
}
if ($baseType instanceof BaseType && $this instanceof EloquentPaginationQuery) {
$arguments[] = $this->resolveSortArguments($baseType);
$arguments[] = new SortArgument($baseType);
}
return Arr::flatten($arguments);
+2 -1
View File
@@ -9,6 +9,7 @@ use App\GraphQL\Schema\Fields\Field;
use App\GraphQL\Schema\Types\BaseType;
use App\GraphQL\Support\Argument\Argument;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
abstract class Filter
{
@@ -17,7 +18,7 @@ abstract class Filter
protected mixed $defaultValue = null,
) {}
public static function getValueWithResolvers(BaseType $baseType)
public static function getValueWithResolvers(BaseType $baseType): Collection
{
return collect($baseType->fieldClasses())
->filter(fn (Field $field) => $field instanceof FilterableField)
+2 -1
View File
@@ -13,6 +13,7 @@ use App\GraphQL\Schema\Unions\BaseUnion;
use App\GraphQL\Support\Argument\Argument;
use App\GraphQL\Support\Argument\FirstArgument;
use App\GraphQL\Support\Argument\PageArgument;
use App\GraphQL\Support\Argument\SortArgument;
use App\GraphQL\Support\Argument\TrashedArgument;
use GraphQL\Type\Definition\Type;
use Illuminate\Database\Eloquent\Model;
@@ -94,7 +95,7 @@ abstract class Relation
$arguments[] = new PageArgument();
if ($type instanceof BaseType) {
$arguments[] = $this->resolveSortArguments($this->baseType);
$arguments[] = new SortArgument($type);
}
}
-2
View File
@@ -8,7 +8,6 @@ use App\Http\Middleware\Api\SetAcceptJsonHeader;
use App\Http\Middleware\Auth\Authenticate;
use App\Http\Middleware\Auth\RedirectIfAuthenticated;
use App\Http\Middleware\LogRequest;
use App\Http\Middleware\Models\RecordView;
use App\Http\Middleware\ThrottleRequestsWithService;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustHosts;
@@ -106,7 +105,6 @@ class Kernel extends HttpKernel
'can' => Authorize::class,
'guest' => RedirectIfAuthenticated::class,
'password.confirm' => RequirePassword::class,
'record_view' => RecordView::class,
'signed' => ValidateSignature::class,
'throttle' => ThrottleRequestsWithService::class,
'verified' => EnsureEmailIsVerified::class,
-48
View File
@@ -1,48 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware\Models;
use App\Constants\FeatureConstants;
use Closure;
use CyrildeWit\EloquentViewable\Contracts\Viewable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Laravel\Pennant\Feature;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response;
class RecordView
{
/**
* The viewable model.
*
* @var (Viewable&Model)|null
*/
public $model = null;
/**
* @param Closure(Request): mixed $next
*/
public function handle(Request $request, Closure $next, string $modelKey): mixed
{
$this->model = $request->route($modelKey);
if (! $this->model instanceof Viewable) {
throw new RuntimeException('record_view should only be configured for viewable models');
}
return $next($request);
}
/**
* Handle tasks after the response has been sent to the browser.
*/
public function terminate(Request $request, Response $response): void
{
if (Feature::for(null)->active(FeatureConstants::ALLOW_VIEW_RECORDING) && $this->model instanceof Viewable) {
views($this->model)->cooldown(now()->addMinutes(5))->record();
}
}
}
+1 -2
View File
@@ -4,12 +4,11 @@ declare(strict_types=1);
namespace App\Models\Aggregate;
use CyrildeWit\EloquentViewable\Contracts\Viewable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property Viewable $viewable
* @property Model $viewable
* @property string $viewable_type
* @property int $viewable_id
* @property int $value
-1
View File
@@ -65,7 +65,6 @@ class Playlist extends BaseModel implements HasAggregateLikes, HasHashids, HasIm
final public const RELATION_LAST = 'last';
final public const RELATION_TRACKS = 'tracks';
final public const RELATION_USER = 'user';
final public const RELATION_VIEWS = 'views';
/**
* Is auditing disabled?
-44
View File
@@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Models\Service;
use App\Models\Auth\User;
use CyrildeWit\EloquentViewable\View as BaseView;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;
/**
* @property string|null $referer
* @property string|null $user_agent
* @property int|null $user_id
* @property User|null $user
*/
class View extends BaseView
{
final public const TABLE = 'views';
/**
* Perform any actions required after the model boots.
*/
protected static function booted(): void
{
parent::booted();
static::creating(function (View $view) {
$view->user_agent = Request::userAgent();
$view->user_id = Auth::id();
$view->referer = Request::header('referer');
});
}
/**
* @return BelongsTo<User, $this>
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
}
+1 -5
View File
@@ -16,8 +16,6 @@ use App\Events\Wiki\Audio\AudioForceDeleting;
use App\Events\Wiki\Audio\AudioRestored;
use App\Events\Wiki\Audio\AudioUpdated;
use App\Models\BaseModel;
use CyrildeWit\EloquentViewable\Contracts\Viewable;
use CyrildeWit\EloquentViewable\InteractsWithViews;
use Database\Factories\Wiki\AudioFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
@@ -35,11 +33,10 @@ use Illuminate\Support\Collection;
*
* @method static AudioFactory factory(...$parameters)
*/
class Audio extends BaseModel implements HasAggregateViews, SoftDeletable, Streamable, Viewable
class Audio extends BaseModel implements HasAggregateViews, SoftDeletable, Streamable
{
use AggregatesView;
use HasFactory;
use InteractsWithViews;
use Reportable;
use SoftDeletes;
@@ -54,7 +51,6 @@ class Audio extends BaseModel implements HasAggregateViews, SoftDeletable, Strea
final public const ATTRIBUTE_SIZE = 'size';
final public const RELATION_VIDEOS = 'videos';
final public const RELATION_VIEWS = 'views';
/**
* The attributes that are mass assignable.
+1 -5
View File
@@ -29,8 +29,6 @@ use App\Models\List\Playlist\PlaylistTrack;
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
use App\Models\Wiki\Video\VideoScript;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
use CyrildeWit\EloquentViewable\Contracts\Viewable;
use CyrildeWit\EloquentViewable\InteractsWithViews;
use Database\Factories\Wiki\VideoFactory;
use Elastic\ScoutDriverPlus\Searchable;
use Illuminate\Database\Eloquent\Builder;
@@ -65,13 +63,12 @@ use Illuminate\Support\Collection;
*
* @method static VideoFactory factory(...$parameters)
*/
class Video extends BaseModel implements HasAggregateLikes, HasAggregateViews, Likeable, SoftDeletable, Streamable, Viewable
class Video extends BaseModel implements HasAggregateLikes, HasAggregateViews, Likeable, SoftDeletable, Streamable
{
use AggregatesLike;
use AggregatesView;
use HasFactory;
use InteractsWithLikes;
use InteractsWithViews;
use Reportable;
use Searchable;
use SoftDeletes;
@@ -104,7 +101,6 @@ class Video extends BaseModel implements HasAggregateLikes, HasAggregateViews, L
final public const RELATION_SCRIPT = 'videoscript';
final public const RELATION_SONG = 'animethemeentries.animetheme.song';
final public const RELATION_TRACKS = 'tracks';
final public const RELATION_VIEWS = 'views';
/**
* The attributes that are mass assignable.
-6
View File
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace App\Providers;
use App\Http\Middleware\Models\RecordView;
use App\Models\Document\Page;
use App\Models\Wiki\Anime;
use App\Models\Wiki\Anime\AnimeSynonym;
@@ -124,9 +123,4 @@ class AppServiceProvider extends ServiceProvider
'artistsong' => ArtistSong::class,
]);
}
public function register(): void
{
$this->app->singleton(RecordView::class);
}
}
-19
View File
@@ -1,19 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Models\Service\View;
use CyrildeWit\EloquentViewable\Contracts\View as ViewContract;
use CyrildeWit\EloquentViewable\EloquentViewableServiceProvider;
class ViewableServiceProvider extends EloquentViewableServiceProvider
{
public function register(): void
{
parent::register();
$this->app->bind(ViewContract::class, View::class);
}
}
-1
View File
@@ -28,7 +28,6 @@
"babenkoivan/elastic-migrations": "^3.4.1",
"babenkoivan/elastic-scout-driver-plus": "^4.8",
"bepsvpt/secure-headers": "^7.5",
"cyrildewit/eloquent-viewable": "^7.0.6",
"fakerphp/faker": "^1.24.1",
"filament/filament": "^4.0.4",
"flowframe/laravel-trend": ">=0.4",
Generated
+1 -137
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "022b4fe4f9b0037a49aacb05ded35c30",
"content-hash": "20c6df267bc580b71b6ff0b7b1762ca3",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -1232,90 +1232,6 @@
],
"time": "2024-07-16T11:13:48+00:00"
},
{
"name": "cyrildewit/eloquent-viewable",
"version": "v7.0.6",
"source": {
"type": "git",
"url": "https://github.com/cyrildewit/eloquent-viewable.git",
"reference": "c4153d03f8c1fd7bd9397844e94c4cefc01b2cf2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cyrildewit/eloquent-viewable/zipball/c4153d03f8c1fd7bd9397844e94c4cefc01b2cf2",
"reference": "c4153d03f8c1fd7bd9397844e94c4cefc01b2cf2",
"shasum": ""
},
"require": {
"illuminate/cache": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"illuminate/contracts": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"illuminate/cookie": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"illuminate/database": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"illuminate/http": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"illuminate/support": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"jaybizzle/crawler-detect": "^1.0",
"nesbot/carbon": "^2.0|^3.0",
"php": "^7.4|^8.0"
},
"require-dev": {
"illuminate/config": "^6.0|^7.0|^8.74|^9.0|^10.0|^11.0|^12.0",
"laravel/legacy-factories": "^1.1|^1.3",
"mockery/mockery": "^1.2.4",
"orchestra/testbench": "^4.9.1|^5.9.1|^6.6.1|^7.0.0|^8.0.0",
"phpunit/phpunit": "^9.6.0|^10.0||^11.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"CyrildeWit\\EloquentViewable\\EloquentViewableServiceProvider"
]
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"CyrildeWit\\EloquentViewable\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Cyril de Wit",
"email": "info@cyrildewit.nl",
"homepage": "http://cyrildewit.nl",
"role": "Developer"
}
],
"description": "Laravel package that allows you to associate views with Eloquent models",
"homepage": "https://github.com/cyrildewit/eloquent-viewable",
"keywords": [
"counter",
"eloquent",
"hits",
"laravel",
"viewable",
"views",
"visitable",
"visits"
],
"support": {
"issues": "https://github.com/cyrildewit/eloquent-viewable/issues",
"source": "https://github.com/cyrildewit/eloquent-viewable/tree/v7.0.6"
},
"funding": [
{
"url": "https://github.com/cyrildewit",
"type": "github"
}
],
"time": "2025-04-02T23:09:30+00:00"
},
{
"name": "danharrin/date-format-converter",
"version": "v0.3.1",
@@ -3316,58 +3232,6 @@
},
"time": "2025-03-17T16:59:46+00:00"
},
{
"name": "jaybizzle/crawler-detect",
"version": "v1.3.5",
"source": {
"type": "git",
"url": "https://github.com/JayBizzle/Crawler-Detect.git",
"reference": "fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34",
"reference": "fbf1a3e81d61b088e7af723fb3c7a4ee92ac7e34",
"shasum": ""
},
"require": {
"php": ">=7.1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.5|^6.5|^9.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Jaybizzle\\CrawlerDetect\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Beech",
"email": "m@rkbee.ch",
"role": "Developer"
}
],
"description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent",
"homepage": "https://github.com/JayBizzle/Crawler-Detect/",
"keywords": [
"crawler",
"crawler detect",
"crawler detector",
"crawlerdetect",
"php crawler detect"
],
"support": {
"issues": "https://github.com/JayBizzle/Crawler-Detect/issues",
"source": "https://github.com/JayBizzle/Crawler-Detect/tree/v1.3.5"
},
"time": "2025-06-11T17:58:05+00:00"
},
{
"name": "kirschbaum-development/eloquent-power-joins",
"version": "4.2.8",
-1
View File
@@ -228,7 +228,6 @@ return [
App\Providers\HorizonServiceProvider::class,
App\Providers\PulseServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\ViewableServiceProvider::class,
])->toArray(),
/*
-109
View File
@@ -1,109 +0,0 @@
<?php
declare(strict_types=1);
return [
/*
|--------------------------------------------------------------------------
| Eloquent Models
|--------------------------------------------------------------------------
*/
'models' => [
/*
* Here you can configure the default `View` model.
*/
'view' => [
'table_name' => 'views',
'connection' => env('DB_CONNECTION', 'mysql_prod'),
],
],
/*
|--------------------------------------------------------------------------
| Cache Configuration
|--------------------------------------------------------------------------
*/
'cache' => [
/*
* Everthing will be stored under the following key.
*/
'key' => 'cyrildewit.eloquent-viewable.cache',
/*
* Here you may define the cache store that should be used.
*/
'store' => env('CACHE_DRIVER', 'file'),
],
/*
|--------------------------------------------------------------------------
| Cooldown Configuration
|--------------------------------------------------------------------------
*/
'cooldown' => [
/*
* Everthing will be stored under the following key in the session.
*/
'key' => 'cyrildewit.eloquent-viewable.cooldowns',
],
/*
|--------------------------------------------------------------------------
| Ignore Bots
|--------------------------------------------------------------------------
|
| If you want to ignore bots, you can specify that here. The default
| service that determines if a visitor is a crawler is a package
| by JayBizzle called CrawlerDetect.
|
*/
'ignore_bots' => true,
/*
|--------------------------------------------------------------------------
| Do Not Track Header
|--------------------------------------------------------------------------
|
| If you want to honor the DNT header, you can specify that here. We won't
| record views from visitors with the Do Not Track header.
|
*/
'honor_dnt' => false,
/*
|--------------------------------------------------------------------------
| Cookies
|--------------------------------------------------------------------------
|
| This package binds visitors to views using a cookie. If you want to
| give this cookie a custom name, you can specify that here.
|
*/
'visitor_cookie_key' => 'eloquent_viewable',
/*
|--------------------------------------------------------------------------
| Ignore IP Addresses
|--------------------------------------------------------------------------
|
| Ignore views of the following IP addresses.
|
*/
'ignored_ip_addresses' => [
// '127.0.0.1',
],
];
@@ -1,65 +0,0 @@
<?php
declare(strict_types=1);
use App\Models\Auth\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Schema\Builder;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* The database schema.
*
* @var Builder
*/
protected Builder $schema;
/**
* The table name.
*
* @var string
*/
protected mixed $table;
public function __construct()
{
$this->schema = Schema::connection(
config('eloquent-viewable.models.view.connection')
);
$this->table = config('eloquent-viewable.models.view.table_name');
}
/**
* Run the migrations.
*/
public function up(): void
{
if (! $this->schema->hasTable($this->table)) {
$this->schema->create($this->table, function (Blueprint $table) {
$table->bigIncrements('id');
$table->morphs('viewable');
$table->text('visitor')->nullable();
$table->string('collection')->nullable();
$table->timestamp('viewed_at')->useCurrent();
$table->unsignedBigInteger('user_id')->nullable();
$table->foreign('user_id')->references(User::ATTRIBUTE_ID)->on(User::TABLE)->nullOnDelete();
$table->string('referer', 1000)->nullable();
$table->string('user_agent', 1000)->nullable();
});
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists($this->table);
}
};
@@ -31,7 +31,6 @@ class FeatureSeeder extends Seeder
Feature::deactivate(AllowVideoStreams::class);
Feature::deactivate(FeatureConstants::ALLOW_DISCORD_NOTIFICATIONS);
Feature::deactivate(FeatureConstants::ALLOW_VIEW_RECORDING);
Feature::activate(FeatureConstants::AUDIO_BITRATE_RESTRICTION);
Feature::activate(FeatureConstants::ENABLED_ONLY_ON_LOCALHOST);
Feature::deactivate(FeatureConstants::IGNORE_ALL_FILE_VALIDATIONS);
+1 -1
View File
@@ -15,4 +15,4 @@ $isAudioStreamingAllowed = Str::of(EnsureFeaturesAreActive::class)
Route::get('/{audio}', [AudioController::class, 'show'])
->name('audio.show')
->middleware([$isAudioStreamingAllowed, 'record_view:audio']);
->middleware([$isAudioStreamingAllowed]);
+1 -1
View File
@@ -15,4 +15,4 @@ $isVideoStreamingAllowed = Str::of(EnsureFeaturesAreActive::class)
Route::get('/{video}', [VideoController::class, 'show'])
->name('video.show')
->middleware([$isVideoStreamingAllowed, 'record_view:video', 'throttle:video']);
->middleware([$isVideoStreamingAllowed, 'throttle:video']);
@@ -3,14 +3,12 @@
declare(strict_types=1);
use App\Constants\Config\AudioConstants;
use App\Constants\FeatureConstants;
use App\Enums\Auth\SpecialPermission;
use App\Enums\Http\StreamingMethod;
use App\Features\AllowAudioStreams;
use App\Models\Auth\User;
use App\Models\Wiki\Audio;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Storage;
use Laravel\Pennant\Feature;
@@ -66,47 +64,6 @@ test('cannot stream soft deleted audio', function () {
$response->assertNotFound();
});
test('view recording not allowed', function () {
Storage::fake(Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowAudioStreams::class);
Feature::deactivate(FeatureConstants::ALLOW_VIEW_RECORDING);
$audio = Audio::factory()->createOne();
get(route('audio.show', ['audio' => $audio]));
$this->assertEquals(0, $audio->views()->count());
});
test('view recording is allowed', function () {
Storage::fake(Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowAudioStreams::class);
Feature::activate(FeatureConstants::ALLOW_VIEW_RECORDING);
$audio = Audio::factory()->createOne();
get(route('audio.show', ['audio' => $audio]));
$this->assertEquals(1, $audio->views()->count());
});
test('view recording cooldown', function () {
Storage::fake(Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowAudioStreams::class);
Feature::activate(FeatureConstants::ALLOW_VIEW_RECORDING);
$audio = Audio::factory()->createOne();
Collection::times(fake()->randomDigitNotNull(), function () use ($audio) {
get(route('audio.show', ['audio' => $audio]));
});
$this->assertEquals(1, $audio->views()->count());
});
test('invalid streaming method error', function () {
Storage::fake(Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED));
@@ -50,19 +50,6 @@ test('cannot stream soft deleted video', function () {
$response->assertNotFound();
});
test('view recording not allowed', function () {
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowVideoStreams::class);
Feature::deactivate(FeatureConstants::ALLOW_VIEW_RECORDING);
$video = Video::factory()->createOne();
get(route('video.show', ['video' => $video]));
$this->assertEquals(0, $video->views()->count());
});
test('video streaming permitted for bypass', function () {
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
@@ -83,34 +70,6 @@ test('video streaming permitted for bypass', function () {
$response->assertSuccessful();
});
test('view recording is allowed', function () {
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowVideoStreams::class);
Feature::activate(FeatureConstants::ALLOW_VIEW_RECORDING);
$video = Video::factory()->createOne();
get(route('video.show', ['video' => $video]));
$this->assertEquals(1, $video->views()->count());
});
test('view recording cooldown', function () {
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
Feature::activate(AllowVideoStreams::class);
Feature::activate(FeatureConstants::ALLOW_VIEW_RECORDING);
$video = Video::factory()->createOne();
Collection::times(fake()->randomDigitNotNull(), function () use ($video) {
get(route('video.show', ['video' => $video]));
});
$this->assertEquals(1, $video->views()->count());
});
test('invalid streaming method error', function () {
Storage::fake(Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED));
-12
View File
@@ -6,9 +6,7 @@ use App\Constants\Config\AudioConstants;
use App\Events\Wiki\Audio\AudioForceDeleting;
use App\Models\Wiki\Audio;
use App\Models\Wiki\Video;
use CyrildeWit\EloquentViewable\View;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Http\Testing\File;
use Illuminate\Support\Facades\Config;
@@ -41,16 +39,6 @@ test('videos', function () {
$this->assertInstanceOf(Video::class, $audio->videos()->first());
});
test('views', function () {
$audio = Audio::factory()->createOne();
views($audio)->record();
$this->assertInstanceOf(MorphMany::class, $audio->views());
$this->assertEquals(1, $audio->views()->count());
$this->assertInstanceOf(View::class, $audio->views()->first());
});
test('audio storage deletion', function () {
$fs = Storage::fake(Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED));
$file = File::fake()->create(fake()->word().'.ogg', fake()->randomDigitNotNull());
-12
View File
@@ -16,12 +16,10 @@ use App\Models\Wiki\Audio;
use App\Models\Wiki\Video;
use App\Models\Wiki\Video\VideoScript;
use App\Pivots\Wiki\AnimeThemeEntryVideo;
use CyrildeWit\EloquentViewable\View;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Http\Testing\File;
use Illuminate\Support\Arr;
@@ -71,16 +69,6 @@ test('has subtitle', function () {
$this->assertIsString($video->getSubtitle());
});
test('views', function () {
$video = Video::factory()->createOne();
views($video)->record();
$this->assertInstanceOf(MorphMany::class, $video->views());
$this->assertEquals(1, $video->views()->count());
$this->assertInstanceOf(View::class, $video->views()->first());
});
test('appends tags', function () {
$video = Video::factory()->createOne();