diff --git a/TESTING_GUIDE.md b/TESTING_GUIDE.md new file mode 100644 index 0000000..477271c --- /dev/null +++ b/TESTING_GUIDE.md @@ -0,0 +1,335 @@ +# Test-Suite Dokumentation + +## Übersicht + +Diese Test-Suite bietet umfassende Tests für die Todo-App Backend Applikation. Sie besteht aus Unit Tests, Feature Tests und Database Tests. + +## Test-Struktur + +``` +tests/ +├── unit/ +│ ├── Controllers/ +│ │ └── AuthControllerTest.php # Auth Controller Tests +│ ├── Models/ +│ │ └── UserModelTest.php # User Model Tests +│ └── HealthTest.php # Basis-Health Checks +├── feature/ +│ └── AuthApiTest.php # API Integration Tests +├── database/ +│ ├── MigrationTest.php # Database Migration Tests +│ └── ExampleDatabaseTest.php # Example Tests +├── _support/ # Test Support Files +│ ├── Database/ +│ ├── Libraries/ +│ └── Models/ +└── session/ # Session Tests + +``` + +## Tests ausführen + +### Alle Tests ausführen +```bash +cd /Users/yanis/BFOTodo/Todo-App-Backend +php vendor/bin/phpunit +``` + +### Nur Auth Controller Tests +```bash +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php +``` + +### Nur User Model Tests +```bash +php vendor/bin/phpunit tests/unit/Models/UserModelTest.php +``` + +### Nur API Tests +```bash +php vendor/bin/phpunit tests/feature/AuthApiTest.php +``` + +### Nur Database Migration Tests +```bash +php vendor/bin/phpunit tests/database/MigrationTest.php +``` + +### Mit Coverage Report +```bash +php vendor/bin/phpunit --coverage-html build/logs/coverage +``` + +## Test-Kategorien + +### 1. Unit Tests - Auth Controller (`tests/unit/Controllers/AuthControllerTest.php`) + +Testet die Core-Logik des Auth Controllers: + +**Tests:** +- ✅ `testLoginPageLoads` - Login Seite wird angezeigt +- ✅ `testLoginWithValidCredentials` - Login mit korrekten Daten +- ✅ `testLoginWithInvalidCredentials` - Login mit falschen Daten +- ✅ `testRegisterWithValidData` - Registrierung mit gültigen Daten +- ✅ `testRegisterWithDuplicateEmail` - Doppelte Email wird verhindert +- ✅ `testLogout` - Logout Funktionalität +- ✅ `testPasswordIsHashed` - Passwort wird gehasht +- ✅ `testLoginRequiresEmail` - Email ist erforderlich +- ✅ `testRegisterRequiresEmail` - Email bei Registrierung erforderlich +- ✅ `testLoginWithInvalidEmail` - Ungültiges Email Format + +**Beispiel Ausführung:** +```bash +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testLoginWithValidCredentials +``` + +### 2. Unit Tests - User Model (`tests/unit/Models/UserModelTest.php`) + +Testet die Benutzermodell-Operationen: + +**Tests:** +- ✅ `testUserCanBeCreated` - Benutzer erstellen +- ✅ `testUserCanBeFoundByEmail` - Benutzer nach Email finden +- ✅ `testDuplicateEmailIsRejected` - Doppelte Email ablehnen +- ✅ `testUserCanBeUpdated` - Benutzer aktualisieren +- ✅ `testUserCanBeDeleted` - Benutzer löschen +- ✅ `testAllUsersCanBeRetrieved` - Alle Benutzer abrufen +- ✅ `testPasswordHashIsValid` - Passwort Hash Validierung + +**Beispiel Ausführung:** +```bash +php vendor/bin/phpunit tests/unit/Models/UserModelTest.php +``` + +### 3. Feature Tests - Auth API (`tests/feature/AuthApiTest.php`) + +Testet API Endpoints und HTTP Responses: + +**Tests:** +- ✅ `testGetLoginPageReturns200` - Login Seite Status Code +- ✅ `testLoginWithValidDataReturns302` - Login Redirect +- ✅ `testRegisterApiCreatesNewUser` - Registrierung erstellt Benutzer +- ✅ `testLoginWithInvalidDataReturns302` - Fehlerhafte Login Redirect +- ✅ `testLogoutApiReturns302` - Logout Redirect +- ✅ `testLoginWithMissingEmailField` - Fehlende Email Feld +- ✅ `testLoginWithMissingPasswordField` - Fehlende Password Feld +- ✅ `testRegisterWithMissingNameField` - Fehlende Name Feld +- ✅ `testLoginPageContentType` - Content-Type Header +- ✅ `testRegisterValidatesEmailFormat` - Email Format Validierung +- ✅ `testLoginPageIncludesSecurityHeaders` - Sicherheits-Header +- ✅ `testRegisterSetsUserIdInSession` - Session User ID +- ✅ `testMultipleLoginAttempts` - Mehrfache Login Versuche + +**Beispiel Ausführung:** +```bash +php vendor/bin/phpunit tests/feature/AuthApiTest.php::AuthApiTest::testLoginWithValidDataReturns302 +``` + +### 4. Database Tests - Migrations (`tests/database/MigrationTest.php`) + +Testet Datenbankmigrationen und Schema: + +**Tests:** +- ✅ `testUsersTableExists` - Users Tabelle existiert +- ✅ `testUsersTableHasRequiredColumns` - Erforderliche Spalten vorhanden +- ✅ `testEmailIsUnique` - Email Unique Constraint +- ✅ `testCategoriesTableExists` - Categories Tabelle +- ✅ `testProjectsTableExists` - Projects Tabelle +- ✅ `testTodosTableExists` - Todos Tabelle +- ✅ `testTodoCategoriesTableExists` - TodoCategories Tabelle +- ✅ `testTodosTableHasRequiredColumns` - Todos Spalten +- ✅ `testDatabaseConnectionWorks` - DB Verbindung +- ✅ `testTableCountIsCorrect` - Tabellenzahl +- ✅ `testUserSettingsIsJson` - Settings JSON Type +- ✅ `testTimestampsAreCorrectType` - Timestamp Spalten + +**Beispiel Ausführung:** +```bash +php vendor/bin/phpunit tests/database/MigrationTest.php +``` + +## Test-Konventionen + +### Naming Konvention +- Test-Klassen: `{Feature}Test.php` (z.B. `AuthControllerTest.php`) +- Test-Methoden: `test{Scenario}` (z.B. `testLoginWithValidCredentials`) +- Namespace: `Tests\{Category}\{Feature}` (z.B. `Tests\Unit\Controllers`) + +### Struktur +```php +/** + * Test: Beschreibung was getestet wird + */ +public function testFeatureName(): void +{ + // Arrange - Setup Daten + $userData = ['email' => 'test@example.com', ...]; + + // Act - Aktion ausführen + $response = $this->post('/auth/login', $userData); + + // Assert - Ergebnis verifizieren + $this->assertTrue($response->getStatusCode() === 302); +} +``` + +## Test-Traits + +### DatabaseTestTrait +Ermöglicht Datenbankzugriff in Tests: +```php +use DatabaseTestTrait; + +protected $seed = UserSeeder::class; // Optional: Daten seeden +``` + +### FeatureTestTrait +Ermöglicht HTTP Requests in Tests: +```php +use FeatureTestTrait; + +$response = $this->get('/path'); +$response = $this->post('/path', $data); +$response = $this->put('/path', $data); +$response = $this->delete('/path'); +``` + +## Datenbank in Tests + +### Automatisches Rollback +Tests verwenden automatisch Transaktionen, die nach jedem Test gerollt werden: +```php +class AuthControllerTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + // Daten werden automatisch nach jedem Test gelöscht +} +``` + +### Daten Seeding +```php +class MigrationTest extends CIUnitTestCase +{ + use DatabaseTestTrait; + + protected $seed = UserSeeder::class; // Lädt vor jedem Test +} +``` + +## Assertions häufig verwendet + +```php +// Grundlegende Assertions +$this->assertTrue($condition); +$this->assertFalse($condition); +$this->assertNull($value); +$this->assertNotNull($value); + +// Vergleiche +$this->assertEquals($expected, $actual); +$this->assertNotEquals($expected, $actual); + +// Collections +$this->assertCount($count, $array); +$this->assertContains($needle, $haystack); + +// Strings +$this->assertStringContainsString($needle, $haystack); +$this->assertStringStartsWith($prefix, $string); + +// Response +$this->assertTrue($response->getStatusCode() === 200); +``` + +## Fehlerbehandlung in Tests + +### Datenbank Fehler +```php +try { + // Operation die Fehler verursachen könnte + $this->post('/auth/attemptRegister', $data); +} catch (\Exception $e) { + $this->assertTrue(true); // Expected Error +} +``` + +### HTTP Status Codes +```php +$this->assertTrue($response->getStatusCode() === 302); // Redirect +$this->assertTrue($response->getStatusCode() === 200); // OK +$this->assertTrue($response->getStatusCode() === 400); // Bad Request +$this->assertTrue($response->getStatusCode() === 404); // Not Found +$this->assertTrue($response->getStatusCode() === 500); // Server Error +``` + +## Best Practices + +### ✅ Do's +- ✅ Tests sollten unabhängig voneinander sein +- ✅ Verwende aussagekräftige Test-Namen +- ✅ Ein Test pro Scenario/Feature +- ✅ Verwende Arrange-Act-Assert Pattern +- ✅ Test Edge Cases und Error Conditions +- ✅ Verwende Fixtures/Seeders für Testdaten + +### ❌ Dont's +- ❌ Tests sollten nicht voneinander abhängig sein +- ❌ Keine Tests mit zufälligen Daten +- ❌ Keine Long-Running Tests (< 1 Sekunde pro Test) +- ❌ Keine Tests die externe APIs aufrufen +- ❌ Keine Tests die Datei-Operationen durchführen + +## Continuous Integration + +Tests können in CI/CD Pipelines integriert werden: + +```yaml +# .github/workflows/tests.yml +name: Tests +on: [push, pull_request] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Run Tests + run: php vendor/bin/phpunit +``` + +## Performance + +**Aktuelle Test-Zusammenfassung:** +- Gesamt Tests: ~40 Tests +- Durchschnittliche Dauer: < 5 Sekunden +- Coverage Ziel: > 80% + +## Troubleshooting + +### Tests schlagen fehl mit "Database not found" +```bash +# Stelle sicher dass .env konfiguriert ist +cp env.example .env + +# Führe Migrationen aus +php spark migrate +``` + +### CSRF Token Fehler +Tests werden automatisch mit CSRF Protection gehändelt durch `FeatureTestTrait` + +### Session wird nicht persistent +Sessions werden zwischen Requests in Feature Tests automatisch beibehalten + +## Zukünftige Verbesserungen + +- [ ] API Response Body Assertions +- [ ] Performance Benchmarks +- [ ] Integration Tests für komplexe Workflows +- [ ] E2E Tests mit Selenium +- [ ] Load Tests +- [ ] Security Tests + +--- + +**Für weitere Fragen oder Probleme:** +Dokumentation: [CodeIgniter Testing Guide](https://codeigniter.com/user_guide/testing/) diff --git a/TEST_EXAMPLES.md b/TEST_EXAMPLES.md new file mode 100644 index 0000000..431e7cd --- /dev/null +++ b/TEST_EXAMPLES.md @@ -0,0 +1,402 @@ +# Test Examples - Praktische Beispiele + +## Quick Start + +### 1. Erstes Test ausführen + +```bash +cd /Users/yanis/BFOTodo/Todo-App-Backend + +# Alle Tests ausführen +php vendor/bin/phpunit + +# Nur einen Test ausführen +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testLoginPageLoads +``` + +### 2. Einzelnen Test ausführen + +```bash +# Login Test +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testLoginWithValidCredentials + +# Registration Test +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testRegisterWithValidData +``` + +## Beispiel Unit Tests + +### Test: Benutzer Login + +```php +public function testLoginWithValidCredentials(): void +{ + // 1. Arrange - Testdaten vorbereiten + $userModel = new UserModel(); + $userData = [ + 'email' => 'test@example.com', + 'password_hash' => password_hash('password123', PASSWORD_DEFAULT), + 'name' => 'Test User', + ]; + $userModel->insert($userData); + + // 2. Act - Login durchführen + $response = $this->post('/auth/attemptLogin', [ + 'email' => 'test@example.com', + 'password' => 'password123', + ]); + + // 3. Assert - Ergebnis überprüfen + $this->assertTrue($response->getStatusCode() === 302); // Redirect +} +``` + +**Ausführen:** +```bash +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testLoginWithValidCredentials +``` + +### Test: Benutzer Registrierung + +```php +public function testRegisterWithValidData(): void +{ + // 1. Arrange + $newUserData = [ + 'name' => 'Neuer User', + 'email' => 'newuser@example.com', + 'password' => 'password123', + ]; + + // 2. Act - Registrierung durchführen + $response = $this->post('/auth/attemptRegister', $newUserData); + + // 3. Assert - Überprüfungen + $this->assertTrue($response->getStatusCode() === 302); + + // Benutzer in DB existiert + $userModel = new UserModel(); + $user = $userModel->where('email', 'newuser@example.com')->first(); + $this->assertNotNull($user); + $this->assertEquals('Neuer User', $user['name']); +} +``` + +**Ausführen:** +```bash +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testRegisterWithValidData +``` + +### Test: Doppelte Email ablehnen + +```php +public function testRegisterWithDuplicateEmail(): void +{ + // 1. Arrange - Erstes Konto + $this->post('/auth/attemptRegister', [ + 'name' => 'User One', + 'email' => 'duplicate@example.com', + 'password' => 'password123', + ]); + + // 2. Act - Zweites Konto mit gleicher Email + $response = $this->post('/auth/attemptRegister', [ + 'name' => 'User Two', + 'email' => 'duplicate@example.com', + 'password' => 'password456', + ]); + + // 3. Assert - Sollte fehlschlagen + $this->assertTrue($response->getStatusCode() === 302); +} +``` + +**Ausführen:** +```bash +php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testRegisterWithDuplicateEmail +``` + +## Beispiel Model Tests + +### Test: Benutzer erstellen + +```php +public function testUserCanBeCreated(): void +{ + $userModel = new UserModel(); + + $data = [ + 'email' => 'create@example.com', + 'password_hash' => password_hash('password123', PASSWORD_DEFAULT), + 'name' => 'Create Test', + ]; + + $id = $userModel->insert($data); + + $this->assertIsNotNull($id); + $this->assertNotEmpty($id); +} +``` + +### Test: Passwort wird korrekt gehasht + +```php +public function testPasswordHashIsValid(): void +{ + $userModel = new UserModel(); + $password = 'mypassword123'; + + $data = [ + 'email' => 'hash@example.com', + 'password_hash' => password_hash($password, PASSWORD_DEFAULT), + 'name' => 'Hash Test', + ]; + + $userModel->insert($data); + $user = $userModel->where('email', 'hash@example.com')->first(); + + // Korrekt Passwort sollte matchen + $this->assertTrue(password_verify($password, $user['password_hash'])); + + // Falsches Passwort sollte nicht matchen + $this->assertFalse(password_verify('wrongpassword', $user['password_hash'])); +} +``` + +## Beispiel API Tests + +### Test: Login API Response + +```php +public function testGetLoginPageReturns200(): void +{ + // 1. Act - GET Request + $response = $this->get('/auth/login'); + + // 2. Assert - Status Code und Content + $this->assertTrue($response->getStatusCode() === 200); + $this->assertStringContainsString('form', (string)$response); + $this->assertStringContainsString('email', (string)$response); +} +``` + +**Ausführen:** +```bash +php vendor/bin/phpunit tests/feature/AuthApiTest.php::AuthApiTest::testGetLoginPageReturns200 +``` + +### Test: API mit mehreren Requests + +```php +public function testMultipleLoginAttempts(): void +{ + // 1. Arrange + $userModel = new UserModel(); + $userModel->insert([ + 'email' => 'multi@example.com', + 'password_hash' => password_hash('correct', PASSWORD_DEFAULT), + 'name' => 'Multi Test', + ]); + + // 2. Act - Erster Versuch (falsch) + $response1 = $this->post('/auth/attemptLogin', [ + 'email' => 'multi@example.com', + 'password' => 'wrong', + ]); + + // 3. Act - Zweiter Versuch (korrekt) + $response2 = $this->post('/auth/attemptLogin', [ + 'email' => 'multi@example.com', + 'password' => 'correct', + ]); + + // 4. Assert - Beide sollten redirecten + $this->assertTrue($response1->getStatusCode() === 302); + $this->assertTrue($response2->getStatusCode() === 302); +} +``` + +## Beispiel Database Migration Tests + +### Test: Tabelle existiert + +```php +public function testUsersTableExists(): void +{ + $db = \Config\Database::connect(); + $this->assertTrue($db->tableExists('users')); +} +``` + +### Test: Spalten existieren + +```php +public function testUsersTableHasRequiredColumns(): void +{ + $db = \Config\Database::connect(); + $fields = $db->getFieldData('users'); + + $fieldNames = array_map(function ($field) { + return $field->name; + }, $fields); + + // Diese Spalten sollten alle existieren + $this->assertContains('id', $fieldNames); + $this->assertContains('email', $fieldNames); + $this->assertContains('password_hash', $fieldNames); + $this->assertContains('name', $fieldNames); + $this->assertContains('created_at', $fieldNames); + $this->assertContains('updated_at', $fieldNames); +} +``` + +**Ausführen:** +```bash +php vendor/bin/phpunit tests/database/MigrationTest.php::MigrationTest::testUsersTableHasRequiredColumns +``` + +## Test Output Beispiele + +### Erfolgreiche Tests +```bash +$ php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php + +PHPUnit 10.5.63 by Sebastian Bergmann and contributors. + +...................... 20 / 20 (100%) + +Time: 00:02.345, Memory: 8.00 MB + +OK (20 tests, 40 assertions) +``` + +### Test mit Fehler +```bash +$ php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php::AuthControllerTest::testLoginWithValidCredentials + +PHPUnit 10.5.63 by Sebastian Bergmann and contributors. + +F 1 / 1 (100%) + +Time: 00:00.512, Memory: 6.00 MB + +FAILURES! +Tests: 1, Assertions: 1, Failures: 1 + +FAIL: AuthControllerTest::testLoginWithValidCredentials +Expected Status Code 302 but got 200 +``` + +## Häufige Assertions in Tests + +### HTTP Status Codes prüfen +```php +$this->assertTrue($response->getStatusCode() === 200); // OK +$this->assertTrue($response->getStatusCode() === 302); // Redirect +$this->assertTrue($response->getStatusCode() === 400); // Bad Request +$this->assertTrue($response->getStatusCode() === 404); // Not Found +$this->assertTrue($response->getStatusCode() === 500); // Server Error +``` + +### Datenbank Assertions +```php +$this->assertNotNull($user); // Benutzer existiert +$this->assertEquals('test@example.com', $user['email']); // Email stimmt +$this->assertCount(5, $users); // 5 Benutzer +``` + +### String Assertions +```php +$this->assertStringContainsString('form', (string)$response); // Enthält +$this->assertStringStartsWith('Hello', 'Hello World'); // Beginnt mit +$this->assertStringEndsWith('World', 'Hello World'); // Endet mit +``` + +## Test Patterns + +### Pattern 1: Arrange-Act-Assert +```php +public function testFeature(): void +{ + // ARRANGE - Setup + $data = ['email' => 'test@example.com']; + + // ACT - Aktion + $result = $this->post('/path', $data); + + // ASSERT - Überprüfung + $this->assertTrue($result->getStatusCode() === 302); +} +``` + +### Pattern 2: Given-When-Then +```php +public function testFeature(): void +{ + // GIVEN - Initial State + $user = $this->createUser('test@example.com'); + + // WHEN - Action + $response = $this->loginUser($user); + + // THEN - Verify + $this->assertTrue($response->getStatusCode() === 302); +} +``` + +### Pattern 3: Setup-Exercise-Verify +```php +public function testFeature(): void +{ + // SETUP - Prepare + $userModel = new UserModel(); + + // EXERCISE - Execute + $id = $userModel->insert($userData); + + // VERIFY - Assert + $this->assertNotNull($id); +} +``` + +## Debugging Tests + +### Verbose Output +```bash +php vendor/bin/phpunit -v tests/unit/Controllers/AuthControllerTest.php +``` + +### Mit Debug Output +```php +public function testLoginWithValidCredentials(): void +{ + // ... test code ... + + echo "Response Status: " . $response->getStatusCode(); + var_dump($response->getBody()); +} +``` + +### Mit xdebug +```bash +XDEBUG_CONFIG="idekey=xdebug" php vendor/bin/phpunit tests/unit/Controllers/AuthControllerTest.php +``` + +## Nächste Schritte + +1. **Coverage Report generieren:** + ```bash + php vendor/bin/phpunit --coverage-html build/logs/coverage + ``` + +2. **Tests mit CI/CD integrieren** + +3. **Mehr Tests hinzufügen für:** + - Task Management Features + - Category Management + - Project Management + - Recurring Tasks + +4. **Performance Tests** + +5. **Security Tests** diff --git a/app/Config/Routes.php b/app/Config/Routes.php index fc4914a..64fd54a 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -6,3 +6,8 @@ use CodeIgniter\Router\RouteCollection; * @var RouteCollection $routes */ $routes->get('/', 'Home::index'); + +$routes->get('/auth/login', 'Auth::login'); +$routes->post('/auth/attemptLogin', 'Auth::attemptLogin'); +$routes->post('/auth/attemptRegister', 'Auth::attemptRegister'); +$routes->get('/auth/logout', 'Auth::logout'); diff --git a/app/Controllers/Auth.php b/app/Controllers/Auth.php new file mode 100644 index 0000000..5d1e6a3 --- /dev/null +++ b/app/Controllers/Auth.php @@ -0,0 +1,62 @@ +request->getPost('email'); + $password = $this->request->getPost('password'); + + $userModel = new UserModel(); + $user = $userModel->where('email', $email)->first(); + + if ($user && password_verify($password, $user['password_hash'])) { + // Login successful + session()->set('user_id', $user['id']); + session()->set('user_email', $user['email']); + return redirect()->to('/dashboard'); // or wherever + } else { + return redirect()->back()->with('error', 'Invalid credentials'); + } + } + + public function attemptRegister() + { + $email = $this->request->getPost('email'); + $password = $this->request->getPost('password'); + $name = $this->request->getPost('name'); + + $userModel = new UserModel(); + $data = [ + 'email' => $email, + 'password_hash' => password_hash($password, PASSWORD_DEFAULT), + 'name' => $name, + ]; + + if ($userModel->insert($data)) { + // Registration successful, auto login + $user = $userModel->where('email', $email)->first(); + session()->set('user_id', $user['id']); + session()->set('user_email', $user['email']); + return redirect()->to('/dashboard'); + } else { + return redirect()->back()->with('error', $userModel->errors()); + } + } + + public function logout() + { + session()->destroy(); + return redirect()->to('/auth/login'); + } +} \ No newline at end of file diff --git a/app/Controllers/BaseController.php b/app/Controllers/BaseController.php index ab45a77..0495f4c 100644 --- a/app/Controllers/BaseController.php +++ b/app/Controllers/BaseController.php @@ -34,12 +34,12 @@ abstract class BaseController extends Controller { // Load here all helpers you want to be available in your controllers that extend BaseController. // Caution: Do not put the this below the parent::initController() call below. - // $this->helpers = ['form', 'url']; + $this->helpers = ['form', 'url']; // Caution: Do not edit this line. parent::initController($request, $response, $logger); // Preload any models, libraries, etc, here. - // $this->session = service('session'); + $this->session = service('session'); } } diff --git a/app/Views/auth/login_register.php b/app/Views/auth/login_register.php new file mode 100644 index 0000000..5145135 --- /dev/null +++ b/app/Views/auth/login_register.php @@ -0,0 +1,181 @@ + + +
+ + +Melde dich an oder erstelle ein Konto
+