-
Notifications
You must be signed in to change notification settings - Fork 2
/
local.go
176 lines (154 loc) · 4.35 KB
/
local.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package main
import (
"bufio"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
)
// TODO this file could be refactored out into proper handler
// functions that properly set the content type AND use a basic
// middleware or setup function to handle the common stuff
func local() {
fmt.Println("Running a local web server on port 9876")
fmt.Println("Browse to http://localhost:9876/__index__ to view the index")
// All requests other than index and json
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
loadBaseTemplate()
errMsg := loadAllFromDisk()
if errMsg != "" {
writeErrorPage(w, "Error loading pages", fmt.Sprintf("Error: (%s)", errMsg))
return
}
// This is a hacked version of loading static files as it
// would exist on the server
path := r.URL.Path[1:]
page := findPage(path)
if page == nil {
writeErrorPage(w, "Page Not Found", fmt.Sprintf("Page (%s) was not found", path))
return
}
htmlBys := renderPage(*page)
err := validateHTML(htmlBys)
if err != nil {
writeErrorPage(w, "Invalid HTML", fmt.Sprintf("%s", err))
return
}
// Success
fmt.Fprintf(w, string(htmlBys))
})
http.HandleFunc("/__index__", func(w http.ResponseWriter, r *http.Request) {
errMsg := loadAllFromDisk()
if errMsg != "" {
writeErrorPage(w, "Error loading pages", fmt.Sprintf("Error: (%s)", errMsg))
return
}
fmt.Fprintf(w, renderLocalIndex())
})
http.HandleFunc("/__json__", func(w http.ResponseWriter, r *http.Request) {
errMsg := loadAllFromDisk()
if errMsg != "" {
writeErrorPage(w, "Error loading pages", fmt.Sprintf("Error: (%s)", errMsg))
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, renderJSONRepr())
})
fs := http.FileServer(http.Dir("./"))
http.Handle("/static/", fs)
http.ListenAndServe(":9876", nil)
}
func writeErrorPage(w http.ResponseWriter, errTitle, errMsg string) {
fmt.Fprintf(w, "<body><h1>%s</h1><p>%s</p></body>", errTitle, errMsg)
}
// Although not exactly how a webserver will look for the static files
// on disk, easy enough to just find on linkdir
func findPage(path string) *Page {
path = strings.TrimSuffix(path, "/")
for i, page := range pages {
if path == page.LinkDir {
return &(pages[i]) // Don't return &page as that memory is reused
}
}
return nil
}
func renderLocalIndex() string {
page := "<body>\n<h1>Local Development Index</h1>\n<h2>Published Pages</h2>\n"
for _, p := range pages {
page += localLink(p)
}
jsonSection := `
<h2>JSON Repr of Internal Pages</h2>
<p><a href="/__json__">JSON Repr</a></p>
`
return page + "\n" + jsonSection + "</body>"
}
func renderJSONRepr() string {
bys, err := json.Marshal(pages)
// Almost no chance of error ever so just panic
if err != nil {
panic(err)
}
return string(bys)
}
func localLinkHTML(url string) string {
return fmt.Sprintf(`<div><a href="%s">%s</a></div>`, url, url) + "\n"
}
func localLink(page Page) string {
u := "http://localhost:9876/"
if page.LinkDir == "" { // will only happen for home page
return localLinkHTML(u)
}
return localLinkHTML(u + page.LinkDir + "/")
}
// Find all pages from disk to populate pages var
func loadAllFromDisk() string {
pgs, errMsg := walkFiles("pages")
if errMsg != "" {
return errMsg
}
pages = pgs
latestPages = nil
return validateIndex()
}
// TODO change this to using error messages not dying so we can
// use with web server
// TODO combine with generateAndWriteHTML() in gen.go
func renderPage(page Page) []byte {
// Render full page!
finalPage := renderHTML(templateData(page))
return finalPage
}
// readOffsetFile will read the whole file starting at offset
// because we read/remove metadata from the top of the file
func readOffsetFile(fullPath string, offset int) ([]byte, error) {
file, err := os.Open(fullPath)
if err != nil {
return nil, err
}
defer file.Close()
var lineNum int
var contents string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lineNum++
if lineNum < offset {
continue
}
lineText := scanner.Text()
if lineText == "" { // Our scanner will remove newlines which markdown needs
lineText = "\n"
}
contents += lineText + "\n"
}
if contents == "" {
return nil, fmt.Errorf("readOffsetFile (%s) Found no file data after %d",
fullPath, offset)
}
if err := scanner.Err(); err != nil {
return nil, err
}
return []byte(contents), nil
}