mirror of
https://github.com/HolgerHatGarKeineNode/einundzwanzig-nostr.git
synced 2026-05-20 10:04:53 +00:00
🔥 Add initial database migrations, seeders, and factories
🎨 Refactor `Lecturer` model to include new fields and factory usage 🔧 Update `DatabaseSeeder` to handle default seeds 🛠️ Enhance `einundzwanzig` database configuration for SQLite compatibility
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Category;
|
||||
use App\Models\Course;
|
||||
use App\Models\CourseEvent;
|
||||
use App\Models\Lecturer;
|
||||
use App\Models\Venue;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class CourseSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$categories = collect([
|
||||
'Grundlagen',
|
||||
'Self Custody',
|
||||
'Lightning',
|
||||
'Nostr',
|
||||
'Privacy',
|
||||
'Wirtschaft',
|
||||
])->map(fn (string $name) => Category::query()->create(['name' => $name]));
|
||||
|
||||
$markus = Lecturer::factory()->markusTurm()->create();
|
||||
$otherLecturers = Lecturer::factory()->count(3)->create();
|
||||
|
||||
$bitcoinBasics = Course::factory()->bitcoinBasics()->for($markus)->create();
|
||||
$bitcoinBasics->categories()->attach([
|
||||
$categories->firstWhere('name', 'Grundlagen')->id,
|
||||
$categories->firstWhere('name', 'Wirtschaft')->id,
|
||||
]);
|
||||
|
||||
$lightningCourse = Course::factory()
|
||||
->state(['name' => 'Lightning Network 101'])
|
||||
->for($markus)
|
||||
->create();
|
||||
$lightningCourse->categories()->attach($categories->firstWhere('name', 'Lightning')->id);
|
||||
|
||||
foreach ($otherLecturers as $lecturer) {
|
||||
$course = Course::factory()->for($lecturer)->create();
|
||||
$course->categories()->attach($categories->random(rand(1, 3))->pluck('id'));
|
||||
}
|
||||
|
||||
$venues = Venue::query()->take(3)->get();
|
||||
if ($venues->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Course::query()->get() as $course) {
|
||||
CourseEvent::factory()->for($course)->for($venues->random())->past()->create();
|
||||
CourseEvent::factory()->for($course)->for($venues->random())->create();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,26 @@
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
User::query()->updateOrCreate(
|
||||
['email' => 'test@example.com'],
|
||||
['name' => 'Test User', 'password' => bcrypt('password')]
|
||||
);
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
$this->call([
|
||||
PlebSeeder::class,
|
||||
ProjectProposalSeeder::class,
|
||||
ElectionSeeder::class,
|
||||
NostrSeeder::class,
|
||||
MeetupSeeder::class,
|
||||
CourseSeeder::class,
|
||||
NotificationSeeder::class,
|
||||
SecuritySeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Election;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ElectionSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$candidates = config('einundzwanzig.config.current_board');
|
||||
|
||||
Election::query()->updateOrCreate(
|
||||
['year' => 2025],
|
||||
[
|
||||
'candidates' => $candidates,
|
||||
'end_time' => now()->setDate(2025, 4, 15),
|
||||
]
|
||||
);
|
||||
|
||||
Election::query()->updateOrCreate(
|
||||
['year' => 2026],
|
||||
[
|
||||
'candidates' => $candidates,
|
||||
'end_time' => now()->addMonths(2),
|
||||
]
|
||||
);
|
||||
|
||||
Election::query()->updateOrCreate(
|
||||
['year' => 2027],
|
||||
[
|
||||
'candidates' => [],
|
||||
'end_time' => now()->addYear()->addMonths(3),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\City;
|
||||
use App\Models\Country;
|
||||
use App\Models\Meetup;
|
||||
use App\Models\MeetupEvent;
|
||||
use App\Models\Venue;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class MeetupSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$de = Country::factory()->germany()->create();
|
||||
$at = Country::factory()->austria()->create();
|
||||
$ch = Country::factory()->switzerland()->create();
|
||||
|
||||
$vienna = City::factory()->vienna()->for($at)->create();
|
||||
$berlin = City::factory()->berlin()->for($de)->create();
|
||||
$munich = City::factory()->munich()->for($de)->create();
|
||||
$zurich = City::factory()->zurich()->for($ch)->create();
|
||||
|
||||
$viennaMeetup = Meetup::factory()->vienna()->for($vienna)->create();
|
||||
$berlinMeetup = Meetup::factory()->berlin()->for($berlin)->create();
|
||||
$munichMeetup = Meetup::factory()->state(['name' => 'Einundzwanzig München'])->for($munich)->create();
|
||||
$zurichMeetup = Meetup::factory()->state(['name' => 'Einundzwanzig Zürich'])->for($zurich)->create();
|
||||
|
||||
Venue::factory()->bitcoinBarVienna()->for($vienna)->create();
|
||||
Venue::factory()->count(2)->for($berlin)->create();
|
||||
Venue::factory()->count(2)->for($munich)->create();
|
||||
Venue::factory()->count(1)->for($zurich)->create();
|
||||
|
||||
foreach ([$viennaMeetup, $berlinMeetup, $munichMeetup, $zurichMeetup] as $meetup) {
|
||||
MeetupEvent::factory()->past()->for($meetup)->count(2)->create();
|
||||
MeetupEvent::factory()->upcoming()->for($meetup)->count(2)->create();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Event;
|
||||
use App\Models\Profile;
|
||||
use App\Models\RenderedEvent;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class NostrSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
Profile::factory()->markusTurm()->create();
|
||||
|
||||
$boardProfiles = [
|
||||
[
|
||||
'pubkey' => '0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
||||
'name' => 'pleb1',
|
||||
'display_name' => 'Vorstandsmitglied 1',
|
||||
],
|
||||
[
|
||||
'pubkey' => '430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
||||
'name' => 'pleb2',
|
||||
'display_name' => 'Vorstandsmitglied 2',
|
||||
],
|
||||
[
|
||||
'pubkey' => '7acf30cf60b85c62b8f654556cc21e4016df8f5604b3b6892794f88bb80d7a1d',
|
||||
'name' => 'pleb3',
|
||||
'display_name' => 'Vorstandsmitglied 3',
|
||||
],
|
||||
[
|
||||
'pubkey' => '19e358b8011f5f4fc653c565c6d4c2f33f32661f4f90982c9eedc292a8774ec3',
|
||||
'name' => 'pleb4',
|
||||
'display_name' => 'Vorstandsmitglied 4',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($boardProfiles as $data) {
|
||||
Profile::query()->create([
|
||||
...$data,
|
||||
'about' => 'Vorstand bei Einundzwanzig. Bitcoin only.',
|
||||
'nip05' => $data['name'].'@einundzwanzig.space',
|
||||
'lud16' => $data['name'].'@walletofsatoshi.com',
|
||||
'website' => 'https://einundzwanzig.space',
|
||||
'picture' => 'https://m.primal.net/'.fake()->uuid().'.jpg',
|
||||
'deleted' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
Event::factory()
|
||||
->fromMarkusTurm()
|
||||
->count(5)
|
||||
->create()
|
||||
->each(function (Event $event): void {
|
||||
RenderedEvent::query()->create([
|
||||
'event_id' => $event->event_id,
|
||||
'html' => '<div class="prose"><p>'.fake()->paragraph().'</p></div>',
|
||||
'profile_image' => 'https://m.primal.net/HQqf.jpg',
|
||||
'profile_name' => 'markusturm',
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\NewsCategory;
|
||||
use App\Models\EinundzwanzigPleb;
|
||||
use App\Models\Notification;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class NotificationSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$markus = EinundzwanzigPleb::query()
|
||||
->where('npub', 'npub17fqtu2mgf7zueq2kdusgzwr2lqwhgfl2scjsez77ddag2qx8vxaq3vnr8y')
|
||||
->first() ?? EinundzwanzigPleb::query()->first();
|
||||
|
||||
if (! $markus) {
|
||||
return;
|
||||
}
|
||||
|
||||
$news = [
|
||||
[
|
||||
'name' => 'Generalversammlung 2026 - Save the Date',
|
||||
'description' => "Die nächste Generalversammlung findet am 21. Juni 2026 in Wien statt. Alle Mitglieder sind herzlich eingeladen.\n\nAgenda:\n- Bericht des Vorstands\n- Wahl des neuen Vorstands\n- Project Support Abstimmungen",
|
||||
'category' => NewsCategory::Veranstaltungen,
|
||||
],
|
||||
[
|
||||
'name' => 'Neuer Lightning Watchtower verfügbar',
|
||||
'description' => 'Mitglieder können ab sofort unseren Watchtower nutzen. Details zur Konfiguration im Mitgliederbereich.',
|
||||
'category' => NewsCategory::Bitcoin,
|
||||
],
|
||||
[
|
||||
'name' => 'Meetup-Welle im Sommer 2026',
|
||||
'description' => 'Über 30 Einundzwanzig Meetups im DACH-Raum geplant. Termine im Portal.',
|
||||
'category' => NewsCategory::Meetups,
|
||||
],
|
||||
[
|
||||
'name' => 'Q1 2026 Finanzbericht',
|
||||
'description' => 'Der Finanzbericht für das erste Quartal 2026 ist im Mitgliederbereich abrufbar.',
|
||||
'category' => NewsCategory::Finanzen,
|
||||
],
|
||||
[
|
||||
'name' => 'Neue Bildungsinitiative gestartet',
|
||||
'description' => 'Die Einundzwanzig Bitcoin Schule startet im September. Anmeldung ab sofort möglich.',
|
||||
'category' => NewsCategory::Bildung,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($news as $item) {
|
||||
Notification::query()->create([
|
||||
...$item,
|
||||
'einundzwanzig_pleb_id' => $markus->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Enums\AssociationStatus;
|
||||
use App\Models\EinundzwanzigPleb;
|
||||
use App\Models\PaymentEvent;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class PlebSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @var array<int, array{npub:string, pubkey:string, email:string, nip05:string, status:AssociationStatus, paid_years:array<int>}>
|
||||
*/
|
||||
private array $boardMembers = [
|
||||
[
|
||||
'npub' => 'npub17fqtu2mgf7zueq2kdusgzwr2lqwhgfl2scjsez77ddag2qx8vxaq3vnr8y',
|
||||
'pubkey' => 'f240be2b684f85cc81566f2081386af81d7427ea86250c8bde6b7a8500c761ba',
|
||||
'email' => 'markus@einundzwanzig.space',
|
||||
'nip05' => 'markusturm',
|
||||
'status' => AssociationStatus::HONORARY,
|
||||
'paid_years' => [2024, 2025, 2026],
|
||||
'name' => 'Markus Turm',
|
||||
],
|
||||
[
|
||||
'npub' => 'npub1pt0kw36ue3w2g4haxq3wgm6a2fhtptmzsjlc2j2vphtcgle72qesgpjyc6',
|
||||
'pubkey' => '0adf67475ccc5ca456fd3022e46f5d526eb0af6284bf85494c0dd7847f3e5033',
|
||||
'email' => 'board1@einundzwanzig.space',
|
||||
'nip05' => 'pleb1',
|
||||
'status' => AssociationStatus::HONORARY,
|
||||
'paid_years' => [2024, 2025, 2026],
|
||||
'name' => 'Vorstand 1',
|
||||
],
|
||||
[
|
||||
'npub' => 'npub1gvqkjccl9urg93svaw60jqkk3ux8r3ycl5t3rlvc9uzjeu0agfuss8x8qy',
|
||||
'pubkey' => '430169631f2f0682c60cebb4f902d68f0c71c498fd1711fd982f052cf1fd4279',
|
||||
'email' => 'board2@einundzwanzig.space',
|
||||
'nip05' => 'pleb2',
|
||||
'status' => AssociationStatus::HONORARY,
|
||||
'paid_years' => [2025, 2026],
|
||||
'name' => 'Vorstand 2',
|
||||
],
|
||||
[
|
||||
'npub' => 'npub10t8npnmqhpwx9w8k232kess7gqtdlr6kqjemdzf8jnughwqd0gwsez0924',
|
||||
'pubkey' => '7acf30cf60b85c62b8f654556cc21e4016df8f5604b3b6892794f88bb80d7a1d',
|
||||
'email' => 'board3@einundzwanzig.space',
|
||||
'nip05' => 'pleb3',
|
||||
'status' => AssociationStatus::HONORARY,
|
||||
'paid_years' => [2025, 2026],
|
||||
'name' => 'Vorstand 3',
|
||||
],
|
||||
[
|
||||
'npub' => 'npub1r8343wqpra05l3jnc4jud4xz7vlnyeslf7gfsty7ahpf92rhfmpsmqwym8',
|
||||
'pubkey' => '19e358b8011f5f4fc653c565c6d4c2f33f32661f4f90982c9eedc292a8774ec3',
|
||||
'email' => 'board4@einundzwanzig.space',
|
||||
'nip05' => 'pleb4',
|
||||
'status' => AssociationStatus::HONORARY,
|
||||
'paid_years' => [2026],
|
||||
'name' => 'Vorstand 4',
|
||||
],
|
||||
];
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
foreach ($this->boardMembers as $member) {
|
||||
$pleb = EinundzwanzigPleb::query()->create([
|
||||
'npub' => $member['npub'],
|
||||
'pubkey' => $member['pubkey'],
|
||||
'email' => $member['email'],
|
||||
'nip05_handle' => $member['nip05'],
|
||||
'association_status' => $member['status'],
|
||||
'application_text' => 'Ich bin Teil des Einundzwanzig Vorstands und unterstütze die Mission, Bitcoin in den deutschsprachigen Raum zu bringen.',
|
||||
]);
|
||||
|
||||
foreach ($member['paid_years'] as $year) {
|
||||
PaymentEvent::query()->create([
|
||||
'einundzwanzig_pleb_id' => $pleb->id,
|
||||
'year' => $year,
|
||||
'amount' => 21000,
|
||||
'paid' => true,
|
||||
'event_id' => 'seed_'.bin2hex(random_bytes(16)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
EinundzwanzigPleb::factory()
|
||||
->count(8)
|
||||
->active()
|
||||
->create()
|
||||
->each(function (EinundzwanzigPleb $pleb): void {
|
||||
PaymentEvent::factory()
|
||||
->paid()
|
||||
->withYear((int) date('Y'))
|
||||
->for($pleb, 'pleb')
|
||||
->create();
|
||||
});
|
||||
|
||||
EinundzwanzigPleb::factory()
|
||||
->count(5)
|
||||
->state(['association_status' => AssociationStatus::PASSIVE])
|
||||
->create();
|
||||
|
||||
EinundzwanzigPleb::factory()
|
||||
->count(3)
|
||||
->state([
|
||||
'association_status' => AssociationStatus::DEFAULT,
|
||||
'application_text' => 'Ich möchte Mitglied bei Einundzwanzig werden und die Bitcoin-Community im deutschsprachigen Raum mitgestalten.',
|
||||
])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\EinundzwanzigPleb;
|
||||
use App\Models\ProjectProposal;
|
||||
use App\Models\Vote;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class ProjectProposalSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* @var array<int, array{name:string, description:string, support_in_sats:int, website:string, accepted:bool}>
|
||||
*/
|
||||
private array $proposals = [
|
||||
[
|
||||
'name' => 'Einundzwanzig Portal Refactoring',
|
||||
'description' => "Das Einundzwanzig Portal benötigt ein größeres Refactoring, um die Performance zu verbessern und neue Features wie Meetup-Anmeldung über Nostr DMs zu ermöglichen.\n\n**Geplante Änderungen:**\n- Migration auf Livewire 4\n- Nostr Login per NIP-46\n- Meetup Kalender als ICS-Feed\n- API für externe Tools",
|
||||
'support_in_sats' => 2_100_000,
|
||||
'website' => 'https://github.com/einundzwanzig-portal/einundzwanzig-portal',
|
||||
'accepted' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'Bitcoin Schule für Plebs',
|
||||
'description' => "Curriculum für eine deutschsprachige Bitcoin-Schule mit modular aufgebauten Kursen für Einsteiger und Fortgeschrittene.\n\n- Onboarding für totale Beginner\n- Self Custody Workshops\n- Lightning Network Hands-on\n- Privacy & Coinjoin Module",
|
||||
'support_in_sats' => 1_500_000,
|
||||
'website' => 'https://einundzwanzig.school',
|
||||
'accepted' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'Lightning Watchtower für Mitglieder',
|
||||
'description' => 'Hosting eines Lightning Watchtower Service exklusiv für Einundzwanzig Mitglieder, um Channel-Verlust durch böswillige Counterparties zu verhindern.',
|
||||
'support_in_sats' => 500_000,
|
||||
'website' => 'https://einundzwanzig.space/benefits',
|
||||
'accepted' => false,
|
||||
],
|
||||
[
|
||||
'name' => 'Nostr Relay Hosting',
|
||||
'description' => 'Betrieb eines schnellen Nostr Relay (`wss://simple-relay.codingarena.top`) für die Einundzwanzig Community. Optimiert für deutschsprachige Inhalte und Meetup-Events.',
|
||||
'support_in_sats' => 800_000,
|
||||
'website' => 'https://simple-relay.codingarena.top',
|
||||
'accepted' => true,
|
||||
],
|
||||
[
|
||||
'name' => 'Meetup Sticker Druck Q3 2026',
|
||||
'description' => 'Bestellung von 5000 Einundzwanzig Stickern für die Meetups im DACH-Raum. Verteilung über die lokalen Meetup Organisatoren.',
|
||||
'support_in_sats' => 210_000,
|
||||
'website' => 'https://einundzwanzig.space',
|
||||
'accepted' => false,
|
||||
],
|
||||
];
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$plebs = EinundzwanzigPleb::query()->get();
|
||||
if ($plebs->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$markus = EinundzwanzigPleb::query()
|
||||
->where('npub', 'npub17fqtu2mgf7zueq2kdusgzwr2lqwhgfl2scjsez77ddag2qx8vxaq3vnr8y')
|
||||
->first();
|
||||
|
||||
foreach ($this->proposals as $index => $data) {
|
||||
$pleb = $markus && $index < 2 ? $markus : $plebs->random();
|
||||
|
||||
$proposal = ProjectProposal::query()->create([
|
||||
'einundzwanzig_pleb_id' => $pleb->id,
|
||||
'name' => $data['name'],
|
||||
'description' => $data['description'],
|
||||
'support_in_sats' => $data['support_in_sats'],
|
||||
'website' => $data['website'],
|
||||
'accepted' => $data['accepted'],
|
||||
'sats_paid' => $data['accepted'] ? $data['support_in_sats'] : null,
|
||||
]);
|
||||
|
||||
foreach ($plebs->random(min($plebs->count(), 6)) as $voter) {
|
||||
Vote::query()->updateOrCreate(
|
||||
[
|
||||
'einundzwanzig_pleb_id' => $voter->id,
|
||||
'project_proposal_id' => $proposal->id,
|
||||
],
|
||||
[
|
||||
'value' => fake()->boolean(70),
|
||||
'reason' => fake()->optional(0.4)->sentence(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\SecurityAttempt;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class SecuritySeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
SecurityAttempt::factory()->count(15)->create();
|
||||
SecurityAttempt::factory()->high()->count(4)->create();
|
||||
SecurityAttempt::factory()->critical()->count(1)->create();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user