mirror of
https://github.com/alexandrev/xslt-lab.git
synced 2026-09-20 04:23:16 +00:00
Initial version
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
# Etapa de build
|
||||||
|
FROM golang:1.21-alpine AS builder
|
||||||
|
|
||||||
|
# Instalar dependencias necesarias
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
|
# Establecer directorio de trabajo
|
||||||
|
WORKDIR /app/src
|
||||||
|
|
||||||
|
# Copiar los archivos
|
||||||
|
COPY src/ .
|
||||||
|
|
||||||
|
# Compilar el binario estático
|
||||||
|
RUN go mod init xslt-playground && \
|
||||||
|
go get github.com/gin-gonic/gin && \
|
||||||
|
go build -o server .
|
||||||
|
|
||||||
|
# Etapa final (runtime)
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# Instalar Java (para Saxon)
|
||||||
|
RUN apk add --no-cache openjdk17-jre curl
|
||||||
|
|
||||||
|
# Crear directorio de trabajo
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copiar binario y configuración
|
||||||
|
COPY --from=builder /app/src/server .
|
||||||
|
COPY src/app.config .
|
||||||
|
|
||||||
|
# Crear carpeta para Saxon y descargarlo
|
||||||
|
RUN mkdir -p /opt/saxon && \
|
||||||
|
curl -L -o /opt/saxon/saxon-he.jar https://repo1.maven.org/maven2/net/sf/saxon/Saxon-HE/11.6/Saxon-HE-11.6.jar
|
||||||
|
|
||||||
|
# Puerto por defecto (puede ser sobre
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"port": "8000",
|
||||||
|
"saxon_path": "/opt/saxon/saxon-he.jar"
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// xslt-playground-backend/main.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TransformRequest struct {
|
||||||
|
XSLT string `json:"xslt"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Parameters map[string]string `json:"parameters"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransformResponse struct {
|
||||||
|
Result string `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppConfig struct {
|
||||||
|
Port string `json:"port"`
|
||||||
|
SaxonPath string `json:"saxon_path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(filename string) (*AppConfig, error) {
|
||||||
|
data, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var config AppConfig
|
||||||
|
if err := json.Unmarshal(data, &config); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config, err := loadConfig("app.config")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to load config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
r.POST("/transform", func(c *gin.Context) {
|
||||||
|
var req TransformRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir, err := ioutil.TempDir("", "xslt")
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot create temp dir"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
xsltPath := filepath.Join(tmpDir, "transform.xsl")
|
||||||
|
inputPath := filepath.Join(tmpDir, "input.xml")
|
||||||
|
outputPath := filepath.Join(tmpDir, "result.xml")
|
||||||
|
|
||||||
|
if err := os.WriteFile(xsltPath, []byte(req.XSLT), 0644); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot write xslt"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(inputPath, []byte("<root/>"), 0644); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot write input"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdArgs := []string{
|
||||||
|
"-cp", config.SaxonPath,
|
||||||
|
"net.sf.saxon.Transform",
|
||||||
|
"-s:" + inputPath,
|
||||||
|
"-xsl:" + xsltPath,
|
||||||
|
"-o:" + outputPath,
|
||||||
|
}
|
||||||
|
for k, v := range req.Parameters {
|
||||||
|
cmdArgs = append(cmdArgs, k+"='"+v+"'")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("java", cmdArgs...)
|
||||||
|
var stderr bytes.Buffer
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
|
||||||
|
timeout := time.Second * 10
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
go func() { errChan <- cmd.Run() }()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-errChan:
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": stderr.String()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-time.After(timeout):
|
||||||
|
cmd.Process.Kill()
|
||||||
|
c.JSON(http.StatusRequestTimeout, gin.H{"error": "transformation timeout"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := os.ReadFile(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "cannot read result"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, TransformResponse{Result: string(result)})
|
||||||
|
})
|
||||||
|
|
||||||
|
r.GET("/", func(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Run(":" + config.Port)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user