forked from NollGo/Noll
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
181 lines (163 loc) · 4.78 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
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
177
178
179
180
181
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/excing/goflag"
)
// Config is gd2b config
type Config struct {
Owner string `flag:"Github repository owner"`
Name string `flag:"Github repository name"`
Token string `flag:"Github authorization token (see https://docs.github.com/zh/graphql/guides/forming-calls-with-graphql)"`
Pages string `flag:"Your github pages repository name, If None, defaults to the repository where the discussion resides"`
Debug bool `flag:"Debug mode if true"`
DebugMod string `flag:"Debug mode: auto, manual. Automatic debugging mode and manual debugging mode"`
BaseURL string `flag:"Web site base url"`
GamID string `flag:"Google Analytics Measurement id, Defaults to empty to not load the Google Analytics script"`
ThemeDir string `flag:"Filesystem path to themes directory, Defaults to embed assets/theme"`
NewSite bool `flag:"Generate theme, Defaults to false"`
Export string `flag:"Export all Discussions to markdown, Value is the export directory"`
}
func main() {
var config Config
goflag.Var(&config)
goflag.Parse("config", "Configuration file path.")
if config.NewSite {
if err := newSite(config.ThemeDir); err != nil {
panic(err)
}
fmt.Println("New site success")
return
}
if config.Export != "" {
if err := export(config); err != nil {
panic(err)
}
fmt.Println("Export success")
return
}
fmt.Println("Start build noll siteweb")
if config.Pages == "" {
config.Pages = config.Name
}
if config.ThemeDir == "" {
config.ThemeDir = "assets/theme"
}
pageDomain := fmt.Sprintf("%v.github.io", config.Owner)
config.BaseURL = UnixPath(strings.ReplaceAll(config.BaseURL, pageDomain, "/"))
var err error
if _, err = os.Stat(config.Pages); os.IsNotExist(err) {
os.MkdirAll(config.Pages, os.ModePerm)
}
var githubData *GithubData
_getGithubData := func() error {
githubData, err = getRepository(config.Owner, config.Name, config.Token)
return err
}
_render := func() error {
return render(
&RenderSite{
BaseURL: config.BaseURL,
GamID: config.GamID,
},
githubData, config.ThemeDir,
config.Debug,
func(s string, b []byte) error {
fname := strings.ReplaceAll(s, ".gtpl", ".html")
htmlPath := filepath.Join(config.Pages, fname)
MkdirFileFolderIfNotExists(htmlPath)
if config.Debug {
fmt.Println(s, string(b), "\n=========================================")
}
return os.WriteFile(htmlPath, b, os.ModePerm)
})
}
if err = _getGithubData(); err != nil {
panic(err)
}
if err = _render(); err != nil {
panic(err)
}
fmt.Println("Build noll siteweb finished")
if config.Debug {
port := ":20000"
fs := &DirWithError{
FS: http.Dir(config.Pages),
Status: map[int]string{http.StatusNotFound: "404.html"},
}
fmt.Println("Start noll debug mode in http://localhost" + port)
if config.DebugMod == "auto" {
http.Handle("/ws", debugWs(config, _render))
}
http.Handle("/", http.StripPrefix("/", http.FileServer(fs)))
// 重新编译渲染接口
// 调试使用
http.Handle("/build", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
mode := query.Get("mode")
switch mode {
case "full":
// 全量更新:
// 删除本地所有文件,
// 然后从网络上获取最新数据,
// 再重新生成所有文件。
case "increase":
// 增量更新:
// 从网络上获取最新数据,
// 并检测本地数据是否需要更新,
// 如果需要,则更新,否则跳过,此操作由渲染引擎处理。
//
// 增量更新和全量更新在流程,仅是否有删除本地所有文件的区别。
if err = _getGithubData(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
}
if err = _render(); err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
} else {
w.WriteHeader(http.StatusOK)
w.Write([]byte("Build successed!"))
}
}))
err = http.ListenAndServe(port, nil)
if err != nil {
panic(err)
}
}
}
// DirWithError 带有错误状态页面的 http 文件系统
type DirWithError struct {
FS http.FileSystem
Status map[int]string
}
// Open 返回指定名称(路径)的文件
func (d *DirWithError) Open(name string) (http.File, error) {
f, err := d.FS.Open(name)
if err != nil {
if os.IsNotExist(err) {
_404, ok := d.Status[http.StatusNotFound]
if ok {
return d.FS.Open(_404)
}
} else if os.IsPermission(err) {
_403, ok := d.Status[http.StatusForbidden]
if ok {
return d.FS.Open(_403)
}
} else {
// Default:
_500, ok := d.Status[http.StatusInternalServerError]
if ok {
return d.FS.Open(_500)
}
}
return f, err
}
return f, nil
}