diff --git a/app/Enums/SelfHostedServiceType.php b/app/Enums/SelfHostedServiceType.php
index e2d4597..69467cf 100644
--- a/app/Enums/SelfHostedServiceType.php
+++ b/app/Enums/SelfHostedServiceType.php
@@ -4,8 +4,49 @@ namespace App\Enums;
enum SelfHostedServiceType: string
{
- case Mempool = 'mempool';
- case LNbits = 'lnbits';
case Alby = 'alby';
+ case BtcpayServer = 'btcpay_server';
+ case ElectrumFulcrumServer = 'electrum_fulcrum_server';
+ case LNbits = 'lnbits';
+ case LnbitsServer = 'lnbits_server';
+ case Mempool = 'mempool';
+ case NostrBlossomServer = 'nostr_blossom_server';
+ case NostrClient = 'nostr_client';
+ case NostrRelayServer = 'nostr_relay_server';
+ case PkarrDnsServer = 'pkarr_dns_server';
case Other = 'other';
+
+ public function color(): string
+ {
+ return match ($this) {
+ self::Mempool => 'blue',
+ self::LNbits => 'purple',
+ self::Alby => 'amber',
+ self::ElectrumFulcrumServer => 'cyan',
+ self::BtcpayServer => 'green',
+ self::LnbitsServer => 'violet',
+ self::NostrRelayServer => 'fuchsia',
+ self::NostrClient => 'pink',
+ self::NostrBlossomServer => 'rose',
+ self::PkarrDnsServer => 'orange',
+ self::Other => 'zinc',
+ };
+ }
+
+ public function label(): string
+ {
+ return match ($this) {
+ self::Mempool => 'Mempool',
+ self::LNbits => 'LNbits',
+ self::Alby => 'Alby',
+ self::ElectrumFulcrumServer => 'Electrum/Fulcrum Server',
+ self::BtcpayServer => 'BTCPay Server',
+ self::LnbitsServer => 'LNbits Server',
+ self::NostrRelayServer => 'Nostr Relay',
+ self::NostrClient => 'Nostr Client',
+ self::NostrBlossomServer => 'Nostr Blossom',
+ self::PkarrDnsServer => 'Pkarr DNS Server',
+ self::Other => 'Other',
+ };
+ }
}
diff --git a/app/Livewire/Forms/ServiceForm.php b/app/Livewire/Forms/ServiceForm.php
new file mode 100644
index 0000000..57685cc
--- /dev/null
+++ b/app/Livewire/Forms/ServiceForm.php
@@ -0,0 +1,119 @@
+ ['required', 'string', 'max:255'],
+ 'type' => [
+ 'required',
+ 'in:' . collect(SelfHostedServiceType::cases())->map(fn($c) => $c->value)->implode(',')
+ ],
+ 'intro' => ['nullable', 'string'],
+ 'url_clearnet' => ['nullable', 'url', 'max:255'],
+ 'url_onion' => ['nullable', 'string', 'max:255'],
+ 'url_i2p' => ['nullable', 'string', 'max:255'],
+ 'url_pkdns' => ['nullable', 'string', 'max:255'],
+ 'contact' => ['nullable', 'string'],
+ 'anonymous' => ['boolean'],
+ ];
+ }
+
+ public function setService(SelfHostedService $service): void
+ {
+ $this->service = $service;
+
+ $this->name = $service->name;
+ $this->intro = $service->intro;
+ $this->url_clearnet = $service->url_clearnet;
+ $this->url_onion = $service->url_onion;
+ $this->url_i2p = $service->url_i2p;
+ $this->url_pkdns = $service->url_pkdns;
+ $this->type = $service->type?->value;
+ $this->contact = $service->contact;
+ $this->anonymous = is_null($service->created_by);
+ }
+
+ public function store(): SelfHostedService
+ {
+ $this->validate();
+ $this->validateAtLeastOneUrl();
+
+ return SelfHostedService::create([
+ 'name' => $this->name,
+ 'type' => $this->type,
+ 'intro' => $this->intro,
+ 'url_clearnet' => $this->url_clearnet,
+ 'url_onion' => $this->url_onion,
+ 'url_i2p' => $this->url_i2p,
+ 'url_pkdns' => $this->url_pkdns,
+ 'contact' => $this->contact,
+ 'created_by' => $this->anonymous ? null : auth()->id(),
+ ]);
+ }
+
+ public function update(): void
+ {
+ $this->validate();
+ $this->validateAtLeastOneUrl();
+
+ $this->service->update([
+ 'name' => $this->name,
+ 'type' => $this->type,
+ 'intro' => $this->intro,
+ 'url_clearnet' => $this->url_clearnet,
+ 'url_onion' => $this->url_onion,
+ 'url_i2p' => $this->url_i2p,
+ 'url_pkdns' => $this->url_pkdns,
+ 'contact' => $this->contact,
+ 'created_by' => $this->anonymous ? null : ($this->service->created_by ?? auth()->id()),
+ ]);
+ }
+
+ protected function validateAtLeastOneUrl(): void
+ {
+ if (empty($this->url_clearnet) && empty($this->url_onion) && empty($this->url_i2p) && empty($this->url_pkdns)) {
+ $this->addError('url_clearnet', __('Mindestens eine URL muss angegeben werden.'));
+ throw new \Illuminate\Validation\ValidationException(
+ \Illuminate\Support\Facades\Validator::make([], [])
+ );
+ }
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 50e583d..288bd27 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -72,7 +72,7 @@ class User extends Authenticatable implements CipherSweetEncrypted
->addOptionalTextField('node_id')
->addOptionalTextField('email')
->addOptionalTextField('paynym')
- ->addJsonField('lnbits', $map)
+ ->addNullableJsonField('lnbits', $map, strict: false)
->addBlindIndex('public_key', new BlindIndex('public_key_index'))
->addBlindIndex('lightning_address', new BlindIndex('lightning_address_index'))
->addBlindIndex('lnurl', new BlindIndex('lnurl_index'))
diff --git a/database/factories/SelfHostedServiceFactory.php b/database/factories/SelfHostedServiceFactory.php
index b695c19..9cf6adb 100644
--- a/database/factories/SelfHostedServiceFactory.php
+++ b/database/factories/SelfHostedServiceFactory.php
@@ -19,16 +19,16 @@ class SelfHostedServiceFactory extends Factory
$name = $this->faker->unique()->company();
return [
- 'created_by' => User::factory(),
+ 'created_by' => $this->faker->optional()->numberBetween(1,9),
'name' => $name,
'slug' => str($name)->slug(),
'intro' => $this->faker->optional()->paragraph(),
'url_clearnet' => $this->faker->optional()->url(),
- 'url_onion' => null,
- 'url_i2p' => null,
- 'url_pkdns' => null,
+ 'url_onion' => $this->faker->optional()->url(),
+ 'url_i2p' => $this->faker->optional()->url(),
+ 'url_pkdns' => $this->faker->optional()->url(),
'type' => $this->faker->randomElement(SelfHostedServiceType::cases())->value,
- 'contact_url' => $this->faker->optional()->url(),
+ 'contact' => $this->faker->optional()->url(),
];
}
}
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 5b566ca..a010854 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -2,7 +2,7 @@
namespace Database\Seeders;
-use App\Models\User;
+use App\Models\SelfHostedService;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -13,9 +13,6 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
- User::factory()->create([
- 'name' => 'Test User',
- 'email' => 'test@example.com',
- ]);
+ SelfHostedService::factory(10)->create();
}
}
diff --git a/resources/views/livewire/services/create.blade.php b/resources/views/livewire/services/create.blade.php
index 3209e0d..a9efda9 100644
--- a/resources/views/livewire/services/create.blade.php
+++ b/resources/views/livewire/services/create.blade.php
@@ -2,7 +2,7 @@
use App\Attributes\SeoDataAttribute;
use App\Enums\SelfHostedServiceType;
-use App\Models\SelfHostedService;
+use App\Livewire\Forms\ServiceForm;
use App\Traits\SeoTrait;
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
@@ -11,85 +11,30 @@ use Livewire\WithFileUploads;
new
#[SeoDataAttribute(key: 'services_create')]
class extends Component {
- use WithFileUploads;
use SeoTrait;
- #[Validate('image|max:10240')] // 10MB
- public $logo;
+ public string $country = 'de';
+ public ServiceForm $form;
- public string $name = '';
- public ?string $intro = null;
- public ?string $url_clearnet = null;
- public ?string $url_onion = null;
- public ?string $url_i2p = null;
- public ?string $url_pkdns = null;
- public ?string $type = null;
- public ?string $contact = null;
- public bool $anonymous = false;
-
- protected function rules(): array
+ public function mount(): void
{
- return [
- 'name' => ['required', 'string', 'max:255'],
- 'type' => [
- 'required', 'in:'.collect(SelfHostedServiceType::cases())->map(fn($c) => $c->value)->implode(',')
- ],
- 'intro' => ['nullable', 'string'],
- 'url_clearnet' => ['nullable', 'url', 'max:255'],
- 'url_onion' => ['nullable', 'string', 'max:255'],
- 'url_i2p' => ['nullable', 'string', 'max:255'],
- 'url_pkdns' => ['nullable', 'string', 'max:255'],
- 'contact' => ['nullable', 'string'],
- 'anonymous' => ['boolean'],
- ];
- }
-
- protected function validateAtLeastOneUrl(): void
- {
- if (empty($this->url_clearnet) && empty($this->url_onion) && empty($this->url_i2p) && empty($this->url_pkdns)) {
- $this->addError('url_clearnet', __('Mindestens eine URL muss angegeben werden.'));
- throw new \Illuminate\Validation\ValidationException(
- validator([], [])
- );
- }
+ $this->country = request()->route('country', config('app.domain_country'));
}
public function save(): void
{
- $validated = $this->validate();
-
- $this->validateAtLeastOneUrl();
-
- /** @var SelfHostedService $service */
- $service = SelfHostedService::create([
- 'name' => $validated['name'],
- 'type' => $validated['type'],
- 'intro' => $validated['intro'] ?? null,
- 'url_clearnet' => $validated['url_clearnet'] ?? null,
- 'url_onion' => $validated['url_onion'] ?? null,
- 'url_i2p' => $validated['url_i2p'] ?? null,
- 'url_pkdns' => $validated['url_pkdns'] ?? null,
- 'contact' => $validated['contact'] ?? null,
- 'created_by' => $this->anonymous ? null : auth()->id(),
- ]);
-
- if ($this->logo) {
- $service
- ->addMedia($this->logo->getRealPath())
- ->usingFileName($this->logo->getClientOriginalName())
- ->toMediaCollection('logo');
- }
+ $service = $this->form->store();
session()->flash('status', __('Service erfolgreich erstellt!'));
- redirect()->route('services.index', ['country' => request()->route('country')]);
+ redirect()->route('services.index', ['country' => $this->country]);
}
public function with(): array
{
return [
'types' => collect(SelfHostedServiceType::cases())->map(fn($c) => [
- 'value' => $c->value, 'label' => ucfirst($c->value)
+ 'value' => $c->value, 'label' => $c->label()
]),
];
}
@@ -105,57 +50,37 @@ class extends Component {