mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-app.git
synced 2025-12-23 02:20:15 +00:00
✨ Add storage configuration, localization updates, and feed generation
- Added `publicDisk` configuration to `filesystems.php`. - Expanded locale translations in `es.json` and `de.json`. - Implemented RSS, Atom, and JSON feed views. - Added `feed.php` configuration for feed generation. - Introduced `ImageController` for image handling. - Updated application routing to include `api.php`.
This commit is contained in:
@@ -1,17 +1,22 @@
|
||||
<?php
|
||||
|
||||
use App\Models\LoginKey;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ModelCreatedNotification;
|
||||
use Illuminate\Auth\Events\Lockout;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\On;
|
||||
use Livewire\Attributes\Renderless;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Volt\Component;
|
||||
use Flux\Flux;
|
||||
use SimpleSoftwareIO\QrCode\Facades\QrCode;
|
||||
use eza\lnurl;
|
||||
|
||||
new #[Layout('components.layouts.auth')]
|
||||
class extends Component {
|
||||
@@ -23,6 +28,30 @@ class extends Component {
|
||||
|
||||
public bool $remember = false;
|
||||
|
||||
public ?string $k1 = null;
|
||||
public ?string $url = null;
|
||||
public ?string $lnurl = null;
|
||||
public ?string $qrCode = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
// Nur beim ersten Mount initialisieren
|
||||
if ($this->k1 === null) {
|
||||
$this->k1 = bin2hex(str()->random(32));
|
||||
if (app()->environment('local')) {
|
||||
$this->url = 'https://mmy4dp8eab.sharedwithexpose.com/api/lnurl-auth-callback?tag=login&k1='.$this->k1.'&action=login';
|
||||
} else {
|
||||
$this->url = url('/api/lnurl-auth-callback?tag=login&k1='.$this->k1.'&action=login');
|
||||
}
|
||||
$this->lnurl = lnurl\encodeUrl($this->url);
|
||||
$this->qrCode = base64_encode(QrCode::format('png')
|
||||
->size(300)
|
||||
->merge('/public/android-chrome-192x192.png', .3)
|
||||
->errorCorrection('H')
|
||||
->generate($this->lnurl));
|
||||
}
|
||||
}
|
||||
|
||||
#[On('nostrLoggedIn')]
|
||||
public function loginListener($pubkey): void
|
||||
{
|
||||
@@ -81,6 +110,26 @@ class extends Component {
|
||||
{
|
||||
return Str::transliterate(Str::lower($this->email).'|'.request()->ip());
|
||||
}
|
||||
|
||||
public function checkAuth()
|
||||
{
|
||||
$loginKey = LoginKey::query()
|
||||
->where('k1', $this->k1)
|
||||
->whereDate('created_at', '>=', now()->subMinutes(5))
|
||||
->first();
|
||||
|
||||
if ($loginKey) {
|
||||
$user = User::find($loginKey->user_id);
|
||||
|
||||
\App\Models\User::find(1)
|
||||
->notify(new ModelCreatedNotification($user, 'users'));
|
||||
auth()->login($user);
|
||||
|
||||
return to_route('dashboard', ['country' => 'de']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="flex min-h-screen" x-data="nostrLogin">
|
||||
@@ -137,7 +186,7 @@ class extends Component {
|
||||
<x-auth-session-status class="text-center" :status="session('status')"/>
|
||||
|
||||
<!-- Login Form -->
|
||||
<div class="flex flex-col gap-6">
|
||||
<div class="flex flex-col gap-6">
|
||||
<!-- Email Input -->
|
||||
{{--<flux:input
|
||||
wire:model="email"
|
||||
@@ -172,7 +221,43 @@ class extends Component {
|
||||
{{--<flux:checkbox wire:model="remember" label="Remember me for 30 days" />--}}
|
||||
|
||||
<!-- Submit Button -->
|
||||
<flux:button variant="primary" @click="openNostrLogin" class="w-full cursor-pointer">{{ __('Log in mit Nostr') }}</flux:button>
|
||||
<flux:button variant="primary" @click="openNostrLogin"
|
||||
class="w-full cursor-pointer">{{ __('Log in mit Nostr') }}</flux:button>
|
||||
|
||||
<div class="text-center text-2xl text-gray-80 dark:text-gray-2000 mt-6">
|
||||
Login with lightning ⚡
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center" wire:key="qrcode">
|
||||
<a href="lightning:{{ $this->lnurl }}">
|
||||
<div class="bg-white p-4">
|
||||
<img src="{{ 'data:image/png;base64, '. $this->qrCode }}" alt="qrcode">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between w-full">
|
||||
<div x-copy-to-clipboard="'{{ $this->lnurl }}'">
|
||||
<flux:button icon="clipboard" class="cursor-pointer">
|
||||
{{ __('Copy') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
<div>
|
||||
<flux:button
|
||||
primary
|
||||
:href="'lightning:'.$this->lnurl"
|
||||
>
|
||||
{{ __('Click to connect') }}
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512"
|
||||
height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"
|
||||
d="M461.81 53.81a4.4 4.4 0 00-3.3-3.39c-54.38-13.3-180 34.09-248.13 102.17a294.9 294.9 0 00-33.09 39.08c-21-1.9-42-.3-59.88 7.5-50.49 22.2-65.18 80.18-69.28 105.07a9 9 0 009.8 10.4l81.07-8.9a180.29 180.29 0 001.1 18.3 18.15 18.15 0 005.3 11.09l31.39 31.39a18.15 18.15 0 0011.1 5.3 179.91 179.91 0 0018.19 1.1l-8.89 81a9 9 0 0010.39 9.79c24.9-4 83-18.69 105.07-69.17 7.8-17.9 9.4-38.79 7.6-59.69a293.91 293.91 0 0039.19-33.09c68.38-68 115.47-190.86 102.37-247.95zM298.66 213.67a42.7 42.7 0 1160.38 0 42.65 42.65 0 01-60.38 0z"></path>
|
||||
<path fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"
|
||||
d="M109.64 352a45.06 45.06 0 00-26.35 12.84C65.67 382.52 64 448 64 448s65.52-1.67 83.15-19.31A44.73 44.73 0 00160 402.32"></path>
|
||||
</svg>
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sign up Link -->
|
||||
@@ -204,4 +289,6 @@ class extends Component {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div wire:poll.4s="checkAuth" wire:key="checkAuth"></div>
|
||||
</div>
|
||||
|
||||
@@ -52,6 +52,10 @@ class extends Component {
|
||||
</x-slot>
|
||||
{{ __('Kartenansicht öffnen') }}
|
||||
</flux:button>
|
||||
|
||||
<flux:button :href="route('dashboard', ['country' => 'de'])" class="cursor-pointer w-full" icon="arrow-right-start-on-rectangle">
|
||||
{{ __('Login') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
48
resources/views/vendor/feed/atom.blade.php
vendored
Normal file
48
resources/views/vendor/feed/atom.blade.php
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
<?=
|
||||
/* Using an echo tag here so the `<? ... ?>` won't get parsed as short tags */
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL
|
||||
?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ $meta['language'] }}">
|
||||
@foreach($meta as $key => $metaItem)
|
||||
@if($key === 'link')
|
||||
<{{ $key }} href="{{ url($metaItem) }}" rel="self"></{{ $key }}>
|
||||
@elseif($key === 'title')
|
||||
<{{ $key }}>{!! \Spatie\Feed\Helpers\Cdata::out($metaItem) !!}</{{ $key }}>
|
||||
@elseif($key === 'description')
|
||||
<subtitle>{{ $metaItem }}</subtitle>
|
||||
@elseif($key === 'language')
|
||||
@elseif($key === 'image')
|
||||
@if(!empty($metaItem))
|
||||
<logo>{!! $metaItem !!}</logo>
|
||||
@else
|
||||
|
||||
@endif
|
||||
@else
|
||||
<{{ $key }}>{{ $metaItem }}</{{ $key }}>
|
||||
@endif
|
||||
@endforeach
|
||||
@foreach($items as $item)
|
||||
<entry>
|
||||
<title>{!! \Spatie\Feed\Helpers\Cdata::out($item->title) !!}</title>
|
||||
<link rel="alternate" href="{{ url($item->link) }}" />
|
||||
<id>{{ url($item->id) }}</id>
|
||||
<author>
|
||||
<name>{!! \Spatie\Feed\Helpers\Cdata::out($item->authorName) !!}</name>
|
||||
@if(!empty($item->authorEmail))
|
||||
<email>{!! \Spatie\Feed\Helpers\Cdata::out($item->authorEmail) !!}</email>
|
||||
|
||||
@endif
|
||||
</author>
|
||||
<summary type="html">
|
||||
{!! \Spatie\Feed\Helpers\Cdata::out($item->summary) !!}
|
||||
</summary>
|
||||
@if($item->__isset('enclosure'))
|
||||
<link href="{{ url($item->enclosure) }}" length="{{ $item->enclosureLength }}" type="{{ $item->enclosureType }}" />
|
||||
@endif
|
||||
@foreach($item->category as $category)
|
||||
<category term="{{ $category }}" />
|
||||
@endforeach
|
||||
<updated>{{ $item->timestamp() }}</updated>
|
||||
</entry>
|
||||
@endforeach
|
||||
</feed>
|
||||
46
resources/views/vendor/feed/json.blade.php
vendored
Normal file
46
resources/views/vendor/feed/json.blade.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"version": "https://jsonfeed.org/version/1.1",
|
||||
"title": "{{ $meta['title'] }}",
|
||||
@if(!empty($meta['description']))
|
||||
"description": "{{ $meta['description'] }}",
|
||||
@endif
|
||||
"home_page_url": "{{ config('app.url') }}",
|
||||
"feed_url": "{{ url($meta['link']) }}",
|
||||
"language": "{{ $meta['language'] }}",
|
||||
@if(!empty($meta['image']))
|
||||
"icon": "{{ $meta['image'] }}",
|
||||
@endif
|
||||
"authors": [@foreach($items->unique('authorName') as $item){
|
||||
"name": "{{ $item->authorName }}"
|
||||
}@if(! $loop->last),@endif
|
||||
@endforeach
|
||||
|
||||
],
|
||||
"items": [@foreach($items as $item){
|
||||
"id": "{{ url($item->id) }}",
|
||||
"title": {!! json_encode($item->title) !!},
|
||||
"url": "{{ url($item->link) }}",
|
||||
"content_html": {!! json_encode($item->summary) !!},
|
||||
"summary": {!! json_encode($item->summary) !!},
|
||||
"date_published": "{{ $item->timestamp() }}",
|
||||
"date_modified": "{{ $item->timestamp() }}",
|
||||
"authors": [{ "name": {!! json_encode($item->authorName) !!} }],
|
||||
@if($item->__isset('image'))
|
||||
"image": "{{ url($item->image) }}",
|
||||
@endif
|
||||
@if($item->__isset('enclosure'))
|
||||
"attachments": [
|
||||
{
|
||||
"url": "{{ url($item->enclosure) }}",
|
||||
"mime_type": "{{ $item->enclosureType }}",
|
||||
"size_in_bytes": {{ $item->enclosureLength }}
|
||||
}
|
||||
],
|
||||
@endif
|
||||
"tags": [ {!! implode(',', array_map(fn($c) => '"'.$c.'"', $item->category)) !!} ]
|
||||
}@if(! $loop->last),
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
]
|
||||
}
|
||||
3
resources/views/vendor/feed/links.blade.php
vendored
Normal file
3
resources/views/vendor/feed/links.blade.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
@foreach($feeds as $name => $feed)
|
||||
<link rel="alternate" type="{{ \Spatie\Feed\Helpers\FeedContentType::forLink($feed['format'] ?? 'atom') }}" href="{{ route("feeds.{$name}") }}" title="{{ $feed['title'] }}">
|
||||
@endforeach
|
||||
42
resources/views/vendor/feed/rss.blade.php
vendored
Normal file
42
resources/views/vendor/feed/rss.blade.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
<?=
|
||||
/* Using an echo tag here so the `<? ... ?>` won't get parsed as short tags */
|
||||
'<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL
|
||||
?>
|
||||
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
|
||||
<channel>
|
||||
<atom:link href="{{ url($meta['link']) }}" rel="self" type="application/rss+xml"/>
|
||||
<title>{!! \Spatie\Feed\Helpers\Cdata::out($meta['title'] ) !!}</title>
|
||||
<link>{!! \Spatie\Feed\Helpers\Cdata::out(url($meta['link']) ) !!}</link>
|
||||
@if(!empty($meta['image']))
|
||||
<image>
|
||||
<url>{{ $meta['image'] }}</url>
|
||||
<title>{!! \Spatie\Feed\Helpers\Cdata::out($meta['title'] ) !!}</title>
|
||||
<link>{!! \Spatie\Feed\Helpers\Cdata::out(url($meta['link']) ) !!}</link>
|
||||
</image>
|
||||
@endif
|
||||
<description>{!! \Spatie\Feed\Helpers\Cdata::out($meta['description'] ) !!}</description>
|
||||
<language>{{ $meta['language'] }}</language>
|
||||
<pubDate>{{ $meta['updated'] }}</pubDate>
|
||||
|
||||
@foreach($items as $item)
|
||||
<item>
|
||||
<title>{!! \Spatie\Feed\Helpers\Cdata::out($item->title) !!}</title>
|
||||
<link>{{ url($item->link) }}</link>
|
||||
<description>{!! \Spatie\Feed\Helpers\Cdata::out($item->summary) !!}</description>
|
||||
<author>{!! \Spatie\Feed\Helpers\Cdata::out($item->authorName.(empty($item->authorEmail)?'':' <'.$item->authorEmail.'>')) !!}</author>
|
||||
<guid isPermaLink="true">{{ url($item->id) }}</guid>
|
||||
<pubDate>{{ $item->timestamp() }}</pubDate>
|
||||
<content:encoded>
|
||||
{{ app(Spatie\LaravelMarkdown\MarkdownRenderer::class)->toHtml($item->content) }}
|
||||
</content:encoded>
|
||||
@if($item->__isset('enclosure'))
|
||||
<enclosure url="{{ url($item->enclosure) }}" length="{{ url($item->enclosureLength) }}" type="{{ $item->enclosureType }}"/>
|
||||
<media:content url="{{ url($item->enclosure) }}" medium="image" type="{{ $item->enclosureType }}"/>
|
||||
@endif
|
||||
@foreach($item->category as $category)
|
||||
<category>{{ $category }}</category>
|
||||
@endforeach
|
||||
</item>
|
||||
@endforeach
|
||||
</channel>
|
||||
</rss>
|
||||
Reference in New Issue
Block a user