Skip to content

Commit

Permalink
Bugfix multiple links bottleneck (#103)
Browse files Browse the repository at this point in the history
* Bugfix for bottleneck about Multiple Links generation

* Database edits

* Added .keep file in Unit Test Folder
  • Loading branch information
optiroot committed Feb 23, 2021
1 parent c6f836d commit 5d0dead
Show file tree
Hide file tree
Showing 16 changed files with 207 additions and 107 deletions.
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
dist: trusty
dist: focal
sudo: false
language: php
php:
- 7.4.0
- 7.4

services:
- mysql

cache:
directories:
Expand Down
1 change: 1 addition & 0 deletions app/ClickUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Model that saves and loads data when an User visits a Short URL.
*
* Class ClickUrl
* @method static select(string $string)
*/
class ClickUrl extends Model
{
Expand Down
6 changes: 4 additions & 2 deletions app/Http/Controllers/AnalyticsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use Carbon\Carbon;
use App\Services\Analytics;
use App\Services\UrlService;
use Illuminate\Contracts\View\Factory;
use Illuminate\View\View;

/**
* Class AnalyticsController.
Expand Down Expand Up @@ -46,7 +48,7 @@ public function __construct(UrlService $urlService, Analytics $analytics)
* Show the URL analytics page to user.
*
* @param $url
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function show($url)
{
Expand Down Expand Up @@ -83,7 +85,7 @@ public function show($url)
/**
* Show the referers list to the user.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function showReferrersList()
{
Expand Down
10 changes: 5 additions & 5 deletions app/Http/Controllers/Api/UrlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ public function store(ShortUrl $request)
], 409);
}

$short = $this->url->shortenUrl($data['url'], $data['customUrl'], $data['privateUrl'], $data['hideUrlStats']);
$url = $this->url->shortenUrl($data['url'], $data['customUrl'], $data['privateUrl'], $data['hideUrlStats']);

return response()->json([
'message' => 'Success! Short URL created.',
'short_url' => $short,
'short_url' => $url->short_url,
], 200);
}

Expand All @@ -92,7 +92,7 @@ public function store(ShortUrl $request)
*/
public function show(Url $url)
{
Url::findOrFail($url);
Url::where('short_url', $url)->firstOrFail();
$selectStatement = ['long_url', 'short_url'];

if ($this->url->isOwner($url)) {
Expand All @@ -113,7 +113,7 @@ public function show(Url $url)
*/
public function update($url, ShortUrl $request)
{
$url = Url::findOrFail($url);
$url = Url::where('short_url', $url)->firstOrFail();

if (! $this->url->OwnerOrAdmin($url->short_url)) {
abort(403);
Expand Down Expand Up @@ -146,7 +146,7 @@ public function update($url, ShortUrl $request)
*/
public function destroy($url)
{
Url::findOrFail($url);
Url::where('short_url', $url)->firstOrFail();

if (! $this->url->OwnerOrAdmin($url)) {
abort(403);
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/ClickUrlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function click($url)
{
$urlService = new UrlService();

if ($result = Url::findOrFail($url)) {
if ($result = Url::where('short_url', $url)->firstOrFail()) {
$externalUrl = $urlService->getLongUrl($result);
}

Expand Down
34 changes: 25 additions & 9 deletions app/Http/Controllers/QRCodeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
namespace App\Http\Controllers;

use App\Url;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Storage;
use SimpleSoftwareIO\QrCode\Facades\QrCode;

Expand All @@ -23,27 +27,38 @@
class QRCodeController
{
/**
* Retreives the QR Code in svg format for the short URL.
* Retrieves the QR Code in svg format for the short URL.
*
* @param $url
* @return \Illuminate\Http\RedirectResponse
* @param string $shortUrl
* @return Application|ResponseFactory|Response
* @throws FileNotFoundException
*/
public function svg(Url $url)
public function svg(string $shortUrl)
{
$url = Url::where('short_url', $shortUrl)->firstOrFail();
return $this->qrCode($url, 'svg', 'image/svg+xml');
}

/**
* Retreives the QR Code in png format for the short URL.
* Retrieves the QR Code in png format for the short URL.
*
* @param $url
* @return \Illuminate\Http\RedirectResponse
* @param string $shortUrl
* @return Application|ResponseFactory|Response
* @throws FileNotFoundException
*/
public function png(Url $url)
public function png(string $shortUrl)
{
$url = Url::where('short_url', $shortUrl)->firstOrFail();
return $this->qrCode($url, 'png', 'image/png');
}

/**
* @param Url $url
* @param $format
* @param $contentType
* @return Application|ResponseFactory|Response
* @throws FileNotFoundException
*/
private function qrCode(Url $url, $format, $contentType)
{
$path = 'qrcodes/'.$url->short_url.'.'.$format;
Expand All @@ -56,4 +71,5 @@ private function qrCode(Url $url, $format, $contentType)

return response($qrCode)->header('Content-Type', $contentType);
}
}

}
49 changes: 27 additions & 22 deletions app/Http/Controllers/UrlController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
use App\DeletedUrls;
use App\Services\UrlService;
use Hashids\Hashids;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use App\Http\Requests\ShortUrl;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\View\View;
use Yajra\DataTables\DataTables;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Redirect;
Expand Down Expand Up @@ -92,8 +96,8 @@ public function storeMultiple(MultipleUrls $multipleUrls): RedirectResponse
$shortened[] = $shortUrl;
$existing++;
} else {
$short = $this->url->shortenUrl($url, null, $data['privateUrl'], $data['hideUrlStats']);
$shortened[] = $short;
$url = $this->url->shortenUrl($url, null, $data['privateUrl'], $data['hideUrlStats']);
$shortened[] = $url->short_url;
}
}

Expand Down Expand Up @@ -132,13 +136,16 @@ public function store(ShortUrl $request)
->with('siteUrl', $siteUrl);
}

$short = $this->url->shortenUrl($data['url'], $data['customUrl'], $data['privateUrl'], $data['hideUrlStats']);

$hashids = new Hashids(env('APP_KEY'), 4);
if (empty($data['customUrl'])) {
$customUrl = null;
} else {
$customUrl = $data['customUrl'];
}

$short_url_id = $hashids->decode($short)[0] ?? Url::where('short_url', $short)->first()->id;
$url = $this->url->shortenUrl($data['url'], $customUrl, $data['privateUrl'], $data['hideUrlStats']);
$short = $url->short_url;

$this->url->assignDeviceTargetUrl($data, $short_url_id);
$this->url->assignDeviceTargetUrl($data, $url->id);

return Redirect::route('home')
->with('success', $short)
Expand All @@ -150,19 +157,19 @@ public function store(ShortUrl $request)
* This method actually shows the URL edit page. It is not actually "@show" URL. The URL show is in clickUrl@view.
*
* @param $url
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Contracts\View\Factory|\Illuminate\Http\Response|\Illuminate\View\View
* @return ResponseFactory|Factory|Response|View
*/
public function show($url)
{
if (! $this->url->OwnerOrAdmin($url) ) {
abort(403);
}

$short_url = Url::with('user:id,name,email')->findOrFail($url);
$url = Url::with('user:id,name,email')->where('short_url', $url)->firstOrFail();

$targets = $this->url->getTargets($short_url);
$targets = $this->url->getTargets($url);

$data['url'] = $short_url;
$data['url'] = $url;

$data['targets'] = $targets;

Expand All @@ -175,11 +182,11 @@ public function show($url)
* @param $url
* @param ShortUrl $request
*
* @return \Illuminate\Contracts\Routing\ResponseFactory|RedirectResponse|\Illuminate\Http\Response
* @return ResponseFactory|RedirectResponse|Response
*/
public function update($url, ShortUrl $request)
{
$url = Url::findOrFail($url);
$url = Url::where('short_url', $url)->firstOrFail();

if (! $this->url->OwnerOrAdmin($url->short_url)) {
return response('Forbidden', 403);
Expand All @@ -192,8 +199,6 @@ public function update($url, ShortUrl $request)
$url->long_url = $data['url'];
$url->update();

$hashids = new Hashids(env('APP_KEY'), 4);

$url->deviceTargets()->delete();

$this->url->assignDeviceTargetUrl($data, $url->id);
Expand All @@ -206,18 +211,18 @@ public function update($url, ShortUrl $request)
* Delete a Short URL on user request.
*
* @param $url
* @return \Illuminate\Contracts\Routing\ResponseFactory|RedirectResponse|\Illuminate\Http\Response
* @return ResponseFactory|RedirectResponse|Response
*/
public function destroy($url)
{
Url::findOrFail($url);
Url::where('short_url', $url)->firstOrFail();

if (! $this->url->OwnerOrAdmin($url)) {
return response('Forbidden', 403);
}

ClickUrl::deleteUrlsClicks($url);
Url::find($url)->deviceTargets()->delete();
Url::where('short_url', $url)->firstOrFail()->deviceTargets()->delete();
Url::destroy($url);

// We add the Short URL to the DeletedUrls Database table.
Expand All @@ -231,7 +236,7 @@ public function destroy($url)
* Response to an AJAX request by the custom Short URL form.
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
* @return ResponseFactory|Response
*/
public function checkExistingUrl(Request $request)
{
Expand All @@ -246,7 +251,7 @@ public function checkExistingUrl(Request $request)
/**
* Show the user its own short URLs.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function getMyUrls()
{
Expand All @@ -258,7 +263,7 @@ public function getMyUrls()
/**
* Show the admin all the Short URLs.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function showUrlsList()
{
Expand Down Expand Up @@ -290,7 +295,7 @@ public function loadUrlsList()
/**
* Load the public URLs list to show.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
* @return Factory|View
*/
public function publicUrls()
{
Expand Down
38 changes: 10 additions & 28 deletions app/Services/UrlService.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,45 +40,27 @@ class UrlService
* @param $hideUrlStats
* @return string
*/
public function shortenUrl($long_url, $short_url, $privateUrl, $hideUrlStats)
public function shortenUrl($long_url, $short_url, $privateUrl, $hideUrlStats): Url
{
if (! empty($short_url)) {
// Set the short url as custom url sent by user
Url::createShortUrl($long_url, $short_url, $privateUrl, $hideUrlStats);
$createdUrlId = Url::createShortUrl($long_url, $short_url, $privateUrl, $hideUrlStats);

return $short_url;
if ($short_url === null) {
$short_url = $this->generateShortUrl($createdUrlId);
}

// Iterate until a not-already-created short url is generated
do {
$short_url = $this->generateShortUrl();
} while ($this->customUrlExisting($short_url));

Url::createShortUrl($long_url, $short_url, $privateUrl, $hideUrlStats);

return $short_url;
return Url::assignShortUrlToUrl($createdUrlId, $short_url);
}

/**
* Generate an unique short URL using hashids. Salt is the APP_KEY, which is always unique.
*
* @param int $id
* @return string
*/
public function generateShortUrl()
public function generateShortUrl(int $id): string
{
$hashids = new Hashids(env('APP_KEY'), 4);

$current = Url::orderBy('id', 'desc')->lockForUpdate()->first();

// If this is the first Short URL, let's encode a 0
if ($current === null) {
return $hashids->encode(0);
}

$currentInc = $current->id;
$currentInc++;

return $hashids->encode($currentInc);
return $hashids->encode($id);
}

/**
Expand Down Expand Up @@ -122,7 +104,7 @@ public function isOwner($url)
return false;
}

$urlUser = Url::find($url);
$urlUser = Url::where('short_url', $url)->firstOrFail();

return $urlUser->user_id === Auth::user()->id;
}
Expand Down Expand Up @@ -220,7 +202,7 @@ public function assignDeviceTargetUrl($request, $short_url_id): void
$enums = DeviceTargetsEnum::all();

foreach ($enums as $device) {
if ($request[$device->name] !== null) {
if (isset($request[$device->name]) && $request[$device->name] !== null) {
$data[] = [
'short_url_id' => $short_url_id,
'device' => $device->id,
Expand Down
Loading

0 comments on commit 5d0dead

Please sign in to comment.