Got basic redirect working

This commit is contained in:
Notoric 2024-06-10 04:31:51 +01:00
parent f59bab2b4a
commit d03bab8e02
6 changed files with 211 additions and 3 deletions

View File

@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Shortlink;
class ShortlinkController extends Controller
{
public function create(Request $request) {
if (auth()->user() == null) {
return response()->json(['error' => 'Unauthorized'], 401);
}
try {
$request->validate([
'url' => 'required|url'
]);
//check if url returns 200 at its final redirect
$shortlink = new Shortlink();
$shortlink->create($request->url, auth()->id());
return redirect("/l/{$shortlink->shortid}");
} catch (\Exception $e) {
return back()->withErrors([
'error' => $e->getMessage()
]);
}
}
public function goto(Request $request, $id) {
try {
$shortlink = (new Shortlink())->get($id);
// check if the link is expired or if it has reached the max clicks
// log the interaction
return redirect($shortlink->destination);
} catch (\Exception $e) {
return response()->json(['error' => $e->getMessage()], 404);
}
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Shortlink extends Model
{
use HasFactory;
protected $fillable = [
'shortid',
'destination',
'user_id',
'max_clicks',
'expires_at',
];
public function create(string $url, int $user_id): Shortlink {
$this->shortid = $this->generateNewId();
$this->destination = $url;
$this->user_id = $user_id;
$this->max_clicks = 0;
$this->expires_at = null;
$this->save();
return $this;
}
public function get(string $id): Shortlink {
$shortlink = Shortlink::where('shortid', $id)->first();
if ($shortlink == null) {
throw new \Exception('This shortened link does not exist');
}
return $shortlink;
}
public function delete(): void {
Shortlink::where('shortid', $this->id)->delete();
}
public function modify(int $max_clicks, $expires_at): void {
$this->max_clicks = $max_clicks;
$this->expires_at = $expires_at;
$this->save();
}
function generateNewId(int $length = 6): string {
$characters = 'qwrtypsdfghjklzxcvbnmQWRTYPSDFGHJKLZXCVBNM256789_';
// try n x 2 times to generate a new id, if it finds an id, return it, otherwise, try to look for an id of length + 1
for ($i = 0; $i < $length * 2; $i++) {
$id = '';
for ($i = 0; $i < $length; $i++) {
$id .= $characters[rand(0, strlen($characters) - 1)];
}
if (Shortlink::where('shortid', $id)->count() == 0) {
return $id;
}
}
return $this->generateNewId($length + 1);
}
}

View File

@ -0,0 +1,40 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('shortlinks', function (Blueprint $table) {
$table->string('shortid')->unique()->primary();
$table->string('destination');
$table->foreignId('user_id')->references('id')->on('users');
$table->integer('max_clicks')->default(0);
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
Schema::create('link_interactions', function (Blueprint $table) {
$table->id();
$table->string('link');
$table->foreign('link')->references('shortid')->on('shortlinks');
$table->string('ip');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('shortlinks');
Schema::dropIfExists('link_interactions');
}
};

View File

@ -120,4 +120,45 @@ header nav a {
background: #ff000028;
border-radius: 5px;
padding: 10px;
}
#shortener-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 60px 50px;
background-color: var(--primary);
width: calc(100% - 100px);
gap: 40px;
}
#shortener-container h1 {
font-size: 2.5em;
color: white;
}
#shortener-container input {
color: black;
background-color: #fff;
border-radius: 5px;
font-size: 1.3em;
margin-bottom: 20px;
padding: 10px;
border: 0;
width: 600px;
box-shadow: 2px 2px 5px 0 #0004;
}
#shortener-container button {
font-size: 1.3em;
padding: 10px 20px;
border: none;
border-radius: 5px;
color: white;
box-shadow: 2px 2px 5px 0 #0004;
background-color: var(--foreground);
}
#shortener-container input::placeholder {
color: #aaa;
}

View File

@ -5,6 +5,21 @@
@endsection
@section('content')
<h1>HOMEPAGE!</h1>
<p>Placeholder text</p>
<div id="shortener-container">
@if (auth()->check())
<h1>Shorten your URL</h1>
<form action="shorten" method="post">
@csrf
<input type="text" name="url" placeholder="Enter your URL here" required>
<button type="submit">Shorten</button>
</form>
@if ($errors->any())
<p class="error">{{ $errors->first() }}</p>
@endif
@else
<h1>Welcome to slink URL Shortener</h1>
<p><a href="register">Sign Up</a> or <a href="login">Log In</a> to shorten your URLs</p>
@endif
</div>
@endsection

View File

@ -3,6 +3,7 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\RegisterController;
use App\Http\Controllers\LoginController;
use App\Http\Controllers\ShortlinkController;
Route::get('/', function () {
return view('welcome');
@ -43,4 +44,10 @@ Route::get('/home', function () {
return view('home');
}
return view('home');
});
});
Route::post('/shorten', [ShortlinkController::class, 'create']);
// Route::get('/l/{id}', );
Route::get('/{id}', [ShortlinkController::class, 'goto']);