Passed
Push — develop ( 31ebc6...f6a1e0 )
by Nikolay
04:27
created

WorkerPrepareAdvices   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 15
eloc 51
c 1
b 0
f 0
dl 0
loc 121
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A afterChangePBXSettings() 0 11 4
A getCacheKey() 0 3 1
B processAdvice() 0 38 7
A start() 0 9 2
A afterChangeSSHConf() 0 6 1
1
<?php
2
/*
3
 * MikoPBX - free phone system for small business
4
 * Copyright © 2017-2023 Alexey Portnov and Nikolay Beketov
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with this program.
17
 * If not, see <https://www.gnu.org/licenses/>.
18
 */
19
20
namespace MikoPBX\Core\Workers;
21
22
use MikoPBX\Common\Models\PbxSettings;
23
use MikoPBX\Common\Providers\ManagedCacheProvider;
24
use MikoPBX\Core\System\Notifications;
25
use MikoPBX\Core\System\Processes;
26
use MikoPBX\Core\System\Util;
27
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckConnection;
28
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckCorruptedFiles;
29
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckFirewalls;
30
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckPasswords;
31
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckSSHConfig;
32
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckStorage;
33
use MikoPBX\Core\Workers\Libs\WorkerPrepareAdvices\CheckUpdates;
34
use Phalcon\Di;
35
use Recoil\React\ReactKernel;
36
use Throwable;
37
use Generator;
38
39
require_once 'Globals.php';
40
41
42
/**
43
 * WorkerPrepareAdvices is a worker class responsible for prepare system advices.
44
 *
45
 * @package MikoPBX\Core\Workers
46
 */
47
class WorkerPrepareAdvices extends WorkerBase
48
{
49
    public const ARR_ADVICE_TYPES = [
50
        ['type' => CheckConnection::class, 'cacheTime' => 120],
51
        ['type' => CheckCorruptedFiles::class, 'cacheTime' => 3600],
52
        ['type' => CheckPasswords::class, 'cacheTime' => 86400, 'dependent'=>PbxSettings::class],
53
        ['type' => CheckFirewalls::class, 'cacheTime' => 86400, 'dependent'=>PbxSettings::class],
54
        ['type' => CheckStorage::class, 'cacheTime' => 3600],
55
        ['type' => CheckUpdates::class, 'cacheTime' => 86400],
56
        ['type' => CheckSSHConfig::class, 'cacheTime' => 3600],
57
    ];
58
59
    // Array of generated advices
60
    public array $messages;
61
62
    /**
63
     * Starts processing advice types.
64
     *
65
     * @param array $params An array of parameters for processing.
66
     *
67
     * @throws Throwable
68
     */
69
    public function start(array $params): void
70
    {
71
        $adviceTypes = self::ARR_ADVICE_TYPES;
72
        // Use ReactKernel to start parallel execution
73
        ReactKernel::start(
74
            function () use ($adviceTypes) {
75
                // Parallel execution https://github.com/recoilphp/recoil
76
                foreach ($adviceTypes as $adviceType) {
77
                    yield $this->processAdvice($adviceType);
78
                }
79
            }
80
        );
81
    }
82
83
    /**
84
     * Processes advice of a specific type and caches the result.
85
     *
86
     * @param array $adviceType An array containing advice type and cache time.
87
     *
88
     * @return Generator|null A Generator object used for parallel execution.
89
     */
90
    private function processAdvice(array $adviceType): ?Generator
91
    {
92
        $start = microtime(true);
93
        $managedCache = $this->getDI()->getShared(ManagedCacheProvider::SERVICE_NAME);
94
        $currentAdviceClass = $adviceType['type'];
95
        $cacheKey = self::getCacheKey($currentAdviceClass);
96
        if (!$managedCache->has($cacheKey)) {
97
            // No cache - generate advice and store in cache
98
            try {
99
                $checkObj = new $currentAdviceClass();
100
                $newAdvice = $checkObj->process();
101
102
                foreach ($newAdvice as $messages){
103
                    if (array_key_exists('error', $messages)){
104
105
                    }
106
                }
107
                if(!empty($messages)){
108
                    Notifications::sendAdminNotification('adv_SSHPasswordWasCompromisedSubject', $messages);
109
                }
110
111
                $managedCache->set($cacheKey, $newAdvice, $adviceType['cacheTime']);
112
            } catch (\Throwable $e) {
113
                global $errorLogger;
114
                $errorLogger->captureException($e);
115
                Util::sysLogMsg(__METHOD__, $e->getMessage(), LOG_ERR);
116
            }
117
            $timeElapsedSecs = round(microtime(true) - $start, 2);
118
            if ($timeElapsedSecs > 5) {
119
                Util::sysLogMsg(
120
                    __METHOD__,
121
                    "WARNING: Service WorkerPrepareAdvices:{$adviceType['type']} processed more than {$timeElapsedSecs} seconds",
122
                    LOG_WARNING
123
                );
124
            }
125
        }
126
        // Yield to allow parallel execution to continue
127
        yield;
128
    }
129
130
    /**
131
     * Prepares redis cache key for advice type
132
     * @param string $currentAdviceType current advice type
133
     * @return string cache key
134
     */
135
    public static function getCacheKey(string $currentAdviceType): string
136
    {
137
        return 'WorkerPrepareAdvices:' . $currentAdviceType;
138
    }
139
140
    /**
141
     * Cleanup cache for all advice types after change dependent models and PBX settings
142
     * on the WorkerModelsEvents worker.
143
     * @return void
144
     */
145
    public static function afterChangePBXSettings(): void
146
    {
147
        $di = Di::getDefault();
148
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
149
        foreach (self::ARR_ADVICE_TYPES as $adviceType) {
150
            if (array_key_exists('dependent', $adviceType) and $adviceType['dependent'] === PbxSettings::class) {
151
                $cacheKey = self::getCacheKey($adviceType['type']);
152
                $managedCache->delete($cacheKey);
153
            }
154
        }
155
        Processes::processPHPWorker(WorkerPrepareAdvices::class);
156
    }
157
158
    /**
159
     * Cleanup cache for all advice types after change SSH external configuration
160
     * @return void
161
     */
162
    public static function afterChangeSSHConf(): void
163
    {
164
        $di = Di::getDefault();
165
        $managedCache = $di->getShared(ManagedCacheProvider::SERVICE_NAME);
166
        $cacheKey = self::getCacheKey(CheckSSHConfig::class);
167
        $managedCache->delete($cacheKey);
168
    }
169
}
170
171
// Start worker process
172
WorkerPrepareAdvices::startWorker($argv ?? []);