Passed
Push — develop ( 68db6a...9f2874 )
by Nikolay
05:55
created

WorkerApiCommands   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 422
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 75
eloc 273
c 8
b 0
f 0
dl 0
loc 422
rs 2.4

9 Methods

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