mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
clean: remove views (#958)
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -228,7 +228,6 @@ return [
|
||||
App\Providers\HorizonServiceProvider::class,
|
||||
App\Providers\PulseServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\ViewableServiceProvider::class,
|
||||
])->toArray(),
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
@@ -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
@@ -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));
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user