Nextcloud Server version (from 31 and above):
Linux, Freebsd
That is not a real support request, but it seems some ppl need to remove some extra files from nextcloud folder after update. Somewhere here were another example, but this version is
- fresh
- vibecoded with some free AI
- can remove all EXTRA files from occ integrity:check-core and occ integrity:check-app --all
- can restore MISSING files from occ integrity:check-core and occ integrity:check-app --all (if you have a “backup” or “updated extra folder”
- it was tested and works (i can confirm)
- run with “php /your/way/to/a/script.php”
- try to use it at your own risk as any script from web
- regards
- with hope to save some time for goods
#!/usr/bin/env php
<?php
declare(strict_types=1);
/* ================= CONFIG ================= */
$php_user = 'www';
$occ_path = '/usr/local/www/nextcloud/occ';
$core_path = '/usr/local/www/nextcloud';
$apps_path = $core_path . '/apps';
$backup = '/mnt/share/nextcloud';
$dryRun = in_array('--dry-run', $argv, true);
$restored = [];
$deleted = [];
/* ================= OCC ================= */
function run_occ_json(string $cmd): string {
global $occ_path, $php_user;
$command = "php $occ_path $cmd --output=json";
$shell = sprintf(
"su -m %s -c %s 2>&1",
escapeshellarg($php_user),
escapeshellarg($command)
);
return trim((string)shell_exec($shell));
}
/* ================= PARSER ================= */
function parse_app_json(string $output): array {
$apps = [];
$lines = explode("\n", $output);
for ($i = 0; $i < count($lines); $i++) {
if (preg_match('/^([a-zA-Z0-9_\-]+): \d+ errors found:/', $lines[$i], $m)) {
$app = $m[1];
$json = $lines[$i + 1] ?? '';
$data = json_decode($json, true);
if (is_array($data)) {
$apps[$app] = $data;
}
$i++;
}
}
return $apps;
}
/* ================= SAFE DELETE ================= */
function safe_delete_app(string $app, string $file): ?string {
global $apps_path, $dryRun;
$base = realpath("$apps_path/$app");
$path = realpath("$apps_path/$app/$file");
if (!$base || !$path) return null;
if (!str_starts_with($path, $base)) return null;
if (!is_file($path)) return null;
if ($dryRun) return $path;
return unlink($path) ? $path : null;
}
/* ================= SAFE RESTORE ================= */
function safe_restore_core(string $file): ?string {
global $backup, $core_path, $dryRun;
$src = preg_replace('#/+#', '/', "$backup/$file");
$dst = preg_replace('#/+#', '/', "$core_path/$file");
if (!file_exists($src)) return null;
if (!is_dir(dirname($dst))) {
mkdir(dirname($dst), 0755, true);
}
if ($dryRun) return $dst;
return copy($src, $dst) ? $dst : null;
}
function safe_restore_app(string $app, string $file): ?string {
global $backup, $apps_path, $dryRun;
$src = preg_replace('#/+#', '/', "$backup/apps/$app/$file");
$dst = preg_replace('#/+#', '/', "$apps_path/$app/$file");
if (!file_exists($src)) return null;
if (!is_dir(dirname($dst))) {
mkdir(dirname($dst), 0755, true);
}
if ($dryRun) return $dst;
return copy($src, $dst) ? $dst : null;
}
/* ================= CORE LOOP ================= */
function process_core(): void {
global $deleted, $restored;
echo "\n[CORE]\n";
for ($i = 1; $i <= 10; $i++) {
echo "\n--- Core iteration $i ---\n";
$out = run_occ_json('integrity:check-core');
if ($out === '') {
echo "No output from OCC\n";
return;
}
$data = json_decode($out, true);
if (!$data || empty($data)) {
echo "Core clean\n";
return;
}
$changes = 0;
if (!empty($data['EXTRA_FILE'])) {
$base = realpath($GLOBALS['core_path']);
if ($base) {
$base .= DIRECTORY_SEPARATOR;
foreach (array_keys($data['EXTRA_FILE']) as $file) {
$path = realpath($GLOBALS['core_path'] . '/' . $file);
if ($path &&
str_starts_with($path, $base) &&
is_file($path)
) {
if ($GLOBALS['dryRun'] || unlink($path)) {
echo "DELETE $path\n";
$deleted[] = $path;
$changes++;
}
}
}
}
}
if (!empty($data['FILE_MISSING'])) {
foreach (array_keys($data['FILE_MISSING']) as $file) {
$r = safe_restore_core($file);
if ($r) {
echo "RESTORE $r\n";
$restored[] = $r;
$changes++;
}
}
}
if ($changes === 0) {
echo "No progress. Stopping.\n";
return;
}
}
echo "Max core iterations reached.\n";
}
/* ================= APPS LOOP ================= */
function process_apps(): void {
global $deleted, $restored;
echo "\n[APPS]\n";
for ($i = 1; $i <= 20; $i++) {
echo "\n--- Apps iteration $i ---\n";
$out = run_occ_json('integrity:check-app --all');
if ($out === '') {
echo "No output from OCC\n";
return;
}
$apps = parse_app_json($out);
$changes = 0;
foreach ($apps as $app => $data) {
if (!empty($data['EXTRA_FILE'])) {
foreach (array_keys($data['EXTRA_FILE']) as $file) {
$d = safe_delete_app($app, $file);
if ($d) {
echo "DELETE $d\n";
$deleted[] = $d;
$changes++;
}
}
}
if (!empty($data['FILE_MISSING'])) {
foreach (array_keys($data['FILE_MISSING']) as $file) {
$r = safe_restore_app($app, $file);
if ($r) {
echo "RESTORE $r\n";
$restored[] = $r;
$changes++;
}
}
}
}
if ($changes === 0) {
echo "Apps clean or no progress. Stopping.\n";
return;
}
}
echo "Max app iterations reached.\n";
}
/* ================= RUN ================= */
echo "=== Nextcloud Integrity Auto Fix (FINAL) ===\n";
echo $dryRun ? "Mode: DRY RUN\n" : "Mode: LIVE\n";
process_core();
process_apps();
echo "\n=== REPORT ===\n";
echo "Deleted: " . count($deleted) . "\n";
echo "Restored: " . count($restored) . "\n";
foreach ($deleted as $f) echo " - DEL $f\n";
foreach ($restored as $f) echo " - RES $f\n";
exit(0);