mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-08-06 13:48:58 +02:00
settings api
This commit is contained in:
parent
a95743286e
commit
5b4ae5590d
@ -0,0 +1,332 @@
|
||||
package stirling.software.proprietary.security.controller.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.configuration.InstallationPathConfig;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.proprietary.security.model.api.admin.SettingValueResponse;
|
||||
import stirling.software.proprietary.security.model.api.admin.UpdateSettingValueRequest;
|
||||
import stirling.software.proprietary.security.model.api.admin.UpdateSettingsRequest;
|
||||
|
||||
@Controller
|
||||
@Tag(name = "Admin Settings", description = "Admin-only Settings Management APIs")
|
||||
@RequestMapping("/api/v1/admin/settings")
|
||||
@RequiredArgsConstructor
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
@Slf4j
|
||||
public class AdminSettingsController {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
@GetMapping
|
||||
@Operation(
|
||||
summary = "Get all application settings",
|
||||
description = "Retrieve all current application settings. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Settings retrieved successfully"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required")
|
||||
})
|
||||
public ResponseEntity<ApplicationProperties> getSettings() {
|
||||
log.debug("Admin requested all application settings");
|
||||
return ResponseEntity.ok(applicationProperties);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@Operation(
|
||||
summary = "Update application settings (delta updates)",
|
||||
description =
|
||||
"Update specific application settings using dot notation keys. Only sends changed values. Changes take effect on restart. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Settings updated successfully"),
|
||||
@ApiResponse(responseCode = "400", description = "Invalid setting key or value"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required"),
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "Failed to save settings to configuration file")
|
||||
})
|
||||
public ResponseEntity<String> updateSettings(@RequestBody UpdateSettingsRequest request) {
|
||||
try {
|
||||
Map<String, Object> settings = request.getSettings();
|
||||
if (settings == null || settings.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("No settings provided to update");
|
||||
}
|
||||
|
||||
int updatedCount = 0;
|
||||
for (Map.Entry<String, Object> entry : settings.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
log.info("Admin updating setting: {} = {}", key, value);
|
||||
GeneralUtils.saveKeyToSettings(key, value);
|
||||
updatedCount++;
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated %d setting(s). Changes will take effect on application restart.",
|
||||
updatedCount));
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to save settings to file: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(
|
||||
"Failed to save settings to configuration file at: "
|
||||
+ InstallationPathConfig.getSettingsPath()
|
||||
+ ". Error: "
|
||||
+ e.getMessage());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error while updating settings: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body("Invalid setting key or value. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/section/{sectionName}")
|
||||
@Operation(
|
||||
summary = "Get specific settings section",
|
||||
description =
|
||||
"Retrieve settings for a specific section (e.g., security, system, ui). Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Section settings retrieved successfully"),
|
||||
@ApiResponse(responseCode = "400", description = "Invalid section name"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required")
|
||||
})
|
||||
public ResponseEntity<?> getSettingsSection(@PathVariable String sectionName) {
|
||||
try {
|
||||
Object sectionData = getSectionData(sectionName);
|
||||
if (sectionData == null) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(
|
||||
"Invalid section name: "
|
||||
+ sectionName
|
||||
+ ". Valid sections: security, system, ui, endpoints, metrics, mail, premium, processExecutor, autoPipeline");
|
||||
}
|
||||
log.debug("Admin requested settings section: {}", sectionName);
|
||||
return ResponseEntity.ok(sectionData);
|
||||
} catch (Exception e) {
|
||||
log.error("Error retrieving section {}: {}", sectionName, e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to retrieve section: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/section/{sectionName}")
|
||||
@Operation(
|
||||
summary = "Update specific settings section",
|
||||
description = "Update all settings within a specific section. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Section settings updated successfully"),
|
||||
@ApiResponse(responseCode = "400", description = "Invalid section name or data"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required"),
|
||||
@ApiResponse(responseCode = "500", description = "Failed to save settings")
|
||||
})
|
||||
public ResponseEntity<String> updateSettingsSection(
|
||||
@PathVariable String sectionName, @RequestBody Map<String, Object> sectionData) {
|
||||
try {
|
||||
if (sectionData == null || sectionData.isEmpty()) {
|
||||
return ResponseEntity.badRequest().body("No section data provided to update");
|
||||
}
|
||||
|
||||
if (!isValidSectionName(sectionName)) {
|
||||
return ResponseEntity.badRequest()
|
||||
.body(
|
||||
"Invalid section name: "
|
||||
+ sectionName
|
||||
+ ". Valid sections: security, system, ui, endpoints, metrics, mail, premium, processExecutor, autoPipeline");
|
||||
}
|
||||
|
||||
int updatedCount = 0;
|
||||
for (Map.Entry<String, Object> entry : sectionData.entrySet()) {
|
||||
String key = sectionName + "." + entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
|
||||
log.info("Admin updating section setting: {} = {}", key, value);
|
||||
GeneralUtils.saveKeyToSettings(key, value);
|
||||
updatedCount++;
|
||||
}
|
||||
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated %d setting(s) in section '%s'. Changes will take effect on application restart.",
|
||||
updatedCount, sectionName));
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to save section settings to file: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to save settings to configuration file: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error while updating section settings: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body("Invalid section data. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/key/{key}")
|
||||
@Operation(
|
||||
summary = "Get specific setting value",
|
||||
description =
|
||||
"Retrieve value for a specific setting key using dot notation. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "Setting value retrieved successfully"),
|
||||
@ApiResponse(responseCode = "400", description = "Invalid setting key"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required")
|
||||
})
|
||||
public ResponseEntity<?> getSettingValue(@PathVariable String key) {
|
||||
try {
|
||||
Object value = getSettingByKey(key);
|
||||
if (value == null) {
|
||||
return ResponseEntity.badRequest().body("Setting key not found: " + key);
|
||||
}
|
||||
log.debug("Admin requested setting: {}", key);
|
||||
return ResponseEntity.ok(new SettingValueResponse(key, value));
|
||||
} catch (Exception e) {
|
||||
log.error("Error retrieving setting {}: {}", key, e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to retrieve setting: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping("/key/{key}")
|
||||
@Operation(
|
||||
summary = "Update specific setting value",
|
||||
description =
|
||||
"Update value for a specific setting key using dot notation. Admin access required.")
|
||||
@ApiResponses(
|
||||
value = {
|
||||
@ApiResponse(responseCode = "200", description = "Setting updated successfully"),
|
||||
@ApiResponse(responseCode = "400", description = "Invalid setting key or value"),
|
||||
@ApiResponse(
|
||||
responseCode = "403",
|
||||
description = "Access denied - Admin role required"),
|
||||
@ApiResponse(responseCode = "500", description = "Failed to save setting")
|
||||
})
|
||||
public ResponseEntity<String> updateSettingValue(
|
||||
@PathVariable String key, @RequestBody UpdateSettingValueRequest request) {
|
||||
try {
|
||||
if (request.getValue() == null) {
|
||||
return ResponseEntity.badRequest().body("Request body must contain 'value' field");
|
||||
}
|
||||
|
||||
Object value = request.getValue();
|
||||
log.info("Admin updating single setting: {} = {}", key, value);
|
||||
GeneralUtils.saveKeyToSettings(key, value);
|
||||
|
||||
return ResponseEntity.ok(
|
||||
String.format(
|
||||
"Successfully updated setting '%s'. Changes will take effect on application restart.",
|
||||
key));
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to save setting to file: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body("Failed to save setting to configuration file: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("Unexpected error while updating setting: {}", e.getMessage(), e);
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body("Invalid setting key or value. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Object getSectionData(String sectionName) {
|
||||
return switch (sectionName.toLowerCase()) {
|
||||
case "security" -> applicationProperties.getSecurity();
|
||||
case "system" -> applicationProperties.getSystem();
|
||||
case "ui" -> applicationProperties.getUi();
|
||||
case "endpoints" -> applicationProperties.getEndpoints();
|
||||
case "metrics" -> applicationProperties.getMetrics();
|
||||
case "mail" -> applicationProperties.getMail();
|
||||
case "premium" -> applicationProperties.getPremium();
|
||||
case "processexecutor" -> applicationProperties.getProcessExecutor();
|
||||
case "autopipeline" -> applicationProperties.getAutoPipeline();
|
||||
case "legal" -> applicationProperties.getLegal();
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isValidSectionName(String sectionName) {
|
||||
return getSectionData(sectionName) != null;
|
||||
}
|
||||
|
||||
private Object getSettingByKey(String key) {
|
||||
String[] parts = key.split("\\.", 2);
|
||||
if (parts.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String sectionName = parts[0];
|
||||
String propertyPath = parts[1];
|
||||
Object section = getSectionData(sectionName);
|
||||
|
||||
if (section == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return getNestedProperty(section, propertyPath);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to get nested property {}: {}", key, e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getNestedProperty(Object obj, String propertyPath) throws Exception {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] parts = propertyPath.split("\\.", 2);
|
||||
String currentProperty = parts[0];
|
||||
|
||||
java.lang.reflect.Field field = obj.getClass().getDeclaredField(currentProperty);
|
||||
field.setAccessible(true);
|
||||
Object value = field.get(obj);
|
||||
|
||||
if (parts.length == 1) {
|
||||
return value;
|
||||
} else {
|
||||
return getNestedProperty(value, parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package stirling.software.proprietary.security.model.api.admin;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Schema(description = "Response object containing a setting key and its value")
|
||||
public class SettingValueResponse {
|
||||
|
||||
@Schema(description = "The setting key in dot notation", example = "system.enableAnalytics")
|
||||
private String key;
|
||||
|
||||
@Schema(description = "The current value of the setting", example = "true")
|
||||
private Object value;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package stirling.software.proprietary.security.model.api.admin;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "Request object for updating a single setting value")
|
||||
public class UpdateSettingValueRequest {
|
||||
|
||||
@Schema(description = "The new value for the setting", example = "true")
|
||||
private Object value;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package stirling.software.proprietary.security.model.api.admin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(
|
||||
description =
|
||||
"Request object for delta updates to application settings. Only include the settings you want to change. Uses dot notation for nested properties (e.g., 'system.enableAnalytics', 'ui.appName')")
|
||||
public class UpdateSettingsRequest {
|
||||
|
||||
@Schema(
|
||||
description =
|
||||
"Map of setting keys to their new values. Only include changed settings (delta updates). Keys use dot notation for nested properties.",
|
||||
example =
|
||||
"{\n"
|
||||
+ " \"system.enableAnalytics\": true,\n"
|
||||
+ " \"ui.appName\": \"My Custom PDF Tool\",\n"
|
||||
+ " \"security.enableLogin\": false\n"
|
||||
+ "}")
|
||||
private Map<String, Object> settings;
|
||||
}
|
Loading…
Reference in New Issue
Block a user