*/ public function loadProject(string $projectKey): array { $state = $this->loadAll(); $projects = is_array($state['projects'] ?? null) ? $state['projects'] : []; if (!isset($projects[$projectKey]) || !is_array($projects[$projectKey])) { $projects[$projectKey] = $this->defaultProjectState($projectKey); $state['projects'] = $projects; $this->saveAll($state); } return array_replace_recursive($this->defaultProjectState($projectKey), $projects[$projectKey]); } /** * @param array $projectState */ public function saveProject(string $projectKey, array $projectState): void { $state = $this->loadAll(); $projects = is_array($state['projects'] ?? null) ? $state['projects'] : []; $projects[$projectKey] = array_replace_recursive($this->defaultProjectState($projectKey), $projectState); $state['projects'] = $projects; $this->saveAll($state); } /** * @return array */ public function loadAuth(): array { $state = $this->loadAll(); $auth = is_array($state['module_auth'] ?? null) ? $state['module_auth'] : []; return array_replace_recursive([ 'required' => true, 'users' => [], 'groups' => [], ], $auth); } /** * @param array $auth */ public function saveAuth(array $auth): void { $state = $this->loadAll(); $state['module_auth'] = [ 'required' => (bool) ($auth['required'] ?? true), 'users' => array_values(array_filter(array_map('strval', (array) ($auth['users'] ?? [])))), 'groups' => array_values(array_filter(array_map('strval', (array) ($auth['groups'] ?? [])))), ]; $this->saveAll($state); } public function nextId(string $projectKey, string $bucket): int { $project = $this->loadProject($projectKey); $counters = is_array($project['_counters'] ?? null) ? $project['_counters'] : []; $next = (int) ($counters[$bucket] ?? 0) + 1; $counters[$bucket] = $next; $project['_counters'] = $counters; $this->saveProject($projectKey, $project); return $next; } /** * @return array */ private function loadAll(): array { $path = $this->path(); if (!is_file($path)) { return [ 'module_auth' => [ 'required' => true, 'users' => [], 'groups' => [], ], 'projects' => [], ]; } $raw = file_get_contents($path); if ($raw === false || trim($raw) === '') { return [ 'module_auth' => [ 'required' => true, 'users' => [], 'groups' => [], ], 'projects' => [], ]; } $data = json_decode($raw, true); return is_array($data) ? $data : [ 'module_auth' => [ 'required' => true, 'users' => [], 'groups' => [], ], 'projects' => [], ]; } /** * @param array $state */ private function saveAll(array $state): void { $directory = dirname($this->path()); if (!is_dir($directory) && !mkdir($directory, 0775, true) && !is_dir($directory)) { throw new RuntimeException('Mining-Checker Speicherverzeichnis konnte nicht erstellt werden.'); } $json = json_encode($state, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if ($json === false) { throw new RuntimeException('Mining-Checker Speicherinhalt konnte nicht serialisiert werden.'); } if (file_put_contents($this->path(), $json . PHP_EOL, LOCK_EX) === false) { throw new RuntimeException('Mining-Checker Speicherinhalt konnte nicht geschrieben werden.'); } } private function path(): string { $safeScope = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $this->userScope) ?: 'guest'; return $this->projectRoot . '/data/mining-checker/users/' . $safeScope . '.json'; } /** * @return array */ private function defaultProjectState(string $projectKey): array { return [ '_counters' => [], 'project' => [ 'project_key' => $projectKey, 'project_name' => strtoupper(str_replace('-', ' ', $projectKey)), ], 'settings' => [ 'baseline_measured_at' => gmdate('Y-m-d H:i:s'), 'baseline_coins_total' => 0, 'daily_cost_amount' => 0, 'daily_cost_currency' => 'EUR', 'report_currency' => 'EUR', 'crypto_currency' => 'DOGE', 'display_timezone' => 'Europe/Berlin', 'fx_max_age_hours' => 3, 'module_theme_mode' => 'inherit', 'module_theme_accent' => 'teal', 'preferred_currencies' => ['DOGE', 'USD', 'EUR'], ], 'measurements' => [], 'targets' => [], 'dashboards' => [], 'wallet_snapshots' => [], 'cost_plans' => [], 'payouts' => [], 'miner_offers' => [], 'purchased_miners' => [], 'fx_history' => [], ]; } }