Canvas: build application infrastructure with drag and drop

Change-Id: I5cfd12e67794f3376c5c025af29470d52d77cf16
diff --git a/apps/canvas/server/main.go b/apps/canvas/server/main.go
new file mode 100644
index 0000000..7608049
--- /dev/null
+++ b/apps/canvas/server/main.go
@@ -0,0 +1,118 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"os"
+)
+
+type deployReq struct {
+	State  any            `json:"state"`
+	Config map[string]any `json:"config"`
+}
+
+type req struct {
+	Id            string         `json:"id"`
+	SSHPrivateKey string         `json:"sshPrivateKey"`
+	Config        map[string]any `json:"config"`
+}
+
+const publicKey = `ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPK58vMu0MwIzdZT+mqpIBkhl48p9+/YwDCZv7MgTesF`
+const privateKey = `-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACDyufLzLtDMCM3WU/pqqSAZIZePKffv2MAwmb+zIE3rBQAAAKA42oIlONqC
+JQAAAAtzc2gtZWQyNTUxOQAAACDyufLzLtDMCM3WU/pqqSAZIZePKffv2MAwmb+zIE3rBQ
+AAAEC2CdpXtaFfqA8/mqjf9uITU1mrPOI4CeWgiQFEefFW1/K58vMu0MwIzdZT+mqpIBkh
+l48p9+/YwDCZv7MgTesFAAAAGXJvb3RAY2FudmFzYnVpbGRlci1tYXN0ZXIBAgME
+-----END OPENSSH PRIVATE KEY-----`
+
+func handleSave(w http.ResponseWriter, r *http.Request) {
+	var state map[string]any
+	if err := json.NewDecoder(r.Body).Decode(&state); err != nil {
+		panic(err)
+	}
+	out, err := os.Create("/tmp/state.json")
+	if err != nil {
+		panic(err)
+	}
+	defer out.Close()
+	if err := json.NewEncoder(out).Encode(state); err != nil {
+		panic(err)
+	}
+}
+
+func handleSavedGet(w http.ResponseWriter, r *http.Request) {
+	inp, err := os.Open("/tmp/state.json")
+	if err != nil {
+		panic(err)
+	}
+	defer inp.Close()
+	io.Copy(w, inp)
+}
+
+func handleDeploy(w http.ResponseWriter, r *http.Request) {
+	var dr deployReq
+	if err := json.NewDecoder(r.Body).Decode(&dr); err != nil {
+		http.Error(w, err.Error(), http.StatusBadRequest)
+		return
+	}
+	req := req{
+		Id:            "test",
+		SSHPrivateKey: privateKey,
+		Config:        dr.Config,
+	}
+	var buf bytes.Buffer
+	if err := json.NewEncoder(&buf).Encode(req); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	resp, err := http.Post("http://appmanager.hgrz-appmanager.svc.cluster.local/api/dodo-app", "application/json", &buf)
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	var b bytes.Buffer
+	io.Copy(&b, resp.Body)
+	if resp.StatusCode != http.StatusOK {
+		http.Error(w, b.String(), http.StatusInternalServerError)
+		return
+	}
+}
+
+type Network struct {
+	Name   string `json:"name"`
+	Domain string `json:"domain"`
+}
+
+type envResp struct {
+	DeployKey string    `json:"deployKey"`
+	Networks  []Network `json:"networks"`
+}
+
+func handleEnv(w http.ResponseWriter, r *http.Request) {
+	resp := envResp{
+		DeployKey: publicKey,
+		Networks: []Network{{
+			Name:   "Public",
+			Domain: "v1.dodo.cloud",
+		}, {
+			Name:   "Private",
+			Domain: "p.v1.dodo.cloud",
+		}},
+	}
+	fmt.Println(resp)
+	json.NewEncoder(w).Encode(resp)
+}
+
+func main() {
+	http.HandleFunc("GET /saved", handleSavedGet)
+	http.HandleFunc("POST /saved", handleSave)
+	http.HandleFunc("GET /env", handleEnv)
+	http.HandleFunc("POST /deploy", handleDeploy)
+	http.Handle("/", http.FileServer(http.Dir("/home/gio/code/dist")))
+	log.Fatal(http.ListenAndServe(":3000", nil))
+}