Skip to content

Commit 2270b08

Browse files
authored
Merge pull request #225 from cronxco/plugin-admin
💄 UI and Sessions
2 parents 4263d6b + 516102e commit 2270b08

File tree

15 files changed

+883
-268
lines changed

15 files changed

+883
-268
lines changed

.DS_Store

0 Bytes
Binary file not shown.

VERSION.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

app/Jobs/Outline/OutlinePullRecentDayNotes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Integrations\Outline\OutlineApi;
66
use App\Jobs\Base\BaseFetchJob;
7+
use Carbon\CarbonImmutable;
78

89
class OutlinePullRecentDayNotes extends BaseFetchJob
910
{

app/Models/Session.php

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7+
use Jenssegers\Agent\Agent;
8+
9+
class Session extends Model
10+
{
11+
/**
12+
* Indicates if the IDs are auto-incrementing.
13+
*/
14+
public $incrementing = false;
15+
16+
/**
17+
* Indicates if the model should be timestamped.
18+
*/
19+
public $timestamps = false;
20+
21+
/**
22+
* The table associated with the model.
23+
*/
24+
protected string $table = 'sessions';
25+
26+
/**
27+
* The primary key for the model.
28+
*/
29+
protected $primaryKey = 'id';
30+
31+
/**
32+
* The "type" of the auto-incrementing ID.
33+
*/
34+
protected $keyType = 'string';
35+
36+
/**
37+
* The attributes that should be cast.
38+
*/
39+
protected $casts = [
40+
'last_activity' => 'datetime',
41+
];
42+
43+
/**
44+
* Get the user that owns the session.
45+
*/
46+
public function user(): BelongsTo
47+
{
48+
return $this->belongsTo(User::class);
49+
}
50+
51+
/**
52+
* Get the device information for the session.
53+
*/
54+
public function getDeviceAttribute(): array
55+
{
56+
$agent = new Agent;
57+
$agent->setUserAgent($this->user_agent);
58+
59+
return [
60+
'platform' => $this->getPlatform($agent),
61+
'browser' => $agent->browser(),
62+
'is_desktop' => $agent->isDesktop(),
63+
'is_mobile' => $agent->isMobile(),
64+
'is_tablet' => $agent->isTablet(),
65+
'device_name' => $this->getDeviceName($agent),
66+
];
67+
}
68+
69+
/**
70+
* Get a human-readable device name.
71+
*/
72+
public function getDeviceNameAttribute(): string
73+
{
74+
$device = $this->device;
75+
76+
if ($device['is_mobile']) {
77+
return $device['platform'] . ' Mobile - ' . $device['browser'];
78+
}
79+
80+
if ($device['is_tablet']) {
81+
return $device['platform'] . ' Tablet - ' . $device['browser'];
82+
}
83+
84+
return $device['platform'] . ' Desktop - ' . $device['browser'];
85+
}
86+
87+
/**
88+
* Check if this is the current session.
89+
*/
90+
public function getIsCurrentAttribute(): bool
91+
{
92+
return $this->id === session()->getId();
93+
}
94+
95+
/**
96+
* Get the last activity in human readable format.
97+
*/
98+
public function getLastActivityHumanAttribute(): string
99+
{
100+
return $this->last_activity->diffForHumans();
101+
}
102+
103+
/**
104+
* Get the location information (if available).
105+
*/
106+
public function getLocationAttribute(): ?string
107+
{
108+
// You could integrate with a GeoIP service here
109+
// For now, we'll just return the IP address
110+
return $this->ip_address;
111+
}
112+
113+
/**
114+
* Scope to get sessions for a specific user.
115+
*/
116+
public function scopeForUser($query, $userId)
117+
{
118+
return $query->where('user_id', $userId);
119+
}
120+
121+
/**
122+
* Scope to get active sessions (recent activity).
123+
*/
124+
public function scopeActive($query, $minutes = 60)
125+
{
126+
return $query->where('last_activity', '>=', now()->subMinutes($minutes)->timestamp);
127+
}
128+
129+
/**
130+
* Invalidate this session.
131+
*/
132+
public function invalidate(): bool
133+
{
134+
return $this->delete();
135+
}
136+
137+
/**
138+
* Get the platform name from the user agent.
139+
*/
140+
protected function getPlatform(Agent $agent): string
141+
{
142+
if ($agent->isAndroidOS()) {
143+
return 'Android';
144+
}
145+
146+
if ($agent->isIOS()) {
147+
return 'iOS';
148+
}
149+
150+
if ($agent->isMac()) {
151+
return 'macOS';
152+
}
153+
154+
if ($agent->isWindows()) {
155+
return 'Windows';
156+
}
157+
158+
if ($agent->isLinux()) {
159+
return 'Linux';
160+
}
161+
162+
return $agent->platform() ?: 'Unknown';
163+
}
164+
165+
/**
166+
* Get a more specific device name.
167+
*/
168+
protected function getDeviceName(Agent $agent): string
169+
{
170+
$platform = $this->getPlatform($agent);
171+
$browser = $agent->browser();
172+
173+
if ($agent->isMobile()) {
174+
return "{$platform} Mobile";
175+
}
176+
177+
if ($agent->isTablet()) {
178+
return "{$platform} Tablet";
179+
}
180+
181+
return "{$platform} Desktop";
182+
}
183+
}

app/Models/User.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ public function events()
8787
return $this->hasMany(Event::class);
8888
}
8989

90+
/**
91+
* Get the user's sessions
92+
*/
93+
public function sessions()
94+
{
95+
return $this->hasMany(Session::class)->orderBy('last_activity', 'desc');
96+
}
97+
9098
/**
9199
* Get the attributes that should be cast.
92100
*

app/View/Components/AppBrand.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,15 @@ public function render(): View|Closure|string
2424
return <<<'HTML'
2525
<a href="/" wire:navigate>
2626
<!-- Hidden when collapsed -->
27-
<div {{ $attributes->class(["hidden-when-collapsed"]) }}>
27+
<div class="flex items-center gap-2 block lg:hidden align-middle">
2828
<div class="flex items-center gap-2 w-fit">
29-
<x-icon name="o-cube" class="w-6 -mb-1.5 text-purple-500" />
30-
<span class="font-bold text-3xl me-3 bg-gradient-to-r from-purple-500 to-pink-300 bg-clip-text text-transparent ">
31-
app
32-
</span>
29+
<x-app-logo class="w-20 -mb-1"/>
3330
</div>
3431
</div>
3532
3633
<!-- Display when collapsed -->
37-
<div class="display-when-collapsed hidden mx-5 mt-5 mb-1 h-[28px]">
38-
<x-icon name="s-cube" class="w-6 -mb-1.5 text-purple-500" />
34+
<div class="flex items-center gap-2 hidden lg:block lg:inline-block align-middle">
35+
<x-app-logo class="w-26 mt-1 -mb-1"/>
3936
</div>
4037
</a>
4138
HTML;

composer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"license": "MIT",
1111
"require": {
1212
"php": "^8.2",
13+
"jenssegers/agent": "^2.6",
1314
"laravel/framework": "^12.0",
1415
"laravel/horizon": "^5.30.3",
1516
"laravel/sanctum": "^4.0",
@@ -20,7 +21,7 @@
2021
"pgvector/pgvector": "^0.2.2",
2122
"pragmarx/version": "dev-master",
2223
"predis/predis": "^3.2",
23-
"robsontenorio/mary": "^2.4",
24+
"robsontenorio/mary": "dev-main",
2425
"sentry/sentry-laravel": "^4.9",
2526
"socialiteproviders/authelia": "^4.0",
2627
"spatie/laravel-activitylog": "^4",
@@ -104,6 +105,10 @@
104105
"type": "vcs",
105106
"url": "git@github.com:cronxco/graby.git"
106107
},
108+
{
109+
"type": "vcs",
110+
"url": "git@github.com:cronxco/mary.git"
111+
},
107112
{
108113
"type": "vcs",
109114
"url": "git@github.com:willscottuk/monzo-php.git"

0 commit comments

Comments
 (0)