Skip to content

Commit 02df131

Browse files
重构项目
1 parent 54bbef0 commit 02df131

File tree

12 files changed

+245
-65
lines changed

12 files changed

+245
-65
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ dist/gitea-pages-$(GOOS)-$(GOARCH).tar.gz: $(shell find . -type f -name "*.go"
2121
@echo Compile $@ via $(GO_DIST_NAME) && \
2222
mkdir -p dist && \
2323
rm -f dist/$(GO_DIST_NAME) && \
24-
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o dist/$(GO_DIST_NAME) . && \
24+
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o dist/$(GO_DIST_NAME) cmd/server && \
2525
cd dist && \
26-
tar zcf gitea-pages-$(GOOS)-$(GOARCH).tar.gz $(GO_DIST_NAME) ../LICENSE ../config.yaml ../errors.html.tmpl ../README.md ../README_*.md && \
26+
tar zcf gitea-pages-$(GOOS)-$(GOARCH).tar.gz $(GO_DIST_NAME) ../LICENSE ../config.yaml ../cmd/server/errors.html.tmpl ../README.md ../README_*.md && \
2727
rm -f $(GO_DIST_NAME)
2828

2929
gitea-pages: $(shell find . -type f -name "*.go" ) go.mod go.sum

cmd/local/main.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"os"
6+
)
7+
8+
var (
9+
org = "pub"
10+
domain = "fbi.com"
11+
repo = org + "." + domain
12+
path = ""
13+
14+
port = 8080
15+
)
16+
17+
func init() {
18+
dir, _ := os.Getwd()
19+
path = dir
20+
flag.StringVar(&org, "org", org, "org")
21+
flag.StringVar(&repo, "repo", repo, "repo")
22+
flag.StringVar(&domain, "domain", domain, "domain")
23+
flag.StringVar(&path, "path", path, "path")
24+
flag.IntVar(&port, "port", port, "port")
25+
flag.Parse()
26+
}
27+
28+
func main() {
29+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

pkg/core/vfs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type PageVFS struct {
1616
commitID string
1717
}
1818

19+
// todo: 限制最大文件加载大小
1920
func NewPageVFS(
2021
client *http.Client,
2122
backend Backend,

pkg/filters/common.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package filters
22

3-
import "gopkg.d7z.net/gitea-pages/pkg/core"
3+
import (
4+
"gopkg.d7z.net/gitea-pages/pkg/core"
5+
"gopkg.d7z.net/gitea-pages/pkg/filters/quickjs"
6+
)
47

58
func DefaultFilters() map[string]core.FilterInstance {
69
return map[string]core.FilterInstance{
@@ -11,6 +14,6 @@ func DefaultFilters() map[string]core.FilterInstance {
1114
"_404_": FilterInstDefaultNotFound,
1215
"failback": FilterInstFailback,
1316
"template": FilterInstTemplate,
14-
"qjs": FilterInstQuickJS,
17+
"qjs": quickjs.FilterInstQuickJS,
1518
}
1619
}

pkg/filters/quickjs.go renamed to pkg/filters/quickjs/quickjs.go

Lines changed: 171 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package filters
1+
package quickjs
22

33
import (
44
"context"
5+
"fmt"
56
"io"
67
"log"
78
"net/http"
@@ -15,7 +16,8 @@ import (
1516

1617
var FilterInstQuickJS core.FilterInstance = func(config core.FilterParams) (core.FilterCall, error) {
1718
var param struct {
18-
Exec string `json:"exec"`
19+
Exec string `json:"exec"`
20+
Debug bool `json:"debug"`
1921
}
2022
if err := config.Unmarshal(&param); err != nil {
2123
return nil, err
@@ -29,45 +31,176 @@ var FilterInstQuickJS core.FilterInstance = func(config core.FilterParams) (core
2931
return err
3032
}
3133

32-
var rt = quickjs.NewRuntime()
34+
rt := quickjs.NewRuntime()
35+
rt.SetExecuteTimeout(5)
3336
defer rt.Close()
3437

3538
jsCtx := rt.NewContext()
3639
defer jsCtx.Close()
3740

41+
// 在 debug 模式下,我们需要拦截输出
42+
var (
43+
outputBuffer strings.Builder
44+
logBuffer strings.Builder
45+
jsError error
46+
)
47+
3848
global := jsCtx.Globals()
39-
global.Set("request", createRequestObject(jsCtx, request))
40-
global.Set("response", createResponseObject(jsCtx, writer, request))
41-
global.Set("console", createConsoleObject(jsCtx))
49+
global.Set("request", createRequestObject(jsCtx, request, metadata))
50+
51+
// 根据是否 debug 模式创建不同的 response 对象
52+
if param.Debug {
53+
// debug 模式下使用虚假的 writer 来捕获输出
54+
global.Set("response", createResponseObject(jsCtx, &debugResponseWriter{
55+
buffer: &outputBuffer,
56+
header: make(http.Header),
57+
}, request))
58+
global.Set("console", createConsoleObject(jsCtx, &logBuffer))
59+
} else {
60+
global.Set("response", createResponseObject(jsCtx, writer, request))
61+
global.Set("console", createConsoleObject(jsCtx, nil))
62+
}
4263

4364
ret := jsCtx.Eval(js)
4465
defer ret.Free()
66+
jsCtx.Loop()
4567

4668
if ret.IsException() {
4769
err := jsCtx.Exception()
48-
return err
70+
jsError = err
4971
}
50-
return nil
72+
73+
// 如果在 debug 模式下,返回 HTML 调试页面
74+
if param.Debug {
75+
return renderDebugPage(writer, &outputBuffer, &logBuffer, jsError)
76+
}
77+
78+
return jsError
5179
}, nil
5280
}
5381

82+
// debugResponseWriter 用于在 debug 模式下捕获响应输出
83+
type debugResponseWriter struct {
84+
buffer *strings.Builder
85+
header http.Header
86+
status int
87+
}
88+
89+
func (w *debugResponseWriter) Header() http.Header {
90+
return w.header
91+
}
92+
93+
func (w *debugResponseWriter) Write(data []byte) (int, error) {
94+
return w.buffer.Write(data)
95+
}
96+
97+
func (w *debugResponseWriter) WriteHeader(statusCode int) {
98+
w.status = statusCode
99+
}
100+
101+
// renderDebugPage 渲染调试页面
102+
func renderDebugPage(writer http.ResponseWriter, outputBuffer, logBuffer *strings.Builder, jsError error) error {
103+
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
104+
105+
html := `<!DOCTYPE html>
106+
<html>
107+
<head>
108+
<title>QuickJS Debug</title>
109+
<style>
110+
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
111+
.section { margin-bottom: 30px; border: 1px solid #ddd; border-radius: 5px; }
112+
.section-header { background: #f5f5f5; padding: 10px 15px; border-bottom: 1px solid #ddd; font-weight: bold; }
113+
.section-content { padding: 15px; background: white; }
114+
.output { white-space: pre-wrap; font-family: monospace; }
115+
.log { white-space: pre-wrap; font-family: monospace; background: #f8f8f8; }
116+
.error { color: #d00; background: #fee; padding: 10px; border-radius: 3px; }
117+
.success { color: #080; background: #efe; padding: 10px; border-radius: 3px; }
118+
</style>
119+
</head>
120+
<body>
121+
<h1>QuickJS Debug Output</h1>
122+
123+
<div class="section">
124+
<div class="section-header">执行结果</div>
125+
<div class="section-content">
126+
<div class="output">`
127+
128+
// 转义输出内容
129+
output := outputBuffer.String()
130+
if output == "" {
131+
output = "(无输出)"
132+
}
133+
html += htmlEscape(output)
134+
135+
html += `</div>
136+
</div>
137+
</div>
138+
139+
<div class="section">
140+
<div class="section-header">控制台日志</div>
141+
<div class="section-content">
142+
<div class="log">`
143+
144+
// 转义日志内容
145+
logs := logBuffer.String()
146+
if logs == "" {
147+
logs = "(无日志)"
148+
}
149+
html += htmlEscape(logs)
150+
151+
html += `</div>
152+
</div>
153+
</div>
154+
155+
<div class="section">
156+
<div class="section-header">执行状态</div>
157+
<div class="section-content">`
158+
159+
if jsError != nil {
160+
html += `<div class="error"><strong>错误:</strong> ` + htmlEscape(jsError.Error()) + `</div>`
161+
} else {
162+
html += `<div class="success">执行成功</div>`
163+
}
164+
165+
html += `</div>
166+
</div>
167+
</body>
168+
</html>`
169+
170+
_, err := writer.Write([]byte(html))
171+
return err
172+
}
173+
174+
// htmlEscape 转义 HTML 特殊字符
175+
func htmlEscape(s string) string {
176+
return strings.NewReplacer(
177+
"&", "&amp;",
178+
"<", "&lt;",
179+
">", "&gt;",
180+
`"`, "&quot;",
181+
"'", "&#39;",
182+
).Replace(s)
183+
}
184+
54185
// createRequestObject 创建表示 HTTP 请求的 JavaScript 对象
55-
func createRequestObject(ctx *quickjs.Context, req *http.Request) *quickjs.Value {
186+
func createRequestObject(ctx *quickjs.Context, req *http.Request, metadata *core.PageContent) *quickjs.Value {
56187
obj := ctx.NewObject()
57-
58188
// 基本属性
59189
obj.Set("method", ctx.NewString(req.Method))
60-
obj.Set("url", ctx.NewString(req.URL.String()))
61-
obj.Set("path", ctx.NewString(req.URL.Path))
62-
obj.Set("query", ctx.NewString(req.URL.RawQuery))
190+
url := *req.URL
191+
url.Path = metadata.Path
192+
obj.Set("url", ctx.NewString(url.String()))
193+
obj.Set("path", ctx.NewString(url.Path))
194+
obj.Set("rawPath", ctx.NewString(req.URL.Path))
195+
obj.Set("query", ctx.NewString(url.RawQuery))
63196
obj.Set("host", ctx.NewString(req.Host))
64197
obj.Set("remoteAddr", ctx.NewString(req.RemoteAddr))
65198
obj.Set("proto", ctx.NewString(req.Proto))
66199
obj.Set("httpVersion", ctx.NewString(req.Proto))
67200

68201
// 解析查询参数
69202
queryObj := ctx.NewObject()
70-
for key, values := range req.URL.Query() {
203+
for key, values := range url.Query() {
71204
if len(values) > 0 {
72205
queryObj.Set(key, ctx.NewString(values[0]))
73206
}
@@ -169,20 +302,20 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
169302
}
170303
return ctx.NewNull()
171304
}))
172-
173305
obj.Set("writeHead", ctx.NewFunction(func(c *quickjs.Context, value *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
174306
if len(args) >= 1 {
175307
statusCode := int(args[0].ToInt32())
176-
177308
// 处理可选的 headers 参数
178309
if len(args) >= 2 && args[1].IsObject() {
179310
headersObj := args[1]
180-
headersObj.Properties().ForEach(func(key string, value *quickjs.Value) bool {
181-
writer.Header().Set(key, value.String())
182-
return true
183-
})
311+
names, err := headersObj.PropertyNames()
312+
if err != nil {
313+
return ctx.NewError(err)
314+
}
315+
for _, key := range names {
316+
writer.Header().Set(key, headersObj.Get(key).String())
317+
}
184318
}
185-
186319
writer.WriteHeader(statusCode)
187320
}
188321
return ctx.NewNull()
@@ -261,11 +394,11 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
261394
}
262395

263396
if secure := options.Get("secure"); !secure.IsNull() {
264-
cookie.Secure = secure.Bool()
397+
cookie.Secure = secure.ToBool()
265398
}
266399

267400
if httpOnly := options.Get("httpOnly"); !httpOnly.IsNull() {
268-
cookie.HttpOnly = httpOnly.Bool()
401+
cookie.HttpOnly = httpOnly.ToBool()
269402
}
270403
}
271404

@@ -278,48 +411,32 @@ func createResponseObject(ctx *quickjs.Context, writer http.ResponseWriter, req
278411
}
279412

280413
// createConsoleObject 创建 console 对象用于日志输出
281-
func createConsoleObject(ctx *quickjs.Context) *quickjs.Value {
414+
func createConsoleObject(ctx *quickjs.Context, buf *strings.Builder) *quickjs.Value {
282415
console := ctx.NewObject()
283416

284-
logFunc := func(level string) func(*quickjs.Context, *quickjs.Value, []*quickjs.Value) *quickjs.Value {
417+
logFunc := func(level string, buffer *strings.Builder) func(*quickjs.Context, *quickjs.Value, []*quickjs.Value) *quickjs.Value {
285418
return func(q *quickjs.Context, value *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
286419
var messages []string
287420
for _, arg := range args {
288421
messages = append(messages, arg.String())
289422
}
290-
log.Printf("[" + level + "] " + strings.Join(messages, " "))
291-
return ctx.NewNull()
292-
}
293-
}
294-
295-
console.Set("log", ctx.NewFunction(logFunc("INFO")))
296-
console.Set("info", ctx.NewFunction(logFunc("INFO")))
297-
console.Set("warn", ctx.NewFunction(logFunc("WARN")))
298-
console.Set("error", ctx.NewFunction(logFunc("ERROR")))
299-
console.Set("debug", ctx.NewFunction(logFunc("DEBUG")))
423+
message := fmt.Sprintf("[%s] %s", level, strings.Join(messages, " "))
300424

301-
// 添加 time 和 timeEnd 方法用于性能测量
302-
timers := make(map[string]time.Time)
425+
// 总是输出到系统日志
426+
log.Print(message)
303427

304-
console.Set("time", ctx.NewFunction(func(c *quickjs.Context, value *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
305-
if len(args) > 0 {
306-
label := args[0].String()
307-
timers[label] = time.Now()
308-
}
309-
return ctx.NewNull()
310-
}))
311-
312-
console.Set("timeEnd", ctx.NewFunction(func(c *quickjs.Context, value *quickjs.Value, args []*quickjs.Value) *quickjs.Value {
313-
if len(args) > 0 {
314-
label := args[0].String()
315-
if start, exists := timers[label]; exists {
316-
elapsed := time.Since(start)
317-
log.Printf("[TIMER] %s: %v", label, elapsed)
318-
delete(timers, label)
428+
// 如果有缓冲区,也写入缓冲区
429+
if buffer != nil {
430+
buffer.WriteString(message + "\n")
319431
}
432+
return ctx.NewNull()
320433
}
321-
return ctx.NewNull()
322-
}))
434+
}
323435

436+
console.Set("log", ctx.NewFunction(logFunc("INFO", buf)))
437+
console.Set("info", ctx.NewFunction(logFunc("INFO", buf)))
438+
console.Set("warn", ctx.NewFunction(logFunc("WARN", buf)))
439+
console.Set("error", ctx.NewFunction(logFunc("ERROR", buf)))
440+
console.Set("debug", ctx.NewFunction(logFunc("DEBUG", buf)))
324441
return console
325442
}

0 commit comments

Comments
 (0)