mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0464406d3 | |||
| 134a82ecc9 | |||
| 3e71ff8ace | |||
| 39250632b2 | |||
| 3cdf824e47 | |||
| 681f07ecc9 | |||
| 9028557f4c | |||
| cea28e61f1 | |||
| fed083b5d3 | |||
| 2e97bb78ea | |||
| b320882d6d | |||
| a8801edec9 | |||
| ed33d4154a | |||
| d3a8d58cd2 | |||
| 4cf68a96ac | |||
| 8b6b196c99 | |||
| 99cf085666 | |||
| 1d93000c6f | |||
| 57cc3d508d | |||
| 2792961a06 | |||
| 95134a384c | |||
| 16c8d1781b | |||
| 23afebc70e | |||
| 7d5cd09768 | |||
| b1e2a65b85 |
+2
-2
@@ -3,9 +3,9 @@ root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
|
||||
@@ -73,6 +73,12 @@ REDIS_SCHEME=tcp
|
||||
REDIS_DB=0
|
||||
REDIS_CACHE_DB=1
|
||||
|
||||
# dump
|
||||
DUMP_DISK=dumps
|
||||
DUMP_DISK_ROOT=
|
||||
DUMP_URL=http://localhost
|
||||
DUMP_PATH=
|
||||
|
||||
# elastic client
|
||||
ELASTIC_CONNECTION=default
|
||||
ELASTIC_HOST=http://localhost:9200
|
||||
@@ -136,11 +142,31 @@ AUDIO_FRA_STREAM_READS=
|
||||
AUDIO_FRA_DISABLE_ASSERTS=
|
||||
AUDIO_FRA_VISIBILITY=
|
||||
|
||||
DUMP_ACCESS_KEY_ID=
|
||||
DUMP_SECRET_ACCESS_KEY=
|
||||
DUMP_DEFAULT_REGION=
|
||||
DUMP_ENDPOINT=
|
||||
DUMP_BUCKET=
|
||||
DUMP_STREAM_READS=
|
||||
DUMP_DISABLE_ASSERTS=
|
||||
DUMP_VISIBILITY=
|
||||
|
||||
SCRIPT_ACCESS_KEY_ID=
|
||||
SCRIPT_SECRET_ACCESS_KEY=
|
||||
SCRIPT_DEFAULT_REGION=
|
||||
SCRIPT_ENDPOINT=
|
||||
SCRIPT_BUCKET=
|
||||
SCRIPT_STREAM_READS=
|
||||
SCRIPT_DISABLE_ASSERTS=
|
||||
SCRIPT_VISIBILITY=
|
||||
|
||||
# flags
|
||||
ALLOW_VIDEO_STREAMS=false
|
||||
ALLOW_AUDIO_STREAMS=false
|
||||
ALLOW_DISCORD_NOTIFICATIONS=false
|
||||
ALLOW_VIEW_RECORDING=false
|
||||
ALLOW_DUMP_DOWNLOADING=false
|
||||
ALLOW_SCRIPT_DOWNLOADING=false
|
||||
|
||||
# fortify
|
||||
FORTIFY_PATH=
|
||||
@@ -241,6 +267,11 @@ VIDEO_NGINX_REDIRECT=
|
||||
VIDEO_ENCODER_VERSION=
|
||||
VIDEO_UPLOAD_DISKS=
|
||||
|
||||
SCRIPT_DISK=scripts
|
||||
SCRIPT_DISK_ROOT=
|
||||
SCRIPT_URL=http://localhost
|
||||
SCRIPT_PATH=/videoscript
|
||||
|
||||
# web
|
||||
WEB_URL=http://localhost
|
||||
WEB_PATH=
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.1'
|
||||
php-version: '8.2'
|
||||
tools: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, phpunit
|
||||
- uses: actions/checkout@v3
|
||||
- name: Copy .env
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.1'
|
||||
php-version: '8.2'
|
||||
tools: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, phpunit
|
||||
- uses: actions/checkout@v3
|
||||
- name: Copy .env
|
||||
|
||||
@@ -2,15 +2,22 @@
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/build
|
||||
/public/audios
|
||||
/public/images
|
||||
/public/videos
|
||||
/public/dumps
|
||||
/public/scripts
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
.phpunit.result.cache
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
/.fleet
|
||||
.vscode
|
||||
.idea
|
||||
auth.json
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Actions;
|
||||
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class ActionResult.
|
||||
@@ -50,4 +52,29 @@ class ActionResult
|
||||
{
|
||||
return ActionStatus::FAILED()->is($this->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to log.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function toLog(): void
|
||||
{
|
||||
$this->hasFailed()
|
||||
? Log::error($this->getMessage())
|
||||
: Log::info($this->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
$this->hasFailed()
|
||||
? $command->error($this->getMessage())
|
||||
: $command->info($this->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Admin\Dump;
|
||||
|
||||
use App\Actions\Http\DownloadAction;
|
||||
use App\Constants\Config\DumpConstants;
|
||||
use App\Models\Admin\Dump;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class DumpDownloadAction.
|
||||
*
|
||||
* @extends DownloadAction<Dump>
|
||||
*/
|
||||
class DumpDownloadAction extends DownloadAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Dump $dump
|
||||
*/
|
||||
public function __construct(Dump $dump)
|
||||
{
|
||||
parent::__construct($dump);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the resource in storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function path(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(DumpConstants::DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http;
|
||||
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* Class DownloadAction.
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*/
|
||||
abstract class DownloadAction implements InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param TModel $model
|
||||
*/
|
||||
public function __construct(protected readonly BaseModel $model)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the resource.
|
||||
*
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function download(): StreamedResponse
|
||||
{
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
return $fs->download($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the resource in storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function path(): string;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class NginxStreamAction.
|
||||
*/
|
||||
abstract class NginxStreamAction extends StreamAction
|
||||
{
|
||||
/**
|
||||
* Stream the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function stream(): Response
|
||||
{
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
// Generate temporary link for the object
|
||||
$temporaryURL = $fs->temporaryUrl($this->streamable->path(), now()->addMinutes(5));
|
||||
|
||||
// Get the url information
|
||||
$url_scheme = parse_url($temporaryURL, PHP_URL_SCHEME);
|
||||
$url_host = parse_url($temporaryURL, PHP_URL_HOST);
|
||||
$url_path_query = parse_url($temporaryURL, PHP_URL_PATH).'?'.parse_url($temporaryURL, PHP_URL_QUERY);
|
||||
|
||||
// Construct the new link for the redirect
|
||||
$link = "{$this->nginxRedirect()}$url_scheme/$url_host$url_path_query";
|
||||
|
||||
$response = new Response();
|
||||
|
||||
$disposition = $response->headers->makeDisposition('inline', $this->streamable->basename());
|
||||
|
||||
return $response->withHeaders([
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Type' => $this->streamable->mimetype(),
|
||||
'Content-Length' => $this->streamable->size(),
|
||||
'Content-Disposition' => $disposition,
|
||||
'X-Accel-Redirect' => $link,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location of the nginx internal redirect.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function nginxRedirect(): string;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http;
|
||||
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* Class ResponseStreamAction.
|
||||
*/
|
||||
abstract class ResponseStreamAction extends StreamAction
|
||||
{
|
||||
/**
|
||||
* Stream the resource.
|
||||
*
|
||||
* @return StreamedResponse
|
||||
*/
|
||||
public function stream(): StreamedResponse
|
||||
{
|
||||
$response = new StreamedResponse();
|
||||
|
||||
$disposition = $response->headers->makeDisposition('inline', $this->streamable->basename());
|
||||
|
||||
$response->headers->replace([
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Type' => $this->streamable->mimetype(),
|
||||
'Content-Length' => $this->streamable->size(),
|
||||
'Content-Disposition' => $disposition,
|
||||
]);
|
||||
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
$response->setCallback(function () use ($fs) {
|
||||
$stream = $fs->readStream($this->streamable->path());
|
||||
fpassthru($stream);
|
||||
fclose($stream);
|
||||
});
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http;
|
||||
|
||||
use App\Contracts\Models\Streamable;
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Class StreamAction.
|
||||
*/
|
||||
abstract class StreamAction implements InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Streamable $streamable
|
||||
*/
|
||||
public function __construct(protected readonly Streamable $streamable)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream the resource.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
abstract public function stream(): Response;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Wiki\Audio;
|
||||
|
||||
use App\Actions\Http\NginxStreamAction;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Models\Wiki\Audio;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class AudioNginxStreamAction.
|
||||
*/
|
||||
class AudioNginxStreamAction extends NginxStreamAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Audio $audio
|
||||
*/
|
||||
public function __construct(Audio $audio)
|
||||
{
|
||||
parent::__construct($audio);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location of the nginx internal redirect.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function nginxRedirect(): string
|
||||
{
|
||||
return Config::get(AudioConstants::NGINX_REDIRECT_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Wiki\Audio;
|
||||
|
||||
use App\Actions\Http\ResponseStreamAction;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Models\Wiki\Audio;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class AudioResponseStreamAction.
|
||||
*/
|
||||
class AudioResponseStreamAction extends ResponseStreamAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Audio $audio
|
||||
*/
|
||||
public function __construct(Audio $audio)
|
||||
{
|
||||
parent::__construct($audio);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Http\DownloadAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ScriptDownloadAction.
|
||||
*
|
||||
* @extends DownloadAction<VideoScript>
|
||||
*/
|
||||
class ScriptDownloadAction extends DownloadAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of the resource in storage.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function path(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Wiki\Video;
|
||||
|
||||
use App\Actions\Http\NginxStreamAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class VideoNginxStreamAction.
|
||||
*/
|
||||
class VideoNginxStreamAction extends NginxStreamAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Video $video
|
||||
*/
|
||||
public function __construct(Video $video)
|
||||
{
|
||||
parent::__construct($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location of the nginx internal redirect.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function nginxRedirect(): string
|
||||
{
|
||||
return Config::get(VideoConstants::NGINX_REDIRECT_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Http\Wiki\Video;
|
||||
|
||||
use App\Actions\Http\ResponseStreamAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class VideoResponseStreamAction.
|
||||
*/
|
||||
class VideoResponseStreamAction extends ResponseStreamAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param Video $video
|
||||
*/
|
||||
public function __construct(Video $video)
|
||||
{
|
||||
parent::__construct($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -53,6 +53,6 @@ class BackfillLargeCoverImageAction extends BackfillStudioImageAction
|
||||
*/
|
||||
protected function getMalImage(ExternalResource $malResource): ?Image
|
||||
{
|
||||
return $this->createImage("https://cdn.myanimelist.net/img/common/companies/$malResource->external_id.png");
|
||||
return $this->createImage("https://cdn.myanimelist.net/images/company/$malResource->external_id.png");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace App\Actions\Models\Wiki\Video\Audio;
|
||||
use App\Actions\ActionResult;
|
||||
use App\Actions\Models\BaseAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositories;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositoriesAction;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
@@ -256,7 +256,7 @@ class BackfillAudioAction extends BaseAction
|
||||
*/
|
||||
protected function reconcileAudio(string $audioPath): ReconcileResults
|
||||
{
|
||||
$action = new ReconcileAudioRepositories();
|
||||
$action = new ReconcileAudioRepositoriesAction();
|
||||
|
||||
/** @var RepositoryInterface $sourceRepository */
|
||||
$sourceRepository = App::make(AudioSourceRepository::class);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Admin\Dump;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Admin\Dump;
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ReconcileDumpRepositories.
|
||||
*
|
||||
* @extends ReconcileRepositoriesAction<Dump>
|
||||
*/
|
||||
class ReconcileDumpRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForCreateDelete(): array
|
||||
{
|
||||
return [
|
||||
Dump::ATTRIBUTE_ID,
|
||||
Dump::ATTRIBUTE_PATH,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for create and delete set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForCreateDelete(): Closure
|
||||
{
|
||||
return fn (Dump $first, Dump $second) => [$first->path] <=> [$second->path];
|
||||
}
|
||||
|
||||
/**
|
||||
* The columns used for update set operation.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForUpdate(): array
|
||||
{
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for update set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForUpdate(): Closure
|
||||
{
|
||||
return fn () => 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source model that has been updated for destination model.
|
||||
*
|
||||
* @param Collection $sourceModels
|
||||
* @param Model $destinationModel
|
||||
* @return Model|null
|
||||
*/
|
||||
protected function resolveUpdatedModel(Collection $sourceModels, Model $destinationModel): ?Model
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reconciliation results.
|
||||
*
|
||||
* @param Collection $created
|
||||
* @param Collection $deleted
|
||||
* @param Collection $updated
|
||||
* @return ReconcileResults
|
||||
*/
|
||||
protected function getResults(Collection $created, Collection $deleted, Collection $updated): ReconcileResults
|
||||
{
|
||||
return new ReconcileDumpResults($created, $deleted, $updated);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Admin\Dump;
|
||||
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Admin\Dump;
|
||||
|
||||
/**
|
||||
* Class ReconcileDumpResults.
|
||||
*
|
||||
* @extends ReconcileResults<Dump>
|
||||
*/
|
||||
class ReconcileDumpResults extends ReconcileResults
|
||||
{
|
||||
/**
|
||||
* Get the model of the reconciliation results.
|
||||
*
|
||||
* @return class-string<Dump>
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return Dump::class;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Billing\Balance;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Enums\Http\Api\Filter\AllowedDateFormat;
|
||||
use App\Models\Billing\Balance;
|
||||
@@ -15,9 +15,9 @@ use Illuminate\Support\Collection;
|
||||
/**
|
||||
* Class ReconcileBalanceRepositories.
|
||||
*
|
||||
* @extends ReconcileRepositories<Balance>
|
||||
* @extends ReconcileRepositoriesAction<Balance>
|
||||
*/
|
||||
class ReconcileBalanceRepositories extends ReconcileRepositories
|
||||
class ReconcileBalanceRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
+3
-3
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Billing\Transaction;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Enums\Http\Api\Filter\AllowedDateFormat;
|
||||
use App\Models\Billing\Transaction;
|
||||
@@ -15,9 +15,9 @@ use Illuminate\Support\Collection;
|
||||
/**
|
||||
* Class ReconcileTransactionRepositories.
|
||||
*
|
||||
* @extends ReconcileRepositories<Transaction>
|
||||
* @extends ReconcileRepositoriesAction<Transaction>
|
||||
*/
|
||||
class ReconcileTransactionRepositories extends ReconcileRepositories
|
||||
class ReconcileTransactionRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
+1
-1
@@ -14,7 +14,7 @@ use Illuminate\Support\Collection;
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*/
|
||||
abstract class ReconcileRepositories
|
||||
abstract class ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* Perform set reconciliation between source and destination repositories.
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\BaseModel;
|
||||
use Countable;
|
||||
use Illuminate\Console\Command;
|
||||
@@ -16,7 +18,7 @@ use Illuminate\Support\Str;
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*/
|
||||
abstract class ReconcileResults
|
||||
abstract class ReconcileResults extends ActionResult
|
||||
{
|
||||
/**
|
||||
* Create a new results instance.
|
||||
@@ -26,10 +28,11 @@ abstract class ReconcileResults
|
||||
* @param Collection $updated
|
||||
*/
|
||||
public function __construct(
|
||||
protected readonly Collection $created,
|
||||
protected readonly Collection $deleted,
|
||||
protected readonly Collection $updated
|
||||
protected readonly Collection $created = new Collection(),
|
||||
protected readonly Collection $deleted = new Collection(),
|
||||
protected readonly Collection $updated = new Collection()
|
||||
) {
|
||||
parent::__construct(ActionStatus::PASSED());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,12 +45,32 @@ abstract class ReconcileResults
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deleted models.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getDeleted(): Collection
|
||||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get updated models.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getUpdated(): Collection
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if any successful changes were made during reconciliation.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasChanges(): bool
|
||||
public function hasChanges(): bool
|
||||
{
|
||||
return $this->created->isNotEmpty() || $this->deleted->isNotEmpty() || $this->updated->isNotEmpty();
|
||||
}
|
||||
@@ -56,6 +79,8 @@ abstract class ReconcileResults
|
||||
* Write reconcile results to log.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toLog(): void
|
||||
{
|
||||
@@ -63,7 +88,7 @@ abstract class ReconcileResults
|
||||
$this->deleted->each(fn (BaseModel $model) => Log::info("{$this->label()} '{$model->getName()}' deleted"));
|
||||
$this->updated->each(fn (BaseModel $model) => Log::info("{$this->label()} '{$model->getName()}' updated"));
|
||||
|
||||
Log::info($this->summary());
|
||||
Log::info($this->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,6 +96,8 @@ abstract class ReconcileResults
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
@@ -78,15 +105,17 @@ abstract class ReconcileResults
|
||||
$this->deleted->each(fn (BaseModel $model) => $command->info("{$this->label()} '{$model->getName()}' deleted"));
|
||||
$this->updated->each(fn (BaseModel $model) => $command->info("{$this->label()} '{$model->getName()}' updated"));
|
||||
|
||||
$command->info($this->summary());
|
||||
$command->info($this->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the summary line.
|
||||
* Get the action result message.
|
||||
*
|
||||
* @return string
|
||||
* @return string|null
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
public function summary(): string
|
||||
public function getMessage(): ?string
|
||||
{
|
||||
if ($this->hasChanges()) {
|
||||
return "{$this->created->count()} {$this->label($this->created)} created, {$this->deleted->count()} {$this->label($this->deleted)} deleted, {$this->updated->count()} {$this->label($this->updated)} updated";
|
||||
@@ -110,6 +139,6 @@ abstract class ReconcileResults
|
||||
*/
|
||||
protected function label(int|array|Countable $models = 1): string
|
||||
{
|
||||
return Str::plural(class_basename($this->model()), $models);
|
||||
return Str::plural(Str::headline(class_basename($this->model())), $models);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Audio;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Audio;
|
||||
use Closure;
|
||||
@@ -14,9 +14,9 @@ use Illuminate\Support\Collection;
|
||||
/**
|
||||
* Class ReconcileAudioRepositories.
|
||||
*
|
||||
* @extends ReconcileRepositories<Audio>
|
||||
* @extends ReconcileRepositoriesAction<Audio>
|
||||
*/
|
||||
class ReconcileAudioRepositories extends ReconcileRepositories
|
||||
class ReconcileAudioRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
+3
-3
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Video;
|
||||
use Closure;
|
||||
@@ -14,9 +14,9 @@ use Illuminate\Support\Collection;
|
||||
/**
|
||||
* Class ReconcileVideoRepositories.
|
||||
*
|
||||
* @extends ReconcileRepositories<Video>
|
||||
* @extends ReconcileRepositoriesAction<Video>
|
||||
*/
|
||||
class ReconcileVideoRepositories extends ReconcileRepositories
|
||||
class ReconcileVideoRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Closure;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptRepositoriesAction.
|
||||
*
|
||||
* @extends ReconcileRepositoriesAction<VideoScript>
|
||||
*/
|
||||
class ReconcileScriptRepositoriesAction extends ReconcileRepositoriesAction
|
||||
{
|
||||
/**
|
||||
* The columns used for create and delete set operations.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForCreateDelete(): array
|
||||
{
|
||||
return [
|
||||
VideoScript::ATTRIBUTE_ID,
|
||||
VideoScript::ATTRIBUTE_PATH,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for create and delete set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForCreateDelete(): Closure
|
||||
{
|
||||
return fn (VideoScript $first, VideoScript $second) => [$first->path] <=> [$second->path];
|
||||
}
|
||||
|
||||
/**
|
||||
* The columns used for update set operation.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function columnsForUpdate(): array
|
||||
{
|
||||
return ['*'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for update set operation item comparison.
|
||||
*
|
||||
* @return Closure
|
||||
*/
|
||||
protected function diffCallbackForUpdate(): Closure
|
||||
{
|
||||
return fn () => 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source model that has been updated for destination model.
|
||||
*
|
||||
* @param Collection $sourceModels
|
||||
* @param Model $destinationModel
|
||||
* @return Model|null
|
||||
*/
|
||||
protected function resolveUpdatedModel(Collection $sourceModels, Model $destinationModel): ?Model
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reconciliation results.
|
||||
*
|
||||
* @param Collection $created
|
||||
* @param Collection $deleted
|
||||
* @param Collection $updated
|
||||
* @return ReconcileResults
|
||||
*/
|
||||
protected function getResults(Collection $created, Collection $deleted, Collection $updated): ReconcileResults
|
||||
{
|
||||
return new ReconcileScriptResults($created, $deleted, $updated);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Repositories\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
|
||||
/**
|
||||
* Class ReconcileScriptResults.
|
||||
*
|
||||
* @extends ReconcileResults<VideoScript>
|
||||
*/
|
||||
class ReconcileScriptResults extends ReconcileResults
|
||||
{
|
||||
/**
|
||||
* Get the model of the reconciliation results.
|
||||
*
|
||||
* @return class-string<VideoScript>
|
||||
*/
|
||||
protected function model(): string
|
||||
{
|
||||
return VideoScript::class;
|
||||
}
|
||||
}
|
||||
+70
-54
@@ -2,21 +2,23 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
namespace App\Actions\Storage\Admin\Dump;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Concerns\Repositories\ReconcilesRepositories;
|
||||
use App\Constants\Config\DumpConstants;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use Exception;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Database\MySqlConnection;
|
||||
use Illuminate\Database\PostgresConnection;
|
||||
use Illuminate\Database\SQLiteConnection;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use PDO;
|
||||
use RuntimeException;
|
||||
use Spatie\DbDumper\Databases\MySql;
|
||||
@@ -26,29 +28,29 @@ use Spatie\DbDumper\DbDumper;
|
||||
use Spatie\DbDumper\Exceptions\CannotSetParameter;
|
||||
|
||||
/**
|
||||
* Class DatabaseDumpCommand.
|
||||
* Class DumpAction.
|
||||
*/
|
||||
abstract class DatabaseDumpCommand extends Command
|
||||
abstract class DumpAction
|
||||
{
|
||||
use ReconcilesRepositories;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @return int
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function handle(): int
|
||||
public function __construct(protected readonly array $options = [])
|
||||
{
|
||||
$validator = Validator::make($this->options(), [
|
||||
'default-character-set' => ['string'],
|
||||
'set-gtid-purged' => [Rule::in(['OFF', 'ON', 'AUTO'])->__toString()],
|
||||
]);
|
||||
}
|
||||
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
/**
|
||||
* Handle action.
|
||||
*
|
||||
* @return ActionResult
|
||||
*/
|
||||
public function handle(): ActionResult
|
||||
{
|
||||
$dumpFile = $this->getDumpFile();
|
||||
|
||||
try {
|
||||
/** @var Connection $connection */
|
||||
@@ -56,28 +58,38 @@ abstract class DatabaseDumpCommand extends Command
|
||||
|
||||
$dumper = $this->getDumper($connection);
|
||||
if ($dumper === null) {
|
||||
Log::error("Unrecognized connection '{$connection->getName()}'");
|
||||
$this->error("Unrecognized connection '{$connection->getName()}'");
|
||||
|
||||
return 1;
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
"Unrecognized connection '{$connection->getName()}'"
|
||||
);
|
||||
}
|
||||
|
||||
$dumpFile = $this->getDumpFile();
|
||||
|
||||
// First, Dump file to temporary location
|
||||
// Writing to disks is not fully supported
|
||||
$dumper->dumpToFile($dumpFile);
|
||||
|
||||
// Assume success if no exceptions were thrown
|
||||
// The library will check if the file exists and is not empty
|
||||
Log::info("Database dump '$dumpFile' has been created");
|
||||
$this->info("Database dump '$dumpFile' has been created");
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
$this->error($e->getMessage());
|
||||
// Then, store dump file in intended location
|
||||
/** @var FilesystemAdapter $fs */
|
||||
$fs = Storage::disk(Config::get(DumpConstants::DISK_QUALIFIED));
|
||||
$fs->putFileAs('', $dumpFile, File::basename($dumpFile));
|
||||
|
||||
return 1;
|
||||
// Finally, delete the temporary file
|
||||
File::delete($dumpFile);
|
||||
} catch (Exception $e) {
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
$e->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
|
||||
return new ActionResult(
|
||||
ActionStatus::PASSED(),
|
||||
"Database dump '$dumpFile' has been created",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,31 +256,35 @@ abstract class DatabaseDumpCommand extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* The target path for the database dump.
|
||||
* Pattern: "/path/to/project/storage/db-dumps/{path}/animethemes-db-dump-{milliseconds from epoch}.sql".
|
||||
* Determine if the string option is set.
|
||||
*
|
||||
* @return string
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function getDumpFile(): string
|
||||
protected function hasOption(string $key): bool
|
||||
{
|
||||
$filesystem = Storage::disk('db-dumps');
|
||||
|
||||
$filesystem->makeDirectory($this->getDumpFilePath());
|
||||
|
||||
return Str::of($filesystem->path($this->getDumpFilePath()))
|
||||
->append(DIRECTORY_SEPARATOR)
|
||||
->append('animethemes-db-dump-')
|
||||
->append(intval(Date::now()->valueOf()))
|
||||
->append('.sql')
|
||||
->__toString();
|
||||
return is_string(Arr::get($this->options, $key));
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory that the file should be dumped to.
|
||||
* Get the option by key.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
protected function option(string $key): mixed
|
||||
{
|
||||
return Arr::get($this->options, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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-{concern}-{milliseconds from epoch}.sql".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function getDumpFilePath(): string;
|
||||
abstract protected function getDumpFile(): string;
|
||||
|
||||
/**
|
||||
* The list of tables to include in the dump.
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Admin\Dump;
|
||||
|
||||
use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories;
|
||||
use App\Models\Document\Page;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class DumpDocumentAction.
|
||||
*/
|
||||
class DumpDocumentAction extends DumpAction
|
||||
{
|
||||
use ReconcilesDumpRepositories;
|
||||
|
||||
final public const FILENAME_PREFIX = 'animethemes-db-dump-document-';
|
||||
|
||||
/**
|
||||
* The list of tables to include in the dump.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function allowedTables(): array
|
||||
{
|
||||
return [
|
||||
Page::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-document-{milliseconds from epoch}.sql".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDumpFile(): string
|
||||
{
|
||||
$filesystem = Storage::disk('local');
|
||||
|
||||
return Str::of($filesystem->path(''))
|
||||
->append(DumpDocumentAction::FILENAME_PREFIX)
|
||||
->append(intval(Date::now()->valueOf()))
|
||||
->append('.sql')
|
||||
->__toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Admin\Dump;
|
||||
|
||||
use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Series;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Models\Wiki\Studio;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use App\Pivots\AnimeImage;
|
||||
use App\Pivots\AnimeResource;
|
||||
use App\Pivots\AnimeSeries;
|
||||
use App\Pivots\AnimeStudio;
|
||||
use App\Pivots\AnimeThemeEntryVideo;
|
||||
use App\Pivots\ArtistImage;
|
||||
use App\Pivots\ArtistMember;
|
||||
use App\Pivots\ArtistResource;
|
||||
use App\Pivots\ArtistSong;
|
||||
use App\Pivots\StudioImage;
|
||||
use App\Pivots\StudioResource;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class DumpWikiAction.
|
||||
*/
|
||||
class DumpWikiAction extends DumpAction
|
||||
{
|
||||
use ReconcilesDumpRepositories;
|
||||
|
||||
final public const FILENAME_PREFIX = 'animethemes-db-dump-wiki-';
|
||||
|
||||
/**
|
||||
* The list of tables to include in the dump.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function allowedTables(): array
|
||||
{
|
||||
return [
|
||||
Anime::TABLE,
|
||||
AnimeImage::TABLE,
|
||||
AnimeResource::TABLE,
|
||||
AnimeSeries::TABLE,
|
||||
AnimeStudio::TABLE,
|
||||
AnimeSynonym::TABLE,
|
||||
AnimeTheme::TABLE,
|
||||
AnimeThemeEntry::TABLE,
|
||||
AnimeThemeEntryVideo::TABLE,
|
||||
Artist::TABLE,
|
||||
ArtistImage::TABLE,
|
||||
ArtistMember::TABLE,
|
||||
ArtistResource::TABLE,
|
||||
ArtistSong::TABLE,
|
||||
Audio::TABLE,
|
||||
ExternalResource::TABLE,
|
||||
Image::TABLE,
|
||||
Series::TABLE,
|
||||
Song::TABLE,
|
||||
Studio::TABLE,
|
||||
StudioImage::TABLE,
|
||||
StudioResource::TABLE,
|
||||
Video::TABLE,
|
||||
VideoScript::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-wiki-{milliseconds from epoch}.sql".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDumpFile(): string
|
||||
{
|
||||
$filesystem = Storage::disk('local');
|
||||
|
||||
return Str::of($filesystem->path(''))
|
||||
->append(DumpWikiAction::FILENAME_PREFIX)
|
||||
->append(intval(Date::now()->valueOf()))
|
||||
->append('.sql')
|
||||
->__toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Admin\Dump;
|
||||
|
||||
use App\Actions\Storage\Base\PruneAction;
|
||||
use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories;
|
||||
use App\Constants\Config\DumpConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class PruneDumpAction.
|
||||
*/
|
||||
class PruneDumpAction extends PruneAction
|
||||
{
|
||||
use ReconcilesDumpRepositories;
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(DumpConstants::DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Actions\Storage\StorageAction;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Concerns\Repositories\ReconcilesRepositories;
|
||||
use App\Contracts\Actions\Storage\StorageAction;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Contracts\Storage\InteractsWithDisks;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\File;
|
||||
@@ -19,8 +19,10 @@ use Illuminate\Support\Facades\Storage;
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*/
|
||||
abstract class DeleteAction extends StorageAction
|
||||
abstract class DeleteAction implements InteractsWithDisks, StorageAction
|
||||
{
|
||||
use ReconcilesRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
@@ -48,65 +50,38 @@ abstract class DeleteAction extends StorageAction
|
||||
$results[$disk] = $result;
|
||||
}
|
||||
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
|
||||
return new DeleteResults($this->model, $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile storage repositories.
|
||||
*
|
||||
* @return ReconcileResults
|
||||
*/
|
||||
protected function reconcileRepositories(): ReconcileResults
|
||||
{
|
||||
$action = $this->action();
|
||||
|
||||
$sourceRepository = $this->getSourceRepository();
|
||||
$destinationRepository = $this->getDestinationRepository();
|
||||
|
||||
$this->handleFilters($sourceRepository, $destinationRepository);
|
||||
|
||||
return $action->reconcileRepositories($sourceRepository, $destinationRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
abstract protected function getSourceRepository(): RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
abstract protected function getDestinationRepository(): RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function handleFilters(
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository
|
||||
RepositoryInterface $destinationRepository,
|
||||
array $data = []
|
||||
): void {
|
||||
$sourceRepository->handleFilter('path', File::dirname($this->path()));
|
||||
$destinationRepository->handleFilter('path', File::dirname($this->path()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function action(): ReconcileRepositories;
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to delete.
|
||||
|
||||
@@ -5,16 +5,17 @@ declare(strict_types=1);
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class DeleteResults.
|
||||
*/
|
||||
class DeleteResults extends StorageResults
|
||||
class DeleteResults implements StorageResults
|
||||
{
|
||||
/**
|
||||
* Create a new action result instance.
|
||||
@@ -43,6 +44,24 @@ class DeleteResults extends StorageResults
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
if (empty($this->deletions)) {
|
||||
$command->error('No deletions were attempted.');
|
||||
}
|
||||
foreach ($this->deletions as $fs => $result) {
|
||||
$result
|
||||
? $command->info("Deleted '{$this->model->getName()}' from disk '$fs'")
|
||||
: $command->error("Failed to delete '{$this->model->getName()}' from disk '$fs'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
|
||||
@@ -4,8 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\StorageAction;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Actions\Storage\StorageAction;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Storage\InteractsWithDisks;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -15,7 +16,7 @@ use Illuminate\Support\Facades\Storage;
|
||||
*
|
||||
* @template TModel of \App\Models\BaseModel
|
||||
*/
|
||||
abstract class MoveAction extends StorageAction
|
||||
abstract class MoveAction implements InteractsWithDisks, StorageAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
@@ -46,11 +47,20 @@ abstract class MoveAction extends StorageAction
|
||||
$results[$disk] = $result;
|
||||
}
|
||||
|
||||
$this->update();
|
||||
|
||||
return new MoveResults($this->model, $from, $this->to, $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
$this->update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to move from.
|
||||
*
|
||||
|
||||
@@ -5,16 +5,17 @@ declare(strict_types=1);
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class MoveResults.
|
||||
*/
|
||||
class MoveResults extends StorageResults
|
||||
class MoveResults implements StorageResults
|
||||
{
|
||||
/**
|
||||
* Create a new action result instance.
|
||||
@@ -49,6 +50,24 @@ class MoveResults extends StorageResults
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
if (empty($this->moves)) {
|
||||
$command->error('No moves were attempted.');
|
||||
}
|
||||
foreach ($this->moves as $fs => $result) {
|
||||
$result
|
||||
? $command->info("Moved '{$this->model->getName()}' from '$this->from' to '$this->to' in disk '$fs'")
|
||||
: $command->error("Failed to move '{$this->model->getName()}' from '$this->from' to '$this->to' in disk '$fs'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Concerns\Repositories\ReconcilesRepositories;
|
||||
use App\Contracts\Actions\Storage\StorageAction;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
/**
|
||||
* Class PruneAction.
|
||||
*/
|
||||
abstract class PruneAction implements InteractsWithDisk, StorageAction
|
||||
{
|
||||
use ReconcilesRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param int $hours
|
||||
*/
|
||||
public function __construct(protected readonly int $hours = 72)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle action.
|
||||
*
|
||||
* @return StorageResults
|
||||
*/
|
||||
public function handle(): StorageResults
|
||||
{
|
||||
$fs = Storage::disk($this->disk());
|
||||
$pruneDate = Date::now()->subHours($this->hours);
|
||||
|
||||
$results = [];
|
||||
|
||||
foreach ($fs->allFiles() as $path) {
|
||||
$lastModified = Date::createFromTimestamp($fs->lastModified($path));
|
||||
if ($lastModified->isBefore($pruneDate)) {
|
||||
$result = $fs->delete($path);
|
||||
|
||||
$results[$path] = $result;
|
||||
}
|
||||
}
|
||||
|
||||
return new PruneResults($this->disk(), $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class PruneResults.
|
||||
*/
|
||||
class PruneResults implements StorageResults
|
||||
{
|
||||
/**
|
||||
* Create a new action result instance.
|
||||
*
|
||||
* @param string $fs
|
||||
* @param array<string, bool> $prunings
|
||||
*/
|
||||
public function __construct(protected readonly string $fs, protected readonly array $prunings = [])
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to log.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function toLog(): void
|
||||
{
|
||||
if (empty($this->prunings)) {
|
||||
Log::error('No prunings were attempted.');
|
||||
}
|
||||
foreach ($this->prunings as $path => $result) {
|
||||
$result === false
|
||||
? Log::error("Failed to prune '$path' from disk '$this->fs'")
|
||||
: Log::info("Pruned '$path' from disk '$this->fs'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
if (empty($this->prunings)) {
|
||||
$command->error('No prunings were attempted.');
|
||||
}
|
||||
foreach ($this->prunings as $path => $result) {
|
||||
$result === false
|
||||
? $command->error("Failed to prune '$path' from disk '$this->fs'")
|
||||
: $command->info("Pruned '$path' from disk '$this->fs'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
* @return ActionResult
|
||||
*/
|
||||
public function toActionResult(): ActionResult
|
||||
{
|
||||
if (empty($this->prunings)) {
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
'No prunings were attempted.'
|
||||
);
|
||||
}
|
||||
|
||||
/** @var Collection $passed */
|
||||
/** @var Collection $failed */
|
||||
[$passed, $failed] = collect($this->prunings)->partition(fn (bool $result, string $path) => $result);
|
||||
|
||||
if ($failed->isNotEmpty()) {
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
"Failed to prune {$failed->keys()->join(', ', ' & ')} from disk '$this->fs'."
|
||||
);
|
||||
}
|
||||
|
||||
return new ActionResult(
|
||||
ActionStatus::PASSED(),
|
||||
"Pruned {$passed->keys()->join(', ', ' & ')} from disk '$this->fs'."
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\ReconcileResults;
|
||||
use App\Actions\Storage\StorageAction;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Contracts\Actions\Storage\StorageAction;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Storage\InteractsWithDisks;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@@ -16,7 +14,7 @@ use Illuminate\Support\Facades\Storage;
|
||||
/**
|
||||
* Class UploadAction.
|
||||
*/
|
||||
abstract class UploadAction extends StorageAction
|
||||
abstract class UploadAction implements InteractsWithDisks, StorageAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
@@ -46,63 +44,6 @@ abstract class UploadAction extends StorageAction
|
||||
$results[$disk] = $result;
|
||||
}
|
||||
|
||||
$reconcileResults = $this->reconcileRepositories();
|
||||
|
||||
$reconcileResults->toLog();
|
||||
|
||||
return new UploadResults($results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile storage repositories.
|
||||
*
|
||||
* @return ReconcileResults
|
||||
*/
|
||||
protected function reconcileRepositories(): ReconcileResults
|
||||
{
|
||||
$action = $this->action();
|
||||
|
||||
$sourceRepository = $this->getSourceRepository();
|
||||
$destinationRepository = $this->getDestinationRepository();
|
||||
|
||||
$this->handleFilters($sourceRepository, $destinationRepository);
|
||||
|
||||
return $action->reconcileRepositories($sourceRepository, $destinationRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
abstract protected function getSourceRepository(): RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
abstract protected function getDestinationRepository(): RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @return void
|
||||
*/
|
||||
protected function handleFilters(
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository
|
||||
): void {
|
||||
$sourceRepository->handleFilter('path', $this->path);
|
||||
$destinationRepository->handleFilter('path', $this->path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
abstract protected function action(): ReconcileRepositories;
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ declare(strict_types=1);
|
||||
namespace App\Actions\Storage\Base;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Actions\Storage\StorageResults;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class UploadResults.
|
||||
*/
|
||||
class UploadResults extends StorageResults
|
||||
class UploadResults implements StorageResults
|
||||
{
|
||||
/**
|
||||
* Create a new action result instance.
|
||||
@@ -41,6 +42,24 @@ class UploadResults extends StorageResults
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void
|
||||
{
|
||||
if (empty($this->uploads)) {
|
||||
$command->error('No uploads were attempted.');
|
||||
}
|
||||
foreach ($this->uploads as $fs => $result) {
|
||||
$result === false
|
||||
? $command->error("Failed to upload to disk '$fs'")
|
||||
: $command->info("Uploaded '$result' to disk '$fs'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage;
|
||||
|
||||
/**
|
||||
* Class StorageAction.
|
||||
*/
|
||||
abstract class StorageAction
|
||||
{
|
||||
/**
|
||||
* Handle action.
|
||||
*
|
||||
* @return StorageResults
|
||||
*/
|
||||
abstract public function handle(): StorageResults;
|
||||
|
||||
/**
|
||||
* Get the disks to update.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function disks(): array;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
|
||||
/**
|
||||
* Class StorageResults.
|
||||
*/
|
||||
abstract class StorageResults
|
||||
{
|
||||
/**
|
||||
* Write results to log.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function toLog(): void;
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
* @return ActionResult
|
||||
*/
|
||||
abstract public function toActionResult(): ActionResult;
|
||||
}
|
||||
@@ -4,15 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositories;
|
||||
use App\Actions\Storage\Base\DeleteAction;
|
||||
use App\Concerns\Repositories\Wiki\ReconcilesAudioRepositories;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Repositories\Eloquent\Wiki\AudioRepository as AudioDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\AudioRepository as AudioSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
@@ -22,6 +17,8 @@ use Illuminate\Support\Facades\Config;
|
||||
*/
|
||||
class DeleteAudioAction extends DeleteAction
|
||||
{
|
||||
use ReconcilesAudioRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
@@ -33,45 +30,15 @@ class DeleteAudioAction extends DeleteAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(AudioConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getSourceRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(AudioSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getDestinationRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(AudioDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function action(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileAudioRepositories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to delete.
|
||||
*
|
||||
|
||||
@@ -29,11 +29,11 @@ class MoveAudioAction extends MoveAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(AudioConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
@@ -4,15 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Audio;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositories;
|
||||
use App\Actions\Storage\Base\UploadAction;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\AudioRepository as AudioDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\AudioRepository as AudioSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Models\Wiki\Audio;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UploadAudioAction.
|
||||
@@ -20,42 +18,54 @@ use Illuminate\Support\Facades\Config;
|
||||
class UploadAudioAction extends UploadAction
|
||||
{
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
if ($storageResults->toActionResult()->hasFailed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getOrCreateAudio();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing or create new audio for file upload.
|
||||
*
|
||||
* @return Audio
|
||||
*/
|
||||
protected function getOrCreateAudio(): Audio
|
||||
{
|
||||
$path = Str::of($this->path)
|
||||
->finish(DIRECTORY_SEPARATOR)
|
||||
->append($this->file->getClientOriginalName())
|
||||
->__toString();
|
||||
|
||||
$attributes = [
|
||||
Audio::ATTRIBUTE_FILENAME => File::name($this->file->getClientOriginalName()),
|
||||
Audio::ATTRIBUTE_MIMETYPE => $this->file->getMimeType(),
|
||||
Audio::ATTRIBUTE_PATH => $path,
|
||||
Audio::ATTRIBUTE_SIZE => $this->file->getSize(),
|
||||
];
|
||||
|
||||
return Audio::updateOrCreate(
|
||||
[
|
||||
Audio::ATTRIBUTE_BASENAME => $this->file->getClientOriginalName(),
|
||||
],
|
||||
$attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(AudioConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getSourceRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(AudioSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getDestinationRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(AudioDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function action(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileAudioRepositories();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Video\ReconcileVideoRepositories;
|
||||
use App\Actions\Storage\Base\DeleteAction;
|
||||
use App\Concerns\Repositories\Wiki\ReconcilesVideoRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Repositories\Eloquent\Wiki\VideoRepository as VideoDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\VideoRepository as VideoSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
@@ -22,6 +17,8 @@ use Illuminate\Support\Facades\Config;
|
||||
*/
|
||||
class DeleteVideoAction extends DeleteAction
|
||||
{
|
||||
use ReconcilesVideoRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
@@ -33,45 +30,15 @@ class DeleteVideoAction extends DeleteAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(VideoConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getSourceRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(VideoSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getDestinationRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(VideoDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function action(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileVideoRepositories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to delete.
|
||||
*
|
||||
|
||||
@@ -29,11 +29,11 @@ class MoveVideoAction extends MoveAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(VideoConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Base\DeleteAction;
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class DeleteScriptAction.
|
||||
*
|
||||
* @extends DeleteAction<VideoScript>
|
||||
*/
|
||||
class DeleteScriptAction extends DeleteAction
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
*/
|
||||
public function __construct(VideoScript $script)
|
||||
{
|
||||
parent::__construct($script);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to delete.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function path(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Base\MoveAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class MoveVideoAction.
|
||||
*
|
||||
* @extends MoveAction<VideoScript>
|
||||
*/
|
||||
class MoveScriptAction extends MoveAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param VideoScript $script
|
||||
* @param string $to
|
||||
*/
|
||||
public function __construct(VideoScript $script, string $to)
|
||||
{
|
||||
parent::__construct($script, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to move from.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function from(): string
|
||||
{
|
||||
return $this->model->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update underlying model.
|
||||
* We want to apply these updates through Eloquent to preserve relations when renaming.
|
||||
* Otherwise, reconciliation would destroy the old model and create a new model for the new name.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function update(): void
|
||||
{
|
||||
$this->model->update([
|
||||
VideoScript::ATTRIBUTE_PATH => $this->to,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video\Script;
|
||||
|
||||
use App\Actions\Storage\Base\UploadAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Models\Wiki\Video\VideoScript;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UploadScriptAction.
|
||||
*/
|
||||
class UploadScriptAction extends UploadAction
|
||||
{
|
||||
/**
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param UploadedFile $file
|
||||
* @param string $path
|
||||
* @param Video|null $video
|
||||
*/
|
||||
public function __construct(UploadedFile $file, string $path, protected ?Video $video = null)
|
||||
{
|
||||
parent::__construct($file, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
if ($storageResults->toActionResult()->hasFailed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getOrCreateScript();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing or create new script for file upload.
|
||||
*
|
||||
* @return VideoScript
|
||||
*/
|
||||
protected function getOrCreateScript(): VideoScript
|
||||
{
|
||||
$path = Str::of($this->path)
|
||||
->finish(DIRECTORY_SEPARATOR)
|
||||
->append($this->file->getClientOriginalName())
|
||||
->__toString();
|
||||
|
||||
$attributes = [
|
||||
VideoScript::ATTRIBUTE_PATH => $path,
|
||||
];
|
||||
|
||||
if ($this->video !== null) {
|
||||
$attributes[VideoScript::ATTRIBUTE_VIDEO] = $this->video->getKey();
|
||||
}
|
||||
|
||||
return VideoScript::updateOrCreate(
|
||||
[
|
||||
VideoScript::ATTRIBUTE_PATH => $path,
|
||||
],
|
||||
$attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array
|
||||
{
|
||||
return Arr::wrap(Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED));
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Storage\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Video\ReconcileVideoRepositories;
|
||||
use App\Actions\Storage\Base\UploadAction;
|
||||
use App\Actions\Storage\Wiki\Video\Script\UploadScriptAction;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\VideoRepository as VideoDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\VideoRepository as VideoSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use App\Contracts\Actions\Storage\StorageResults;
|
||||
use App\Enums\Models\Wiki\VideoOverlap;
|
||||
use App\Enums\Models\Wiki\VideoSource;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Video;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class UploadVideoAction.
|
||||
@@ -20,42 +24,132 @@ use Illuminate\Support\Facades\Config;
|
||||
class UploadVideoAction extends UploadAction
|
||||
{
|
||||
/**
|
||||
* Get the disks to update.
|
||||
* Create a new action instance.
|
||||
*
|
||||
* @param UploadedFile $file
|
||||
* @param string $path
|
||||
* @param array $attributes
|
||||
* @param AnimeThemeEntry|null $entry
|
||||
* @param UploadedFile|null $script
|
||||
*/
|
||||
public function __construct(
|
||||
UploadedFile $file,
|
||||
string $path,
|
||||
protected array $attributes = [],
|
||||
protected ?AnimeThemeEntry $entry = null,
|
||||
protected readonly ?UploadedFile $script = null
|
||||
) {
|
||||
parent::__construct($file, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void
|
||||
{
|
||||
if ($storageResults->toActionResult()->hasFailed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$video = $this->getOrCreateVideo();
|
||||
|
||||
$this->attachEntry($video);
|
||||
|
||||
$this->uploadScript($video);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get existing or create new video for file upload.
|
||||
*
|
||||
* @return Video
|
||||
*/
|
||||
protected function getOrCreateVideo(): Video
|
||||
{
|
||||
$path = Str::of($this->path)
|
||||
->finish(DIRECTORY_SEPARATOR)
|
||||
->append($this->file->getClientOriginalName())
|
||||
->__toString();
|
||||
|
||||
$attributes = [
|
||||
Video::ATTRIBUTE_FILENAME => File::name($this->file->getClientOriginalName()),
|
||||
Video::ATTRIBUTE_MIMETYPE => $this->file->getMimeType(),
|
||||
Video::ATTRIBUTE_PATH => $path,
|
||||
Video::ATTRIBUTE_SIZE => $this->file->getSize(),
|
||||
];
|
||||
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_RESOLUTION)) {
|
||||
$attributes[Video::ATTRIBUTE_RESOLUTION] = Arr::get($this->attributes, Video::ATTRIBUTE_RESOLUTION);
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_NC)) {
|
||||
$attributes[Video::ATTRIBUTE_NC] = Arr::get($this->attributes, Video::ATTRIBUTE_NC);
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_SUBBED)) {
|
||||
$attributes[Video::ATTRIBUTE_SUBBED] = Arr::get($this->attributes, Video::ATTRIBUTE_SUBBED);
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_LYRICS)) {
|
||||
$attributes[Video::ATTRIBUTE_LYRICS] = Arr::get($this->attributes, Video::ATTRIBUTE_LYRICS);
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_UNCEN)) {
|
||||
$attributes[Video::ATTRIBUTE_UNCEN] = Arr::get($this->attributes, Video::ATTRIBUTE_UNCEN);
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_UNCEN)) {
|
||||
$overlap = VideoOverlap::unstrictCoerce(Arr::get($this->attributes, Video::ATTRIBUTE_OVERLAP));
|
||||
if ($overlap !== null) {
|
||||
$attributes[Video::ATTRIBUTE_OVERLAP] = $overlap;
|
||||
}
|
||||
}
|
||||
if (Arr::has($this->attributes, Video::ATTRIBUTE_SOURCE)) {
|
||||
$attributes[Video::ATTRIBUTE_SOURCE] = VideoSource::unstrictCoerce(Arr::get($this->attributes, Video::ATTRIBUTE_SOURCE));
|
||||
}
|
||||
|
||||
return Video::updateOrCreate(
|
||||
[
|
||||
Video::ATTRIBUTE_BASENAME => $this->file->getClientOriginalName(),
|
||||
],
|
||||
$attributes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach entry to created video if uploaded from entry detail screen.
|
||||
*
|
||||
* @param Video $video
|
||||
* @return void
|
||||
*/
|
||||
protected function attachEntry(Video $video): void
|
||||
{
|
||||
if ($this->entry !== null && $video->wasRecentlyCreated) {
|
||||
$video->animethemeentries()->attach($this->entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload & Associate Script if video upload was successful.
|
||||
*
|
||||
* @param Video $video
|
||||
* @return void
|
||||
*/
|
||||
protected function uploadScript(Video $video): void
|
||||
{
|
||||
if ($this->script !== null) {
|
||||
$uploadScript = new UploadScriptAction($this->script, $this->path, $video);
|
||||
|
||||
$scriptResult = $uploadScript->handle();
|
||||
|
||||
$uploadScript->then($scriptResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function disks(): array
|
||||
public function disks(): array
|
||||
{
|
||||
return Config::get(VideoConstants::DISKS_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getSourceRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(VideoSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @return RepositoryInterface
|
||||
*/
|
||||
protected function getDestinationRepository(): RepositoryInterface
|
||||
{
|
||||
return App::make(VideoDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function action(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileVideoRepositories();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,11 @@ use App\Enums\Http\Api\Filter\UnaryLogicalOperator;
|
||||
use App\Http\Api\Criteria\Filter\Criteria;
|
||||
use App\Http\Api\Filter\Filter;
|
||||
use App\Http\Api\Parser\FilterParser;
|
||||
use App\Rules\Api\DelimitedRule;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Fluent;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Validator;
|
||||
use Spatie\ValidationRules\Rules\Delimited;
|
||||
|
||||
/**
|
||||
* Trait ValidatesFilters.
|
||||
@@ -113,7 +113,7 @@ trait ValidatesFilters
|
||||
$validator->sometimes(
|
||||
$formattedParameter,
|
||||
$filter->getRules(),
|
||||
fn (Fluent $fluent) => is_string(Arr::get($fluent->toArray(), $formattedParameter)) && ! Str::of(Arr::get($fluent->toArray(), $formattedParameter))->contains(Criteria::VALUE_SEPARATOR)
|
||||
fn (Fluent $fluent) => Arr::has($fluent->toArray(), $formattedParameter) && ! Str::of(Arr::get($fluent->toArray(), $formattedParameter))->contains(Criteria::VALUE_SEPARATOR)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@ trait ValidatesFilters
|
||||
|
||||
$multiValueRules = [];
|
||||
foreach ($filter->getRules() as $rule) {
|
||||
$multiValueRules[] = new Delimited($rule);
|
||||
$multiValueRules[] = new DelimitedRule($rule);
|
||||
}
|
||||
|
||||
$multiValueFilterFormats = $this->getFilterFormats($filter, UnaryLogicalOperator::getInstances());
|
||||
@@ -131,7 +131,7 @@ trait ValidatesFilters
|
||||
$validator->sometimes(
|
||||
$formattedParameter,
|
||||
$multiValueRules,
|
||||
fn (Fluent $fluent) => is_string(Arr::get($fluent->toArray(), $formattedParameter)) && Str::of(Arr::get($fluent->toArray(), $formattedParameter))->contains(Criteria::VALUE_SEPARATOR)
|
||||
fn (Fluent $fluent) => Arr::has($fluent->toArray(), $formattedParameter) && Str::of(Arr::get($fluent->toArray(), $formattedParameter))->contains(Criteria::VALUE_SEPARATOR)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Http\Requests\Api;
|
||||
|
||||
use App\Rules\Api\DelimitedRule;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Spatie\ValidationRules\Rules\Delimited;
|
||||
|
||||
/**
|
||||
* Trait ValidatesParameters.
|
||||
@@ -30,7 +30,8 @@ trait ValidatesParameters
|
||||
|
||||
return [
|
||||
$param => [
|
||||
'nullable',
|
||||
'sometimes',
|
||||
'required',
|
||||
"array:$types",
|
||||
],
|
||||
];
|
||||
@@ -48,7 +49,13 @@ trait ValidatesParameters
|
||||
{
|
||||
return [
|
||||
$param => array_merge(
|
||||
['bail', 'sometimes', 'required', 'string', new Delimited(Rule::in($values))],
|
||||
[
|
||||
'bail',
|
||||
'sometimes',
|
||||
'required',
|
||||
'string',
|
||||
new DelimitedRule(['required', Rule::in($values)->__toString()]),
|
||||
],
|
||||
$customRules,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Admin;
|
||||
|
||||
use App\Actions\Repositories\Admin\Dump\ReconcileDumpRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Admin\DumpRepository as DumpDestinationRepository;
|
||||
use App\Repositories\Storage\Admin\DumpRepository as DumpSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesDumpRepositories.
|
||||
*/
|
||||
trait ReconcilesDumpRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(DumpSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(DumpDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileDumpRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Billing;
|
||||
|
||||
use App\Actions\Repositories\Billing\Balance\ReconcileBalanceRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use App\Repositories\DigitalOcean\Billing\DigitalOceanBalanceRepository as DigitalOceanSourceRepository;
|
||||
use App\Repositories\Eloquent\Billing\DigitalOceanBalanceRepository as DigitalOceanDestinationRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesBalanceRepositories.
|
||||
*/
|
||||
trait ReconcilesBalanceRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
$service = Service::unstrictCoerce(Arr::get($data, 'service'));
|
||||
|
||||
return match ($service?->value) {
|
||||
Service::DIGITALOCEAN => App::make(DigitalOceanSourceRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
$service = Service::unstrictCoerce(Arr::get($data, 'service'));
|
||||
|
||||
return match ($service?->value) {
|
||||
Service::DIGITALOCEAN => App::make(DigitalOceanDestinationRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileBalanceRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Billing;
|
||||
|
||||
use App\Actions\Repositories\Billing\Transaction\ReconcileTransactionRepositoriesAction;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use App\Repositories\DigitalOcean\Billing\DigitalOceanTransactionRepository as DigitalOceanSourceRepository;
|
||||
use App\Repositories\Eloquent\Billing\DigitalOceanTransactionRepository as DigitalOceanDestinationRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesTransactionRepositories.
|
||||
*/
|
||||
trait ReconcilesTransactionRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
$service = Service::unstrictCoerce(Arr::get($data, 'service'));
|
||||
|
||||
return match ($service?->value) {
|
||||
Service::DIGITALOCEAN => App::make(DigitalOceanSourceRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
$service = Service::unstrictCoerce(Arr::get($data, 'service'));
|
||||
|
||||
return match ($service?->value) {
|
||||
Service::DIGITALOCEAN => App::make(DigitalOceanDestinationRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileTransactionRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Actions\ActionStatus;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesRepositories.
|
||||
*/
|
||||
trait ReconcilesRepositories
|
||||
{
|
||||
/**
|
||||
* Reconcile repositories.
|
||||
*
|
||||
* @param array $data
|
||||
* @return ActionResult
|
||||
*/
|
||||
protected function reconcileRepositories(array $data = []): ActionResult
|
||||
{
|
||||
$sourceRepository = $this->getSourceRepository($data);
|
||||
if ($sourceRepository === null) {
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
'Could not find source repository'
|
||||
);
|
||||
}
|
||||
|
||||
$destinationRepository = $this->getDestinationRepository($data);
|
||||
if ($destinationRepository === null) {
|
||||
return new ActionResult(
|
||||
ActionStatus::FAILED(),
|
||||
'Could not find destination repository'
|
||||
);
|
||||
}
|
||||
|
||||
$this->handleFilters($sourceRepository, $destinationRepository, $data);
|
||||
|
||||
$action = $this->action();
|
||||
|
||||
return $action->reconcileRepositories($sourceRepository, $destinationRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
abstract protected function action(): ReconcileRepositoriesAction;
|
||||
|
||||
/**
|
||||
* Get source repository.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
abstract protected function getSourceRepository(array $data = []): ?RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Get destination repository.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
abstract protected function getDestinationRepository(array $data = []): ?RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function handleFilters(
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository,
|
||||
array $data = []
|
||||
): void {
|
||||
// Not supported by default
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Wiki;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\AudioRepository as AudioDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\AudioRepository as AudioSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesAudioRepositories.
|
||||
*/
|
||||
trait ReconcilesAudioRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(AudioSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(AudioDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileAudioRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Wiki;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\Wiki\Video\ReconcileVideoRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\VideoRepository as VideoDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\VideoRepository as VideoSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesVideoRepositories.
|
||||
*/
|
||||
trait ReconcilesVideoRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(VideoSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(VideoDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileVideoRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Concerns\Repositories\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositoriesAction;
|
||||
use App\Actions\Repositories\Wiki\Video\Script\ReconcileScriptRepositoriesAction;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\Video\ScriptRepository as ScriptDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\Video\ScriptRepository as ScriptSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Trait ReconcilesScriptRepositories.
|
||||
*/
|
||||
trait ReconcilesScriptRepositories
|
||||
{
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(ScriptSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $data
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $data = []): ?RepositoryInterface
|
||||
{
|
||||
return App::make(ScriptDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconcile action.
|
||||
*
|
||||
* @return ReconcileRepositoriesAction
|
||||
*/
|
||||
protected function action(): ReconcileRepositoriesAction
|
||||
{
|
||||
return new ReconcileScriptRepositoriesAction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Console\Isolatable;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Class BaseCommand.
|
||||
*/
|
||||
abstract class BaseCommand extends Command implements Isolatable
|
||||
{
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
abstract public function handle(): int;
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
abstract protected function validator(): Validator;
|
||||
|
||||
/**
|
||||
* Configure the console command for isolation.
|
||||
* Note: Overrides default framework behavior which disables isolation by default.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*/
|
||||
protected function configureIsolation(): void
|
||||
{
|
||||
$this->getDefinition()->addOption(new InputOption(
|
||||
'isolated',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Do not run the command if another instance of the command is already running',
|
||||
true
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Billing\Balance;
|
||||
|
||||
use App\Actions\Repositories\Billing\Balance\ReconcileBalanceRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Console\Commands\Billing\ServiceReconcileCommand;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use App\Repositories\DigitalOcean\Billing\DigitalOceanBalanceRepository as DigitalOceanSourceRepository;
|
||||
use App\Repositories\Eloquent\Billing\DigitalOceanBalanceRepository as DigitalOceanDestinationRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Class BalanceReconcileCommand.
|
||||
*/
|
||||
class BalanceReconcileCommand extends ServiceReconcileCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:balance {service}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between vendor billing API and balance database';
|
||||
|
||||
/**
|
||||
* Get the reconciliation action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function getAction(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileBalanceRepositories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for service.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return match (Arr::get($validated, 'service')) {
|
||||
Service::DIGITALOCEAN()->key => App::make(DigitalOceanSourceRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for service.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return match (Arr::get($validated, 'service')) {
|
||||
Service::DIGITALOCEAN()->key => App::make(DigitalOceanDestinationRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Billing;
|
||||
|
||||
use App\Console\Commands\ReconcileCommand;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use BenSampo\Enum\Rules\EnumKey;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
|
||||
/**
|
||||
* Class ServiceReconcileCommand.
|
||||
*/
|
||||
abstract class ServiceReconcileCommand extends ReconcileCommand
|
||||
{
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
protected function validator(): Validator
|
||||
{
|
||||
return \Illuminate\Support\Facades\Validator::make($this->arguments(), [
|
||||
'service' => ['required', new EnumKey(Service::class)],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param array $validated
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @return void
|
||||
*/
|
||||
protected function handleFilters(
|
||||
array $validated,
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository
|
||||
): void {
|
||||
// Not supported
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Billing\Transaction;
|
||||
|
||||
use App\Actions\Repositories\Billing\Transaction\ReconcileTransactionRepositories;
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Console\Commands\Billing\ServiceReconcileCommand;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use App\Repositories\DigitalOcean\Billing\DigitalOceanTransactionRepository as DigitalOceanSourceRepository;
|
||||
use App\Repositories\Eloquent\Billing\DigitalOceanTransactionRepository as DigitalOceanDestinationRepository;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\App;
|
||||
|
||||
/**
|
||||
* Class TransactionReconcileCommand.
|
||||
*/
|
||||
class TransactionReconcileCommand extends ServiceReconcileCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:transaction {service}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between vendor billing API and transaction database';
|
||||
|
||||
/**
|
||||
* Get the reconciliation action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function getAction(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileTransactionRepositories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for service.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return match (Arr::get($validated, 'service')) {
|
||||
Service::DIGITALOCEAN()->key => App::make(DigitalOceanSourceRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for service.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return match (Arr::get($validated, 'service')) {
|
||||
Service::DIGITALOCEAN()->key => App::make(DigitalOceanDestinationRepository::class),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* Class PruneDatabaseDumpsCommand.
|
||||
*/
|
||||
class PruneDatabaseDumpsCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'db:prune-dumps {--H|hours=72 : The number of hours to retain sanitized database dumps}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Prune stale database dumps from local storage';
|
||||
|
||||
/**
|
||||
* The number of dumps deleted.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected int $deleted = 0;
|
||||
|
||||
/**
|
||||
* The number of dumps whose deletion failed.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected int $deletedFailed = 0;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$hours = $this->option('hours');
|
||||
if (! is_numeric($hours)) {
|
||||
Log::error("Invalid hours value '$hours'");
|
||||
$this->error("Invalid hours value '$hours'");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->prune(
|
||||
Storage::disk('db-dumps'),
|
||||
Date::now()->subHours(intval($hours))
|
||||
);
|
||||
|
||||
$this->printResults();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prune database dumps in filesystem against date.
|
||||
*
|
||||
* @param Filesystem $filesystem
|
||||
* @param Carbon $pruneDate
|
||||
* @return void
|
||||
*/
|
||||
protected function prune(Filesystem $filesystem, Carbon $pruneDate): void
|
||||
{
|
||||
foreach ($filesystem->allFiles() as $path) {
|
||||
$lastModified = Date::createFromTimestamp($filesystem->lastModified($path));
|
||||
if (Str::contains($path, 'animethemes-db-dump') && $lastModified->isBefore($pruneDate)) {
|
||||
$result = $filesystem->delete($path);
|
||||
if ($result) {
|
||||
$this->deleted++;
|
||||
Log::info("Deleted database dump '$path'");
|
||||
$this->info("Deleted database dump '$path'");
|
||||
} else {
|
||||
$this->deletedFailed++;
|
||||
Log::error("Failed to delete database dump '$path'");
|
||||
$this->error("Failed to delete database dump '$path'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print results to console and logs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function printResults(): void
|
||||
{
|
||||
if ($this->hasResults()) {
|
||||
if ($this->hasDeletions()) {
|
||||
Log::info("$this->deleted database dumps deleted");
|
||||
$this->info("$this->deleted database dumps deleted");
|
||||
}
|
||||
if ($this->hasFailures()) {
|
||||
Log::error("Failed to delete $this->deletedFailed database dumps");
|
||||
$this->error("Failed to delete $this->deletedFailed database dumps");
|
||||
}
|
||||
} else {
|
||||
Log::info('No database dumps deleted');
|
||||
$this->info('No database dumps deleted');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if any deletions, successful or not, were made during pruning.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasResults(): bool
|
||||
{
|
||||
return $this->hasDeletions() || $this->hasFailures();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if any successful deletions were made during pruning.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasDeletions(): bool
|
||||
{
|
||||
return $this->deleted > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if any unsuccessful deletions were attempted during pruning.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function hasFailures(): bool
|
||||
{
|
||||
return $this->deletedFailed > 0;
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
/**
|
||||
* Class ReconcileCommand.
|
||||
*/
|
||||
abstract class ReconcileCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$validator = $this->validator();
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$sourceRepository = $this->getSourceRepository($validator->validated());
|
||||
if ($sourceRepository === null) {
|
||||
Log::error('Could not find source repository', $validator->validated());
|
||||
$this->error('Could not find source repository');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$destinationRepository = $this->getDestinationRepository($validator->validated());
|
||||
if ($destinationRepository === null) {
|
||||
Log::error('Could not find destination repository', $validator->validated());
|
||||
$this->error('Could not find destination repository');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->handleFilters($validator->validated(), $sourceRepository, $destinationRepository);
|
||||
|
||||
$action = $this->getAction();
|
||||
|
||||
$results = $action->reconcileRepositories($sourceRepository, $destinationRepository);
|
||||
|
||||
$results->toLog();
|
||||
$results->toConsole($this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
abstract protected function validator(): Validator;
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
abstract protected function getSourceRepository(array $validated): ?RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
abstract protected function getDestinationRepository(array $validated): ?RepositoryInterface;
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param array $validated
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @return void
|
||||
*/
|
||||
abstract protected function handleFilters(
|
||||
array $validated,
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Get the reconciliation action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
abstract protected function getAction(): ReconcileRepositories;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Billing\Balance;
|
||||
|
||||
use App\Concerns\Repositories\Billing\ReconcilesBalanceRepositories;
|
||||
use App\Console\Commands\Repositories\Billing\ServiceReconcileCommand;
|
||||
|
||||
/**
|
||||
* Class BalanceReconcileCommand.
|
||||
*/
|
||||
class BalanceReconcileCommand extends ServiceReconcileCommand
|
||||
{
|
||||
use ReconcilesBalanceRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:balance {service}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between vendor billing API and balance database';
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Billing;
|
||||
|
||||
use App\Console\Commands\Repositories\ReconcileCommand;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use BenSampo\Enum\Rules\EnumKey;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
|
||||
/**
|
||||
* Class ServiceReconcileCommand.
|
||||
*/
|
||||
abstract class ServiceReconcileCommand extends ReconcileCommand
|
||||
{
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
protected function validator(): Validator
|
||||
{
|
||||
return ValidatorFacade::make($this->arguments(), [
|
||||
'service' => ['required', new EnumKey(Service::class)],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Billing\Transaction;
|
||||
|
||||
use App\Concerns\Repositories\Billing\ReconcilesTransactionRepositories;
|
||||
use App\Console\Commands\Repositories\Billing\ServiceReconcileCommand;
|
||||
|
||||
/**
|
||||
* Class TransactionReconcileCommand.
|
||||
*/
|
||||
class TransactionReconcileCommand extends ServiceReconcileCommand
|
||||
{
|
||||
use ReconcilesTransactionRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:transaction {service}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between vendor billing API and transaction database';
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories;
|
||||
|
||||
use App\Concerns\Repositories\ReconcilesRepositories;
|
||||
use App\Console\Commands\BaseCommand;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
/**
|
||||
* Class ReconcileCommand.
|
||||
*/
|
||||
abstract class ReconcileCommand extends BaseCommand
|
||||
{
|
||||
use ReconcilesRepositories;
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$validator = $this->validator();
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$result = $this->reconcileRepositories($validator->validated());
|
||||
|
||||
$result->toLog();
|
||||
$result->toConsole($this);
|
||||
|
||||
return $result->hasFailed() ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
abstract protected function validator(): Validator;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Storage\Admin;
|
||||
|
||||
use App\Concerns\Repositories\Admin\ReconcilesDumpRepositories;
|
||||
use App\Console\Commands\Repositories\Storage\StorageReconcileCommand;
|
||||
use App\Constants\Config\DumpConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class DumpReconcileCommand.
|
||||
*/
|
||||
class DumpReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
use ReconcilesDumpRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:dump';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and dump database';
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(DumpConstants::DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
+12
-15
@@ -2,41 +2,38 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Wiki;
|
||||
namespace App\Console\Commands\Repositories\Storage;
|
||||
|
||||
use App\Console\Commands\ReconcileCommand;
|
||||
use App\Console\Commands\Repositories\ReconcileCommand;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Contracts\Storage\InteractsWithDisk;
|
||||
use App\Rules\Storage\StorageDirectoryExistsRule;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
|
||||
/**
|
||||
* Class StorageReconcileCommand.
|
||||
*/
|
||||
abstract class StorageReconcileCommand extends ReconcileCommand
|
||||
abstract class StorageReconcileCommand extends ReconcileCommand implements InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* Get the name of the disk that represents the filesystem.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function disk(): string;
|
||||
|
||||
/**
|
||||
* Apply filters to repositories before reconciliation.
|
||||
*
|
||||
* @param array $validated
|
||||
* @param RepositoryInterface $sourceRepository
|
||||
* @param RepositoryInterface $destinationRepository
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function handleFilters(
|
||||
array $validated,
|
||||
RepositoryInterface $sourceRepository,
|
||||
RepositoryInterface $destinationRepository
|
||||
RepositoryInterface $destinationRepository,
|
||||
array $data = []
|
||||
): void {
|
||||
$path = Arr::get($validated, 'path');
|
||||
parent::handleFilters($sourceRepository, $destinationRepository, $data);
|
||||
|
||||
$path = Arr::get($data, 'path');
|
||||
if ($path !== null) {
|
||||
$sourceRepository->handleFilter('path', $path);
|
||||
$destinationRepository->handleFilter('path', $path);
|
||||
@@ -52,7 +49,7 @@ abstract class StorageReconcileCommand extends ReconcileCommand
|
||||
{
|
||||
$fs = Storage::disk($this->disk());
|
||||
|
||||
return \Illuminate\Support\Facades\Validator::make($this->options(), [
|
||||
return ValidatorFacade::make($this->options(), [
|
||||
'path' => ['nullable', 'string', 'doesnt_start_with:/', new StorageDirectoryExistsRule($fs)],
|
||||
]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Storage\Wiki;
|
||||
|
||||
use App\Concerns\Repositories\Wiki\ReconcilesAudioRepositories;
|
||||
use App\Console\Commands\Repositories\Storage\StorageReconcileCommand;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class AudioReconcileCommand.
|
||||
*/
|
||||
class AudioReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
use ReconcilesAudioRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:audio
|
||||
{--path= : The directory of audios to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and audios database';
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Storage\Wiki\Video;
|
||||
|
||||
use App\Concerns\Repositories\Wiki\Video\ReconcilesScriptRepositories;
|
||||
use App\Console\Commands\Repositories\Storage\StorageReconcileCommand;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class ScriptReconcileCommand.
|
||||
*/
|
||||
class ScriptReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
use ReconcilesScriptRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:script
|
||||
{--path= : The directory of scripts to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and script database';
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::SCRIPT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Repositories\Storage\Wiki;
|
||||
|
||||
use App\Concerns\Repositories\Wiki\ReconcilesVideoRepositories;
|
||||
use App\Console\Commands\Repositories\Storage\StorageReconcileCommand;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class VideoReconcileCommand.
|
||||
*/
|
||||
class VideoReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
use ReconcilesVideoRepositories;
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:video
|
||||
{--path= : The directory of videos to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and video database';
|
||||
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
}
|
||||
+9
-21
@@ -2,15 +2,15 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Document;
|
||||
namespace App\Console\Commands\Storage\Admin;
|
||||
|
||||
use App\Console\Commands\DatabaseDumpCommand;
|
||||
use App\Models\Document\Page;
|
||||
use App\Actions\Storage\Admin\Dump\DumpAction;
|
||||
use App\Actions\Storage\Admin\Dump\DumpDocumentAction;
|
||||
|
||||
/**
|
||||
* Class DocumentDatabaseDumpCommand.
|
||||
* Class DocumentDumpCommand.
|
||||
*/
|
||||
class DocumentDatabaseDumpCommand extends DatabaseDumpCommand
|
||||
class DocumentDumpCommand extends DumpCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
@@ -43,24 +43,12 @@ class DocumentDatabaseDumpCommand extends DatabaseDumpCommand
|
||||
protected $description = 'Produces sanitized database dump, targeting document-related tables for seeding purposes';
|
||||
|
||||
/**
|
||||
* The list of tables to include in the dump.
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return array
|
||||
* @return DumpAction
|
||||
*/
|
||||
protected function allowedTables(): array
|
||||
protected function action(): DumpAction
|
||||
{
|
||||
return [
|
||||
Page::TABLE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory that the file should be dumped to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDumpFilePath(): string
|
||||
{
|
||||
return 'document';
|
||||
return new DumpDocumentAction($this->options());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Storage\Admin;
|
||||
|
||||
use App\Actions\Storage\Admin\Dump\DumpAction;
|
||||
use App\Console\Commands\BaseCommand;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class DatabaseDumpCommand.
|
||||
*/
|
||||
abstract class DumpCommand extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$validator = $this->validator();
|
||||
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
Log::error($error);
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$action = $this->action();
|
||||
|
||||
$result = $action->handle();
|
||||
|
||||
$result->toLog();
|
||||
$result->toConsole($this);
|
||||
|
||||
return $result->hasFailed() ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
protected function validator(): Validator
|
||||
{
|
||||
return ValidatorFacade::make($this->options(), [
|
||||
'default-character-set' => ['string'],
|
||||
'set-gtid-purged' => [Rule::in(['OFF', 'ON', 'AUTO'])->__toString()],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return DumpAction
|
||||
*/
|
||||
abstract protected function action(): DumpAction;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Storage\Admin;
|
||||
|
||||
use App\Actions\Storage\Admin\Dump\PruneDumpAction;
|
||||
use App\Actions\Storage\Base\PruneAction;
|
||||
use App\Console\Commands\Storage\Base\PruneCommand;
|
||||
|
||||
/**
|
||||
* Class DumpPruneCommand.
|
||||
*/
|
||||
class DumpPruneCommand extends PruneCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'prune:dump {--H|hours=72 : The number of hours to retain dumps}';
|
||||
|
||||
/**
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return PruneAction
|
||||
*/
|
||||
protected function getAction(): PruneAction
|
||||
{
|
||||
$hours = $this->option('hours');
|
||||
|
||||
return new PruneDumpAction(intval($hours));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Storage\Admin;
|
||||
|
||||
use App\Actions\Storage\Admin\Dump\DumpAction;
|
||||
use App\Actions\Storage\Admin\Dump\DumpWikiAction;
|
||||
|
||||
/**
|
||||
* Class WikiDumpCommand.
|
||||
*/
|
||||
class WikiDumpCommand extends DumpCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'db:dump-wiki
|
||||
{--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 : 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}
|
||||
';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Produces sanitized database dump, targeting wiki-related tables for seeding purposes';
|
||||
|
||||
/**
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return DumpAction
|
||||
*/
|
||||
protected function action(): DumpAction
|
||||
{
|
||||
return new DumpWikiAction($this->options());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Storage\Base;
|
||||
|
||||
use App\Actions\Storage\Base\PruneAction;
|
||||
use App\Console\Commands\Storage\StorageCommand;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Facades\Validator as ValidatorFacade;
|
||||
|
||||
/**
|
||||
* Class PruneCommand.
|
||||
*/
|
||||
abstract class PruneCommand extends StorageCommand
|
||||
{
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Prune stale files from storage';
|
||||
|
||||
/**
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return PruneAction
|
||||
*/
|
||||
abstract protected function getAction(): PruneAction;
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
protected function validator(): Validator
|
||||
{
|
||||
return ValidatorFacade::make($this->options(), [
|
||||
'hours' => ['required', 'integer'],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Storage;
|
||||
|
||||
use App\Console\Commands\BaseCommand;
|
||||
use App\Contracts\Actions\Storage\StorageAction;
|
||||
use Illuminate\Contracts\Validation\Validator;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class StorageCommand.
|
||||
*/
|
||||
abstract class StorageCommand extends BaseCommand
|
||||
{
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$validator = $this->validator();
|
||||
|
||||
if ($validator->fails()) {
|
||||
foreach ($validator->errors()->all() as $error) {
|
||||
Log::error($error);
|
||||
$this->error($error);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$action = $this->getAction();
|
||||
|
||||
$storageResults = $action->handle();
|
||||
|
||||
$storageResults->toLog();
|
||||
$storageResults->toConsole($this);
|
||||
|
||||
$action->then($storageResults);
|
||||
|
||||
$result = $storageResults->toActionResult();
|
||||
|
||||
return $result->hasFailed() ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying action.
|
||||
*
|
||||
* @return StorageAction
|
||||
*/
|
||||
abstract protected function getAction(): StorageAction;
|
||||
|
||||
/**
|
||||
* Get the validator for options.
|
||||
*
|
||||
* @return Validator
|
||||
*/
|
||||
abstract protected function validator(): Validator;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Wiki\Audio;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Audio\ReconcileAudioRepositories;
|
||||
use App\Console\Commands\Wiki\StorageReconcileCommand;
|
||||
use App\Constants\Config\AudioConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\AudioRepository as AudioDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\AudioRepository as AudioSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class AudioReconcileCommand.
|
||||
*/
|
||||
class AudioReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:audio
|
||||
{--path= : The directory of audios to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and audios database';
|
||||
|
||||
/**
|
||||
* Get the name of the disk that represents the filesystem.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function disk(): string
|
||||
{
|
||||
return Config::get(AudioConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return App::make(AudioSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return App::make(AudioDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconciliation action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function getAction(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileAudioRepositories();
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Wiki\Video;
|
||||
|
||||
use App\Actions\Repositories\ReconcileRepositories;
|
||||
use App\Actions\Repositories\Wiki\Video\ReconcileVideoRepositories;
|
||||
use App\Console\Commands\Wiki\StorageReconcileCommand;
|
||||
use App\Constants\Config\VideoConstants;
|
||||
use App\Contracts\Repositories\RepositoryInterface;
|
||||
use App\Repositories\Eloquent\Wiki\VideoRepository as VideoDestinationRepository;
|
||||
use App\Repositories\Storage\Wiki\VideoRepository as VideoSourceRepository;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
|
||||
/**
|
||||
* Class VideoReconcileCommand.
|
||||
*/
|
||||
class VideoReconcileCommand extends StorageReconcileCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'reconcile:video
|
||||
{--path= : The directory of videos to reconcile. Ex: 2022/Spring/. If unspecified, all directories will be listed.}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Perform set reconciliation between object storage and video database';
|
||||
|
||||
/**
|
||||
* Get the name of the disk that represents the filesystem.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function disk(): string
|
||||
{
|
||||
return Config::get(VideoConstants::DEFAULT_DISK_QUALIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get source repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getSourceRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return App::make(VideoSourceRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get destination repository for action.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return RepositoryInterface|null
|
||||
*/
|
||||
protected function getDestinationRepository(array $validated): ?RepositoryInterface
|
||||
{
|
||||
return App::make(VideoDestinationRepository::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reconciliation action.
|
||||
*
|
||||
* @return ReconcileRepositories
|
||||
*/
|
||||
protected function getAction(): ReconcileRepositories
|
||||
{
|
||||
return new ReconcileVideoRepositories();
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Console\Commands\Wiki;
|
||||
|
||||
use App\Console\Commands\DatabaseDumpCommand;
|
||||
use App\Models\Wiki\Anime;
|
||||
use App\Models\Wiki\Anime\AnimeSynonym;
|
||||
use App\Models\Wiki\Anime\AnimeTheme;
|
||||
use App\Models\Wiki\Anime\Theme\AnimeThemeEntry;
|
||||
use App\Models\Wiki\Artist;
|
||||
use App\Models\Wiki\Audio;
|
||||
use App\Models\Wiki\ExternalResource;
|
||||
use App\Models\Wiki\Image;
|
||||
use App\Models\Wiki\Series;
|
||||
use App\Models\Wiki\Song;
|
||||
use App\Models\Wiki\Studio;
|
||||
use App\Models\Wiki\Video;
|
||||
use App\Pivots\AnimeImage;
|
||||
use App\Pivots\AnimeResource;
|
||||
use App\Pivots\AnimeSeries;
|
||||
use App\Pivots\AnimeStudio;
|
||||
use App\Pivots\AnimeThemeEntryVideo;
|
||||
use App\Pivots\ArtistImage;
|
||||
use App\Pivots\ArtistMember;
|
||||
use App\Pivots\ArtistResource;
|
||||
use App\Pivots\ArtistSong;
|
||||
use App\Pivots\StudioImage;
|
||||
use App\Pivots\StudioResource;
|
||||
|
||||
/**
|
||||
* Class WikiDatabaseDumpCommand.
|
||||
*/
|
||||
class WikiDatabaseDumpCommand extends DatabaseDumpCommand
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'db:dump-wiki
|
||||
{--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 : 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}
|
||||
';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Produces sanitized database dump, targeting wiki-related tables for seeding purposes';
|
||||
|
||||
/**
|
||||
* The list of tables to include in the dump.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function allowedTables(): array
|
||||
{
|
||||
return [
|
||||
Anime::TABLE,
|
||||
AnimeImage::TABLE,
|
||||
AnimeResource::TABLE,
|
||||
AnimeSeries::TABLE,
|
||||
AnimeStudio::TABLE,
|
||||
AnimeSynonym::TABLE,
|
||||
AnimeTheme::TABLE,
|
||||
AnimeThemeEntry::TABLE,
|
||||
AnimeThemeEntryVideo::TABLE,
|
||||
Artist::TABLE,
|
||||
ArtistImage::TABLE,
|
||||
ArtistMember::TABLE,
|
||||
ArtistResource::TABLE,
|
||||
ArtistSong::TABLE,
|
||||
Audio::TABLE,
|
||||
ExternalResource::TABLE,
|
||||
Image::TABLE,
|
||||
Series::TABLE,
|
||||
Song::TABLE,
|
||||
Studio::TABLE,
|
||||
StudioImage::TABLE,
|
||||
StudioResource::TABLE,
|
||||
Video::TABLE,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* The directory that the file should be dumped to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDumpFilePath(): string
|
||||
{
|
||||
return 'wiki';
|
||||
}
|
||||
}
|
||||
+22
-17
@@ -4,16 +4,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Console\Commands\Billing\Balance\BalanceReconcileCommand;
|
||||
use App\Console\Commands\Billing\Transaction\TransactionReconcileCommand;
|
||||
use App\Console\Commands\Document\DocumentDatabaseDumpCommand;
|
||||
use App\Console\Commands\PruneDatabaseDumpsCommand;
|
||||
use App\Console\Commands\Wiki\WikiDatabaseDumpCommand;
|
||||
use App\Console\Commands\Repositories\Billing\Balance\BalanceReconcileCommand;
|
||||
use App\Console\Commands\Repositories\Billing\Transaction\TransactionReconcileCommand;
|
||||
use App\Console\Commands\Storage\Admin\DocumentDumpCommand;
|
||||
use App\Console\Commands\Storage\Admin\DumpPruneCommand;
|
||||
use App\Console\Commands\Storage\Admin\WikiDumpCommand;
|
||||
use App\Enums\Models\Billing\Service;
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Database\Console\MonitorCommand as MonitorDatabaseCommand;
|
||||
use Illuminate\Database\Console\PruneCommand as PruneModelsCommand;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
use Illuminate\Queue\Console\MonitorCommand as MonitorQueueCommand;
|
||||
use Illuminate\Queue\Console\PruneFailedJobsCommand;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Laravel\Horizon\Console\SnapshotCommand;
|
||||
@@ -41,33 +43,36 @@ class Kernel extends ConsoleKernel
|
||||
->storeOutput()
|
||||
->hourly();
|
||||
|
||||
// Managed database requires --single-transaction and --set-gtid-purged=OFF
|
||||
$schedule->command(DocumentDatabaseDumpCommand::class, ['--single-transaction', '--set-gtid-purged' => 'OFF'])
|
||||
$schedule->command(DocumentDumpCommand::class)
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->daily();
|
||||
|
||||
// Managed database requires --single-transaction and --set-gtid-purged=OFF
|
||||
$schedule->command(WikiDatabaseDumpCommand::class, ['--single-transaction', '--set-gtid-purged' => 'OFF'])
|
||||
$schedule->command(WikiDumpCommand::class)
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->daily();
|
||||
|
||||
// Managed database requires --single-transaction and --set-gtid-purged=OFF
|
||||
$schedule->command(WikiDatabaseDumpCommand::class, ['--single-transaction', '--set-gtid-purged' => 'OFF', '--no-create-info'])
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->daily();
|
||||
|
||||
$schedule->command(PruneDatabaseDumpsCommand::class)
|
||||
$schedule->command(DumpPruneCommand::class)
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->dailyAt('00:15');
|
||||
|
||||
$schedule->command(MonitorDatabaseCommand::class, ['--max' => 100])
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->everyMinute();
|
||||
|
||||
$schedule->command(MonitorQueueCommand::class, ['queues' => 'default', '--max' => 100])
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
->storeOutput()
|
||||
->everyMinute();
|
||||
|
||||
$schedule->command(PruneExpired::class)
|
||||
->withoutOverlapping()
|
||||
->runInBackground()
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Constants\Config;
|
||||
|
||||
/**
|
||||
* Class DumpConstants.
|
||||
*/
|
||||
class DumpConstants
|
||||
{
|
||||
final public const DISK_QUALIFIED = 'dump.disk';
|
||||
|
||||
final public const PATH_QUALIFIED = 'dump.path';
|
||||
|
||||
final public const URL_QUALIFIED = 'dump.url';
|
||||
}
|
||||
@@ -24,4 +24,12 @@ class FlagConstants
|
||||
final public const ALLOW_VIEW_RECORDING_FLAG = 'allow_view_recording';
|
||||
|
||||
final public const ALLOW_VIEW_RECORDING_FLAG_QUALIFIED = 'flags.allow_view_recording';
|
||||
|
||||
final public const ALLOW_DUMP_DOWNLOADING_FLAG = 'allow_dump_downloading';
|
||||
|
||||
final public const ALLOW_DUMP_DOWNLOADING_FLAG_QUALIFIED = 'flags.allow_dump_downloading';
|
||||
|
||||
final public const ALLOW_SCRIPT_DOWNLOADING_FLAG = 'allow_script_downloading';
|
||||
|
||||
final public const ALLOW_SCRIPT_DOWNLOADING_FLAG_QUALIFIED = 'flags.allow_script_downloading';
|
||||
}
|
||||
|
||||
@@ -21,7 +21,15 @@ class VideoConstants
|
||||
|
||||
final public const PATH_QUALIFIED = 'video.path';
|
||||
|
||||
final public const RATE_LIMITER_QUALIFIED = 'video.rate_limiter';
|
||||
|
||||
final public const STREAMING_METHOD_QUALIFIED = 'video.streaming_method';
|
||||
|
||||
final public const URL_QUALIFIED = 'video.url';
|
||||
|
||||
final public const SCRIPT_DISK_QUALIFIED = 'video.script.disk';
|
||||
|
||||
final public const SCRIPT_PATH_QUALIFIED = 'video.script.path';
|
||||
|
||||
final public const SCRIPT_URL_QUALIFIED = 'video.script.url';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Actions\Storage;
|
||||
|
||||
/**
|
||||
* Interface StorageAction.
|
||||
*/
|
||||
interface StorageAction
|
||||
{
|
||||
/**
|
||||
* Handle action.
|
||||
*
|
||||
* @return StorageResults
|
||||
*/
|
||||
public function handle(): StorageResults;
|
||||
|
||||
/**
|
||||
* Processes to be completed after handling action.
|
||||
*
|
||||
* @param StorageResults $storageResults
|
||||
* @return void
|
||||
*/
|
||||
public function then(StorageResults $storageResults): void;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Actions\Storage;
|
||||
|
||||
use App\Actions\ActionResult;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
/**
|
||||
* Interface StorageResults.
|
||||
*/
|
||||
interface StorageResults
|
||||
{
|
||||
/**
|
||||
* Write results to log.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function toLog(): void;
|
||||
|
||||
/**
|
||||
* Write results to console output.
|
||||
*
|
||||
* @param Command $command
|
||||
* @return void
|
||||
*/
|
||||
public function toConsole(Command $command): void;
|
||||
|
||||
/**
|
||||
* Transform to Action Result.
|
||||
*
|
||||
* @return ActionResult
|
||||
*/
|
||||
public function toActionResult(): ActionResult;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Storage;
|
||||
|
||||
/**
|
||||
* Class InteractsWithDisk.
|
||||
*/
|
||||
interface InteractsWithDisk
|
||||
{
|
||||
/**
|
||||
* The name of the disk.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function disk(): string;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Contracts\Storage;
|
||||
|
||||
/**
|
||||
* Class InteractsWithDisks.
|
||||
*/
|
||||
interface InteractsWithDisks
|
||||
{
|
||||
/**
|
||||
* The list of disk names.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function disks(): array;
|
||||
}
|
||||
@@ -27,4 +27,15 @@ abstract class BaseEnum extends Enum implements LocalizedEnum
|
||||
fn (BaseEnum $enum) => Str::lower($enum->description) === Str::lower($description)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to instantiate a new Enum using the given key or value.
|
||||
* Note: library coerce function does not attempt unstrict value comparision.
|
||||
*/
|
||||
public static function unstrictCoerce(mixed $enumKeyOrValue): ?static
|
||||
{
|
||||
return is_numeric($enumKeyOrValue)
|
||||
? static::coerce(intval($enumKeyOrValue))
|
||||
: static::coerce($enumKeyOrValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Enums\Http;
|
||||
|
||||
use App\Enums\BaseEnum;
|
||||
|
||||
/**
|
||||
* Class StreamingMethod.
|
||||
*/
|
||||
class StreamingMethod extends BaseEnum
|
||||
{
|
||||
public const NGINX = 'nginx';
|
||||
public const RESPONSE = 'response';
|
||||
}
|
||||
@@ -175,12 +175,34 @@ final class ResourceSite extends BaseEnum
|
||||
public static function formatAnimeResourceLink(ResourceSite $site, int $id, ?string $slug = null): ?string
|
||||
{
|
||||
return match ($site->value) {
|
||||
ResourceSite::ANIDB => "https://anidb.net/anime/$id/",
|
||||
ResourceSite::ANILIST => "https://anilist.co/anime/$id/",
|
||||
ResourceSite::TWITTER => "https://twitter.com/$slug",
|
||||
ResourceSite::ANIDB => "https://anidb.net/anime/$id",
|
||||
ResourceSite::ANILIST => "https://anilist.co/anime/$id",
|
||||
ResourceSite::ANIME_PLANET => "https://www.anime-planet.com/anime/$slug",
|
||||
ResourceSite::ANN => "https://www.animenewsnetwork.com/encyclopedia/anime.php?id=$id",
|
||||
ResourceSite::KITSU => "https://kitsu.io/anime/$slug",
|
||||
ResourceSite::MAL => "https://myanimelist.net/anime/$id/",
|
||||
ResourceSite::MAL => "https://myanimelist.net/anime/$id",
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL of the site for artist resources.
|
||||
*
|
||||
* @param ResourceSite $site
|
||||
* @param int $id
|
||||
* @param string|null $slug
|
||||
* @return string|null
|
||||
*/
|
||||
public static function formatArtistResourceLink(ResourceSite $site, int $id, ?string $slug = null): ?string
|
||||
{
|
||||
return match ($site->value) {
|
||||
ResourceSite::TWITTER => "https://twitter.com/$slug",
|
||||
ResourceSite::ANIDB => "https://anidb.net/creator/$id",
|
||||
ResourceSite::ANILIST => "https://anilist.co/staff/$id",
|
||||
ResourceSite::ANIME_PLANET => "https://www.anime-planet.com/people/$slug",
|
||||
ResourceSite::ANN => "https://www.animenewsnetwork.com/encyclopedia/people.php?id=$id",
|
||||
ResourceSite::MAL => "https://myanimelist.net/people/$id",
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
@@ -196,11 +218,12 @@ final class ResourceSite extends BaseEnum
|
||||
public static function formatStudioResourceLink(ResourceSite $site, int $id, ?string $slug = null): ?string
|
||||
{
|
||||
return match ($site->value) {
|
||||
ResourceSite::ANIDB => "https://anidb.net/creator/$id/",
|
||||
ResourceSite::ANILIST => "https://anilist.co/studio/$id/",
|
||||
ResourceSite::TWITTER => "https://twitter.com/$slug",
|
||||
ResourceSite::ANIDB => "https://anidb.net/creator/$id",
|
||||
ResourceSite::ANILIST => "https://anilist.co/studio/$id",
|
||||
ResourceSite::ANIME_PLANET => "https://www.anime-planet.com/anime/studios/$slug",
|
||||
ResourceSite::ANN => "https://www.animenewsnetwork.com/encyclopedia/company.php?id=$id",
|
||||
ResourceSite::MAL => "https://myanimelist.net/anime/producer/$id/",
|
||||
ResourceSite::MAL => "https://myanimelist.net/anime/producer/$id",
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Admin\Dump;
|
||||
|
||||
use App\Events\Base\Admin\AdminCreatedEvent;
|
||||
use App\Models\Admin\Dump;
|
||||
|
||||
/**
|
||||
* Class DumpCreated.
|
||||
*
|
||||
* @extends AdminCreatedEvent<Dump>
|
||||
*/
|
||||
class DumpCreated extends AdminCreatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Dump $dump
|
||||
*/
|
||||
public function __construct(Dump $dump)
|
||||
{
|
||||
parent::__construct($dump);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return Dump
|
||||
*/
|
||||
public function getModel(): Dump
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Dump '**{$this->getModel()->getName()}**' has been created.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Admin\Dump;
|
||||
|
||||
use App\Events\Base\Admin\AdminDeletedEvent;
|
||||
use App\Models\Admin\Dump;
|
||||
|
||||
/**
|
||||
* Class DumpDeleted.
|
||||
*
|
||||
* @extends AdminDeletedEvent<Dump>
|
||||
*/
|
||||
class DumpDeleted extends AdminDeletedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Dump $dump
|
||||
*/
|
||||
public function __construct(Dump $dump)
|
||||
{
|
||||
parent::__construct($dump);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return Dump
|
||||
*/
|
||||
public function getModel(): Dump
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Dump '**{$this->getModel()->getName()}**' has been deleted.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Admin\Dump;
|
||||
|
||||
use App\Events\Base\Admin\AdminRestoredEvent;
|
||||
use App\Models\Admin\Dump;
|
||||
|
||||
/**
|
||||
* Class DumpRestored.
|
||||
*
|
||||
* @extends AdminRestoredEvent<Dump>
|
||||
*/
|
||||
class DumpRestored extends AdminRestoredEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Dump $dump
|
||||
*/
|
||||
public function __construct(Dump $dump)
|
||||
{
|
||||
parent::__construct($dump);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return Dump
|
||||
*/
|
||||
public function getModel(): Dump
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Dump '**{$this->getModel()->getName()}**' has been restored.";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Events\Admin\Dump;
|
||||
|
||||
use App\Events\Base\Admin\AdminUpdatedEvent;
|
||||
use App\Models\Admin\Dump;
|
||||
|
||||
/**
|
||||
* Class DumpUpdated.
|
||||
*
|
||||
* @extends AdminUpdatedEvent<Dump>
|
||||
*/
|
||||
class DumpUpdated extends AdminUpdatedEvent
|
||||
{
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param Dump $dump
|
||||
*/
|
||||
public function __construct(Dump $dump)
|
||||
{
|
||||
parent::__construct($dump);
|
||||
$this->initializeEmbedFields($dump);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the model that has fired this event.
|
||||
*
|
||||
* @return Dump
|
||||
*/
|
||||
public function getModel(): Dump
|
||||
{
|
||||
return $this->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description for the Discord message payload.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getDiscordMessageDescription(): string
|
||||
{
|
||||
return "Dump '**{$this->getModel()->getName()}**' has been updated.";
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ abstract class PivotDeletedEvent extends BasePivotEvent
|
||||
{
|
||||
return DiscordMessage::create('', [
|
||||
'description' => $this->getDiscordMessageDescription(),
|
||||
'color' => EmbedColor::GREEN,
|
||||
'color' => EmbedColor::RED,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user