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

marscoin / martianrepublic / 13357345334

16 Feb 2025 05:39PM UTC coverage: 4.846% (-0.07%) from 4.919%
13357345334

push

github

Lennart Lopin
feat: add public notarization api endpoint

0 of 52 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

168 of 3467 relevant lines covered (4.85%)

0.24 hits per line

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

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

4
use App\Http\Controllers\Controller;
5
use Illuminate\Http\Request;
6
use Illuminate\Http\Response;
7
use Illuminate\Support\Facades\Auth;
8
use App\Includes\jsonRPCClient;
9
use App\Includes\AppHelper;
10
use App\Models\Feed;
11
use App\Models\Profile;
12
use App\Models\User;
13
use App\Models\Proposals;
14
use App\Models\Threads;
15
use App\Models\Posts;
16
use App\Models\Citizen;
17
use App\Models\HDWallet;
18
use App\Models\CivicWallet;
19
use App\Models\MSession;
20
use Illuminate\Support\Facades\DB;
21
use Illuminate\Support\Str;
22
use Carbon\Carbon;
23
use Illuminate\Support\Facades\Cache;
24
use Illuminate\Support\Facades\Hash;
25
use Illuminate\Support\Facades\Log;
26
use BitcoinPHP\BitcoinECDSA\BitcoinECDSA;
27
use App\Includes\MarscoinECDSA;
28
use App\Livewire\CitizenStats;
29
use Illuminate\Database\Eloquent\ModelNotFoundException;
30
use App\Includes\IPFSRoot; 
31

32
class ApiController extends Controller
33
{
34
    public function allPublic()
×
35
    {
36
        $perPage = 25;
×
37
        $cacheKey = 'all_public_cache'; // Define a unique cache key for this query
×
38
        $excludedUserIds = [6462, 7601]; // ID of the user you want to exclude, for example, a test account
×
39
    
40
        // Attempt to get cached data. If not available, the closure will be run to fetch and cache the data.
41
        $feeds = Cache::remember($cacheKey, 60, function () use ($perPage, $excludedUserIds) {
×
42
            $feeds = Feed::with(['user' => function ($query) use ($excludedUserIds) {
×
43
                $query->select('id', 'fullname', 'created_at')
×
44
                      ->where('id', '!=', $excludedUserIds); // Exclude the specific user by ID
×
45
            }, 'user.profile' => function ($query) {
×
46
                $query->select('userid', 'general_public', 'endorse_cnt', 'citizen', 'has_application');
×
47
            }, 'user.citizen' => function ($query) {
×
48
                $query->select('userid', 'avatar_link', 'liveness_link')
×
49
                      ->whereNotNull('avatar_link');
×
50
            }])
×
51
            ->joinSub(
×
52
                Feed::selectRaw('max(id) as latest_id, userid')
×
53
                    ->where('tag', 'GP') // Ensure the tag is 'GP'
×
54
                    ->groupBy('userid'),
×
55
                'latest_feeds',
×
56
                function($join) {
×
57
                    $join->on('feed.id', '=', 'latest_feeds.latest_id');
×
58
                }
×
59
            )
×
60
            ->whereHas('user.profile', function ($query) {
×
61
                $query->where('tag', 'GP');
×
62
            })
×
63
            ->whereHas('user', function ($query) use ($excludedUserIds) {
×
64
                $query->whereNotIn('id', $excludedUserIds);
×
65
            })
×
66
            ->orderByDesc('id')
×
67
            ->take($perPage)
×
68
            ->distinct()
×
69
            ->get();
×
70
    
71
            return $feeds; // Directly return the fetched feeds
×
72
        });
×
73
    
74
        return response()->json($feeds);
×
75
    }
76
    
77
    
78

79

80
    public function allCitizen()
×
81
    {
82
        $perPage = 25;
×
83
        $cacheKey = 'all_citizens_cache';
×
84
        $excludedUserId = '';
×
85
    
86
        $feeds = Cache::remember($cacheKey, 60, function () use ($perPage, $excludedUserId) {
×
87
            return Feed::with([
×
88
                'user' => function ($query) use ($excludedUserId) {
×
89
                    $query->where('id', '!=', $excludedUserId)
×
90
                          ->select('id', 'fullname', 'created_at');
×
91
                },
×
92
                'user.profile' => function ($query) {
×
93
                    $query->select('userid', 'general_public', 'endorse_cnt', 'citizen', 'has_application');
×
94
                },
×
95
                'user.citizen' => function ($query) {
×
96
                    $query->select('userid', 'avatar_link', 'liveness_link')
×
97
                          ->whereNotNull('avatar_link'); // Ensure that avatar_link is not NULL
×
98
                }
×
99
            ])
×
100
            ->whereHas('user', function ($query) use ($excludedUserId) {  // Ensure user exists and is not excluded
×
101
                $query->where('id', '!=', $excludedUserId);
×
102
            })
×
103
            ->whereHas('user.citizen', function ($query) {  // Ensure user has valid citizen data
×
104
                $query->whereNotNull('avatar_link');
×
105
            })
×
106
            ->whereHas('user.profile', function ($query) {  // Additional filters for the profile
×
107
                $query->where('tag', 'CT');
×
108
            })
×
109
            ->select('id', 'address', 'userid', 'tag', 'message', 'embedded_link', 'txid', 'blockid', 'mined', 'updated_at', 'created_at')  // Only select columns from the feed table
×
110
            ->orderByDesc('id')
×
111
            ->take($perPage)
×
112
            ->get();
×
113
        });
×
114
    
115
        return response()->json($feeds);
×
116
    }
117
    
118
    
119

120
    
121

122
    // public function allApplicants()
123
    // {
124
    //     $perPage = 25; 
125
    
126
    //     $applicants = User::whereHas('profile', function ($query) {
127
    //         $query->where('has_application', 1);
128
    //     })
129
    //     ->with(['profile', 'hdWallet' => function($query) {
130
    //         // If you only need the public_addr from HdWallet, select it specifically
131
    //         $query->select('user_id', 'public_addr');
132
    //     }])
133
    //     ->orderByDesc('id')
134
    //     ->paginate($perPage, ['id', 'fullname']); 
135
    //     // Customize the result to match the raw query structure, if needed
136
    //     $customResult = $applicants->getCollection()->transform(function ($user) {
137
    //         return [
138
    //             'userid' => $user->id,
139
    //             'fullname' => $user->fullname,
140
    //             'address' => $user->hdWallet ? $user->hdWallet->public_addr : null,
141
    //         ];
142
    //     });
143
    
144
    //     return response()->json([
145
    //         'current_page' => $applicants->currentPage(),
146
    //         'data' => $customResult,
147
    //         'total' => $applicants->total(),
148
    //         'per_page' => $applicants->perPage(),
149
    //         'last_page' => $applicants->lastPage(),
150
    //     ]);
151
    // }
152

153
    public function allApplicants()
×
154
    {
155
        $perPage = 25; 
×
156

157
        $applicants = User::whereHas('profile', function ($query) {
×
158
            $query->where('has_application', 1);
×
159
        })
×
160
        ->where('id', '!=', 6462) // Exclude user with id 6462
×
161
        ->with(['profile', 'hdWallet' => function($query) {
×
162
            $query->select('user_id', 'public_addr');
×
163
        }, 'citizen']) // Add citizen relationship
×
164
        ->orderByDesc('id')
×
165
        ->paginate($perPage, ['id', 'fullname']); 
×
166

167
        $customResult = $applicants->getCollection()->transform(function ($user) {
×
168
            return [
×
169
                'userid' => $user->id,
×
170
                'fullname' => $user->fullname,
×
171
                'address' => $user->hdWallet ? $user->hdWallet->public_addr : null,
×
172
                'citizen' => $user->citizen, // Include citizen data
×
173
            ];
×
174
        });
×
175

176
        return response()->json([
×
177
            'current_page' => $applicants->currentPage(),
×
178
            'data' => $customResult,
×
179
            'total' => $applicants->total(),
×
180
            'per_page' => $applicants->perPage(),
×
181
            'last_page' => $applicants->lastPage(),
×
182
        ]);
×
183
    }
184

185

NEW
186
    public function allFeed(Request $request)
×
187
    {
NEW
188
        $perPage = 25;
×
NEW
189
        $page = $request->input('page', 1);
×
NEW
190
        $excludedUserIds = [6462, 7601];
×
NEW
191
        $cacheKey = 'all_feed_cache_' . $page;
×
192

NEW
193
        $feeds = Cache::remember($cacheKey, 60, function () use ($perPage, $excludedUserIds) {
×
NEW
194
            return Feed::with(['user' => function ($query) use ($excludedUserIds) {
×
NEW
195
                $query->select('id', 'fullname', 'created_at')
×
NEW
196
                    ->whereNotIn('id', $excludedUserIds);
×
NEW
197
            }, 'user.profile' => function ($query) {
×
NEW
198
                $query->select('userid', 'general_public', 'endorse_cnt', 'citizen', 'has_application');
×
NEW
199
            }, 'user.citizen' => function ($query) {
×
NEW
200
                $query->select('userid', 'avatar_link', 'liveness_link')
×
NEW
201
                    ->whereNotNull('avatar_link');
×
NEW
202
            }])
×
NEW
203
            ->whereHas('user', function ($query) use ($excludedUserIds) {
×
NEW
204
                $query->whereNotIn('id', $excludedUserIds);
×
NEW
205
            })
×
NEW
206
            ->whereNotNull('mined')
×
NEW
207
            ->select(
×
NEW
208
                'id',
×
NEW
209
                'address',
×
NEW
210
                'userid',
×
NEW
211
                'tag',
×
NEW
212
                'message',
×
NEW
213
                'embedded_link',
×
NEW
214
                'txid',
×
NEW
215
                'blockid',
×
NEW
216
                'mined',
×
NEW
217
                'created_at',
×
NEW
218
                'updated_at'
×
NEW
219
            )
×
NEW
220
            ->orderByDesc('mined')
×
NEW
221
            ->orderByDesc('id')
×
NEW
222
            ->paginate($perPage);
×
NEW
223
        });
×
224

NEW
225
        return $this->paginatedResponse($feeds);
×
226
    }
227

228
    // Helper method to format paginated responses consistently
NEW
229
    private function paginatedResponse($feeds)
×
230
    {
NEW
231
        return response()->json([
×
NEW
232
            'current_page' => $feeds->currentPage(),
×
NEW
233
            'data' => $feeds->items(),
×
NEW
234
            'first_page_url' => $feeds->url(1),
×
NEW
235
            'from' => $feeds->firstItem(),
×
NEW
236
            'last_page' => $feeds->lastPage(),
×
NEW
237
            'last_page_url' => $feeds->url($feeds->lastPage()),
×
NEW
238
            'next_page_url' => $feeds->nextPageUrl(),
×
NEW
239
            'path' => $feeds->path(),
×
NEW
240
            'per_page' => $feeds->perPage(),
×
NEW
241
            'prev_page_url' => $feeds->previousPageUrl(),
×
NEW
242
            'to' => $feeds->lastItem(),
×
NEW
243
            'total' => $feeds->total(),
×
NEW
244
        ]);
×
245
    }
246

247

UNCOV
248
    public function showCitizen(Request $request, $address)
×
249
    {
250
        // Look up the Martian user by public address
251
        $martianWallet = CivicWallet::where('public_addr', $address)->first();
×
252

253
        if (!$martianWallet) {
×
254
            return response()->json(['message' => 'Martian not found.'], 404);
×
255
        }
256

257
        // Attempt to fetch the user's profile and additional data
258
        $citizen = Citizen::where('public_address', $address)->first();
×
259
        $profile = Profile::where('userid', $martianWallet->user_id)->first();
×
260
        $feedItems = Feed::where('userid', $martianWallet->user_id)->whereNotNull('mined')->whereNotIn('tag', ['GP','CT'])->orderBy('created_at', 'desc')->get();
×
261
        
262
        // Fetch the latest 3 activities
263
        $activity = DB::table('feed')
×
264
            ->join('users', 'feed.userid', '=', 'users.id')
×
265
            ->join('profile', 'feed.userid', '=', 'profile.userid')
×
266
            ->select('profile.userid', 'users.fullname', 'feed.tag', 'feed.mined') 
×
267
            ->where('feed.userid', $martianWallet->user_id)
×
268
            ->orderBy('feed.id', 'desc')
×
269
            ->get();
×
270

271
        // Construct response
272
        $response = [
×
273
            'citizen' => $citizen,
×
274
            'profile' => [
×
275
                'general_public' => $profile ? $profile->general_public : null,
×
276
                'isCitizen' => $profile ? $profile->citizen : null,
×
277
            ],
×
278
            'feedItems' => $feedItems,
×
279
            'activity' => $activity,
×
280
        ];
×
281

282
        return response()->json($response);
×
283
    }
284

285

286

287
    public function token(Request $request) 
×
288
    {
289
        $publicAddress = $request->input('a');
×
290
        $msg = $request->input('m');
×
291
        $sig = $request->input('s');
×
292
        $timestamp = $request->input('t');
×
293

294
        Log::debug("Address: " . $publicAddress);
×
295
        Log::debug("msg: " . $msg);
×
296
        Log::debug("sig: " . $sig);
×
297
        Log::debug("timestamp: " . $timestamp);
×
298

299
        if (empty($msg)){
×
300
            $data = $request->json()->all();
×
301

302
            if (isset($data['data'])) {
×
303
                $msg = $data['data']['m'] ?? null;
×
304
                $sig = $data['data']['s'] ?? null;
×
305
                $publicAddress = $data['data']['a'] ?? null;
×
306
                $timestamp = $data['data']['t'] ?? '0';
×
307
            }
308
        }
309
        // Validate timestamp
310
        if (Carbon::now()->diffInSeconds(Carbon::createFromTimestamp($timestamp)) > 600) { // 5 minutes tolerance
×
311
            return response()->json(['error' => 'Request timestamp is too old.'], 401);
×
312
        }
313

314
        $bitcoinECDSA = new BitcoinECDSA();          
×
315
        $marscoinECDSA = new MarscoinECDSA();
×
316
        if ($marscoinECDSA->checkSignatureForMessage($publicAddress, $sig, $msg))    
×
317
        {
318
            $wallet = CivicWallet::where('public_addr', $publicAddress)->first();
×
319

320
            if ($wallet) {
×
321
                // Wallet found, get the associated user
322
                $user = $wallet->user;
×
323
            } else {
324
                // Wallet not found, create a new user without a wallet
325
                $user = User::where('email', $publicAddress . '@martianrepublic.org')->first();
×
326
                if (!$user) {
×
327
                    $user = User::create([
×
328
                        'fullname' => 'UserWithoutWallet', // Or any other default or generated name
×
329
                        'email' => $publicAddress . '@martianrepublic.org',
×
330
                        'password' => Hash::make(Str::random(10)), // Random password
×
331
                    ]);
×
332
                    Log::debug("..created user");
×
333
                }
334
                $profile = Profile::where('userid', $user->id)->first();
×
335
                if (!$profile) {
×
336
                    $profile = Profile::create([
×
337
                        'userid' => $user->id,
×
338
                        'wallet_open' => 0,
×
339
                        'civic_wallet_open' => 0,
×
340
                        'has_application' => 0,
×
341
                    ]);
×
342
                    Log::debug("..created profile");
×
343
                }
344
                //create a citcache entry
345
                $citcache = Citizen::where('userid', $user->id)->first();
×
346
                if (!$citcache) {
×
347
                    $citizen = Citizen::create([
×
348
                        'userid' => $user->id,
×
349
                        'public_address' => $publicAddress,
×
350
                        'created_at' => now(),
×
351
                        'updated_at' => now(),
×
352
                    ]);
×
353
                    Log::debug("..created citizen cache");
×
354
                }
355
                // register civic wallet for the new user
356
                $wallet = CivicWallet::create([
×
357
                    'user_id' => $user->id,
×
358
                    'wallet_type' => 'MARS',
×
359
                    'backup' => 0,
×
360
                    'encrypted_seed' => '',
×
361
                    'public_addr' => $publicAddress,
×
362
                    'created_at' => now(),
×
363
                    'opened_at' => now()
×
364
                ]);
×
365
                Log::debug("..created civic wallet");
×
366
            }
367
            if($user->status == 'inactive')
×
368
                return response()->json(['token' => 'inactive']);
×
369

370
            $token = $user->createToken('authToken')->plainTextToken;
×
371
            
372
            return response()->json(['token' => $token]);
×
373
        } 
374
        else {
375
            return response()->json(['message' => 'couldnt verify message'], 406);
×
376
        }
377
    }
378

379
    public function test()
×
380
    {
381

382
        $messageM="https://martianrepublic.org/api/token?a=MCHe2XTUEegyqsYc5ePe2dQiPtixLfhppR&t=1715354045";
×
383
        $addressM="MCHe2XTUEegyqsYc5ePe2dQiPtixLfhppR";
×
384
        $signatureM="H+Qrav6eWNrB0P5DiRaGRRR/RVtD8qd5dKlWA3FOfeiFc4h04769HtfMbsmxrrNPk0MeTJmwPCR9xg67f1NatOA=";
×
385

386
        $bitcoinECDSA = new BitcoinECDSA(); 
×
387
        $bitcoinECDSA->generateRandomPrivateKey(); //generate new random private key
×
388
        $address = $bitcoinECDSA->getAddress();
×
389
        $message = "Test message";
×
390
        $signedMessage = $bitcoinECDSA->signMessage($message, true);
×
391
        echo "message: " . PHP_EOL . "<br>";
×
392
        echo $message . PHP_EOL . "<br>";
×
393
        echo "signed message: " . PHP_EOL . "<br>";
×
394
        echo $signedMessage . PHP_EOL . "<br>";
×
395
        //$signedMessage = "random";
396

397
        //loading Bitcoin crypto library
398
        if ($bitcoinECDSA->checkSignatureForMessage($address, $signedMessage, $message))       //verifying signature
×
399
        {
400
            Log::debug("True");
×
401
            echo "True<br>";
×
402
        }else{
403
            Log::debug("False");
×
404
            echo "False<br>";
×
405
        }
406

407
        echo "mars test";
×
408
        $marscoinECDSA = new MarscoinECDSA();
×
409
        echo "Address; " . $addressM;
×
410
        echo "Message:" . $messageM;
×
411
        echo "sig:" . $signatureM;
×
412
         //loading Bitcoin crypto library
413
         if ($marscoinECDSA->checkSignatureForMessage($addressM, $signatureM, $messageM))       //verifying signature
×
414
         {
415
             Log::debug("True");
×
416
             echo "True<br>";
×
417
         }else{
418
             Log::debug("False");
×
419
             echo "False<br>";
×
420
         }
421
    }
422

423

424

425
    //token access
426
    public function scitizen(Request $request)
×
427
        {
428
                
429
        $uid = Auth::user()->id;
×
430
        $citcache = Citizen::where('userid', '=', $uid)->first();
×
431
        if(is_null($citcache)) $citcache = new Citizen;        
×
432
        $citcache->userid = $uid;
×
433
        
434
        $firstname = $request->input('firstname');
×
435
        $lastname = $request->input('lastname');
×
436
        $shortbio = $request->input('shortbio');
×
437
        $displayname = $request->input('displayname');
×
438
        
439
        if(isset($firstname) && isset($lastname))
×
440
        {
441
            $fullname = $firstname . " " . $lastname;
×
442
            
443
            $citcache->firstname = $firstname;
×
444
            $citcache->lastname = $lastname;
×
445
            $user = User::where('id', '=', $uid)->first();
×
446
            $user->fullname = $fullname;
×
447
            $user->save();
×
448
            $profile = Profile::where('userid', '=', $uid)->first();
×
449
            $profile->has_application = 1;
×
450
            $profile->save();
×
451
        }
452
        if(isset($shortbio))
×
453
        {
454
            $citcache->shortbio = $shortbio;
×
455
            $profile = Profile::where('userid', '=', $uid)->first();
×
456
            $profile->has_application = 1;
×
457
            $profile->save();
×
458
        }
459
        if(isset($displayname))
×
460
        {
461
            $citcache->displayname = $displayname;
×
462
            $profile = Profile::where('userid', '=', $uid)->first();
×
463
            $profile->has_application = 1;
×
464
            $profile->save();
×
465
        }
466
        
467
        $citcache->save();
×
468
        // Fetch profile data
469
        $profile = Profile::where('userid', '=', $uid)->first();
×
470

471
        // Merge citizen and profile data
472
        $response = [
×
473
            'citizen' => $citcache,
×
474
            'profile' => [
×
475
                'citizen' => $profile->citizen ?? null,
×
476
                'endorse_cnt' => $profile->endorse_cnt ?? null,
×
477
                'general_public' => $profile->general_public ?? null,
×
478
                'has_application' => $profile->has_application ?? null,
×
479
            ]
×
480
        ];
×
481

482
        // Return the merged data as a JSON response
483
        return response()->json($response);
×
484
        }
485

486

487
    public function pinpic(Request $request)
×
488
    {
489
                $uid = Auth::user()->id;
×
490
        $hash = "";
×
491
        $dataPic = $request->input('picture');
×
492
        $type = $request->input('type');
×
493
        $public_address = $request->input('address');
×
494
        $file_path = "./assets/citizen/" . $public_address . "/";
×
495
        if (!file_exists($file_path)) {
×
496
            mkdir($file_path);
×
497
        }
498
        list($type, $dataPic) = explode(';', $dataPic);
×
499
        list(, $type) = explode('/', $type);
×
500
        $file_path = "./assets/citizen/" . $public_address . "/profile_pic." . $type;
×
501
        //if (!file_exists($file_path)) { overwrite by default
502
        
503
        list(, $dataPic) = explode(',', $dataPic);
×
504
        $dataPic = base64_decode($dataPic);
×
505
        file_put_contents($file_path, $dataPic);
×
506
        $hash = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
507

508
        $citcache = Citizen::where('userid', '=', $uid)->first();
×
509
        if(is_null($citcache)) $citcache = new Citizen;        
×
510
        $citcache->userid = $uid;
×
511
        $citcache->avatar_link = "https://ipfs.marscoin.org/ipfs/".$hash;
×
512
        $citcache->save();
×
513

514
        return (new Response(json_encode(array("Hash" => $hash)), 200))
×
515
            ->header('Content-Type', "application/json;");
×
516

517
        }
518

519

520

521
        public function pinvideo(Request $request){
×
522

523
        Log::debug('pinvid');
×
524
        $uid = Auth::user()->id;
×
525
        $hash = "";
×
526
        $dataPic = $request->input('file');
×
527
        $type = $request->input('type');
×
528
        $public_address = $request->input('address');
×
529
        Log::debug('public: ' . $public_address);
×
530
        if ($request->hasFile('file'))
×
531
        {
532
            Log::debug('storing file');
×
533
            $file_path = "./assets/citizen/" . $public_address . "/";
×
534
            if (!file_exists($file_path)) {
×
535
                mkdir($file_path);
×
536
                Log::debug('making dir');
×
537
            }
538
            $file_path = "./assets/citizen/" . $public_address  . "/";
×
539
            $request->file('file')->move($file_path, "profile_video.webm" );
×
540
            $file_path = $file_path . "profile_video.webm";
×
541
            $hash = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
542
            Log::debug('upload complete: ' . $hash);
×
543
            $citcache = Citizen::where('userid', '=', $uid)->first();
×
544
            if(is_null($citcache)) $citcache = new Citizen;        
×
545
            $citcache->userid = $uid;
×
546
            $citcache->liveness_link = "https://ipfs.marscoin.org/ipfs/".$hash;
×
547
            $citcache->save();
×
548
            Log::debug('saved cit data');
×
549
            return (new Response(json_encode(array("Hash" => $hash)), 200))
×
550
            ->header('Content-Type', "application/json;");
×
551
        }else{
552
            Log::debug('no file found!');
×
553
        }
554
                        
555
        }
556

557
    public function marsAuth(Request $request)
×
558
    {
559
        $this->setCorsHeaders();
×
560

561
        if (!$request->isMethod('post')) {
×
562
            return $this->fastcode(400, "bad request");
×
563
        }
564

565
        $challenge = $request->query('c');
×
566
        $session_string = $request->query('sid');
×
567
        $timestamp = hexdec($request->query('t'));
×
568
        $pubk = $request->input('pubk');
×
569
        $msg = $request->input('msg');
×
570
        $sig = $request->input('sig');
×
571
        $address = $request->input('addr');
×
572

573
        if ($timestamp < (time() - 600)) {
×
574
            return $this->fastcode(408, "request timed out");
×
575
        }
576

577
        $session = MSession::where('sid', $session_string)->first();
×
578

579
        if ($session && in_array($challenge, explode(",", $session->v))) {
×
580
            $marscoinECDSA = new MarscoinECDSA();
×
581
            if ($marscoinECDSA->checkSignatureForMessage($address, $sig, $msg)) {
×
582
                $session->s = $address;
×
583
                $session->save();
×
584
                return $this->fastcode(200, "successfully authenticated");
×
585
            } else {
586
                return $this->fastcode(406, "couldn't verify message");
×
587
            }
588
        } else {
589
            return $this->fastcode(404, "no challenge found");
×
590
        }
591
    }
592

593
    public function checkAuth(Request $request)
×
594
    {
595
        $session_string = $request->query('sid');
×
596
        $session = MSession::where('sid', $session_string)->first();
×
597
    
598
        $authenticated = (!is_null($session) && !empty($session->s)) ? true : false; 
×
599
    
600
        return response()->json(['sid' => $session_string, 'authenticated' => $authenticated]);
×
601
    }
602
    
603
    private function fastcode($status, $message)
×
604
    {
605
        return response()->json(["status" => $status, "message" => $message], $status);
×
606
    }
607

608
    private function setCorsHeaders()
×
609
    {
610
        header("Access-Control-Allow-Origin: *");
×
611
        header("Access-Control-Allow-Methods: GET, POST");
×
612
        header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");
×
613
    }
614

615
    public function wauth(Request $request)
×
616
    {
617
        Log::debug('MarsAuth');
×
618
        $sid = $request->query('sid');
×
619
        Log::debug('MarsAuth');
×
620

621
        if (!empty($sid)) {
×
622
            $mars_session = MSession::where('sid', $sid)->first(); // Retrieve the session using Eloquent
×
623

624
            if ($mars_session && !is_null($mars_session->s) && !empty($mars_session->s)) {
×
625
                // Attempt to retrieve a user via the public address
626
                $citizen = Citizen::where('public_address', $mars_session->s)->first();
×
627

628
                if ($citizen) {
×
629
                    // If a citizen record exists, retrieve the associated user
630
                    $user = User::find($citizen->userid);
×
631
                } else {
632
                    // Create a new user and citizen if no citizen record exists
633
                    $user = User::create([
×
634
                        'email' => $mars_session->s . '@martianrepublic.org',
×
635
                        'password' => Hash::make(Str::random(16)), // Generate a random password
×
636
                    ]);
×
637

638
                    $citizen = new Citizen([
×
639
                        'userid' => $user->id,
×
640
                        'firstname' => 'Unknown', // Set default or use input
×
641
                        'lastname' => 'Uknown',
×
642
                        'public_address' => $mars_session->s,
×
643
                    ]);
×
644

645
                    $citizen->save();
×
646
                }
647

648
                Auth::login($user); // Authenticate the user
×
649
                session()->save(); // Ensure the session is saved immediately
×
650

651
                Log::debug('User authenticated immediately after login: ' . (Auth::check() ? 'true' : 'false'));
×
652
                return redirect('/wallet/dashboard');
×
653
            }
654

655
            return redirect('/login')->withErrors('Could not find User.');
×
656
        } else {
657
            // Handle invalid or expired SID
658
            return redirect('/login')->withErrors('Your session has expired or is invalid.');
×
659
        }
660
    }
661

662

663
    /**
664
         * Handles JSON storage and pinning to a distributed file system.
665
         *
666
         * @hideFromAPIDocumentation
667
         */
668
        public function pinjson(Request $request)
×
669
        {
670
                Log::info("in function");
×
671
                $public_address = $request->input('address');
×
672
                $type = $request->input('type');
×
673
                $json = $request->input('payload');
×
674
                $projectRoot = config('app.project_root', base_path());
×
675
                $base_path =  $projectRoot . "/assets/citizen/" . $public_address;
×
676

677
                // Check and create the directory if it doesn't exist
678
                Log::info($base_path);
×
679
                clearstatcache();
×
680
                if (!is_dir($base_path)) {
×
681
                        Log::info("Trying to create directory: " . $base_path);
×
682
                        if (!mkdir($base_path, 0755, true)) {
×
683
                                Log::error("Failed to create directory: " . $base_path);
×
684
                                return response()->json(["error" => "Failed to create directory. Check permissions."], 500);
×
685
                        }
686
                        Log::info("Directory created: " . $base_path);
×
687
                }
688
                
689
                // Check if the directory is writable, regardless of whether it was just created or already existed
690
                if (!is_writable($base_path)) {
×
691
                        Log::error("Directory not writable: " . $base_path);
×
692
                        return response()->json(["error" => "Directory is not writable. Check permissions."], 500);
×
693
                }
694

695
                $file_path = $base_path . "/" . $type . ".json";
×
696

697
                // Attempt to write the JSON data to the file
698
                if (file_put_contents($file_path, $json) === false) {
×
699
                        return response()->json(["error" => "Failed to write to file."], 500);
×
700
                }
701

702
                try {
703
                        Log::info("PermaJson: " . $file_path);
×
704
                
705
                        // Check if the type contains the word 'log'
706
                        if (strpos($type, 'log') !== false) {
×
707
                                // The type contains 'log', use uploadFolder
708
                                $apiResponse = AppHelper::uploadFolder($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true&recursive=true&wrap-with-directory=true&quieter");
×
709
                        } else {
710
                                // The type does not contain 'log', use upload
711
                                $apiResponse = AppHelper::upload($file_path, "http://127.0.0.1:5001/api/v0/add?pin=true");
×
712
                        }
713

714
                        if (is_string($apiResponse)) {
×
715
                                $formattedResponse = ['Hash' => $apiResponse];
×
716
                        } else {
717
                                Log::error("Upload error: Formatting");
×
718
                                return response()->json(["error"=>"formatting error"], 500);
×
719
                        }
720
                        
721
                        return response()->json($formattedResponse, 200)->header('Content-Type', "application/json;");
×
722
                } catch (\Exception $e) {
×
723
                        // Handle exceptions during the upload and pinning process
724
                        Log::error("Upload error: " . $e->getMessage());
×
725
                        return response()->json(["error" => $e->getMessage()], 500);
×
726
                }
727
        }
728

729
    public function getThreadsByCategory($categoryId) {
×
730
        $threads = $this->fetchThreads($categoryId);
×
731
        return response()->json(['threads' => $threads]);
×
732
    }
733
    
734
    // private function fetchThreads($categoryId) {
735
    //     $threads = DB::table('forum_threads')
736
    //         ->where('forum_threads.category_id', $categoryId)
737
    //         ->leftJoin('users', 'forum_threads.author_id', '=', 'users.id')
738
    //         ->leftJoin('profile', 'users.id', '=', 'profile.userid')
739
    //         ->select(
740
    //             'forum_threads.id',
741
    //             'forum_threads.title',
742
    //             'forum_threads.created_at',
743
    //             'forum_threads.reply_count',
744
    //             'users.fullname as author_name'
745
    //         )
746
    //         ->orderBy('forum_threads.created_at', 'desc')
747
    //         ->get();
748
    //     return $threads;
749
    // }
750

751
    private function fetchThreads($categoryId) {
×
752
        $userId = Auth::id();
×
753
    
754
        $threads = DB::table('forum_threads')
×
755
            ->where('forum_threads.category_id', $categoryId)
×
756
            ->leftJoin('users', 'forum_threads.author_id', '=', 'users.id')
×
757
            ->leftJoin('profile', 'users.id', '=', 'profile.userid')
×
758
            ->leftJoin('user_blocks as ub', function($join) use ($userId) {
×
759
                $join->on('forum_threads.author_id', '=', 'ub.blocked_user_id')
×
760
                     ->where('ub.user_id', '=', $userId);
×
761
            })
×
762
            ->select(
×
763
                'forum_threads.id',
×
764
                'forum_threads.title',
×
765
                'forum_threads.created_at',
×
766
                'forum_threads.reply_count',
×
767
                'users.fullname as author_name',
×
768
                DB::raw('IF(ub.blocked_user_id IS NOT NULL, true, false) as is_blocked')
×
769
            )
×
770
            ->orderBy('forum_threads.created_at', 'desc')
×
771
            ->get();
×
772
    
773
        return $threads;
×
774
    }
775

776

777
    public function getThreadComments($threadId) {
×
778
        // Assume $threadId is passed correctly to the function
779
        $comments = $this->fetchCommentsByThread($threadId);
×
780
        return response()->json(['comments' => $comments]);
×
781
    }
782
    
783
    // private function fetchCommentsByThread($threadId) {
784
    //     // The query as outlined above
785
    //     // Return the collection of comments
786
    //     $query = "
787
    //         WITH RECURSIVE CommentTree AS (
788
    //             SELECT 
789
    //                 p.id,
790
    //                 p.thread_id,
791
    //                 p.author_id,
792
    //                 p.content,
793
    //                 p.post_id as pid,
794
    //                 p.created_at,
795
    //                 CHAR_LENGTH(p.content) as char_length_sum
796
    //             FROM 
797
    //                 forum_posts p
798
    //             WHERE 
799
    //                 p.thread_id = ? AND p.post_id IS NULL
800

801
    //             UNION ALL
802

803
    //             SELECT 
804
    //                 p.id,
805
    //                 p.thread_id,
806
    //                 p.author_id,
807
    //                 p.content,
808
    //                 p.post_id,
809
    //                 p.created_at,
810
    //                 ct.char_length_sum + CHAR_LENGTH(p.content)
811
    //             FROM 
812
    //                 forum_posts p
813
    //             INNER JOIN 
814
    //                 CommentTree ct ON p.post_id = ct.id
815
    //         )
816
    //         SELECT 
817
    //             ct.id,
818
    //             ct.thread_id,
819
    //             ct.author_id,
820
    //             u.fullname,
821
    //             ct.content,
822
    //             ct.created_at,
823
    //             ct.pid,
824
    //             CHAR_LENGTH(ct.content) as char_length_sum
825
    //         FROM 
826
    //             CommentTree ct
827
    //         LEFT JOIN users u ON ct.author_id = u.id
828
    //         LEFT JOIN profile pr ON ct.author_id = pr.userid
829
    //         ORDER BY 
830
    //             ct.pid ASC,
831
    //             ct.created_at ASC;
832
    //     ";
833

834
    //     $comments = DB::select($query, [$threadId]);
835
    //     $commentsCollection = collect($comments);
836

837
    //     return response()->json(['comments' => $commentsCollection]);
838
    // }
839

840
    private function fetchCommentsByThread($threadId) {
×
841
        $userId = Auth::id();
×
842
        
843
        $query = "
×
844
            WITH RECURSIVE CommentTree AS (
845
                SELECT 
846
                    p.id,
847
                    p.thread_id,
848
                    p.author_id,
849
                    p.content,
850
                    p.post_id as pid,
851
                    p.created_at,
852
                    CHAR_LENGTH(p.content) as char_length_sum
853
                FROM 
854
                    forum_posts p
855
                WHERE 
856
                    p.thread_id = ? AND p.post_id IS NULL
857
    
858
                UNION ALL
859
    
860
                SELECT 
861
                    p.id,
862
                    p.thread_id,
863
                    p.author_id,
864
                    p.content,
865
                    p.post_id,
866
                    p.created_at,
867
                    ct.char_length_sum + CHAR_LENGTH(p.content)
868
                FROM 
869
                    forum_posts p
870
                INNER JOIN 
871
                    CommentTree ct ON p.post_id = ct.id
872
            )
873
            SELECT 
874
                ct.id,
875
                ct.thread_id,
876
                ct.author_id,
877
                u.fullname,
878
                ct.content,
879
                ct.created_at,
880
                ct.pid,
881
                CHAR_LENGTH(ct.content) as char_length_sum,
882
                IF(ub.blocked_user_id IS NOT NULL, true, false) as is_blocked
883
            FROM 
884
                CommentTree ct
885
            LEFT JOIN users u ON ct.author_id = u.id
886
            LEFT JOIN profile pr ON ct.author_id = pr.userid
887
            LEFT JOIN user_blocks ub ON ub.blocked_user_id = ct.author_id AND ub.user_id = ?
888
            ORDER BY 
889
                ct.pid ASC,
890
                ct.created_at ASC;
891
        ";
×
892
    
893
        $comments = DB::select($query, [$threadId, $userId]);
×
894
        $commentsCollection = collect($comments);
×
895
        return response()->json(['comments' => $commentsCollection]);
×
896
    }
897
    
898

899
    public function getAllCategoriesWithThreads() {
×
900
        $categories = DB::table('forum_categories')
×
901
            ->get();
×
902
    
903
        foreach ($categories as $category) {
×
904
            $category->threads = $this->fetchThreads($category->id);
×
905
        }
906
    
907
        return response()->json(['categories' => $categories]);
×
908
    }
909

910

911
    public function createThread(Request $request)
×
912
    {
913
        $request->validate([
×
914
            'category_id' => 'required|exists:forum_categories,id',
×
915
            'title' => 'required|string|max:255',
×
916
            'content' => 'required|string',
×
917
        ]);
×
918

919
        $thread = new Threads();
×
920
        $thread->category_id = $request->category_id;
×
921
        $thread->author_id = Auth::id();
×
922
        $thread->title = $request->title;
×
923
        $thread->save();
×
924

925
        $post = new Posts();
×
926
        $post->thread_id = $thread->id;
×
927
        $post->author_id = Auth::id();
×
928
        $post->content = $request->content;
×
929
        $post->save();
×
930

931
        $thread->first_post_id = $post->id;
×
932
        $thread->last_post_id = $post->id;
×
933
        $thread->reply_count = 0;
×
934
        $thread->save();
×
935

936
        return response()->json([
×
937
            'message' => 'Thread created successfully',
×
938
            'thread_id' => $thread->id,
×
939
            'post_id' => $post->id,
×
940
        ], 201);
×
941
    }
942

943
    public function createComment(Request $request, $threadId)
×
944
    {
945
        $request->validate([
×
946
            'content' => 'required|string',
×
947
            'post_id' => 'nullable|exists:forum_posts,id',
×
948
        ]);
×
949

950
        $thread = Threads::findOrFail($threadId);
×
951

952
        $post = new Posts();
×
953
        $post->thread_id = $threadId;
×
954
        $post->author_id = Auth::id();
×
955
        $post->content = $request->content;
×
956
        $post->post_id = $request->post_id; // This will be null for top-level comments
×
957
        $post->save();
×
958

959
        $thread->last_post_id = $post->id;
×
960
        $thread->reply_count += 1;
×
961
        $thread->save();
×
962

963
        return response()->json([
×
964
            'message' => 'Comment created successfully',
×
965
            'post_id' => $post->id,
×
966
        ], 201);
×
967
    }
968

969

970
    public function blockUser(Request $request, $id) 
×
971
    {
972
        Log::debug("in function"); 
×
973
        $uid = Auth::user()->id;
×
974
        Log::debug("auth happened");
×
975
        $blockedUserId = $id; 
×
976
    
977
        // Insert into `user_blocks` table if not already blocked
978
        DB::table('user_blocks')->updateOrInsert(
×
979
            ['user_id' => $uid, 'blocked_user_id' => $blockedUserId]
×
980
        );
×
981
    
982
        return response()->json(['message' => 'User blocked successfully'], 201);
×
983
    }
984

985
    public function handleEula(Request $request)
×
986
    {
987
        $uid = Auth::user()->id;
×
988
        $profile = Profile::where('userid', $uid)->firstOrFail();
×
989
        
990
        return response()->json([
×
991
            'is_signed' => (bool)$profile->signed_eula
×
992
        ]); 
×
993
    }
994

995
    public function setEula(Request $request)
×
996
    {
997
        $uid = Auth::user()->id;
×
998
        $profile = Profile::where('userid', $uid)->firstOrFail();
×
999
        if (!$profile->signed_eula) {
×
1000
            $profile->signed_eula = 1;
×
1001
            $profile->save();
×
1002
        }
1003
        return response()->json([
×
1004
            'message' => 'EULA signed successfully',
×
1005
            'is_signed' => true
×
1006
        ]);
×
1007
    }
1008

1009

1010
    // App/Http/Controllers/ApiController.php
1011
    public function deleteUser(Request $request, $id)
×
1012
    {
1013
        try {
1014
            // Find the user
1015
            $user = User::findOrFail($id);
×
1016
            
1017
            
1018
            Log::debug("Deleting... ". $id);
×
1019
           
1020
            $user->update([
×
1021
                'status' => 'inactive'
×
1022
            ]);
×
1023
            Log::debug("Status updated");
×
1024
            
1025
            // Revoke all tokens
1026
            if (method_exists($user, 'tokens')) {
×
1027
                $user->tokens()->delete();
×
1028
            }
1029
            Log::debug("Token wiped...");
×
1030

1031
            DB::commit();
×
1032
            
1033
            return response()->json([
×
1034
                'message' => 'User deleted successfully'
×
1035
            ], 200);
×
1036
            
1037
            
1038
        } catch (ModelNotFoundException $e) {
×
1039
            return response()->json([
×
1040
                'message' => 'User not found'
×
1041
            ], 404);
×
1042
        } catch (\Exception $e) {
×
1043
            \Log::error('Error deleting user: ' . $e->getMessage());
×
1044
            return response()->json([
×
1045
                'message' => 'An error occurred while deleting the user'
×
1046
            ], 500);
×
1047
        }
1048
    }
1049

1050

1051

1052

1053
}
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

© 2025 Coveralls, Inc