Skip to content

Commit 00f6208

Browse files
Merge pull request #18 from luadotsh/mobile-links
feat: added link targeting for ios and android
2 parents 8cffde4 + 271ce2d commit 00f6208

24 files changed

+639
-270
lines changed

.env.ci

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
APP_ENV=testing
2+
APP_URL=https://lua.sh
3+
APP_KEY=base64:Zfo+2dkSaHvem5LCiZS/baYHv2Pv1vrSc0F2NaF29Ec=
4+
5+
LOG_CHANNEL=stack
6+
LOG_DEPRECATIONS_CHANNEL=null
7+
LOG_LEVEL=debug
8+
9+
BROADCAST_DRIVER=log
10+
CACHE_DRIVER=file
11+
FILESYSTEM_DISK=local
12+
QUEUE_CONNECTION=sync
13+
SESSION_DRIVER=file
14+
SESSION_LIFETIME=1440
15+
16+
MAIL_MAILER=log
17+
18+
# Stripe
19+
STRIPE_KEY=
20+
STRIPE_SECRET=
21+
STRIPE_WEBHOOK_SECRET=
22+
23+
# Sentry
24+
SENTRY_LARAVEL_DSN=
25+
SENTRY_TRACES_SAMPLE_RATE=1.0
26+
SENTRY_AUTH_TOKEN=
27+
SENTRY_ENVIRONMENT=
28+
29+
TELESCOPE_ENABLED=false
30+
31+
# Vite
32+
VITE_STRIPE_KEY="${STRIPE_KEY}"
33+
VITE_SENTRY_AUTH_TOKEN="${SENTRY_AUTH_TOKEN}"
34+
VITE_SENTRY_DSN_PUBLIC="${SENTRY_LARAVEL_DSN}"
35+
VITE_SENTRY_ENVIRONMENT="${SENTRY_ENVIRONMENT}"
36+
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
37+
VITE_PUSHER_HOST="${PUSHER_HOST}"
38+
VITE_PUSHER_PORT="${PUSHER_PORT}"
39+
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
40+
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

.github/workflows/backend-tests.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ jobs:
6969
run: php artisan config:clear
7070

7171
- name: Run Migration
72-
run: php artisan migrate -v
72+
run: php artisan migrate:fresh --seed -v
7373
env:
7474
DB_PORT: ${{ job.services.mysql.ports['3306'] }}
7575
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
7676

7777
- name: Run PHP Unit Tests
78-
run: vendor/bin/phpunit
78+
run: vendor/bin/pest
7979
env:
8080
DB_PORT: ${{ job.services.mysql.ports['3306'] }}
8181
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

.github/workflows/commitlint.yml

-14
This file was deleted.

app/Enums/Link/Os.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Enums\Link;
6+
7+
enum Os: string
8+
{
9+
case ANDROID = 'Android';
10+
case IOS = 'iOS';
11+
case WINDOWS = 'Windows';
12+
case MACOS = 'MacOS';
13+
case LINUX = 'Linux';
14+
case UNKNOWN = 'Unknown OS';
15+
}

app/Http/Controllers/Api/LinkController.php

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public function store(CreateRequest $request)
4949
'key' => $request->key,
5050
'url' => $request->url,
5151
'link' => "https://{$request->domain}/{$request->key}",
52+
'ios' => $request->ios,
53+
'android' => $request->android,
5254
'utm_source' => $request->utm_source,
5355
'utm_medium' => $request->utm_medium,
5456
'utm_campaign' => $request->utm_campaign,
@@ -75,6 +77,8 @@ public function update($id, UpdateRequest $request)
7577
'key' => $request->key,
7678
'url' => $request->url,
7779
'link' => "https://{$request->domain}/{$request->key}",
80+
'ios' => $request->ios,
81+
'android' => $request->android,
7882
'utm_source' => $request->utm_source,
7983
'utm_medium' => $request->utm_medium,
8084
'utm_campaign' => $request->utm_campaign,

app/Http/Controllers/LinkController.php

+4
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public function store(CreateRequest $request)
7878
'domain' => $request->domain,
7979
'key' => $key,
8080
'url' => $request->url,
81+
'ios' => $request->ios,
82+
'android' => $request->android,
8183
'link' => "https://{$request->domain}/{$key}",
8284
'utm_source' => $request->utm_source,
8385
'utm_medium' => $request->utm_medium,
@@ -108,6 +110,8 @@ public function update($id, UpdateRequest $request)
108110
'key' => $key,
109111
'url' => $request->url,
110112
'link' => "https://{$request->domain}/{$key}",
113+
'ios' => $request->ios,
114+
'android' => $request->android,
111115
'utm_source' => $request->utm_source,
112116
'utm_medium' => $request->utm_medium,
113117
'utm_campaign' => $request->utm_campaign,

app/Http/Controllers/RedirectController.php

+22-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
namespace App\Http\Controllers;
66

7+
use App\Services\UserAgentService;
8+
79
use Illuminate\Support\Facades\Gate;
810
use Illuminate\Http\RedirectResponse;
911
use Illuminate\Http\Request;
1012

13+
use App\Enums\Link\Os;
14+
1115
use App\Jobs\ProcessLinkStat;
1216

1317
use App\Models\Link;
@@ -20,7 +24,6 @@ public function __invoke($key, Request $request): RedirectResponse
2024
->with('workspace')
2125
->firstOrFail();
2226

23-
2427
$reachEventLimit = Gate::inspect('reached-event-limit', $link->workspace);
2528

2629
ProcessLinkStat::dispatchIf(
@@ -32,6 +35,24 @@ public function __invoke($key, Request $request): RedirectResponse
3235
$request->input('qr') ? true : false,
3336
$request->header('Referer')
3437
);
38+
39+
/**
40+
* If the link has an iOS or Android redirect URL, we need to check the user's OS
41+
* and redirect to the appropriate URL.
42+
*/
43+
if ($link->ios || $link->android) {
44+
$service = new UserAgentService();
45+
$os = $service->getOS($request->userAgent());
46+
47+
if ($os === Os::IOS->value && $link->ios) {
48+
return redirect($link->ios, 302);
49+
}
50+
51+
if ($os === Os::ANDROID->value && $link->android) {
52+
return redirect($link->android, 302);
53+
}
54+
}
55+
3556
return redirect($link->url, 302);
3657
}
3758
}

app/Http/Requests/Link/CreateRequest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public function rules(): array
4040
Rule::unique('links')->where('key', $this->key)->ignore($this->route('id')),
4141
],
4242
'url' => ['required', 'url', 'max:255', 'min:2'],
43-
43+
'ios' => ['nullable', 'url', 'max:255', 'min:2'],
44+
'android' => ['nullable', 'url', 'max:255', 'min:2'],
4445
'utm_source' => Rule::when(
4546
fn() => $this->utm_source,
4647
['required', 'string', 'max:255', 'min:2']

app/Http/Requests/Link/UpdateRequest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ public function rules(): array
4040
Rule::unique('links')->where('key', $this->key)->ignore($this->route('id')),
4141
],
4242
'url' => ['required', 'url', 'max:255', 'min:2'],
43-
43+
'ios' => ['nullable', 'url', 'max:255', 'min:2'],
44+
'android' => ['nullable', 'url', 'max:255', 'min:2'],
4445
'utm_source' => Rule::when(
4546
fn() => $this->utm_source,
4647
['required', 'string', 'max:255', 'min:2']

app/Http/Resources/Api/LinkResource.php

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public function toArray(Request $request): array
2020
'domain' => $this->domain,
2121
'key' => $this->key,
2222
'url' => $this->url,
23+
'ios' => $this->ios,
24+
'android' => $this->android,
2325
'link' => $this->link,
2426
'utm_source' => $this->utm_source,
2527
'utm_medium' => $this->utm_medium,

app/Jobs/ProcessLinkStat.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public function handle(): void
4040
// user geo
4141
$geo = geoip($this->ip);
4242

43-
$linkStat = LinkStat::create([
43+
LinkStat::create([
4444
'workspace_id' => $this->link->workspace_id,
4545
'link_id' => $this->link->id,
4646
'event' => $this->qr ? Event::QR_SCAN : Event::CLICK,

app/Models/Link.php

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
1212
use Illuminate\Database\Eloquent\Relations\HasMany;
1313

14+
use App\Enums\Link\Os;
15+
1416
class Link extends Model
1517
{
1618
use HasFactory;
@@ -27,6 +29,8 @@ class Link extends Model
2729
'key',
2830
'url',
2931
'link',
32+
'ios',
33+
'android',
3034
'utm_source',
3135
'utm_medium',
3236
'utm_campaign',
@@ -54,6 +58,7 @@ protected function casts(): array
5458
{
5559
return [
5660
'last_click' => 'datetime',
61+
'os' => Os::class,
5762
];
5863
}
5964

app/Services/UserAgentService.php

+8-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace App\Services;
66

7+
use App\Enums\Link\Os;
8+
79
class UserAgentService
810
{
911
/**
@@ -41,11 +43,11 @@ public function getBrowser(string $userAgent): string
4143
public function getOS(string $userAgent): string
4244
{
4345
$osArray = [
44-
'Windows' => 'Windows',
45-
'MacOS' => '(Mac_PowerPC)|(Macintosh)',
46-
'Linux' => 'Linux',
47-
'iOS' => 'iPhone|iPad',
48-
'Android' => 'Android',
46+
Os::ANDROID->value => 'Android',
47+
Os::IOS->value => 'iPhone|iPad',
48+
Os::WINDOWS->value => 'Windows',
49+
Os::MACOS->value => '(Mac_PowerPC)|(Macintosh)',
50+
Os::LINUX->value => 'Linux',
4951
];
5052

5153
foreach ($osArray as $key => $pattern) {
@@ -54,7 +56,7 @@ public function getOS(string $userAgent): string
5456
}
5557
}
5658

57-
return 'Unknown OS';
59+
return Os::UNKNOWN->value;
5860
}
5961

6062
/**

config/database.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
|
1717
*/
1818

19-
'default' => env('DB_CONNECTION', 'sqlite'),
19+
'default' => env('DB_CONNECTION', 'mysql'),
2020

2121
/*
2222
|--------------------------------------------------------------------------

database/factories/LinkFactory.php

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public function definition(): array
3131
'key' => $slug,
3232
'url' => $this->faker->url,
3333
'link' => "https://{$domain}/{$slug}",
34+
'ios' => $this->faker->url,
35+
'android' => $this->faker->url,
3436
'utm_source' => $this->faker->word,
3537
'utm_medium' => $this->faker->word,
3638
'utm_campaign' => $this->faker->word,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('links', function (Blueprint $table) {
15+
$table->string('ios')->after('link')->nullable();
16+
$table->string('android')->after('ios')->nullable();
17+
});
18+
}
19+
20+
/**
21+
* Reverse the migrations.
22+
*/
23+
public function down(): void
24+
{
25+
Schema::table('links', function (Blueprint $table) {
26+
$table->dropColumn('ios');
27+
$table->dropColumn('android');
28+
});
29+
}
30+
};

0 commit comments

Comments
 (0)