add composer-unused, comprehensive docs, and project restructure

- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 15:27:35 +01:00
parent 3eb9cc0ac4
commit 25370a1a55
389 changed files with 40506 additions and 8071 deletions

View File

@@ -1,5 +1,6 @@
-- Consolidated schema (fresh install)
-- Includes all current tables, columns, indexes, and seed data.
-- Generated: 2026-02-19
-- Includes all current tables, columns, indexes and seed data.
CREATE TABLE IF NOT EXISTS `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
@@ -21,6 +22,7 @@ CREATE TABLE IF NOT EXISTS `users` (
`hire_date` DATE NULL,
`email_verified_at` DATETIME NULL DEFAULT NULL,
`last_login_at` DATETIME NULL DEFAULT NULL,
`last_login_provider` VARCHAR(20) NULL DEFAULT NULL,
`password` VARCHAR(255) NOT NULL,
`locale` VARCHAR(10) DEFAULT NULL,
`totp_secret` VARCHAR(255) DEFAULT NULL,
@@ -32,6 +34,7 @@ CREATE TABLE IF NOT EXISTS `users` (
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`active` TINYINT(1) NOT NULL DEFAULT 1,
`authz_version` INT UNSIGNED NOT NULL DEFAULT 1,
`active_changed_at` DATETIME NULL DEFAULT NULL,
`active_changed_by` INT UNSIGNED DEFAULT NULL,
PRIMARY KEY (`id`),
@@ -69,6 +72,8 @@ CREATE TABLE IF NOT EXISTS `tenants` (
`privacy_url` VARCHAR(255) NULL,
`imprint_url` VARCHAR(255) NULL,
`primary_color` VARCHAR(7) NULL,
`default_theme` VARCHAR(32) NULL,
`allow_user_theme` TINYINT(1) NULL,
`status` VARCHAR(20) NOT NULL DEFAULT 'active',
`status_changed_at` DATETIME NULL DEFAULT NULL,
`status_changed_by` INT UNSIGNED DEFAULT NULL,
@@ -123,6 +128,7 @@ CREATE TABLE IF NOT EXISTS `departments` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` CHAR(36) NOT NULL,
`description` VARCHAR(255) NOT NULL,
`tenant_id` INT UNSIGNED NOT NULL,
`code` VARCHAR(50) DEFAULT NULL,
`cost_center` VARCHAR(50) DEFAULT NULL,
`active` TINYINT(1) NOT NULL DEFAULT 1,
@@ -132,9 +138,11 @@ CREATE TABLE IF NOT EXISTS `departments` (
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_departments_uuid` (`uuid`),
KEY `idx_departments_tenant` (`tenant_id`),
KEY `idx_departments_active` (`active`),
KEY `idx_departments_created_by` (`created_by`),
KEY `idx_departments_modified_by` (`modified_by`),
CONSTRAINT `fk_departments_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE RESTRICT,
CONSTRAINT `fk_departments_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_departments_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -183,6 +191,42 @@ CREATE TABLE IF NOT EXISTS `settings` (
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `tenant_auth_microsoft` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`tenant_id` INT UNSIGNED NOT NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 0,
`enforce_microsoft_login` TINYINT(1) NOT NULL DEFAULT 0,
`sync_profile_on_login` TINYINT(1) NOT NULL DEFAULT 0,
`sync_profile_fields` TEXT NULL,
`entra_tenant_id` VARCHAR(64) NULL,
`allowed_domains` TEXT NULL,
`use_shared_app` TINYINT(1) NOT NULL DEFAULT 1,
`client_id_override` VARCHAR(191) NULL,
`client_secret_override_enc` TEXT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_tenant_auth_microsoft_tenant` (`tenant_id`),
CONSTRAINT `fk_tenant_auth_microsoft_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_external_identities` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT UNSIGNED NOT NULL,
`provider` VARCHAR(32) NOT NULL,
`oid` VARCHAR(128) NOT NULL,
`tid` VARCHAR(64) NOT NULL,
`issuer` VARCHAR(255) NOT NULL,
`subject` VARCHAR(255) NOT NULL,
`email_at_link_time` VARCHAR(255) NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_user_external_provider_tid_oid` (`provider`, `tid`, `oid`),
UNIQUE KEY `uniq_user_external_provider_iss_sub` (`provider`, `issuer`, `subject`),
KEY `idx_user_external_user` (`user_id`),
CONSTRAINT `fk_user_external_identity_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_tenants` (
`user_id` INT UNSIGNED NOT NULL,
`tenant_id` INT UNSIGNED NOT NULL,
@@ -226,17 +270,117 @@ CREATE TABLE IF NOT EXISTS `user_departments` (
CONSTRAINT `fk_user_departments_department` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `tenant_departments` (
CREATE TABLE IF NOT EXISTS `user_saved_filters` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` CHAR(36) NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
`context` VARCHAR(64) NOT NULL,
`name` VARCHAR(120) NOT NULL,
`query_json` TEXT NOT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_user_saved_filters_uuid` (`uuid`),
KEY `idx_user_saved_filters_user_context` (`user_id`, `context`),
KEY `idx_user_saved_filters_user_context_created` (`user_id`, `context`, `created`),
CONSTRAINT `fk_user_saved_filters_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `tenant_custom_field_definitions` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` CHAR(36) NOT NULL,
`tenant_id` INT UNSIGNED NOT NULL,
`department_id` INT UNSIGNED NOT NULL,
`field_key` VARCHAR(120) NOT NULL,
`label` VARCHAR(160) NOT NULL,
`type` VARCHAR(32) NOT NULL,
`is_required` TINYINT(1) NOT NULL DEFAULT 0,
`is_filterable` TINYINT(1) NOT NULL DEFAULT 0,
`active` TINYINT(1) NOT NULL DEFAULT 1,
`sort_order` INT NOT NULL DEFAULT 100,
`created_by` INT UNSIGNED DEFAULT NULL,
`modified_by` INT UNSIGNED DEFAULT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_tenant_custom_field_uuid` (`uuid`),
UNIQUE KEY `uniq_tenant_custom_field_key` (`tenant_id`, `field_key`),
KEY `idx_tenant_custom_field_tenant_active` (`tenant_id`, `active`, `sort_order`),
KEY `idx_tenant_custom_field_created_by` (`created_by`),
KEY `idx_tenant_custom_field_modified_by` (`modified_by`),
CONSTRAINT `fk_tenant_custom_field_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_tenant_custom_field_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_tenant_custom_field_modified_by` FOREIGN KEY (`modified_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `tenant_custom_field_options` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`definition_id` INT UNSIGNED NOT NULL,
`option_key` VARCHAR(120) NOT NULL,
`label` VARCHAR(160) NOT NULL,
`active` TINYINT(1) NOT NULL DEFAULT 1,
`sort_order` INT NOT NULL DEFAULT 100,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_custom_field_option_key` (`definition_id`, `option_key`),
KEY `idx_custom_field_option_definition_active` (`definition_id`, `active`, `sort_order`),
CONSTRAINT `fk_custom_field_option_definition` FOREIGN KEY (`definition_id`) REFERENCES `tenant_custom_field_definitions` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_custom_field_values` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT UNSIGNED NOT NULL,
`definition_id` INT UNSIGNED NOT NULL,
`value_text` TEXT NULL,
`value_bool` TINYINT(1) NULL,
`value_date` DATE NULL,
`option_id` INT UNSIGNED NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_user_custom_field_definition` (`user_id`, `definition_id`),
KEY `idx_ucfv_definition` (`definition_id`),
KEY `idx_ucfv_option` (`option_id`),
KEY `idx_ucfv_bool` (`definition_id`, `value_bool`),
KEY `idx_ucfv_date` (`definition_id`, `value_date`),
CONSTRAINT `fk_ucfv_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_ucfv_definition` FOREIGN KEY (`definition_id`) REFERENCES `tenant_custom_field_definitions` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_ucfv_option` FOREIGN KEY (`option_id`) REFERENCES `tenant_custom_field_options` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_custom_field_value_options` (
`value_id` INT UNSIGNED NOT NULL,
`option_id` INT UNSIGNED NOT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`value_id`, `option_id`),
KEY `idx_ucfvo_option` (`option_id`),
CONSTRAINT `fk_ucfvo_value` FOREIGN KEY (`value_id`) REFERENCES `user_custom_field_values` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_ucfvo_option` FOREIGN KEY (`option_id`) REFERENCES `tenant_custom_field_options` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_api_tokens` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` CHAR(36) NOT NULL,
`user_id` INT UNSIGNED NOT NULL,
`name` VARCHAR(120) NOT NULL DEFAULT '',
`selector` CHAR(24) NOT NULL,
`token_hash` CHAR(64) NOT NULL,
`tenant_id` INT UNSIGNED NULL COMMENT 'Optional: scope token to specific tenant',
`last_used_at` DATETIME NULL DEFAULT NULL,
`last_ip` VARCHAR(45) NULL DEFAULT NULL,
`expires_at` DATETIME NULL DEFAULT NULL,
`revoked_at` DATETIME NULL DEFAULT NULL,
`created_by` INT UNSIGNED NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_tenant_department` (`tenant_id`, `department_id`),
KEY `idx_tenant_departments_tenant` (`tenant_id`),
KEY `idx_tenant_departments_department` (`department_id`),
CONSTRAINT `fk_tenant_departments_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_tenant_departments_department` FOREIGN KEY (`department_id`) REFERENCES `departments` (`id`) ON DELETE CASCADE
UNIQUE KEY `uniq_user_api_token_uuid` (`uuid`),
UNIQUE KEY `uniq_user_api_token_selector` (`selector`),
KEY `idx_user_api_token_user` (`user_id`),
KEY `idx_user_api_token_tenant` (`tenant_id`),
KEY `idx_user_api_token_expires` (`expires_at`),
CONSTRAINT `fk_user_api_token_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_user_api_token_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_user_api_token_created_by` FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `mail_log` (
@@ -254,6 +398,169 @@ CREATE TABLE IF NOT EXISTS `mail_log` (
KEY `idx_mail_log_to_email` (`to_email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `api_audit_log` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`request_id` CHAR(36) NOT NULL,
`method` VARCHAR(8) NOT NULL,
`path` VARCHAR(255) NOT NULL,
`query_json` TEXT NULL,
`status_code` SMALLINT UNSIGNED NOT NULL,
`duration_ms` INT UNSIGNED NULL,
`error_code` VARCHAR(100) NULL,
`user_id` INT UNSIGNED NULL,
`tenant_id` INT UNSIGNED NULL,
`api_token_id` INT UNSIGNED NULL,
`token_tenant_id` INT UNSIGNED NULL,
`ip` VARCHAR(45) NULL,
`user_agent` VARCHAR(255) NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_api_audit_request_id_created` (`request_id`, `created_at`),
KEY `idx_api_audit_created` (`created_at`),
KEY `idx_api_audit_status_created` (`status_code`, `created_at`),
KEY `idx_api_audit_user_created` (`user_id`, `created_at`),
KEY `idx_api_audit_tenant_created` (`tenant_id`, `created_at`),
KEY `idx_api_audit_path_created` (`path`, `created_at`),
KEY `idx_api_audit_error_created` (`error_code`, `created_at`),
KEY `idx_api_audit_token_created` (`api_token_id`, `created_at`),
CONSTRAINT `fk_api_audit_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_api_audit_tenant` FOREIGN KEY (`tenant_id`) REFERENCES `tenants` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_api_audit_token` FOREIGN KEY (`api_token_id`) REFERENCES `user_api_tokens` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `import_audit_runs` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`run_uuid` CHAR(36) NOT NULL,
`profile_key` VARCHAR(32) NOT NULL,
`status` VARCHAR(16) NOT NULL,
`source_filename` VARCHAR(255) NULL,
`mapped_targets_csv` TEXT NULL,
`rows_total` INT UNSIGNED NOT NULL DEFAULT 0,
`created_count` INT UNSIGNED NOT NULL DEFAULT 0,
`skipped_count` INT UNSIGNED NOT NULL DEFAULT 0,
`failed_count` INT UNSIGNED NOT NULL DEFAULT 0,
`error_codes_json` TEXT NULL,
`started_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`finished_at` DATETIME NULL DEFAULT NULL,
`duration_ms` INT UNSIGNED NULL,
`user_id` INT UNSIGNED NULL,
`current_tenant_id` INT UNSIGNED NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_import_audit_run_uuid` (`run_uuid`),
KEY `idx_import_audit_started` (`started_at`),
KEY `idx_import_audit_status_started` (`status`, `started_at`),
KEY `idx_import_audit_profile_started` (`profile_key`, `started_at`),
KEY `idx_import_audit_user_started` (`user_id`, `started_at`),
CONSTRAINT `fk_import_audit_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_import_audit_tenant` FOREIGN KEY (`current_tenant_id`) REFERENCES `tenants` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `user_lifecycle_audit_log` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`run_uuid` CHAR(36) NOT NULL,
`action` VARCHAR(16) NOT NULL,
`trigger_type` VARCHAR(16) NOT NULL,
`status` VARCHAR(16) NOT NULL,
`reason_code` VARCHAR(64) NULL,
`policy_deactivate_days` INT UNSIGNED NOT NULL DEFAULT 0,
`policy_delete_days` INT UNSIGNED NOT NULL DEFAULT 0,
`actor_user_id` INT UNSIGNED NULL,
`target_user_id` INT UNSIGNED NULL,
`target_user_uuid` CHAR(36) NULL,
`target_user_email` VARCHAR(255) NULL,
`snapshot_enc` LONGTEXT NULL,
`snapshot_version` SMALLINT UNSIGNED NOT NULL DEFAULT 1,
`restored_at` DATETIME NULL DEFAULT NULL,
`restored_by_user_id` INT UNSIGNED NULL,
`restored_user_id` INT UNSIGNED NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_lifecycle_created` (`created_at`),
KEY `idx_user_lifecycle_action_created` (`action`, `created_at`),
KEY `idx_user_lifecycle_status_created` (`status`, `created_at`),
KEY `idx_user_lifecycle_target_uuid_created` (`target_user_uuid`, `created_at`),
KEY `idx_user_lifecycle_run_uuid` (`run_uuid`),
KEY `idx_user_lifecycle_restored` (`restored_at`),
CONSTRAINT `fk_user_lifecycle_actor` FOREIGN KEY (`actor_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_user_lifecycle_target` FOREIGN KEY (`target_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_user_lifecycle_restored_by` FOREIGN KEY (`restored_by_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_user_lifecycle_restored_user` FOREIGN KEY (`restored_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `scheduled_jobs` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`job_key` VARCHAR(64) NOT NULL,
`label` VARCHAR(120) NOT NULL,
`description` VARCHAR(255) NULL,
`enabled` TINYINT(1) NOT NULL DEFAULT 1,
`timezone` VARCHAR(64) NOT NULL DEFAULT 'Europe/Berlin',
`schedule_type` VARCHAR(16) NOT NULL,
`schedule_interval` SMALLINT UNSIGNED NOT NULL DEFAULT 1,
`schedule_time` CHAR(5) NULL,
`schedule_weekdays_csv` VARCHAR(32) NULL,
`catchup_once` TINYINT(1) NOT NULL DEFAULT 1,
`next_run_at` DATETIME NULL DEFAULT NULL,
`last_run_started_at` DATETIME NULL DEFAULT NULL,
`last_run_finished_at` DATETIME NULL DEFAULT NULL,
`last_run_status` VARCHAR(16) NULL,
`last_error_code` VARCHAR(64) NULL,
`last_error_message` VARCHAR(255) NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_scheduled_jobs_job_key` (`job_key`),
KEY `idx_scheduled_jobs_enabled_next` (`enabled`, `next_run_at`),
KEY `idx_scheduled_jobs_status` (`last_run_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `scheduled_job_runs` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`run_uuid` CHAR(36) NOT NULL,
`job_id` BIGINT UNSIGNED NOT NULL,
`job_key` VARCHAR(64) NOT NULL,
`trigger_type` VARCHAR(16) NOT NULL,
`status` VARCHAR(16) NOT NULL,
`actor_user_id` INT UNSIGNED NULL,
`started_at` DATETIME NOT NULL,
`finished_at` DATETIME NULL DEFAULT NULL,
`duration_ms` INT UNSIGNED NULL,
`error_code` VARCHAR(64) NULL,
`error_message` VARCHAR(255) NULL,
`result_json` TEXT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_sched_runs_run_uuid` (`run_uuid`),
KEY `idx_sched_runs_job_created` (`job_id`, `created_at`),
KEY `idx_sched_runs_status_created` (`status`, `created_at`),
KEY `idx_sched_runs_trigger_created` (`trigger_type`, `created_at`),
CONSTRAINT `fk_sched_runs_job` FOREIGN KEY (`job_id`) REFERENCES `scheduled_jobs` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sched_runs_actor_user` FOREIGN KEY (`actor_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `scheduler_runtime_status` (
`id` TINYINT UNSIGNED NOT NULL,
`last_heartbeat_at` DATETIME NOT NULL,
`last_result` VARCHAR(32) NULL,
`last_error_code` VARCHAR(64) NULL,
`updated_at` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `request_rate_limits` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`scope` VARCHAR(64) NOT NULL,
`subject_hash` CHAR(64) NOT NULL,
`hits` INT UNSIGNED NOT NULL DEFAULT 0,
`window_started_at` DATETIME NOT NULL,
`blocked_until` DATETIME NULL DEFAULT NULL,
`created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`modified` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_request_rate_scope_subject` (`scope`, `subject_hash`),
KEY `idx_request_rate_scope_blocked` (`scope`, `blocked_until`),
KEY `idx_request_rate_modified` (`modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS `password_resets` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT UNSIGNED NOT NULL,
@@ -302,9 +609,11 @@ INSERT INTO `tenants` (`uuid`, `description`, `created`)
SELECT UUID(), 'Cronus', NOW()
WHERE NOT EXISTS (SELECT 1 FROM tenants WHERE description = 'Cronus');
INSERT INTO `departments` (`uuid`, `description`, `code`, `active`, `created`)
SELECT UUID(), 'IT', 'IT', 1, NOW()
WHERE NOT EXISTS (SELECT 1 FROM departments WHERE description = 'IT');
INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`)
SELECT UUID(), 'IT', t.id, 'IT', 1, NOW()
FROM tenants t
WHERE t.description = 'Cronus'
AND NOT EXISTS (SELECT 1 FROM departments WHERE description = 'IT');
INSERT INTO `roles` (`uuid`, `description`, `code`, `active`, `created`)
SELECT UUID(), 'Admin', 'ADMIN', 1, NOW()
@@ -363,15 +672,6 @@ WHERE u.email = 'demo@user.com'
SELECT 1 FROM user_departments ud WHERE ud.user_id = u.id AND ud.department_id = d.id
);
INSERT INTO `tenant_departments` (`tenant_id`, `department_id`, `created`)
SELECT t.id, d.id, NOW()
FROM tenants t
JOIN departments d ON d.description = 'IT'
WHERE t.description = 'Cronus'
AND NOT EXISTS (
SELECT 1 FROM tenant_departments td WHERE td.tenant_id = t.id AND td.department_id = d.id
);
INSERT INTO `user_roles` (`user_id`, `role_id`, `created`)
SELECT u.id, r.id, NOW()
FROM users u
@@ -399,6 +699,7 @@ VALUES
('users.view_audit', 'Can view user audit log', 1, 1),
('users.create', 'Can create users', 1, 1),
('users.update', 'Can update users', 1, 1),
('users.access_pdf', 'Can generate onboarding access PDFs', 1, 1),
('users.delete', 'Can delete users', 1, 1),
('users.self_update', 'Can update own user', 1, 1),
('users.update_assignments', 'Can update user assignments', 1, 1),
@@ -406,11 +707,13 @@ VALUES
('tenants.view', 'Can view tenants', 1, 1),
('tenants.create', 'Can create tenants', 1, 1),
('tenants.update', 'Can update tenants', 1, 1),
('tenants.sso_manage', 'Can manage tenant Microsoft SSO settings', 1, 1),
('tenants.delete', 'Can delete tenants', 1, 1),
('departments.view', 'Can view departments', 1, 1),
('departments.create', 'Can create departments', 1, 1),
('departments.update', 'Can update departments', 1, 1),
('departments.delete', 'Can delete departments', 1, 1),
('departments.import', 'Can import departments', 1, 1),
('roles.view', 'Can view roles', 1, 1),
('roles.create', 'Can create roles', 1, 1),
('roles.update', 'Can update roles', 1, 1),
@@ -421,26 +724,90 @@ VALUES
('permissions.delete', 'Can delete permissions', 1, 1),
('settings.view', 'Can view settings', 1, 1),
('settings.update', 'Can update settings', 1, 1),
('imports.view', 'Can view imports', 1, 1),
('imports.audit.view', 'Can view import audit logs', 1, 1),
('jobs.view', 'Can view scheduled jobs', 1, 1),
('jobs.manage', 'Can manage scheduled jobs', 1, 1),
('jobs.run_now', 'Can run scheduled jobs manually', 1, 1),
('user_lifecycle_audit.view', 'Can view user lifecycle audit logs', 1, 1),
('users.import', 'Can import users', 1, 1),
('users.import_assignments', 'Can assign tenant, role and department during user import', 1, 1),
('users.lifecycle_restore', 'Can restore users from lifecycle audit', 1, 1),
('custom_fields.manage', 'Can manage tenant custom field definitions', 1, 1),
('custom_fields.edit_values', 'Can edit user custom field values', 1, 1),
('api_docs.view', 'Can view API documentation', 1, 1),
('docs.view', 'Can view developer documentation', 1, 1),
('mail_log.view', 'Can view mail logs', 1, 1),
('api_audit.view', 'Can view API audit logs', 1, 1),
('stats.view', 'Can view statistics', 1, 1)
ON DUPLICATE KEY UPDATE
`description` = VALUES(`description`),
`active` = VALUES(`active`),
`is_system` = VALUES(`is_system`);
INSERT INTO `scheduled_jobs` (
`job_key`,
`label`,
`description`,
`enabled`,
`timezone`,
`schedule_type`,
`schedule_interval`,
`schedule_time`,
`schedule_weekdays_csv`,
`catchup_once`,
`next_run_at`
)
VALUES (
'user_lifecycle_run',
'User lifecycle run',
'Runs automatic user deactivate/delete policy',
1,
'Europe/Berlin',
'daily',
1,
'02:15',
NULL,
1,
NULL
)
ON DUPLICATE KEY UPDATE
`label` = VALUES(`label`),
`description` = VALUES(`description`);
INSERT INTO `scheduler_runtime_status` (
`id`,
`last_heartbeat_at`,
`last_result`,
`last_error_code`
)
VALUES (
1,
NOW(),
'ok',
NULL
)
ON DUPLICATE KEY UPDATE
`id` = `id`;
INSERT INTO `role_permissions` (`role_id`, `permission_id`, `created`)
SELECT r.id, p.id, NOW()
FROM roles r
JOIN permissions p ON p.`key` IN (
'users.view', 'users.view_meta', 'users.view_audit', 'users.create', 'users.update', 'users.delete',
'users.view', 'users.view_meta', 'users.view_audit', 'users.create', 'users.update', 'users.access_pdf', 'users.delete',
'users.self_update', 'users.update_assignments',
'address_book.view',
'tenants.view', 'tenants.create', 'tenants.update', 'tenants.delete',
'departments.view', 'departments.create', 'departments.update', 'departments.delete',
'tenants.view', 'tenants.create', 'tenants.update', 'tenants.sso_manage', 'tenants.delete',
'departments.view', 'departments.create', 'departments.update', 'departments.delete', 'departments.import',
'roles.view', 'roles.create', 'roles.update', 'roles.delete',
'permissions.view', 'permissions.create', 'permissions.update', 'permissions.delete',
'settings.view', 'settings.update',
'mail_log.view',
'imports.view', 'imports.audit.view', 'jobs.view', 'jobs.manage', 'jobs.run_now',
'user_lifecycle_audit.view', 'users.import', 'users.import_assignments',
'users.lifecycle_restore',
'custom_fields.manage', 'custom_fields.edit_values',
'api_docs.view', 'docs.view',
'mail_log.view', 'api_audit.view',
'stats.view'
)
WHERE r.description IN ('Admin', 'Administrator') OR r.id = 1