isUltimate()) { return ResponseHelper::returnUnauthorized(); } $keys = $request->input('keys', []); if (!empty($keys)) { $settings = SystemSetting::whereIn('key', (array)$keys)->get(); return response()->json([ 'success' => true, 'data' => $settings ]); } $settings = SystemSetting::all()->groupBy('group'); return response()->json([ 'success' => true, 'data' => $settings ]); } /** * Update multiple settings. */ public function update(Request $request) { if (!Auth::user()->isUltimate()) { return ResponseHelper::returnUnauthorized(); } $validated = $request->validate([ 'settings' => 'required|array', ]); try { foreach ($validated['settings'] as $key => $value) { SystemSetting::setValue($key, $value); } SystemSetting::clearCache(); return response()->json([ 'success' => true, 'message' => 'System settings updated successfully' ]); } catch (Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to update settings: ' . $e->getMessage() ], 500); } } /** * Upload application logo. */ public function uploadLogo(Request $request) { if (!Auth::user()->isUltimate()) { return ResponseHelper::returnUnauthorized(); } if (!$request->hasFile('logo')) { return response()->json(['success' => false, 'message' => 'No logo file provided'], 400); } try { $file = $request->file('logo'); $filename = 'app_logo_' . time() . '.' . $file->getExtension(); // Extract palette from the uploaded file before it is moved by storage. $palette = \App\Support\PaletteExtractor::extract($file->getRealPath()); // Reusing existing file storage system $result = FilesMainController::uploadFileList( $file, 'System Logo', $filename, 'Application branding logo', [], 'system', [], 0, 'app_logo', ); if ($result && isset($result->hashkey)) { SystemSetting::setValue('app_logo', $result->hashkey); if ($palette) { SystemSetting::setValue('primary_color', $palette['primary']); SystemSetting::setValue('accent_color', $palette['accent']); SystemSetting::setValue('background_tint', $palette['tint']); } SystemSetting::clearCache(); return response()->json([ 'success' => true, 'hashkey' => $result->hashkey, 'url' => FilesMainController::generateURLforFileListHash($result->hashkey), 'palette' => $palette, 'message' => 'Logo uploaded successfully' ]); } throw new Exception('File upload failed'); } catch (Exception $e) { return response()->json([ 'success' => false, 'message' => 'Logo upload failed: ' . $e->getMessage() ], 500); } } /** * Get public settings data. */ public static function getPublicSettingsData() { $publicKeys = ['app_name', 'app_logo', 'app_description', 'app_tagline', 'primary_color', 'accent_color', 'background_tint', 'footer_text', 'disabled_pages', 'top_up_enabled', 'app_mode', 'main_organization', 'accounting_theme', 'default_org_type', 'group_types', 'bible_verse_text', 'bible_verse_reference']; $settings = []; foreach ($publicKeys as $key) { $settings[$key] = SystemSetting::getValue($key); } // Add module states $settings['module_states'] = \App\Support\ModuleHelper::getModuleStates(); // Generate URL for logo if it exists if (!empty($settings['app_logo'])) { $settings['app_logo_url'] = FilesMainController::generateURLforFileListHash($settings['app_logo']); } else { $settings['app_logo_url'] = cdn_asset('vendor/assets/icons/192x192.png'); // Fallback to PWA icon on the CDN } return $settings; } /** * Get all modules with their effective state, env/config default, and * any DB-stored override. Used by the Ultimate Console module manager. */ public function getModules() { if (!Auth::user()->isUltimate()) { return ResponseHelper::returnUnauthorized(); } return response()->json([ 'success' => true, 'data' => [ 'system_enabled' => \App\Support\ModuleHelper::isSystemEnabled(), 'modules' => \App\Support\ModuleHelper::getAllModules(), ], ]); } /** * Update the module override map. Accepts an associative array of * { module_key: bool|null }. A null value clears that key's override * so the env/config default takes effect again. */ public function updateModules(Request $request) { if (!Auth::user()->isUltimate()) { return ResponseHelper::returnUnauthorized(); } $validated = $request->validate([ 'overrides' => 'required|array', ]); $configModules = (array) \Hypervel\Support\Facades\Config::get('modules', []); $current = SystemSetting::getValue(\App\Support\ModuleHelper::OVERRIDE_KEY); if (is_string($current) && $current !== '') { $decoded = json_decode($current, true); $current = is_array($decoded) ? $decoded : []; } elseif (!is_array($current)) { $current = []; } foreach ($validated['overrides'] as $key => $value) { // Only allow keys that are actually defined as modules. if (!isset($configModules[$key]) || !is_array($configModules[$key])) { continue; } if ($value === null || $value === 'null') { unset($current[$key]); } else { $current[$key] = filter_var($value, FILTER_VALIDATE_BOOLEAN); } } try { SystemSetting::setValue( \App\Support\ModuleHelper::OVERRIDE_KEY, json_encode((object) $current), 'modules', 'json' ); SystemSetting::clearCache(); return response()->json([ 'success' => true, 'message' => 'Module states updated', 'data' => [ 'modules' => \App\Support\ModuleHelper::getAllModules(), ], ]); } catch (Exception $e) { return response()->json([ 'success' => false, 'message' => 'Failed to update modules: ' . $e->getMessage(), ], 500); } } /** * Get public settings for frontend. */ public function getPublicSettings() { return response()->json([ 'success' => true, 'data' => self::getPublicSettingsData() ]); } }