adasd
All checks were successful
Deploy / deploy-staging (push) Successful in 41s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-06-25 23:36:23 +02:00
parent 3be4394bec
commit 44d9aa0969
20 changed files with 1063 additions and 87 deletions

View File

@@ -0,0 +1,235 @@
<?php
declare(strict_types=1);
namespace App;
use Desktop\AppRegistry;
use Desktop\WidgetRegistry;
use ModulesCore\ModuleRegistry;
use SystemAddons\GiteaDeployStatus\GiteaDeployStatusService;
final class AdminAppsService
{
public function __construct(
private readonly string $projectRoot,
) {
}
/**
* @return array<string, mixed>
*/
public function bootstrap(bool $isAdmin): array
{
require_once AppPaths::systemAddonPath($this->projectRoot, 'gitea-deploy-status') . '/GiteaDeployStatusService.php';
$moduleRegistry = new ModuleRegistry(AppPaths::customAppsRoot($this->projectRoot));
$appRegistry = new AppRegistry($this->projectRoot . '/config/apps.php', $moduleRegistry->desktopApps());
$widgetRegistry = new WidgetRegistry($this->projectRoot . '/config/widgets.php', $moduleRegistry->widgets());
$apps = $this->normalizeApps($appRegistry->all());
$widgets = $this->normalizeWidgets($widgetRegistry->all());
$giteaConfig = ConfigLoader::load($this->projectRoot, 'gitea');
$giteaService = new GiteaDeployStatusService($giteaConfig);
$currentEnvironment = ConfigLoader::currentEnvironment();
return [
'meta' => [
'environment' => $currentEnvironment,
'gitea_config_path' => $this->environmentConfigPath('gitea', $currentEnvironment),
],
'stats' => [
'apps_total' => count($apps),
'installable_apps' => count(array_filter($apps, static fn (array $app): bool => !empty($app['installable']))),
'system_tools' => count(array_filter($apps, static fn (array $app): bool => ($app['app_scope'] ?? '') === 'system_tool')),
'widgets_total' => count($widgets),
'admin_only_apps' => count(array_filter($apps, static fn (array $app): bool => !empty($app['admin_only']))),
],
'apps' => $apps,
'widgets' => $widgets,
'gitea' => [
'config' => [
'base_url' => (string) ($giteaConfig['base_url'] ?? ''),
'token' => (string) ($giteaConfig['token'] ?? ''),
'owner' => (string) ($giteaConfig['owner'] ?? ''),
'repositories' => array_values(array_map('strval', (array) ($giteaConfig['repositories'] ?? []))),
'poll_idle_seconds' => max(1, (int) ($giteaConfig['poll_idle_seconds'] ?? 5)),
'poll_running_seconds' => max(1, (int) ($giteaConfig['poll_running_seconds'] ?? 3)),
'visibility' => (string) ($giteaConfig['visibility'] ?? 'admins'),
],
'diagnostics' => $giteaService->diagnostics(),
'tray_visible_for_current_user' => $giteaService->shouldShowInTray($isAdmin),
],
];
}
/**
* @param array<string, mixed> $input
* @return array<string, mixed>
*/
public function saveGiteaConfig(array $input): array
{
$environment = ConfigLoader::currentEnvironment();
$config = [
'base_url' => $this->sanitizeBaseUrl((string) ($input['base_url'] ?? '')),
'token' => trim((string) ($input['token'] ?? '')),
'owner' => trim((string) ($input['owner'] ?? '')),
'repositories' => $this->sanitizeRepositories((string) ($input['repositories'] ?? '')),
'poll_idle_seconds' => $this->sanitizePositiveInt($input['poll_idle_seconds'] ?? 5, 5),
'poll_running_seconds' => $this->sanitizePositiveInt($input['poll_running_seconds'] ?? 3, 3),
'visibility' => $this->sanitizeVisibility((string) ($input['visibility'] ?? 'admins')),
];
$this->writeEnvironmentConfig('gitea', $environment, $config);
return $config;
}
/**
* @param array<int, array<string, mixed>> $apps
* @return array<int, array<string, mixed>>
*/
private function normalizeApps(array $apps): array
{
$normalized = array_map(static function (array $app): array {
$requiredGroups = array_values(array_map('strval', (array) ($app['required_groups'] ?? [])));
return [
'app_id' => (string) ($app['app_id'] ?? ''),
'title' => (string) ($app['title'] ?? ''),
'summary' => trim((string) ($app['summary'] ?? '')),
'entry_route' => (string) ($app['entry_route'] ?? ''),
'app_scope' => (string) ($app['app_scope'] ?? 'module'),
'module_name' => (string) ($app['module_name'] ?? ''),
'installable' => !array_key_exists('installable', $app) || (bool) $app['installable'],
'show_on_desktop' => !empty($app['show_on_desktop']),
'supports_widget' => !empty($app['supports_widget']),
'supports_tray' => !empty($app['supports_tray']),
'enabled_by_default' => !empty($app['enabled_by_default']),
'required_groups' => $requiredGroups,
'admin_only' => in_array('administrators', array_map(
static fn (string $group): string => strtolower(trim($group, '/')),
$requiredGroups
), true),
'source' => isset($app['module_id']) ? 'custom_app' : 'system',
];
}, $apps);
usort($normalized, static fn (array $left, array $right): int => [$left['title'], $left['app_id']] <=> [$right['title'], $right['app_id']]);
return $normalized;
}
/**
* @param array<int, array<string, mixed>> $widgets
* @return array<int, array<string, mixed>>
*/
private function normalizeWidgets(array $widgets): array
{
$normalized = array_map(static function (array $widget): array {
return [
'widget_id' => (string) ($widget['widget_id'] ?? ''),
'title' => (string) ($widget['title'] ?? ''),
'summary' => trim((string) ($widget['summary'] ?? '')),
'zone' => (string) ($widget['zone'] ?? 'sidebar'),
'default_enabled' => !empty($widget['default_enabled']),
'source_app_id' => (string) ($widget['source_app_id'] ?? ''),
'source_module_id' => (string) ($widget['source_module_id'] ?? ''),
'launch_app_id' => (string) ($widget['launch_app_id'] ?? ''),
'status_api' => isset($widget['status_api']) ? (string) $widget['status_api'] : '',
];
}, $widgets);
usort($normalized, static fn (array $left, array $right): int => [$left['title'], $left['widget_id']] <=> [$right['title'], $right['widget_id']]);
return $normalized;
}
private function sanitizeBaseUrl(string $value): string
{
return rtrim(trim($value), '/');
}
/**
* @return array<int, string>
*/
private function sanitizeRepositories(string $value): array
{
$entries = preg_split('/[\r\n,;]+/', $value) ?: [];
$result = [];
foreach ($entries as $entry) {
$normalized = trim($entry);
if ($normalized === '' || !preg_match('/^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/', $normalized)) {
continue;
}
$result[$normalized] = $normalized;
}
return array_values($result);
}
private function sanitizePositiveInt(mixed $value, int $fallback): int
{
$normalized = (int) $value;
return $normalized > 0 ? $normalized : $fallback;
}
private function sanitizeVisibility(string $value): string
{
$normalized = strtolower(trim($value));
return in_array($normalized, ['all', 'admins', 'disabled'], true) ? $normalized : 'admins';
}
/**
* @param array<string, mixed> $config
*/
private function writeEnvironmentConfig(string $name, string $environment, array $config): void
{
$directory = $this->projectRoot . '/config/' . $environment;
if (!is_dir($directory) && !mkdir($directory, 0775, true) && !is_dir($directory)) {
throw new \RuntimeException('Konfigurationsverzeichnis konnte nicht angelegt werden.');
}
$path = $this->environmentConfigPath($name, $environment);
$content = "<?php\n\n";
$content .= "declare(strict_types=1);\n\n";
$content .= 'return ' . $this->exportPhpValue($config) . ";\n";
if (file_put_contents($path, $content) === false) {
throw new \RuntimeException('Konfigurationsdatei konnte nicht geschrieben werden.');
}
}
private function environmentConfigPath(string $name, string $environment): string
{
return $this->projectRoot . '/config/' . $environment . '/' . $name . '.php';
}
private function exportPhpValue(mixed $value, int $depth = 0): string
{
if (is_array($value)) {
if ($value === []) {
return '[]';
}
$indent = str_repeat(' ', $depth);
$childIndent = str_repeat(' ', $depth + 1);
$lines = ['['];
foreach ($value as $key => $item) {
$prefix = is_int($key) ? '' : $this->exportPhpValue($key) . ' => ';
$lines[] = $childIndent . $prefix . $this->exportPhpValue($item, $depth + 1) . ',';
}
$lines[] = $indent . ']';
return implode("\n", $lines);
}
return var_export($value, true);
}
}

View File

@@ -2,6 +2,33 @@
declare(strict_types=1);
if (!function_exists('desktop_configure_session_storage')) {
function desktop_configure_session_storage(): void
{
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
if (ini_get('session.save_handler') !== 'files') {
return;
}
$sessionPath = dirname(__DIR__, 2) . '/data/system/sessions';
if (!is_dir($sessionPath) && !mkdir($sessionPath, 0775, true) && !is_dir($sessionPath)) {
return;
}
if (!is_writable($sessionPath)) {
return;
}
ini_set('session.save_path', $sessionPath);
}
}
desktop_configure_session_storage();
spl_autoload_register(static function (string $class): void {
$prefixes = [
'App\\' => __DIR__ . '/',

View File

@@ -37,29 +37,20 @@ final class AppIconResolver
return match ($skin) {
'windows' => [
'welcome-center' => '/assets/desktop/skin-icons/windows/welcome-center.ico',
'public-dashboard' => '/assets/desktop/skin-icons/windows/public-dashboard.ico',
'workspace-manager' => '/assets/desktop/skin-icons/windows/workspace-manager.ico',
'admin-apps' => '/assets/desktop/skin-icons/windows/admin-apps.ico',
'user-self-management' => '/assets/desktop/skin-icons/windows/admin-apps.ico',
'desktop-login' => '/assets/desktop/skin-icons/windows/desktop-login.ico',
'mining-checker' => '/assets/desktop/skin-icons/windows/admin-apps.ico',
],
'apple' => [
'welcome-center' => '/assets/desktop/skin-icons/apple/welcome-center.svg',
'public-dashboard' => '/assets/desktop/skin-icons/apple/public-dashboard.svg',
'workspace-manager' => '/assets/desktop/skin-icons/apple/workspace-manager.svg',
'admin-apps' => '/assets/desktop/skin-icons/apple/admin-apps.svg',
'user-self-management' => '/assets/desktop/skin-icons/apple/admin-apps.svg',
'desktop-login' => '/assets/desktop/skin-icons/apple/desktop-login.svg',
'mining-checker' => '/assets/desktop/skin-icons/apple/admin-apps.svg',
],
'linux' => [
'welcome-center' => '/assets/desktop/skin-icons/linux/welcome-center.svg',
'public-dashboard' => '/assets/desktop/skin-icons/linux/public-dashboard.svg',
'workspace-manager' => '/assets/desktop/skin-icons/linux/workspace-manager.svg',
'admin-apps' => '/assets/desktop/skin-icons/linux/admin-apps.svg',
'user-self-management' => '/assets/desktop/skin-icons/linux/admin-apps.svg',
'desktop-login' => '/assets/desktop/skin-icons/linux/desktop-login.svg',
'mining-checker' => '/assets/desktop/skin-icons/linux/admin-apps.svg',
],
default => [],

View File

@@ -48,11 +48,6 @@ final class DesktopState
'label' => 'Setup',
'description' => 'Oeffnet die persoenlichen Desktop-Einstellungen.',
],
[
'action_id' => 'open-public-dashboard',
'label' => 'Public Dashboard',
'description' => 'Oeffnet die oeffentliche Startansicht als Fenster.',
],
[
'action_id' => 'toggle-all-widgets',
'label' => 'Infobereich umschalten',