Плагин NGCMS для поиска файлов в
`uploads`, которые не используются сайтом, и их безопасного ручного удаления.
Код:
ID: filecleaner
Name: Очистка неиспользуемых файлов
Name_EN: Unused Files Cleaner
Version: 0.1.0
Acts: core, admin:mod:extra-config
File: filecleaner.php
Config: config.php
Type: maintanance
Description: Поиск и безопасное удаление файлов из uploads, которые не используются в новостях.
Author: NGCMS
Title: Очистка файлов
Information: Сканирует uploads, показывает кандидатов на удаление и ведёт журнал операций.
Плагин «Очистка неиспользуемых файлов»Плагин работает в режиме предварительной проверки:
- сканирует только выбранные каталоги;
- собирает ссылки на файлы из контента и таблиц базы данных;
- защищает новые файлы и явно исключённые пути;
- показывает результат с фильтрами, сортировкой и пагинацией;
- удаляет файлы только после ручного подтверждения;
- повторно проверяет файл непосредственно перед удалением;
- ведёт отдельные журналы удаления и операций;
- может запускать фоновое сканирование через cron;
- не удаляет файлы автоматически.
Требования- NGCMS с поддержкой плагинов и
`extra-config`;
- PHP 7.2 или новее, рекомендуется PHP 8.3;
- доступ к каталогу
`uploads`;
- права веб-сервера на чтение файлов и удаление только тех файлов, которые администратор выбрал вручную;
- для источников базы данных должен быть доступен штатный объект
`$mysql`.
Плагин не изменяет файлы ядра NGCMS. Его основные файлы находятся в:
engine/plugins/filecleaner/ Установка и запуск1. Установите каталог плагина в
`engine/plugins/filecleaner/`.2. Включите плагин штатным менеджером плагинов NGCMS.
3. Откройте административную страницу:
admin.php?mod=extra-config&plugin=filecleaner4. На вкладке
«Настройки» выберите каталоги для сканирования.
5. Сохраните настройки.
6. Нажмите
«Сканировать».
7. После завершения проверьте статусы файлов.
8. Удаляйте только файлы со статусом
«Не используется».
Перед первым удалением рекомендуется сделать резервную копию файлов и базы данных.
Вкладка «Файлы»
СтатистикаВ верхней части отображаются:
- общее количество найденных файлов;
- количество файлов со статусом
«Используется»;
- количество файлов со статусом
«Не используется»;
- общий размер потенциального мусора.
Статистика рассчитывается по последнему завершённому сканированию.
Поиск и фильтрыДоступны следующие фильтры:
- поиск по пути файла;
- статус;
- тип файла;
- диапазон размера;
- сортировка по имени, размеру, дате изменения или возрасту;
- направление сортировки;
- количество строк на странице.
Количество файлов на странице:
- 30 по умолчанию;
- 50;
- 70;
- 100;
- 200.
Выбранное значение сохраняется в URL при переходе между страницами.
Для изображений доступен предпросмотр и ссылка
«Открыть файл». Предпросмотр выводится только для файлов, которые физически существуют в
`uploads`, поэтому старые записи сканирования не должны создавать 404-запросы.
УдалениеУдаление бывает одиночным и массовым.
Перед отправкой формы:
- показывается подтверждение;
- для массового удаления отображается количество файлов и общий размер;
- кнопки блокируются и показывают индикатор загрузки;
- повторная отправка формы запрещается.
На сервере выполняется повторная проверка:
1. путь нормализуется;
2. файл должен находиться внутри физического каталога
`uploads`;
3. запись должна присутствовать в последнем
`scan.json`;
4. статус записи должен быть
`unused`;
5. файл должен существовать;
6. файл не должен быть исключён настройками;
7. возраст файла должен соответствовать настройке защиты;
8. все источники использования вызываются повторно;
9. только после этого выполняется
`unlink()`.
Если запись осталась в результате старого сканирования, а файла уже нет, запись удаляется из
`scan.json` и выводится сообщение
«Файл уже отсутствует».
Ошибки источников использования блокируют удаление, потому что при недоступной проверке нельзя безопасно считать файл неиспользуемым.
Статусы файлов «Используется» (`used`)Файл найден в активном содержимом или источнике, который сообщает фактическое использование. Такой файл не удаляется.
«Файл зарегистрирован» (`registered`)Файл зарегистрирован в файловых таблицах или связан с объектом, но источник не подтвердил прямое использование. Такой файл также не удаляется автоматически и не предлагается к удалению.
«Защищён» (`protected`)Файл исключён настройками или является новым относительно периода защиты.
«Недостаточно данных» (`insufficient`)Один или несколько источников завершились ошибкой. Такой статус блокирует удаление, чтобы ошибка базы данных или плагина не привела к потере файла.
«Не используется» (`unused`)Файл найден в выбранном каталоге, не найден источниками использования, не исключён и старше периода защиты. Только такие файлы могут быть предложены для ручного удаления.
Приоритет проверки статусов:
used > registered > protected > insufficient > unused Вкладка «Настройки» Папки для сканированияВыберите один или несколько каталогов внутри
`uploads`. Доступны физически существующие каталоги, включая корень
`uploads`.
Если каталог не выбран, сканирование не запускается.
Сканер:
- не выходит за пределы
`uploads`;
- пропускает символические ссылки;
- обрабатывает только обычные файлы;
- не включает исключённые пути.
Защита новых файловНастройка
«Не удалять файлы младше» задаётся в днях. Доступные значения:
0, 1, 3, 7, 14, 30, 60, 90Значение по умолчанию: `30` дней.Возраст рассчитывается по времени изменения файла. При значении
`0` защита по возрасту отключается, но остальные проверки безопасности остаются.
Исключённые директорииУкажите относительные пути внутри
`uploads`, по одному на строку. Например:
Код:
avatars/custom
images/cache
Все файлы внутри указанных каталогов получают защиту и не могут быть удалены.
Исключённые маскиДоступны стандартные маски:
Код:
*.ico
*.svg
*.tmp
*.bak
*.log
Можно добавить собственные маски, например:
Код:
Маски сравниваются с именем файла, без учёта регистра.
Постоянные исключенияНезависимо от настроек администратора всегда защищаются:
Код:
avatars/noavatar.gif
avatars/noavatar.png
Источники использованияПлагин анализирует следующие источники.
НовостиИз всех строковых полей таблицы
Код:
извлекаются пути из HTML и URL-атрибутов:
Код:
- `src`;
- `href`;
- `data-src`;
- `data-original`;
- `poster`;
- абсолютные и относительные URL, ведущие в `uploads`.
Зарегистрированные файлыПроверяются таблицы:
Код:
<prefix>_files
<prefix>_images
Учитываются каталоги
`files`,
`images` и
`dsn` в зависимости от значения
Код:
.
Изображения категорий NGCMS
Связи категорий с таблицей
`*_images` проверяются через
`image_id`.
Аватары пользователейЧитается поле
`avatar` таблицы:
Код:
Поддерживаются оба формата:
Код:
user.jpg
uploads/avatars/user.jpg
В первом случае путь нормализуется как
`avatars/user.jpg`, во втором используется указанный путь после удаления префикса
`uploads/`.
XFieldsПроверяются записи с:
Код:
Отдельно учитываются связанные изображения и файлы xfields с положительными
`linked_ds` и
`linked_id`.
Аттачи новостейПроверяются изображения и файлы с:
Код:
linked_ds = 1
linked_id > 0
GalleryИсточник запускается только если плагин
`gallery` активен и нужные таблицы существуют. Изображения активных галерей сопоставляются по:
i.folder = g.name EShopИсточники запускаются только если плагин
`eshop` активен.
Изображения товаров читаются из:
Код:
Физические пути:
Код:
uploads/eshop/products/<product_id>/<filepath>
uploads/eshop/products/<product_id>/thumb/<filepath>
Изображения категорий читаются из:
Код:
<prefix>_eshop_categories.image
Физические пути:
Код:
uploads/eshop/categories/<image>
uploads/eshop/categories/thumb/<image>
Временные каталоги импорта
`temp` не считаются постоянным использованием.
Если
`gallery` или
`eshop` выключен, плагин очистки не выполняет для него даже запрос проверки таблиц. Если активный плагин установлен неполностью и ожидаемой таблицы нет, источник пропускается.
СканированиеПолное сканирование из админкиКнопка
«Сканировать» выполняет полный проход выбранных каталогов и сохраняет результат в:
engine/plugins/filecleaner/data/scan.jsonВ
`scan.json` сохраняются:
- время сканирования;
- путь;
- размер;
- время изменения;
- статус;
- признак регистрации источником;
- список ошибок источников.
Cursor-сканирование и cronCron обрабатывает файлы порциями по 500 файлов через:
Код:
filecleaner_scan_step(500)
Промежуточное состояние хранится в:
engine/plugins/filecleaner/data/scan-progress.jsonПосле завершения формируется обычный
`scan.json`.
Cron выполняет только сканирование. Автоматического удаления файлов нет.
Настройки cronДоступны:
- включение и выключение;
- час от `0` до `23`;
- минута `0`, `15`, `30` или `45`.
Значения по умолчанию:
Код:
Включено: да
Час: 3
Минута: 15
Задача регистрируется как:
Код:
plugin: filecleaner
handler: scan
Журналы Журнал удаленияФайл:
engine/plugins/filecleaner/data/deletions.logВ журнал записываются дата, путь и размер удалённого файла.
На вкладке журнала:
- выводится 30 записей на страницу;
- есть отдельная пагинация;
- записи старше 30 дней автоматически удаляются при чтении журнала;
- доступна ручная очистка журнала удаления.
Журнал операцийФайл:
engine/plugins/filecleaner/data/operations.logЗаписываются операции сканирования, прогресс cron, удаление файлов и обработка отсутствующих файлов.
На отдельной вкладке:
- выводится 30 записей на страницу;
- есть отдельная пагинация;
- записи старше 30 дней автоматически удаляются при чтении журнала;
- доступна отдельная ручная очистка журнала операций.
Формат операцийКаждая строка журнала операций является JSON-объектом:
Код:
{
"date": "2026-09-17T03:15:00+03:00",
"action": "scan_step",
"status": "completed",
"details": {
"files": 500,
"source_errors": []
}
} Файлы данных плагинаКод:
engine/plugins/filecleaner/data/scan.json
engine/plugins/filecleaner/data/scan-progress.json
engine/plugins/filecleaner/data/deletions.log
engine/plugins/filecleaner/data/operations.log
Не редактируйте эти файлы во время активного сканирования или удаления. Для очистки журналов используйте кнопки в админке.
Диагностика Файл отображается как «Не используется», но удалить его нельзяПроверьте:
1. не устарел ли
`scan.json`;
2. существует ли файл физически в
`uploads`;
3. не изменился ли путь после последнего сканирования;
4. не активировался ли после сканирования источник использования;
5. не изменились ли настройки защиты и исключений.
Запустите новое полное сканирование и повторите проверку.
Сообщение «Файл не прошёл повторную проверку безопасности»Это означает, что не выполнено одно из условий:
- файл отсутствует в последнем результате сканирования;
- статус не равен
`unused`;
- путь нельзя безопасно сопоставить с
Код:
;
- запись или файл уже были изменены.
Это защитное поведение, а не разрешение на принудительное удаление.
Сообщение «Файл зарегистрирован или используется»После сканирования файл был найден одним из источников. Удалять его вручную через обход проверки не следует.
Статус «Недостаточно данных»Один из SQL-источников или источников контента завершился ошибкой. Сначала проверьте журнал операций и наличие таблиц/плагинов, затем повторите сканирование.
Ошибки 404 для предпросмотраСтарые версии результата могли содержать уже удалённые файлы. Текущая админка не выводит записи, для которых физический файл отсутствует. Выполните новое сканирование для обновления списка.
Источник gallery или eshop выдаёт ошибку отсутствующей таблицыУбедитесь, что соответствующий плагин активен и установлен. Неактивные
gallery или
eshop не должны проверяться. Для активного, но неполного плагина источник пропускается, если таблица отсутствует.
Ограничения- Плагин не восстанавливает удалённые файлы.
- Плагин не удаляет автоматически файлы cron-задачей.
- Ссылки, сформированные динамически или сохранённые нестандартным способом, могут потребовать дополнительного source callback.
- Если сторонний плагин хранит файлы вне известных таблиц и не выводит их в контенте, он может потребовать отдельного источника.
- Статус
`registered` означает регистрацию в базе, а не доказанное отображение на странице.
- Проверка выполняется только в выбранных каталогах.
- Права ОС могут запретить удаление даже после успешной логической проверки.
Расширение источникамиИсточники регистрируются в
`filecleaner.php` через:
Код:
filecleaner_register_source('source_name', static function (): array {
return [
[
'path' => 'images/example/file.jpg',
'status' => 'used',
'source' => 'custom_plugin',
],
];
});Допустимы также старые источники, возвращающие массив строковых путей:
Код:
return ['images/example/file.jpg'];
Для записей используются статусы:
Код:
Источник должен возвращать нормализуемые пути относительно
`uploads`. При ошибке источник должен выбрасывать исключение: плагин пометит потенциально удаляемые файлы как
`insufficient` и запретит опасное удаление.
Безопасная рабочая процедура1. Сделайте резервную копию базы данных и
`uploads`.
2. Выберите только нужные каталоги.
3. Настройте защиту новых файлов минимум на 30 дней.
4. Добавьте каталоги кеша и системные файлы в исключения.
5. Выполните сканирование.
6. Проверьте список со статусом
«Не используется».
7. Откройте несколько файлов и проверьте, что они действительно не нужны.
8. Удаляйте небольшими партиями.
9. После удаления выполните новое сканирование.
10. Проверьте журнал удаления и журнал операций.
Никогда не удаляйте файлы со статусами
«Используется», «Файл зарегистрирован», «Защищён» или
«Недостаточно данных» вручную через файловую систему без понимания их назначения.
Скачать:filecleaner.zip