Configured the removal of old files that were not attached to the model or were marked as deleted.

This commit is contained in:
Leonid Nikitin 2024-07-26 22:18:01 +05:00
parent dde792b97a
commit c84ed9f12b
Signed by: kor-elf
GPG Key ID: 3C0F720C170F6E1D
10 changed files with 235 additions and 8 deletions

View File

@ -0,0 +1,50 @@
<?php declare(strict_types=1);
namespace App\Console\Commands\Files;
use App\Services\Commands\DeleteOldFilesService;
use Illuminate\Console\Command;
use Illuminate\Support\Carbon;
final class DeleteOldFilesFromStorage extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'files:delete-old-files-from-storage';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove temporary files or files that have been deleted from the storage table';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle(DeleteOldFilesService $deleteOldFilesService): void
{
$temporaryBeforeDate = Carbon::now()->subDays(3);
$deletedBeforeDate = Carbon::now()->subDays(7);
$result = $deleteOldFilesService->fromStorage($temporaryBeforeDate, $deletedBeforeDate);
if ($result->isError()) {
$this->error($result->getMessage());
return;
}
$this->info($result->getMessage());
}
}

View File

@ -0,0 +1,46 @@
<?php declare(strict_types=1);
namespace App\Services\Commands;
use App\Models\Storage as StorageModel;
use App\ServiceResults\ServiceResultError;
use App\ServiceResults\ServiceResultSuccess;
use App\Services\Service;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Storage as StorageSupport;
final class DeleteOldFilesService extends Service
{
/**
* $temporaryBeforeDate = date of deletion of temporary files
* $deletedBeforeDate = date of deletion files that were marked as deleted
*
* @param Carbon $temporaryBeforeDate
* @param Carbon $deletedBeforeDate
* @return ServiceResultError|ServiceResultSuccess
*/
public function fromStorage(Carbon $temporaryBeforeDate, Carbon $deletedBeforeDate): ServiceResultError|ServiceResultSuccess
{
$disk = config('storage.disk');
StorageModel::withTrashed()
->where(function (Builder $query) use($temporaryBeforeDate) {
$query->whereNull('morph_id')->where('updated_at', '<', $temporaryBeforeDate);
})->orWhere(function (Builder $query) use($deletedBeforeDate) {
$query->whereNotNull('deleted_at')->where('deleted_at', '<', $deletedBeforeDate);
})->chunkById(100, function ($items) use($disk) {
$deleteIds = [];
foreach ($items as $item) {
$deleteIds[] = $item->id;
if (StorageSupport::disk($disk)->exists($item->file)) {
StorageSupport::disk($disk)->delete($item->file);
}
}
StorageModel::withTrashed()->whereIn('id', $deleteIds)->forceDelete();
});
return $this->ok(__('Old Files deleted.'));
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('storage', function (Blueprint $table) {
$table->dropIndex(['morph_id']);
$table->index(['morph_id', 'updated_at']);
$table->index(['deleted_at']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('storage', function (Blueprint $table) {
$table->dropIndex(['morph_id', 'updated_at']);
$table->dropIndex(['deleted_at']);
$table->index(['morph_id']);
});
}
};

View File

@ -267,5 +267,6 @@
"Documentation successfully removed": "Documentation successfully removed",
"Category successfully created": "Category successfully created",
"Category updated successfully": "Category updated successfully",
"Category successfully deleted": "Category successfully deleted"
"Category successfully deleted": "Category successfully deleted",
"Old Files deleted": "Old Files deleted"
}

View File

@ -267,5 +267,6 @@
"Documentation successfully removed": "Документация успешно удалена",
"Category successfully created": "Категория успешно создана",
"Category updated successfully": "Категория успешно обновлена",
"Category successfully deleted": "Категория успешно удалена"
"Category successfully deleted": "Категория успешно удалена",
"Old Files deleted": "Старые файлы удалены"
}

View File

@ -1,2 +1,6 @@
<?php
use Illuminate\Support\Facades\Schedule;
$timezone = config('app.user_timezone');
Schedule::command(\App\Console\Commands\Files\DeleteOldFilesFromStorage::class)->timezone($timezone)->dailyAt('3:30');

View File

@ -64,10 +64,12 @@ FROM BUILD AS PRODUCTION
COPY --from=APP_BUILD_FOR_PRODUCTION /home/app /var/www/html
COPY docker/docker-entrypoint_prod.sh /home/unit/docker-entrypoint.sh
COPY docker/start.sh /usr/local/bin/start
WORKDIR /var/www/html
RUN chmod 755 /home/unit/docker-entrypoint.sh
RUN chmod 755 /home/unit/docker-entrypoint.sh \
&& chmod 755 /usr/local/bin/start
STOPSIGNAL SIGTERM
@ -81,17 +83,19 @@ FROM BUILD AS DEVELOP
WORKDIR /var/www/html
COPY docker/docker-entrypoint_dev.sh /home/unit/docker-entrypoint.sh
COPY docker/start.sh /usr/local/bin/start
STOPSIGNAL SIGTERM
RUN chmod 755 /home/unit/docker-entrypoint.sh \
&& chmod 755 /usr/local/bin/start \
&& apk --no-cache add git nodejs npm \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
ENTRYPOINT ["/home/unit/docker-entrypoint.sh"]
EXPOSE 9000
CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock", "--user", "unit", "--group", "unit"]
CMD ["/usr/local/bin/start"]

21
app/docker/start.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env sh
set -e
role=${CONTAINER_ROLE:-app}
if [ "$role" = "app" ]; then
exec unitd --no-daemon --control unix:/var/run/control.unit.sock --user unit --group unit
elif [ "$role" = "queue" ]; then
echo "Running the queue..."
while [ true ]
do
php /var/www/html/artisan queue:work --verbose --sleep=5 --tries=100 --backoff=10 --max-time=3600 --queue=high,normal,low,default
done
elif [ "$role" = "scheduler" ]; then
while [ true ]
do
php /var/www/html/artisan schedule:run --verbose --no-interaction &
sleep 60
done
else
echo "Could not match the container role \"$role\""
exit 1
fi

View File

@ -17,6 +17,35 @@ services:
environment:
CONTAINER_ROLE: app
UNIT_SOURCE: '"172.16.0.0/12"'
queue:
build:
context: app
dockerfile: docker/Dockerfile
target: PRODUCTION
# restart: always
depends_on:
- db
- app-redis
environment:
CONTAINER_ROLE: queue
volumes:
- ./app/application:/var/www/html
scheduler:
build:
context: app
dockerfile: docker/Dockerfile
target: PRODUCTION
# restart: always
depends_on:
- db
- app-redis
environment:
CONTAINER_ROLE: scheduler
volumes:
- ./app/application:/var/www/html
app-redis:
image: redis:3.0-alpine
# restart: always

View File

@ -13,13 +13,40 @@ services:
- ${DOCKER_APP_PORT}:9000
volumes:
- ./app/application:/var/www/html
queue:
build:
context: app
dockerfile: docker/Dockerfile
target: DEVELOP
depends_on:
- db
- app-redis
environment:
CONTAINER_ROLE: queue
volumes:
- ./app/application:/var/www/html
scheduler:
build:
context: app
dockerfile: docker/Dockerfile
target: DEVELOP
depends_on:
- db
- app-redis
environment:
CONTAINER_ROLE: scheduler
volumes:
- ./app/application:/var/www/html
app-redis:
image: redis:3.0-alpine
volumes:
- ./redis/data:/data
captcha-app:
image: korelf/service-captcha:0.8.1
image: korelf/service-captcha:0.8.2
cap_drop:
- ALL
cap_add:
@ -31,10 +58,13 @@ services:
- db
- captcha-redis
env_file: captcha-app/.env
volumes:
- ./captcha-app/app/storage/app:/var/www/html/storage/app
- ./captcha-app/app/storage/logs:/var/www/html/storage/logs
ports:
- ${DOCKER_CAPTCHA_PORT}:9000
captcha-queue:
image: korelf/service-captcha:0.8.1
image: korelf/service-captcha:0.8.2
# restart: always
depends_on:
- db
@ -42,8 +72,11 @@ services:
environment:
CONTAINER_ROLE: queue
env_file: captcha-app/.env
volumes:
- ./captcha-app/app/storage/app:/var/www/html/storage/app
- ./captcha-app/app/storage/logs:/var/www/html/storage/logs
captcha-reverb:
image: korelf/service-captcha:0.8.1
image: korelf/service-captcha:0.8.2
# restart: always
depends_on:
- db
@ -51,10 +84,13 @@ services:
environment:
CONTAINER_ROLE: websockets
env_file: captcha-app/.env
volumes:
- ./captcha-app/app/storage/app:/var/www/html/storage/app
- ./captcha-app/app/storage/logs:/var/www/html/storage/logs
ports:
- ${DOCKER_CAPTCHA_WEBSOCKET_PORT}:9000
captcha-scheduler:
image: korelf/service-captcha:0.8.1
image: korelf/service-captcha:0.8.2
# restart: always
depends_on:
- db
@ -62,6 +98,9 @@ services:
environment:
CONTAINER_ROLE: scheduler
env_file: captcha-app/.env
volumes:
- ./captcha-app/app/storage/app:/var/www/html/storage/app
- ./captcha-app/app/storage/logs:/var/www/html/storage/logs
captcha-redis:
image: redis:3.0-alpine
volumes: