feat: initialize Infra Manager with React, Tailwind CSS, and Inertia.js

- Added main application entry point (app.jsx) and bootstrap file for React setup.
- Created Wiki index page (Index.jsx) for documentation and file management.
- Implemented utility functions for formatting and status handling in utils.js.
- Set up Blade template (app.blade.php) for Inertia.js integration.
- Defined API routes for metrics and terminal access in api.php.
- Established console commands for scheduled tasks in console.php.
- Created web routes for various application features including authentication, hypervisors, containers, cloud accounts, security, backups, and wiki management in web.php.
- Developed setup scripts for Windows (setup.ps1) and Linux (setup.sh) environments.
- Configured Tailwind CSS for styling with custom themes and animations in tailwind.config.js.
- Set up Vite for asset bundling and development server in vite.config.js.
This commit is contained in:
I-Fun
2026-04-07 20:29:32 +07:00
commit c649f055d8
126 changed files with 8122 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
<?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('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password')->nullable(); // nullable for LDAP users
$table->string('avatar')->nullable();
$table->string('auth_source')->default('local'); // local | ldap
$table->string('ldap_guid')->nullable()->unique();
$table->string('ldap_domain')->nullable();
$table->boolean('is_active')->default(true);
$table->boolean('mfa_enabled')->default(false);
$table->string('mfa_secret')->nullable();
$table->json('mfa_recovery_codes')->nullable();
$table->timestamp('last_login_at')->nullable();
$table->string('last_login_ip')->nullable();
$table->json('preferences')->nullable(); // theme, language, etc.
$table->rememberToken();
$table->timestamps();
$table->softDeletes();
});
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();
});
}
public function down(): void
{
Schema::dropIfExists('sessions');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('users');
}
};

View File

@@ -0,0 +1,29 @@
<?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('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration');
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration');
});
}
public function down(): void
{
Schema::dropIfExists('cache_locks');
Schema::dropIfExists('cache');
}
};

View File

@@ -0,0 +1,51 @@
<?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('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
public function down(): void
{
Schema::dropIfExists('failed_jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('jobs');
}
};

View File

@@ -0,0 +1,26 @@
<?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('settings', function (Blueprint $table) {
$table->id();
$table->string('group')->default('general');
$table->string('key')->unique();
$table->text('value')->nullable();
$table->string('type')->default('string'); // string, json, boolean, integer
$table->boolean('is_encrypted')->default(false);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('settings');
}
};

View File

@@ -0,0 +1,102 @@
<?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('hypervisors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('type'); // vcenter, esxi, proxmox, hyper-v, xen, kvm
$table->string('version')->nullable();
$table->string('host');
$table->integer('port')->default(443);
$table->string('username');
$table->text('password'); // encrypted
$table->boolean('ssl_verify')->default(true);
$table->string('datacenter')->nullable();
$table->string('status')->default('unknown'); // online, offline, unknown, error
$table->json('metadata')->nullable();
$table->timestamp('last_sync_at')->nullable();
$table->text('last_error')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('virtual_machines', function (Blueprint $table) {
$table->id();
$table->foreignId('hypervisor_id')->constrained('hypervisors')->cascadeOnDelete();
$table->string('remote_id'); // ID from hypervisor
$table->string('name');
$table->string('status'); // running, stopped, suspended, unknown
$table->string('os_type')->nullable();
$table->string('os_version')->nullable();
$table->integer('cpu_count')->default(0);
$table->bigInteger('memory_mb')->default(0);
$table->bigInteger('disk_gb')->default(0);
$table->string('ip_address')->nullable();
$table->string('mac_address')->nullable();
$table->string('host_node')->nullable();
$table->string('cluster')->nullable();
$table->string('datastore')->nullable();
$table->json('tags')->nullable();
$table->json('metadata')->nullable();
$table->float('cpu_usage')->default(0);
$table->float('memory_usage')->default(0);
$table->foreignId('created_by')->nullable()->constrained('users');
$table->timestamps();
$table->unique(['hypervisor_id', 'remote_id']);
});
Schema::create('vm_templates', function (Blueprint $table) {
$table->id();
$table->foreignId('hypervisor_id')->nullable()->constrained('hypervisors')->nullOnDelete();
$table->string('name');
$table->string('description')->nullable();
$table->string('os_type');
$table->string('os_version')->nullable();
$table->integer('cpu_count')->default(2);
$table->bigInteger('memory_mb')->default(2048);
$table->bigInteger('disk_gb')->default(50);
$table->json('network_config')->nullable();
$table->json('customization')->nullable(); // ansible playbooks, cloud-init, etc.
$table->json('variables')->nullable(); // template variables
$table->text('provision_script')->nullable(); // post-deploy script
$table->string('status')->default('ready'); // ready, deploying, error
$table->string('remote_id')->nullable(); // template ID on hypervisor
$table->json('tags')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('vm_deployments', function (Blueprint $table) {
$table->id();
$table->foreignId('template_id')->constrained('vm_templates');
$table->foreignId('hypervisor_id')->constrained('hypervisors');
$table->foreignId('virtual_machine_id')->nullable()->constrained('virtual_machines');
$table->string('vm_name');
$table->json('deploy_config');
$table->string('status'); // queued, running, success, failed
$table->text('log')->nullable();
$table->foreignId('deployed_by')->constrained('users');
$table->timestamp('started_at')->nullable();
$table->timestamp('completed_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('vm_deployments');
Schema::dropIfExists('vm_templates');
Schema::dropIfExists('virtual_machines');
Schema::dropIfExists('hypervisors');
}
};

View File

@@ -0,0 +1,80 @@
<?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('docker_endpoints', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('type')->default('docker'); // docker, swarm, k8s
$table->string('host'); // unix:///var/run/docker.sock or tcp://host:port
$table->boolean('tls_enabled')->default(false);
$table->text('tls_cert')->nullable();
$table->text('tls_key')->nullable();
$table->text('tls_ca')->nullable();
$table->string('status')->default('unknown');
$table->json('info')->nullable(); // Docker engine info
$table->timestamp('last_sync_at')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('containers', function (Blueprint $table) {
$table->id();
$table->foreignId('endpoint_id')->constrained('docker_endpoints')->cascadeOnDelete();
$table->string('container_id', 64); // Docker container ID
$table->string('name');
$table->string('image');
$table->string('image_id')->nullable();
$table->string('status'); // running, stopped, paused, exited, dead
$table->string('state')->nullable();
$table->json('ports')->nullable();
$table->json('volumes')->nullable();
$table->json('env')->nullable();
$table->json('labels')->nullable();
$table->json('networks')->nullable();
$table->bigInteger('created_at_unix')->nullable();
$table->timestamps();
$table->unique(['endpoint_id', 'container_id']);
});
Schema::create('stacks', function (Blueprint $table) {
$table->id();
$table->foreignId('endpoint_id')->constrained('docker_endpoints')->cascadeOnDelete();
$table->string('name');
$table->string('type')->default('compose'); // compose, swarm-stack
$table->text('compose_content'); // docker-compose.yml content
$table->json('env_vars')->nullable();
$table->string('status')->default('stopped');
$table->string('remote_id')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('docker_images', function (Blueprint $table) {
$table->id();
$table->foreignId('endpoint_id')->constrained('docker_endpoints')->cascadeOnDelete();
$table->string('image_id', 71);
$table->json('repo_tags')->nullable();
$table->bigInteger('size')->default(0);
$table->bigInteger('created_at_unix')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('docker_images');
Schema::dropIfExists('stacks');
Schema::dropIfExists('containers');
Schema::dropIfExists('docker_endpoints');
}
};

View File

@@ -0,0 +1,50 @@
<?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('cloud_accounts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('provider'); // aws, alibaba, tencent, gcp, azure, digitalocean, vultr, hetzner
$table->string('account_id')->nullable(); // AWS account ID, etc.
$table->text('credentials'); // encrypted JSON
$table->string('region')->nullable(); // default region
$table->json('regions')->nullable(); // all available regions
$table->boolean('is_active')->default(true);
$table->string('status')->default('unknown');
$table->json('tags')->nullable();
$table->timestamp('last_sync_at')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('cloud_resources', function (Blueprint $table) {
$table->id();
$table->foreignId('account_id')->constrained('cloud_accounts')->cascadeOnDelete();
$table->string('resource_id'); // cloud provider resource ID
$table->string('resource_type'); // ec2, ecs, rds, vpc, s3, etc.
$table->string('name');
$table->string('status');
$table->string('region');
$table->json('properties')->nullable();
$table->json('tags')->nullable();
$table->decimal('monthly_cost', 10, 4)->nullable();
$table->timestamps();
$table->unique(['account_id', 'resource_id', 'resource_type']);
});
}
public function down(): void
{
Schema::dropIfExists('cloud_resources');
Schema::dropIfExists('cloud_accounts');
}
};

View File

@@ -0,0 +1,64 @@
<?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('security_scans', function (Blueprint $table) {
$table->id();
$table->string('target_type'); // vm, container, host, cloud_resource
$table->unsignedBigInteger('target_id');
$table->string('target_name');
$table->string('scan_type'); // vulnerability, cis, pci, soc2, custom
$table->string('status'); // queued, running, completed, failed
$table->float('score')->nullable(); // 0-100
$table->string('grade')->nullable(); // A, B, C, D, F
$table->integer('critical_count')->default(0);
$table->integer('high_count')->default(0);
$table->integer('medium_count')->default(0);
$table->integer('low_count')->default(0);
$table->integer('info_count')->default(0);
$table->json('findings')->nullable();
$table->text('raw_output')->nullable();
$table->foreignId('initiated_by')->constrained('users');
$table->timestamp('started_at')->nullable();
$table->timestamp('completed_at')->nullable();
$table->timestamps();
});
Schema::create('security_scores', function (Blueprint $table) {
$table->id();
$table->string('target_type');
$table->unsignedBigInteger('target_id');
$table->float('score')->default(0);
$table->string('grade')->default('F');
$table->json('breakdown')->nullable();
$table->timestamp('calculated_at');
$table->timestamps();
$table->unique(['target_type', 'target_id']);
});
Schema::create('security_policies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('type'); // firewall, password, access, encryption
$table->text('description')->nullable();
$table->json('rules');
$table->boolean('is_active')->default(true);
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('security_policies');
Schema::dropIfExists('security_scores');
Schema::dropIfExists('security_scans');
}
};

View File

@@ -0,0 +1,69 @@
<?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('backup_policies', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('target_type'); // vm, container, database, files
$table->json('target_ids');
$table->string('schedule'); // cron expression
$table->string('retention_type')->default('count'); // count, days
$table->integer('retention_value')->default(7);
$table->string('storage_type'); // local, s3, sftp, nfs
$table->json('storage_config')->nullable(); // encrypted
$table->boolean('is_active')->default(true);
$table->json('compression')->nullable();
$table->json('encryption')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
});
Schema::create('backups', function (Blueprint $table) {
$table->id();
$table->foreignId('policy_id')->nullable()->constrained('backup_policies')->nullOnDelete();
$table->string('target_type');
$table->unsignedBigInteger('target_id');
$table->string('target_name');
$table->string('status'); // running, success, failed, expired
$table->string('storage_path')->nullable();
$table->bigInteger('size_bytes')->nullable();
$table->string('checksum')->nullable();
$table->json('metadata')->nullable();
$table->text('notes')->nullable();
$table->timestamp('expires_at')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamp('started_at')->nullable();
$table->timestamp('completed_at')->nullable();
$table->timestamps();
});
Schema::create('restore_jobs', function (Blueprint $table) {
$table->id();
$table->foreignId('backup_id')->constrained('backups');
$table->string('target_type');
$table->unsignedBigInteger('target_id')->nullable();
$table->string('restore_mode'); // overwrite, new, test
$table->json('restore_config')->nullable();
$table->string('status'); // queued, running, success, failed
$table->text('log')->nullable();
$table->foreignId('initiated_by')->constrained('users');
$table->timestamp('started_at')->nullable();
$table->timestamp('completed_at')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('restore_jobs');
Schema::dropIfExists('backups');
Schema::dropIfExists('backup_policies');
}
};

View File

@@ -0,0 +1,75 @@
<?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('metrics', function (Blueprint $table) {
$table->id();
$table->string('target_type');
$table->unsignedBigInteger('target_id');
$table->string('metric_name');
$table->float('value');
$table->string('unit')->nullable();
$table->json('labels')->nullable();
$table->timestamp('recorded_at');
$table->timestamps();
$table->index(['target_type', 'target_id', 'metric_name', 'recorded_at']);
});
Schema::create('alert_rules', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('target_type')->nullable(); // null = all targets
$table->string('metric_name');
$table->string('condition'); // >, <, >=, <=, ==, !=
$table->float('threshold');
$table->integer('duration_seconds')->default(60);
$table->string('severity'); // critical, warning, info
$table->json('notification_channels')->nullable();
$table->boolean('is_active')->default(true);
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
});
Schema::create('alerts', function (Blueprint $table) {
$table->id();
$table->foreignId('rule_id')->nullable()->constrained('alert_rules')->nullOnDelete();
$table->string('target_type');
$table->unsignedBigInteger('target_id');
$table->string('target_name');
$table->string('title');
$table->text('message');
$table->string('severity'); // critical, warning, info
$table->string('status')->default('firing'); // firing, acknowledged, resolved
$table->foreignId('acknowledged_by')->nullable()->constrained('users');
$table->timestamp('acknowledged_at')->nullable();
$table->timestamp('resolved_at')->nullable();
$table->timestamps();
});
Schema::create('monitoring_dashboards', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->json('panels'); // dashboard panel config
$table->boolean('is_default')->default(false);
$table->boolean('is_public')->default(false);
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('monitoring_dashboards');
Schema::dropIfExists('alerts');
Schema::dropIfExists('alert_rules');
Schema::dropIfExists('metrics');
}
};

View File

@@ -0,0 +1,72 @@
<?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('password_entries', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->string('title');
$table->string('category')->nullable();
$table->string('url')->nullable();
$table->string('username')->nullable();
$table->text('password'); // encrypted
$table->text('notes')->nullable(); // encrypted
$table->json('custom_fields')->nullable(); // encrypted
$table->json('tags')->nullable();
$table->boolean('is_shared')->default(false);
$table->json('shared_with')->nullable(); // user IDs
$table->timestamp('expires_at')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('certificates', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('domain');
$table->json('san')->nullable(); // Subject Alternative Names
$table->text('certificate'); // encrypted
$table->text('private_key'); // encrypted
$table->text('chain')->nullable(); // encrypted
$table->string('issuer')->nullable();
$table->string('type'); // self-signed, lets-encrypt, custom
$table->boolean('auto_renew')->default(false);
$table->timestamp('issued_at')->nullable();
$table->timestamp('expires_at');
$table->json('tags')->nullable();
$table->string('status'); // valid, expiring, expired, revoked
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('ssh_keys', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->string('name');
$table->string('fingerprint')->unique();
$table->text('public_key');
$table->text('private_key')->nullable(); // encrypted
$table->string('algorithm')->default('rsa');
$table->integer('key_size')->default(4096);
$table->boolean('has_passphrase')->default(false);
$table->json('tags')->nullable();
$table->timestamps();
$table->softDeletes();
});
}
public function down(): void
{
Schema::dropIfExists('ssh_keys');
Schema::dropIfExists('certificates');
Schema::dropIfExists('password_entries');
}
};

View File

@@ -0,0 +1,73 @@
<?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('wiki_spaces', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->string('icon')->default('BookOpen');
$table->string('color')->default('#3b82f6');
$table->boolean('is_public')->default(false);
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
$table->softDeletes();
});
Schema::create('wiki_pages', function (Blueprint $table) {
$table->id();
$table->foreignId('space_id')->constrained('wiki_spaces')->cascadeOnDelete();
$table->foreignId('parent_id')->nullable()->constrained('wiki_pages')->nullOnDelete();
$table->string('title');
$table->string('slug');
$table->longText('content'); // Markdown content
$table->longText('content_html')->nullable(); // rendered HTML cache
$table->json('tags')->nullable();
$table->boolean('is_published')->default(true);
$table->integer('order')->default(0);
$table->foreignId('created_by')->constrained('users');
$table->foreignId('updated_by')->nullable()->constrained('users');
$table->timestamps();
$table->softDeletes();
$table->unique(['space_id', 'slug']);
});
Schema::create('wiki_page_revisions', function (Blueprint $table) {
$table->id();
$table->foreignId('page_id')->constrained('wiki_pages')->cascadeOnDelete();
$table->longText('content');
$table->string('change_summary')->nullable();
$table->foreignId('created_by')->constrained('users');
$table->timestamps();
});
Schema::create('wiki_attachments', function (Blueprint $table) {
$table->id();
$table->foreignId('page_id')->nullable()->constrained('wiki_pages')->nullOnDelete();
$table->string('filename');
$table->string('original_name');
$table->string('mime_type');
$table->bigInteger('size');
$table->string('path');
$table->string('disk')->default('local');
$table->foreignId('uploaded_by')->constrained('users');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('wiki_attachments');
Schema::dropIfExists('wiki_page_revisions');
Schema::dropIfExists('wiki_pages');
Schema::dropIfExists('wiki_spaces');
}
};

View File

@@ -0,0 +1,37 @@
<?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('ldap_configs', function (Blueprint $table) {
$table->id();
$table->string('name')->default('default');
$table->string('host');
$table->integer('port')->default(389);
$table->boolean('use_ssl')->default(false);
$table->boolean('use_tls')->default(false);
$table->string('base_dn');
$table->string('bind_dn')->nullable();
$table->text('bind_password')->nullable(); // encrypted
$table->string('user_search_base')->nullable();
$table->string('user_filter')->default('(sAMAccountName={username})');
$table->string('group_search_base')->nullable();
$table->string('group_filter')->nullable();
$table->string('admin_group_dn')->nullable();
$table->json('attribute_map')->nullable(); // ldap attr -> user field map
$table->boolean('is_active')->default(false);
$table->string('status')->default('unconfigured');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('ldap_configs');
}
};