220 lines
4.5 KiB
Go
220 lines
4.5 KiB
Go
package kyra
|
|
|
|
import (
|
|
"surdeus.su/util/kyra/mdx"
|
|
httpx "surdeus.su/core/xgo/v2/stdlib/http"
|
|
"surdeus.su/core/xgo/v2/stdlib"
|
|
"surdeus.su/util/gopp"
|
|
|
|
"bytes"
|
|
"path/filepath"
|
|
"net/http"
|
|
"context"
|
|
"path"
|
|
"mime"
|
|
"log"
|
|
"os"
|
|
"io"
|
|
)
|
|
|
|
var _ = http.Handler(&Handler{})
|
|
|
|
// The type describes behaviour
|
|
// of handling stuff like in PHP.
|
|
type Handler struct {
|
|
// THe field represents
|
|
// where we store site's
|
|
// source files and request handlers.
|
|
sourcePath string
|
|
// Preprocessor must be set by user
|
|
// to be able to bring custom features in.
|
|
pp *gopp.Preprocessor
|
|
// Aditional extension. ".kr" by default.
|
|
// For example "file.html.kr" will be
|
|
// first preprocessed by gopp and sent back as simple HTML.
|
|
ext string
|
|
|
|
// The global map accessable from all the
|
|
// request so you can keep the states
|
|
// between requests.
|
|
global any
|
|
md *mdx.Markdown
|
|
indexSuffix string
|
|
}
|
|
|
|
// Returns the new Handler with
|
|
// specified preprocessor, source path,
|
|
// preprocessor extension and the global value.
|
|
func NewHandler(
|
|
pp *gopp.Preprocessor,
|
|
src, ext, index string,
|
|
global any,
|
|
) *Handler {
|
|
ret := &Handler{}
|
|
ret.sourcePath = src
|
|
ret.ext = ext
|
|
ret.pp = pp
|
|
ret.global = global
|
|
ret.indexSuffix = index
|
|
return ret
|
|
}
|
|
|
|
// Returns the default gopp.Preprocessor suitable for
|
|
// the most needs.
|
|
func DefaultPP(mod string) *gopp.Preprocessor {
|
|
t := gopp.NewXGo().SetPreCompile(func(
|
|
ctx context.Context,
|
|
s *gopp.Script,
|
|
) {
|
|
|
|
s.SetImportDir(mod)
|
|
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
|
|
modules.Add("markdown", mdx.GetModule())
|
|
s.SetImports(modules)
|
|
s.EnableFileImport(true)
|
|
|
|
vals := ctx.Value(KeyValues).(*ContextValues)
|
|
s.Add("__http_request__", vals.Request)
|
|
s.Add("__global__", vals.Global)
|
|
s.Add("__markdown__", vals.Markdown)
|
|
s.Add("__is_index__", vals.IsIndex)
|
|
s.Add("__index_suffix__", vals.IndexSuffix)
|
|
s.Add("__pp_ext__", vals.PPExt)
|
|
s.Add("__source_path__", vals.SourcePath)
|
|
s.Add("__fext__", vals.FExt)
|
|
s.Add("__fpath__", vals.FPath)
|
|
|
|
}).SetPreCode(func(ctx context.Context) []byte {
|
|
return []byte(`
|
|
__os__ := import("os")
|
|
|
|
markdown := func(...args) {
|
|
return pp.write_raw(__markdown__(args...))
|
|
}
|
|
|
|
__http__ := immutable({
|
|
request : __http_request__
|
|
})
|
|
|
|
context.http = __http__
|
|
context.pp = pp
|
|
context.global = __global__
|
|
context.is_index = __is_index__
|
|
context.index_suffix = __index_suffix__
|
|
context.pp_ext = __pp_ext__
|
|
context.source_path = __source_path__
|
|
context.fext = __fext__
|
|
context.fpath = __fpath__
|
|
|
|
import("./pre")(context)
|
|
`)
|
|
}).SetPostCode(func(ctx context.Context) []byte {
|
|
return []byte(`
|
|
import("./post")(context)
|
|
`)
|
|
})
|
|
|
|
return gopp.New(t)
|
|
}
|
|
|
|
func (h *Handler) SetMD(md *mdx.Markdown) *Handler {
|
|
h.md = md
|
|
return h
|
|
}
|
|
|
|
func (h *Handler) ServeHTTP(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
) {
|
|
isIndex := false
|
|
shouldProcess := true
|
|
urlPath := r.URL.Path
|
|
// Cleaning URL path to prevent injections.
|
|
urlPath = path.Clean(urlPath)
|
|
urlExt := path.Ext(urlPath)
|
|
if urlExt == h.ext {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
|
|
filePath := filepath.Join(
|
|
filepath.FromSlash(h.sourcePath),
|
|
filepath.FromSlash(urlPath),
|
|
)
|
|
fileStat, err := os.Stat(filePath)
|
|
if err == nil {
|
|
if fileStat.IsDir() {
|
|
filePath = filepath.Join(
|
|
filePath,
|
|
h.indexSuffix,
|
|
)
|
|
isIndex = true
|
|
}
|
|
}
|
|
|
|
filePathTpp := filePath + h.ext
|
|
//log.Println("pth:", urlPath, filePathTpp)
|
|
file, err := os.Open(filePathTpp)
|
|
if err != nil {
|
|
shouldProcess = false
|
|
file, err = os.Open(filePath)
|
|
if err != nil {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
}
|
|
|
|
fext := filepath.Ext(filePath)
|
|
fpath := filePath
|
|
ctx := context.WithValue(
|
|
r.Context(),
|
|
KeyValues,
|
|
&ContextValues{
|
|
Request: &httpx.Request{
|
|
Request: r,
|
|
},
|
|
Global: h.global,
|
|
Markdown: h.md,
|
|
IsIndex: isIndex,
|
|
IndexSuffix: h.indexSuffix,
|
|
PPExt: h.ext,
|
|
SourcePath: h.sourcePath,
|
|
FPath: fpath,
|
|
FExt: fext,
|
|
},
|
|
)
|
|
|
|
// Setting before the code to let it change own
|
|
// content type?
|
|
fileExt := filepath.Ext(filePath)
|
|
contentType := mime.TypeByExtension(fileExt)
|
|
w.Header().Set("Content-Type", contentType)
|
|
|
|
var buf bytes.Buffer
|
|
var reader io.Reader
|
|
if shouldProcess {
|
|
compiled, err := h.pp.Compile(
|
|
ctx, file,
|
|
)
|
|
if err != nil {
|
|
log.Printf("gopp.Compile(...): %s\n", err)
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
|
|
err = h.pp.XGo().RunContext(
|
|
ctx, compiled,
|
|
filePath, &buf,
|
|
)
|
|
if err != nil {
|
|
log.Printf("gopp.RunContext(...): %s\n", err)
|
|
http.Error(w, err.Error(), 500)
|
|
return
|
|
}
|
|
reader = &buf
|
|
} else {
|
|
reader = file
|
|
}
|
|
|
|
io.Copy(w, reader)
|
|
}
|