Basic user management
This commit is contained in:
29
app/Domains/Invoice/Views/CreateInvoice.vue
Normal file
29
app/Domains/Invoice/Views/CreateInvoice.vue
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Neue Abrechnung' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<!-- Alles hier wird in den Slot von AppLayout eingefügt -->
|
||||||
|
<h2>Dashboard Content</h2>
|
||||||
|
<p>Test 1!
|
||||||
|
Hier wird mal eine Rechnung erstellt.
|
||||||
|
Wenn es geht oder auch nicht</p>
|
||||||
|
{{props.tenant}}
|
||||||
|
|
||||||
|
<button @click="$toast.success('Hallo vom Dashboard!')">Test Toaster</button>
|
||||||
|
<button @click="$toast.error('Soe sieht ein Fehler aus')">Error Toaster</button>
|
||||||
|
</AppLayout>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\MessageSystem\Actions\SendMessage;
|
||||||
|
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
use Illuminate\Support\Facades\Mail;
|
||||||
|
|
||||||
|
class SendMessageCommand {
|
||||||
|
private SendMessageRequest $request;
|
||||||
|
|
||||||
|
public function __construct(SendMessageRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send() : SendMessageResponse {
|
||||||
|
$response = new SendMessageResponse();
|
||||||
|
|
||||||
|
foreach ($this->request->messageTypes as $messageType) {
|
||||||
|
switch (true) {
|
||||||
|
case $messageType->value === MessageType::EMAIL->value:
|
||||||
|
$this->sendAsEmail();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case $messageType->value === MessageType::INTERNAL->value:
|
||||||
|
$this->sendAsInternalMessage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendAsEmail() {
|
||||||
|
foreach ($this->request->recipient->getEmailAddresses() as $emailAddress) {
|
||||||
|
Mail::html($this->request->message, function ($message) use ($emailAddress) {
|
||||||
|
$message
|
||||||
|
->to($emailAddress->getValue(), $this->request->recipient->getName())
|
||||||
|
->subject($this->request->subject);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendAsInternalMessage() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\MessageSystem\Actions\SendMessage;
|
||||||
|
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
use App\ValueObjects\MessageRecipient;
|
||||||
|
|
||||||
|
class SendMessageRequest {
|
||||||
|
public string $message;
|
||||||
|
public string $subject;
|
||||||
|
|
||||||
|
public MessageRecipient $recipient;
|
||||||
|
|
||||||
|
/** @var MessageType[] */
|
||||||
|
public array $messageTypes;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $message,
|
||||||
|
string $subject,
|
||||||
|
MessageRecipient $recipient,
|
||||||
|
array $messageTypes
|
||||||
|
) {
|
||||||
|
$this->message = $message;
|
||||||
|
$this->recipient = $recipient;
|
||||||
|
$this->subject = $subject;
|
||||||
|
$this->messageTypes = $messageTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\MessageSystem\Actions\SendMessage;
|
||||||
|
|
||||||
|
class SendMessageResponse {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\GenerateActivationToken;
|
||||||
|
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageCommand;
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageRequest;
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
use App\MessageTemplates\activationCodeTemplate;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
use App\ValueObjects\MessageRecipient;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class GenerateActivationTokenCommand {
|
||||||
|
private GenerateActivationTokenRequest $request;
|
||||||
|
|
||||||
|
public function __construct(GenerateActivationTokenRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : GenerateActivationTokenResponse {
|
||||||
|
$response = new GenerateActivationTokenResponse;
|
||||||
|
|
||||||
|
$activationCode = Str::password(24,true,true,false,false);
|
||||||
|
$this->request->user->activation_token = $activationCode;
|
||||||
|
if (null !== $this->request->expirationDate) {
|
||||||
|
$this->request->user->activation_token_expires_at = $this->request->expirationDate;
|
||||||
|
}
|
||||||
|
$this->request->user->save();
|
||||||
|
|
||||||
|
$response->activationCode = $activationCode;
|
||||||
|
|
||||||
|
$activationMessage = new activationCodeTemplate();
|
||||||
|
$activationMessage->composeMessage(EmailAddress::fromString($this->request->user->email), $activationCode);
|
||||||
|
|
||||||
|
$recipient = new MessageRecipient();
|
||||||
|
$recipient->addEmailAddress(EmailAddress::fromString($this->request->user->email));
|
||||||
|
$recipient->setName($this->request->user->getFullname());
|
||||||
|
|
||||||
|
$message = activationCodeTemplate::createForUser(EmailAddress::fromString($this->request->user->email), $activationCode);
|
||||||
|
|
||||||
|
$userMessageRequests = new SendMessageRequest($message->getMessage(), $message->getSubject(), $recipient, [MessageType::EMAIL]);
|
||||||
|
|
||||||
|
$userMessageCommand = new SendMessageCommand($userMessageRequests);
|
||||||
|
$userMessageCommand->send();
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\GenerateActivationToken;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class GenerateActivationTokenRequest {
|
||||||
|
public User $user;
|
||||||
|
public ?\DateTime $expirationDate;
|
||||||
|
|
||||||
|
public function __construct(User $user, ?\DateTime $expirationDate = null) {
|
||||||
|
$this->user = $user;
|
||||||
|
$this->expirationDate = $expirationDate;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\GenerateActivationToken;
|
||||||
|
|
||||||
|
class GenerateActivationTokenResponse {
|
||||||
|
public string $activationCode;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
class UserActivationCommand {
|
||||||
|
private UserActivationRequest $request;
|
||||||
|
public function __construct(UserActivationRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UserActivationResponse {
|
||||||
|
$response = new UserActivationResponse();
|
||||||
|
$this->request->user->active = true;
|
||||||
|
$this->request->user->activation_token = null;
|
||||||
|
$this->request->user->activation_token_expires_at = null;
|
||||||
|
$this->request->user->save();
|
||||||
|
$response->success = true;
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserActivationRequest {
|
||||||
|
public User $user;
|
||||||
|
|
||||||
|
public function __construct(User $user) {
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
class UserActivationResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserChangePassword;
|
||||||
|
|
||||||
|
|
||||||
|
class UserChangePasswordCommand {
|
||||||
|
private UserChangePasswordRequest $request;
|
||||||
|
|
||||||
|
public function __construct(UserChangePasswordRequest $request)
|
||||||
|
{
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UserChangePasswordResponse {
|
||||||
|
$response = new UserChangePasswordResponse();
|
||||||
|
|
||||||
|
$this->request->user->password = $this->request->newPassword;
|
||||||
|
$this->request->user->save();
|
||||||
|
|
||||||
|
$response->success = true;
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserChangePassword;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserChangePasswordRequest {
|
||||||
|
public User $user;
|
||||||
|
public string $newPassword;
|
||||||
|
|
||||||
|
public function __construct(User $user, string $newPassword) {
|
||||||
|
$this->user = $user;
|
||||||
|
$this->newPassword = $newPassword;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserChangePassword;
|
||||||
|
|
||||||
|
class UserChangePasswordResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
class UserDeactivationCommand {
|
||||||
|
private UserDeactivationRequest $request;
|
||||||
|
public function __construct(UserDeactivationRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UserDeactivationResponse {
|
||||||
|
$response = new UserDeactivationResponse();
|
||||||
|
$this->request->user->active = false;
|
||||||
|
$this->request->user->password = NULL;
|
||||||
|
$this->request->user->username = 'deleted-' . $this->request->user->username;
|
||||||
|
$this->request->user->email = 'null@example.com';
|
||||||
|
$this->request->user->save();
|
||||||
|
$response->success = true;
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserDeactivationRequest {
|
||||||
|
public User $user;
|
||||||
|
|
||||||
|
public function __construct(User $user) {
|
||||||
|
$this->user = $user;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserActivation;
|
||||||
|
|
||||||
|
class UserDeactivationResponse {
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserRegistration;
|
||||||
|
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageCommand;
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\GenerateActivationToken\GenerateActivationTokenCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\GenerateActivationToken\GenerateActivationTokenRequest;
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
use App\MessageTemplates\Registration\InformAdminAboutNewUserTemplate;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
use App\ValueObjects\MessageRecipient;
|
||||||
|
|
||||||
|
class UserRegistrationCommand {
|
||||||
|
private UserRegistrationRequest $request;
|
||||||
|
|
||||||
|
public function __construct(UserRegistrationRequest $request) {
|
||||||
|
$this->request = $request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute() : UserRegistrationResponse {
|
||||||
|
$response = new UserRegistrationResponse;
|
||||||
|
|
||||||
|
$user = User::create([
|
||||||
|
'user_role_main' => $this->request->userRoleMain,
|
||||||
|
'user_role_local_group' => $this->request->userRoleLocalGroup,
|
||||||
|
'username' => $this->request->email->getValue(),
|
||||||
|
'local_group' => $this->request->localGroup,
|
||||||
|
'firstname' => $this->request->firstname,
|
||||||
|
'lastname' => $this->request->lastname,
|
||||||
|
'nickname' => $this->request->nickname !== '' ? $this->request->nickname : null,
|
||||||
|
'email' => $this->request->email->getValue(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($user === null) {
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$generateActivationCoedeRequest = new GenerateActivationTokenRequest($user);
|
||||||
|
$generateActivationCoedeDommand = new GenerateActivationTokenCommand($generateActivationCoedeRequest);
|
||||||
|
$result = $generateActivationCoedeDommand->execute();
|
||||||
|
|
||||||
|
$user->activation_token = $result->activationCode;
|
||||||
|
|
||||||
|
$siteAdmin = new MessageRecipient();
|
||||||
|
$siteAdmin->addEmailAddress(EmailAddress::fromString(env('APP_ADMIN_MAIL')));
|
||||||
|
$siteAdmin->setName(env('APP_ADMIN_NAME'));
|
||||||
|
|
||||||
|
$registrationMessage = InformAdminAboutNewUserTemplate::createNew($user);
|
||||||
|
|
||||||
|
$registrationMessageRequest = new SendMessageRequest($registrationMessage->getMessage(), $registrationMessage->getSubject(), $siteAdmin, [MessageType::EMAIL]);
|
||||||
|
$registrationMessageCommand = new SendMessageCommand($registrationMessageRequest);
|
||||||
|
$registrationMessageCommand->send();
|
||||||
|
|
||||||
|
$response->user = $user;
|
||||||
|
$response->success = true;
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserRegistration;
|
||||||
|
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
|
||||||
|
class UserRegistrationRequest {
|
||||||
|
public string $firstname;
|
||||||
|
public string $lastname;
|
||||||
|
public EmailAddress $email;
|
||||||
|
public string $nickname;
|
||||||
|
public string $userRoleMain;
|
||||||
|
public string $userRoleLocalGroup;
|
||||||
|
public string $localGroup;
|
||||||
|
|
||||||
|
public function __construct(string $firstname, string $lastname, string $nickname, EmailAddress $email, string $userRoleMain, string $userRoleLocalGroup, string $localGroup) {
|
||||||
|
$this->firstname = $firstname;
|
||||||
|
$this->lastname = $lastname;
|
||||||
|
$this->nickname = $nickname;
|
||||||
|
$this->email = $email;
|
||||||
|
$this->userRoleMain = $userRoleMain;
|
||||||
|
$this->userRoleLocalGroup = $userRoleLocalGroup;
|
||||||
|
$this->localGroup = $localGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Actions\UserRegistration;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class UserRegistrationResponse {
|
||||||
|
public ?User $user;
|
||||||
|
public bool $success;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->user = null;
|
||||||
|
$this->success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserActivationCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserActivationRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserDeactivationCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserDeactivationRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\UserChangePassword\UserChangePasswordCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserChangePassword\UserChangePasswordRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\UserRegistration\UserRegistrationCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserRegistration\UserRegistrationRequest;
|
||||||
|
use App\Enumerations\UserRole;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
use Carbon\Traits\Date;
|
||||||
|
use DateTime;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class EmailVerificationController extends CommonController
|
||||||
|
{
|
||||||
|
public function verifyEmailForm(Request $request) {
|
||||||
|
$inertiaProvider = new InertiaProvider('UserManagement/VerifyEmail', ['appName' => app('tenant')->name]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doVerification(Request $request) : JsonResponse
|
||||||
|
{
|
||||||
|
|
||||||
|
$user = $this->users->findByUsername($request->get('email'));
|
||||||
|
if ($user === null) {
|
||||||
|
return response()->json([
|
||||||
|
'error_types' => [
|
||||||
|
'email' => 'Die E-Mail-Adresse konnte nicht zugeordnet werden.',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new DateTime() > DateTime::createFromFormat('Y-m-d H:i:s', $user->activation_token_expires_at)) {
|
||||||
|
return response()->json([
|
||||||
|
'error_types' => [
|
||||||
|
'verificationToken' => 'Der Sicherheitsschlüssel ist abgelaufen.',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->users->checkVerificationToken($user, $request->get('verificationToken'))) {
|
||||||
|
return response()->json([
|
||||||
|
'error_types' => [
|
||||||
|
'verificationToken' => 'Der Sicherheitsschlüssel ist nicht korrekt',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$userActivationRequest = new UserActivationRequest($user);
|
||||||
|
$userActivationCommand = new UserActivationCommand($userActivationRequest);
|
||||||
|
$activationResult = $userActivationCommand->execute();
|
||||||
|
|
||||||
|
|
||||||
|
if (!$activationResult->success) {
|
||||||
|
return response()->json([
|
||||||
|
'error_types' => [
|
||||||
|
'verificationToken' => 'Ein allgemeiner Fehler ist aufgetreten. Bitte versuche es später noch einmal.',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$userPasswordResetRequest = new UserChangePasswordRequest($user, $request->get('password'));
|
||||||
|
$userPasswordResetCommand = new UserChangePasswordCommand($userPasswordResetRequest);
|
||||||
|
$userPasswordResetCommand->execute();
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Dein Account wurde aktiviert.Du kannst dich nun mit deinem neuen Passwort einloggen.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,11 @@
|
|||||||
namespace App\Domains\UserManagement\Controllers;
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
use App\Providers\InertiaProvider;
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class LoginController {
|
class LoginController extends CommonController {
|
||||||
public function loginForm(Request $request) {
|
public function loginForm(Request $request) {
|
||||||
$errors = [];
|
$errors = [];
|
||||||
|
|
||||||
@@ -32,6 +33,12 @@ class LoginController {
|
|||||||
'password.required' => 'Bitte gib dein Passwort ein.',
|
'password.required' => 'Bitte gib dein Passwort ein.',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$user = $this->users->findByUsername($request->get('username'));
|
||||||
|
if ($user !== null && $user->password === null) {
|
||||||
|
return redirect()->intended('/register/verifyEmail');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#$credentials = ['username' => 'development', 'password' => 'development'];
|
#$credentials = ['username' => 'development', 'password' => 'development'];
|
||||||
|
|
||||||
if (!Auth::attempt($credentials)) {
|
if (!Auth::attempt($credentials)) {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageCommand;
|
||||||
|
use App\Domains\MessageSystem\Actions\SendMessage\SendMessageRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\UserRegistration\UserRegistrationCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserRegistration\UserRegistrationRequest;
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
use App\Enumerations\UserRole;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
use App\ValueObjects\MessageRecipient;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class RegistrationController extends CommonController {
|
||||||
|
public function loginForm(Request $request) {
|
||||||
|
$errors = [];
|
||||||
|
|
||||||
|
if ($request->session()->has('errors')) {
|
||||||
|
$errors = $request->session()->get('errors')->getBag('default')->getMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$inertiaProvider = new InertiaProvider('UserManagement/Registration', ['errors' => $errors, 'appName' => app('tenant')->name]);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doRegistration(Request $request) : JsonResponse {
|
||||||
|
|
||||||
|
$user = $this->users->findByUsername($request->get('email'));
|
||||||
|
if ($user !== null) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Dieser Account existiert bereits.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = EmailAddress::fromString($request->get('email'));
|
||||||
|
$userRoleMain = UserRole::USER_ROLE_USER;
|
||||||
|
$userRoleLocalGroup = UserRole::USER_ROLE_USER;
|
||||||
|
|
||||||
|
$localGroup = app('tenant')->slug === 'lv' ? $request->get('localGroup') : app('tenant')->slug;
|
||||||
|
|
||||||
|
|
||||||
|
$registrationRequest = new UserRegistrationRequest(
|
||||||
|
$request->get('firstname'),
|
||||||
|
$request->get('lastname'),
|
||||||
|
$request->get('nickname'),
|
||||||
|
$email,
|
||||||
|
$userRoleMain,
|
||||||
|
$userRoleLocalGroup,
|
||||||
|
$localGroup
|
||||||
|
);
|
||||||
|
|
||||||
|
$registrationCommand = new UserRegistrationCommand($registrationRequest);
|
||||||
|
$result = $registrationCommand->execute();
|
||||||
|
|
||||||
|
if (!$result->success) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Beim Erstellen des Accounts ist ein Fehler aufgetreten.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Registrierung erfolgreich! Bitte prüfe nun dein E-Mail-Postfach'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Domains\UserManagement\Controllers;
|
||||||
|
|
||||||
|
use App\Domains\UserManagement\Actions\GenerateActivationToken\GenerateActivationTokenCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\GenerateActivationToken\GenerateActivationTokenRequest;
|
||||||
|
use App\Domains\UserManagement\Actions\GenerateActivationToken\GenerateActivationTokenResponse;
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserActivationCommand;
|
||||||
|
use App\Domains\UserManagement\Actions\UserActivation\UserActivationRequest;
|
||||||
|
use App\Providers\InertiaProvider;
|
||||||
|
use App\Scopes\CommonController;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class ResetPasswordController extends CommonController {
|
||||||
|
public function resetPasswordForm() {
|
||||||
|
$inertiaProvider = new InertiaProvider('UserManagement/ResetPassword', []);
|
||||||
|
return $inertiaProvider->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doResetPassword(Request $request) : JsonResponse {
|
||||||
|
$user = $this->users->findByUsername($request->get('email'));
|
||||||
|
|
||||||
|
if (null !== $user) {
|
||||||
|
$expirationDate = new \DateTime()->modify('+15 Minutes');
|
||||||
|
|
||||||
|
$resetAccountRequest = new GenerateActivationTokenRequest($user, $expirationDate);
|
||||||
|
$resetAccountCommand = new GenerateActivationTokenCommand($resetAccountRequest);
|
||||||
|
$resetAccountCommand->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Falls deine E-Mail-Adresse gefunden wurde, erhältst du nun eine E-Mail mit weiteren Schritten zum Zurücksetzen deines Passwortes.'
|
||||||
|
]);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Domains/UserManagement/Routes/api.php
Normal file
17
app/Domains/UserManagement/Routes/api.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
Route::prefix('v1')
|
||||||
|
->group(function () {
|
||||||
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::post('/register', [RegistrationController::class, 'doRegistration']);
|
||||||
|
Route::post('/register/confirmEmail', [EmailVerificationController::class, 'doVerification']);
|
||||||
|
Route::post('/reset-password', [ResetPasswordController::class, 'doResetPassword']);
|
||||||
|
});
|
||||||
|
});
|
||||||
31
app/Domains/UserManagement/Routes/web.php
Normal file
31
app/Domains/UserManagement/Routes/web.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LoginController;
|
||||||
|
use App\Domains\UserManagement\Controllers\LogOutController;
|
||||||
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
|
use App\Http\Controllers\TestRenderInertiaProvider;
|
||||||
|
use App\Middleware\IdentifyTenant;
|
||||||
|
use Illuminate\Support\Facades\Route;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
|
Route::get('/register', [RegistrationController::class, 'loginForm']);
|
||||||
|
Route::get('/register/verifyEmail', [EmailVerificationController::class, 'verifyEmailForm']);
|
||||||
|
|
||||||
|
Route::get('/reset-password', [ResetPasswordController::class, 'resetPasswordForm']);
|
||||||
|
|
||||||
|
route::get('/logout', LogOutController::class);
|
||||||
|
route::post('/login', [LoginController::class, 'doLogin']);
|
||||||
|
route::get('/login', [LoginController::class, 'loginForm']);
|
||||||
|
|
||||||
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
Route::post('/logout', [LogoutController::class, 'logout']);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +25,10 @@ const username = ref('')
|
|||||||
const password = ref('')
|
const password = ref('')
|
||||||
|
|
||||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
|
||||||
|
function resetPassword() {
|
||||||
|
window.location.href = '/reset-password';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -48,6 +52,7 @@ const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute
|
|||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<input type="submit" value="Anmelden" style="margin-top: 20px;" />
|
<input type="submit" value="Anmelden" style="margin-top: 20px;" />
|
||||||
|
<input type="button" @click="resetPassword" style="margin-top: 20px; margin-left: 20px;" value="Passwort vergessen" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
166
app/Domains/UserManagement/Views/Registration.vue
Normal file
166
app/Domains/UserManagement/Views/Registration.vue
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import {computed, onMounted, reactive, ref} from 'vue'
|
||||||
|
import { toast } from 'vue3-toastify'
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import {useAjax} from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ErrorText from "../../../Views/Components/ErrorText.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
errors: Object,
|
||||||
|
availableLocalGroups: Array,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.errors && undefined !== props.errors.username) {
|
||||||
|
toast.error(props.errors.username[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
//console.log(props.errors.password[0])
|
||||||
|
const username = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
firstname: '',
|
||||||
|
lastname: '',
|
||||||
|
nickname: '',
|
||||||
|
email: '',
|
||||||
|
localGroup: '',
|
||||||
|
password: '',
|
||||||
|
password_confirmation: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const errors = reactive({})
|
||||||
|
const { request } = useAjax()
|
||||||
|
|
||||||
|
const isValid = computed(() => {
|
||||||
|
errors.firstname = ''
|
||||||
|
errors.lastname = ''
|
||||||
|
errors.localGroup = ''
|
||||||
|
errors.email = ''
|
||||||
|
|
||||||
|
|
||||||
|
if (!form.firstname) {
|
||||||
|
errors.firstname = 'Bitte gib deinen Vornamen ein'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!form.lastname) {
|
||||||
|
errors.lastname = 'Bitte gib deinen Nachnamen ein'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!form.localGroup) {
|
||||||
|
errors.localGroup = 'Bitte gib deinen Stamm an'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!form.email) {
|
||||||
|
errors.email = 'Bitte gib deine E-Mail-Adresse ein'
|
||||||
|
} else if (!/^\S+@\S+\.\S+$/.test(form.email)) {
|
||||||
|
errors.email = 'Ungültige E-Mail'
|
||||||
|
}
|
||||||
|
|
||||||
|
return !errors.password && !errors.firstname && !errors.lastname && !errors.localGroup
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
|
||||||
|
if (!isValid.value) return false
|
||||||
|
|
||||||
|
const data = await request("/api/v1/register", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
"firstname": form.firstname,
|
||||||
|
"lastname": form.lastname,
|
||||||
|
'nickname': form.nickname,
|
||||||
|
"email": form.email,
|
||||||
|
"localGroup": form.localGroup,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.status === 'error') {
|
||||||
|
toast.error(data.message);
|
||||||
|
} else {
|
||||||
|
toast.success(data.message)
|
||||||
|
window.location.href = '/register/verifyEmail';
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Registrieren' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<form method="POST" action="/register" @submit.prevent="submit">
|
||||||
|
<input type="hidden" name="_token" :value="csrfToken" />
|
||||||
|
<shadowed-box style="width: 75%; margin: 150px auto; padding: 20px;">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Vorname</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="firstname" id="firstname" v-model="form.firstname" />
|
||||||
|
<ErrorText :message="errors.firstname" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Nachname</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="lastname" id="lastname" v-model="form.lastname" />
|
||||||
|
<ErrorText :message="errors.lastname" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Pfadi-Name*<br />
|
||||||
|
<small>*Falls vorhanden</small>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="nickname" id="nickname" v-model="form.nickname" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>E-Mail-Adresse</th>
|
||||||
|
<td>
|
||||||
|
<input type="email" name="email" id="email" v-model="form.email" />
|
||||||
|
<ErrorText :message="errors.email" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr v-if="props.tenant.slug === 'lv'">
|
||||||
|
<th>Stamm</th>
|
||||||
|
<td>
|
||||||
|
<select name="localgroup" v-model="form.localGroup">
|
||||||
|
<option v-for="localGroup in props.availableLocalGroups" :value="localGroup.slug">{{localGroup.name}}</option>
|
||||||
|
</select>
|
||||||
|
<ErrorText :message="errors.localGroup" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="submit" value="Registrieren" style="margin-top: 20px;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</form>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
th {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
102
app/Domains/UserManagement/Views/ResetPassword.vue
Normal file
102
app/Domains/UserManagement/Views/ResetPassword.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import {computed, onMounted, reactive, ref} from 'vue'
|
||||||
|
import { toast } from 'vue3-toastify'
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import {useAjax} from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ErrorText from "../../../Views/Components/ErrorText.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
errors: Object,
|
||||||
|
availableLocalGroups: Array,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.errors && undefined !== props.errors.username) {
|
||||||
|
toast.error(props.errors.username[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const form = reactive({
|
||||||
|
email: '',
|
||||||
|
verificationToken: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const errors = reactive({})
|
||||||
|
const { request } = useAjax()
|
||||||
|
|
||||||
|
const isValid = computed(() => {
|
||||||
|
errors.email = ''
|
||||||
|
|
||||||
|
if (!form.email) {
|
||||||
|
errors.email = 'Bitte gib deine E-Mail-Adresse ein'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return !errors.email
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
|
||||||
|
if (!isValid.value) return false
|
||||||
|
|
||||||
|
const data = await request("/api/v1/reset-password", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
"email": form.email,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.error_types) {
|
||||||
|
Object.keys(data.error_types).forEach((key) => {
|
||||||
|
if (key in errors) {
|
||||||
|
errors[key] = data.error_types[key]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
window.location.href = '/register/verifyEmail';
|
||||||
|
toast.success(data.message)
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='Passwort zurücksetzen' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<form method="POST" action="/reset-password" @submit.prevent="submit">
|
||||||
|
<input type="hidden" name="_token" :value="csrfToken" />
|
||||||
|
<shadowed-box style="width: 75%; margin: 150px auto; padding: 20px;">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>E-Mail-Adresse</th>
|
||||||
|
<td>
|
||||||
|
<input type="email" name="email" id="email" v-model="form.email" />
|
||||||
|
<ErrorText :message="errors.email" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="submit" value="Verifizierung starten" style="margin-top: 20px;" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</form>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
th {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
152
app/Domains/UserManagement/Views/VerifyEmail.vue
Normal file
152
app/Domains/UserManagement/Views/VerifyEmail.vue
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppLayout from '../../../../resources/js/layouts/AppLayout.vue'
|
||||||
|
import {computed, onMounted, reactive, ref} from 'vue'
|
||||||
|
import { toast } from 'vue3-toastify'
|
||||||
|
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
||||||
|
import {useAjax} from "../../../../resources/js/components/ajaxHandler.js";
|
||||||
|
import ErrorText from "../../../Views/Components/ErrorText.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
navbar: Object,
|
||||||
|
tenant: String,
|
||||||
|
user: Object,
|
||||||
|
currentPath: String,
|
||||||
|
errors: Object,
|
||||||
|
availableLocalGroups: Array,
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (undefined !== props.errors && undefined !== props.errors.username) {
|
||||||
|
toast.error(props.errors.username[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const form = reactive({
|
||||||
|
email: '',
|
||||||
|
verificationToken: '',
|
||||||
|
password: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
const errors = reactive({})
|
||||||
|
const { request } = useAjax()
|
||||||
|
|
||||||
|
const isValid = computed(() => {
|
||||||
|
errors.email = '';
|
||||||
|
errors.verificationToken = '';
|
||||||
|
errors.password = '';
|
||||||
|
|
||||||
|
if (!form.email) {
|
||||||
|
errors.email = 'Bitte gib deine E-Mail-Adresse ein'
|
||||||
|
} else if (!/^\S+@\S+\.\S+$/.test(form.email)) {
|
||||||
|
errors.email = 'Ungültige E-Mail'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!form.verificationToken) {
|
||||||
|
errors.verificationToken = 'Bitte gib den Sicherheitsschlüssel, den du per E-Mail erhalten hast, ein'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!form.password) {
|
||||||
|
errors.password = 'Bitte gib ein Passwort ein'
|
||||||
|
} else if (form.password.length < 2) {
|
||||||
|
errors.password = 'Das Passwort muss mindestens 12 Zeichen lang sein'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form.password !== form.password_confirmation) {
|
||||||
|
errors.password = 'Das Passwort und die Wiederholung stimmen nicht überein'
|
||||||
|
}
|
||||||
|
|
||||||
|
return !errors.password && !errors.email && !errors.verificationToken
|
||||||
|
})
|
||||||
|
|
||||||
|
async function submit() {
|
||||||
|
|
||||||
|
if (!isValid.value) return false
|
||||||
|
|
||||||
|
const data = await request("/api/v1/register/confirmEmail", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
"email": form.email,
|
||||||
|
"verificationToken": form.verificationToken,
|
||||||
|
"password": form.password,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.error_types) {
|
||||||
|
Object.keys(data.error_types).forEach((key) => {
|
||||||
|
if (key in errors) {
|
||||||
|
errors[key] = data.error_types[key]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toast.success(data.message)
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPassword() {
|
||||||
|
window.location.href = '/reset-password';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<AppLayout title='E-Mail bestätigen' :user="props.user" :navbar="props.navbar" :tenant="props.tenant" :currentPath="props.currentPath">
|
||||||
|
<form method="POST" action="/register" @submit.prevent="submit">
|
||||||
|
<input type="hidden" name="_token" :value="csrfToken" />
|
||||||
|
<shadowed-box style="width: 75%; margin: 150px auto; padding: 20px;">
|
||||||
|
<p>
|
||||||
|
Bitte prüfe dein E-Mail-Postfach.<br />
|
||||||
|
Solltest du keinen Aktivierungsschlüssel erhalten haben, kontaktiere bitte einen Administrator.
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>E-Mail-Adresse</th>
|
||||||
|
<td>
|
||||||
|
<input type="email" name="email" id="email" v-model="form.email" />
|
||||||
|
<ErrorText :message="errors.email" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Sicherheitsschlüssel</th>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="verificationToken" id="verificationToken" v-model="form.verificationToken" />
|
||||||
|
<ErrorText :message="errors.verificationToken" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Dein neues Passwort</th>
|
||||||
|
<td><input type="password" name="password" id="password" v-model="form.password" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<th>Passwort (wiederholen)</th>
|
||||||
|
<td>
|
||||||
|
<input type="password" name="password_retype" id="password_retype" v-model="form.password_confirmation" />
|
||||||
|
<ErrorText :message="errors.password" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">
|
||||||
|
<input type="submit" value="E-Mail validieren" style="margin-top: 20px;" />
|
||||||
|
<input type="button" @click="resetPassword" style="margin-top: 20px; margin-left: 20px;" value="Validierungscode neu zusenden" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</shadowed-box>
|
||||||
|
</form>
|
||||||
|
</AppLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
th {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
8
app/Enumerations/MessageType.php
Normal file
8
app/Enumerations/MessageType.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enumerations;
|
||||||
|
|
||||||
|
enum MessageType : string {
|
||||||
|
case INTERNAL = 'INTERNAL';
|
||||||
|
case EMAIL = 'EMAIL';
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ class DevelopmentDataSeeder {
|
|||||||
private function installTenants() {
|
private function installTenants() {
|
||||||
Tenant::create([
|
Tenant::create([
|
||||||
'slug' => 'wilde-moehre',
|
'slug' => 'wilde-moehre',
|
||||||
'local_group_name' => 'Stamm Wilde Möhre',
|
'name' => 'Stamm Wilde Möhre',
|
||||||
'url' => 'wilde-moehre.mareike.local',
|
'url' => 'wilde-moehre.mareike.local',
|
||||||
'account_iban' => 'DE12345678901234567890',
|
'account_iban' => 'DE12345678901234567890',
|
||||||
'email' => 'test@example1.com',
|
'email' => 'test@example1.com',
|
||||||
@@ -32,11 +32,11 @@ class DevelopmentDataSeeder {
|
|||||||
User::create([
|
User::create([
|
||||||
'firstname' => 'Development',
|
'firstname' => 'Development',
|
||||||
'lastname' => 'User',
|
'lastname' => 'User',
|
||||||
'user_role' => UserRole::USER_ROLE_ADMIN,
|
'user_role_main' => UserRole::USER_ROLE_ADMIN,
|
||||||
'tenant' => 'lv',
|
'user_role_local_group' => UserRole::USER_ROLE_GROUP_LEADER,
|
||||||
|
'local_group' => 'wilde-moehre',
|
||||||
'email' => 'th.guenther@saale-mail.de',
|
'email' => 'th.guenther@saale-mail.de',
|
||||||
'password' => bcrypt('development'),
|
'password' => bcrypt('development'),
|
||||||
'local_group_id' => 1,
|
|
||||||
'username' => 'development',
|
'username' => 'development',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class ProductionDataSeeder {
|
|||||||
private function installTenants() {
|
private function installTenants() {
|
||||||
Tenant::create([
|
Tenant::create([
|
||||||
'slug' => 'lv',
|
'slug' => 'lv',
|
||||||
'local_group_name' => 'Landesunmittelbare Mitglieder',
|
'name' => 'Landesunmittelbare Mitglieder',
|
||||||
'url' => 'mareike.local',
|
'url' => 'mareike.local',
|
||||||
'account_iban' => 'DE12345678901234567890',
|
'account_iban' => 'DE12345678901234567890',
|
||||||
'email' => 'test@example.com',
|
'email' => 'test@example.com',
|
||||||
|
|||||||
37
app/MessageTemplates/MessageTemplate.php
Normal file
37
app/MessageTemplates/MessageTemplate.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\MessageTemplates;
|
||||||
|
|
||||||
|
use App\Enumerations\MessageType;
|
||||||
|
|
||||||
|
abstract class MessageTemplate {
|
||||||
|
protected string $subject;
|
||||||
|
protected string $message;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubject(): string
|
||||||
|
{
|
||||||
|
return $this->subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubject(string $subject): void
|
||||||
|
{
|
||||||
|
$this->subject = $subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMessage(): string
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMessage(string $message): void
|
||||||
|
{
|
||||||
|
$this->message = $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\MessageTemplates\Registration;
|
||||||
|
|
||||||
|
use App\MessageTemplates\activationCodeTemplate;
|
||||||
|
use App\MessageTemplates\MessageTemplate;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
|
||||||
|
class InformAdminAboutNewUserTemplate extends MessageTemplate
|
||||||
|
{
|
||||||
|
public static function createNew(User $user) : InformAdminAboutNewUserTemplate {
|
||||||
|
$template = new InformAdminAboutNewUserTemplate();
|
||||||
|
$template->composeMessage($user);
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->subject = "Eine Person hat sich auf mareike registriert";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function composeMessage(User $user): void {
|
||||||
|
$this->message =
|
||||||
|
<<<HTML
|
||||||
|
|
||||||
|
<h1>Eine Person hat sich auf mareike angemeldet</h1>
|
||||||
|
<p>Soeben hat sich eine neue Person auf mareike angemeldet:</p>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Name:</th>
|
||||||
|
<td>%1\$s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Email:</th>
|
||||||
|
<td>%2\$s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Stamm:</th>
|
||||||
|
<td>%3\$s</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Freischaltcode:</th>
|
||||||
|
<td>%4\$s</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Sollte die Person unberechtigt angemeldet sein, lösche den Account.
|
||||||
|
</p>
|
||||||
|
HTML;
|
||||||
|
|
||||||
|
$this->message = sprintf($this->message,
|
||||||
|
$user->getFullname(),
|
||||||
|
$user->email,
|
||||||
|
$user->local_group,
|
||||||
|
$user->activation_token
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
app/MessageTemplates/activationCodeTemplate.php
Normal file
23
app/MessageTemplates/activationCodeTemplate.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\MessageTemplates;
|
||||||
|
|
||||||
|
use App\ValueObjects\EmailAddress;
|
||||||
|
|
||||||
|
class activationCodeTemplate extends MessageTemplate {
|
||||||
|
|
||||||
|
public static function createForUser(EmailAddress $emailAddress, string $activationCode) : activationCodeTemplate {
|
||||||
|
$template = new activationCodeTemplate();
|
||||||
|
$template->composeMessage($emailAddress, $activationCode);
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->subject = "Dein Aktivierungscode";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function composeMessage(EmailAddress $emailAddress, string $activationCode): void {
|
||||||
|
$this->message = "Dein Aktivierungscode lautet: {$activationCode}" . PHP_EOL .
|
||||||
|
"Gib diesen zusammen mit der Mailadresse {$emailAddress->getValue()} ein.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@ class Tenant extends CommonModel
|
|||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'slug',
|
'slug',
|
||||||
'local_group_name',
|
'name',
|
||||||
'email',
|
'email',
|
||||||
'url',
|
'url',
|
||||||
'account_iban',
|
'account_iban',
|
||||||
|
|||||||
@@ -5,6 +5,34 @@ namespace App\Models;
|
|||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property string $username
|
||||||
|
* @property string $local_group
|
||||||
|
* @property string $firstname
|
||||||
|
* @property string $nickname
|
||||||
|
* @property string $lastname
|
||||||
|
* @property string $membership_id
|
||||||
|
* @property string $address_1
|
||||||
|
* @property string $address_2
|
||||||
|
* @property string $postcode
|
||||||
|
* @property string $city
|
||||||
|
* @property string $email
|
||||||
|
* @property string $phone
|
||||||
|
* @property string $birthday
|
||||||
|
* @property string $medications
|
||||||
|
* @property string $allergies
|
||||||
|
* @property string $intolerances
|
||||||
|
* @property string $eating_habits
|
||||||
|
* @property string $swimming_permission
|
||||||
|
* @property string $first_aid_permission
|
||||||
|
* @property string $bank_account_iban
|
||||||
|
* @property string $password
|
||||||
|
* @property boolean $active
|
||||||
|
* @property string $user_role_main
|
||||||
|
* @property string $user_role_local_group
|
||||||
|
* @property string $activation_token
|
||||||
|
* @property string $activation_token_expires_at
|
||||||
|
*/
|
||||||
class User extends Authenticatable
|
class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use Notifiable;
|
use Notifiable;
|
||||||
@@ -15,13 +43,15 @@ class User extends Authenticatable
|
|||||||
* @var list<string>
|
* @var list<string>
|
||||||
*/
|
*/
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'tenant_id',
|
'user_role_main',
|
||||||
'user_role',
|
'user_role_local_group',
|
||||||
|
'activation_token',
|
||||||
|
'activation_token_expires_at',
|
||||||
'username',
|
'username',
|
||||||
|
'local_group',
|
||||||
'firstname',
|
'firstname',
|
||||||
'nickname',
|
'nickname',
|
||||||
'lastname',
|
'lastname',
|
||||||
'local_group_id',
|
|
||||||
'membership_id',
|
'membership_id',
|
||||||
'address_1',
|
'address_1',
|
||||||
'address_2',
|
'address_2',
|
||||||
@@ -58,8 +88,19 @@ class User extends Authenticatable
|
|||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'email_verified_at' => 'datetime',
|
|
||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFullname() : string {
|
||||||
|
return sprintf('%1$1s %2$s %3$s',
|
||||||
|
$this->firstname,
|
||||||
|
$this->nickname !== '' ? '(' . $this->nickname . ')' : '',
|
||||||
|
$this->lastname
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNicename() : string {
|
||||||
|
return $this->nickname !== '' ? $this->nickname : $this->firstname;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ class AuthCheckProvider {
|
|||||||
|
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$tenant = app('tenant');
|
$tenant = app('tenant');
|
||||||
|
if ($tenant->slug === 'lv') {
|
||||||
|
return $user->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return $user->active && $tenant->slug === $user->tenant;
|
return $user->active && $tenant->slug === $user->tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +23,10 @@ class AuthCheckProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return auth()->user()->user_role;
|
if (app('tenant')->slug === 'lv') {
|
||||||
|
return auth()->user()->user_role_main;
|
||||||
|
}
|
||||||
|
|
||||||
|
return auth()->user()->user_role_local_group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\Tenant;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use Inertia\Response;
|
use Inertia\Response;
|
||||||
@@ -21,9 +22,10 @@ final class InertiaProvider
|
|||||||
|
|
||||||
public function render() : Response {
|
public function render() : Response {
|
||||||
$this->props['navbar'] = $this->generateNavbar();
|
$this->props['navbar'] = $this->generateNavbar();
|
||||||
$this->props['tenant'] = app('tenant')->local_group_name;
|
$this->props['tenant'] = app('tenant');
|
||||||
$this->props['user'] = $this->user;
|
$this->props['user'] = $this->user;
|
||||||
$this->props['currentPath'] = request()->path();
|
$this->props['currentPath'] = request()->path();
|
||||||
|
$this->props['availableLocalGroups'] = Tenant::where(['is_active_local_group' => true])->get();
|
||||||
|
|
||||||
return Inertia::render(
|
return Inertia::render(
|
||||||
str_replace('/', '/Views/', $this->vueFile),
|
str_replace('/', '/Views/', $this->vueFile),
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class TenantUserProvider extends EloquentUserProvider
|
|||||||
{
|
{
|
||||||
public function retrieveByCredentials(array $credentials)
|
public function retrieveByCredentials(array $credentials)
|
||||||
{
|
{
|
||||||
|
$credentials['active'] = true;
|
||||||
|
|
||||||
$query = $this->createModel()->newQuery();
|
$query = $this->createModel()->newQuery();
|
||||||
|
|
||||||
foreach ($credentials as $key => $value) {
|
foreach ($credentials as $key => $value) {
|
||||||
@@ -16,8 +18,12 @@ class TenantUserProvider extends EloquentUserProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app('tenant')->slug === 'lv') {
|
||||||
|
return $query->first();
|
||||||
|
}
|
||||||
|
|
||||||
$query->where([
|
$query->where([
|
||||||
'tenant' => app('tenant')->slug,
|
'local_group' => app('tenant')->slug,
|
||||||
'active' => true
|
'active' => true
|
||||||
|
|
||||||
]);
|
]);
|
||||||
|
|||||||
20
app/Repositories/UserRepository.php
Normal file
20
app/Repositories/UserRepository.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class UserRepository {
|
||||||
|
public function findByUsername(string $username) : ?User {
|
||||||
|
return User::where(['username' => $username])->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkVerificationToken(User $user, string $token) : bool {
|
||||||
|
if (new DateTime() > DateTime::createFromFormat('Y-m-d H:i:s', $user->activation_token_expires_at)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $token === $user->activation_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,15 @@
|
|||||||
namespace App\Scopes;
|
namespace App\Scopes;
|
||||||
|
|
||||||
use App\Providers\AuthCheckProvider;
|
use App\Providers\AuthCheckProvider;
|
||||||
|
use App\Repositories\UserRepository;
|
||||||
|
|
||||||
abstract class CommonController {
|
abstract class CommonController {
|
||||||
|
protected UserRepository $users;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->users = new UserRepository();
|
||||||
|
}
|
||||||
|
|
||||||
protected function checkAuth() {
|
protected function checkAuth() {
|
||||||
$authCheckProvider = new AuthCheckProvider;
|
$authCheckProvider = new AuthCheckProvider;
|
||||||
return $authCheckProvider->checkLoggedIn();
|
return $authCheckProvider->checkLoggedIn();
|
||||||
|
|||||||
26
app/ValueObjects/EmailAddress.php
Normal file
26
app/ValueObjects/EmailAddress.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ValueObjects;
|
||||||
|
|
||||||
|
class EmailAddress {
|
||||||
|
private string $value;
|
||||||
|
public static function fromString(string $value) : EmailAddress {
|
||||||
|
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
throw new \InvalidArgumentException("Invalid email address: $value");
|
||||||
|
}
|
||||||
|
|
||||||
|
$emailAddress = new EmailAddress();
|
||||||
|
$emailAddress->setValue($value);
|
||||||
|
return $emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValue(): string
|
||||||
|
{
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValue(string $value): void
|
||||||
|
{
|
||||||
|
$this->value = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
app/ValueObjects/MessageRecipient.php
Normal file
50
app/ValueObjects/MessageRecipient.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\ValueObjects;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
class MessageRecipient {
|
||||||
|
private ?User $user;
|
||||||
|
/** @var EmailAddress[] */
|
||||||
|
private array $emailAddresses;
|
||||||
|
private string $name;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->user = null;
|
||||||
|
$this->emailAddresses = [];
|
||||||
|
$this->name = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(?User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEmailAddresses(): array
|
||||||
|
{
|
||||||
|
return $this->emailAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addEmailAddress(EmailAddress $emailAddresses): void
|
||||||
|
{
|
||||||
|
$this->emailAddresses[] = $emailAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
10
app/Views/Components/ErrorText.vue
Normal file
10
app/Views/Components/ErrorText.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
message: String,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<small class="error_text" v-if="props.message">{{ props.message }}</small>
|
||||||
|
</template>
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'timezone' => 'UTC',
|
'timezone' => 'Europe/Berlin',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ return new class extends Migration {
|
|||||||
Schema::create('tenants', function (Blueprint $table) {
|
Schema::create('tenants', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('slug')->unique();
|
$table->string('slug')->unique();
|
||||||
$table->string('local_group_name');
|
$table->string('name');
|
||||||
$table->string('email');
|
$table->string('email');
|
||||||
$table->string('url');
|
$table->string('url');
|
||||||
$table->string('account_iban');
|
$table->string('account_iban');
|
||||||
|
|||||||
@@ -38,14 +38,14 @@ return new class extends Migration
|
|||||||
|
|
||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('tenant');
|
$table->string('user_role_main');
|
||||||
$table->string('user_role');
|
$table->string('user_role_local_group');
|
||||||
$table->string('username')->unique();
|
$table->string('username')->unique();
|
||||||
$table->string('password')->nullable();
|
$table->string('password')->nullable();
|
||||||
|
$table->string('local_group');
|
||||||
$table->string('firstname');
|
$table->string('firstname');
|
||||||
$table->string('nickname')->nullable();
|
$table->string('nickname')->nullable();
|
||||||
$table->string('lastname');
|
$table->string('lastname');
|
||||||
$table->foreignId('local_group_id')->references('id')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
|
||||||
$table->string('membership_id')->nullable();
|
$table->string('membership_id')->nullable();
|
||||||
$table->string('address_1')->nullable();
|
$table->string('address_1')->nullable();
|
||||||
$table->string('address_2')->nullable();
|
$table->string('address_2')->nullable();
|
||||||
@@ -62,10 +62,12 @@ return new class extends Migration
|
|||||||
$table->string('first_aid_permission')->nullable();
|
$table->string('first_aid_permission')->nullable();
|
||||||
$table->string('bank_account_iban')->nullable();
|
$table->string('bank_account_iban')->nullable();
|
||||||
$table->string('activation_token')->nullable();
|
$table->string('activation_token')->nullable();
|
||||||
|
$table->dateTime('activation_token_expires_at')->nullable()->default(date('Y-m-d H:i:s', strtotime('+3 days')));
|
||||||
$table->boolean('active')->default(false);
|
$table->boolean('active')->default(false);
|
||||||
|
|
||||||
$table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('local_group')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
$table->foreign('user_role')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('user_role_main')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
|
$table->foreign('user_role_local_group')->references('slug')->on('user_roles')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
$table->foreign('swimming_permission')->references('slug')->on('swimming_permissions')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('swimming_permission')->references('slug')->on('swimming_permissions')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
$table->foreign('eating_habits')->references('slug')->on('eating_habits')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('eating_habits')->references('slug')->on('eating_habits')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
$table->foreign('first_aid_permission')->references('slug')->on('first_aid_permissions')->cascadeOnDelete()->cascadeOnUpdate();
|
$table->foreign('first_aid_permission')->references('slug')->on('first_aid_permissions')->cascadeOnDelete()->cascadeOnUpdate();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ services:
|
|||||||
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.mareike.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`)"
|
- "traefik.http.routers.mareike.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`) || Host(`fennek.mareike.local`)"
|
||||||
- "traefik.http.routers.mareike.entrypoints=websecure"
|
- "traefik.http.routers.mareike.entrypoints=websecure"
|
||||||
- "traefik.http.routers.mareike.tls=true"
|
- "traefik.http.routers.mareike.tls=true"
|
||||||
- "traefik.http.services.mareike.loadbalancer.server.port=80"
|
- "traefik.http.services.mareike.loadbalancer.server.port=80"
|
||||||
@@ -46,13 +46,6 @@ services:
|
|||||||
- ./:/var/www/html
|
- ./:/var/www/html
|
||||||
command: >
|
command: >
|
||||||
sh -c "
|
sh -c "
|
||||||
npm install &&
|
|
||||||
npm install vue3-toastify && npm install @inertiajs/progress && npm install @inertiajs/progress &&
|
|
||||||
npm install @fortawesome/fontawesome-svg-core &&
|
|
||||||
npm install @fortawesome/free-solid-svg-icons &&
|
|
||||||
npm install @fortawesome/vue-fontawesome@latest &&
|
|
||||||
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
npm run build
|
npm run build
|
||||||
echo 'Vite Dev-Server beendet. Neustart in 3 Sekunden...'
|
echo 'Vite Dev-Server beendet. Neustart in 3 Sekunden...'
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ html {
|
|||||||
th {
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-right: 1em;
|
padding-right: 1em;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
th:after {
|
th:after {
|
||||||
|
|||||||
@@ -12,20 +12,28 @@
|
|||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="email"],
|
input[type="email"],
|
||||||
input[type="password"] {
|
input[type="password"],
|
||||||
|
select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: #656363;
|
color: #656363;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: calc(100% + 10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"]:focus,
|
input[type="text"]:focus,
|
||||||
input[type="email"]:focus,
|
input[type="email"]:focus,
|
||||||
input[type="password"]:focus {
|
input[type="password"]:focus,
|
||||||
|
select:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #1d4899;
|
border-color: #1d4899;
|
||||||
|
color: #1d4899;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -33,7 +41,8 @@ table {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, input[type="submit"] {
|
input[type="button"],
|
||||||
|
input[type="submit"] {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #809dd5 !important;
|
border: 1px solid #809dd5 !important;
|
||||||
@@ -41,7 +50,13 @@ button, input[type="submit"] {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
button:hover, input[type="submit"]:hover {
|
input[type="button"]:hover,
|
||||||
|
input[type="submit"]:hover {
|
||||||
background-color: #1d4899;
|
background-color: #1d4899;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error_text {
|
||||||
|
color: red;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|||||||
80
resources/js/components/ajaxHandler.js
Normal file
80
resources/js/components/ajaxHandler.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { ref } from "vue"
|
||||||
|
|
||||||
|
export function useAjax() {
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref(null)
|
||||||
|
const data = ref(null)
|
||||||
|
|
||||||
|
async function request(url, options = {}) {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
data.value = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: options.method || "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...(options.headers || {}),
|
||||||
|
},
|
||||||
|
body: options.body ? JSON.stringify(options.body) : null,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
||||||
|
const result = await response.json()
|
||||||
|
data.value = result
|
||||||
|
return result
|
||||||
|
} catch (err) {
|
||||||
|
error.value = err
|
||||||
|
console.error("AJAX Error:", err)
|
||||||
|
return null
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function download(url, options = {}) {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: options.method || "GET",
|
||||||
|
headers: {
|
||||||
|
...(options.headers || {}),
|
||||||
|
},
|
||||||
|
body: options.body ? JSON.stringify(options.body) : null,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
||||||
|
|
||||||
|
const blob = await response.blob()
|
||||||
|
const filename =
|
||||||
|
options.filename ||
|
||||||
|
response.headers
|
||||||
|
.get("Content-Disposition")
|
||||||
|
?.split("filename=")[1]
|
||||||
|
?.replace(/["']/g, "") ||
|
||||||
|
"download.bin"
|
||||||
|
|
||||||
|
const downloadUrl = window.URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement("a")
|
||||||
|
a.href = downloadUrl
|
||||||
|
a.download = filename
|
||||||
|
document.body.appendChild(a)
|
||||||
|
a.click()
|
||||||
|
a.remove()
|
||||||
|
window.URL.revokeObjectURL(downloadUrl)
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch (err) {
|
||||||
|
error.value = err
|
||||||
|
console.error("Download Error:", err)
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {data, loading, error, request, download}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" href="css/app.css" />
|
<link rel="stylesheet" href="/css/app.css" />
|
||||||
<link rel="stylesheet" href="css/elements.css" />
|
<link rel="stylesheet" href="/css/elements.css" />
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
require __DIR__.'/../app/Domains/UserManagement/Routes/api.php';
|
||||||
use Inertia\Inertia;
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('/', ['TestController', 'index']);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('/inertia', function () {
|
|
||||||
return Inertia::render('Pages/Home', [
|
|
||||||
'appName' => config('app.name'),
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Domains\Dashboard\Controllers\DashboardController;
|
use App\Domains\Dashboard\Controllers\DashboardController;
|
||||||
|
use App\Domains\UserManagement\Controllers\EmailVerificationController;
|
||||||
use App\Domains\UserManagement\Controllers\LoginController;
|
use App\Domains\UserManagement\Controllers\LoginController;
|
||||||
use App\Domains\UserManagement\Controllers\LogOutController;
|
use App\Domains\UserManagement\Controllers\LogOutController;
|
||||||
|
use App\Domains\UserManagement\Controllers\RegistrationController;
|
||||||
|
use App\Domains\UserManagement\Controllers\ResetPasswordController;
|
||||||
use App\Http\Controllers\TestRenderInertiaProvider;
|
use App\Http\Controllers\TestRenderInertiaProvider;
|
||||||
use App\Middleware\IdentifyTenant;
|
use App\Middleware\IdentifyTenant;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../app/Domains/UserManagement/Routes/web.php';
|
||||||
|
|
||||||
Route::middleware(IdentifyTenant::class)->group(function () {
|
Route::middleware(IdentifyTenant::class)->group(function () {
|
||||||
Route::get('/', DashboardController::class);
|
Route::get('/', DashboardController::class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Route::middleware(['auth'])->group(function () {
|
Route::middleware(['auth'])->group(function () {
|
||||||
|
|
||||||
Route::get('/messages', fn () => inertia('Messages'));
|
Route::get('/messages', fn () => inertia('Messages'));
|
||||||
Route::post('/logout', [LogoutController::class, 'logout']);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,9 +28,6 @@ Route::middleware(IdentifyTenant::class)->group(function () {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
route::get('/logout', LogOutController::class);
|
|
||||||
route::post('/login', [LoginController::class, 'doLogin']);
|
|
||||||
route::get('/login', [LoginController::class, 'loginForm']);
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('/messages', [TestRenderInertiaProvider::class, 'index']);
|
Route::get('/messages', [TestRenderInertiaProvider::class, 'index']);
|
||||||
|
|||||||
Reference in New Issue
Block a user