package main import ( "encoding/json" "fmt" "html/template" "io" "log" "net/http" "strings" gliffy2drawio "gliffy2drawio" ) const ( maxUploadSize = 10 << 20 // 10MB ) var uploadTpl = template.Must(template.New("upload").Parse(` Gliffy → draw.io Converter

Gliffy → draw.io Converter


API docs: Swagger UI

`)) type apiRequest struct { Data string `json:"data"` // raw Gliffy JSON } type apiResponse struct { XML string `json:"xml"` Warn string `json:"warning,omitempty"` } func main() { mux := http.NewServeMux() mux.HandleFunc("/", uploadHandler) mux.HandleFunc("/convert", uploadConvertHandler) mux.HandleFunc("/api/convert", apiConvertHandler) mux.HandleFunc("/openapi.json", openAPISpecHandler) mux.HandleFunc("/swagger", swaggerUIHandler) addr := ":8080" log.Printf("Server listening on %s", addr) log.Fatal(http.ListenAndServe(addr, mux)) } func uploadHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if err := uploadTpl.Execute(w, nil); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func uploadConvertHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } log.Printf("[upload] %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr) r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize) if err := r.ParseMultipartForm(maxUploadSize); err != nil { http.Error(w, "failed to parse form: "+err.Error(), http.StatusBadRequest) return } file, header, err := r.FormFile("file") if err != nil { http.Error(w, "missing file: "+err.Error(), http.StatusBadRequest) return } defer file.Close() data, err := io.ReadAll(file) if err != nil { http.Error(w, "failed to read file: "+err.Error(), http.StatusInternalServerError) return } xml, warn, err := convert(string(data)) if err != nil { log.Printf("[upload] conversion failed: %v", err) http.Error(w, "conversion failed: "+err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.drawio"`, safeName(header.Filename))) w.Header().Set("Content-Type", "application/xml") if warn != "" { w.Header().Set("X-Conversion-Warning", warn) } log.Printf("[upload] conversion succeeded, warn: %q, bytes out: %d", warn, len(xml)) _, _ = w.Write([]byte(xml)) } func apiConvertHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } log.Printf("[api] %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr) defer r.Body.Close() var req apiRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "invalid JSON: "+err.Error(), http.StatusBadRequest) return } xml, warn, err := convert(req.Data) if err != nil { log.Printf("[api] conversion failed: %v", err) http.Error(w, "conversion failed: "+err.Error(), http.StatusBadRequest) return } resp := apiResponse{XML: xml, Warn: warn} w.Header().Set("Content-Type", "application/json") log.Printf("[api] conversion succeeded, warn: %q, bytes out: %d", warn, len(xml)) _ = json.NewEncoder(w).Encode(resp) } func convert(gliffyJSON string) (string, string, error) { log.Printf("[convert] starting, input bytes: %d", len(gliffyJSON)) converter, err := gliffy2drawio.NewGliffyDiagramConverter(gliffyJSON) if err != nil { log.Printf("[convert] failed to initialize converter: %v", err) return "", "", err } xml, err := converter.GraphXML() if err != nil { log.Printf("[convert] failed to generate XML: %v", err) return "", "", err } warn := "" log.Printf("[convert] done, output bytes: %d", len(xml)) return xml, warn, nil } func safeName(name string) string { if name == "" { return "diagram" } dot := strings.LastIndex(name, ".") if dot > 0 { name = name[:dot] } name = strings.ReplaceAll(name, "\"", "") return name } func openAPISpecHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(openAPISpec)) } func swaggerUIHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } swaggerHTML := ` Gliffy → draw.io API
` w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write([]byte(swaggerHTML)) } var openAPISpec = ` { "openapi": "3.0.0", "info": { "title": "Gliffy to draw.io Converter", "version": "1.0.0" }, "paths": { "/api/convert": { "post": { "summary": "Convert Gliffy JSON to draw.io XML", "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { "type": "string", "description": "Raw Gliffy JSON content" } }, "required": ["data"] } } } }, "responses": { "200": { "description": "Conversion succeeded", "content": { "application/json": { "schema": { "type": "object", "properties": { "xml": { "type": "string" }, "warning": { "type": "string" } } } } } }, "400": { "description": "Conversion failed" } } } } } } `