Delete expires ots & add duration to create
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\OneTimeSecret;
|
||||
use Illuminate\Console\Attributes\Signature;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
#[Signature('app:delete-expired-one-time-secrets')]
|
||||
class DeleteExpiredOneTimeSecrets extends Command
|
||||
{
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
OneTimeSecret::whereDate('expires_at', '<', now())->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum OneTimeSecretDuration: int
|
||||
{
|
||||
case ThirtyMinutes = 1800;
|
||||
case OneHour = 3600;
|
||||
case SixHours = 21_600;
|
||||
case OneDay = 86_400;
|
||||
case ThreeDays = 259_200;
|
||||
case OneWeek = 604_800;
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::ThirtyMinutes => '30 Minutes',
|
||||
self::OneHour => '1 Hour',
|
||||
self::SixHours => '6 Hours',
|
||||
self::OneDay => '1 Day',
|
||||
self::ThreeDays => '3 Days',
|
||||
self::OneWeek => '1 Week',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\OneTimeSecretDuration;
|
||||
use App\Http\Requests\StoreOTSRequest;
|
||||
use App\Models\OneTimeSecret;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -29,7 +30,10 @@ class OTSController extends Controller
|
||||
{
|
||||
$validated = $request->validated();
|
||||
|
||||
$ots = OneTimeSecret::create(($validated + ['expires_at' => now()->addMinutes(30)]));
|
||||
$ots = OneTimeSecret::create([
|
||||
'secret' => $validated['secret'],
|
||||
'expires_at' => now()->addSeconds($validated['duration']),
|
||||
]);
|
||||
$otsLink = route('ots.show', ['oneTimeSecret' => $ots->id]);
|
||||
|
||||
return redirect()->route('ots.created')->with(['otsLink' => $otsLink]);
|
||||
@@ -37,19 +41,31 @@ class OTSController extends Controller
|
||||
|
||||
public function create(): Response
|
||||
{
|
||||
return Inertia::render('ots/Create', []);
|
||||
return Inertia::render('ots/Create', [
|
||||
'durations' => array_map(
|
||||
fn (OneTimeSecretDuration $duration): array => [
|
||||
'value' => $duration->value,
|
||||
'label' => $duration->label(),
|
||||
],
|
||||
OneTimeSecretDuration::cases(),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(OneTimeSecret $oneTimeSecret): Response
|
||||
public function show(OneTimeSecret $oneTimeSecret): Response|RedirectResponse
|
||||
{
|
||||
if ($oneTimeSecret->expires_at->isPast()) {
|
||||
$oneTimeSecret->delete();
|
||||
return redirect()->route('ots.create');
|
||||
}
|
||||
return Inertia::render('ots/Show', ['oneTimeSecretId' => $oneTimeSecret->id]);
|
||||
}
|
||||
|
||||
public function reveal(OneTimeSecret $oneTimeSecret): JsonResponse
|
||||
public function reveal(OneTimeSecret $oneTimeSecret): JsonResponse|RedirectResponse
|
||||
{
|
||||
$oneTimeSecret->delete();
|
||||
if ($oneTimeSecret->expires_at->isPast()) {
|
||||
abort(404);
|
||||
return redirect()->route('ots.create');
|
||||
}
|
||||
|
||||
return response()->json(['secret' => $oneTimeSecret->secret]);
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Enums\OneTimeSecretDuration;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreOTSRequest extends FormRequest
|
||||
{
|
||||
@@ -16,6 +18,7 @@ class StoreOTSRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'secret' => 'required|string',
|
||||
'duration' => ['required', 'integer', Rule::enum(OneTimeSecretDuration::class)],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ use Carbon\CarbonImmutable;
|
||||
use Database\Factories\OneTimeSecretFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Concerns\HasUuids;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
@@ -23,7 +22,6 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
* @property-read User|Team|null $recipient
|
||||
* @property-read Team|null $team
|
||||
*
|
||||
* @method static \Database\Factories\OneTimeSecretFactory factory($count = null, $state = [])
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|OneTimeSecret newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|OneTimeSecret newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|OneTimeSecret query()
|
||||
@@ -41,8 +39,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
#[Fillable(['secret', 'expires_at'])]
|
||||
class OneTimeSecret extends Model
|
||||
{
|
||||
/** @use HasFactory<OneTimeSecretFactory> */
|
||||
use HasFactory, HasUuids;
|
||||
use HasUuids;
|
||||
|
||||
public function team(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -7,9 +7,18 @@ import { Label } from '@/components/ui/label';
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import {Card, CardContent} from '@/components/ui/card';
|
||||
import {Textarea} from '@/components/ui/textarea';
|
||||
import {Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
|
||||
|
||||
const props = defineProps<{
|
||||
durations: {
|
||||
value: number,
|
||||
label: string
|
||||
}[],
|
||||
}>();
|
||||
|
||||
const form = useForm({
|
||||
secret: '',
|
||||
duration: props.durations[props.durations.length - 1].value
|
||||
});
|
||||
|
||||
function submit(): void {
|
||||
@@ -23,7 +32,7 @@ function submit(): void {
|
||||
<Card class="w-full">
|
||||
<CardContent>
|
||||
<form class="grid w-full gap-5 py-4" @submit.prevent="submit">
|
||||
<div class="grid grid-cols-2 items-start">
|
||||
<div class="grid grid-cols-2 items-start gap-4">
|
||||
<div class="col-span-2 grid items-start gap-3">
|
||||
<Label for="secret">Secret</Label>
|
||||
<Textarea
|
||||
@@ -35,6 +44,22 @@ function submit(): void {
|
||||
/>
|
||||
<InputError :message="form.errors.secret"/>
|
||||
</div>
|
||||
<div class="grid items-start gap-3">
|
||||
<Label for="duration">Duration</Label>
|
||||
<Select v-model="form.duration">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue placeholder="Select expires duration" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem v-for="duration in durations" :value="duration.value">
|
||||
{{ duration.label }}
|
||||
</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputError :message="form.errors.duration"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full justify-end">
|
||||
<Button :disabled="form.processing" class="w-fit" type="submit">
|
||||
|
||||
+2
-5
@@ -1,8 +1,5 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use App\Console\Commands\DeleteExpiredOneTimeSecrets;
|
||||
|
||||
Artisan::command('inspire', function () {
|
||||
$this->comment(Inspiring::quote());
|
||||
})->purpose('Display an inspiring quote');
|
||||
Schedule::command(DeleteExpiredOneTimeSecrets::class)->hourly();
|
||||
|
||||
Reference in New Issue
Block a user