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

visavi / rotor / 27380511181

11 Jun 2026 10:07PM UTC coverage: 14.172% (+0.3%) from 13.909%
27380511181

push

github

visavi
Календарь с тестом переехал в модуль news

2 of 2 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

816 of 5758 relevant lines covered (14.17%)

1.64 hits per line

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

30.43
/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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

138
        return $paths;
×
139
    }
140

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
218
                return redirect('/install/finish');
×
219
            }
220

221
            setInput($request->all());
×
222
            setFlash('danger', $validator->getErrors());
×
223
        }
224

225
        return view('install/account', compact('login', 'email'));
×
226
    }
227

228
    /**
229
     * Завершение установки
230
     */
231
    public function finish(): View
×
232
    {
233
        if ($this->isUpdate()) {
×
234
            abort(403);
×
235
        }
236

237
        // Помечаем все апгрейды как выполненные — свежая схема уже содержит все изменения
238
        $batch = DB::table('migrations')->max('batch') + 1;
×
239
        foreach (glob(database_path('upgrades/*.php')) as $file) {
×
240
            DB::table('migrations')->insertOrIgnore([
×
241
                'migration' => pathinfo($file, PATHINFO_FILENAME),
×
242
                'batch'     => $batch,
×
243
            ]);
×
244
        }
245

246
        Setting::query()
×
247
            ->where('name', 'app_installed')
×
248
            ->update(['value' => 1]);
×
249

250
        clearCache('settings');
×
251

252
        return view('install/finish');
×
253
    }
254

255
    private function isUpdate(): bool
3✔
256
    {
257
        return (bool) setting('app_installed');
3✔
258
    }
259

260
    /**
261
     * Parse PHP modules
262
     */
263
    private static function parsePhpModules(): array
3✔
264
    {
265
        ob_start();
3✔
266
        phpinfo(INFO_MODULES);
3✔
267
        $s = ob_get_clean();
3✔
268
        $s = strip_tags($s, '<h2><th><td>');
3✔
269
        $s = preg_replace('/<th[^>]*>([^<]+)<\/th>/', '<info>\\1</info>', $s);
3✔
270
        $s = preg_replace('/<td[^>]*>([^<]+)<\/td>/', '<info>\\1</info>', $s);
3✔
271
        $vTmp = preg_split('/(<h2[^>]*>[^<]+<\/h2>)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);
3✔
272
        $vModules = [];
3✔
273
        $iMax = count($vTmp);
3✔
274

275
        for ($i = 1; $i < $iMax; $i++) {
3✔
276
            if (preg_match('/<h2[^>]*>([^<]+)<\/h2>/', $vTmp[$i], $vMat)) {
×
277
                $vName = trim($vMat[1]);
×
278
                $vTmp2 = explode("\n", $vTmp[$i + 1]);
×
279
                foreach ($vTmp2 as $vOne) {
×
280
                    $vPat = '<info>([^<]+)<\/info>';
×
281
                    $vPat3 = "/$vPat\s*$vPat\s*$vPat/";
×
282
                    $vPat2 = "/$vPat\s*$vPat/";
×
283
                    if (preg_match($vPat3, $vOne, $vMat)) {
×
284
                        $vModules[$vName][trim($vMat[1])] = [trim($vMat[2]), trim($vMat[3])];
×
285
                    } elseif (preg_match($vPat2, $vOne, $vMat)) {
×
286
                        $vModules[$vName][trim($vMat[1])] = trim($vMat[2]);
×
287
                    }
288
                }
289
            }
290
        }
291

292
        return $vModules;
3✔
293
    }
294

295
    /**
296
     * Get PHP module setting
297
     */
298
    public static function getModuleSetting(string $pModuleName, array $pSettings): string
3✔
299
    {
300
        $vModules = self::parsePhpModules();
3✔
301

302
        foreach ($pSettings as $pSetting) {
3✔
303
            if (isset($vModules[$pModuleName][$pSetting])) {
3✔
304
                return $vModules[$pModuleName][$pSetting];
×
305
            }
306
        }
307

308
        return __('main.undefined');
3✔
309
    }
310
}
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