TaskWorker   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 131
c 1
b 0
f 0
dl 0
loc 238
rs 9.92
wmc 31

5 Methods

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