Skip to content

Commit

Permalink
Abstract URL shortening, create API endpoints & logic
Browse files Browse the repository at this point in the history
  • Loading branch information
cydrobolt committed Jan 29, 2016
1 parent db612b7 commit e25c5b1
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 73 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ env.*.php
env
.env.php
.env
.env.bak
.env.example
vendor/
composer.phar
100 changes: 100 additions & 0 deletions app/Factories/LinkFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
namespace App\Factories;

use App\Models\Link;
use App\Helpers\CryptoHelper;
use App\Helpers\LinkHelper;


class LinkFactory {
private static function formatLink($link_ending, $secret_ending=false) {
/**
* Given a link ending and a boolean indicating whether a secret ending is needed,
* return a link formatted with app protocol, app address, and link ending.
* @param string $link_ending
* @param boolean $secret_ending
* @return string
*/
$short_url = env('APP_PROTOCOL') . env('APP_ADDRESS') . '/' . $link_ending;

if ($secret_ending) {
$short_url .= '/' . $secret_ending;
}

return $short_url;
}

public static function createLink($long_url, $is_secret=false, $custom_ending=null, $link_ip='127.0.0.1', $creator=false) {
/**
* Given parameters needed to create a link, generate appropriate ending and
* return formatted link.
*
* @param string $custom_ending
* @param boolean (optional) $is_secret
* @param string (optional) $custom_ending
* @param string $link_ip
* @param string $creator
* @return string $formatted_link
*/

$is_already_short = LinkHelper::checkIfAlreadyShortened($long_url);

if ($is_already_short) {
throw new \Exception('Sorry, but your link already
looks like a shortened URL.');
}

if (!$is_secret && !$custom_ending && $existing_link = LinkHelper::longLinkExists($long_url)) {
// if link is not specified as secret, is non-custom, and
// already exists in Polr, lookup the value and return
return self::formatLink($existing_link);
}

if ($custom_ending) {
// has custom ending
$ending_conforms = LinkHelper::validateEnding($custom_ending);
if (!$ending_conforms) {
throw new \Exception('Sorry, but custom endings
can only contain alphanumeric characters');
}

$ending_in_use = LinkHelper::linkExists($custom_ending);
if ($ending_in_use) {
throw new \Exception('Sorry, but this URL ending is already in use.');
}

$link_ending = $custom_ending;
}
else {
// no custom ending
$link_ending = LinkHelper::findSuitableEnding();
}

$link = new Link;
$link->short_url = $link_ending;
$link->long_url = $long_url;
$link->ip = $link_ip;
$link->is_custom = $custom_ending != null;

if ($creator) {
// if user is logged in, save user as creator
$link->creator = $creator;
}

if ($is_secret) {
$rand_bytes_num = intval(env('POLR_SECRET_BYTES'));
$secret_key = CryptoHelper::generateRandomHex($rand_bytes_num);
$link->secret_key = $secret_key;
}
else {
$secret_key = false;
}

$link->save();

$formatted_link = self::formatLink($link_ending, $secret_key);

return $formatted_link;
}

}
3 changes: 2 additions & 1 deletion app/Factories/UserFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
use Hash;
use App\Models\User;
use App\Helpers\CryptoHelper;
class UserFactory {

class UserFactory {
public static function createUser($username, $email, $password, $active=0, $ip='127.0.0.1') {
$hashed_password = Hash::make($password);

Expand Down
12 changes: 12 additions & 0 deletions app/Helpers/ApiHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers\Api;

use App\Models\User;
use App\Helpers\ApiHelper;

class ApiHelper {
public static function checkUserApiQuota($username) {
// pass
return true;
}
}
15 changes: 12 additions & 3 deletions app/Helpers/LinkHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ static public function checkIfAlreadyShortened($long_link) {
static public function linkExists($link_ending) {
/**
* Provided a link ending (string),
* check whether the ending is in use.
* @return boolean
* return the link object, or false.
* @return Link model instance
*/

$link = Link::where('short_url', $link_ending)
->first();

if ($link == null) {
return false;
return $link;
}
else {
return true;
Expand Down Expand Up @@ -73,6 +74,14 @@ static public function validateEnding($link_ending) {
return $is_alphanum;
}

static public function processPostClick($link) {
/**
* Given a Link model instance, process post click operations.
* @param Link model instance $link
* @return boolean
*/
}

static public function findSuitableEnding() {
/**
* Provided an in-use link ending (string),
Expand Down
1 change: 1 addition & 0 deletions app/Http/Controllers/AjaxController.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Helpers\LinkHelper;
use App\Helpers\CryptoHelper;
Expand Down
31 changes: 31 additions & 0 deletions app/Http/Controllers/Api/ApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Api;

use App\Models\User;
use App\Helpers\ApiHelper;

class ApiController extends ApiController {
protected static function getApiUserInfo(Request $request) {
$api_key = $request->input('api_key');
$user = User::where('active', 1)
->where('api_key', $api_key)
->where('api_active', true)
->first();

$api_limited_reached = ApiHelper::checkUserApiQuota($user->username);
}

protected static function encodeResponse($result, $action, $response_type='json') {
$response = {
"action" => $action,
"result" => $result
}

if ($response_type == 'json') {
return json_encode($response);
}
else if ($response_type == 'plain_text') {
return $result;
}
}
}
38 changes: 38 additions & 0 deletions app/Http/Controllers/Api/ApiLinkController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
namespace App\Http\Controllers\Api;

use App\Factories\LinkFactory;
use App\Helpers\LinkHelper;

class ApiLinkController extends ApiController {
public static function shortenLink(Request $request) {
$response_type = $request->input('response_type');
$ard = self::getApiUserInfo($request);

/* */
$long_url = $request->input('url');
$is_secret = $request->input('is_secret');
$custom_ending = $request->input('custom_ending');

$formatted_link = LinkFactory::createLink();

return self::encodeResponse($formatted_link, 'shorten', $response_type);
}

public static function lookupLink(Request $request) {
$response_type = $request->input('response_type');
$ard = self::getApiUserInfo($request);

/* */
$url_ending = $request->input('url_ending');
$link_or_false = LinkHelper::linkExists($url_ending);

if ($link_or_false) {
return $link_or_false;
}
else {
abort(404, "Link not found.");
}

}
}
Empty file.
73 changes: 10 additions & 63 deletions app/Http/Controllers/LinkController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use Illuminate\Http\Redirect;

use App\Models\Link;

use App\Factories\LinkFactory;
use App\Helpers\CryptoHelper;
use App\Helpers\LinkHelper;

Expand All @@ -19,78 +19,25 @@ private function renderError($message) {
return redirect(route('index'))->with('error', $message);
}

private function formatAndRender($link_ending, $secret_ending=False) {
$short_url = env('APP_PROTOCOL') . env('APP_ADDRESS') . '/' . $link_ending;
if ($secret_ending) {
$short_url .= '/' . $secret_ending;
}
return view('shorten_result', ['short_url' => $short_url]);
}


public function performShorten(Request $request) {
$this->request = $request;

$long_url = $request->input('link-url');
$custom_ending = $request->input('custom-ending');
$is_secret = ($request->input('options') == "s" ? true : false);
$creator = session('username');

$is_already_short = LinkHelper::checkIfAlreadyShortened($long_url);
if ($is_already_short) {
return $this->renderError('Sorry, but your link already\
looks like a shortened URL.');
}

if (!$is_secret && $existing_link = LinkHelper::longLinkExists($long_url)) {
// if link is not specified as secret, is non-custom, and
// already exists in Polr, lookup the value and return
return $this->formatAndRender($existing_link);
}

if ($custom_ending) {
// has custom ending
$ending_conforms = LinkHelper::validateEnding($custom_ending);
if (!$ending_conforms) {
return $this->renderError('Sorry, but custom endings\
can only contain alphanumeric characters');
}

$ending_in_use = LinkHelper::linkExists($custom_ending);
if ($ending_in_use) {
return $this->renderError('Sorry, but this URL ending is already in use.');
}

$link_ending = $custom_ending;
}
else {
// no custom ending
$link_ending = LinkHelper::findSuitableEnding();
}

$link = new Link;
$link->short_url = $link_ending;
$link->long_url = $long_url;
$link->ip = $request->ip();
$link->is_custom = $custom_ending != null;
$creator = session('username');

if ($creator) {
// if user is logged in, save user as creator
$link->creator = $creator;
}
$link_ip = $request->ip();

if ($is_secret) {
$rand_bytes_num = intval(env('POLR_SECRET_BYTES'));
$secret_key = CryptoHelper::generateRandomHex($rand_bytes_num);
$link->secret_key = $secret_key;
try {
$short_url = LinkFactory::createLink($long_url, $is_secret, $custom_ending, $link_ip, $creator);
}
else {
$secret_key = false;
catch (\Exception $e) {
return self::renderError($e->getMessage());
}

$link->save();

return $this->formatAndRender($link_ending, $secret_key);
return view('shorten_result', ['short_url' => $short_url]);
}

public function performRedirect(Request $request, $short_url, $secret_key=false) {
Expand All @@ -109,8 +56,6 @@ public function performRedirect(Request $request, $short_url, $secret_key=false)
]);
}



if ($link_secret_key) {
if (!$secret_key) {
// if we do not receieve a secret key
Expand All @@ -137,6 +82,8 @@ public function performRedirect(Request $request, $short_url, $secret_key=false)

$link->save();

LinkHelper::processPostClick($link);

return redirect()->to($long_url);
}
}
4 changes: 3 additions & 1 deletion app/Http/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@
$app->post('/api/v2/link_avail_check', ['as' => 'api_link_check', 'uses' => 'AjaxController@checkLinkAvailability']);
$app->post('/api/v2/admin/toggle_api_active', ['as' => 'api_toggle_api_active', 'uses' => 'AjaxController@toggleAPIActive']);
$app->post('/api/v2/admin/generate_new_api_key', ['as' => 'api_generate_new_api_key', 'uses' => 'AjaxController@generateNewAPIKey']);

$app->post('/api/v2/admin/delete_user', ['as' => 'api_generate_new_api_key', 'uses' => 'AjaxController@deleteUser']);

$app->post('/api/v2/action/shorten', ['as' => 'api_shorten_url', 'uses' => 'Api\ApiLinkController@shortenLink']);
$app->post('/api/v2/action/lookup', ['as' => 'api_lookup_url', 'uses' => 'Api\ApiLinkController@lookupLink']);
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public function up()

$table->boolean('is_disabled')->default(0);
$table->boolean('is_custom')->default(0);
$table->boolean('is_api')->default(0);

$table->timestamps();
});
Expand Down
8 changes: 4 additions & 4 deletions resources/views/layouts/base.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@
<script src='/js/base.js'></script>
<script>
@if (Session::has('info'))
toastr["info"]("{{session('infoo')}}", "Info")
toastr["info"](`{{session('infoo')}}`, "Info")
@endif
@if (Session::has('error'))
toastr["error"]("{{session('error')}}", "Error")
toastr["error"](`{{session('error')}}`, "Error")
@endif
@if (Session::has('warning'))
toastr["warning"]("{{session('warning')}}", "Warning")
toastr["warning"](`{{session('warning')}}`, "Warning")
@endif
@if (Session::has('success'))
toastr["success"]("{{session('success')}}", "Success")
toastr["success"](`{{session('success')}}`, "Success")
@endif
</script>

Expand Down
Loading

0 comments on commit e25c5b1

Please sign in to comment.