kyra/handler.go

221 lines
4.5 KiB
Go
Raw Normal View History

2024-08-02 18:30:32 +03:00
package kyra
2024-06-09 15:16:38 +03:00
import (
2024-08-02 18:30:32 +03:00
"surdeus.su/util/kyra/mdx"
httpx "surdeus.su/core/xgo/v2/stdlib/http"
"surdeus.su/core/xgo/v2/stdlib"
"surdeus.su/util/gopp"
"bytes"
2024-06-09 15:16:38 +03:00
"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.
2024-08-02 18:30:32 +03:00
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.
2024-06-09 15:16:38 +03:00
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(
2024-08-02 18:30:32 +03:00
pp *gopp.Preprocessor,
2024-06-09 15:16:38 +03:00
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
}
2024-08-02 18:30:32 +03:00
// Returns the default gopp.Preprocessor suitable for
2024-06-09 15:16:38 +03:00
// the most needs.
2024-08-02 18:30:32 +03:00
func DefaultPP(mod string) *gopp.Preprocessor {
t := gopp.NewXGo().SetPreCompile(func(
2024-06-09 15:16:38 +03:00
ctx context.Context,
2024-08-02 18:30:32 +03:00
s *gopp.Script,
2024-06-09 15:16:38 +03:00
) {
2024-06-09 15:16:38 +03:00
s.SetImportDir(mod)
2024-08-02 18:30:32 +03:00
modules := stdlib.GetModuleMap(stdlib.AllModuleNames()...)
modules.Add("markdown", mdx.GetModule())
s.SetImports(modules)
2024-06-09 15:16:38 +03:00
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)
2024-08-02 18:30:32 +03:00
s.Add("__pp_ext__", vals.PPExt)
s.Add("__source_path__", vals.SourcePath)
2024-08-02 18:30:32 +03:00
s.Add("__fext__", vals.FExt)
s.Add("__fpath__", vals.FPath)
2024-06-09 15:16:38 +03:00
}).SetPreCode(func(ctx context.Context) []byte {
return []byte(`
2024-07-28 16:16:41 +03:00
__os__ := import("os")
2024-06-09 15:16:38 +03:00
markdown := func(...args) {
2024-07-28 16:16:41 +03:00
return pp.write_raw(__markdown__(args...))
}
2024-06-09 15:16:38 +03:00
__http__ := immutable({
request : __http_request__
})
2024-06-09 15:16:38 +03:00
context.http = __http__
context.pp = pp
context.global = __global__
context.is_index = __is_index__
context.index_suffix = __index_suffix__
2024-08-02 18:30:32 +03:00
context.pp_ext = __pp_ext__
context.source_path = __source_path__
2024-08-02 18:30:32 +03:00
context.fext = __fext__
context.fpath = __fpath__
2024-06-09 15:16:38 +03:00
import("./pre")(context)
`)
}).SetPostCode(func(ctx context.Context) []byte {
return []byte(`
import("./post")(context)
`)
})
2024-08-02 18:30:32 +03:00
return gopp.New(t)
2024-06-09 15:16:38 +03:00
}
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
2024-06-09 15:16:38 +03:00
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
}
2024-06-09 15:16:38 +03:00
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
2024-06-09 15:16:38 +03:00
}
}
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
}
}
2024-08-02 18:30:32 +03:00
fext := filepath.Ext(filePath)
fpath := filePath
2024-06-09 15:16:38 +03:00
ctx := context.WithValue(
r.Context(),
KeyValues,
&ContextValues{
Request: &httpx.Request{
Request: r,
},
Global: h.global,
Markdown: h.md,
IsIndex: isIndex,
IndexSuffix: h.indexSuffix,
2024-08-02 18:30:32 +03:00
PPExt: h.ext,
SourcePath: h.sourcePath,
2024-08-02 18:30:32 +03:00
FPath: fpath,
FExt: fext,
2024-06-09 15:16:38 +03:00
},
)
// Setting before the code to let it change own
// content type?
fileExt := filepath.Ext(filePath)
contentType := mime.TypeByExtension(fileExt)
2024-06-09 15:16:38 +03:00
w.Header().Set("Content-Type", contentType)
2024-08-02 18:30:32 +03:00
var buf bytes.Buffer
var reader io.Reader
2024-06-09 15:16:38 +03:00
if shouldProcess {
2024-08-02 18:30:32 +03:00
compiled, err := h.pp.Compile(
ctx, file,
2024-06-09 15:16:38 +03:00
)
if err != nil {
2024-08-02 18:30:32 +03:00
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)
2024-06-09 15:16:38 +03:00
return
}
2024-08-02 18:30:32 +03:00
reader = &buf
} else {
reader = file
2024-06-09 15:16:38 +03:00
}
2024-08-02 18:30:32 +03:00
io.Copy(w, reader)
2024-06-09 15:16:38 +03:00
}