-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.go
128 lines (109 loc) · 3.07 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import (
"context"
"embed"
"flag"
"fmt"
"html/template"
"io/fs"
"log"
"net/http"
"github.com/google/uuid"
"tailscale.com/client/tailscale"
)
//go:embed ui/*
var ui embed.FS
var (
port = flag.String("port", "80", "the port to listen on")
dev = flag.Bool("dev", false, "enable dev mode")
)
func main() {
flag.Parse()
var templateFn func() *template.Template
if *dev {
// load assets from local filesystem
http.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(http.Dir("ui"))))
templateFn = func() *template.Template {
t, _ := template.ParseFiles("./ui/index.html")
return t
}
} else {
// load assets from embedded filesystem
uiAssets, _ := fs.Sub(ui, "ui")
http.Handle("/ui/", http.StripPrefix("/ui/", http.FileServer(http.FS(uiAssets))))
t, _ := template.ParseFS(uiAssets, "index.html")
templateFn = func() *template.Template {
return t
}
}
http.HandleFunc("/api/uuid", func(w http.ResponseWriter, r *http.Request) {
uuid := uuid.New().String()
fmt.Fprintf(w, "%s\n", uuid) // write to http response
fmt.Printf("%s\n", uuid) // write to stdout - TODO: maybe only in dev mode?
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var pageData *page
whois, err := tailscaleWhois(r.Context(), r)
if err != nil {
pageData = &page{
UserProfile: nil,
}
} else if whois != nil {
var firstInitial string
if whois.DisplayName != "" {
firstInitial = string(whois.DisplayName[0])
} else {
firstInitial = string(whois.LoginName[0])
}
pageData = &page{
UserProfile: whois,
FirstInitial: firstInitial,
}
}
fmt.Printf("user info [%+v] for [%+v] \n", pageData, r.RemoteAddr)
err = templateFn().Execute(w, pageData)
if err != nil {
fmt.Printf("error rendering template [%+v] \n", err)
// TODO: re-render template with nil pageData to not interrupt demo?
}
})
fmt.Printf("Starting server: http://localhost:%s/\n", *port)
if err := http.ListenAndServe(fmt.Sprintf(":%s", *port), nil); err != nil {
log.Fatal(err)
}
}
type page struct {
UserProfile *whoisData
FirstInitial string
}
type whoisData struct {
LoginName string
DisplayName string
}
func tailscaleWhois(ctx context.Context, r *http.Request) (*whoisData, error) {
var u *whoisData
localClient := &tailscale.LocalClient{}
whois, err := localClient.WhoIs(ctx, r.RemoteAddr)
if err != nil {
if r.Header.Get("Tailscale-User-Login") != "" {
// https://tailscale.com/kb/1312/serve#identity-headers
u = &whoisData{
LoginName: r.Header.Get("Tailscale-User-Login"),
DisplayName: r.Header.Get("Tailscale-User-Name"),
}
} else {
return nil, fmt.Errorf("failed to identify remote host: %w", err)
}
} else {
if whois.Node.IsTagged() {
return nil, fmt.Errorf("tagged nodes do not have a user identity")
} else if whois.UserProfile == nil || whois.UserProfile.LoginName == "" {
return nil, fmt.Errorf("failed to identify remote user")
}
u = &whoisData{
LoginName: whois.UserProfile.LoginName,
DisplayName: whois.UserProfile.DisplayName,
}
}
return u, nil
}