TaskWorker   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 122
c 1
b 0
f 0
dl 0
loc 226
rs 10
wmc 29

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B execute() 0 32 9
B processSubTasks() 0 76 11
A handleTaskFailure() 0 13 1
B completeTask() 0 48 7
1
<?php
2
/**
3
 * Teampass - a collaborative passwords manager.
4
 * ---
5
 * This file is part of the TeamPass project.
6
 * 
7
 * TeamPass is free software: you can redistribute it and/or modify it
8
 * under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, version 3 of the License.
10
 * 
11
 * TeamPass 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
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 * 
19
 * Certain components of this file may be under different licenses. For
20
 * details, see the `licenses` directory or individual file headers.
21
 * ---
22
 * @file      background_tasks___worker.php
23
 * @author    Nils Laumaillé ([email protected])
24
 * @copyright 2009-2025 Teampass.net
25
 * @license   GPL-3.0
26
 * @see       https://www.teampass.net
27
 */
28
29
use TeampassClasses\ConfigManager\ConfigManager;
30
require_once __DIR__.'/../sources/main.functions.php';
31
require_once __DIR__.'/background_tasks___functions.php';
32
require_once __DIR__.'/traits/ItemHandlerTrait.php';
33
require_once __DIR__.'/traits/UserHandlerTrait.php';
34
require_once __DIR__.'/traits/EmailTrait.php';
35
require_once __DIR__ . '/taskLogger.php';
36
37
class TaskWorker {
38
    use ItemHandlerTrait;
39
    use UserHandlerTrait;
40
    use EmailTrait;
41
42
    private $taskId;
43
    private $processType;
44
    private $taskData;
45
    private $settings;
46
    private $logger;
47
48
    public function __construct(int $taskId, string $processType, array $taskData) {
49
        $this->taskId = $taskId;
50
        $this->processType = $processType;
51
        $this->taskData = $taskData;
52
        
53
        $configManager = new ConfigManager();
54
        $this->settings = $configManager->getAllSettings();
55
        $this->logger = new TaskLogger($this->settings, LOG_TASKS_FILE);
56
    }
57
58
    /**
59
     * Execute the task based on its type.
60
     * This method will handle different types of tasks such as item copy, new item creation,
61
     * user cache tree building, email sending, and user key generation.
62
     * 
63
     * @return void
64
     */
65
    public function execute() {
66
        try {
67
            if (LOG_TASKS=== true) $this->logger->log('Processing task: ' . print_r($this->taskData, true), 'DEBUG');
68
            // Dispatch selon le type de processus
69
            switch ($this->processType) {
70
                case 'item_copy':
71
                    $this->processSubTasks($this->taskData);
72
                    break;
73
                case 'new_item':
74
                    $this->processSubTasks($this->taskData);
75
                    break;
76
                case 'item_update_create_keys':
77
                    $this->processSubTasks($this->taskData);
78
                    break;
79
                case 'user_build_cache_tree':
80
                    $this->handleUserBuildCacheTree($this->taskData);
81
                    break;
82
                case 'send_email':
83
                    $this->sendEmail($this->taskData);
84
                    break;
85
                case 'create_user_keys':
86
                    $this->generateUserKeys($this->taskData);
87
                    break;
88
                default:
89
                    throw new Exception("Type of subtask unknown: {$this->processType}");
90
            }
91
92
            // Mark the task as completed
93
            $this->completeTask();
94
95
        } catch (Exception $e) {
96
            $this->handleTaskFailure($e);
97
        }
98
    }
99
    
100
    /**
101
     * Mark the task as completed in the database.
102
     * This method updates the task status to 'completed' and sets the finished_at timestamp.
103
     * 
104
     * @return void
105
     */
106
    private function completeTask() {
107
        // Prepare data for updating the task status
108
        $updateData = [
109
            'is_in_progress' => -1,
110
            'finished_at' => time(),
111
            'status' => 'completed'
112
        ];
113
114
        // Prepare anonimzation of arguments
115
        if ($this->processType === 'send_mail') {
116
            $arguments = json_encode(
117
                [
118
                    'email' => $this->taskData['receivers'],
119
                    'login' => $this->taskData['receiver_name'],
120
                ]
121
            );
122
        } elseif ($this->processType === 'create_user_keys') {
123
            $arguments = json_encode(
124
                [
125
                    'user_id' => $this->taskData['new_user_id'],
126
                ]
127
            );
128
        } elseif ($this->processType === 'item_update_create_keys') {
129
            $arguments = json_encode(
130
                [
131
                    'item_id' => $this->taskData['item_id'],
132
                    'author' => $this->taskData['author'],
133
                ]
134
            );
135
        } else {
136
            $arguments = '';
137
        }
138
139
        if (LOG_TASKS=== true) $this->logger->log('Process: '.$this->processType.' -- '.print_r($arguments, true), 'DEBUG');
140
141
        // Add 'arguments' only if not empty
142
        if (!empty($arguments)) {
143
            $updateData['arguments'] = $arguments;
144
        }
145
146
        // Store completed status in the database
147
        DB::update(
148
            prefixTable('background_tasks'),
149
            $updateData,
150
            'increment_id = %i',
151
            $this->taskId
152
        );
153
        if (LOG_TASKS=== true) $this->logger->log('Finishing task: ' . $this->taskId, 'DEBUG');
154
    }
155
156
    /**
157
     * Handle task failure by updating the task status in the database.
158
     * This method sets the task status to 'failed', updates the finished_at timestamp,
159
     * and logs the error message.
160
     * 
161
     * @param Exception $e The exception that occurred during task processing.
162
     * @return void
163
     */
164
    private function handleTaskFailure(Exception $e) {
165
        DB::update(
166
            prefixTable('background_tasks'),
167
            [
168
                'is_in_progress' => -1,
169
                'finished_at' => time(),
170
                'status' => 'failed',
171
                'error_message' => $e->getMessage()
172
            ],
173
            'increment_id = %i',
174
            $this->taskId
175
        );
176
        $this->logger->log('Task failure: ' . $e->getMessage(), 'ERROR');
177
    }
178
179
    /**
180
     * Handle subtasks for the current task.
181
     * This method retrieves all subtasks related to the current task and processes them.
182
     * If all subtasks are completed, it marks the main task as completed.
183
     * 
184
     * @param array $arguments Arguments for the subtasks.
185
     * @return void
186
     */
187
    private function processSubTasks($arguments) {
188
        if (LOG_TASKS=== true) $this->logger->log('processSubTasks: '.print_r($arguments, true), 'DEBUG');
189
        // Get all subtasks related to this task
190
        $subtasks = DB::query(
191
            'SELECT * FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0 ORDER BY `task` ASC',
192
            $this->taskId
193
        );
194
    
195
        // Check if there are any subtasks to process
196
        if (empty($subtasks)) {
197
            if (LOG_TASKS=== true) $this->logger->log('No subtask was found for task: ' . $this->taskId, 'DEBUG');
198
            return;
199
        }
200
    
201
        // Process each subtask
202
        foreach ($subtasks as $subtask) {
203
            try {
204
                // Get the subtask data
205
                $subtaskData = json_decode($subtask['task'], true);
206
207
                if (LOG_TASKS=== true) $this->logger->log('Processing subtask: ' . $subtaskData['step'], 'DEBUG');
208
209
                // Process the subtask based on its type
210
                switch ($subtaskData['step'] ?? '') {
211
                    case 'create_users_pwd_key':
212
                        $this->generateUserPasswordKeys($arguments);
213
                        break;
214
                    case 'create_users_fields_key':
215
                        $this->generateUserFieldKeys($subtaskData);
216
                        break;
217
                    case 'create_users_files_key':
218
                        $this->generateUserFileKeys($subtaskData);
219
                        break;
220
                    default:
221
                        throw new Exception("Type de sous-tâche inconnu (".$subtaskData['step'].")");
222
                }                
223
        
224
                // Mark subtask as completed
225
                DB::update(
226
                    prefixTable('background_subtasks'),
227
                    [
228
                        'is_in_progress' => -1,
229
                        'finished_at' => time(),
230
                        'status' => 'completed',
231
                    ],
232
                    'increment_id = %i',
233
                    $subtask['increment_id']
234
                );
235
        
236
            } catch (Exception $e) {
237
                // Mark subtask as failed
238
                DB::update(
239
                    prefixTable('background_subtasks'),
240
                    [
241
                        'is_in_progress' => -1,
242
                        'finished_at' => time(),
243
                        'updated_at' => time(),
244
                        'status' => 'failed',
245
                        'error_message' => $e->getMessage(),
246
                    ],
247
                    'increment_id = %i',
248
                    $subtask['increment_id']
249
                );
250
        
251
                $this->logger->log('processSubTasks : ' . $e->getMessage(), 'ERROR');
252
            }
253
        }
254
    
255
        // Are all subtasks completed?
256
        $remainingSubtasks = DB::queryFirstField(
257
            'SELECT COUNT(*) FROM ' . prefixTable('background_subtasks') . ' WHERE task_id = %i AND is_in_progress = 0',
258
            $this->taskId
259
        );
260
    
261
        if ($remainingSubtasks == 0) {
262
            $this->completeTask();
263
        }
264
    }
265
}
266
267
// Prepare the environment
268
// Get the task ID and process type from command line arguments
269
if ($argc < 3) {
270
    error_log("Usage: php background_tasks___worker.php <task_id> <process_type> [<task_data>]");
271
    exit(1);
272
}
273
$taskId = (int)$argv[1];
274
$processType = $argv[2];
275
$taskData = $argv[3] ?? null;
276
if ($taskData) {
277
    $taskData = json_decode($taskData, true);
278
} else {
279
    $taskData = [];
280
}
281
282
// Initialize the worker
283
$worker = new TaskWorker($taskId, $processType, $taskData);
284
$worker->execute();