1: | <?php |
2: | |
3: | namespace Aurora\System\Console\Commands; |
4: | |
5: | use Aurora\System\Console\Commands\BaseCommand; |
6: | use Symfony\Component\Console\Command\Command; |
7: | use Symfony\Component\Console\Input\InputInterface; |
8: | use Symfony\Component\Console\Input\InputOption; |
9: | use Symfony\Component\Console\Logger\ConsoleLogger; |
10: | use Symfony\Component\Console\Output\OutputInterface; |
11: | use Symfony\Component\Console\Question\ConfirmationQuestion; |
12: | |
13: | class GetOrphansCommand extends BaseCommand |
14: | { |
15: | |
16: | |
17: | |
18: | private $logger = false; |
19: | |
20: | |
21: | |
22: | |
23: | public function __construct() |
24: | { |
25: | parent::__construct(); |
26: | } |
27: | |
28: | protected function configure(): void |
29: | { |
30: | $this->setName('get-orphans') |
31: | ->setDescription('Collect orphan entries') |
32: | ->addOption('remove', 'r', InputOption::VALUE_NONE, 'Remove orphan entries from DB.') |
33: | ; |
34: | } |
35: | |
36: | protected function rewriteFile($fd, $str) |
37: | { |
38: | ftruncate($fd, 0); |
39: | fseek($fd, 0, SEEK_END); |
40: | fwrite($fd, $str); |
41: | } |
42: | |
43: | protected function jsonPretify($sJsonStr) |
44: | { |
45: | $sOutput = '{'; |
46: | $bFirstElement = true; |
47: | foreach ($sJsonStr as $key => $value) { |
48: | if (!$bFirstElement) { |
49: | $sOutput .= ","; |
50: | } |
51: | $bFirstElement = false; |
52: | |
53: | $sOutput .= PHP_EOL . "\t\"" . $key . "\": ["; |
54: | $sOutput .= PHP_EOL . "\t\t\"" . implode('","', $value) . "\""; |
55: | $sOutput .= PHP_EOL . "\t]"; |
56: | } |
57: | $sOutput .= PHP_EOL . '}'; |
58: | $sOutput = str_replace('\\', '\\\\', $sOutput); |
59: | |
60: | return $sOutput; |
61: | } |
62: | |
63: | protected function checkOrphans($fdEntities, $input, $output) |
64: | { |
65: | $helper = $this->getHelper('question'); |
66: | $question = new ConfirmationQuestion('Remove these orphan entries? [yes]', true); |
67: | |
68: | $aOrphansEntities = []; |
69: | $aModels = $this->getAllModels(); |
70: | foreach ($aModels as $modelName => $modelPath) { |
71: | $model = str_replace('/', DIRECTORY_SEPARATOR, $modelPath); |
72: | $model = str_replace('\\', DIRECTORY_SEPARATOR, $model); |
73: | $model = explode(DIRECTORY_SEPARATOR, $model); |
74: | $modelClass = []; |
75: | |
76: | while ($model[0] !== 'modules') { |
77: | array_shift($model); |
78: | } |
79: | $model[0] = 'Modules'; |
80: | array_unshift($model, "Aurora"); |
81: | $model = implode('\\', $model); |
82: | |
83: | $this->logger->info('Checking ' . $model::query()->getQuery()->from . ' table.'); |
84: | |
85: | $modelObject = new $model(); |
86: | $checkOrphan = $modelObject->getOrphanIds(); |
87: | switch($checkOrphan['status']) { |
88: | case 0: |
89: | $this->logger->info($checkOrphan['message']); |
90: | break; |
91: | case 1: |
92: | $aOrphansEntities[$model] = array_values($checkOrphan['orphansIds']); |
93: | if ($input->getOption('remove') && !empty($aOrphansEntities[$model])) { |
94: | $this->logger->error($checkOrphan['message']); |
95: | $bRemove = $helper->ask($input, $output, $question); |
96: | |
97: | if ($bRemove) { |
98: | $modelObject::whereIn('id', $aOrphansEntities[$model])->delete(); |
99: | $this->logger->warning('Orphan entries was removed.'); |
100: | } else { |
101: | $this->logger->warning('Orphan entries removing was skipped.'); |
102: | } |
103: | } else { |
104: | $this->logger->error($checkOrphan['message']); |
105: | } |
106: | break; |
107: | default: |
108: | $this->logger->info($checkOrphan['message']); |
109: | break; |
110: | } |
111: | echo PHP_EOL; |
112: | } |
113: | $this->rewriteFile($fdEntities, $this->jsonPretify($aOrphansEntities)); |
114: | } |
115: | |
116: | protected function checkFileOrphans($fdEntities, $input, $output) |
117: | { |
118: | $helper = $this->getHelper('question'); |
119: | $question = new ConfirmationQuestion('Remove files of the orphan users? [yes]', true); |
120: | |
121: | $dirFiles = \Aurora\System\Api::DataPath() . "/files"; |
122: | $dirPersonalFiles = $dirFiles . "/private"; |
123: | $dirOrphanFiles = $dirFiles . "/orphan_user_files"; |
124: | $aOrphansEntities = []; |
125: | |
126: | if (is_dir($dirPersonalFiles)) { |
127: | $this->logger->info("Checking Personal files."); |
128: | |
129: | $dirs = array_diff(scandir($dirPersonalFiles), array('..', '.')); |
130: | |
131: | $users = new \Aurora\Modules\Core\Models\User(); |
132: | $orphanUUIDs = array_values(array_diff($dirs, $users::pluck('UUID')->toArray())); |
133: | |
134: | $aOrphansEntities['PersonalFiles'] = $orphanUUIDs; |
135: | $this->rewriteFile($fdEntities, $this->jsonPretify($aOrphansEntities)); |
136: | |
137: | if (!empty($orphanUUIDs)) { |
138: | $this->logger->error("Personal files orphans were found: " . count($orphanUUIDs)); |
139: | |
140: | if ($input->getOption('remove') && !empty($orphanUUIDs)) { |
141: | $bRemove = $helper->ask($input, $output, $question); |
142: | |
143: | if ($bRemove) { |
144: | if (!is_dir($dirOrphanFiles)) { |
145: | mkdir($dirOrphanFiles); |
146: | } |
147: | |
148: | foreach ($orphanUUIDs as $orphanUUID) { |
149: | rename($dirPersonalFiles."/".$orphanUUID, $dirOrphanFiles."/".$orphanUUID); |
150: | } |
151: | |
152: | $this->logger->warning('Orphan user files were moved to ' . $dirOrphanFiles . '.'); |
153: | } else { |
154: | $this->logger->warning('Orphan user files removing was skipped.'); |
155: | } |
156: | } |
157: | } else { |
158: | $this->logger->info("Personal files orphans were not found."); |
159: | } |
160: | } |
161: | } |
162: | |
163: | protected function execute(InputInterface $input, OutputInterface $output): int |
164: | { |
165: | $verbosityLevelMap = array( |
166: | 'notice' => OutputInterface::VERBOSITY_NORMAL, |
167: | 'info' => OutputInterface::VERBOSITY_NORMAL |
168: | ); |
169: | $dirName = \Aurora\System\Api::DataPath() . "/get-orphans-logs"; |
170: | $entitiesFileName = $dirName . "/orphans_".date('Y-m-d_H-i-s').".json"; |
171: | |
172: | $dirname = dirname($entitiesFileName); |
173: | if (!is_dir($dirname)) { |
174: | mkdir($dirname, 0755, true); |
175: | } |
176: | |
177: | $fdEntities = fopen($entitiesFileName, 'a+') or die("Can't create migration-progress.txt file"); |
178: | |
179: | $this->logger = new ConsoleLogger($output, $verbosityLevelMap); |
180: | $this->checkOrphans($fdEntities, $input, $output); |
181: | $this->checkFileOrphans($fdEntities, $input, $output); |
182: | return Command::SUCCESS; |
183: | } |
184: | } |
185: | |