mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-06-14 03:25:02 +00:00
pipeline stuff
This commit is contained in:
parent
94526de04b
commit
50bcca10e2
@ -1,73 +1,76 @@
|
|||||||
package stirling.software.SPDF;
|
package stirling.software.SPDF;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
public class SPdfApplication {
|
public class SPdfApplication {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private Environment env;
|
private Environment env;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Check if the BROWSER_OPEN environment variable is set to true
|
// Check if the BROWSER_OPEN environment variable is set to true
|
||||||
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
|
||||||
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
boolean browserOpen = browserOpenEnv != null && browserOpenEnv.equalsIgnoreCase("true");
|
||||||
|
|
||||||
if (browserOpen) {
|
if (browserOpen) {
|
||||||
try {
|
try {
|
||||||
String port = env.getProperty("local.server.port");
|
String port = env.getProperty("local.server.port");
|
||||||
if(port == null || port.length() == 0) {
|
if(port == null || port.length() == 0) {
|
||||||
port="8080";
|
port="8080";
|
||||||
}
|
}
|
||||||
String url = "http://localhost:" + port;
|
String url = "http://localhost:" + port;
|
||||||
|
|
||||||
String os = System.getProperty("os.name").toLowerCase();
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
if (os.contains("win")) {
|
if (os.contains("win")) {
|
||||||
// For Windows
|
// For Windows
|
||||||
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
rt.exec("rundll32 url.dll,FileProtocolHandler " + url);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication.run(SPdfApplication.class, args);
|
SpringApplication.run(SPdfApplication.class, args);
|
||||||
try {
|
try {
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// TODO Auto-generated catch block
|
// TODO Auto-generated catch block
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeneralUtils.createDir("customFiles/static/");
|
GeneralUtils.createDir("customFiles/static/");
|
||||||
GeneralUtils.createDir("customFiles/templates/");
|
GeneralUtils.createDir("customFiles/templates/");
|
||||||
GeneralUtils.createDir("config");
|
GeneralUtils.createDir("config");
|
||||||
System.out.println("Stirling-PDF Started.");
|
|
||||||
|
|
||||||
String port = System.getProperty("local.server.port");
|
|
||||||
if(port == null || port.length() == 0) {
|
System.out.println("Stirling-PDF Started.");
|
||||||
port="8080";
|
|
||||||
}
|
String port = System.getProperty("local.server.port");
|
||||||
String url = "http://localhost:" + port;
|
if(port == null || port.length() == 0) {
|
||||||
System.out.println("Navigate to " + url);
|
port="8080";
|
||||||
}
|
}
|
||||||
|
String url = "http://localhost:" + port;
|
||||||
|
System.out.println("Navigate to " + url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -1,60 +1,60 @@
|
|||||||
package stirling.software.SPDF.config;
|
package stirling.software.SPDF.config;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.web.servlet.LocaleResolver;
|
import org.springframework.web.servlet.LocaleResolver;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
|
||||||
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class Beans implements WebMvcConfigurer {
|
public class Beans implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
registry.addInterceptor(localeChangeInterceptor());
|
registry.addInterceptor(localeChangeInterceptor());
|
||||||
registry.addInterceptor(new CleanUrlInterceptor());
|
registry.addInterceptor(new CleanUrlInterceptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LocaleChangeInterceptor localeChangeInterceptor() {
|
public LocaleChangeInterceptor localeChangeInterceptor() {
|
||||||
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
|
LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
|
||||||
lci.setParamName("lang");
|
lci.setParamName("lang");
|
||||||
return lci;
|
return lci;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public LocaleResolver localeResolver() {
|
public LocaleResolver localeResolver() {
|
||||||
SessionLocaleResolver slr = new SessionLocaleResolver();
|
SessionLocaleResolver slr = new SessionLocaleResolver();
|
||||||
|
|
||||||
String appLocaleEnv = System.getProperty("APP_LOCALE");
|
String appLocaleEnv = System.getProperty("APP_LOCALE");
|
||||||
if (appLocaleEnv == null)
|
if (appLocaleEnv == null)
|
||||||
appLocaleEnv = System.getenv("APP_LOCALE");
|
appLocaleEnv = System.getenv("APP_LOCALE");
|
||||||
Locale defaultLocale = Locale.UK; // Fallback to UK locale if environment variable is not set
|
Locale defaultLocale = Locale.UK; // Fallback to UK locale if environment variable is not set
|
||||||
|
|
||||||
if (appLocaleEnv != null && !appLocaleEnv.isEmpty()) {
|
if (appLocaleEnv != null && !appLocaleEnv.isEmpty()) {
|
||||||
Locale tempLocale = Locale.forLanguageTag(appLocaleEnv);
|
Locale tempLocale = Locale.forLanguageTag(appLocaleEnv);
|
||||||
String tempLanguageTag = tempLocale.toLanguageTag();
|
String tempLanguageTag = tempLocale.toLanguageTag();
|
||||||
|
|
||||||
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
||||||
defaultLocale = tempLocale;
|
defaultLocale = tempLocale;
|
||||||
} else {
|
} else {
|
||||||
tempLocale = Locale.forLanguageTag(appLocaleEnv.replace("_","-"));
|
tempLocale = Locale.forLanguageTag(appLocaleEnv.replace("_","-"));
|
||||||
tempLanguageTag = tempLocale.toLanguageTag();
|
tempLanguageTag = tempLocale.toLanguageTag();
|
||||||
|
|
||||||
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
if (appLocaleEnv.equalsIgnoreCase(tempLanguageTag)) {
|
||||||
defaultLocale = tempLocale;
|
defaultLocale = tempLocale;
|
||||||
} else {
|
} else {
|
||||||
System.err.println("Invalid APP_LOCALE environment variable value. Falling back to default Locale.UK.");
|
System.err.println("Invalid APP_LOCALE environment variable value. Falling back to default Locale.UK.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
slr.setDefaultLocale(defaultLocale);
|
slr.setDefaultLocale(defaultLocale);
|
||||||
return slr;
|
return slr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,497 +1,497 @@
|
|||||||
package stirling.software.SPDF.controller.api.pipeline;
|
package stirling.software.SPDF.controller.api.pipeline;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.core.io.ByteArrayResource;
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import stirling.software.SPDF.model.PipelineConfig;
|
import stirling.software.SPDF.model.PipelineConfig;
|
||||||
import stirling.software.SPDF.model.PipelineOperation;
|
import stirling.software.SPDF.model.PipelineOperation;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
@Tag(name = "Pipeline", description = "Pipeline APIs")
|
||||||
public class Controller {
|
public class Controller {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
|
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private ObjectMapper objectMapper;
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
final String jsonFileName = "pipelineConfig.json";
|
final String jsonFileName = "pipelineConfig.json";
|
||||||
final String watchedFoldersDir = "watchedFolders/";
|
final String watchedFoldersDir = "./pipeline/watchedFolders/";
|
||||||
final String finishedFoldersDir = "finishedFolders/";
|
final String finishedFoldersDir = "./pipeline/finishedFolders/";
|
||||||
|
|
||||||
@Scheduled(fixedRate = 25000)
|
@Scheduled(fixedRate = 25000)
|
||||||
public void scanFolders() {
|
public void scanFolders() {
|
||||||
logger.info("Scanning folders...");
|
logger.info("Scanning folders...");
|
||||||
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
Path watchedFolderPath = Paths.get(watchedFoldersDir);
|
||||||
if (!Files.exists(watchedFolderPath)) {
|
if (!Files.exists(watchedFolderPath)) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(watchedFolderPath);
|
Files.createDirectories(watchedFolderPath);
|
||||||
logger.info("Created directory: {}", watchedFolderPath);
|
logger.info("Created directory: {}", watchedFolderPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error creating directory: {}", watchedFolderPath, e);
|
logger.error("Error creating directory: {}", watchedFolderPath, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try (Stream<Path> paths = Files.walk(watchedFolderPath)) {
|
try (Stream<Path> paths = Files.walk(watchedFolderPath)) {
|
||||||
paths.filter(Files::isDirectory).forEach(t -> {
|
paths.filter(Files::isDirectory).forEach(t -> {
|
||||||
try {
|
try {
|
||||||
if (!t.equals(watchedFolderPath) && !t.endsWith("processing")) {
|
if (!t.equals(watchedFolderPath) && !t.endsWith("processing")) {
|
||||||
handleDirectory(t);
|
handleDirectory(t);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error handling directory: {}", t, e);
|
logger.error("Error handling directory: {}", t, e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error walking through directory: {}", watchedFolderPath, e);
|
logger.error("Error walking through directory: {}", watchedFolderPath, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDirectory(Path dir) throws Exception {
|
private void handleDirectory(Path dir) throws Exception {
|
||||||
logger.info("Handling directory: {}", dir);
|
logger.info("Handling directory: {}", dir);
|
||||||
Path jsonFile = dir.resolve(jsonFileName);
|
Path jsonFile = dir.resolve(jsonFileName);
|
||||||
Path processingDir = dir.resolve("processing"); // Directory to move files during processing
|
Path processingDir = dir.resolve("processing"); // Directory to move files during processing
|
||||||
if (!Files.exists(processingDir)) {
|
if (!Files.exists(processingDir)) {
|
||||||
Files.createDirectory(processingDir);
|
Files.createDirectory(processingDir);
|
||||||
logger.info("Created processing directory: {}", processingDir);
|
logger.info("Created processing directory: {}", processingDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Files.exists(jsonFile)) {
|
if (Files.exists(jsonFile)) {
|
||||||
// Read JSON file
|
// Read JSON file
|
||||||
String jsonString;
|
String jsonString;
|
||||||
try {
|
try {
|
||||||
jsonString = new String(Files.readAllBytes(jsonFile));
|
jsonString = new String(Files.readAllBytes(jsonFile));
|
||||||
logger.info("Read JSON file: {}", jsonFile);
|
logger.info("Read JSON file: {}", jsonFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error reading JSON file: {}", jsonFile, e);
|
logger.error("Error reading JSON file: {}", jsonFile, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode JSON to PipelineConfig
|
// Decode JSON to PipelineConfig
|
||||||
PipelineConfig config;
|
PipelineConfig config;
|
||||||
try {
|
try {
|
||||||
config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
config = objectMapper.readValue(jsonString, PipelineConfig.class);
|
||||||
// Assuming your PipelineConfig class has getters for all necessary fields, you
|
// Assuming your PipelineConfig class has getters for all necessary fields, you
|
||||||
// can perform checks here
|
// can perform checks here
|
||||||
if (config.getOperations() == null || config.getOutputDir() == null || config.getName() == null) {
|
if (config.getOperations() == null || config.getOutputDir() == null || config.getName() == null) {
|
||||||
throw new IOException("Invalid JSON format");
|
throw new IOException("Invalid JSON format");
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error parsing PipelineConfig: {}", jsonString, e);
|
logger.error("Error parsing PipelineConfig: {}", jsonString, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each operation in the pipeline
|
// For each operation in the pipeline
|
||||||
for (PipelineOperation operation : config.getOperations()) {
|
for (PipelineOperation operation : config.getOperations()) {
|
||||||
// Collect all files based on fileInput
|
// Collect all files based on fileInput
|
||||||
File[] files;
|
File[] files;
|
||||||
String fileInput = (String) operation.getParameters().get("fileInput");
|
String fileInput = (String) operation.getParameters().get("fileInput");
|
||||||
if ("automated".equals(fileInput)) {
|
if ("automated".equals(fileInput)) {
|
||||||
// If fileInput is "automated", process all files in the directory
|
// If fileInput is "automated", process all files in the directory
|
||||||
try (Stream<Path> paths = Files.list(dir)) {
|
try (Stream<Path> paths = Files.list(dir)) {
|
||||||
files = paths
|
files = paths
|
||||||
.filter(path -> !Files.isDirectory(path)) // exclude directories
|
.filter(path -> !Files.isDirectory(path)) // exclude directories
|
||||||
.filter(path -> !path.equals(jsonFile)) // exclude jsonFile
|
.filter(path -> !path.equals(jsonFile)) // exclude jsonFile
|
||||||
.map(Path::toFile)
|
.map(Path::toFile)
|
||||||
.toArray(File[]::new);
|
.toArray(File[]::new);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If fileInput contains a path, process only this file
|
// If fileInput contains a path, process only this file
|
||||||
files = new File[] { new File(fileInput) };
|
files = new File[] { new File(fileInput) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare the files for processing
|
// Prepare the files for processing
|
||||||
List<File> filesToProcess = new ArrayList<>();
|
List<File> filesToProcess = new ArrayList<>();
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
logger.info(file.getName());
|
logger.info(file.getName());
|
||||||
logger.info("{} to {}",file.toPath(), processingDir.resolve(file.getName()));
|
logger.info("{} to {}",file.toPath(), processingDir.resolve(file.getName()));
|
||||||
Files.move(file.toPath(), processingDir.resolve(file.getName()));
|
Files.move(file.toPath(), processingDir.resolve(file.getName()));
|
||||||
filesToProcess.add(processingDir.resolve(file.getName()).toFile());
|
filesToProcess.add(processingDir.resolve(file.getName()).toFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the files
|
// Process the files
|
||||||
try {
|
try {
|
||||||
List<Resource> resources = handleFiles(filesToProcess.toArray(new File[0]), jsonString);
|
List<Resource> resources = handleFiles(filesToProcess.toArray(new File[0]), jsonString);
|
||||||
|
|
||||||
if(resources == null) {
|
if(resources == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Move resultant files and rename them as per config in JSON file
|
// Move resultant files and rename them as per config in JSON file
|
||||||
for (Resource resource : resources) {
|
for (Resource resource : resources) {
|
||||||
String resourceName = resource.getFilename();
|
String resourceName = resource.getFilename();
|
||||||
String baseName = resourceName.substring(0, resourceName.lastIndexOf("."));
|
String baseName = resourceName.substring(0, resourceName.lastIndexOf("."));
|
||||||
String extension = resourceName.substring(resourceName.lastIndexOf(".")+1);
|
String extension = resourceName.substring(resourceName.lastIndexOf(".")+1);
|
||||||
|
|
||||||
String outputFileName = config.getOutputPattern().replace("{filename}", baseName);
|
String outputFileName = config.getOutputPattern().replace("{filename}", baseName);
|
||||||
|
|
||||||
outputFileName = outputFileName.replace("{pipelineName}", config.getName());
|
outputFileName = outputFileName.replace("{pipelineName}", config.getName());
|
||||||
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||||
outputFileName = outputFileName.replace("{date}", LocalDate.now().format(dateFormatter));
|
outputFileName = outputFileName.replace("{date}", LocalDate.now().format(dateFormatter));
|
||||||
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmmss");
|
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HHmmss");
|
||||||
outputFileName = outputFileName.replace("{time}", LocalTime.now().format(timeFormatter));
|
outputFileName = outputFileName.replace("{time}", LocalTime.now().format(timeFormatter));
|
||||||
|
|
||||||
outputFileName += "." + extension;
|
outputFileName += "." + extension;
|
||||||
// {filename} {folder} {date} {tmime} {pipeline}
|
// {filename} {folder} {date} {tmime} {pipeline}
|
||||||
String outputDir = config.getOutputDir();
|
String outputDir = config.getOutputDir();
|
||||||
|
|
||||||
// Check if the environment variable 'automatedOutputFolder' is set
|
// Check if the environment variable 'automatedOutputFolder' is set
|
||||||
String outputFolder = System.getenv("automatedOutputFolder");
|
String outputFolder = System.getenv("automatedOutputFolder");
|
||||||
|
|
||||||
if (outputFolder == null || outputFolder.isEmpty()) {
|
if (outputFolder == null || outputFolder.isEmpty()) {
|
||||||
// If the environment variable is not set, use the default value
|
// If the environment variable is not set, use the default value
|
||||||
outputFolder = finishedFoldersDir;
|
outputFolder = finishedFoldersDir;
|
||||||
}
|
}
|
||||||
logger.info("outputDir 0={}", outputDir);
|
logger.info("outputDir 0={}", outputDir);
|
||||||
// Replace the placeholders in the outputDir string
|
// Replace the placeholders in the outputDir string
|
||||||
outputDir = outputDir.replace("{outputFolder}", outputFolder);
|
outputDir = outputDir.replace("{outputFolder}", outputFolder);
|
||||||
outputDir = outputDir.replace("{folderName}", dir.toString());
|
outputDir = outputDir.replace("{folderName}", dir.toString());
|
||||||
logger.info("outputDir 1={}", outputDir);
|
logger.info("outputDir 1={}", outputDir);
|
||||||
outputDir = outputDir.replace("\\watchedFolders", "");
|
outputDir = outputDir.replace("\\watchedFolders", "");
|
||||||
outputDir = outputDir.replace("//watchedFolders", "");
|
outputDir = outputDir.replace("//watchedFolders", "");
|
||||||
outputDir = outputDir.replace("\\\\watchedFolders", "");
|
outputDir = outputDir.replace("\\\\watchedFolders", "");
|
||||||
outputDir = outputDir.replace("/watchedFolders", "");
|
outputDir = outputDir.replace("/watchedFolders", "");
|
||||||
|
|
||||||
Path outputPath;
|
Path outputPath;
|
||||||
logger.info("outputDir 2={}", outputDir);
|
logger.info("outputDir 2={}", outputDir);
|
||||||
if (Paths.get(outputDir).isAbsolute()) {
|
if (Paths.get(outputDir).isAbsolute()) {
|
||||||
// If it's an absolute path, use it directly
|
// If it's an absolute path, use it directly
|
||||||
outputPath = Paths.get(outputDir);
|
outputPath = Paths.get(outputDir);
|
||||||
} else {
|
} else {
|
||||||
// If it's a relative path, make it relative to the current working directory
|
// If it's a relative path, make it relative to the current working directory
|
||||||
outputPath = Paths.get(".", outputDir);
|
outputPath = Paths.get(".", outputDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("outputPath={}", outputPath);
|
logger.info("outputPath={}", outputPath);
|
||||||
|
|
||||||
if (!Files.exists(outputPath)) {
|
if (!Files.exists(outputPath)) {
|
||||||
try {
|
try {
|
||||||
Files.createDirectories(outputPath);
|
Files.createDirectories(outputPath);
|
||||||
logger.info("Created directory: {}", outputPath);
|
logger.info("Created directory: {}", outputPath);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Error creating directory: {}", outputPath, e);
|
logger.error("Error creating directory: {}", outputPath, e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info("outputPath {}", outputPath);
|
logger.info("outputPath {}", outputPath);
|
||||||
logger.info("outputPath.resolve(outputFileName).toString() {}", outputPath.resolve(outputFileName).toString());
|
logger.info("outputPath.resolve(outputFileName).toString() {}", outputPath.resolve(outputFileName).toString());
|
||||||
File newFile = new File(outputPath.resolve(outputFileName).toString());
|
File newFile = new File(outputPath.resolve(outputFileName).toString());
|
||||||
OutputStream os = new FileOutputStream(newFile);
|
OutputStream os = new FileOutputStream(newFile);
|
||||||
os.write(((ByteArrayResource)resource).getByteArray());
|
os.write(((ByteArrayResource)resource).getByteArray());
|
||||||
os.close();
|
os.close();
|
||||||
logger.info("made {}", outputPath.resolve(outputFileName));
|
logger.info("made {}", outputPath.resolve(outputFileName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If successful, delete the original files
|
// If successful, delete the original files
|
||||||
for (File file : filesToProcess) {
|
for (File file : filesToProcess) {
|
||||||
Files.deleteIfExists(processingDir.resolve(file.getName()));
|
Files.deleteIfExists(processingDir.resolve(file.getName()));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// If an error occurs, move the original files back
|
// If an error occurs, move the original files back
|
||||||
for (File file : filesToProcess) {
|
for (File file : filesToProcess) {
|
||||||
Files.move(processingDir.resolve(file.getName()), file.toPath());
|
Files.move(processingDir.resolve(file.getName()), file.toPath());
|
||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
|
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
|
||||||
|
|
||||||
logger.info("Processing files... " + outputFiles);
|
logger.info("Processing files... " + outputFiles);
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||||
|
|
||||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||||
PrintStream logPrintStream = new PrintStream(logStream);
|
PrintStream logPrintStream = new PrintStream(logStream);
|
||||||
|
|
||||||
boolean hasErrors = false;
|
boolean hasErrors = false;
|
||||||
|
|
||||||
for (JsonNode operationNode : pipelineNode) {
|
for (JsonNode operationNode : pipelineNode) {
|
||||||
String operation = operationNode.get("operation").asText();
|
String operation = operationNode.get("operation").asText();
|
||||||
logger.info("Running operation: {}", operation);
|
logger.info("Running operation: {}", operation);
|
||||||
JsonNode parametersNode = operationNode.get("parameters");
|
JsonNode parametersNode = operationNode.get("parameters");
|
||||||
String inputFileExtension = "";
|
String inputFileExtension = "";
|
||||||
if (operationNode.has("inputFileType")) {
|
if (operationNode.has("inputFileType")) {
|
||||||
inputFileExtension = operationNode.get("inputFileType").asText();
|
inputFileExtension = operationNode.get("inputFileType").asText();
|
||||||
} else {
|
} else {
|
||||||
inputFileExtension = ".pdf";
|
inputFileExtension = ".pdf";
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Resource> newOutputFiles = new ArrayList<>();
|
List<Resource> newOutputFiles = new ArrayList<>();
|
||||||
boolean hasInputFileType = false;
|
boolean hasInputFileType = false;
|
||||||
|
|
||||||
for (Resource file : outputFiles) {
|
for (Resource file : outputFiles) {
|
||||||
if (file.getFilename().endsWith(inputFileExtension)) {
|
if (file.getFilename().endsWith(inputFileExtension)) {
|
||||||
hasInputFileType = true;
|
hasInputFileType = true;
|
||||||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||||
body.add("fileInput", file);
|
body.add("fileInput", file);
|
||||||
|
|
||||||
Iterator<Map.Entry<String, JsonNode>> parameters = parametersNode.fields();
|
Iterator<Map.Entry<String, JsonNode>> parameters = parametersNode.fields();
|
||||||
while (parameters.hasNext()) {
|
while (parameters.hasNext()) {
|
||||||
Map.Entry<String, JsonNode> parameter = parameters.next();
|
Map.Entry<String, JsonNode> parameter = parameters.next();
|
||||||
body.add(parameter.getKey(), parameter.getValue().asText());
|
body.add(parameter.getKey(), parameter.getValue().asText());
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
|
|
||||||
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(body, headers);
|
||||||
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
RestTemplate restTemplate = new RestTemplate();
|
||||||
String url = "http://localhost:8080/" + operation;
|
String url = "http://localhost:8080/" + operation;
|
||||||
|
|
||||||
ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
ResponseEntity<byte[]> response = restTemplate.exchange(url, HttpMethod.POST, entity, byte[].class);
|
||||||
|
|
||||||
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
if (!response.getStatusCode().equals(HttpStatus.OK)) {
|
||||||
logPrintStream.println("Error: " + response.getBody());
|
logPrintStream.println("Error: " + response.getBody());
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the response body is a zip file
|
// Check if the response body is a zip file
|
||||||
if (isZip(response.getBody())) {
|
if (isZip(response.getBody())) {
|
||||||
// Unzip the file and add all the files to the new output files
|
// Unzip the file and add all the files to the new output files
|
||||||
newOutputFiles.addAll(unzip(response.getBody()));
|
newOutputFiles.addAll(unzip(response.getBody()));
|
||||||
} else {
|
} else {
|
||||||
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
Resource outputResource = new ByteArrayResource(response.getBody()) {
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return file.getFilename(); // Preserving original filename
|
return file.getFilename(); // Preserving original filename
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
newOutputFiles.add(outputResource);
|
newOutputFiles.add(outputResource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasInputFileType) {
|
if (!hasInputFileType) {
|
||||||
logPrintStream.println(
|
logPrintStream.println(
|
||||||
"No files with extension " + inputFileExtension + " found for operation " + operation);
|
"No files with extension " + inputFileExtension + " found for operation " + operation);
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
outputFiles = newOutputFiles;
|
outputFiles = newOutputFiles;
|
||||||
}
|
}
|
||||||
logPrintStream.close();
|
logPrintStream.close();
|
||||||
|
|
||||||
}
|
}
|
||||||
if (hasErrors) {
|
if (hasErrors) {
|
||||||
logger.error("Errors occurred during processing. Log: {}", logStream.toString());
|
logger.error("Errors occurred during processing. Log: {}", logStream.toString());
|
||||||
}
|
}
|
||||||
return outputFiles;
|
return outputFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Resource> handleFiles(File[] files, String jsonString) throws Exception {
|
List<Resource> handleFiles(File[] files, String jsonString) throws Exception {
|
||||||
if(files == null || files.length == 0) {
|
if(files == null || files.length == 0) {
|
||||||
logger.info("No files");
|
logger.info("No files");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||||
|
|
||||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||||
|
|
||||||
boolean hasErrors = false;
|
boolean hasErrors = false;
|
||||||
List<Resource> outputFiles = new ArrayList<>();
|
List<Resource> outputFiles = new ArrayList<>();
|
||||||
|
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
Path path = Paths.get(file.getAbsolutePath());
|
Path path = Paths.get(file.getAbsolutePath());
|
||||||
System.out.println("Reading file: " + path); // debug statement
|
System.out.println("Reading file: " + path); // debug statement
|
||||||
|
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) {
|
Resource fileResource = new ByteArrayResource(Files.readAllBytes(path)) {
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return file.getName();
|
return file.getName();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
outputFiles.add(fileResource);
|
outputFiles.add(fileResource);
|
||||||
} else {
|
} else {
|
||||||
System.out.println("File not found: " + path); // debug statement
|
System.out.println("File not found: " + path); // debug statement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.info("Files successfully loaded. Starting processing...");
|
logger.info("Files successfully loaded. Starting processing...");
|
||||||
return processFiles(outputFiles, jsonString);
|
return processFiles(outputFiles, jsonString);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Resource> handleFiles(MultipartFile[] files, String jsonString) throws Exception {
|
List<Resource> handleFiles(MultipartFile[] files, String jsonString) throws Exception {
|
||||||
if(files == null || files.length == 0) {
|
if(files == null || files.length == 0) {
|
||||||
logger.info("No files");
|
logger.info("No files");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
logger.info("Handling files: {} files, with JSON string of length: {}", files.length, jsonString.length());
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
JsonNode jsonNode = mapper.readTree(jsonString);
|
JsonNode jsonNode = mapper.readTree(jsonString);
|
||||||
|
|
||||||
JsonNode pipelineNode = jsonNode.get("pipeline");
|
JsonNode pipelineNode = jsonNode.get("pipeline");
|
||||||
|
|
||||||
boolean hasErrors = false;
|
boolean hasErrors = false;
|
||||||
List<Resource> outputFiles = new ArrayList<>();
|
List<Resource> outputFiles = new ArrayList<>();
|
||||||
|
|
||||||
for (MultipartFile file : files) {
|
for (MultipartFile file : files) {
|
||||||
Resource fileResource = new ByteArrayResource(file.getBytes()) {
|
Resource fileResource = new ByteArrayResource(file.getBytes()) {
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return file.getOriginalFilename();
|
return file.getOriginalFilename();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
outputFiles.add(fileResource);
|
outputFiles.add(fileResource);
|
||||||
}
|
}
|
||||||
logger.info("Files successfully loaded. Starting processing...");
|
logger.info("Files successfully loaded. Starting processing...");
|
||||||
return processFiles(outputFiles, jsonString);
|
return processFiles(outputFiles, jsonString);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/handleData")
|
@PostMapping("/handleData")
|
||||||
public ResponseEntity<byte[]> handleData(@RequestPart("fileInput") MultipartFile[] files,
|
public ResponseEntity<byte[]> handleData(@RequestPart("fileInput") MultipartFile[] files,
|
||||||
@RequestParam("json") String jsonString) {
|
@RequestParam("json") String jsonString) {
|
||||||
logger.info("Received POST request to /handleData with {} files", files.length);
|
logger.info("Received POST request to /handleData with {} files", files.length);
|
||||||
try {
|
try {
|
||||||
List<Resource> outputFiles = handleFiles(files, jsonString);
|
List<Resource> outputFiles = handleFiles(files, jsonString);
|
||||||
|
|
||||||
if (outputFiles != null && outputFiles.size() == 1) {
|
if (outputFiles != null && outputFiles.size() == 1) {
|
||||||
// If there is only one file, return it directly
|
// If there is only one file, return it directly
|
||||||
Resource singleFile = outputFiles.get(0);
|
Resource singleFile = outputFiles.get(0);
|
||||||
InputStream is = singleFile.getInputStream();
|
InputStream is = singleFile.getInputStream();
|
||||||
byte[] bytes = new byte[(int) singleFile.contentLength()];
|
byte[] bytes = new byte[(int) singleFile.contentLength()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
is.close();
|
is.close();
|
||||||
|
|
||||||
logger.info("Returning single file response...");
|
logger.info("Returning single file response...");
|
||||||
return WebResponseUtils.bytesToWebResponse(bytes, singleFile.getFilename(),
|
return WebResponseUtils.bytesToWebResponse(bytes, singleFile.getFilename(),
|
||||||
MediaType.APPLICATION_OCTET_STREAM);
|
MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} else if (outputFiles == null) {
|
} else if (outputFiles == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a ByteArrayOutputStream to hold the zip
|
// Create a ByteArrayOutputStream to hold the zip
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
ZipOutputStream zipOut = new ZipOutputStream(baos);
|
ZipOutputStream zipOut = new ZipOutputStream(baos);
|
||||||
|
|
||||||
// Loop through each file and add it to the zip
|
// Loop through each file and add it to the zip
|
||||||
for (Resource file : outputFiles) {
|
for (Resource file : outputFiles) {
|
||||||
ZipEntry zipEntry = new ZipEntry(file.getFilename());
|
ZipEntry zipEntry = new ZipEntry(file.getFilename());
|
||||||
zipOut.putNextEntry(zipEntry);
|
zipOut.putNextEntry(zipEntry);
|
||||||
|
|
||||||
// Read the file into a byte array
|
// Read the file into a byte array
|
||||||
InputStream is = file.getInputStream();
|
InputStream is = file.getInputStream();
|
||||||
byte[] bytes = new byte[(int) file.contentLength()];
|
byte[] bytes = new byte[(int) file.contentLength()];
|
||||||
is.read(bytes);
|
is.read(bytes);
|
||||||
|
|
||||||
// Write the bytes of the file to the zip
|
// Write the bytes of the file to the zip
|
||||||
zipOut.write(bytes, 0, bytes.length);
|
zipOut.write(bytes, 0, bytes.length);
|
||||||
zipOut.closeEntry();
|
zipOut.closeEntry();
|
||||||
|
|
||||||
is.close();
|
is.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
zipOut.close();
|
zipOut.close();
|
||||||
|
|
||||||
logger.info("Returning zipped file response...");
|
logger.info("Returning zipped file response...");
|
||||||
return WebResponseUtils.boasToWebResponse(baos, "output.zip", MediaType.APPLICATION_OCTET_STREAM);
|
return WebResponseUtils.boasToWebResponse(baos, "output.zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error handling data: ", e);
|
logger.error("Error handling data: ", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isZip(byte[] data) {
|
private boolean isZip(byte[] data) {
|
||||||
if (data == null || data.length < 4) {
|
if (data == null || data.length < 4) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the first four bytes of the data against the standard zip magic number
|
// Check the first four bytes of the data against the standard zip magic number
|
||||||
return data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04;
|
return data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Resource> unzip(byte[] data) throws IOException {
|
private List<Resource> unzip(byte[] data) throws IOException {
|
||||||
logger.info("Unzipping data of length: {}", data.length);
|
logger.info("Unzipping data of length: {}", data.length);
|
||||||
List<Resource> unzippedFiles = new ArrayList<>();
|
List<Resource> unzippedFiles = new ArrayList<>();
|
||||||
|
|
||||||
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
|
||||||
ZipInputStream zis = new ZipInputStream(bais)) {
|
ZipInputStream zis = new ZipInputStream(bais)) {
|
||||||
|
|
||||||
ZipEntry entry;
|
ZipEntry entry;
|
||||||
while ((entry = zis.getNextEntry()) != null) {
|
while ((entry = zis.getNextEntry()) != null) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
while ((count = zis.read(buffer)) != -1) {
|
while ((count = zis.read(buffer)) != -1) {
|
||||||
baos.write(buffer, 0, count);
|
baos.write(buffer, 0, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String filename = entry.getName();
|
final String filename = entry.getName();
|
||||||
Resource fileResource = new ByteArrayResource(baos.toByteArray()) {
|
Resource fileResource = new ByteArrayResource(baos.toByteArray()) {
|
||||||
@Override
|
@Override
|
||||||
public String getFilename() {
|
public String getFilename() {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the unzipped file is a zip file, unzip it
|
// If the unzipped file is a zip file, unzip it
|
||||||
if (isZip(baos.toByteArray())) {
|
if (isZip(baos.toByteArray())) {
|
||||||
logger.info("File {} is a zip file. Unzipping...", filename);
|
logger.info("File {} is a zip file. Unzipping...", filename);
|
||||||
unzippedFiles.addAll(unzip(baos.toByteArray()));
|
unzippedFiles.addAll(unzip(baos.toByteArray()));
|
||||||
} else {
|
} else {
|
||||||
unzippedFiles.add(fileResource);
|
unzippedFiles.add(fileResource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Unzipping completed. {} files were unzipped.", unzippedFiles.size());
|
logger.info("Unzipping completed. {} files were unzipped.", unzippedFiles.size());
|
||||||
return unzippedFiles;
|
return unzippedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,76 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Hidden;
|
import io.swagger.v3.oas.annotations.Hidden;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@Tag(name = "General", description = "General APIs")
|
@Tag(name = "General", description = "General APIs")
|
||||||
public class GeneralWebController {
|
public class GeneralWebController {
|
||||||
@GetMapping("/pipeline")
|
|
||||||
@Hidden
|
@GetMapping("/pipeline")
|
||||||
public String pipelineForm(Model model) {
|
@Hidden
|
||||||
model.addAttribute("currentPage", "pipeline");
|
public String pipelineForm(Model model) {
|
||||||
return "pipeline";
|
model.addAttribute("currentPage", "pipeline");
|
||||||
|
|
||||||
|
List<String> pipelineConfigs = new ArrayList<>();
|
||||||
|
try (Stream<Path> paths = Files.walk(Paths.get("./pipeline/defaultWebUIConfigs/"))) {
|
||||||
|
List<Path> jsonFiles = paths
|
||||||
|
.filter(Files::isRegularFile)
|
||||||
|
.filter(p -> p.toString().endsWith(".json"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
for (Path jsonFile : jsonFiles) {
|
||||||
|
String content = Files.readString(jsonFile, StandardCharsets.UTF_8);
|
||||||
|
pipelineConfigs.add(content);
|
||||||
|
}
|
||||||
|
List<Map<String, String>> pipelineConfigsWithNames = new ArrayList<>();
|
||||||
|
for (String config : pipelineConfigs) {
|
||||||
|
Map<String, Object> jsonContent = new ObjectMapper().readValue(config, Map.class);
|
||||||
|
String name = (String) jsonContent.get("name");
|
||||||
|
Map<String, String> configWithName = new HashMap<>();
|
||||||
|
configWithName.put("json", config);
|
||||||
|
configWithName.put("name", name);
|
||||||
|
pipelineConfigsWithNames.add(configWithName);
|
||||||
|
}
|
||||||
|
model.addAttribute("pipelineConfigsWithNames", pipelineConfigsWithNames);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model.addAttribute("pipelineConfigs", pipelineConfigs);
|
||||||
|
|
||||||
|
return "pipeline";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/merge-pdfs")
|
@GetMapping("/merge-pdfs")
|
||||||
@Hidden
|
@Hidden
|
||||||
|
@ -1,432 +1,456 @@
|
|||||||
document.getElementById('validateButton').addEventListener('click', function(event) {
|
document.getElementById('validateButton').addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
validatePipeline();
|
validatePipeline();
|
||||||
});
|
});
|
||||||
function validatePipeline() {
|
function validatePipeline() {
|
||||||
let pipelineListItems = document.getElementById('pipelineList').children;
|
let pipelineListItems = document.getElementById('pipelineList').children;
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
let containsAddPassword = false;
|
let containsAddPassword = false;
|
||||||
for (let i = 0; i < pipelineListItems.length - 1; i++) {
|
for (let i = 0; i < pipelineListItems.length - 1; i++) {
|
||||||
let currentOperation = pipelineListItems[i].querySelector('.operationName').textContent;
|
let currentOperation = pipelineListItems[i].querySelector('.operationName').textContent;
|
||||||
let nextOperation = pipelineListItems[i + 1].querySelector('.operationName').textContent;
|
let nextOperation = pipelineListItems[i + 1].querySelector('.operationName').textContent;
|
||||||
if (currentOperation === '/add-password') {
|
if (currentOperation === '/add-password') {
|
||||||
containsAddPassword = true;
|
containsAddPassword = true;
|
||||||
}
|
}
|
||||||
console.log(currentOperation);
|
console.log(currentOperation);
|
||||||
console.log(apiDocs[currentOperation]);
|
console.log(apiDocs[currentOperation]);
|
||||||
let currentOperationDescription = apiDocs[currentOperation]?.post?.description || "";
|
let currentOperationDescription = apiDocs[currentOperation]?.post?.description || "";
|
||||||
let nextOperationDescription = apiDocs[nextOperation]?.post?.description || "";
|
let nextOperationDescription = apiDocs[nextOperation]?.post?.description || "";
|
||||||
|
|
||||||
console.log("currentOperationDescription", currentOperationDescription);
|
console.log("currentOperationDescription", currentOperationDescription);
|
||||||
console.log("nextOperationDescription", nextOperationDescription);
|
console.log("nextOperationDescription", nextOperationDescription);
|
||||||
|
|
||||||
let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || "";
|
let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || "";
|
||||||
let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || "";
|
let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || "";
|
||||||
|
|
||||||
console.log("Operation " + currentOperation + " Output: " + currentOperationOutput);
|
console.log("Operation " + currentOperation + " Output: " + currentOperationOutput);
|
||||||
console.log("Operation " + nextOperation + " Input: " + nextOperationInput);
|
console.log("Operation " + nextOperation + " Input: " + nextOperationInput);
|
||||||
|
|
||||||
// Splitting in case of multiple possible output/input
|
// Splitting in case of multiple possible output/input
|
||||||
let currentOperationOutputArr = currentOperationOutput.split('/');
|
let currentOperationOutputArr = currentOperationOutput.split('/');
|
||||||
let nextOperationInputArr = nextOperationInput.split('/');
|
let nextOperationInputArr = nextOperationInput.split('/');
|
||||||
|
|
||||||
if (currentOperationOutput !== 'ANY' && nextOperationInput !== 'ANY') {
|
if (currentOperationOutput !== 'ANY' && nextOperationInput !== 'ANY') {
|
||||||
let intersection = currentOperationOutputArr.filter(value => nextOperationInputArr.includes(value));
|
let intersection = currentOperationOutputArr.filter(value => nextOperationInputArr.includes(value));
|
||||||
console.log(`Intersection: ${intersection}`);
|
console.log(`Intersection: ${intersection}`);
|
||||||
|
|
||||||
if (intersection.length === 0) {
|
if (intersection.length === 0) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
||||||
alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (containsAddPassword && pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') {
|
if (containsAddPassword && pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') {
|
||||||
alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.');
|
alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
console.log('Pipeline is valid');
|
console.log('Pipeline is valid');
|
||||||
// Continue with the pipeline operation
|
// Continue with the pipeline operation
|
||||||
} else {
|
} else {
|
||||||
console.error('Pipeline is not valid');
|
console.error('Pipeline is not valid');
|
||||||
// Stop operation, maybe display an error to the user
|
// Stop operation, maybe display an error to the user
|
||||||
}
|
}
|
||||||
|
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('submitConfigBtn').addEventListener('click', function() {
|
document.getElementById('submitConfigBtn').addEventListener('click', function() {
|
||||||
|
|
||||||
if (validatePipeline() === false) {
|
if (validatePipeline() === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let selectedOperation = document.getElementById('operationsDropdown').value;
|
let selectedOperation = document.getElementById('operationsDropdown').value;
|
||||||
let parameters = operationSettings[selectedOperation] || {};
|
let parameters = operationSettings[selectedOperation] || {};
|
||||||
|
|
||||||
let pipelineConfig = {
|
let pipelineConfig = {
|
||||||
"name": "uniquePipelineName",
|
"name": "uniquePipelineName",
|
||||||
"pipeline": [{
|
"pipeline": [{
|
||||||
"operation": selectedOperation,
|
"operation": selectedOperation,
|
||||||
"parameters": parameters
|
"parameters": parameters
|
||||||
}],
|
}],
|
||||||
"_examples": {
|
"_examples": {
|
||||||
"outputDir" : "{outputFolder}/{folderName}",
|
"outputDir" : "{outputFolder}/{folderName}",
|
||||||
"outputFileName" : "{filename}-{pipelineName}-{date}-{time}"
|
"outputFileName" : "{filename}-{pipelineName}-{date}-{time}"
|
||||||
},
|
},
|
||||||
"outputDir" : "httpWebRequest",
|
"outputDir" : "httpWebRequest",
|
||||||
"outputFileName" : "{filename}"
|
"outputFileName" : "{filename}"
|
||||||
};
|
};
|
||||||
|
|
||||||
let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2);
|
let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2);
|
||||||
|
|
||||||
let formData = new FormData();
|
let formData = new FormData();
|
||||||
|
|
||||||
let fileInput = document.getElementById('fileInput');
|
let fileInput = document.getElementById('fileInput');
|
||||||
let files = fileInput.files;
|
let files = fileInput.files;
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
console.log("files[i]", files[i].name);
|
console.log("files[i]", files[i].name);
|
||||||
formData.append('fileInput', files[i], files[i].name);
|
formData.append('fileInput', files[i], files[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("pipelineConfigJson", pipelineConfigJson);
|
console.log("pipelineConfigJson", pipelineConfigJson);
|
||||||
formData.append('json', pipelineConfigJson);
|
formData.append('json', pipelineConfigJson);
|
||||||
console.log("formData", formData);
|
console.log("formData", formData);
|
||||||
|
|
||||||
fetch('/handleData', {
|
fetch('/handleData', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(response => response.blob())
|
.then(response => response.blob())
|
||||||
.then(blob => {
|
.then(blob => {
|
||||||
|
|
||||||
let url = window.URL.createObjectURL(blob);
|
let url = window.URL.createObjectURL(blob);
|
||||||
let a = document.createElement('a');
|
let a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'outputfile';
|
a.download = 'outputfile';
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
a.remove();
|
a.remove();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let apiDocs = {};
|
let apiDocs = {};
|
||||||
|
|
||||||
let operationSettings = {};
|
let operationSettings = {};
|
||||||
|
|
||||||
fetch('v3/api-docs')
|
fetch('v3/api-docs')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
||||||
apiDocs = data.paths;
|
apiDocs = data.paths;
|
||||||
let operationsDropdown = document.getElementById('operationsDropdown');
|
let operationsDropdown = document.getElementById('operationsDropdown');
|
||||||
const ignoreOperations = ["/handleData", "operationToIgnore"]; // Add the operations you want to ignore here
|
const ignoreOperations = ["/handleData", "operationToIgnore"]; // Add the operations you want to ignore here
|
||||||
|
|
||||||
operationsDropdown.innerHTML = '';
|
operationsDropdown.innerHTML = '';
|
||||||
|
|
||||||
let operationsByTag = {};
|
let operationsByTag = {};
|
||||||
|
|
||||||
// Group operations by tags
|
// Group operations by tags
|
||||||
Object.keys(data.paths).forEach(operationPath => {
|
Object.keys(data.paths).forEach(operationPath => {
|
||||||
let operation = data.paths[operationPath].post;
|
let operation = data.paths[operationPath].post;
|
||||||
if (operation && !ignoreOperations.includes(operationPath) && !operation.description.includes("Type:MISO")) {
|
if (operation && !ignoreOperations.includes(operationPath) && !operation.description.includes("Type:MISO")) {
|
||||||
let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag
|
let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag
|
||||||
if (!operationsByTag[operationTag]) {
|
if (!operationsByTag[operationTag]) {
|
||||||
operationsByTag[operationTag] = [];
|
operationsByTag[operationTag] = [];
|
||||||
}
|
}
|
||||||
operationsByTag[operationTag].push(operationPath);
|
operationsByTag[operationTag].push(operationPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Specify the order of tags
|
// Specify the order of tags
|
||||||
let tagOrder = ["General", "Security", "Convert", "Other", "Filter"];
|
let tagOrder = ["General", "Security", "Convert", "Other", "Filter"];
|
||||||
|
|
||||||
// Create dropdown options
|
// Create dropdown options
|
||||||
tagOrder.forEach(tag => {
|
tagOrder.forEach(tag => {
|
||||||
if (operationsByTag[tag]) {
|
if (operationsByTag[tag]) {
|
||||||
let group = document.createElement('optgroup');
|
let group = document.createElement('optgroup');
|
||||||
group.label = tag;
|
group.label = tag;
|
||||||
|
|
||||||
operationsByTag[tag].forEach(operationPath => {
|
operationsByTag[tag].forEach(operationPath => {
|
||||||
let option = document.createElement('option');
|
let option = document.createElement('option');
|
||||||
let operationWithoutSlash = operationPath.replace(/\//g, ''); // Remove slashes
|
let operationWithoutSlash = operationPath.replace(/\//g, ''); // Remove slashes
|
||||||
option.textContent = operationWithoutSlash;
|
option.textContent = operationWithoutSlash;
|
||||||
option.value = operationPath; // Keep the value with slashes for querying
|
option.value = operationPath; // Keep the value with slashes for querying
|
||||||
group.appendChild(option);
|
group.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
operationsDropdown.appendChild(group);
|
operationsDropdown.appendChild(group);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('addOperationBtn').addEventListener('click', function() {
|
document.getElementById('addOperationBtn').addEventListener('click', function() {
|
||||||
let selectedOperation = document.getElementById('operationsDropdown').value;
|
let selectedOperation = document.getElementById('operationsDropdown').value;
|
||||||
let pipelineList = document.getElementById('pipelineList');
|
let pipelineList = document.getElementById('pipelineList');
|
||||||
|
|
||||||
let listItem = document.createElement('li');
|
let listItem = document.createElement('li');
|
||||||
listItem.className = "list-group-item";
|
listItem.className = "list-group-item";
|
||||||
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
|
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
|
||||||
apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0);
|
apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0);
|
||||||
|
|
||||||
|
|
||||||
listItem.innerHTML = `
|
listItem.innerHTML = `
|
||||||
<div class="d-flex justify-content-between align-items-center w-100">
|
<div class="d-flex justify-content-between align-items-center w-100">
|
||||||
<div class="operationName">${selectedOperation}</div>
|
<div class="operationName">${selectedOperation}</div>
|
||||||
<div class="arrows d-flex">
|
<div class="arrows d-flex">
|
||||||
<button class="btn btn-secondary move-up btn-margin"><span>↑</span></button>
|
<button class="btn btn-secondary move-up btn-margin"><span>↑</span></button>
|
||||||
<button class="btn btn-secondary move-down btn-margin"><span>↓</span></button>
|
<button class="btn btn-secondary move-down btn-margin"><span>↓</span></button>
|
||||||
<button class="btn btn-warning pipelineSettings btn-margin" ${hasSettings ? "" : "disabled"}><span style="color: ${hasSettings ? "black" : "grey"};">⚙️</span></button>
|
<button class="btn btn-warning pipelineSettings btn-margin" ${hasSettings ? "" : "disabled"}><span style="color: ${hasSettings ? "black" : "grey"};">⚙️</span></button>
|
||||||
<button class="btn btn-danger remove"><span>X</span></button>
|
<button class="btn btn-danger remove"><span>X</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
pipelineList.appendChild(listItem);
|
pipelineList.appendChild(listItem);
|
||||||
|
|
||||||
listItem.querySelector('.move-up').addEventListener('click', function(event) {
|
listItem.querySelector('.move-up').addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (listItem.previousElementSibling) {
|
if (listItem.previousElementSibling) {
|
||||||
pipelineList.insertBefore(listItem, listItem.previousElementSibling);
|
pipelineList.insertBefore(listItem, listItem.previousElementSibling);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listItem.querySelector('.move-down').addEventListener('click', function(event) {
|
listItem.querySelector('.move-down').addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (listItem.nextElementSibling) {
|
if (listItem.nextElementSibling) {
|
||||||
pipelineList.insertBefore(listItem.nextElementSibling, listItem);
|
pipelineList.insertBefore(listItem.nextElementSibling, listItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listItem.querySelector('.remove').addEventListener('click', function(event) {
|
listItem.querySelector('.remove').addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
pipelineList.removeChild(listItem);
|
pipelineList.removeChild(listItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) {
|
listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
showpipelineSettingsModal(selectedOperation);
|
showpipelineSettingsModal(selectedOperation);
|
||||||
});
|
});
|
||||||
|
|
||||||
function showpipelineSettingsModal(operation) {
|
function showpipelineSettingsModal(operation) {
|
||||||
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
|
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
|
||||||
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
|
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
|
||||||
let operationData = apiDocs[operation].post.parameters || [];
|
let operationData = apiDocs[operation].post.parameters || [];
|
||||||
|
|
||||||
pipelineSettingsContent.innerHTML = '';
|
pipelineSettingsContent.innerHTML = '';
|
||||||
|
|
||||||
operationData.forEach(parameter => {
|
operationData.forEach(parameter => {
|
||||||
let parameterDiv = document.createElement('div');
|
let parameterDiv = document.createElement('div');
|
||||||
parameterDiv.className = "form-group";
|
parameterDiv.className = "form-group";
|
||||||
|
|
||||||
let parameterLabel = document.createElement('label');
|
let parameterLabel = document.createElement('label');
|
||||||
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
|
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
|
||||||
parameterLabel.title = parameter.description;
|
parameterLabel.title = parameter.description;
|
||||||
parameterDiv.appendChild(parameterLabel);
|
parameterDiv.appendChild(parameterLabel);
|
||||||
|
|
||||||
let parameterInput;
|
let parameterInput;
|
||||||
switch (parameter.schema.type) {
|
switch (parameter.schema.type) {
|
||||||
case 'string':
|
case 'string':
|
||||||
case 'number':
|
case 'number':
|
||||||
case 'integer':
|
case 'integer':
|
||||||
parameterInput = document.createElement('input');
|
parameterInput = document.createElement('input');
|
||||||
parameterInput.type = parameter.schema.type === 'string' ? 'text' : 'number';
|
parameterInput.type = parameter.schema.type === 'string' ? 'text' : 'number';
|
||||||
parameterInput.className = "form-control";
|
parameterInput.className = "form-control";
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
parameterInput = document.createElement('input');
|
parameterInput = document.createElement('input');
|
||||||
parameterInput.type = 'checkbox';
|
parameterInput.type = 'checkbox';
|
||||||
break;
|
break;
|
||||||
case 'array':
|
case 'array':
|
||||||
case 'object':
|
case 'object':
|
||||||
parameterInput = document.createElement('textarea');
|
parameterInput = document.createElement('textarea');
|
||||||
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`;
|
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`;
|
||||||
parameterInput.className = "form-control";
|
parameterInput.className = "form-control";
|
||||||
break;
|
break;
|
||||||
case 'enum':
|
case 'enum':
|
||||||
parameterInput = document.createElement('select');
|
parameterInput = document.createElement('select');
|
||||||
parameterInput.className = "form-control";
|
parameterInput.className = "form-control";
|
||||||
parameter.schema.enum.forEach(option => {
|
parameter.schema.enum.forEach(option => {
|
||||||
let optionElement = document.createElement('option');
|
let optionElement = document.createElement('option');
|
||||||
optionElement.value = option;
|
optionElement.value = option;
|
||||||
optionElement.text = option;
|
optionElement.text = option;
|
||||||
parameterInput.appendChild(optionElement);
|
parameterInput.appendChild(optionElement);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
parameterInput = document.createElement('input');
|
parameterInput = document.createElement('input');
|
||||||
parameterInput.type = 'text';
|
parameterInput.type = 'text';
|
||||||
parameterInput.className = "form-control";
|
parameterInput.className = "form-control";
|
||||||
}
|
}
|
||||||
parameterInput.id = parameter.name;
|
parameterInput.id = parameter.name;
|
||||||
|
|
||||||
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {
|
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {
|
||||||
let savedValue = operationSettings[operation][parameter.name];
|
let savedValue = operationSettings[operation][parameter.name];
|
||||||
|
|
||||||
switch (parameter.schema.type) {
|
switch (parameter.schema.type) {
|
||||||
case 'number':
|
case 'number':
|
||||||
case 'integer':
|
case 'integer':
|
||||||
parameterInput.value = savedValue.toString();
|
parameterInput.value = savedValue.toString();
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
parameterInput.checked = savedValue;
|
parameterInput.checked = savedValue;
|
||||||
break;
|
break;
|
||||||
case 'array':
|
case 'array':
|
||||||
case 'object':
|
case 'object':
|
||||||
parameterInput.value = JSON.stringify(savedValue);
|
parameterInput.value = JSON.stringify(savedValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
parameterInput.value = savedValue;
|
parameterInput.value = savedValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parameterDiv.appendChild(parameterInput);
|
parameterDiv.appendChild(parameterInput);
|
||||||
|
|
||||||
pipelineSettingsContent.appendChild(parameterDiv);
|
pipelineSettingsContent.appendChild(parameterDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
let saveButton = document.createElement('button');
|
let saveButton = document.createElement('button');
|
||||||
saveButton.textContent = "Save Settings";
|
saveButton.textContent = "Save Settings";
|
||||||
saveButton.className = "btn btn-primary";
|
saveButton.className = "btn btn-primary";
|
||||||
saveButton.addEventListener('click', function(event) {
|
saveButton.addEventListener('click', function(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let settings = {};
|
let settings = {};
|
||||||
operationData.forEach(parameter => {
|
operationData.forEach(parameter => {
|
||||||
let value = document.getElementById(parameter.name).value;
|
let value = document.getElementById(parameter.name).value;
|
||||||
switch (parameter.schema.type) {
|
switch (parameter.schema.type) {
|
||||||
case 'number':
|
case 'number':
|
||||||
case 'integer':
|
case 'integer':
|
||||||
settings[parameter.name] = Number(value);
|
settings[parameter.name] = Number(value);
|
||||||
break;
|
break;
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
settings[parameter.name] = document.getElementById(parameter.name).checked;
|
settings[parameter.name] = document.getElementById(parameter.name).checked;
|
||||||
break;
|
break;
|
||||||
case 'array':
|
case 'array':
|
||||||
case 'object':
|
case 'object':
|
||||||
try {
|
try {
|
||||||
settings[parameter.name] = JSON.parse(value);
|
settings[parameter.name] = JSON.parse(value);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Invalid JSON format for ${parameter.name}`);
|
console.error(`Invalid JSON format for ${parameter.name}`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
settings[parameter.name] = value;
|
settings[parameter.name] = value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
operationSettings[operation] = settings;
|
operationSettings[operation] = settings;
|
||||||
console.log(settings);
|
console.log(settings);
|
||||||
pipelineSettingsModal.style.display = "none";
|
pipelineSettingsModal.style.display = "none";
|
||||||
});
|
});
|
||||||
pipelineSettingsContent.appendChild(saveButton);
|
pipelineSettingsContent.appendChild(saveButton);
|
||||||
|
|
||||||
pipelineSettingsModal.style.display = "block";
|
pipelineSettingsModal.style.display = "block";
|
||||||
|
|
||||||
pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() {
|
pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() {
|
||||||
pipelineSettingsModal.style.display = "none";
|
pipelineSettingsModal.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onclick = function(event) {
|
window.onclick = function(event) {
|
||||||
if (event.target == pipelineSettingsModal) {
|
if (event.target == pipelineSettingsModal) {
|
||||||
pipelineSettingsModal.style.display = "none";
|
pipelineSettingsModal.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('savePipelineBtn').addEventListener('click', function() {
|
document.getElementById('savePipelineBtn').addEventListener('click', function() {
|
||||||
if (validatePipeline() === false) {
|
if (validatePipeline() === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var pipelineName = document.getElementById('pipelineName').value;
|
var pipelineName = document.getElementById('pipelineName').value;
|
||||||
let pipelineList = document.getElementById('pipelineList').children;
|
let pipelineList = document.getElementById('pipelineList').children;
|
||||||
let pipelineConfig = {
|
let pipelineConfig = {
|
||||||
"name": pipelineName,
|
"name": pipelineName,
|
||||||
"pipeline": [],
|
"pipeline": [],
|
||||||
"_examples": {
|
"_examples": {
|
||||||
"outputDir" : "{outputFolder}/{folderName}",
|
"outputDir" : "{outputFolder}/{folderName}",
|
||||||
"outputFileName" : "{filename}-{pipelineName}-{date}-{time}"
|
"outputFileName" : "{filename}-{pipelineName}-{date}-{time}"
|
||||||
},
|
},
|
||||||
"outputDir" : "httpWebRequest",
|
"outputDir" : "httpWebRequest",
|
||||||
"outputFileName" : "{filename}"
|
"outputFileName" : "{filename}"
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = 0; i < pipelineList.length; i++) {
|
for (let i = 0; i < pipelineList.length; i++) {
|
||||||
let operationName = pipelineList[i].querySelector('.operationName').textContent;
|
let operationName = pipelineList[i].querySelector('.operationName').textContent;
|
||||||
let parameters = operationSettings[operationName] || {};
|
let parameters = operationSettings[operationName] || {};
|
||||||
|
|
||||||
pipelineConfig.pipeline.push({
|
pipelineConfig.pipeline.push({
|
||||||
"operation": operationName,
|
"operation": operationName,
|
||||||
"parameters": parameters
|
"parameters": parameters
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let a = document.createElement('a');
|
let a = document.createElement('a');
|
||||||
a.href = URL.createObjectURL(new Blob([JSON.stringify(pipelineConfig, null, 2)], {
|
a.href = URL.createObjectURL(new Blob([JSON.stringify(pipelineConfig, null, 2)], {
|
||||||
type: 'application/json'
|
type: 'application/json'
|
||||||
}));
|
}));
|
||||||
a.download = 'pipelineConfig.json';
|
a.download = 'pipelineConfig.json';
|
||||||
a.style.display = 'none';
|
a.style.display = 'none';
|
||||||
|
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('uploadPipelineBtn').addEventListener('click', function() {
|
async function processPipelineConfig(configString) {
|
||||||
document.getElementById('uploadPipelineInput').click();
|
let pipelineConfig = JSON.parse(configString);
|
||||||
});
|
let pipelineList = document.getElementById('pipelineList');
|
||||||
|
|
||||||
document.getElementById('uploadPipelineInput').addEventListener('change', function(e) {
|
while (pipelineList.firstChild) {
|
||||||
let reader = new FileReader();
|
pipelineList.removeChild(pipelineList.firstChild);
|
||||||
reader.onload = function(event) {
|
}
|
||||||
let pipelineConfig = JSON.parse(event.target.result);
|
document.getElementById('pipelineName').value = pipelineConfig.name
|
||||||
let pipelineList = document.getElementById('pipelineList');
|
for (const operationConfig of pipelineConfig.pipeline) {
|
||||||
|
let operationsDropdown = document.getElementById('operationsDropdown');
|
||||||
while (pipelineList.firstChild) {
|
operationsDropdown.value = operationConfig.operation;
|
||||||
pipelineList.removeChild(pipelineList.firstChild);
|
operationSettings[operationConfig.operation] = operationConfig.parameters;
|
||||||
}
|
|
||||||
document.getElementById('pipelineName').value = pipelineConfig.name
|
// assuming addOperation is async
|
||||||
pipelineConfig.pipeline.forEach(operationConfig => {
|
await new Promise((resolve) => {
|
||||||
let operationsDropdown = document.getElementById('operationsDropdown');
|
document.getElementById('addOperationBtn').addEventListener('click', resolve, { once: true });
|
||||||
operationsDropdown.value = operationConfig.operation;
|
document.getElementById('addOperationBtn').click();
|
||||||
operationSettings[operationConfig.operation] = operationConfig.parameters;
|
});
|
||||||
document.getElementById('addOperationBtn').click();
|
|
||||||
|
let lastOperation = pipelineList.lastChild;
|
||||||
let lastOperation = pipelineList.lastChild;
|
|
||||||
|
Object.keys(operationConfig.parameters).forEach(parameterName => {
|
||||||
lastOperation.querySelector('.pipelineSettings').click();
|
let input = document.getElementById(parameterName);
|
||||||
|
if (input) {
|
||||||
Object.keys(operationConfig.parameters).forEach(parameterName => {
|
switch (input.type) {
|
||||||
let input = document.getElementById(parameterName);
|
case 'checkbox':
|
||||||
if (input) {
|
input.checked = operationConfig.parameters[parameterName];
|
||||||
switch (input.type) {
|
break;
|
||||||
case 'checkbox':
|
case 'number':
|
||||||
input.checked = operationConfig.parameters[parameterName];
|
input.value = operationConfig.parameters[parameterName].toString();
|
||||||
break;
|
break;
|
||||||
case 'number':
|
case 'file':
|
||||||
input.value = operationConfig.parameters[parameterName].toString();
|
if (parameterName !== 'fileInput') {
|
||||||
break;
|
// Create a new file input element
|
||||||
case 'text':
|
let newInput = document.createElement('input');
|
||||||
case 'textarea':
|
newInput.type = 'file';
|
||||||
default:
|
newInput.id = parameterName;
|
||||||
input.value = JSON.stringify(operationConfig.parameters[parameterName]);
|
|
||||||
}
|
// Add the new file input to the main page (change the selector according to your needs)
|
||||||
}
|
document.querySelector('#main').appendChild(newInput);
|
||||||
});
|
}
|
||||||
|
break;
|
||||||
document.querySelector('#pipelineSettingsModal .btn-primary').click();
|
case 'text':
|
||||||
});
|
case 'textarea':
|
||||||
};
|
default:
|
||||||
reader.readAsText(e.target.files[0]);
|
input.value = JSON.stringify(operationConfig.parameters[parameterName]);
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById('uploadPipelineBtn').addEventListener('click', function() {
|
||||||
|
document.getElementById('uploadPipelineInput').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('uploadPipelineInput').addEventListener('change', function(e) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function(event) {
|
||||||
|
processPipelineConfig(event.target.result);
|
||||||
|
};
|
||||||
|
reader.readAsText(e.target.files[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('pipelineSelect').addEventListener('change', function(e) {
|
||||||
|
let selectedPipelineJson = e.target.value; // assuming the selected value is the JSON string of the pipeline config
|
||||||
|
processPipelineConfig(selectedPipelineJson);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
@ -1,115 +1,102 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html th:lang="${#locale.toString()}"
|
<html th:lang="${#locale.toString()}"
|
||||||
th:lang-direction="#{language.direction}"
|
th:lang-direction="#{language.direction}"
|
||||||
xmlns:th="http://www.thymeleaf.org">
|
xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
<th:block th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block>
|
<th:block th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block>
|
||||||
<body>
|
<body>
|
||||||
<div id="page-container">
|
<div id="page-container">
|
||||||
<div id="content-wrap">
|
<div id="content-wrap">
|
||||||
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
|
||||||
<br> <br>
|
<br> <br>
|
||||||
<div class="container" id="dropContainer">
|
<div class="container" id="dropContainer">
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-6">
|
|
||||||
|
<!-- Trigger/Open The Modal -->
|
||||||
<div class="mb-3">
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#pipelineSettingsModal">
|
||||||
<button id="savePipelineBtn" class="btn btn-success">Download</button>
|
Open Pipeline Settings
|
||||||
|
</button>
|
||||||
<button id="validateButton" class="btn btn-success">Validate</button>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button id="uploadPipelineBtn" class="btn btn-primary">Upload</button>
|
<button id="uploadPipelineBtn" class="btn btn-primary">Upload Custom Pipeline</button>
|
||||||
<input type="file" id="uploadPipelineInput" accept=".json"
|
<select id="pipelineSelect">
|
||||||
style="display: none;">
|
<option value="">Select a pipeline</option>
|
||||||
</div>
|
<th:block th:each="config : ${pipelineConfigsWithNames}">
|
||||||
</div>
|
<option th:value="${config.json}" th:text="${config.name}"></option>
|
||||||
|
</th:block>
|
||||||
<div id="pipelineContainer" class="card">
|
|
||||||
|
</select>
|
||||||
<!-- Pipeline Configuration Card Header -->
|
|
||||||
<div class="card-header">
|
|
||||||
<h2 class="card-title">Pipeline Configuration</h2>
|
|
||||||
</div>
|
<input type="file" id="fileInput" multiple>
|
||||||
|
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
|
||||||
<!-- Pipeline Configuration Body -->
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="mb-3">
|
<!-- The Modal -->
|
||||||
<label for="pipelineName" class="form-label">Pipeline Name</label>
|
<div class="modal" id="pipelineSettingsModal">
|
||||||
<input type="text" id="pipelineName" class="form-control" placeholder="Enter pipeline name here">
|
<div class="modal-dialog">
|
||||||
</div>
|
<div class="modal-content">
|
||||||
<div class="mb-3">
|
|
||||||
<select id="operationsDropdown" class="form-select">
|
<!-- Modal Header -->
|
||||||
<!-- Options will be dynamically populated here -->
|
<div class="modal-header">
|
||||||
</select>
|
<h2 class="modal-title">Pipeline Configuration</h2>
|
||||||
</div>
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
<div class="mb-3">
|
</div>
|
||||||
<button id="addOperationBtn" class="btn btn-primary">Add operation</button>
|
|
||||||
</div>
|
<!-- Modal body -->
|
||||||
<h3>Pipeline:</h3>
|
<div class="modal-body">
|
||||||
<ol id="pipelineList" class="list-group">
|
<div class="mb-3">
|
||||||
<!-- Pipeline operations will be dynamically populated here -->
|
<label for="pipelineName" class="form-label">Pipeline Name</label>
|
||||||
</ol>
|
<input type="text" id="pipelineName" class="form-control" placeholder="Enter pipeline name here">
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
<input type="file" id="fileInput" multiple>
|
<select id="operationsDropdown" class="form-select">
|
||||||
|
<!-- Options will be dynamically populated here -->
|
||||||
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
</div>
|
<button id="addOperationBtn" class="btn btn-primary">Add operation</button>
|
||||||
|
</div>
|
||||||
<!-- pipelineSettings modal -->
|
<h3>Pipeline:</h3>
|
||||||
<div id="pipelineSettingsModal" class="modal">
|
<ol id="pipelineList" class="list-group">
|
||||||
<div class="modal-content">
|
<!-- Pipeline operations will be dynamically populated here -->
|
||||||
<div class="modal-body">
|
</ol>
|
||||||
<span class="close">×</span>
|
<div id="pipelineSettingsContent">
|
||||||
<h2>Operation Settings</h2>
|
<!-- pipelineSettings will be dynamically populated here -->
|
||||||
<div id="pipelineSettingsContent">
|
</div>
|
||||||
<!-- pipelineSettings will be dynamically populated here -->
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
<!-- Modal footer -->
|
||||||
</div>
|
<div class="modal-footer">
|
||||||
<script src="js/pipeline.js"></script>
|
<button id="savePipelineBtn" class="btn btn-success">Download</button>
|
||||||
</div>
|
<button id="validateButton" class="btn btn-success">Validate</button>
|
||||||
</div>
|
<div class="btn-group">
|
||||||
</div>
|
<input type="file" id="uploadPipelineInput" accept=".json" style="display: none;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
|
||||||
.modal {
|
</div>
|
||||||
display: none; /* Hidden by default */
|
</div>
|
||||||
position: fixed; /* Stay in place */
|
</div>
|
||||||
z-index: 1; /* Sit on top */
|
|
||||||
padding-top: 100px; /* Location of the box */
|
<script src="js/pipeline.js"></script>
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%; /* Full width */
|
|
||||||
height: 100%; /* Full height */
|
</div>
|
||||||
overflow: auto; /* Enable scroll if needed */
|
</div>
|
||||||
background-color: rgb(0, 0, 0); /* Fallback color */
|
</div>
|
||||||
background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
|
<style>
|
||||||
}
|
|
||||||
|
|
||||||
/* Modal Content */
|
.btn-margin {
|
||||||
.modal-content {
|
margin-right: 2px;
|
||||||
background-color: #fefefe;
|
}
|
||||||
margin: auto;
|
|
||||||
padding: 20px;
|
</style>
|
||||||
border: 1px solid #888;
|
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
||||||
width: 50%;
|
</div>
|
||||||
}
|
|
||||||
|
</body>
|
||||||
.btn-margin {
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<div th:insert="~{fragments/footer.html :: footer}"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
Loading…
x
Reference in New Issue
Block a user