diff --git a/app/DTOs/CreatePasteDataDTO.php b/app/DTOs/CreatePasteDataDTO.php new file mode 100644 index 0000000..32a553f --- /dev/null +++ b/app/DTOs/CreatePasteDataDTO.php @@ -0,0 +1,19 @@ + self::GITHUB_DARK, + 'material-theme-palenight' => self::MATERIAL_THEME_PALENIGHT, + 'dracula' => self::DRACULA, + 'nord' => self::NORD, + 'liver-dark' => self::LIVER_DARK, + default => null, + }; + } +} \ No newline at end of file diff --git a/app/Enums/ExpiryOption.php b/app/Enums/ExpiryOption.php new file mode 100644 index 0000000..1417d51 --- /dev/null +++ b/app/Enums/ExpiryOption.php @@ -0,0 +1,23 @@ + self::ONE_HOUR, + '1_day' => self::ONE_DAY, + '1_week' => self::ONE_WEEK, + 'custom' => self::CUSTOM, + default => self::NEVER, + }; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/PastesController.php b/app/Http/Controllers/PastesController.php index 2d5d68f..4dfcf35 100644 --- a/app/Http/Controllers/PastesController.php +++ b/app/Http/Controllers/PastesController.php @@ -2,32 +2,81 @@ namespace App\Http\Controllers; -use App\Http\Requests\PasteRequest; +use App\DTOs\CreatePasteDataDTO; +use App\Enums\ColorScheme; +use App\Enums\ExpiryOption; use App\Models\Paste; -use Illuminate\Http\RedirectResponse; use Illuminate\View\View; +use Illuminate\Http\Request; +use App\Http\Requests\PasteRequest; +use App\Services\PasteService; +use Illuminate\Support\Facades\Hash; +use Illuminate\Http\RedirectResponse; class PastesController extends Controller { + public function __construct(private PasteService $pasteService) {} + public function post(PasteRequest $request): RedirectResponse { - $paste = Paste::fromRequest($request); + $validated = $request->validated(); + + $data = new CreatePasteDataDTO( + code: $validated['code'], + colorScheme: ColorScheme::fromInput($validated['color_scheme']), + expiryOption: ExpiryOption::fromInput($validated['expiry']), + customExpiry: $validated['custom_expiry'] ? + \Carbon\CarbonImmutable::parse($validated['custom_expiry']) : null, + passwordPlain: $validated['password'], + ); + + $paste = $this->pasteService->create($data); return redirect()->route('show', $paste->hash); } - public function show(Paste $paste): View + public function show(Paste $paste, Request $request): View { + if ($paste->expires_at && now()->greaterThan($paste->expires_at)) { + abort(404, 'This paste has expired.'); + } + + if ($paste->password) { + if (!$request->session()->get('paste_access_' . $paste->id)) { + return view('lock', compact('paste')); + } + } + return view('show', compact('paste')); } - public function raw(Paste $paste): View + public function raw(Paste $paste, Request $request): View { + if ($paste->expires_at && now()->greaterThan($paste->expires_at)) { + abort(404, 'This paste has expired.'); + } + + if ($paste->password) { + if (!$request->session()->get('paste_access_' . $paste->id)) { + return view('lock', compact('paste')); + } + } + return view('raw', compact('paste')); } - public function edit(Paste $paste): View + public function edit(Paste $paste, Request $request): View { + if ($paste->expires_at && now()->greaterThan($paste->expires_at)) { + abort(404, 'This paste has expired.'); + } + + if ($paste->password) { + if (!$request->session()->get('paste_access_' . $paste->id)) { + return view('lock', compact('paste')); + } + } + return view('edit', compact('paste')); } @@ -37,4 +86,16 @@ public function fork(PasteRequest $request, Paste $paste): RedirectResponse return redirect()->route('show', $paste->hash); } + + public function unlock(Request $request, Paste $paste) + { + $request->validate(['password' => 'required|string']); + + if (Hash::check($request->password, $paste->password)) { + $request->session()->put('paste_access_' . $paste->id, true); + return redirect()->route('show', $paste); + } + + return back()->withErrors(['password' => 'Incorrect password.']); + } } diff --git a/app/Http/Requests/PasteRequest.php b/app/Http/Requests/PasteRequest.php index c18649e..a3bc1a3 100644 --- a/app/Http/Requests/PasteRequest.php +++ b/app/Http/Requests/PasteRequest.php @@ -2,7 +2,10 @@ namespace App\Http\Requests; +use App\Enums\ColorScheme; +use App\Enums\ExpiryOption; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Validation\Rules\Enum; class PasteRequest extends FormRequest { @@ -21,6 +24,10 @@ public function rules(): array { return [ 'code' => 'required|max:50000', + 'color_scheme' => ['nullable', new Enum(ColorScheme::class)], + 'expiry' => ['nullable', new Enum(ExpiryOption::class)], + 'custom_expiry' => 'nullable|date', + 'password' => 'nullable|string|min:6', ]; } } diff --git a/app/Models/Paste.php b/app/Models/Paste.php index e593583..38f1428 100644 --- a/app/Models/Paste.php +++ b/app/Models/Paste.php @@ -2,10 +2,14 @@ namespace App\Models; -use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Http\Request; +use App\Enums\ColorScheme; +use Carbon\Carbon; use Ramsey\Uuid\Uuid; +use App\Enums\ExpiryOption; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Factories\HasFactory; class Paste extends Model { @@ -18,6 +22,12 @@ class Paste extends Model */ protected $table = 'pastes'; + protected $casts = [ + 'expiry' => ExpiryOption::class, + 'color_scheme' => ColorScheme::class, + 'custom_expiry' => 'immutable_datetime', + ]; + public static function fromRequest(Request $request): self { return static::createNew(new static, $request); @@ -35,6 +45,9 @@ private static function createNew(self $paste, Request $request): self { $paste->code = $request->get('code'); $paste->hash = Uuid::uuid4()->toString(); + $paste->expires_at = self::parseExpiry($request); + $paste->color_scheme = $request->input('color_scheme'); + $paste->password = self::hashPasswordIfProvided($request); $paste->save(); @@ -48,4 +61,24 @@ public function getRouteKeyName(): string { return 'hash'; } + + private static function parseExpiry(Request $request): ?Carbon + { + return match ($request->input('expiry')) { + '1_hour' => now()->addHour(), + '1_day' => now()->addDay(), + '1_week' => now()->addWeek(), + 'custom' => $request->filled('custom_expiry') + ? Carbon::parse($request->input('custom_expiry')) + : null, + default => null, + }; + } + + private static function hashPasswordIfProvided(Request $request): ?string + { + return $request->filled('password') + ? Hash::make($request->password) + : null; + } } diff --git a/app/Services/PasteService.php b/app/Services/PasteService.php new file mode 100644 index 0000000..8ec4c3d --- /dev/null +++ b/app/Services/PasteService.php @@ -0,0 +1,43 @@ +resolveExpiry($data->expiryOption, $data->customExpiry); + + $paste = new Paste(); + $paste->code = $data->code; + $paste->hash = Uuid::uuid4()->toString(); + $paste->expires_at = $expiresAt; + $paste->color_scheme = $data->colorScheme; + $paste->password = $data->passwordPlain ? Hash::make($data->passwordPlain) : null; + + $paste->save(); + + return $paste; + + } + + private function resolveExpiry(ExpiryOption $option, ?CarbonImmutable $custom): ?CarbonImmutable + { + return match ($option) { + ExpiryOption::ONE_HOUR => CarbonImmutable::now()->addHour(), + ExpiryOption::ONE_DAY => CarbonImmutable::now()->addDay(), + ExpiryOption::ONE_WEEK => CarbonImmutable::now()->addWeek(), + ExpiryOption::CUSTOM => $custom, + default => null, + }; + } + + +} \ No newline at end of file diff --git a/bootstrap/app.php b/bootstrap/app.php index ef88883..0e2b9f5 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -15,6 +15,7 @@ health: '/up', ) ->withMiddleware(function (Middleware $middleware) { + $middleware->append(\Torchlight\Middleware\RenderTorchlight::class); $middleware->redirectGuestsTo(fn () => route('login')); $middleware->redirectUsersTo(AppServiceProvider::HOME); diff --git a/composer.json b/composer.json index fd155cb..eefa974 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ "league/flysystem-aws-s3-v3": "^3.0", "ramsey/uuid": "^4.3", "sentry/sentry-laravel": "^4.3", - "spatie/laravel-ignition": "^2.4" + "spatie/laravel-ignition": "^2.4", + "torchlight/torchlight-laravel": "^0.6.1" }, "require-dev": { "doctrine/dbal": "^3.5", diff --git a/composer.lock b/composer.lock index 57ad392..ffc76ac 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "07fc20b8d5bb2b4653965a262201d118", + "content-hash": "1556e224f2e779ab97b91e367b4b4a6c", "packages": [ { "name": "aws/aws-crt-php", @@ -945,22 +945,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.9.3", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", - "reference": "7b2f29fe81dc4da0ca0ea7d42107a0845946ea77", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1051,7 +1051,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.3" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -1067,20 +1067,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:37:11+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.2.0", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/7c69f28996b0a6920945dd20b3857e499d9ca96c", - "reference": "7c69f28996b0a6920945dd20b3857e499d9ca96c", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1088,7 +1088,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1134,7 +1134,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.2.0" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1150,20 +1150,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T13:27:01+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.1", + "version": "2.8.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16" + "reference": "21dc724a0583619cd1652f673303492272778051" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/c2270caaabe631b3b44c85f99e5a04bbb8060d16", - "reference": "c2270caaabe631b3b44c85f99e5a04bbb8060d16", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051", + "reference": "21dc724a0583619cd1652f673303492272778051", "shasum": "" }, "require": { @@ -1179,7 +1179,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1250,7 +1250,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.1" + "source": "https://github.com/guzzle/psr7/tree/2.8.0" }, "funding": [ { @@ -1266,20 +1266,20 @@ "type": "tidelift" } ], - "time": "2025-03-27T12:30:47+00:00" + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.4", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/30e286560c137526eccd4ce21b2de477ab0676d2", - "reference": "30e286560c137526eccd4ce21b2de477ab0676d2", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1288,7 +1288,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1336,7 +1336,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.4" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1352,7 +1352,7 @@ "type": "tidelift" } ], - "time": "2025-02-03T10:55:03+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "jean85/pretty-package-versions", @@ -1631,16 +1631,16 @@ }, { "name": "laravel/nightwatch", - "version": "v1.12.0", + "version": "v1.13.2", "source": { "type": "git", "url": "https://github.com/laravel/nightwatch.git", - "reference": "6fc8774f6c2244269719cd82988a95d84517bf12" + "reference": "a9585c7f2af7ebb11468bd6a8a41340baf979067" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/nightwatch/zipball/6fc8774f6c2244269719cd82988a95d84517bf12", - "reference": "6fc8774f6c2244269719cd82988a95d84517bf12", + "url": "https://api.github.com/repos/laravel/nightwatch/zipball/a9585c7f2af7ebb11468bd6a8a41340baf979067", + "reference": "a9585c7f2af7ebb11468bd6a8a41340baf979067", "shasum": "" }, "require": { @@ -1713,7 +1713,7 @@ "issues": "https://github.com/laravel/nightwatch/issues", "source": "https://github.com/laravel/nightwatch" }, - "time": "2025-08-14T07:10:52+00:00" + "time": "2025-08-25T07:31:52+00:00" }, { "name": "laravel/prompts", @@ -3157,16 +3157,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.4", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", + "reference": "638a154f8d4ee6a5cfa96d6a34dfbe0cffa9566d", "shasum": "" }, "require": { @@ -3174,7 +3174,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -3216,7 +3216,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.4" }, "funding": [ { @@ -3228,7 +3228,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-08-21T11:53:16+00:00" }, { "name": "psr/clock", @@ -5528,7 +5528,7 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -5587,7 +5587,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { @@ -5598,6 +5598,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5607,16 +5611,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { @@ -5665,7 +5669,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -5676,16 +5680,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", @@ -5748,7 +5756,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, "funding": [ { @@ -5759,6 +5767,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5768,7 +5780,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -5829,7 +5841,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -5840,6 +5852,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5849,7 +5865,7 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", @@ -5910,7 +5926,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -5921,6 +5937,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -5930,7 +5950,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -5990,7 +6010,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -6001,6 +6021,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6010,16 +6034,16 @@ }, { "name": "symfony/polyfill-php83", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, "require": { @@ -6066,7 +6090,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, "funding": [ { @@ -6077,16 +6101,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-uuid", - "version": "v1.32.0", + "version": "v1.33.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-uuid.git", @@ -6145,7 +6173,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.33.0" }, "funding": [ { @@ -6156,6 +6184,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6960,6 +6992,71 @@ }, "time": "2024-12-21T16:25:41+00:00" }, + { + "name": "torchlight/torchlight-laravel", + "version": "v0.6.1", + "source": { + "type": "git", + "url": "https://github.com/torchlight-api/torchlight-laravel.git", + "reference": "5670af34e9637761c89687848b8bd71e043d6819" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/torchlight-api/torchlight-laravel/zipball/5670af34e9637761c89687848b8bd71e043d6819", + "reference": "5670af34e9637761c89687848b8bd71e043d6819", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^7.2", + "illuminate/cache": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/http": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/view": "^8.0|^9.0|^10.0|^11.0|^12.0", + "php": "^7.3|^8.0", + "ramsey/uuid": "^3.7|^4.0" + }, + "require-dev": { + "mockery/mockery": "^1.3.3", + "orchestra/testbench": "^5.0|^6.0|^7.0|^9.0|^10.0", + "phpunit/phpunit": "^8.5.23|^9.5|^10.5|^11.5.3" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Torchlight\\TorchlightServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Torchlight\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Francis", + "email": "aaron@hammerstone.dev" + } + ], + "description": "A Laravel Client for Torchlight, the syntax highlighting API.", + "homepage": "https://torchlight.dev", + "keywords": [ + "Code highlighting", + "laravel", + "syntax highlighting" + ], + "support": { + "issues": "https://github.com/torchlight-api/torchlight-laravel/issues", + "source": "https://github.com/torchlight-api/torchlight-laravel/tree/v0.6.1" + }, + "time": "2025-03-22T15:38:26+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.6.2", diff --git a/config/torchlight.php b/config/torchlight.php new file mode 100644 index 0000000..4dc14d2 --- /dev/null +++ b/config/torchlight.php @@ -0,0 +1,66 @@ + env('TORCHLIGHT_CACHE_DRIVER'), + + // Cache blocks for 30 days. Set null to store permanently + 'cache_seconds' => env('TORCHLIGHT_CACHE_TTL', 60 * 60 * 24 * 30), + + // Which theme you want to use. You can find all of the themes at + // https://torchlight.dev/docs/themes. + 'theme' => env('TORCHLIGHT_THEME', 'material-theme-palenight'), + + // If you want to use two separate themes for dark and light modes, + // you can use an array to define both themes. Torchlight renders + // both on the page, and you will be responsible for hiding one + // or the other depending on the dark / light mode via CSS. + // 'theme' => [ + // 'dark' => 'github-dark', + // 'light' => 'github-light', + // ], + + // Your API token from torchlight.dev. + 'token' => env('TORCHLIGHT_TOKEN'), + + // If you want to register the blade directives, set this to true. + 'blade_components' => true, + + // The Host of the API. + 'host' => env('TORCHLIGHT_HOST', 'https://api.torchlight.dev'), + + // We replace tabs in your code blocks with spaces in HTML. Set + // the number of spaces you'd like to use per tab. Set to + // `false` to leave literal tabs in the HTML. + 'tab_width' => 4, + + // If you pass a filename to the code component or in a markdown + // block, Torchlight will look for code snippets in the + // following directories. + 'snippet_directories' => [ + resource_path() + ], + + // Global options to control blocks-level settings. + // https://torchlight.dev/docs/options + 'options' => [ + // Turn line numbers on or off globally. + // 'lineNumbers' => false, + + // Control the `style` attribute applied to line numbers. + 'lineNumbersStyle' => 'margin-right: 16px;', + + // Turn on +/- diff indicators. + // 'diffIndicators' => true, + + // If there are any diff indicators for a line, put them + // in place of the line number to save horizontal space. + // 'diffIndicatorsInPlaceOfLineNumbers' => true, + + // When lines are collapsed, this is the text that will + // be shown to indicate that they can be expanded. + // 'summaryCollapsedIndicator' => '...', + ] +]; diff --git a/database/migrations/2017_02_05_115413_create_pastes_table.php b/database/migrations/2017_02_05_115413_create_pastes_table.php index 01b05a6..f598b9d 100644 --- a/database/migrations/2017_02_05_115413_create_pastes_table.php +++ b/database/migrations/2017_02_05_115413_create_pastes_table.php @@ -15,6 +15,7 @@ public function up(): void $table->text('code'); $table->string('hash')->nullable(); $table->string('ip')->nullable(); + $table->timestamp('expires_at')->nullable(); $table->timestamps(); }); } diff --git a/database/migrations/2025_09_02_084107_add_color_scheme_to_pastes_table.php b/database/migrations/2025_09_02_084107_add_color_scheme_to_pastes_table.php new file mode 100644 index 0000000..c3f4c0e --- /dev/null +++ b/database/migrations/2025_09_02_084107_add_color_scheme_to_pastes_table.php @@ -0,0 +1,32 @@ +string('color_scheme')->nullable()->after('code'); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pastes', function (Blueprint $table) { + if (Schema::hasColumn('pastes', 'color_scheme')) { + $table->dropColumn('color_scheme'); + } + }); + } +}; diff --git a/database/migrations/2025_09_02_095426_add_password_to_pastes_table.php b/database/migrations/2025_09_02_095426_add_password_to_pastes_table.php new file mode 100644 index 0000000..923d40c --- /dev/null +++ b/database/migrations/2025_09_02_095426_add_password_to_pastes_table.php @@ -0,0 +1,32 @@ +string('password')->nullable(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pastes', function (Blueprint $table) { + if (Schema::hasColumn('pastes', 'password')) { + $table->dropColumn('password'); + } + }); + } +}; diff --git a/package-lock.json b/package-lock.json index 0538a58..26430c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2322,9 +2322,9 @@ } }, "node_modules/sass": { - "version": "1.90.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", - "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", + "version": "1.91.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.91.0.tgz", + "integrity": "sha512-aFOZHGf+ur+bp1bCHZ+u8otKGh77ZtmFyXDo4tlYvT7PWql41Kwd8wdkPqhhT+h2879IVblcHFglIMofsFd1EA==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/resources/views/components/color-scheme.blade.php b/resources/views/components/color-scheme.blade.php new file mode 100644 index 0000000..5652327 --- /dev/null +++ b/resources/views/components/color-scheme.blade.php @@ -0,0 +1,13 @@ +
+
+ + +
+
diff --git a/resources/views/components/expiry-dropdown.blade.php b/resources/views/components/expiry-dropdown.blade.php new file mode 100644 index 0000000..51c9495 --- /dev/null +++ b/resources/views/components/expiry-dropdown.blade.php @@ -0,0 +1,16 @@ +
+
+ + +
+ + +
+
+
diff --git a/resources/views/components/password.blade.php b/resources/views/components/password.blade.php new file mode 100644 index 0000000..5ef5814 --- /dev/null +++ b/resources/views/components/password.blade.php @@ -0,0 +1,6 @@ +
+
+ + +
+
diff --git a/resources/views/components/sidebar.blade.php b/resources/views/components/sidebar.blade.php index f72c52e..4a13468 100644 --- a/resources/views/components/sidebar.blade.php +++ b/resources/views/components/sidebar.blade.php @@ -5,7 +5,15 @@ -{!! $errors->first('code', '

:message

') !!} +@if ($errors->any()) +
+ +
+@endif