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

visavi / rotor / 26723576697

31 May 2026 08:27PM UTC coverage: 14.172% (-0.4%) from 14.618%
26723576697

push

github

visavi
Перенес файлы обновлений бд в отдельную папку

785 of 5539 relevant lines covered (14.17%)

1.26 hits per line

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

19.66
/app/Http/Controllers/ApiController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Http\Controllers;
6

7
use App\Http\Resources\DialogueResource;
8
use App\Http\Resources\MessageResource;
9
use App\Http\Resources\NewMessageResource;
10
use App\Http\Resources\UserProfileResource;
11
use App\Http\Resources\UserResource;
12
use App\Models\Dialogue;
13
use App\Models\Flood;
14
use App\Models\Message;
15
use App\Models\User;
16
use Closure;
17
use Illuminate\Database\Query\JoinClause;
18
use Illuminate\Http\JsonResponse;
19
use Illuminate\Http\Request;
20
use Illuminate\Http\Resources\Json\JsonResource;
21
use Illuminate\Support\Facades\Cache;
22
use Illuminate\Support\Facades\DB;
23
use Illuminate\Support\Str;
24
use Illuminate\View\View;
25

26
class ApiController extends Controller
27
{
28
    /**
29
     * Главная страница
30
     */
31
    public function index(): View
×
32
    {
33
        return view('api/index');
×
34
    }
35

36
    /**
37
     * Авторизация и получение токена
38
     */
39
    public function auth(Request $request): JsonResponse
×
40
    {
41
        $request->validate([
×
42
            'login'    => ['required', 'string'],
×
43
            'password' => ['required', 'string'],
×
44
        ]);
×
45

46
        $user = getUserByLoginOrEmail($request->input('login'));
×
47

48
        if (! $user || ! password_verify($request->input('password'), $user->password)) {
×
49
            abort(401, __('users.incorrect_login_or_password'));
×
50
        }
51

52
        if ($user->level === User::BANNED) {
×
53
            abort(403, 'User banned');
×
54
        }
55

56
        if (! $user->apikey) {
×
57
            $user->update(['apikey' => Str::random(32)]);
×
58
        }
59

60
        return response()->json(['token' => $user->apikey]);
×
61
    }
62

63
    /**
64
     * Api пользователей
65
     */
66
    public function user(): JsonResource
×
67
    {
68
        $user = getUser();
×
69

70
        return new UserProfileResource($user);
×
71
    }
72

73
    /**
74
     * Api пользователей
75
     */
76
    public function users(string $login): JsonResource
×
77
    {
78
        $user = getUserByLogin($login);
×
79

80
        if (! $user) {
×
81
            abort(404, __('validator.user'));
×
82
        }
83

84
        return new UserResource($user);
×
85
    }
86

87
    /**
88
     * Api диалогов
89
     */
90
    public function dialogues(Request $request): JsonResource
×
91
    {
92
        $user = getUser();
×
93

94
        $lastMessage = Dialogue::query()
×
95
            ->select(
×
96
                'author_id',
×
97
                DB::raw('max(message_id) as message_id'),
×
98
                DB::raw('min(case when reading then 1 else 0 end) as all_reading')
×
99
            )
×
100
            ->where('user_id', $user->id)
×
101
            ->groupBy('author_id');
×
102

103
        $dialogues = Message::query()
×
104
            ->select('d.*', 'm.text', 'd2.all_reading', 'd3.reading as recipient_read')
×
105
            ->from('messages as m')
×
106
            ->join('dialogues as d', 'd.message_id', 'm.id')
×
107
            ->joinSub($lastMessage, 'd2', static function (JoinClause $join) {
×
108
                $join->on('d.message_id', 'd2.message_id');
×
109
            })
×
110
            ->leftJoin('dialogues as d3', function ($join) {
×
111
                $join->on('d.user_id', 'd3.author_id')
×
112
                    ->whereColumn('d.message_id', 'd3.message_id');
×
113
            })
×
114
            ->where('d.user_id', $user->id)
×
115
            ->with('author')
×
116
            ->orderBy('d.created_at', $this->getOrder($request))
×
117
            ->paginate($this->getPerPage($request));
×
118

119
        return DialogueResource::collection($dialogues);
×
120
    }
121

122
    /**
123
     * Api приватных сообщений
124
     */
125
    public function talk(string $login, Request $request): JsonResource
×
126
    {
127
        $user = getUser();
×
128

129
        if (is_numeric($login)) {
×
130
            $author = (new User())->setAttribute('id', $login);
×
131
        } else {
132
            $author = getUserByLogin($login);
×
133

134
            if (! $author) {
×
135
                abort(404, __('validator.user'));
×
136
            }
137
        }
138

139
        if ($user->id === $author->id) {
×
140
            abort(200, __('messages.empty_dialogue'));
×
141
        }
142

143
        $messages = Message::query()
×
144
            ->select('d.*', 'm.id', 'm.text', 'd2.reading as recipient_read')
×
145
            ->from('messages as m')
×
146
            ->join('dialogues as d', 'd.message_id', 'm.id')
×
147
            ->leftJoin('dialogues as d2', function ($join) {
×
148
                $join->on('d.user_id', 'd2.author_id')
×
149
                    ->whereColumn('d.message_id', 'd2.message_id');
×
150
            })
×
151
            ->where('d.user_id', $user->id)
×
152
            ->where('d.author_id', $author->id)
×
153
            ->orderBy('d.created_at', $this->getOrder($request))
×
154
            ->with('user', 'author', 'files')
×
155
            ->paginate($this->getPerPage($request));
×
156

157
        Dialogue::query()
×
158
            ->where('user_id', $user->id)
×
159
            ->where('author_id', $author->id)
×
160
            ->where('reading', 0)
×
161
            ->update(['reading' => 1]);
×
162

163
        $dialogue = $messages->first();
×
164
        $dialogue?->setAttribute('all_reading', true);
×
165

166
        return MessageResource::collection($messages)
×
167
            ->additional(['dialogue' => $dialogue ? DialogueResource::make($dialogue) : null]);
×
168
    }
169

170
    /**
171
     * Отправляет приватное сообщение
172
     *
173
     * @deprecated Используйте POST /talk/{login}
174
     */
175
    public function send(Request $request, Flood $flood): JsonResponse
×
176
    {
177
        return $this->createTalk($request->input('login', ''), $request, $flood);
×
178
    }
179

180
    /**
181
     * Отправляет приватное сообщение
182
     */
183
    public function createTalk(string $login, Request $request, Flood $flood): JsonResponse
×
184
    {
185
        $user = getUser();
×
186
        $recipient = getUserByLogin($login);
×
187

188
        $validated = $request->validate([
×
189
            'text' => [
×
190
                'required',
×
191
                'string',
×
192
                'min:' . setting('comment_text_min'),
×
193
                'max:' . setting('comment_text_max'),
×
194
                function (string $attribute, mixed $value, Closure $fail) use ($user, $recipient, $flood) {
×
195
                    if (! $recipient) {
×
196
                        $fail(__('validator.user'));
×
197
                    } elseif ($recipient->id === $user->id) {
×
198
                        $fail(__('messages.send_yourself'));
×
199
                    } elseif ($flood->isFlood()) {
×
200
                        $fail(__('validator.flood', ['sec' => $flood->getPeriod()]));
×
201
                    }
202
                },
×
203
            ],
×
204
            'files'   => ['nullable', 'array', 'max:' . setting('maxfiles')],
×
205
            'files.*' => ['file', 'max:' . setting('filesize'), 'mimes:' . setting('file_extensions')],
×
206
        ]);
×
207

208
        $text = antimat($validated['text']);
×
209
        $message = $recipient->sendMessage($user, $text);
×
210

211
        foreach ($request->file('files', []) as $file) {
×
212
            $message->uploadFile($file);
×
213
        }
214

215
        $flood->saveState();
×
216

217
        $message->load('user', 'files');
×
218
        $message->setAttribute('type', Message::OUT);
×
219

220
        return response()->json([
×
221
            'message' => __('messages.success_sent'),
×
222
            'data'    => MessageResource::make($message),
×
223
        ], 201);
×
224
    }
225

226
    /**
227
     * Api новых сообщений
228
     */
229
    public function newMessages(): JsonResponse
×
230
    {
231
        $user = getUser();
×
232

233
        $countMessages = Dialogue::query()
×
234
            ->where('user_id', $user->id)
×
235
            ->where('reading', 0)
×
236
            ->count();
×
237

238
        if (! $countMessages) {
×
239
            return response()->json(['count' => 0, 'dialogues' => []]);
×
240
        }
241

242
        $dialogues = Dialogue::query()
×
243
            ->select(
×
244
                'author_id',
×
245
                DB::raw('max(created_at) as last_created_at')
×
246
            )
×
247
            ->selectRaw('count(*) as cnt')
×
248
            ->where('user_id', $user->id)
×
249
            ->where('reading', 0)
×
250
            ->groupBy('author_id')
×
251
            ->with('author')
×
252
            ->get();
×
253

254
        return response()->json([
×
255
            'count'     => $countMessages,
×
256
            'dialogues' => NewMessageResource::collection($dialogues),
×
257
        ]);
×
258
    }
259

260
    /**
261
     * Api конфигурации
262
     */
263
    public function config(): JsonResponse
3✔
264
    {
265
        $data = Cache::remember('apiConfig', 600, static fn () => [
3✔
266
            'site' => [
3✔
267
                'title'             => setting('title'),
3✔
268
                'language'          => setting('language'),
3✔
269
                'money_name'        => setting('moneyname'),
3✔
270
                'score_name'        => setting('scorename'),
3✔
271
                'site_closed'       => (bool) setting('closedsite'),
3✔
272
                'registration_open' => (bool) setting('openreg'),
3✔
273
            ],
3✔
274
            'upload' => [
3✔
275
                'max_files'     => setting('maxfiles'),
3✔
276
                'max_file_size' => setting('filesize'),
3✔
277
                'extensions'    => explode(',', setting('file_extensions')),
3✔
278
            ],
3✔
279
            'forum' => [
3✔
280
                'title_min' => setting('forum_title_min'),
3✔
281
                'title_max' => setting('forum_title_max'),
3✔
282
                'text_min'  => setting('forum_text_min'),
3✔
283
                'text_max'  => setting('forum_text_max'),
3✔
284
            ],
3✔
285
            'vote' => [
3✔
286
                'title_min'   => setting('vote_title_min'),
3✔
287
                'title_max'   => setting('vote_title_max'),
3✔
288
                'answer_min'  => setting('vote_answer_min'),
3✔
289
                'answer_max'  => setting('vote_answer_max'),
3✔
290
                'answers_min' => 2,
3✔
291
                'answers_max' => 10,
3✔
292
            ],
3✔
293
            'message' => [
3✔
294
                'text_min' => setting('comment_text_min'),
3✔
295
                'text_max' => setting('comment_text_max'),
3✔
296
            ],
3✔
297
        ]);
3✔
298

299
        return response()->json($data);
3✔
300
    }
301

302
    /**
303
     * Get order direction from request
304
     */
305
    private function getOrder(Request $request, string $default = 'desc'): string
×
306
    {
307
        $order = $request->input('order', $default);
×
308

309
        return in_array($order, ['asc', 'desc']) ? $order : $default;
×
310
    }
311

312
    /**
313
     * Get per page from request
314
     */
315
    private function getPerPage(Request $request): int
×
316
    {
317
        $perPage = $request->integer('per_page', 10);
×
318

319
        return max(1, min($perPage, 100));
×
320
    }
321
}
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