blob: a6eb24381fa390c72bc1feafe12f5993bf13554f [file] [log] [blame]
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +04001package welcome
2
3import (
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +04004 "context"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +04005 "embed"
6 "encoding/json"
7 "fmt"
8 "html/template"
9 "io/ioutil"
10 "log"
11 "net/http"
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040012 "time"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040013
14 "github.com/Masterminds/sprig/v3"
15 "github.com/labstack/echo/v4"
16
17 "github.com/giolekva/pcloud/core/installer"
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040018 "github.com/giolekva/pcloud/core/installer/tasks"
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040019)
20
21//go:embed appmanager-tmpl
22var mgrTmpl embed.FS
23
24//go:embed appmanager-tmpl/base.html
25var baseHtmlTmpl string
26
27//go:embed appmanager-tmpl/app.html
28var appHtmlTmpl string
29
30type AppManagerServer struct {
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040031 port int
32 m *installer.AppManager
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +040033 r installer.AppRepository
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040034 reconciler tasks.Reconciler
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040035}
36
37func NewAppManagerServer(
38 port int,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040039 m *installer.AppManager,
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +040040 r installer.AppRepository,
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040041 reconciler tasks.Reconciler,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040042) *AppManagerServer {
43 return &AppManagerServer{
44 port,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040045 m,
46 r,
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +040047 reconciler,
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040048 }
49}
50
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040051func (s *AppManagerServer) Start() error {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040052 e := echo.New()
53 e.StaticFS("/static", echo.MustSubFS(staticAssets, "static"))
54 e.GET("/api/app-repo", s.handleAppRepo)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040055 e.POST("/api/app/:slug/install", s.handleAppInstall)
56 e.GET("/api/app/:slug", s.handleApp)
57 e.GET("/api/instance/:slug", s.handleInstance)
58 e.POST("/api/instance/:slug/update", s.handleAppUpdate)
59 e.POST("/api/instance/:slug/remove", s.handleAppRemove)
60 e.GET("/", s.handleIndex)
61 e.GET("/app/:slug", s.handleAppUI)
62 e.GET("/instance/:slug", s.handleInstanceUI)
63 fmt.Printf("Starting HTTP server on port: %d\n", s.port)
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +040064 return e.Start(fmt.Sprintf(":%d", s.port))
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040065}
66
67type app struct {
gio3cdee592024-04-17 10:15:56 +040068 Name string `json:"name"`
69 Icon template.HTML `json:"icon"`
70 ShortDescription string `json:"shortDescription"`
71 Slug string `json:"slug"`
72 Instances []installer.AppInstanceConfig `json:"instances,omitempty"`
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040073}
74
75func (s *AppManagerServer) handleAppRepo(c echo.Context) error {
76 all, err := s.r.GetAll()
77 if err != nil {
78 return err
79 }
80 resp := make([]app, len(all))
81 for i, a := range all {
gio44f621b2024-04-29 09:44:38 +040082 resp[i] = app{a.Name(), a.Icon(), a.Description(), a.Slug(), nil}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040083 }
84 return c.JSON(http.StatusOK, resp)
85}
86
87func (s *AppManagerServer) handleApp(c echo.Context) error {
88 slug := c.Param("slug")
89 a, err := s.r.Find(slug)
90 if err != nil {
91 return err
92 }
gio308105e2024-04-19 13:12:13 +040093 instances, err := s.m.FindAllAppInstances(slug)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040094 if err != nil {
95 return err
96 }
gio44f621b2024-04-29 09:44:38 +040097 return c.JSON(http.StatusOK, app{a.Name(), a.Icon(), a.Description(), a.Slug(), instances})
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +040098}
99
100func (s *AppManagerServer) handleInstance(c echo.Context) error {
101 slug := c.Param("slug")
102 instance, err := s.m.FindInstance(slug)
103 if err != nil {
104 return err
105 }
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400106 a, err := s.r.Find(instance.AppId)
107 if err != nil {
108 return err
109 }
gio44f621b2024-04-29 09:44:38 +0400110 return c.JSON(http.StatusOK, app{a.Name(), a.Icon(), a.Description(), a.Slug(), []installer.AppInstanceConfig{instance}})
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400111}
112
113func (s *AppManagerServer) handleAppInstall(c echo.Context) error {
114 slug := c.Param("slug")
115 contents, err := ioutil.ReadAll(c.Request().Body)
116 if err != nil {
117 return err
118 }
119 var values map[string]any
120 if err := json.Unmarshal(contents, &values); err != nil {
121 return err
122 }
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400123 log.Printf("Values: %+v\n", values)
gio3cdee592024-04-17 10:15:56 +0400124 a, err := installer.FindEnvApp(s.r, slug)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400125 if err != nil {
126 return err
127 }
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400128 log.Printf("Found application: %s\n", slug)
gio3cdee592024-04-17 10:15:56 +0400129 env, err := s.m.Config()
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400130 if err != nil {
131 return err
132 }
gio3cdee592024-04-17 10:15:56 +0400133 log.Printf("Configuration: %+v\n", env)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400134 suffixGen := installer.NewFixedLengthRandomSuffixGenerator(3)
gio3af43942024-04-16 08:13:50 +0400135 suffix, err := suffixGen.Generate()
136 if err != nil {
137 return err
138 }
gio44f621b2024-04-29 09:44:38 +0400139 instanceId := a.Slug() + suffix
gio3cdee592024-04-17 10:15:56 +0400140 appDir := fmt.Sprintf("/apps/%s", instanceId)
141 namespace := fmt.Sprintf("%s%s%s", env.NamespacePrefix, a.Namespace(), suffix)
142 if err := s.m.Install(a, instanceId, appDir, namespace, values); err != nil {
Giorgi Lekveishvili743fb432023-11-08 17:19:40 +0400143 log.Printf("%s\n", err.Error())
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400144 return err
145 }
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +0400146 ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
147 go s.reconciler.Reconcile(ctx)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400148 return c.String(http.StatusOK, "Installed")
149}
150
151func (s *AppManagerServer) handleAppUpdate(c echo.Context) error {
152 slug := c.Param("slug")
153 appConfig, err := s.m.AppConfig(slug)
154 if err != nil {
155 return err
156 }
157 contents, err := ioutil.ReadAll(c.Request().Body)
158 if err != nil {
159 return err
160 }
161 var values map[string]any
162 if err := json.Unmarshal(contents, &values); err != nil {
163 return err
164 }
gio3cdee592024-04-17 10:15:56 +0400165 a, err := installer.FindEnvApp(s.r, appConfig.AppId)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400166 if err != nil {
167 return err
168 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400169 if err := s.m.Update(a, slug, values); err != nil {
gioe72b54f2024-04-22 10:44:41 +0400170 fmt.Println(err)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400171 return err
172 }
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +0400173 ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
174 go s.reconciler.Reconcile(ctx)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400175 return c.String(http.StatusOK, "Installed")
176}
177
178func (s *AppManagerServer) handleAppRemove(c echo.Context) error {
179 slug := c.Param("slug")
180 if err := s.m.Remove(slug); err != nil {
181 return err
182 }
Giorgi Lekveishvilid2f3dca2023-12-20 09:31:30 +0400183 ctx, _ := context.WithTimeout(context.Background(), 2*time.Minute)
184 go s.reconciler.Reconcile(ctx)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400185 return c.String(http.StatusOK, "Installed")
186}
187
188func (s *AppManagerServer) handleIndex(c echo.Context) error {
189 tmpl, err := template.ParseFS(mgrTmpl, "appmanager-tmpl/base.html", "appmanager-tmpl/index.html")
190 if err != nil {
191 return err
192 }
193 all, err := s.r.GetAll()
194 if err != nil {
195 return err
196 }
197 resp := make([]app, len(all))
198 for i, a := range all {
gio44f621b2024-04-29 09:44:38 +0400199 resp[i] = app{a.Name(), a.Icon(), a.Description(), a.Slug(), nil}
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400200 }
201 return tmpl.Execute(c.Response(), resp)
202}
203
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400204type appContext struct {
gio3cdee592024-04-17 10:15:56 +0400205 App installer.EnvApp
206 Instance *installer.AppInstanceConfig
207 Instances []installer.AppInstanceConfig
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400208 AvailableNetworks []installer.Network
209}
210
211func (s *AppManagerServer) handleAppUI(c echo.Context) error {
212 baseTmpl, err := newTemplate().Parse(baseHtmlTmpl)
213 if err != nil {
214 return err
215 }
216 appTmpl, err := template.Must(baseTmpl.Clone()).Parse(appHtmlTmpl)
217 if err != nil {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400218 return err
219 }
220 global, err := s.m.Config()
221 if err != nil {
222 return err
223 }
224 slug := c.Param("slug")
gio3cdee592024-04-17 10:15:56 +0400225 a, err := installer.FindEnvApp(s.r, slug)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400226 if err != nil {
227 return err
228 }
gio308105e2024-04-19 13:12:13 +0400229 instances, err := s.m.FindAllAppInstances(slug)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400230 if err != nil {
231 return err
232 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400233 err = appTmpl.Execute(c.Response(), appContext{
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400234 App: a,
235 Instances: instances,
236 AvailableNetworks: installer.CreateNetworks(global),
237 })
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400238 return err
239}
240
241func (s *AppManagerServer) handleInstanceUI(c echo.Context) error {
242 baseTmpl, err := newTemplate().Parse(baseHtmlTmpl)
243 if err != nil {
244 return err
245 }
246 appTmpl, err := template.Must(baseTmpl.Clone()).Parse(appHtmlTmpl)
247 // tmpl, err := newTemplate().ParseFS(mgrTmpl, "appmanager-tmpl/base.html", "appmanager-tmpl/app.html")
248 if err != nil {
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400249 return err
250 }
251 global, err := s.m.Config()
252 if err != nil {
253 return err
254 }
255 slug := c.Param("slug")
256 instance, err := s.m.FindInstance(slug)
257 if err != nil {
258 return err
259 }
gio3cdee592024-04-17 10:15:56 +0400260 a, err := installer.FindEnvApp(s.r, instance.AppId)
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400261 if err != nil {
262 return err
263 }
gio44f621b2024-04-29 09:44:38 +0400264 instances, err := s.m.FindAllAppInstances(a.Slug())
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400265 if err != nil {
266 return err
267 }
Giorgi Lekveishvili08af67a2024-01-18 08:53:05 +0400268 err = appTmpl.Execute(c.Response(), appContext{
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400269 App: a,
270 Instance: &instance,
271 Instances: instances,
272 AvailableNetworks: installer.CreateNetworks(global),
273 })
Giorgi Lekveishvili4257b902023-07-07 17:08:42 +0400274 return err
275}
276
277func newTemplate() *template.Template {
278 return template.New("base").Funcs(template.FuncMap(sprig.FuncMap()))
279}