blob: 4e3f01f61b7c0c7503a5d71006e9d00dd7dc474c [file] [log] [blame]
Davit Tabidze207ce082024-04-09 19:15:25 +04001package welcome
2
3import (
4 "embed"
5 "fmt"
6 "html/template"
7 "log"
8 "net/http"
Davit Tabidze96718462024-05-22 14:06:02 +04009 "sort"
Davit Tabidze207ce082024-04-09 19:15:25 +040010 "strings"
Davit Tabidze56f86a42024-04-09 19:15:25 +040011
12 "github.com/giolekva/pcloud/core/installer"
gioc23530e2024-05-01 11:06:09 +040013
giobe7b7242024-05-20 18:13:55 +040014 "github.com/Masterminds/sprig/v3"
gioc23530e2024-05-01 11:06:09 +040015 "github.com/gomarkdown/markdown"
Davit Tabidze207ce082024-04-09 19:15:25 +040016)
17
18//go:embed launcher-tmpl/launcher.html
19var indexHTML embed.FS
20
21//go:embed static/*
22var files embed.FS
23
24type AppLauncherInfo struct {
giod4affa02024-06-04 18:29:33 +040025 Id string
Davit Tabidze56f86a42024-04-09 19:15:25 +040026 Name string
27 Icon template.HTML
gioc23530e2024-05-01 11:06:09 +040028 Help []HelpDocumentRendered
Davit Tabidze56f86a42024-04-09 19:15:25 +040029 Url string
Davit Tabidze207ce082024-04-09 19:15:25 +040030}
31
gioc23530e2024-05-01 11:06:09 +040032type HelpDocumentRendered struct {
33 Title string
34 Contents template.HTML
35 Children []HelpDocumentRendered
36}
37
Davit Tabidze207ce082024-04-09 19:15:25 +040038type AppDirectory interface {
39 GetAllApps() ([]AppLauncherInfo, error)
40}
41
Davit Tabidze56f86a42024-04-09 19:15:25 +040042type AppManagerDirectory struct {
43 AppManager *installer.AppManager
44}
45
gio09a3e5b2024-04-26 14:11:06 +040046func (d *AppManagerDirectory) GetAllApps() ([]AppLauncherInfo, error) {
47 all, err := d.AppManager.FindAllInstances()
48 if err != nil {
49 return nil, err
50 }
51 ret := []AppLauncherInfo{}
52 for _, a := range all {
53 if a.URL == "" && len(a.Help) == 0 {
54 continue
55 }
56 ret = append(ret, AppLauncherInfo{
giod4affa02024-06-04 18:29:33 +040057 Id: a.Id,
gio09a3e5b2024-04-26 14:11:06 +040058 Name: a.AppId,
59 Icon: template.HTML(a.Icon),
gioc23530e2024-05-01 11:06:09 +040060 Help: toMarkdown(a.Help),
gio09a3e5b2024-04-26 14:11:06 +040061 Url: a.URL,
62 })
63 }
Davit Tabidze96718462024-05-22 14:06:02 +040064 sort.Slice(ret, func(i, j int) bool {
65 if ret[i].Name == "app-manager" {
66 return true
67 }
68 if ret[j].Name == "app-manager" {
69 return false
70 }
71 if ret[i].Name == "headscale" {
72 return ret[j].Name != "app-manager"
73 }
74 if ret[j].Name == "headscale" {
75 return false
76 }
77 return ret[i].Name < ret[j].Name
78 })
gio09a3e5b2024-04-26 14:11:06 +040079 return ret, nil
80}
81
Davit Tabidze207ce082024-04-09 19:15:25 +040082type LauncherServer struct {
83 port int
84 logoutUrl string
85 appDirectory AppDirectory
86 homeTmpl *template.Template
87}
88
89func NewLauncherServer(
90 port int,
91 logoutUrl string,
92 appDirectory AppDirectory,
93) (*LauncherServer, error) {
94 tmpl, err := indexHTML.ReadFile("launcher-tmpl/launcher.html")
95 if err != nil {
96 return nil, fmt.Errorf("failed to parse template: %v", err)
97 }
giobe7b7242024-05-20 18:13:55 +040098 t := template.New("index").Funcs(template.FuncMap(sprig.FuncMap())).Funcs(template.FuncMap{
Davit Tabidze207ce082024-04-09 19:15:25 +040099 "GetUserInitials": getUserInitials,
100 "CleanAppName": cleanAppName,
101 })
102 t, err = t.Parse(string(tmpl))
103 if err != nil {
104 return nil, fmt.Errorf("failed to parse template: %v", err)
105 }
106 return &LauncherServer{
107 port,
108 logoutUrl,
109 appDirectory,
110 t,
111 }, nil
112}
113
114func getUserInitials(username string) string {
115 if username == "" {
116 return ""
117 }
118 return strings.ToUpper(username[:1])
119}
120
121func cleanAppName(name string) string {
122 cleanName := strings.ToLower(name)
123 cleanName = strings.ReplaceAll(cleanName, " ", "-")
124 return cleanName
125}
126
Davit Tabidze56f86a42024-04-09 19:15:25 +0400127func getLoggedInUser(r *http.Request) (string, error) {
128 if user := r.Header.Get("X-User"); user != "" {
129 return user, nil
130 } else {
131 return "", fmt.Errorf("unauthenticated")
132 }
133 // return "Username", nil
134}
135
Davit Tabidze207ce082024-04-09 19:15:25 +0400136func (s *LauncherServer) Start() {
137 http.Handle("/static/", http.FileServer(http.FS(files)))
138 http.HandleFunc("/", s.homeHandler)
139 log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", s.port), nil))
140}
141
142type homeHandlerData struct {
143 LoggedInUsername string
144 AllAppsInfo []AppLauncherInfo
Davit Tabidze7304dab2024-06-10 21:34:00 +0400145 LogoutUrl string
Davit Tabidze207ce082024-04-09 19:15:25 +0400146}
147
148func (s *LauncherServer) homeHandler(w http.ResponseWriter, r *http.Request) {
Davit Tabidze56f86a42024-04-09 19:15:25 +0400149 loggedInUsername, err := getLoggedInUser(r)
150 if err != nil {
151 http.Error(w, "User Not Logged In", http.StatusUnauthorized)
152 return
153 }
Davit Tabidze207ce082024-04-09 19:15:25 +0400154 allAppsInfo, err := s.appDirectory.GetAllApps()
155 if err != nil {
Davit Tabidze56f86a42024-04-09 19:15:25 +0400156 http.Error(w, err.Error(), http.StatusInternalServerError)
Davit Tabidze207ce082024-04-09 19:15:25 +0400157 }
158 data := homeHandlerData{
159 LoggedInUsername: loggedInUsername,
160 AllAppsInfo: allAppsInfo,
Davit Tabidze7304dab2024-06-10 21:34:00 +0400161 LogoutUrl: s.logoutUrl,
Davit Tabidze207ce082024-04-09 19:15:25 +0400162 }
163 if err := s.homeTmpl.Execute(w, data); err != nil {
164 http.Error(w, err.Error(), http.StatusInternalServerError)
165 return
166 }
167}
gioc23530e2024-05-01 11:06:09 +0400168
169func toMarkdown(help []installer.HelpDocument) []HelpDocumentRendered {
170 if help == nil {
171 return nil
172 }
173 var ret []HelpDocumentRendered
174 for _, h := range help {
175 ret = append(ret, HelpDocumentRendered{
176 Title: h.Title,
177 Contents: template.HTML(markdown.ToHTML([]byte(h.Contents), nil, nil)),
178 Children: toMarkdown(h.Children),
179 })
180 }
181 return ret
182}