Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3570f442f5 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,3 +22,4 @@
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
Thumbs.db
|
||||
/docker-compose.yaml
|
||||
|
||||
5
Makefile
5
Makefile
@@ -2,6 +2,11 @@
|
||||
|
||||
FRONTEND_DIR ?= .
|
||||
|
||||
setup:
|
||||
rm -f docker-compose.yaml
|
||||
cp docker-compose.dev docker-compose.yaml
|
||||
docker-compose up -d
|
||||
|
||||
frontend:
|
||||
@cd $(FRONTEND_DIR) && \
|
||||
export QT_QPA_PLATFORM=offscreen && \
|
||||
|
||||
21
app/Enumerations/CostUnitType.php
Normal file
21
app/Enumerations/CostUnitType.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enumerations;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property string $slug
|
||||
* @property string $name
|
||||
*/
|
||||
class CostUnitType extends CommonModel
|
||||
{
|
||||
public const COST_UNIT_TYPE_EVENT = 'event';
|
||||
public const COST_UNIT_TYPE_RUNNING_JOB = 'running_job';
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
];
|
||||
}
|
||||
22
app/Enumerations/EatingHabit.php
Normal file
22
app/Enumerations/EatingHabit.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enumerations;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property string $slug
|
||||
* @property string $name
|
||||
*/
|
||||
class EatingHabit extends CommonModel
|
||||
{
|
||||
public const string EATING_HABIT_OMNIVOR = 'EATING_HABIT_OMNIVOR';
|
||||
public const string EATING_HABIT_VEGETARIAN = 'EATING_HABIT_VEGETARIAN';
|
||||
public const string EATING_HABIT_VEGAN = 'EATING_HABIT_VEGAN';
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
];
|
||||
}
|
||||
22
app/Enumerations/FirstAidPermission.php
Normal file
22
app/Enumerations/FirstAidPermission.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enumerations;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
|
||||
/**
|
||||
* @property string $slug
|
||||
* @property string $name
|
||||
*/
|
||||
class FirstAidPermission extends CommonModel
|
||||
{
|
||||
public const string FIRST_AID_PERMISSION_ALLOWED = 'FIRST_AID_PERMISSION_ALLOWED';
|
||||
public const string FIRST_AID_PERMISSION_DENIED = 'FIRST_AID_PERMISSION_DENIED';
|
||||
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
'description'
|
||||
];
|
||||
}
|
||||
26
app/Enumerations/SwimmingPermission.php
Normal file
26
app/Enumerations/SwimmingPermission.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enumerations;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property string $slug
|
||||
* @property string $name
|
||||
*/
|
||||
class SwimmingPermission extends CommonModel
|
||||
{
|
||||
public const string SWIMMING_PERMISSION_ALLOWED = 'SWIMMING_PERMISSION_ALLOWED';
|
||||
public const string SWIMMING_PERMISSION_LIMITED = 'SWIMMING_PERMISSION_LIMITED';
|
||||
public const string SWIMMING_PERMISSION_DENIED = 'SWIMMING_PERMISSION_DENIED';
|
||||
|
||||
|
||||
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
];
|
||||
}
|
||||
17
app/Enumerations/UserRole.php
Normal file
17
app/Enumerations/UserRole.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enumerations;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
|
||||
class UserRole extends CommonModel {
|
||||
public const USER_ROLE_ADMIN = 'ROLE_ADMINISTRATOR';
|
||||
public const USER_ROLE_GROUP_LEADER = 'ROLE_GROUP_LEADER';
|
||||
public const USER_ROLE_USER = 'ROLE_USER';
|
||||
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'slug'
|
||||
];
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
43
app/Installer/DevelopmentDataSeeder.php
Normal file
43
app/Installer/DevelopmentDataSeeder.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Installer;
|
||||
|
||||
use App\Enumerations\UserRole;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
|
||||
class DevelopmentDataSeeder {
|
||||
public function execute() {
|
||||
$this->installTenants();
|
||||
$this->installUsers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function installTenants() {
|
||||
Tenant::create([
|
||||
'slug' => 'wilde-moehre',
|
||||
'local_group_name' => 'Stamm Wilde Möhre',
|
||||
'url' => 'wilde-moehre.mareike.local',
|
||||
'account_iban' => 'DE12345678901234567890',
|
||||
'email' => 'test@example1.com',
|
||||
'city' => 'Halle (Saale)',
|
||||
'postcode' => '06120',
|
||||
'is_active_local_group' => true,
|
||||
'has_active_instance' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
private function installUsers() {
|
||||
User::create([
|
||||
'firstname' => 'Development',
|
||||
'lastname' => 'User',
|
||||
'user_role' => UserRole::USER_ROLE_ADMIN,
|
||||
'tenant' => 'lv',
|
||||
'email' => 'th.guenther@saale-mail.de',
|
||||
'password' => bcrypt('development'),
|
||||
'local_group_id' => 1,
|
||||
'username' => 'development',
|
||||
]);
|
||||
}
|
||||
}
|
||||
71
app/Installer/ProductionDataSeeder.php
Normal file
71
app/Installer/ProductionDataSeeder.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Installer;
|
||||
|
||||
use App\Enumerations\CostUnitType;
|
||||
use App\Enumerations\EatingHabit;
|
||||
use App\Enumerations\FirstAidPermission;
|
||||
use App\Enumerations\SwimmingPermission;
|
||||
use App\Enumerations\UserRole;
|
||||
use App\Models\Tenant;
|
||||
|
||||
class ProductionDataSeeder {
|
||||
public function execute() {
|
||||
$this->installUserRoles();
|
||||
$this->installCostUnitTypes();
|
||||
$this->installSwimmingPermissions();
|
||||
$this->installEatingHabits();
|
||||
$this->installFirstAidPermissions();
|
||||
$this->installTenants();
|
||||
}
|
||||
|
||||
private function installUserRoles() {
|
||||
UserRole::create(['name' => 'Administrator*in', 'slug' => UserRole::USER_ROLE_ADMIN]);
|
||||
UserRole::create(['name' => 'Vorstandsmitglied', 'slug' => UserRole::USER_ROLE_GROUP_LEADER]);
|
||||
UserRole::create(['name' => 'Benutzer*in', 'slug' => UserRole::USER_ROLE_USER]);
|
||||
}
|
||||
|
||||
private function installSwimmingPermissions() {
|
||||
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED]);
|
||||
SwimmingPermission::create(['name' => 'Mein Kind darf baden und kann NICHT schwimmen', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_LIMITED]);
|
||||
SwimmingPermission::create(['name' => 'Mein Kind darf nicht baden', 'slug' => SwimmingPermission::SWIMMING_PERMISSION_DENIED]);
|
||||
}
|
||||
|
||||
private function installEatingHabits() {
|
||||
EatingHabit::create(['name' => 'Vegan', 'slug' => EatingHabit::EATING_HABIT_VEGAN]);
|
||||
EatingHabit::create(['name' => 'Vegetarisch', 'slug' => EatingHabit::EATING_HABIT_VEGETARIAN]);
|
||||
EatingHabit::create(['name' => 'Omnivor', 'slug' => EatingHabit::EATING_HABIT_OMNIVOR]);
|
||||
|
||||
}
|
||||
private function installFirstAidPermissions() {
|
||||
FirstAidPermission::create([
|
||||
'name' => 'Zugestimmt',
|
||||
'description' => 'Ich STIMME der Anwendung von erweiteren Erste-Hilfe-Maßnahmen an meinem Kind explizit ZU.',
|
||||
'slug' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED]);
|
||||
|
||||
FirstAidPermission::create([
|
||||
'name' => 'Verweigert',
|
||||
'description' => 'Ich LEHNE die Anwendung von erweiteren Erste-Hilfe-Maßnahmen an meinem Kind explizit AB.',
|
||||
'slug' => FirstAidPermission::FIRST_AID_PERMISSION_DENIED]);
|
||||
}
|
||||
|
||||
private function installCostUnitTypes() {
|
||||
CostUnitType::create(['slug' => CostUnitType::COST_UNIT_TYPE_EVENT, 'name' => 'Veranstaltung']);
|
||||
CostUnitType::create(['slug' => CostUnitType::COST_UNIT_TYPE_RUNNING_JOB, 'name' => 'Laufende Tätigkeit']);
|
||||
|
||||
}
|
||||
|
||||
private function installTenants() {
|
||||
Tenant::create([
|
||||
'slug' => 'lv',
|
||||
'local_group_name' => 'Landesunmittelbare Mitglieder',
|
||||
'url' => 'mareike.local',
|
||||
'account_iban' => 'DE12345678901234567890',
|
||||
'email' => 'test@example.com',
|
||||
'city' => 'Lommatzsch',
|
||||
'postcode' => '01623',
|
||||
'is_active_local_group' => true,
|
||||
'has_active_instance' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
namespace App\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
24
app/Middleware/IdentifyTenant.php
Normal file
24
app/Middleware/IdentifyTenant.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Middleware;
|
||||
|
||||
use App\Models\Tenant;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class IdentifyTenant
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$host = $request->getHost();
|
||||
$tenant = Tenant::where(['url' => $host, 'has_active_instance' => true])->first();
|
||||
|
||||
if (! $tenant) {
|
||||
throw new NotFoundHttpException('Tenant not found');
|
||||
}
|
||||
|
||||
app()->instance('tenant', $tenant);
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
39
app/Models/Tenant.php
Normal file
39
app/Models/Tenant.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Scopes\CommonModel;
|
||||
|
||||
/**
|
||||
* @property string $slug
|
||||
* @property string $local_group
|
||||
* @property string $email
|
||||
* @property string $url
|
||||
* @property string $account_iban
|
||||
* @property string $city
|
||||
* @property string $postcode
|
||||
* @property string $gdpr_text
|
||||
* @property string $impress_text
|
||||
* @property string $url_participation_rules
|
||||
* @property boolean $events_allowed
|
||||
* @property boolean $has_active_instance
|
||||
*/
|
||||
class Tenant extends CommonModel
|
||||
{
|
||||
public const PRIMARY_TENANT_NAME = 'LV';
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'local_group_name',
|
||||
'email',
|
||||
'url',
|
||||
'account_iban',
|
||||
'city',
|
||||
'postcode',
|
||||
'gdpr_text',
|
||||
'impress_text',
|
||||
'url_participation_rules',
|
||||
'is_active_local_group',
|
||||
'has_active_instance'
|
||||
];
|
||||
}
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
@@ -18,8 +15,28 @@ class User extends Authenticatable
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'tenant_id',
|
||||
'user_role',
|
||||
'username',
|
||||
'firstname',
|
||||
'nickname',
|
||||
'lastname',
|
||||
'local_group_id',
|
||||
'membership_id',
|
||||
'address_1',
|
||||
'address_2',
|
||||
'postcode',
|
||||
'city',
|
||||
'email',
|
||||
'phone',
|
||||
'birthday',
|
||||
'medications',
|
||||
'allergies',
|
||||
'intolerances',
|
||||
'eating_habits',
|
||||
'swimming_permission',
|
||||
'first_aid_permission',
|
||||
'bank_account_iban',
|
||||
'password',
|
||||
];
|
||||
|
||||
@@ -29,7 +46,6 @@ class User extends Authenticatable
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -19,6 +20,11 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
Auth::provider('tenant-users', function ($app, array $config) {
|
||||
return new TenantUserProvider(
|
||||
$app['hash'],
|
||||
$config['model']
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
26
app/Providers/InertiaProvider.php
Normal file
26
app/Providers/InertiaProvider.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
final class InertiaProvider
|
||||
{
|
||||
private string $vueFile;
|
||||
private array $props;
|
||||
|
||||
public function __construct(string $vueFile, array $props) {
|
||||
$this->vueFile = $vueFile;
|
||||
$this->props = $props;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function render() : Response {
|
||||
return Inertia::render(
|
||||
str_replace('/', '/Views/', $this->vueFile),
|
||||
$this->props
|
||||
);
|
||||
}
|
||||
}
|
||||
23
app/Providers/TenantUserProvider.php
Normal file
23
app/Providers/TenantUserProvider.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\EloquentUserProvider;
|
||||
|
||||
class TenantUserProvider extends EloquentUserProvider
|
||||
{
|
||||
public function retrieveByCredentials(array $credentials)
|
||||
{
|
||||
$query = $this->createModel()->newQuery();
|
||||
|
||||
foreach ($credentials as $key => $value) {
|
||||
if (! str_contains($key, 'password')) {
|
||||
$query->where($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$query->where('tenant', app('tenant')->slug);
|
||||
|
||||
return $query->first();
|
||||
}
|
||||
}
|
||||
10
app/Scopes/CommonModel.php
Normal file
10
app/Scopes/CommonModel.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Scopes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class CommonModel extends Model
|
||||
{
|
||||
|
||||
}
|
||||
13
app/Scopes/InstancedModel.php
Normal file
13
app/Scopes/InstancedModel.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Scopes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class InstancedModel extends Model
|
||||
{
|
||||
protected static function booted()
|
||||
{
|
||||
static::addGlobalScope(new SiteScope());
|
||||
}
|
||||
}
|
||||
14
app/Scopes/SiteScope.php
Normal file
14
app/Scopes/SiteScope.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
namespace App\Scopes;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Scope;
|
||||
|
||||
class SiteScope implements Scope
|
||||
{
|
||||
public function apply(Builder $builder, Model $model): void
|
||||
{
|
||||
$builder->where($model->getTable() . '.tenant', app('tenant')->slug);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Middleware\IdentifyTenant;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
@@ -8,10 +9,11 @@ return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->append(IdentifyTenant::class);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
|
||||
];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"keywords": ["laravel", "framework"],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"php": "^8.5",
|
||||
"inertiajs/inertia-laravel": "^2.0",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1"
|
||||
|
||||
20
composer.lock
generated
20
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0e42fe1a7066e7a110e956ae26703d94",
|
||||
"content-hash": "587caff9de06de75c1e22cceac366334",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -2194,16 +2194,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "3.11.0",
|
||||
"version": "3.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/CarbonPHP/carbon.git",
|
||||
"reference": "bdb375400dcd162624531666db4799b36b64e4a1"
|
||||
"reference": "f438fcc98f92babee98381d399c65336f3a3827f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1",
|
||||
"reference": "bdb375400dcd162624531666db4799b36b64e4a1",
|
||||
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/f438fcc98f92babee98381d399c65336f3a3827f",
|
||||
"reference": "f438fcc98f92babee98381d399c65336f3a3827f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -2227,7 +2227,7 @@
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan": "^2.1.22",
|
||||
"phpunit/phpunit": "^10.5.53",
|
||||
"squizlabs/php_codesniffer": "^3.13.4"
|
||||
"squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/carbon"
|
||||
@@ -2270,14 +2270,14 @@
|
||||
}
|
||||
],
|
||||
"description": "An API extension for DateTime that supports 281 different languages.",
|
||||
"homepage": "https://carbon.nesbot.com",
|
||||
"homepage": "https://carbonphp.github.io/carbon/",
|
||||
"keywords": [
|
||||
"date",
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://carbon.nesbot.com/docs",
|
||||
"docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html",
|
||||
"issues": "https://github.com/CarbonPHP/carbon/issues",
|
||||
"source": "https://github.com/CarbonPHP/carbon"
|
||||
},
|
||||
@@ -2295,7 +2295,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-12-02T21:04:28+00:00"
|
||||
"time": "2026-01-29T09:26:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
@@ -8436,7 +8436,7 @@
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.2"
|
||||
"php": "^8.5"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.9.0"
|
||||
|
||||
@@ -61,8 +61,8 @@ return [
|
||||
|
||||
'providers' => [
|
||||
'users' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
||||
'driver' => 'tenant-users',
|
||||
'model' => App\Models\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
}
|
||||
};
|
||||
32
database/migrations/2026_01_30_140001_create_tenants.php
Normal file
32
database/migrations/2026_01_30_140001_create_tenants.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('tenants', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('slug')->unique();
|
||||
$table->string('local_group_name');
|
||||
$table->string('email');
|
||||
$table->string('url');
|
||||
$table->string('account_iban');
|
||||
$table->string('city');
|
||||
$table->string('postcode');
|
||||
$table->string('gdpr_text')-> nullable();
|
||||
$table->string('impress_text')->nullable();
|
||||
$table->string('url_participation_rules')->nullable();
|
||||
$table->boolean('has_active_instance')->default(true);
|
||||
$table->boolean('is_active_local_group')->default(true);
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('tenants');
|
||||
}
|
||||
};
|
||||
106
database/migrations/2026_01_30_140002_create_users_table.php
Normal file
106
database/migrations/2026_01_30_140002_create_users_table.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_roles', function (Blueprint $table) {
|
||||
$table->string('slug')->unique()->primary();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('eating_habits', function (Blueprint $table) {
|
||||
$table->string('slug')->unique()->primary();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('swimming_permissions', function (Blueprint $table) {
|
||||
$table->string('slug')->unique()->primary();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('first_aid_permissions', function (Blueprint $table) {
|
||||
$table->string('slug')->unique()->primary();
|
||||
$table->string('name');
|
||||
$table->string('description');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('tenant');
|
||||
$table->string('user_role');
|
||||
$table->string('username')->unique();
|
||||
$table->string('password')->nullable();
|
||||
$table->string('firstname');
|
||||
$table->string('nickname')->nullable();
|
||||
$table->string('lastname');
|
||||
$table->foreignId('local_group_id')->references('id')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->string('membership_id')->nullable();
|
||||
$table->string('address_1')->nullable();
|
||||
$table->string('address_2')->nullable();
|
||||
$table->string('postcode')->nullable();
|
||||
$table->string('city')->nullable();
|
||||
$table->string('email')->nullable();
|
||||
$table->string('phone')->nullable();
|
||||
$table->date('birthday')->nullable();
|
||||
$table->string('medications')->nullable();
|
||||
$table->string('allergies')->nullable();
|
||||
$table->string('intolerances')->nullable();
|
||||
$table->string('eating_habits')->nullable();
|
||||
$table->string('swimming_permission')->nullable();
|
||||
$table->string('first_aid_permission')->nullable();
|
||||
$table->string('bank_account_iban')->nullable();
|
||||
$table->string('activation_token')->nullable();
|
||||
$table->boolean('activated')->default(false);
|
||||
|
||||
$table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->foreign('user_role')->references('slug')->on('user_roles')->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('first_aid_permission')->references('slug')->on('first_aid_permissions')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
||||
$table->string('email')->primary();
|
||||
$table->string('token');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('sessions', function (Blueprint $table) {
|
||||
$table->string('id')->primary();
|
||||
$table->foreignId('user_id')->nullable()->index();
|
||||
$table->string('ip_address', 45)->nullable();
|
||||
$table->text('user_agent')->nullable();
|
||||
$table->longText('payload');
|
||||
$table->integer('last_activity')->index();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('users');
|
||||
Schema::dropIfExists('password_reset_tokens');
|
||||
Schema::dropIfExists('sessions');
|
||||
Schema::dropIfExists('user_roles');
|
||||
Schema::dropIfExists('eating_habits');
|
||||
Schema::dropIfExists('swimming_permissions');
|
||||
Schema::dropIfExists('first_aid_permissions');
|
||||
}
|
||||
};
|
||||
43
database/migrations/2026_01_30_140010_create_cost_units.php
Normal file
43
database/migrations/2026_01_30_140010_create_cost_units.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('cost_unit_types', function (Blueprint $table) {
|
||||
$table->string('slug')->primary();
|
||||
$table->string('name');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
|
||||
|
||||
Schema::create('cost_units', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('tenant');
|
||||
$table->string('name');
|
||||
$table->string('type');
|
||||
$table->dateTime('billing_deadline');
|
||||
$table->float('distance_allowance');
|
||||
$table->boolean('mail_on_new')->default(true);
|
||||
$table->boolean('allow_new')->default(true);
|
||||
$table->boolean('archived')->default(false);
|
||||
$table->string('treasurers');
|
||||
|
||||
$table->foreign('tenant')->references('slug')->on('tenants')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->foreign('type')->references('slug')->on('cost_unit_types')->cascadeOnDelete()->cascadeOnUpdate();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('cost_units');
|
||||
Schema::dropIfExists('cost_unit_types');
|
||||
}
|
||||
};
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Installer\DevelopmentDataSeeder;
|
||||
use App\Installer\ProductionData;
|
||||
use App\Installer\ProductionDataSeeder;
|
||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
@@ -15,11 +17,12 @@ class DatabaseSeeder extends Seeder
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
$productionSeeeder = new ProductionDataSeeder();
|
||||
$productionSeeeder->execute();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
if (str_ends_with(env('APP_URL'), 'mareike.local')) {
|
||||
$deveopmentDataSeeder = new DevelopmentDataSeeder();
|
||||
$deveopmentDataSeeder->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ services:
|
||||
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.mareike.rule=Host(`mareike.local`)"
|
||||
- "traefik.http.routers.mareike.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`)"
|
||||
- "traefik.http.routers.mareike.entrypoints=websecure"
|
||||
- "traefik.http.routers.mareike.tls=true"
|
||||
- "traefik.http.services.mareike.loadbalancer.server.port=80"
|
||||
|
||||
- "traefik.http.routers.mareike-http.rule=Host(`mareike.local`)"
|
||||
- "traefik.http.routers.mareike-http.rule=Host(`mareike.local`) || Host(`admin.mareike.local`) || Host(`wilde-moehre.mareike.local`)"
|
||||
- "traefik.http.routers.mareike-http.entrypoints=web"
|
||||
- "traefik.http.routers.mareike-http.middlewares=redirect-to-https"
|
||||
|
||||
@@ -5,43 +5,32 @@ import { InertiaProgress } from '@inertiajs/progress'
|
||||
import Vue3Toastify, { toast } from 'vue3-toastify'
|
||||
import 'vue3-toastify/dist/index.css'
|
||||
|
||||
// Optional: Lade-Balken für Inertia
|
||||
InertiaProgress.init()
|
||||
|
||||
// Inertia App starten
|
||||
createInertiaApp({
|
||||
// Alle Pages in app/Views/Pages/**/*.vue werden automatisch importiert
|
||||
resolve: name => {
|
||||
// Vite scannt die Pages dynamisch
|
||||
const pages = import.meta.glob('@views/**/*.vue')
|
||||
const pages = import.meta.glob('@domains/**/*.vue')
|
||||
|
||||
// Suche nach der richtigen Page-Datei
|
||||
const key = Object.keys(pages).find(k =>
|
||||
k.endsWith(`/${name}.vue`) || k.endsWith(`/${name}/index.vue`)
|
||||
)
|
||||
|
||||
if (!key) throw new Error(`Page not found: ${name}`)
|
||||
|
||||
// Unterstützt sowohl <script setup> als auch klassische Exports
|
||||
return pages[key]()
|
||||
},
|
||||
|
||||
// Setup der App
|
||||
setup({ el, App, props, plugin }) {
|
||||
const vueApp = createApp({ render: () => h(App, props) })
|
||||
|
||||
// Inertia Plugin
|
||||
vueApp.use(plugin)
|
||||
|
||||
// Toastify global verfügbar machen
|
||||
vueApp.use(Vue3Toastify, {
|
||||
autoClose: 3000,
|
||||
position: 'top-right',
|
||||
pauseOnHover: true,
|
||||
})
|
||||
vueApp.config.globalProperties.$toast = toast
|
||||
|
||||
// Mounten auf das DOM
|
||||
vueApp.config.globalProperties.$toast = toast
|
||||
vueApp.mount(el)
|
||||
},
|
||||
})
|
||||
|
||||
15
routes/api.php
Normal file
15
routes/api.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
|
||||
|
||||
Route::get('/', ['TestController', 'index']);
|
||||
|
||||
|
||||
|
||||
Route::get('/inertia', function () {
|
||||
return Inertia::render('Pages/Home', [
|
||||
'appName' => config('app.name'),
|
||||
]);
|
||||
});
|
||||
@@ -15,6 +15,7 @@ export default defineConfig({
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'resources/js'),
|
||||
'@views': path.resolve(__dirname, 'app/Views'),
|
||||
'@domains': path.resolve(__dirname, 'app/Domains'),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user