diff --git a/config/configuration.go b/config/configuration.go index 6a253f7..81d4241 100644 --- a/config/configuration.go +++ b/config/configuration.go @@ -15,18 +15,23 @@ import ( "github.com/peterbourgon/ff/v4/ffhelp" ) +type AspectRatio struct { + AspectRatio string + ValueChanged chan string +} + type Config struct { - Version bool `ff:"short: v, long: version, usage: 'show version'"` - Tag bool `ff:"short: t, long: tag, usage: 'show tag'"` - Compress string `ff:"short: c, long: compress, usage: 'compress current folder'"` - File string `ff:"short: f, long: file, usage: 'file to open (.tar.gz format)'"` - GIT string `ff:"short: g, long: git, usage: 'git repository URL'"` - GITKey string `ff:" long: key, usage: 'ssh key used for git clone auth'"` - Dir string `ff:"short: d, long: dir, usage: 'directory to open'"` - Help bool `ff:" long: help, usage: 'help'"` - - Address string `ff:"short: h, long:host, default: 127.0.0.1, usage: 'address that present will listen on'"` - Port int `ff:"short: p, long:port, default: 8080, usage: 'port that present will listen on'"` + Version bool `ff:"short: v, long: version, usage: 'show version'"` + Address string `ff:"short: h, long: host, default: 127.0.0.1, usage: 'address that present will listen on'"` + Port int `ff:"short: p, long: port, default: 8080, usage: 'port that present will listen on'"` + Tag bool `ff:"short: t, long: tag, usage: 'show tag'"` + Compress string `ff:"short: c, long: compress, usage: 'compress current folder'"` + File string `ff:"short: f, long: file, usage: 'file to open (.tar.gz format)'"` + GIT string `ff:"short: g, long: git, usage: 'git repository URL'"` + GITKey string `ff:" long: key, usage: 'ssh key used for git clone auth'"` + Dir string `ff:"short: d, long: dir, usage: 'directory to open'"` + Help bool `ff:" long: help, usage: 'help'"` + AspectRatio AspectRatio Security Security Controls Controls @@ -48,7 +53,11 @@ type Controls struct { } func Get() Config { - configuration := Config{} + configuration := Config{ + AspectRatio: AspectRatio{ + ValueChanged: make(chan string), + }, + } security := Security{} controls := Controls{} update := Update{} diff --git a/data/data.go b/data/data.go index bb99c18..227fa74 100644 --- a/data/data.go +++ b/data/data.go @@ -85,6 +85,9 @@ func Init(server Server, config configuration.Config) { //nolint:funlen,gocognit for range filesModified { muPresentation.Lock() presentation = reader.ReadFiles() + if presentation.Options.AspectRatio != "" { + config.AspectRatio.ValueChanged <- presentation.Options.AspectRatio + } var err error for i := range presentation.Slides { var adminHTML string diff --git a/data/reader/reader-slides.go b/data/reader/reader-slides.go index 4d3c10f..9942325 100644 --- a/data/reader/reader-slides.go +++ b/data/reader/reader-slides.go @@ -14,14 +14,16 @@ import ( func ReadFiles() types.Presentation { //nolint:funlen,gocognit,gocyclo,cyclop,maintidx ro := types.ReadOptions{ - DefaultFontSize: "5vh", + DefaultFontSize: "5svh", EveryDashIsATransition: false, - DefaultTerminalFontSize: "6vh", + DefaultTerminalFontSize: "6svh", DefaultBackgroundColor: "white", DefaultTerminalFontColor: "black", DefaultTerminalBackgroundColor: "rgb(253, 246, 227)", HidePageNumber: true, KeepPagePrintOnCut: false, + AspectRatio: "", + ForceAspectRatio: false, } endpoints := map[string]types.TerminalCommand{} @@ -55,6 +57,9 @@ func ReadFiles() types.Presentation { //nolint:funlen,gocognit,gocyclo,cyclop,ma } presentationFile := processSlides(buff.String(), ro) + if presentationFile.Options.AspectRatio != "" { + presentationFiles.Options.AspectRatio = presentationFile.Options.AspectRatio + } presentationFiles.Slides = append(presentationFiles.Slides, presentationFile.Slides...) if presentationFile.Title != "" { presentationFiles.Title = presentationFile.Title @@ -434,6 +439,7 @@ func ReadFiles() types.Presentation { //nolint:funlen,gocognit,gocyclo,cyclop,ma } return types.Presentation{ + Options: presentationFiles.Options, Slides: presentations, CSS: presentationFiles.CSS, JS: presentationFiles.JS, diff --git a/data/reader/reader.go b/data/reader/reader.go index dc980da..42f0407 100644 --- a/data/reader/reader.go +++ b/data/reader/reader.go @@ -301,6 +301,13 @@ func processSlides(fileContent string, ro types.ReadOptions) types.Presentation lines[index] = "" continue } + if strings.HasPrefix(line, ".global.aspect-ratio(") && strings.HasSuffix(line, ")") { + aspect := strings.TrimPrefix(line, ".global.aspect-ratio(") + aspect = strings.TrimSuffix(aspect, ")") + ro.AspectRatio = aspect + lines[index] = "" + continue + } if strings.HasPrefix(line, ".global.background-color(") && strings.HasSuffix(line, ")") { currentBackgroundColor = strings.TrimPrefix(line, ".global.background-color(") currentBackgroundColor = strings.TrimSuffix(currentBackgroundColor, ")") @@ -612,6 +619,9 @@ func processSlides(fileContent string, ro types.ReadOptions) types.Presentation } } return types.Presentation{ + Options: types.PresentationOptions{ + AspectRatio: ro.AspectRatio, + }, Slides: slides, Title: title, Replacers: replacersAfter, diff --git a/examples/showcase/.slide b/examples/showcase/.slide index 6cb6e2e..b5432e8 100644 --- a/examples/showcase/.slide +++ b/examples/showcase/.slide @@ -1,4 +1,5 @@ .global.font-size(5svh) +.global.aspect-ratio(16x9) .title(Present - example slides) .replace.after{#space#}( ) diff --git a/handlers/cast-ws.go b/handlers/cast-ws.go index 3cf8ffb..1ea9802 100644 --- a/handlers/cast-ws.go +++ b/handlers/cast-ws.go @@ -19,6 +19,10 @@ import ( func CastWS(server data.Server, config configuration.Config) http.Handler { //nolint:funlen,gocognit,revive return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + } conn, err := websocket.Accept(w, r, nil) if err != nil { log.Print("upgrade:", err) diff --git a/handlers/frame.go b/handlers/frame.go new file mode 100644 index 0000000..50b1dc9 --- /dev/null +++ b/handlers/frame.go @@ -0,0 +1,160 @@ +package handlers + +import ( + "hash/fnv" + "log" + "net/http" + "strconv" + "strings" + "sync" + + configuration "github.com/oktalz/present/config" +) + +func IFrame(config configuration.Config) http.Handler { //nolint:funlen + eTagFrame := "" + var mu sync.RWMutex + pageResult := page + aspectRatioChange := func() { + mu.Lock() + defer mu.Unlock() + pageResult = page + if config.AspectRatio.AspectRatio != "" { + aspectRatio := strings.Split(config.AspectRatio.AspectRatio, ":") + if len(aspectRatio) == 1 { + aspectRatio = strings.Split(config.AspectRatio.AspectRatio, "x") + } + if len(aspectRatio) == 2 { + width, _ := strconv.Atoi(aspectRatio[0]) + height, _ := strconv.Atoi(aspectRatio[1]) + pageResult = strings.Replace(pageResult, "widthRatio = 16", "widthRatio = "+strconv.Itoa(width), 1) + pageResult = strings.Replace(pageResult, "heightRatio = 9", "heightRatio = "+strconv.Itoa(height), 1) + } + } + hasher := fnv.New64a() + hasher.Write([]byte(pageResult)) + eTagFrame = strconv.FormatUint(hasher.Sum64(), 16) + } + + aspectRatioChange() + go func() { + for { + aspectRatio := <-config.AspectRatio.ValueChanged + config.AspectRatio.AspectRatio = aspectRatio + aspectRatioChange() + } + }() + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _ = cookieIDValue(w, r) + userOK, adminPrivileges := cookieAuth(config.Security.UserPwd, config.Security.AdminPwd, r) + if config.Security.AdminPwdDisable && !adminPrivileges { + adminPrivileges = true + } + if config.Security.UserPwd != "" && !adminPrivileges { + if !(userOK) { + http.Redirect(w, r, "/login", http.StatusFound) + return + } + } + + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + } + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + if r.Method == http.MethodOptions { + return + } + + var response []byte + _ = response + eTag := r.Header.Get("If-None-Match") + mu.RLock() + defer mu.RUnlock() + if eTagFrame == eTag { + w.WriteHeader(http.StatusNotModified) + return + } + + w.Header().Set("Content-Type", "text/html") + w.Header().Set("ETag", eTagFrame) + w.WriteHeader(http.StatusOK) + + _, err := w.Write([]byte(pageResult)) + if err != nil { + log.Println(err) + return + } + }) +} + +var page = ` + + + + + + + + +
+ +
+ + + + +` diff --git a/handlers/middleware.go b/handlers/middleware.go index 4b6b81d..e5415f2 100644 --- a/handlers/middleware.go +++ b/handlers/middleware.go @@ -4,7 +4,10 @@ import "net/http" func AccessControlAllow(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "*") + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + } w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") next.ServeHTTP(w, r) diff --git a/handlers/homepage.go b/handlers/no-layout.go similarity index 78% rename from handlers/homepage.go rename to handlers/no-layout.go index 81c7bae..8105582 100644 --- a/handlers/homepage.go +++ b/handlers/no-layout.go @@ -8,7 +8,7 @@ import ( "github.com/oktalz/present/data" ) -func Homepage(config configuration.Config) http.Handler { +func NoLayout(config configuration.Config) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = cookieIDValue(w, r) userOK, adminPrivileges := cookieAuth(config.Security.UserPwd, config.Security.AdminPwd, r) @@ -22,6 +22,15 @@ func Homepage(config configuration.Config) http.Handler { } } + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + } + w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") + if r.Method == http.MethodOptions { + return + } + var response []byte var status int eTag := r.Header.Get("If-None-Match") diff --git a/http-file-server.go b/http-file-server.go index 89e27a0..56050ef 100644 --- a/http-file-server.go +++ b/http-file-server.go @@ -42,6 +42,11 @@ func (s *fallbackFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + } + rw := responseWriter{ //nolint:exhaustruct CustomHeader: make(http.Header), StatusCode: http.StatusOK, diff --git a/server.go b/server.go index 114b5c2..5a09c90 100644 --- a/server.go +++ b/server.go @@ -22,11 +22,13 @@ func configureServer(config configuration.Config) { wsServer := data.NewServer() data.Init(wsServer, config) - // http.Handle("/cast", handlers.Cast()) http.Handle("/cast", handlers.CastWS(wsServer, config)) http.Handle("/ws", handlers.WS(wsServer, config)) - http.Handle("/{$}", handlers.Homepage(config)) + // http.Handle("/{$}", handlers.NoLayout(config)) + http.Handle("/{$}", handlers.IFrame(config)) + http.Handle("/print", handlers.NoLayout(config)) + http.Handle("/iframe", handlers.IFrame(config)) http.Handle("/login", handlers.Login(loginPage)) http.Handle("GET /api/login", handlers.APILogin(config)) http.Handle("GET /api/users", handlers.APIUsers(config)) diff --git a/types/type.go b/types/type.go index 56d43d1..9a15f1c 100644 --- a/types/type.go +++ b/types/type.go @@ -88,8 +88,13 @@ type Menu struct { Title string } +type PresentationOptions struct { + AspectRatio string +} + type Presentation struct { CSS string + Options PresentationOptions JS string Slides []Slide Menu []Menu @@ -105,6 +110,8 @@ type TerminalOutputLine struct { type ReadOptions struct { DefaultFontSize string + AspectRatio string + ForceAspectRatio bool DefaultBackgroundColor string EveryDashIsATransition bool DefaultTerminalFontSize string diff --git a/ui/web.tmpl b/ui/web.tmpl index 6005eb6..47141e9 100644 --- a/ui/web.tmpl +++ b/ui/web.tmpl @@ -74,7 +74,7 @@ onPageChange = function() { {{- end -}} return; }; - {{ define "slide" }}