blob: 9fd20456ad32f941f74e04f258a7ef295c65ebac [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 Lekveishvili0ccd1482023-06-21 15:02:24 +040066 m, err := installer.NewAppManager(installer.NewRepoIO(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 {
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400104 Name string `json:"name"`
105 Slug string `json:"slug"`
106 Schema string `json:"schema"`
107 Config map[string]any `json:"config"`
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400108}
109
110func (s *server) handleAppRepo(c echo.Context) error {
111 all, err := s.r.GetAll()
112 if err != nil {
113 return err
114 }
115 resp := make([]app, len(all))
116 for i, a := range all {
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400117 config, _ := s.m.AppConfig(a.Name) // TODO(gio): handle error
118 resp[i] = app{a.Name, a.Name, a.Schema, config}
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400119 }
120 return c.JSON(http.StatusOK, resp)
121}
122
123func (s *server) handleApp(c echo.Context) error {
124 slug := c.Param("slug")
125 a, err := s.r.Find(slug)
126 if err != nil {
127 return err
128 }
Giorgi Lekveishvili03ee5852023-05-30 13:20:10 +0400129 config, _ := s.m.AppConfig(a.Name) // TODO(gio): handle error
130 return c.JSON(http.StatusOK, app{a.Name, a.Name, a.Schema, config})
Giorgi Lekveishvili7efe22f2023-05-30 13:01:53 +0400131}
132
133type file struct {
134 Name string `json:"name"`
135 Contents string `json:"contents"`
136}
137
138type rendered struct {
139 Readme string `json:"readme"`
140 Files []file `json:"files"`
141}
142
143func (s *server) handleAppRender(c echo.Context) error {
144 slug := c.Param("slug")
145 contents, err := ioutil.ReadAll(c.Request().Body)
146 if err != nil {
147 return err
148 }
149 global, err := s.m.Config()
150 if err != nil {
151 return err
152 }
153 var values map[string]any
154 if err := json.Unmarshal(contents, &values); err != nil {
155 return err
156 }
157 all := map[string]any{
158 "Global": global.Values,
159 "Values": values,
160 }
161 a, err := s.r.Find(slug)
162 if err != nil {
163 return err
164 }
165 var readme bytes.Buffer
166 if err := a.Readme.Execute(&readme, all); err != nil {
167 return err
168 }
169 var resp rendered
170 resp.Readme = readme.String()
171 for _, tmpl := range a.Templates {
172 var f bytes.Buffer
173 if err := tmpl.Execute(&f, all); err != nil {
174 fmt.Printf("%+v\n", all)
175 fmt.Println(err.Error())
176 return err
177 } else {
178 resp.Files = append(resp.Files, file{tmpl.Name(), f.String()})
179 }
180 }
181 out, err := json.Marshal(resp)
182 if err != nil {
183 return err
184 }
185 if _, err := c.Response().Writer.Write(out); err != nil {
186 return err
187 }
188 return nil
189}
190
191func (s *server) handleAppInstall(c echo.Context) error {
192 slug := c.Param("slug")
193 contents, err := ioutil.ReadAll(c.Request().Body)
194 if err != nil {
195 return err
196 }
197 var values map[string]any
198 if err := json.Unmarshal(contents, &values); err != nil {
199 return err
200 }
201 a, err := s.r.Find(slug)
202 if err != nil {
203 return err
204 }
205 if err := s.m.Install(*a, values); err != nil {
206 return err
207 }
208 return c.String(http.StatusOK, "Installed")
209}