Antonio Mika
·
08 Oct 24
api.go
1package shared
2
3import (
4 "encoding/json"
5 "fmt"
6 "html/template"
7 "net/http"
8 "os"
9 "strings"
10
11 "github.com/charmbracelet/ssh"
12 "github.com/picosh/pico/db"
13 "github.com/picosh/utils"
14)
15
16func CorsHeaders(headers http.Header) {
17 headers.Add("Access-Control-Allow-Origin", "*")
18 headers.Add("Vary", "Origin")
19 headers.Add("Vary", "Access-Control-Request-Method")
20 headers.Add("Vary", "Access-Control-Request-Headers")
21 headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept")
22 headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE")
23}
24
25func UnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
26 http.Error(w, "You do not have access to this site", http.StatusUnauthorized)
27}
28
29type errPayload struct {
30 Message string `json:"message"`
31}
32
33func JSONError(w http.ResponseWriter, msg string, code int) {
34 w.Header().Set("Content-Type", "application/json")
35 w.WriteHeader(code)
36 _ = json.NewEncoder(w).Encode(errPayload{Message: msg})
37}
38
39type UserApi struct {
40 *db.User
41 Fingerprint string `json:"fingerprint"`
42}
43
44func NewUserApi(user *db.User, pubkey ssh.PublicKey) *UserApi {
45 return &UserApi{
46 User: user,
47 Fingerprint: utils.KeyForSha256(pubkey),
48 }
49}
50
51func CheckHandler(w http.ResponseWriter, r *http.Request) {
52 dbpool := GetDB(r)
53 cfg := GetCfg(r)
54
55 if cfg.IsCustomdomains() {
56 hostDomain := r.URL.Query().Get("domain")
57 appDomain := strings.Split(cfg.Domain, ":")[0]
58
59 if !strings.Contains(hostDomain, appDomain) {
60 subdomain := GetCustomDomain(hostDomain, cfg.Space)
61 if subdomain != "" {
62 u, err := dbpool.FindUserForName(subdomain)
63 if u != nil && err == nil {
64 w.WriteHeader(http.StatusOK)
65 return
66 }
67 }
68 }
69 }
70
71 w.WriteHeader(http.StatusNotFound)
72}
73
74func GetUsernameFromRequest(r *http.Request) string {
75 subdomain := GetSubdomain(r)
76 cfg := GetCfg(r)
77
78 if !cfg.IsSubdomains() || subdomain == "" {
79 return GetField(r, 0)
80 }
81 return subdomain
82}
83
84func ServeFile(file string, contentType string) http.HandlerFunc {
85 return func(w http.ResponseWriter, r *http.Request) {
86 logger := GetLogger(r)
87 cfg := GetCfg(r)
88
89 contents, err := os.ReadFile(cfg.StaticPath(fmt.Sprintf("public/%s", file)))
90 if err != nil {
91 logger.Error(err.Error())
92 http.Error(w, "file not found", 404)
93 }
94
95 w.Header().Add("Content-Type", contentType)
96
97 _, err = w.Write(contents)
98 if err != nil {
99 logger.Error(err.Error())
100 }
101 }
102}
103
104func minus(a, b int) int {
105 return a - b
106}
107
108func intRange(start, end int) []int {
109 n := end - start + 1
110 result := make([]int, n)
111 for i := 0; i < n; i++ {
112 result[i] = start + i
113 }
114 return result
115}
116
117var FuncMap = template.FuncMap{
118 "minus": minus,
119 "intRange": intRange,
120}
121
122func RenderTemplate(cfg *ConfigSite, templates []string) (*template.Template, error) {
123 files := make([]string, len(templates))
124 copy(files, templates)
125 files = append(
126 files,
127 cfg.StaticPath("html/footer.partial.tmpl"),
128 cfg.StaticPath("html/marketing-footer.partial.tmpl"),
129 cfg.StaticPath("html/base.layout.tmpl"),
130 )
131
132 ts, err := template.New("base").Funcs(FuncMap).ParseFiles(files...)
133 if err != nil {
134 return nil, err
135 }
136 return ts, nil
137}
138
139func CreatePageHandler(fname string) http.HandlerFunc {
140 return func(w http.ResponseWriter, r *http.Request) {
141 logger := GetLogger(r)
142 cfg := GetCfg(r)
143 ts, err := RenderTemplate(cfg, []string{cfg.StaticPath(fname)})
144
145 if err != nil {
146 logger.Error(err.Error())
147 http.Error(w, err.Error(), http.StatusInternalServerError)
148 return
149 }
150
151 data := PageData{
152 Site: *cfg.GetSiteData(),
153 }
154 err = ts.Execute(w, data)
155 if err != nil {
156 logger.Error(err.Error())
157 http.Error(w, err.Error(), http.StatusInternalServerError)
158 }
159 }
160}