123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- 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)
- }
|