$data['kling']['access_key'], 'secret_key' => $data['kling']['secret_key'], 'source' => 'runtime', 'updated_at' => $data['kling']['updated_at'] ?? null, 'updated_by' => $data['kling']['updated_by'] ?? null, ]; } } return [ 'access_key' => getenv('KLING_ACCESS_KEY') ?: '', 'secret_key' => getenv('KLING_SECRET_KEY') ?: '', 'source' => 'env', 'updated_at' => null, 'updated_by' => null, ]; } /** * Writes new Kling credentials atomically (tempfile + rename). */ function setKlingCredentials(string $accessKey, string $secretKey, string $updatedBy): bool { $data = []; if (file_exists(RUNTIME_CONFIG_PATH)) { $existing = json_decode(file_get_contents(RUNTIME_CONFIG_PATH), true); if (is_array($existing)) { $data = $existing; } } $data['kling'] = [ 'access_key' => $accessKey, 'secret_key' => $secretKey, 'updated_at' => gmdate('Y-m-d\TH:i:s\Z'), 'updated_by' => $updatedBy, ]; $tmpPath = RUNTIME_CONFIG_PATH . '.tmp.' . getmypid(); if (file_put_contents($tmpPath, json_encode($data, JSON_PRETTY_PRINT)) === false) { return false; } return rename($tmpPath, RUNTIME_CONFIG_PATH); } /** * Returns masked status suitable for the admin UI (never exposes raw secrets). */ function getKlingStatus(): array { $creds = getKlingCredentials(); $key = $creds['access_key']; if (strlen($key) > 8) { $masked = substr($key, 0, 4) . '…' . substr($key, -4); } elseif ($key !== '') { $masked = str_repeat('*', strlen($key)); } else { $masked = null; } return [ 'access_key_masked' => $masked, 'secret_set' => $creds['secret_key'] !== '', 'source' => $creds['source'], 'updated_at' => $creds['updated_at'], 'updated_by' => $creds['updated_by'], ]; }