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

marscoin / martianrepublic / 22642820042

03 Mar 2026 09:08PM UTC coverage: 5.205% (+0.6%) from 4.588%
22642820042

push

github

Lennart Lopin
security: Phase 2 - CSRF, SQL, error pages, security headers

22 of 187 new or added lines in 4 files covered. (11.76%)

190 of 3650 relevant lines covered (5.21%)

0.31 hits per line

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

0.0
/app/Http/Controllers/Wallet/ApiController.php
1
<?php
2
namespace App\Http\Controllers\Wallet;
3

4
use App\Http\Controllers\Controller;
5
use Illuminate\Http\Request;
6
use Illuminate\Support\Facades\Auth;
7
use App\Includes\AppHelper;
8
use App\Models\Posts;
9
use App\Models\Profile;
10
use App\Models\User;
11
use App\Models\Proposals;
12
use App\Models\Publication;
13
use App\Models\Threads;
14
use App\Models\Citizen;
15
use App\Models\HDWallet;
16
use Illuminate\Support\Facades\Log;
17

18
class ApiController extends Controller {
19

20
        /**
21
         * Setup the layout used by the controller.
22
         *
23
         * @return void
24
         */
25

26
        public function __construct() {
×
NEW
27
                $this->middleware("auth");
×
28
        }
29

30

31

32
        /**
33
         * Internal
34
         *
35
         * @ignore
36
         * @hideFromAPIDocumentation
37
         */
38
        public function permapinpic(Request $request){
×
NEW
39
                $uid = Auth::user()->id;
×
NEW
40
                $hash = "";
×
NEW
41
                $dataPic = $request->input('picture');
×
NEW
42
                $type = $request->input('type');
×
NEW
43
                $public_address = $request->input('address');
×
44

45
                        // --- SECURITY: Sanitize the public_address to prevent directory traversal ---
46
                        $safeAddress = AppHelper::sanitizePathSegment($public_address);
×
47
                        if ($safeAddress === null) {
×
48
                                return response()->json(['error' => 'Invalid address format.'], 400);
×
49
                        }
50

51
                        // --- SECURITY: Validate the base64 image data (extension, MIME, size, PHP code) ---
52
                        $validation = AppHelper::validateBase64Image($dataPic);
×
53
                        if (!$validation['valid']) {
×
54
                                Log::warning('permapinpic upload rejected: ' . $validation['error'] . ' (user: ' . $uid . ')');
×
55
                                return response()->json(['error' => $validation['error']], 422);
×
56
                        }
57

58
                        $safeExtension = $validation['extension'];
×
59
                        $decodedData = $validation['data'];
×
60

61
                        $file_path = "./assets/citizen/" . $safeAddress . "/";
×
62
                        if (!file_exists($file_path)) {
×
63
                                mkdir($file_path, 0755, true);
×
64
                        }
65

66
                        // --- SECURITY: Write .htaccess to prevent PHP execution in upload dir ---
67
                        AppHelper::writeUploadHtaccess($file_path);
×
68

69
                        // Use validated extension, not user-supplied one
70
                        $file_path = "./assets/citizen/" . $safeAddress . "/profile_pic." . $safeExtension;
×
71

72
                        file_put_contents($file_path, $decodedData);
×
73
                        $hash = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
74

75
                        $citcache = Citizen::where('userid', '=', $uid)->first();
×
76
                        if(is_null($citcache)) $citcache = new Citizen;
×
77
                        $citcache->userid = $uid;
×
78
                        $citcache->avatar_link = "https://ipfs.marscoin.org/ipfs/".$hash;
×
79
                        $citcache->save();
×
80

NEW
81
                        return response()->json(["Hash" => $hash], 200);
×
82
        }
83

84

85
        /**
86
         * Internal
87
         *
88
         * @ignore
89
         * @hideFromAPIDocumentation
90
         */
91
        public function permapinvideo(Request $request){
×
NEW
92
                $uid = Auth::user()->id;
×
NEW
93
                $hash = "";
×
NEW
94
                $dataPic = $request->input('file');
×
NEW
95
                $type = $request->input('type');
×
NEW
96
                $public_address = $request->input('address');
×
97

98
                        // --- SECURITY: Sanitize the public_address to prevent directory traversal ---
99
                        $safeAddress = AppHelper::sanitizePathSegment($public_address);
×
100
                        if ($safeAddress === null) {
×
101
                                return response()->json(['error' => 'Invalid address format.'], 400);
×
102
                        }
103

104
                        if ($request->hasFile('file'))
×
105
                        {
106
                                // --- SECURITY: Validate the uploaded file (extension, MIME, size, PHP code) ---
107
                                $uploadedFile = $request->file('file');
×
108
                                $validation = AppHelper::validateUploadedFile($uploadedFile, [
×
109
                                        'webm' => ['video/webm', 'audio/webm'],
×
110
                                ]);
×
111
                                if (!$validation['valid']) {
×
112
                                        Log::warning('permapinvideo upload rejected: ' . $validation['error'] . ' (user: ' . $uid . ')');
×
113
                                        return response()->json(['error' => $validation['error']], 422);
×
114
                                }
115

116
                                $file_path = "./assets/citizen/" . $safeAddress . "/";
×
117
                                if (!file_exists($file_path)) {
×
118
                                        mkdir($file_path, 0755, true);
×
119
                                }
120

121
                                // --- SECURITY: Write .htaccess to prevent PHP execution in upload dir ---
122
                                AppHelper::writeUploadHtaccess($file_path);
×
123

124
                                $file_path = "./assets/citizen/" . $safeAddress  . "/";
×
125
                                $request->file('file')->move($file_path, "profile_video.webm" );
×
126
                                $file_path = $file_path . "profile_video.webm";
×
127
                                $hash = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
128

129
                                $citcache = Citizen::where('userid', '=', $uid)->first();
×
130
                                if(is_null($citcache)) $citcache = new Citizen;
×
131
                                $citcache->userid = $uid;
×
132
                                $citcache->liveness_link = "https://ipfs.marscoin.org/ipfs/".$hash;
×
133
                                $citcache->save();
×
134

NEW
135
                                return response()->json(["Hash" => $hash], 200);
×
136
                        }
137

NEW
138
                return response()->json(['error' => 'No file uploaded.'], 400);
×
139
        }
140

141

142
        /**
143
         * Internal
144
         *
145
         * @hideFromAPIDocumentation
146
         */
147
        public function permapinlog(Request $request)
×
148
        {
149
                $public_address = $request->input('address');
×
150
                $title = $request->input('title');
×
151
                $entry = $request->input('entry');
×
152
                $uid = Auth::user()->id;
×
153

154
                // --- SECURITY: Sanitize the public_address to prevent directory traversal ---
155
                $safeAddress = AppHelper::sanitizePathSegment($public_address);
×
156
                if ($safeAddress === null) {
×
157
                        return response()->json(['error' => 'Invalid address format.'], 400);
×
158
                }
159

160
                // --- SECURITY: Sanitize title for use in path (md5 hash is safe, but validate title exists) ---
161
                if (empty($title)) {
×
162
                        return response()->json(['error' => 'Title is required.'], 400);
×
163
                }
164

165
                $file_path = "./assets/citizen/" . $safeAddress . "/logbook/" . md5($title);
×
166

167
                if (!file_exists($file_path)) {
×
168
                        mkdir($file_path, 0755, true); // More secure permissions
×
169
                }
170

171
                // --- SECURITY: Write .htaccess to prevent PHP execution in upload dir ---
172
                AppHelper::writeUploadHtaccess($file_path);
×
173

174
                // --- SECURITY: Check content for PHP code ---
175
                $logContent = $title . "\n\n" . $entry;
×
176
                if (AppHelper::containsPhpCode($logContent)) {
×
177
                        Log::warning('permapinlog rejected: content contains PHP code (user: ' . $uid . ')');
×
178
                        return response()->json(['error' => 'Content contains potentially dangerous code.'], 422);
×
179
                }
180

181
                // --- SECURITY: Check content size (max 5MB) ---
182
                if (strlen($logContent) > 5242880) {
×
183
                        return response()->json(['error' => 'Content exceeds maximum size of 5MB.'], 422);
×
184
                }
185

186
                $file = $file_path . "/log.markdown";
×
187
                file_put_contents($file, $logContent);
×
188

189
                $files = $request->file('filenames');
×
190
                if ($files && is_array($files)) {
×
191
                        foreach ($files as $f) {
×
192
                                // --- SECURITY: Validate each uploaded file ---
193
                                $validation = AppHelper::validateUploadedFile($f);
×
194
                                if (!$validation['valid']) {
×
195
                                        Log::warning('permapinlog file rejected: ' . $validation['error'] . ' (user: ' . $uid . ', file: ' . $f->getClientOriginalName() . ')');
×
196
                                        // Skip invalid files but continue processing valid ones
197
                                        continue;
×
198
                                }
199

200
                                $name = $f->hashName(); // Generates a unique, random name...
×
201

202
                                // --- SECURITY: Ensure the generated filename does not have a dangerous extension ---
203
                                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
×
204
                                if (AppHelper::isExtensionBlocked($ext)) {
×
205
                                        Log::warning('permapinlog blocked dangerous file extension: ' . $ext . ' (user: ' . $uid . ')');
×
206
                                        continue;
×
207
                                }
208

209
                                $f->move($file_path, $name);
×
210
                        }
211
                }
212

213
                try {
214
                        $hash = AppHelper::uploadFolder($file_path, 'http://127.0.0.1:5001/api/v0/add?pin=true&recursive=true&wrap-with-directory=true&quieter'); // Example: use a config value or env variable
×
215
                        AppHelper::insertPublicationCache($uid, $file_path, $hash, $title);
×
216
                } catch (\Exception $e) {
×
217
                        // Handle error; possibly log it and return a user-friendly message
218
                        return response()->json(["error" => $e->getMessage()], 500);
×
219
                }
220

221
                return response()->json(["Hash" => $hash, "Path" => $file_path], 200)
×
222
                        ->header('Content-Type', "application/json;");
×
223
        }
224

225

226

227

228
        public function removepinlog(Request $request)
×
229
        {
230
                $cid = $request->input('cid'); // The CID to unpin
×
231

232
                if (!$cid) {
×
233
                        return response()->json(["error" => "CID is required"], 400);
×
234
                }
235

236
                // Validate CID format to prevent injection into the IPFS API URL
237
                if (!AppHelper::isValidCID($cid)) {
×
238
                        return response()->json(["error" => "Invalid CID format"], 400);
×
239
                }
240

241
                // Verify the publication belongs to the current user
242
                $uid = Auth::user()->id;
×
243
                $publication = Publication::where('ipfs_hash', $cid)->first();
×
244
                if ($publication && $publication->userid != $uid) {
×
245
                        return response()->json(["error" => "Unauthorized: you can only remove your own publications."], 403);
×
246
                }
247

248
                $ipfsApiUrl = 'http://127.0.0.1:5001/api/v0/pin/rm?arg=' . urlencode($cid) . "&recursive=true";
×
249
                Log::debug($ipfsApiUrl);
×
250
                try {
251
                        // Initialize cURL session
252
                        $ch = curl_init();
×
253
                        curl_setopt($ch, CURLOPT_URL, $ipfsApiUrl);
×
254
                        curl_setopt($ch, CURLOPT_VERBOSE, true);
×
255
                        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
×
256
                        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST"); // Ensure this is POST
×
257

258
                        $response = curl_exec($ch);
×
259
                        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
×
260

261
                        Log::debug($response);
×
262

263
                        if ($httpCode != 200) {
×
264
                                // Error handling, IPFS might return error messages as JSON
265
                                $errorMsg = "Failed to unpin CID. HTTP status code: $httpCode";
×
266
                                if ($responseJson = json_decode($response)) {
×
267
                                        if (!empty($responseJson->Message)) {
×
268
                                                $errorMsg = $responseJson->Message;
×
269
                                        }
270
                                }
271
                                throw new \Exception($errorMsg);
×
272
                        }
273

274
                        $publicationDeleted = Publication::where('ipfs_hash', $cid)->delete();
×
275
                        if (!$publicationDeleted) {
×
276
                                throw new \Exception("Failed to delete publication from the database.");
×
277
                        }
278

279
                } catch (\Exception $e) {
×
280
                        // Handle error; possibly log it and return a user-friendly message
281
                        return response()->json(["error" => $e->getMessage()], 500);
×
282
                }
283

284
                // Close cURL session
285
                curl_close($ch);
×
286

287
                // Respond to the client
288
                return response()->json(["message" => "Successfully unpinned CID: $cid"], 200)
×
289
                        ->header('Content-Type', "application/json;");
×
290
        }
291

292

293

294
        /**
295
         * Handles JSON storage and pinning to a distributed file system.
296
         *
297
         * @hideFromAPIDocumentation
298
         */
299
        public function permapinjson(Request $request)
×
300
        {
301
                $public_address = $request->input('address');
×
302
                $type = $request->input('type');
×
303
                $json = $request->input('payload');
×
304

305
                // --- SECURITY: Sanitize the public_address to prevent directory traversal ---
306
                $safeAddress = AppHelper::sanitizePathSegment($public_address);
×
307
                if ($safeAddress === null) {
×
308
                        return response()->json(['error' => 'Invalid address format.'], 400);
×
309
                }
310

311
                // --- SECURITY: Sanitize the type parameter to prevent path traversal ---
312
                $safeType = AppHelper::sanitizePathSegment($type);
×
313
                if ($safeType === null) {
×
314
                        return response()->json(['error' => 'Invalid type format.'], 400);
×
315
                }
316

317
                // --- SECURITY: Check file size (max 5MB) ---
318
                if (strlen($json) > 5242880) {
×
319
                        return response()->json(['error' => 'Payload exceeds maximum size of 5MB.'], 422);
×
320
                }
321

322
                // --- SECURITY: Reject payloads containing PHP code ---
323
                if (AppHelper::containsPhpCode($json)) {
×
324
                        Log::warning('permapinjson rejected: payload contains PHP code');
×
325
                        return response()->json(['error' => 'Payload contains potentially dangerous content.'], 422);
×
326
                }
327

328
                // --- SECURITY: Validate that payload is valid JSON ---
329
                $decodedJson = json_decode($json);
×
330
                if ($json !== '' && json_last_error() !== JSON_ERROR_NONE) {
×
331
                        return response()->json(['error' => 'Invalid JSON payload.'], 422);
×
332
                }
333

334
                $projectRoot = config('app.project_root', base_path());
×
335
                $base_path =  $projectRoot . "/assets/citizen/" . $safeAddress;
×
336

337
                // Check and create the directory if it doesn't exist
338
                Log::info($base_path);
×
339
                clearstatcache();
×
340
                if (!is_dir($base_path)) {
×
341
                        Log::info("Trying to create directory: " . $base_path);
×
342
                        if (!mkdir($base_path, 0755, true)) {
×
343
                                Log::error("Failed to create directory: " . $base_path);
×
344
                                return response()->json(["error" => "Failed to create directory. Check permissions."], 500);
×
345
                        }
346
                        Log::info("Directory created: " . $base_path);
×
347
                }
348

349
                // Check if the directory is writable, regardless of whether it was just created or already existed
350
                if (!is_writable($base_path)) {
×
351
                        Log::error("Directory not writable: " . $base_path);
×
352
                        return response()->json(["error" => "Directory is not writable. Check permissions."], 500);
×
353
                }
354

355
                // --- SECURITY: Write .htaccess to prevent PHP execution in upload dir ---
356
                AppHelper::writeUploadHtaccess($base_path);
×
357

358
                $file_path = $base_path . "/" . $safeType . ".json";
×
359

360
                // Attempt to write the JSON data to the file
361
                if (file_put_contents($file_path, $json) === false) {
×
362
                        return response()->json(["error" => "Failed to write to file."], 500);
×
363
                }
364

365
                try {
366
                        Log::info("PermaJson: " . $file_path);
×
367

368
                        // Check if the type contains the word 'log'
369
                        if (strpos($safeType, 'log') !== false) {
×
370
                                // The type contains 'log', use uploadFolder
371
                                $apiResponse = AppHelper::uploadFolder($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true&recursive=true&wrap-with-directory=true&quieter");
×
372
                        } else {
373
                                // The type does not contain 'log', use upload
374
                                $apiResponse = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
375
                        }
376

377
                        if (is_string($apiResponse)) {
×
378
                                $formattedResponse = ['Hash' => $apiResponse];
×
379
                        } else {
380
                                Log::error("Upload error: Formatting");
×
381
                                return response()->json(["error"=>"formatting error"], 500);
×
382
                        }
383

384
                        return response()->json($formattedResponse, 200)->header('Content-Type', "application/json;");
×
385
                } catch (\Exception $e) {
×
386
                        // Handle exceptions during the upload and pinning process
387
                        Log::error("Upload error: " . $e->getMessage());
×
388
                        return response()->json(["error" => $e->getMessage()], 500);
×
389
                }
390
        }
391

392

393

394

395
        /**
396
         * Internal
397
         *
398
         * @hideFromAPIDocumentation
399
         */
400
        public function setfeed(Request $request)
×
401
        {
NEW
402
                $uid = Auth::user()->id;
×
NEW
403
                $txid = $request->input('txid');
×
NEW
404
                $action_tag = $request->input('type');
×
NEW
405
                $public_address = $request->input('address');
×
NEW
406
                $embedded_link = $request->input('embedded_link');
×
NEW
407
                $message = $request->input('message');
×
408

NEW
409
                AppHelper::insertBlockchainCache($public_address, $uid, $action_tag, $message, $embedded_link, $txid);
×
410

NEW
411
                $profile = Profile::where('userid', '=', $uid)->first();
×
NEW
412
                if ($profile) {
×
413
                        $profile->general_public = 1;
×
414
                        $profile->save();
×
415
                }
416

NEW
417
                return response()->json(["Hash" => $txid], 200);
×
418
        }
419

420

421
        /**
422
         * Internal
423
         *
424
         * @hideFromAPIDocumentation
425
         */
426
        public function getBalance($address)
×
427
        {
NEW
428
                $balance = AppHelper::getMarscoinBalance($address);
×
NEW
429
                return response()->json(["balance" => $balance], 200);
×
430
        }
431

432

433

434
        /**
435
         * Internal
436
         *
437
         * @hideFromAPIDocumentation
438
         */
439
        public function dismissAlert(Request $request)
×
440
        {
NEW
441
                $alertType = $request->input('alertType');
×
442

443
                // Only allow known alert types to prevent session key injection
NEW
444
                $allowedAlerts = ['wallet_alert', 'citizen_alert', 'onboarding_alert', 'backup_alert', 'endorsement_alert'];
×
NEW
445
                if (!in_array($alertType, $allowedAlerts, true)) {
×
NEW
446
                        return response()->json(['error' => 'Invalid alert type.'], 400);
×
447
                }
448

NEW
449
                session()->put($alertType, true);
×
NEW
450
                return response()->json(['success' => true]);
×
451
        }
452

453

454

455
        /**
456
         * Internal
457
         *
458
         * @hideFromAPIDocumentation
459
         */
460
        public function getPrice(Request $request)
×
461
        {
NEW
462
                $price = AppHelper::getMarscoinPrice();
×
NEW
463
                return response()->json(["mars_price" => $price], 200);
×
464
        }
465

466

467
        /**
468
         * Internal
469
         *
470
         * @hideFromAPIDocumentation
471
         */
472
        public function getTransactions(Request $request)
×
473
        {
NEW
474
                $address = $request->input('address');
×
475

476
                // Validate Marscoin address to prevent URL injection
NEW
477
                if (!$address || !AppHelper::isValidMarscoinAddress($address)) {
×
NEW
478
                        return response()->json(['error' => 'Invalid Marscoin address.'], 400);
×
479
                }
480

NEW
481
                $json = AppHelper::file_get_contents_curl("http://explore1.marscoin.org/api/txs/?address=" . urlencode($address));
×
482

NEW
483
                return response($json)->header('Content-Type', 'application/json');
×
484
        }
485

486
        /**
487
         * Internal
488
         *
489
         * @hideFromAPIDocumentation
490
         */
491
        public function setfullname(Request $request)
×
492
        {
NEW
493
                $uid = Auth::user()->id;
×
NEW
494
                $firstname = $request->input('firstname');
×
NEW
495
                $lastname = $request->input('lastname');
×
NEW
496
                if (!$firstname || !$lastname) {
×
NEW
497
                        return response()->json(['error' => 'First and last name are required.'], 400);
×
498
                }
499

NEW
500
                $fullname = $firstname . " " . $lastname;
×
501

NEW
502
                $citcache = Citizen::where('userid', '=', $uid)->first();
×
NEW
503
                if (is_null($citcache)) $citcache = new Citizen;
×
504

NEW
505
                $citcache->userid = $uid;
×
NEW
506
                $citcache->firstname = $firstname;
×
NEW
507
                $citcache->lastname = $lastname;
×
NEW
508
                $citcache->save();
×
509

NEW
510
                $user = User::where('id', '=', $uid)->first();
×
NEW
511
                if ($user) {
×
512
                        $user->fullname = $fullname;
×
513
                        $user->save();
×
514
                }
NEW
515
                return response()->json(['success' => true]);
×
516
        }
517

518
        /**
519
         *
520
         * @hideFromAPIDocumentation
521
         */
522
        public function cacheonboarding(Request $request)
×
523
        {
NEW
524
                $uid = Auth::user()->id;
×
NEW
525
                $shortbio = $request->input('shortbio');
×
NEW
526
                $displayname = $request->input('displayname');
×
NEW
527
                $publicaddress = $request->input('publicaddress');
×
528

NEW
529
                $citcache = Citizen::where('userid', '=', $uid)->first();
×
NEW
530
                if (is_null($citcache)) $citcache = new Citizen;
×
531

NEW
532
                $citcache->userid = $uid;
×
NEW
533
                $citcache->shortbio = $shortbio;
×
NEW
534
                $citcache->displayname = $displayname;
×
NEW
535
                $citcache->public_address = $publicaddress;
×
NEW
536
                $citcache->save();
×
537

NEW
538
                return response()->json(['success' => true]);
×
539
        }
540

541

542
        public function rejectApplication(Request $request)
×
543
        {
NEW
544
                $user = Auth::user();
×
NEW
545
                $profile = Profile::where('userid', $user->id)->first();
×
NEW
546
                $reporter = Citizen::where('userid', '=', $user->id)->first();
×
547

NEW
548
                $rejectionReasons = [
×
NEW
549
                        'avatar_link' => 'Missing Personal Image',
×
NEW
550
                        'liveness_link' => 'Incomplete Video',
×
NEW
551
                        'duplicate' => 'Duplicate Entry'
×
NEW
552
                ];
×
553

NEW
554
                if (!$profile || !$profile->citizen) {
×
NEW
555
                        return response()->json(['error' => 'Unauthorized access.'], 403);
×
556
                }
557

NEW
558
                $applicantUserId = $request->input('applicantUserId');
×
NEW
559
                $fieldToUpdate = $request->input('field');
×
560

561
                // Validate the field to update
NEW
562
                if (!in_array($fieldToUpdate, ['avatar_link', 'liveness_link'])) {
×
NEW
563
                        return response()->json(['error' => 'Invalid field specified.'], 400);
×
564
                }
565

566
                // Update the citizen table, setting the specified field to NULL for the applicant
NEW
567
                Citizen::where('userid', $applicantUserId)->update([$fieldToUpdate => NULL]);
×
568

NEW
569
                $applicant = Citizen::where('userid', '=', $applicantUserId)->first();
×
NEW
570
                $applicantAddress = $applicant ? $applicant->public_address : 'Unknown';
×
571

NEW
572
                $content = "The application of {$applicantAddress} has been rejected due to " . $rejectionReasons[$fieldToUpdate] . ".";
×
573

NEW
574
                $fullname = $reporter ? ($reporter->firstname . ' ' . $reporter->lastname) : $user->fullname;
×
NEW
575
                Posts::create([
×
NEW
576
                        'thread_id' => 27,
×
NEW
577
                        'author_id' => $user->id,
×
NEW
578
                        'content' => $content,
×
NEW
579
                        'authorName' => $fullname,
×
NEW
580
                        'created_at' => now(),
×
NEW
581
                        'updated_at' => now(),
×
NEW
582
                ]);
×
583

NEW
584
                return response()->json(['success' => 'Application has been rejected and recorded.']);
×
585
        }
586

587
        /**
588
         *
589
         * @hideFromAPIDocumentation
590
         */
591
        public function closewallet(Request $request)
×
592
        {
NEW
593
                $uid = Auth::user()->id;
×
NEW
594
                $profile = Profile::where('userid', '=', $uid)->first();
×
NEW
595
                if ($profile) {
×
596
                        $profile->wallet_open = 0;
×
597
                        $profile->save();
×
598
                }
NEW
599
                return response()->json(['success' => true]);
×
600
        }
601

602
        /**
603
         *
604
         * @hideFromAPIDocumentation
605
         */
606
        public function renameWallet(Request $request)
×
607
        {
608
                $request->validate([
×
609
                        'hdwallet_id' => 'required',
×
610
                        'new_name' => 'required|string|max:500',
×
611
                ]);
×
612

NEW
613
                $wallet = HDWallet::where('id', $request->hdwallet_id)
×
NEW
614
                                                        ->where('user_id', Auth::id())
×
NEW
615
                                                        ->firstOrFail();
×
NEW
616
                $wallet->wallet_type = $request->new_name;
×
NEW
617
                $wallet->save();
×
NEW
618
                return response()->json(['success' => 'Wallet renamed successfully']);
×
619
        }
620

621

622
        /**
623
         * @hideFromAPIDocumentation
624
         */
625
        public function setendorsed(Request $request)
×
626
        {
NEW
627
                $endorserId = Auth::user()->id;
×
NEW
628
                $targetUserId = $request->input("id");
×
629

630
                // Prevent self-endorsement
NEW
631
                if ((int) $endorserId === (int) $targetUserId) {
×
NEW
632
                        return response()->json(['error' => 'Cannot endorse yourself.'], 400);
×
633
                }
634

635
                // Only citizens can endorse
NEW
636
                $endorserProfile = Profile::where('userid', '=', $endorserId)->first();
×
NEW
637
                if (!$endorserProfile || !$endorserProfile->citizen) {
×
NEW
638
                        return response()->json(['error' => 'Only citizens can endorse.'], 403);
×
639
                }
640

NEW
641
                $profile = Profile::where('userid', '=', $targetUserId)->first();
×
NEW
642
                if (!$profile) {
×
NEW
643
                        return response()->json(['error' => 'User not found.'], 404);
×
644
                }
NEW
645
                $profile->endorse_cnt = ($profile->endorse_cnt ?? 0) + 1;
×
NEW
646
                $profile->save();
×
NEW
647
                return response()->json(['success' => true]);
×
648
        }
649

650

651
        /**
652
         * @hideFromAPIDocumentation
653
         */
654
        public function cacheproposal(Request $request)
×
655
        {
NEW
656
                $uid = Auth::user()->id;
×
NEW
657
                $txid = $request->input('txid');
×
NEW
658
                $public_address = $request->input('address');
×
NEW
659
                $embedded_link = $request->input('embedded_link');
×
NEW
660
                $json = $request->input('message');
×
NEW
661
                $data = json_decode($json);
×
662

NEW
663
                if (!$data || !isset($data->data)) {
×
NEW
664
                        return response()->json(['error' => 'Invalid message payload.'], 400);
×
665
                }
666

NEW
667
                $citcache = Citizen::where('userid', '=', $uid)->first();
×
668

NEW
669
                if (!AppHelper::isValidCID($embedded_link ?? '')) {
×
NEW
670
                        return response()->json(['error' => 'Invalid IPFS hash'], 400);
×
671
                }
672

NEW
673
                $proposal = new Proposals;
×
NEW
674
                $proposal->user_id = $uid;
×
NEW
675
                $proposal->title = $data->data->title ?? '';
×
NEW
676
                $proposal->description = $data->data->description ?? '';
×
NEW
677
                $proposal->category = $data->data->category ?? '';
×
NEW
678
                $proposal->author = Auth::user()->fullname;
×
NEW
679
                $proposal->ipfs_hash = $embedded_link;
×
NEW
680
                $proposal->participation = $data->data->participation ?? 0;
×
NEW
681
                $proposal->threshold = $data->data->threshold ?? 0;
×
NEW
682
                $proposal->duration = $data->data->duration ?? 0;
×
NEW
683
                $proposal->expiration = $data->data->expiration ?? '';
×
NEW
684
                $proposal->txid = $txid;
×
NEW
685
                $proposal->public_address = $public_address;
×
686

NEW
687
                $proposal->save();
×
NEW
688
                $prop_id = $proposal->id;
×
689

NEW
690
                $authorName = $citcache ? ($citcache->firstname . ' ' . $citcache->lastname) : Auth::user()->fullname;
×
691

NEW
692
                $post = new Posts;
×
NEW
693
                $post->thread_id = 2;
×
NEW
694
                $post->author_id = $uid;
×
NEW
695
                $post->content = $proposal->description;
×
NEW
696
                $post->authorName = $authorName;
×
NEW
697
                $post->save();
×
698

NEW
699
                $post_id = $post->id;
×
700

NEW
701
                $threads = new Threads;
×
NEW
702
                $threads->category_id = 2;
×
NEW
703
                $threads->author_id = $uid;
×
NEW
704
                $threads->title = $data->data->title ?? '';
×
NEW
705
                $threads->first_post_id = $post_id;
×
NEW
706
                $threads->proposal_id = $prop_id;
×
NEW
707
                $threads->save();
×
708

NEW
709
                $thd_id = $threads->id;
×
710

NEW
711
                Proposals::where('id', $prop_id)->update(['discussion' => $thd_id]);
×
712

NEW
713
                return response()->json(["Proposal" => $prop_id, "Discussion" => $thd_id], 200);
×
714
        }
715

716

717

718

719

720
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc