• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visavi / rotor / 27267213075

10 Jun 2026 09:35AM UTC coverage: 13.943% (+0.08%) from 13.868%
27267213075

push

github

visavi
Поправил кешировование настроек, тесты

8 of 14 new or added lines in 6 files covered. (57.14%)

2 existing lines in 1 file now uncovered.

807 of 5788 relevant lines covered (13.94%)

1.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.82
/app/Http/Controllers/InstallController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Http\Controllers;
6

7
use App\Classes\Validator;
8
use App\Models\Setting;
9
use App\Models\User;
10
use App\Services\MigrationService;
11
use Illuminate\Http\JsonResponse;
12
use Illuminate\Http\RedirectResponse;
13
use Illuminate\Http\Request;
14
use Illuminate\Support\Facades\Artisan;
15
use Illuminate\Support\Facades\Auth;
16
use Illuminate\Support\Facades\DB;
17
use Illuminate\Support\Facades\Hash;
18
use Illuminate\Support\Facades\Lang;
19
use Illuminate\Support\Facades\Schema;
20
use Illuminate\Support\Str;
21
use Illuminate\View\View;
22
use Modules\News\Models\News;
23

24
class InstallController extends Controller
25
{
26
    /**
27
     * Конструктор
28
     */
29
    public function __construct(
3✔
30
        Request $request,
31
        private readonly MigrationService $migrations
32
    ) {
33
        $lang = $request->input('lang', 'ru');
3✔
34

35
        Lang::setLocale($lang);
3✔
36

37
        view()->share('lang', $lang);
3✔
38
    }
39

40
    /**
41
     * Главная страница
42
     */
43
    public function index(): View
3✔
44
    {
45
        $keys = [
3✔
46
            'APP_ENV',
3✔
47
            'APP_DEBUG',
3✔
48
            'DB_CONNECTION',
3✔
49
            'DB_HOST',
3✔
50
            'DB_PORT',
3✔
51
            'DB_DATABASE',
3✔
52
            'DB_USERNAME',
3✔
53
            'APP_URL',
3✔
54
            'APP_EMAIL',
3✔
55
            'APP_ADMIN',
3✔
56
        ];
3✔
57

58
        $versions = [
3✔
59
            'php'   => '8.3.0',
3✔
60
            'mysql' => '5.7.8',
3✔
61
            'maria' => '10.2.7',
3✔
62
            'pgsql' => '9.2',
3✔
63
        ];
3✔
64

65
        $storage = glob(storage_path('{*,*/*,*/*/*}'), GLOB_BRACE | GLOB_ONLYDIR);
3✔
66
        $uploads = glob(public_path('uploads/*'), GLOB_ONLYDIR);
3✔
67
        $dirs = [public_path('assets/modules'), base_path('bootstrap/cache'), base_path('modules')];
3✔
68

69
        $dirs = array_merge($storage, $uploads, $dirs);
3✔
70
        $languages = getAvailableLanguages();
3✔
71

72
        $isUpdate = $this->isUpdate();
3✔
73

74
        return view('install/index', compact('keys', 'languages', 'versions', 'dirs', 'isUpdate'));
3✔
75
    }
76

77
    /**
78
     * Проверка статуса и выполнение миграций
79
     */
80
    public function status(): View
×
81
    {
82
        if (! Schema::hasTable('migrations')) {
×
83
            Artisan::call('migrate:install');
×
84
        }
85

86
        $isUpdate = $this->isUpdate();
×
87
        $pendingMigrations = $this->migrations->getPendingMigrations($this->paths());
×
88

89
        return view('install/status', compact('isUpdate', 'pendingMigrations'));
×
90
    }
91

92
    /**
93
     * Выполняет одну следующую миграцию
94
     */
95
    public function migrateNext(): JsonResponse
×
96
    {
97
        ini_set('max_execution_time', 0);
×
98
        set_time_limit(0);
×
99

100
        $pending = $this->migrations->getPendingMigrations($this->paths());
×
101

102
        if (empty($pending)) {
×
103
            if (! $this->isUpdate()) {
×
104
                Artisan::call('key:generate', ['--force' => true]);
×
105
            }
106

107
            Artisan::call('cache:clear');
×
108
            Artisan::call('route:clear');
×
109
            Artisan::call('config:clear');
×
110

111
            return response()->json(['done' => true, 'migration' => null, 'output' => '']);
×
112
        }
113

114
        $name = $pending[0];
×
115
        $file = $this->migrations->findFile($name);
×
116

117
        if (! $file) {
×
118
            return response()->json(['error' => "Файл миграции не найден: {$name}"], 500);
×
119
        }
120

121
        $remaining = count($pending) - 1;
×
122

123
        return response()->json([
×
124
            'done'      => $remaining === 0,
×
125
            'migration' => $name,
×
126
            'output'    => $this->migrations->runOne($file),
×
127
            'remaining' => $remaining,
×
128
        ]);
×
129
    }
130

131
    private function paths(): array
×
132
    {
133
        $paths = [database_path('migrations')];
×
134

135
        if ($this->isUpdate()) {
×
136
            $paths[] = database_path('upgrades');
×
137
        }
138

139
        return $paths;
×
140
    }
141

142
    /**
143
     * Заполнение БД
144
     */
145
    public function seed(): View
×
146
    {
147
        if (setting('app_installed')) {
×
148
            abort(403);
×
149
        }
150

151
        Artisan::call('db:seed', ['--force' => true]);
×
152
        $output = Artisan::output();
×
153

154
        Artisan::call('cache:clear');
×
155
        Artisan::call('route:clear');
×
156
        Artisan::call('config:clear');
×
157

158
        return view('install/seed', compact('output'));
×
159
    }
160

161
    /**
162
     * Создание администратора
163
     */
164
    public function account(Request $request, Validator $validator): View|RedirectResponse
×
165
    {
166
        if (setting('app_installed')) {
×
167
            abort(403);
×
168
        }
169

170
        $lang = $request->input('lang', 'ru');
×
171
        $login = (string) $request->input('login');
×
172
        $password = $request->input('password');
×
173
        $password2 = $request->input('password2');
×
174
        $email = strtolower((string) $request->input('email'));
×
175

176
        if ($request->isMethod('post')) {
×
177
            $validator->regex($login, '|^[a-z0-9\-]+$|i', ['login' => __('validator.login')])
×
178
                ->regex(Str::substr($login, 0, 1), '|^[a-z0-9]+$|i', ['login' => __('users.login_begin_requirements')])
×
179
                ->email($email, ['email' => __('validator.email')])
×
180
                ->length($login, 3, 20, ['login' => __('users.login_length_requirements')])
×
181
                ->length($password, 6, 20, ['password' => __('users.password_length_requirements')])
×
182
                ->equal($password, $password2, ['password2' => __('users.passwords_different')])
×
183
                ->false(ctype_digit($login), ['login' => __('users.field_characters_requirements')])
×
184
                ->false(ctype_digit($password), ['password' => __('users.field_characters_requirements')])
×
185
                ->false(substr_count($login, '-') > 2, ['login' => __('users.login_hyphens_requirements')]);
×
186

187
            if ($validator->isValid()) {
×
188
                // Проверка логина на существование
189
                $checkLogin = User::query()->where('login', $login)->exists();
×
190
                $validator->false($checkLogin, ['login' => __('users.login_already_exists')]);
×
191

192
                // Проверка email на существование
193
                $checkMail = User::query()->where('email', $email)->exists();
×
194
                $validator->false($checkMail, ['email' => __('users.email_already_exists')]);
×
195
            }
196

197
            if ($validator->isValid()) {
×
198
                $user = User::query()->create([
×
199
                    'login'      => $login,
×
200
                    'password'   => Hash::make($password),
×
201
                    'email'      => $email,
×
202
                    'level'      => User::BOSS,
×
203
                    'gender'     => User::MALE,
×
204
                    'themes'     => 'default',
×
205
                    'point'      => 500,
×
206
                    'money'      => 100000,
×
207
                    'status'     => 'Boss',
×
208
                    'language'   => $lang,
×
209
                    'created_at' => SITETIME,
×
210
                ]);
×
211

212
                // ------------- Авторизация -----------//
213
                Auth::login($user, true);
×
214

215
                // -------------- Приват ---------------//
216
                $text = __('install.text_message', ['login' => $login]);
×
217
                $user->sendMessage(null, $text);
×
218

219
                // -------------- Новость ---------------//
220
                if (class_exists(News::class)) {
×
221
                    $textnews = __('install.text_news');
×
222

223
                    News::query()->create([
×
224
                        'title'      => __('install.welcome'),
×
225
                        'text'       => $textnews,
×
226
                        'user_id'    => $user->id,
×
227
                        'created_at' => SITETIME,
×
228
                    ]);
×
229

230
                    clearCache(['statNews', 'pinnedNews', 'statNewsDate']);
×
231
                }
232

233
                return redirect('/install/finish');
×
234
            }
235

236
            setInput($request->all());
×
237
            setFlash('danger', $validator->getErrors());
×
238
        }
239

240
        return view('install/account', compact('login', 'email'));
×
241
    }
242

243
    /**
244
     * Завершение установки
245
     */
246
    public function finish(): View
×
247
    {
248
        if ($this->isUpdate()) {
×
249
            abort(403);
×
250
        }
251

252
        // Помечаем все апгрейды как выполненные — свежая схема уже содержит все изменения
253
        $batch = DB::table('migrations')->max('batch') + 1;
×
254
        foreach (glob(database_path('upgrades/*.php')) as $file) {
×
255
            DB::table('migrations')->insertOrIgnore([
×
256
                'migration' => pathinfo($file, PATHINFO_FILENAME),
×
257
                'batch'     => $batch,
×
258
            ]);
×
259
        }
260

261
        Setting::query()
×
262
            ->where('name', 'app_installed')
×
263
            ->update(['value' => 1]);
×
264

NEW
265
        clearCache('settings');
×
266

267
        return view('install/finish');
×
268
    }
269

270
    private function isUpdate(): bool
3✔
271
    {
272
        return (bool) setting('app_installed');
3✔
273
    }
274

275
    /**
276
     * Parse PHP modules
277
     */
278
    private static function parsePhpModules(): array
3✔
279
    {
280
        ob_start();
3✔
281
        phpinfo(INFO_MODULES);
3✔
282
        $s = ob_get_clean();
3✔
283
        $s = strip_tags($s, '<h2><th><td>');
3✔
284
        $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', '<info>\\1</info>', $s);
3✔
285
        $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', '<info>\\1</info>', $s);
3✔
286
        $vTmp = preg_split('/(<h2[^>]*>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
3✔
287
        $vModules = [];
3✔
288
        $iMax = count($vTmp);
3✔
289

290
        for ($i = 1; $i < $iMax; $i++) {
3✔
291
            if (preg_match('/<h2[^>]*>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
×
292
                $vName = trim($vMat[1]);
×
293
                $vTmp2 = explode("\n", $vTmp[$i + 1]);
×
294
                foreach ($vTmp2 as $vOne) {
×
295
                    $vPat = '<info>([^<]+)<\/info>';
×
296
                    $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
×
297
                    $vPat2 = "/$vPat\s*$vPat/";
×
298
                    if (preg_match($vPat3, $vOne, $vMat)) {
×
299
                        $vModules[$vName][trim($vMat[1])] = [trim($vMat[2]), trim($vMat[3])];
×
300
                    } elseif (preg_match($vPat2, $vOne, $vMat)) {
×
301
                        $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
×
302
                    }
303
                }
304
            }
305
        }
306

307
        return $vModules;
3✔
308
    }
309

310
    /**
311
     * Get PHP module setting
312
     */
313
    public static function getModuleSetting(string $pModuleName, array $pSettings): string
3✔
314
    {
315
        $vModules = self::parsePhpModules();
3✔
316

317
        foreach ($pSettings as $pSetting) {
3✔
318
            if (isset($vModules[$pModuleName][$pSetting])) {
3✔
319
                return $vModules[$pModuleName][$pSetting];
×
320
            }
321
        }
322

323
        return __('main.undefined');
3✔
324
    }
325
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc