Passed
Push — develop ( ec71a0...627254 )
by Nikolay
12:54 queued 13s
created

WorkerApiCommands   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 372
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 69
eloc 233
c 4
b 0
f 0
dl 0
loc 372
rs 2.88

8 Methods

Rating   Name   Duplication   Size   Complexity  
C modulesCallBack() 0 89 14
B prepareAnswer() 0 32 8
A cdrCallBack() 0 16 3
A start() 0 22 5
A iaxCallBack() 0 14 2
A sipCallBack() 0 20 4
D systemCallBack() 0 81 24
B storageCallBack() 0 31 9

How to fix   Complexity   

Complex Class

Complex classes like WorkerApiCommands often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WorkerApiCommands, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Copyright © MIKO LLC - All Rights Reserved
4
 * Unauthorized copying of this file, via any medium is strictly prohibited
5
 * Proprietary and confidential
6
 * Written by Alexey Portnov, 2 2020
7
 */
8
9
namespace MikoPBX\PBXCoreREST\Workers;
10
11
use MikoPBX\Core\Asterisk\CdrDb;
12
use MikoPBX\Core\Asterisk\Configs\{IAXConf, SIPConf, VoiceMailConf};
13
use MikoPBX\Core\System\{BeanstalkClient, TimeManagement, Firewall, Notifications, Storage, System, Util};
14
use MikoPBX\Core\Workers\Cron\WorkerSafeScriptsCore;
15
use MikoPBX\Core\Workers\WorkerBase;
16
use MikoPBX\Core\Workers\WorkerMergeUploadedFile;
17
use MikoPBX\Modules\Setup\PbxExtensionSetupFailure;
18
use MikoPBX\Modules\PbxExtensionState;
19
use Phalcon\Exception;
20
21
use function MikoPBX\Common\Config\appPath;
22
23
require_once 'Globals.php';
24
25
class WorkerApiCommands extends WorkerBase
26
{
27
    /**
28
     * Modules can expose additional REST methods and processors,
29
     * look at src/Modules/Config/RestAPIConfigInterface.php
30
     */
31
    private array $additionalProcessors;
32
33
    /**
34
     * @param $argv
35
     */
36
    public function start($argv): void
37
    {
38
        $client = new BeanstalkClient();
39
        $client->subscribe($this->makePingTubeName(self::class), [$this, 'pingCallBack']);
40
        $client->subscribe(__CLASS__, [$this, 'prepareAnswer']);
41
42
        // Every module config class can process requests under root rights,
43
        // if it described in Config class
44
        $additionalModules = $this->di->getShared('pbxConfModules');
45
        foreach ($additionalModules as $moduleConfigObject) {
46
            if (method_exists($moduleConfigObject, 'moduleRestAPICallback')) {
47
                $this->additionalProcessors[] = [$moduleConfigObject, 'moduleRestAPICallback',];
48
            }
49
        }
50
51
        while (true) {
52
            try {
53
                $client->wait();
54
            } catch (Exception $e) {
55
                global $errorLogger;
56
                $errorLogger->captureException($e);
57
                sleep(1);
58
            }
59
        }
60
    }
61
62
63
    /**
64
     * Process request
65
     *
66
     * @param BeanstalkClient $message
67
     */
68
    public function prepareAnswer($message): void
69
    {
70
        $request   = json_decode($message->getBody(), true);
71
        $processor = $request['processor'];
72
        try {
73
            switch ($processor) {
74
                case 'cdr':
75
                    $answer = $this->cdrCallBack($request);
76
                    break;
77
                case 'sip':
78
                    $answer = $this->sipCallBack($request);
79
                    break;
80
                case 'iax':
81
                    $answer = $this->iaxCallBack($request);
82
                    break;
83
                case 'system':
84
                    $answer = $this->systemCallBack($request);
85
                    break;
86
                case 'storage':
87
                    $answer = $this->storageCallBack($request);
88
                    break;
89
                case 'modules':
90
                    $answer = $this->modulesCallBack($request);
91
                    break;
92
                default:
93
                    $answer = "Unknown processor - {$processor}";
94
            }
95
        } catch (\Exception $exception) {
96
            $answer = 'Exception on WorkerApiCommands - ' . $exception->getMessage();
97
        }
98
99
        $message->reply(json_encode($answer));
100
    }
101
102
    /**
103
     * Запросы с CDR таблице
104
     *
105
     * @param array $request
106
     */
107
    private function cdrCallBack($request): array
108
    {
109
        $action = $request['action'];
110
        switch ($action) {
111
            case 'getActiveCalls':
112
                $result['data'] = CdrDb::getActiveCalls();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$result was never initialized. Although not strictly required by PHP, it is generally a good practice to add $result = array(); before regardless.
Loading history...
113
                break;
114
            case 'getActiveChannels':
115
                $result['data'] = CdrDb::getActiveChannels();
116
                break;
117
            default:
118
                $result = ["Unknown action - {$action}"];
119
                break;
120
        }
121
122
        return $result;
123
    }
124
125
    /**
126
     * Обработка команд SIP.
127
     *
128
     * @param array $request
129
     */
130
    public function sipCallBack($request): array
131
    {
132
        $action = $request['action'];
133
        $data   = $request['data'];
134
135
        $result = [
136
            'result' => 'ERROR',
137
        ];
138
        if ('getPeersStatuses' === $action) {
139
            $result = SIPConf::getPeersStatuses();
140
        } elseif ('getSipPeer' === $action) {
141
            $result = SIPConf::getPeerStatus($data['peer']);
142
        } elseif ('getRegistry' === $action) {
143
            $result = SIPConf::getRegistry();
144
        } else {
145
            $result['data'] = 'API action not found;';
146
        }
147
        $result['function'] = $action;
148
149
        return $result;
150
    }
151
152
    /**
153
     * Обработка команду IAX.
154
     *
155
     * @param array $request
156
     */
157
    public function iaxCallBack($request): array
158
    {
159
        $action = $request['action'];
160
        $result = [
161
            'result' => 'ERROR',
162
        ];
163
        if ('getRegistry' === $action) {
164
            $result = IAXConf::getRegistry();
165
        } else {
166
            $result['data'] = 'API action not found;';
167
        }
168
        $result['function'] = $action;
169
170
        return $result;
171
    }
172
173
    /**
174
     * Обработка системных команд.
175
     *
176
     * @param array $request
177
     */
178
    public function systemCallBack($request)
179
    {
180
        $action = $request['action'];
181
        $data   = $request['data'];
182
183
        $result = [
184
            'result' => 'ERROR',
185
        ];
186
187
        if ('reboot' === $action) {
188
            $result['result'] = 'Success';
189
            System::rebootSync();
190
191
            return $result;
192
        } elseif ('merge_uploaded_file' === $action) {
193
            $result               = [
194
                'result' => 'Success',
195
            ];
196
            $phpPath              = Util::which('php');
197
            $workerDownloaderPath = Util::getFilePathByClassName(WorkerMergeUploadedFile::class);
198
            Util::mwExecBg("{$phpPath} -f {$workerDownloaderPath} '{$data['settings_file']}'");
199
        } elseif ('shutdown' === $action) {
200
            $result['result'] = 'Success';
201
            System::shutdown();
202
203
            return $result;
204
        } elseif ('setDate' === $action) {
205
            $result = TimeManagement::setDate($data['date']);
206
        } elseif ('getInfo' === $action) {
207
            $result = System::getInfo();
208
        } elseif ('sendMail' === $action) {
209
            if (isset($data['email']) && isset($data['subject']) && isset($data['body'])) {
210
                if (isset($data['encode']) && $data['encode'] === 'base64') {
211
                    $data['subject'] = base64_decode($data['subject']);
212
                    $data['body']    = base64_decode($data['body']);
213
                }
214
                $result = Notifications::sendMail($data['email'], $data['subject'], $data['body']);
215
            } else {
216
                $result['message'] = 'Not all query parameters are populated.';
217
            }
218
        } elseif ('fileReadContent' === $action) {
219
            $result = Util::fileReadContent($data['filename'], $data['needOriginal']);
220
        } elseif ('getExternalIpInfo' === $action) {
221
            $result = System::getExternalIpInfo();
222
        } elseif ('reloadMsmtp' === $action) {
223
            $notifications = new Notifications();
224
            $result        = $notifications->configure();
225
            $OtherConfigs  = new VoiceMailConf();
226
            $OtherConfigs->generateConfig();
227
            $asteriskPath = Util::which('asterisk');
228
            Util::mwExec("{$asteriskPath} -rx 'voicemail reload'");
229
        } elseif ('unBanIp' === $action) {
230
            $result = Firewall::fail2banUnbanAll($data['ip']);
231
        } elseif ('getBanIp' === $action) {
232
            $result['result'] = 'Success';
233
            $result['data']   = Firewall::getBanIp();
234
        } elseif ('startLog' === $action) {
235
            $result['result'] = 'Success';
236
            Util::startLog();
237
        } elseif ('stopLog' === $action) {
238
            $result['result']   = 'Success';
239
            $result['filename'] = Util::stopLog();
240
        } elseif ('statusUpgrade' === $action) {
241
            $result = System::statusUpgrade();
242
        } elseif ('upgradeOnline' === $action) {
243
            $result = System::upgradeOnline($request['data']);
244
        } elseif ('upgrade' === $action) {
245
            $result = System::upgradeFromImg();
246
        } elseif ('removeAudioFile' === $action) {
247
            $result = Util::removeAudioFile($data['filename']);
248
        } elseif ('convertAudioFile' === $action) {
249
            $mvPath = Util::which('mv');
250
            Util::mwExec("{$mvPath} {$data['uploadedBlob']} {$data['filename']}");
251
            $result = Util::convertAudioFile($data['filename']);
252
        } else {
253
            $result['message'] = 'API action not found;';
254
        }
255
256
        $result['function'] = $action;
257
258
        return $result;
259
    }
260
261
    /**
262
     * Обработка команд управления дисками.
263
     *
264
     * @param array $request
265
     *
266
     * @return array
267
     */
268
    public function storageCallBack($request): array
269
    {
270
        $action = $request['action'];
271
        $data   = $request['data'];
272
273
        $result = [
274
            'result' => 'ERROR',
275
            'data'   => null,
276
        ];
277
278
        if ('list' === $action) {
279
            $st               = new Storage();
280
            $result['result'] = 'Success';
281
            $result['data']   = $st->getAllHdd();
282
        } elseif ('mount' === $action) {
283
            $res              = Storage::mountDisk($data['dev'], $data['format'], $data['dir']);
284
            $result['result'] = ($res === true) ? 'Success' : 'ERROR';
285
        } elseif ('umount' === $action) {
286
            $res              = Storage::umountDisk($data['dir']);
287
            $result['result'] = ($res === true) ? 'Success' : 'ERROR';
288
        } elseif ('mkfs' === $action) {
289
            $res              = Storage::mkfs_disk($data['dev']);
290
            $result['result'] = ($res === true) ? 'Success' : 'ERROR';
291
            $result['data']   = 'inprogress';
292
        } elseif ('statusMkfs' === $action) {
293
            $result['result'] = 'Success';
294
            $result['data']   = Storage::statusMkfs($data['dev']);
295
        }
296
        $result['function'] = $action;
297
298
        return $result;
299
    }
300
301
    /**
302
     * Обработка команд управления модулями.
303
     *
304
     * @param array $request
305
     *
306
     * @return array
307
     */
308
    public function modulesCallBack($request): array
309
    {
310
        clearstatcache();
311
312
        $action = $request['action'];
313
        $module = $request['module'];
314
315
        $result = [
316
            'result' => 'ERROR',
317
            'data'   => null,
318
        ];
319
320
        switch ($action) {
321
            case 'upload':
322
                System::moduleStartDownload(
323
                    $module,
324
                    $request['data']['url'],
325
                    $request['data']['md5']
326
                );
327
                $result['uniqid']   = $module;
328
                $result['function'] = $action;
329
                $result['result']   = 'Success';
330
331
                return $result;
332
            case 'status':
333
                $result             = System::moduleDownloadStatus($module);
334
                $result['function'] = $action;
335
                $result['result']   = 'Success';
336
337
                return $result;
338
            case 'enable':
339
                $moduleStateProcessor = new PbxExtensionState($module);
340
                if ($moduleStateProcessor->enableModule() === false) {
341
                    $result['messages'] = $moduleStateProcessor->getMessages();
342
                } else {
343
                    unset($result);
344
                    $result['result'] = 'Success';
345
                }
346
347
                return $result;
348
            case 'disable':
349
                $moduleStateProcessor = new PbxExtensionState($module);
350
                if ($moduleStateProcessor->disableModule() === false) {
351
                    $result['messages'] = $moduleStateProcessor->getMessages();
352
                } else {
353
                    unset($result);
354
                    $result['result'] = 'Success';
355
                }
356
357
                return $result;
358
            case 'uninstall':
359
                $moduleClass = "\\Modules\\{$module}\\Setup\\PbxExtensionSetup";
360
                if (class_exists($moduleClass)
361
                    && method_exists($moduleClass, 'uninstallModule')) {
362
                    $setup = new $moduleClass($module);
363
                } else {
364
                    // Заглушка которая позволяет удалить модуль из базы данных, которого нет на диске
365
                    $moduleClass = PbxExtensionSetupFailure::class;
366
                    $setup       = new $moduleClass($module);
367
                }
368
                $prams = json_decode($request['input'], true);
369
                if (array_key_exists('keepSettings', $prams)) {
370
                    $keepSettings = $prams['keepSettings'] === 'true';
371
                } else {
372
                    $keepSettings = false;
373
                }
374
                if ($setup->uninstallModule($keepSettings)) {
375
                    $result['result'] = 'Success';
376
                } else {
377
                    $result['result'] = 'Error';
378
                    $result['data']   = implode('<br>', $setup->getMessages());
379
                }
380
                WorkerSafeScriptsCore::restartAllWorkers();
381
382
                return $result;
383
            default:
384
        }
385
386
        // Try process request over additional modules
387
        foreach ($this->additionalProcessors as [$moduleConfigObject, $callBack]) {
388
            if (stripos($module, $moduleConfigObject->moduleUniqueId) === 0) {
389
                $result = $moduleConfigObject->$callBack($request);
390
                break;
391
            }
392
        }
393
394
        $result['function'] = $action;
395
396
        return $result;
397
    }
398
399
}
400
401
// Start worker process
402
$workerClassname = WorkerApiCommands::class;
403
if (isset($argv) && count($argv) > 1 && $argv[1] === 'start') {
404
    cli_set_process_title($workerClassname);
405
    while (true) {
406
        try {
407
            $worker = new $workerClassname();
408
            $worker->start($argv);
409
        } catch (Exception $e) {
410
            global $errorLogger;
411
            $errorLogger->captureException($e);
412
            Util::sysLogMsg("{$workerClassname}_EXCEPTION", $e->getMessage());
413
        }
414
    }
415
}
416