blob: 249dcfbdd08b3de1e38a0615b14f4ae55b078f4e [file] [log] [blame]
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +04001package main
2
3import (
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +04004 "bytes"
5 "encoding/json"
6 "fmt"
7 "io/ioutil"
8 "log"
9 "net/http"
10 "net/http/httputil"
11 "net/url"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040012 "os"
13
14 "github.com/giolekva/pcloud/core/installer"
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040015
16 "github.com/labstack/echo/v4"
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040017 "github.com/spf13/cobra"
18 "golang.org/x/crypto/ssh"
19)
20
21var appManagerFlags struct {
22 sshKey string
23 repoAddr string
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040024 port int
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040025}
26
27func appManagerCmd() *cobra.Command {
28 cmd := &cobra.Command{
29 Use: "appmanager",
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040030 RunE: appManagerCmdRun,
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040031 }
32 cmd.Flags().StringVar(
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040033 &appManagerFlags.sshKey,
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040034 "ssh-key",
35 "",
36 "",
37 )
38 cmd.Flags().StringVar(
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040039 &appManagerFlags.repoAddr,
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040040 "repo-addr",
41 "",
42 "",
43 )
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040044 cmd.Flags().IntVar(
45 &appManagerFlags.port,
46 "port",
47 8080,
48 "",
49 )
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040050 return cmd
51}
52
53func appManagerCmdRun(cmd *cobra.Command, args []string) error {
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040054 sshKey, err := os.ReadFile(appManagerFlags.sshKey)
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040055 if err != nil {
56 return err
57 }
58 signer, err := ssh.ParsePrivateKey(sshKey)
59 if err != nil {
60 return err
61 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040062 repo, err := cloneRepo(appManagerFlags.repoAddr, signer)
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040063 if err != nil {
64 return err
65 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040066 m, err := installer.NewAppManager(repo, signer)
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040067 if err != nil {
68 return err
69 }
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040070 r := installer.NewInMemoryAppRepository(installer.CreateAllApps())
71 s := &server{
72 port: appManagerFlags.port,
73 m: m,
74 r: r,
75 }
76 s.start()
Giorgi Lekveishvilibd6be7f2023-05-26 15:51:28 +040077 return nil
78}
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +040079
80type server struct {
81 port int
82 m *installer.AppManager
83 r installer.AppRepository
84}
85
86func (s *server) start() {
87 e := echo.New()
88 e.GET("/api/app-repo", s.handleAppRepo)
89 e.POST("/api/app/:slug/render", s.handleAppRender)
90 e.POST("/api/app/:slug/install", s.handleAppInstall)
91 e.GET("/api/app/:slug", s.handleApp)
92 webapp, err := url.Parse("http://localhost:5173")
93 if err != nil {
94 panic(err)
95 }
96 // var f ff
97 e.Any("/*", echo.WrapHandler(httputil.NewSingleHostReverseProxy(webapp)))
98 // e.Any("/*", echo.WrapHandler(&f))
99 fmt.Printf("Starting HTTP server on port: %d\n", s.port)
100 log.Fatal(e.Start(fmt.Sprintf(":%d", s.port)))
101}
102
103type app struct {
104 Name string `json:"name"`
105 Slug string `json:"slug"`
106 Schema string `json:"schema"`
107}
108
109func (s *server) handleAppRepo(c echo.Context) error {
110 all, err := s.r.GetAll()
111 if err != nil {
112 return err
113 }
114 resp := make([]app, len(all))
115 for i, a := range all {
116 resp[i] = app{a.Name, a.Name, a.Schema}
117 }
118 return c.JSON(http.StatusOK, resp)
119}
120
121func (s *server) handleApp(c echo.Context) error {
122 slug := c.Param("slug")
123 a, err := s.r.Find(slug)
124 if err != nil {
125 return err
126 }
127 return c.JSON(http.StatusOK, app{a.Name, a.Name, a.Schema})
128}
129
130type file struct {
131 Name string `json:"name"`
132 Contents string `json:"contents"`
133}
134
135type rendered struct {
136 Readme string `json:"readme"`
137 Files []file `json:"files"`
138}
139
140func (s *server) handleAppRender(c echo.Context) error {
141 slug := c.Param("slug")
142 contents, err := ioutil.ReadAll(c.Request().Body)
143 if err != nil {
144 return err
145 }
146 global, err := s.m.Config()
147 if err != nil {
148 return err
149 }
150 var values map[string]any
151 if err := json.Unmarshal(contents, &values); err != nil {
152 return err
153 }
154 all := map[string]any{
155 "Global": global.Values,
156 "Values": values,
157 }
158 a, err := s.r.Find(slug)
159 if err != nil {
160 return err
161 }
162 var readme bytes.Buffer
163 if err := a.Readme.Execute(&readme, all); err != nil {
164 return err
165 }
166 var resp rendered
167 resp.Readme = readme.String()
168 for _, tmpl := range a.Templates {
169 var f bytes.Buffer
170 if err := tmpl.Execute(&f, all); err != nil {
171 fmt.Printf("%+v\n", all)
172 fmt.Println(err.Error())
173 return err
174 } else {
175 resp.Files = append(resp.Files, file{tmpl.Name(), f.String()})
176 }
177 }
178 out, err := json.Marshal(resp)
179 if err != nil {
180 return err
181 }
182 if _, err := c.Response().Writer.Write(out); err != nil {
183 return err
184 }
185 return nil
186}
187
188func (s *server) handleAppInstall(c echo.Context) error {
189 slug := c.Param("slug")
190 contents, err := ioutil.ReadAll(c.Request().Body)
191 if err != nil {
192 return err
193 }
194 var values map[string]any
195 if err := json.Unmarshal(contents, &values); err != nil {
196 return err
197 }
198 a, err := s.r.Find(slug)
199 if err != nil {
200 return err
201 }
202 if err := s.m.Install(*a, values); err != nil {
203 return err
204 }
205 return c.String(http.StatusOK, "Installed")
206}