From 1375038a66a33471f21abf28a82c01f7350f6be3 Mon Sep 17 00:00:00 2001 From: Henrique Dias Date: Sun, 6 Mar 2016 20:37:49 +0000 Subject: [PATCH] structure changes --- Gruntfile.js | 14 +- assets/{ => public}/css/main.min.css | 0 assets/{ => public}/fonts/FontAwesome.otf | Bin .../fonts/fontawesome-webfont.eot | Bin .../fonts/fontawesome-webfont.svg | 0 .../fonts/fontawesome-webfont.ttf | Bin .../fonts/fontawesome-webfont.woff | Bin .../fonts/fontawesome-webfont.woff2 | Bin assets/{ => public}/js/app.min.js | 0 assets/{ => public}/js/plugins.min.js | 0 assets/{src => public_src}/css/main.css | 0 assets/{src => public_src}/js/app.js | 0 assets/{src => public_src}/js/browse.js | 0 assets/{src => public_src}/js/editor.js | 0 .../{src => public_src}/js/notifications.js | 0 {templates => assets/templates}/404.tmpl | 0 .../templates}/base_full.tmpl | 0 .../templates}/base_minimal.tmpl | 0 {templates => assets/templates}/browse.tmpl | 0 {templates => assets/templates}/editor.tmpl | 0 .../templates}/frontmatter.tmpl | 0 config/config.go | 3 +- config/installer.go | 211 +++++++++++++++++ hugo.go | 13 +- routes/assets/.gitkeep | 0 routes/browse/delete.go | 8 +- routes/browse/get.go | 3 +- routes/browse/post.go | 22 +- routes/browse/put.go | 8 +- routes/editor/get.go | 5 +- routes/editor/post.go | 4 +- routes/git/post.go | 12 +- tools/commands/commands.go | 2 +- tools/frontmatter/frontmatter.go | 6 +- tools/hugo/hugo.go | 223 +----------------- tools/server/response.go | 25 ++ tools/{utils/utils.go => server/url.go} | 25 +- tools/templates/templates.go | 73 +----- tools/templates/templates_test.go | 28 --- tools/variables/transform.go | 42 ++++ tools/variables/transform_test.go | 31 +++ tools/{types => variables}/types.go | 2 +- tools/variables/variables.go | 37 +++ 43 files changed, 405 insertions(+), 392 deletions(-) rename assets/{ => public}/css/main.min.css (100%) rename assets/{ => public}/fonts/FontAwesome.otf (100%) rename assets/{ => public}/fonts/fontawesome-webfont.eot (100%) rename assets/{ => public}/fonts/fontawesome-webfont.svg (100%) rename assets/{ => public}/fonts/fontawesome-webfont.ttf (100%) rename assets/{ => public}/fonts/fontawesome-webfont.woff (100%) rename assets/{ => public}/fonts/fontawesome-webfont.woff2 (100%) rename assets/{ => public}/js/app.min.js (100%) rename assets/{ => public}/js/plugins.min.js (100%) rename assets/{src => public_src}/css/main.css (100%) rename assets/{src => public_src}/js/app.js (100%) rename assets/{src => public_src}/js/browse.js (100%) rename assets/{src => public_src}/js/editor.js (100%) rename assets/{src => public_src}/js/notifications.js (100%) rename {templates => assets/templates}/404.tmpl (100%) rename {templates => assets/templates}/base_full.tmpl (100%) rename {templates => assets/templates}/base_minimal.tmpl (100%) rename {templates => assets/templates}/browse.tmpl (100%) rename {templates => assets/templates}/editor.tmpl (100%) rename {templates => assets/templates}/frontmatter.tmpl (100%) create mode 100644 config/installer.go create mode 100644 routes/assets/.gitkeep create mode 100644 tools/server/response.go rename tools/{utils/utils.go => server/url.go} (51%) create mode 100644 tools/variables/transform.go create mode 100644 tools/variables/transform_test.go rename tools/{types => variables}/types.go (94%) create mode 100644 tools/variables/variables.go diff --git a/Gruntfile.js b/Gruntfile.js index c3d357e0..999f215e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -8,11 +8,11 @@ module.exports = function(grunt) { grunt.initConfig({ watch: { sass: { - files: ['assets/src/css/**/*.css'], + files: ['assets/public_src/css/**/*.css'], tasks: ['concat', 'cssmin'] }, js: { - files: ['assets/src/js/**/*.js'], + files: ['assets/public_src/js/**/*.js'], tasks: ['uglify:main'] }, }, @@ -24,7 +24,7 @@ module.exports = function(grunt) { 'node_modules/animate.css/source/bouncing_entrances/bounceInRight.css', 'node_modules/animate.css/source/fading_entrances/fadeIn.css', 'node_modules/animate.css/source/fading_exits/fadeOut.css', - 'assets/src/css/main.css' + 'assets/public_src/css/main.css' ], dest: 'temp/css/main.css', }, @@ -35,7 +35,7 @@ module.exports = function(grunt) { expand: true, flatten: true, src: ['node_modules/font-awesome/fonts/**'], - dest: 'assets/fonts' + dest: 'assets/public/fonts' }], }, }, @@ -48,7 +48,7 @@ module.exports = function(grunt) { expand: true, cwd: 'temp/css/', src: ['*.css', '!*.min.css'], - dest: 'assets/css/', + dest: 'assets/public/css/', ext: '.min.css' }] } @@ -56,7 +56,7 @@ module.exports = function(grunt) { uglify: { plugins: { files: { - 'assets/js/plugins.min.js': ['node_modules/jquery/dist/jquery.min.js', + 'assets/public/js/plugins.min.js': ['node_modules/jquery/dist/jquery.min.js', 'node_modules/perfect-scrollbar/dist/js/min/perfect-scrollbar.jquery.min.js', 'node_modules/showdown/dist/showdown.min.js', 'node_modules/noty/js/noty/packaged/jquery.noty.packaged.min.js', @@ -67,7 +67,7 @@ module.exports = function(grunt) { }, main: { files: { - 'assets/js/app.min.js': ['assets/src/js/**/*.js'] + 'assets/public/js/app.min.js': ['assets/src/js/**/*.js'] } } } diff --git a/assets/css/main.min.css b/assets/public/css/main.min.css similarity index 100% rename from assets/css/main.min.css rename to assets/public/css/main.min.css diff --git a/assets/fonts/FontAwesome.otf b/assets/public/fonts/FontAwesome.otf similarity index 100% rename from assets/fonts/FontAwesome.otf rename to assets/public/fonts/FontAwesome.otf diff --git a/assets/fonts/fontawesome-webfont.eot b/assets/public/fonts/fontawesome-webfont.eot similarity index 100% rename from assets/fonts/fontawesome-webfont.eot rename to assets/public/fonts/fontawesome-webfont.eot diff --git a/assets/fonts/fontawesome-webfont.svg b/assets/public/fonts/fontawesome-webfont.svg similarity index 100% rename from assets/fonts/fontawesome-webfont.svg rename to assets/public/fonts/fontawesome-webfont.svg diff --git a/assets/fonts/fontawesome-webfont.ttf b/assets/public/fonts/fontawesome-webfont.ttf similarity index 100% rename from assets/fonts/fontawesome-webfont.ttf rename to assets/public/fonts/fontawesome-webfont.ttf diff --git a/assets/fonts/fontawesome-webfont.woff b/assets/public/fonts/fontawesome-webfont.woff similarity index 100% rename from assets/fonts/fontawesome-webfont.woff rename to assets/public/fonts/fontawesome-webfont.woff diff --git a/assets/fonts/fontawesome-webfont.woff2 b/assets/public/fonts/fontawesome-webfont.woff2 similarity index 100% rename from assets/fonts/fontawesome-webfont.woff2 rename to assets/public/fonts/fontawesome-webfont.woff2 diff --git a/assets/js/app.min.js b/assets/public/js/app.min.js similarity index 100% rename from assets/js/app.min.js rename to assets/public/js/app.min.js diff --git a/assets/js/plugins.min.js b/assets/public/js/plugins.min.js similarity index 100% rename from assets/js/plugins.min.js rename to assets/public/js/plugins.min.js diff --git a/assets/src/css/main.css b/assets/public_src/css/main.css similarity index 100% rename from assets/src/css/main.css rename to assets/public_src/css/main.css diff --git a/assets/src/js/app.js b/assets/public_src/js/app.js similarity index 100% rename from assets/src/js/app.js rename to assets/public_src/js/app.js diff --git a/assets/src/js/browse.js b/assets/public_src/js/browse.js similarity index 100% rename from assets/src/js/browse.js rename to assets/public_src/js/browse.js diff --git a/assets/src/js/editor.js b/assets/public_src/js/editor.js similarity index 100% rename from assets/src/js/editor.js rename to assets/public_src/js/editor.js diff --git a/assets/src/js/notifications.js b/assets/public_src/js/notifications.js similarity index 100% rename from assets/src/js/notifications.js rename to assets/public_src/js/notifications.js diff --git a/templates/404.tmpl b/assets/templates/404.tmpl similarity index 100% rename from templates/404.tmpl rename to assets/templates/404.tmpl diff --git a/templates/base_full.tmpl b/assets/templates/base_full.tmpl similarity index 100% rename from templates/base_full.tmpl rename to assets/templates/base_full.tmpl diff --git a/templates/base_minimal.tmpl b/assets/templates/base_minimal.tmpl similarity index 100% rename from templates/base_minimal.tmpl rename to assets/templates/base_minimal.tmpl diff --git a/templates/browse.tmpl b/assets/templates/browse.tmpl similarity index 100% rename from templates/browse.tmpl rename to assets/templates/browse.tmpl diff --git a/templates/editor.tmpl b/assets/templates/editor.tmpl similarity index 100% rename from templates/editor.tmpl rename to assets/templates/editor.tmpl diff --git a/templates/frontmatter.tmpl b/assets/templates/frontmatter.tmpl similarity index 100% rename from templates/frontmatter.tmpl rename to assets/templates/frontmatter.tmpl diff --git a/config/config.go b/config/config.go index eb8a3802..280622c1 100644 --- a/config/config.go +++ b/config/config.go @@ -5,7 +5,6 @@ import ( "path/filepath" "strings" - "github.com/hacdias/caddy-hugo/tools/hugo" "github.com/mholt/caddy/caddy/setup" ) @@ -27,7 +26,7 @@ func ParseHugo(c *setup.Controller) (*Config, error) { Git: false, } - conf.Hugo = hugo.GetPath() + conf.Hugo = GetPath() for c.Next() { args := c.RemainingArgs() diff --git a/config/installer.go b/config/installer.go new file mode 100644 index 00000000..3e08aa5e --- /dev/null +++ b/config/installer.go @@ -0,0 +1,211 @@ +package config + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "io/ioutil" + "log" + "net/http" + "os" + "os/exec" + "path/filepath" + "runtime" + + "github.com/hacdias/caddy-hugo/tools/files" + "github.com/mitchellh/go-homedir" + "github.com/pivotal-golang/archiver/extractor" +) + +const ( + version = "0.15" + baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/" +) + +var ( + caddy, bin, temp, hugo, tempfile, zipname, exename string + sha256Hash = map[string]string{ + "hugo_0.15_darwin_386.zip": "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2", + "hugo_0.15_darwin_amd64.zip": "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1", + "hugo_0.15_dragonfly_amd64.zip": "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc", + "hugo_0.15_freebsd_386.zip": "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4", + "hugo_0.15_freebsd_amd64.zip": "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100", + "hugo_0.15_freebsd_arm.zip": "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac", + "hugo_0.15_linux_386.tar.gz": "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf", + "hugo_0.15_linux_amd64.tar.gz": "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b", + "hugo_0.15_linux_arm.tar.gz": "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234", + "hugo_0.15_netbsd_386.zip": "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72", + "hugo_0.15_netbsd_amd64.zip": "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88", + "hugo_0.15_netbsd_arm.zip": "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a", + "hugo_0.15_openbsd_386.zip": "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1", + "hugo_0.15_openbsd_amd64.zip": "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87", + "hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756", + "hugo_0.15_windows_amd64.zip": "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727", + } +) + +// GetPath retrives the Hugo path for the user or install it if it's not found +func GetPath() string { + initializeVariables() + + var err error + + // Check if Hugo is already on $PATH + if hugo, err := exec.LookPath("hugo"); err == nil { + return hugo + } + + // Check if Hugo is on $HOME/.caddy/bin + if _, err = os.Stat(hugo); err == nil { + return hugo + } + + fmt.Println("Unable to find Hugo on your computer.") + + // Create the neccessary folders + os.MkdirAll(caddy, 0774) + os.Mkdir(bin, 0774) + + if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil { + fmt.Println(err) + os.Exit(-1) + } + + downloadHugo() + checkSHA256() + + fmt.Print("Unzipping... ") + + // Unzip or Ungzip the file + switch runtime.GOOS { + case "darwin", "windows": + zp := extractor.NewZip() + err = zp.Extract(tempfile, temp) + default: + gz := extractor.NewTgz() + err = gz.Extract(tempfile, temp) + } + + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + + fmt.Println("done.") + + var exetorename string + + err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error { + if f.Name() == exename { + exetorename = path + } + + return nil + }) + + // Copy the file + fmt.Print("Moving Hugo executable... ") + err = files.CopyFile(exetorename, hugo) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + + err = os.Chmod(hugo, 0755) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + + fmt.Println("done.") + fmt.Println("Hugo installed at " + hugo) + defer os.RemoveAll(temp) + return hugo +} + +func initializeVariables() { + exename = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH + zipname = exename + + homedir, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + + caddy = filepath.Join(homedir, ".caddy") + bin = filepath.Join(caddy, "bin") + hugo = filepath.Join(bin, "hugo") + + switch runtime.GOOS { + case "darwin": + zipname += ".zip" + case "windows": + // At least for v0.15 version + if runtime.GOARCH == "386" { + zipname += "32-bit-only" + } + + zipname += ".zip" + exename += ".exe" + hugo += ".exe" + default: + zipname += ".tar.gz" + } +} + +func downloadHugo() { + tempfile = filepath.Join(temp, zipname) + + fmt.Print("Downloading Hugo from GitHub releases... ") + + // Create the file + out, err := os.Create(tempfile) + out.Chmod(0774) + if err != nil { + defer os.RemoveAll(temp) + fmt.Println(err) + os.Exit(-1) + } + defer out.Close() + + // Get the data + resp, err := http.Get(baseurl + zipname) + if err != nil { + fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.") + fmt.Println(err) + os.Exit(-1) + } + defer resp.Body.Close() + + // Writer the body to file + _, err = io.Copy(out, resp.Body) + if err != nil { + fmt.Println(err) + os.Exit(-1) + } + + fmt.Println("downloaded.") +} + +func checkSHA256() { + fmt.Print("Checking SHA256...") + + hasher := sha256.New() + f, err := os.Open(tempfile) + if err != nil { + log.Fatal(err) + } + defer f.Close() + if _, err := io.Copy(hasher, f); err != nil { + log.Fatal(err) + } + + if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] { + fmt.Println("can't verify SHA256.") + os.Exit(-1) + } + + fmt.Println("checked!") +} diff --git a/hugo.go b/hugo.go index 4f3b0972..c1aee8d4 100644 --- a/hugo.go +++ b/hugo.go @@ -1,6 +1,6 @@ //go:generate go get github.com/jteeuwen/go-bindata //go:generate go install github.com/jteeuwen/go-bindata/go-bindata -//go:generate go-bindata -pkg assets -o assets/assets.go templates/ assets/css/ assets/js/ assets/fonts/ +//go:generate go-bindata -prefix assets/ -pkg assets -o routes/assets/assets.go assets/templates/ assets/public/... // Package hugo makes the bridge between the static website generator Hugo // and the webserver Caddy, also providing an administrative user interface. @@ -14,13 +14,14 @@ import ( "path/filepath" "strings" - "github.com/hacdias/caddy-hugo/assets" "github.com/hacdias/caddy-hugo/config" + "github.com/hacdias/caddy-hugo/routes/assets" "github.com/hacdias/caddy-hugo/routes/browse" "github.com/hacdias/caddy-hugo/routes/editor" "github.com/hacdias/caddy-hugo/routes/git" "github.com/hacdias/caddy-hugo/tools/commands" "github.com/hacdias/caddy-hugo/tools/hugo" + "github.com/hacdias/caddy-hugo/tools/server" "github.com/mholt/caddy/caddy/setup" "github.com/mholt/caddy/middleware" ) @@ -79,10 +80,10 @@ func (h CaddyHugo) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error // If the length of the components string is less than one, the variable // page will always be "admin" - if len(utils.ParseComponents(r)) > 1 { - page = utils.ParseComponents(r)[1] + if len(server.ParseURLComponents(r)) > 1 { + page = server.ParseURLComponents(r)[1] } else { - page = utils.ParseComponents(r)[0] + page = server.ParseURLComponents(r)[0] } // If the page isn't "assets" neither "edit", it should always put a @@ -145,7 +146,7 @@ func (h CaddyHugo) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error // Whenever the header "X-Regenerate" is true, the website should be // regenerated. Used in edit and settings, for example. if r.Header.Get("X-Regenerate") == "true" { - go utils.Run(h.Config, false) + go hugo.Run(h.Config, false) } if err != nil { diff --git a/routes/assets/.gitkeep b/routes/assets/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/routes/browse/delete.go b/routes/browse/delete.go index e88d3cfe..33627296 100644 --- a/routes/browse/delete.go +++ b/routes/browse/delete.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/hacdias/caddy-hugo/config" - "github.com/hacdias/caddy-hugo/tools/utils" + "github.com/hacdias/caddy-hugo/tools/server" ) // DELETE handles the delete requests on browse pages @@ -32,17 +32,17 @@ func DELETE(w http.ResponseWriter, r *http.Request, c *config.Config) (int, erro // Check for errors if err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, nil) } } else { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "File not found.", }, 404, nil) } - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": message, }, 200, nil) } diff --git a/routes/browse/get.go b/routes/browse/get.go index 1385b3ec..ef22cbcf 100644 --- a/routes/browse/get.go +++ b/routes/browse/get.go @@ -6,6 +6,7 @@ import ( "github.com/hacdias/caddy-hugo/config" "github.com/hacdias/caddy-hugo/tools/templates" + "github.com/hacdias/caddy-hugo/tools/variables" "github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware/browse" ) @@ -15,7 +16,7 @@ import ( func GET(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) { functions := template.FuncMap{ "CanBeEdited": templates.CanBeEdited, - "Defined": templates.Defined, + "Defined": variables.Defined, } tpl, err := templates.Get(r, functions, "browse") diff --git a/routes/browse/post.go b/routes/browse/post.go index 6240e40f..3a3440a6 100644 --- a/routes/browse/post.go +++ b/routes/browse/post.go @@ -12,7 +12,7 @@ import ( "github.com/hacdias/caddy-hugo/config" "github.com/hacdias/caddy-hugo/tools/commands" - "github.com/hacdias/caddy-hugo/tools/utils" + "github.com/hacdias/caddy-hugo/tools/server" ) // POST handles the POST method on browse page. It's used to create new files, @@ -37,13 +37,13 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) // Check if filename and archetype are specified in // the request if _, ok := info["filename"]; !ok { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Filename not specified.", }, 500, nil) } if _, ok := info["archetype"]; !ok { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Archtype not specified.", }, 500, nil) } @@ -67,7 +67,7 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) } if err := commands.Run(c.Hugo, args, c.Path); err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } @@ -84,14 +84,14 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) } if err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } } - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "location": url, "message": "File created.", }, 200, nil) @@ -101,7 +101,7 @@ func upload(w http.ResponseWriter, r *http.Request, c *config.Config) (int, erro // Parse the multipart form in the request err := r.ParseMultipartForm(100000) if err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } @@ -113,7 +113,7 @@ func upload(w http.ResponseWriter, r *http.Request, c *config.Config) (int, erro // Open the first file var infile multipart.File if infile, err = hdr.Open(); nil != err { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } @@ -121,14 +121,14 @@ func upload(w http.ResponseWriter, r *http.Request, c *config.Config) (int, erro // Create the file var outfile *os.File if outfile, err = os.Create(c.Path + r.URL.Path + hdr.Filename); nil != err { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } // Copy the file content if _, err = io.Copy(outfile, infile); nil != err { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } @@ -137,5 +137,5 @@ func upload(w http.ResponseWriter, r *http.Request, c *config.Config) (int, erro } } - return utils.RespondJSON(w, nil, 200, nil) + return server.RespondJSON(w, nil, 200, nil) } diff --git a/routes/browse/put.go b/routes/browse/put.go index 6a3311a3..22be9d0d 100644 --- a/routes/browse/put.go +++ b/routes/browse/put.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/hacdias/caddy-hugo/config" - "github.com/hacdias/caddy-hugo/tools/utils" + "github.com/hacdias/caddy-hugo/tools/server" ) // PUT handles the HTTP PUT request for all /admin/browse related requests. @@ -31,7 +31,7 @@ func PUT(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) // Check if filename and archetype are specified in // the request if _, ok := info["filename"]; !ok { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Filename not specified.", }, 400, nil) } @@ -44,12 +44,12 @@ func PUT(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) // Renames the file/folder if err := os.Rename(old, new); err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Something went wrong.", }, 500, err) } - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "File renamed.", }, 200, nil) } diff --git a/routes/editor/get.go b/routes/editor/get.go index 33df6751..88568930 100644 --- a/routes/editor/get.go +++ b/routes/editor/get.go @@ -13,6 +13,7 @@ import ( "github.com/hacdias/caddy-hugo/config" "github.com/hacdias/caddy-hugo/tools/frontmatter" "github.com/hacdias/caddy-hugo/tools/templates" + "github.com/hacdias/caddy-hugo/tools/variables" "github.com/spf13/hugo/parser" ) @@ -110,8 +111,8 @@ func GET(w http.ResponseWriter, r *http.Request, c *config.Config, filename stri // Create the functions map, then the template, check for erros and // execute the template if there aren't errors functions := template.FuncMap{ - "SplitCapitalize": templates.SplitCapitalize, - "Defined": templates.Defined, + "SplitCapitalize": variables.SplitCapitalize, + "Defined": variables.Defined, } tpl, err := templates.Get(r, functions, "editor", "frontmatter") diff --git a/routes/editor/post.go b/routes/editor/post.go index 7102a32a..74ff8779 100644 --- a/routes/editor/post.go +++ b/routes/editor/post.go @@ -11,7 +11,7 @@ import ( "time" "github.com/hacdias/caddy-hugo/config" - "github.com/hacdias/caddy-hugo/tools/commands" + "github.com/hacdias/caddy-hugo/tools/hugo" "github.com/robfig/cron" "github.com/spf13/cast" "github.com/spf13/hugo/parser" @@ -144,7 +144,7 @@ func parseCompleteFile(r *http.Request, c *config.Config, rawFile map[string]int return } - go commands.Run(c, false) + go hugo.Run(c, false) }) scheduler.Start() } diff --git a/routes/git/post.go b/routes/git/post.go index 259abb99..bccc332d 100644 --- a/routes/git/post.go +++ b/routes/git/post.go @@ -8,14 +8,14 @@ import ( "strings" "github.com/hacdias/caddy-hugo/config" - "github.com/hacdias/caddy-hugo/tools/utils" + "github.com/hacdias/caddy-hugo/tools/server" ) // POST handles the POST method on GIT page which is only an API. func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) { // Check if git is installed on the computer if _, err := exec.LookPath("git"); err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Git is not installed on your computer.", }, 400, nil) } @@ -30,7 +30,7 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) // Check if command was sent if _, ok := info["command"]; !ok { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Command not specified.", }, 400, nil) } @@ -43,7 +43,7 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) } if len(args) == 0 { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": "Command not specified.", }, 400, nil) } @@ -53,12 +53,12 @@ func POST(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) output, err := cmd.CombinedOutput() if err != nil { - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": err.Error(), }, 500, err) } - return utils.RespondJSON(w, map[string]string{ + return server.RespondJSON(w, map[string]string{ "message": string(output), }, 200, nil) } diff --git a/tools/commands/commands.go b/tools/commands/commands.go index 7e8056c8..2e8626d4 100644 --- a/tools/commands/commands.go +++ b/tools/commands/commands.go @@ -5,7 +5,7 @@ import ( "os/exec" ) -// RunCommand executes an external command +// Run executes an external command func Run(command string, args []string, path string) error { cmd := exec.Command(command, args...) cmd.Dir = path diff --git a/tools/frontmatter/frontmatter.go b/tools/frontmatter/frontmatter.go index c787c503..1e54c100 100644 --- a/tools/frontmatter/frontmatter.go +++ b/tools/frontmatter/frontmatter.go @@ -6,7 +6,7 @@ import ( "sort" "strings" - "github.com/hacdias/caddy-hugo/tools/types" + "github.com/hacdias/caddy-hugo/tools/variables" "github.com/spf13/cast" "github.com/spf13/hugo/parser" ) @@ -60,9 +60,9 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} { } for name, element := range cnf { - if types.IsMap(element) { + if variables.IsMap(element) { objects = append(objects, handleObjects(element, parent, name)) - } else if types.IsSlice(element) { + } else if variables.IsSlice(element) { arrays = append(arrays, handleArrays(element, parent, name)) } else { if name == "title" && parent.Name == mainName { diff --git a/tools/hugo/hugo.go b/tools/hugo/hugo.go index 966935cb..023afd05 100644 --- a/tools/hugo/hugo.go +++ b/tools/hugo/hugo.go @@ -1,48 +1,11 @@ package hugo import ( - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "io/ioutil" "log" - "net/http" "os" - "os/exec" - "path/filepath" - "runtime" "github.com/hacdias/caddy-hugo/config" - "github.com/mitchellh/go-homedir" - "github.com/pivotal-golang/archiver/extractor" -) - -const ( - version = "0.15" - baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/" -) - -var ( - caddy, bin, temp, hugo, tempfile, zipname, exename string - sha256Hash = map[string]string{ - "hugo_0.15_darwin_386.zip": "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2", - "hugo_0.15_darwin_amd64.zip": "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1", - "hugo_0.15_dragonfly_amd64.zip": "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc", - "hugo_0.15_freebsd_386.zip": "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4", - "hugo_0.15_freebsd_amd64.zip": "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100", - "hugo_0.15_freebsd_arm.zip": "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac", - "hugo_0.15_linux_386.tar.gz": "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf", - "hugo_0.15_linux_amd64.tar.gz": "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b", - "hugo_0.15_linux_arm.tar.gz": "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234", - "hugo_0.15_netbsd_386.zip": "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72", - "hugo_0.15_netbsd_amd64.zip": "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88", - "hugo_0.15_netbsd_arm.zip": "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a", - "hugo_0.15_openbsd_386.zip": "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1", - "hugo_0.15_openbsd_amd64.zip": "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87", - "hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756", - "hugo_0.15_windows_amd64.zip": "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727", - } + "github.com/hacdias/caddy-hugo/tools/commands" ) // Run is used to run the static website generator @@ -60,7 +23,7 @@ func Run(c *config.Config, force bool) { } } - if err := RunCommand(c.Hugo, c.Args, c.Path); err != nil { + if err := commands.Run(c.Hugo, c.Args, c.Path); err != nil { log.Panic(err) } } @@ -73,185 +36,3 @@ func stringInSlice(a string, list []string) (bool, int) { } return false, 0 } - -// GetPath retrives the Hugo path for the user or install it if it's not found -func GetPath() string { - initializeVariables() - - var err error - - // Check if Hugo is already on $PATH - if hugo, err := exec.LookPath("hugo"); err == nil { - return hugo - } - - // Check if Hugo is on $HOME/.caddy/bin - if _, err = os.Stat(hugo); err == nil { - return hugo - } - - fmt.Println("Unable to find Hugo on your computer.") - - // Create the neccessary folders - os.MkdirAll(caddy, 0774) - os.Mkdir(bin, 0774) - - if temp, err = ioutil.TempDir("", "caddy-hugo"); err != nil { - fmt.Println(err) - os.Exit(-1) - } - - downloadHugo() - checkSHA256() - - fmt.Print("Unzipping... ") - - // Unzip or Ungzip the file - switch runtime.GOOS { - case "darwin", "windows": - zp := extractor.NewZip() - err = zp.Extract(tempfile, temp) - default: - gz := extractor.NewTgz() - err = gz.Extract(tempfile, temp) - } - - if err != nil { - fmt.Println(err) - os.Exit(-1) - } - - fmt.Println("done.") - - var exetorename string - - err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error { - if f.Name() == exename { - exetorename = path - } - - return nil - }) - - // Copy the file - fmt.Print("Moving Hugo executable... ") - r, err := os.Open(exetorename) - if err != nil { - panic(err) - } - defer r.Close() - - w, err := os.Create(hugo) - if err != nil { - panic(err) - } - defer w.Close() - - _, err = io.Copy(w, r) - if err != nil { - panic(err) - } - - if err != nil { - fmt.Println(err) - os.Exit(-1) - } - - err = os.Chmod(hugo, 0755) - - if err != nil { - fmt.Println(err) - os.Exit(-1) - } - - fmt.Println("done.") - fmt.Println("Hugo installed at " + hugo) - defer os.RemoveAll(temp) - return hugo -} - -func initializeVariables() { - exename = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH - zipname = exename - - homedir, err := homedir.Dir() - if err != nil { - fmt.Println(err) - os.Exit(-1) - } - - caddy = filepath.Join(homedir, ".caddy") - bin = filepath.Join(caddy, "bin") - hugo = filepath.Join(bin, "hugo") - - switch runtime.GOOS { - case "darwin": - zipname += ".zip" - case "windows": - // At least for v0.15 version - if runtime.GOARCH == "386" { - zipname += "32-bit-only" - } - - zipname += ".zip" - exename += ".exe" - hugo += ".exe" - default: - zipname += ".tar.gz" - } -} - -func downloadHugo() { - tempfile = filepath.Join(temp, zipname) - - fmt.Print("Downloading Hugo from GitHub releases... ") - - // Create the file - out, err := os.Create(tempfile) - out.Chmod(0774) - if err != nil { - defer os.RemoveAll(temp) - fmt.Println(err) - os.Exit(-1) - } - defer out.Close() - - // Get the data - resp, err := http.Get(baseurl + zipname) - if err != nil { - fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.") - fmt.Println(err) - os.Exit(-1) - } - defer resp.Body.Close() - - // Writer the body to file - _, err = io.Copy(out, resp.Body) - if err != nil { - fmt.Println(err) - os.Exit(-1) - } - - fmt.Println("downloaded.") -} - -func checkSHA256() { - fmt.Print("Checking SHA256...") - - hasher := sha256.New() - f, err := os.Open(tempfile) - if err != nil { - log.Fatal(err) - } - defer f.Close() - if _, err := io.Copy(hasher, f); err != nil { - log.Fatal(err) - } - - if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] { - fmt.Println("can't verify SHA256.") - os.Exit(-1) - } - - fmt.Println("checked!") -} diff --git a/tools/server/response.go b/tools/server/response.go new file mode 100644 index 00000000..cc440deb --- /dev/null +++ b/tools/server/response.go @@ -0,0 +1,25 @@ +package server + +import ( + "encoding/json" + "errors" + "net/http" +) + +// RespondJSON +func RespondJSON(w http.ResponseWriter, message map[string]string, code int, err error) (int, error) { + msg, msgErr := json.Marshal(message) + + if msgErr != nil { + return 500, msgErr + } + + if code == 500 && err != nil { + err = errors.New(message["message"]) + } + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(code) + w.Write(msg) + return 0, err +} diff --git a/tools/utils/utils.go b/tools/server/url.go similarity index 51% rename from tools/utils/utils.go rename to tools/server/url.go index eadc6014..e88d8b04 100644 --- a/tools/utils/utils.go +++ b/tools/server/url.go @@ -1,14 +1,12 @@ -package utils +package server import ( - "encoding/json" - "errors" "net/http" "strings" ) -// ParseComponents parses the components of an URL creating an array -func ParseComponents(r *http.Request) []string { +// ParseURLComponents parses the components of an URL creating an array +func ParseURLComponents(r *http.Request) []string { //The URL that the user queried. path := r.URL.Path path = strings.TrimSpace(path) @@ -26,20 +24,3 @@ func ParseComponents(r *http.Request) []string { components := strings.Split(path, "/") return components } - -func RespondJSON(w http.ResponseWriter, message map[string]string, code int, err error) (int, error) { - msg, msgErr := json.Marshal(message) - - if msgErr != nil { - return 500, msgErr - } - - if code == 500 && err != nil { - err = errors.New(message["message"]) - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(code) - w.Write(msg) - return 0, err -} diff --git a/tools/templates/templates.go b/tools/templates/templates.go index 18f9b309..d8d09ceb 100644 --- a/tools/templates/templates.go +++ b/tools/templates/templates.go @@ -1,15 +1,12 @@ package templates import ( - "errors" - "html/template" "log" "net/http" - "reflect" "strings" - "unicode" + "text/template" - "github.com/hacdias/caddy-hugo/assets" + "github.com/hacdias/caddy-hugo/routes/assets" ) // CanBeEdited checks if the extension of a file is supported by the editor @@ -34,36 +31,6 @@ func CanBeEdited(filename string) bool { return false } -// Defined checks if variable is defined in a struct -func Defined(data interface{}, field string) bool { - t := reflect.Indirect(reflect.ValueOf(data)).Type() - - if t.Kind() != reflect.Struct { - log.Print("Non-struct type not allowed.") - return false - } - - _, b := t.FieldByName(field) - return b -} - -// Dict allows to send more than one variable into a template -func Dict(values ...interface{}) (map[string]interface{}, error) { - if len(values)%2 != 0 { - return nil, errors.New("invalid dict call") - } - dict := make(map[string]interface{}, len(values)/2) - for i := 0; i < len(values); i += 2 { - key, ok := values[i].(string) - if !ok { - return nil, errors.New("dict keys must be strings") - } - dict[key] = values[i+1] - } - - return dict, nil -} - // Get is used to get a ready to use template based on the url and on // other sent templates func Get(r *http.Request, functions template.FuncMap, templates ...string) (*template.Template, error) { @@ -104,39 +71,3 @@ func Get(r *http.Request, functions template.FuncMap, templates ...string) (*tem return tpl, nil } - -var splitCapitalizeExceptions = map[string]string{ - "youtube": "YouTube", - "github": "GitHub", - "googleplus": "Google Plus", - "linkedin": "LinkedIn", -} - -// SplitCapitalize splits a string by its uppercase letters and capitalize the -// first letter of the string -func SplitCapitalize(name string) string { - if val, ok := splitCapitalizeExceptions[strings.ToLower(name)]; ok { - return val - } - - var words []string - l := 0 - for s := name; s != ""; s = s[l:] { - l = strings.IndexFunc(s[1:], unicode.IsUpper) + 1 - if l <= 0 { - l = len(s) - } - words = append(words, s[:l]) - } - - name = "" - - for _, element := range words { - name += element + " " - } - - name = strings.ToLower(name[:len(name)-1]) - name = strings.ToUpper(string(name[0])) + name[1:] - - return name -} diff --git a/tools/templates/templates_test.go b/tools/templates/templates_test.go index a9173669..f1363c3b 100644 --- a/tools/templates/templates_test.go +++ b/tools/templates/templates_test.go @@ -75,31 +75,3 @@ func TestDefined(t *testing.T) { } } } - -type testSplitCapitalize struct { - name string - result string -} - -var testSplitCapitalizeCases = []testSplitCapitalize{ - {"loremIpsum", "Lorem ipsum"}, - {"LoremIpsum", "Lorem ipsum"}, - {"loremipsum", "Loremipsum"}, - {"YouTube", "YouTube"}, - {"GitHub", "GitHub"}, - {"GooglePlus", "Google Plus"}, - {"Facebook", "Facebook"}, -} - -func TestSplitCapitalize(t *testing.T) { - for _, pair := range testSplitCapitalizeCases { - v := SplitCapitalize(pair.name) - if v != pair.result { - t.Error( - "For", pair.name, - "expected", pair.result, - "got", v, - ) - } - } -} diff --git a/tools/variables/transform.go b/tools/variables/transform.go new file mode 100644 index 00000000..3307b7a3 --- /dev/null +++ b/tools/variables/transform.go @@ -0,0 +1,42 @@ +package variables + +import ( + "strings" + "unicode" +) + +var splitCapitalizeExceptions = map[string]string{ + "youtube": "YouTube", + "github": "GitHub", + "googleplus": "Google Plus", + "linkedin": "LinkedIn", +} + +// SplitCapitalize splits a string by its uppercase letters and capitalize the +// first letter of the string +func SplitCapitalize(name string) string { + if val, ok := splitCapitalizeExceptions[strings.ToLower(name)]; ok { + return val + } + + var words []string + l := 0 + for s := name; s != ""; s = s[l:] { + l = strings.IndexFunc(s[1:], unicode.IsUpper) + 1 + if l <= 0 { + l = len(s) + } + words = append(words, s[:l]) + } + + name = "" + + for _, element := range words { + name += element + " " + } + + name = strings.ToLower(name[:len(name)-1]) + name = strings.ToUpper(string(name[0])) + name[1:] + + return name +} diff --git a/tools/variables/transform_test.go b/tools/variables/transform_test.go new file mode 100644 index 00000000..1a0c2fbf --- /dev/null +++ b/tools/variables/transform_test.go @@ -0,0 +1,31 @@ +package variables + +import "testing" + +type testSplitCapitalize struct { + name string + result string +} + +var testSplitCapitalizeCases = []testSplitCapitalize{ + {"loremIpsum", "Lorem ipsum"}, + {"LoremIpsum", "Lorem ipsum"}, + {"loremipsum", "Loremipsum"}, + {"YouTube", "YouTube"}, + {"GitHub", "GitHub"}, + {"GooglePlus", "Google Plus"}, + {"Facebook", "Facebook"}, +} + +func TestSplitCapitalize(t *testing.T) { + for _, pair := range testSplitCapitalizeCases { + v := SplitCapitalize(pair.name) + if v != pair.result { + t.Error( + "For", pair.name, + "expected", pair.result, + "got", v, + ) + } + } +} diff --git a/tools/types/types.go b/tools/variables/types.go similarity index 94% rename from tools/types/types.go rename to tools/variables/types.go index 2de62cf5..ee43dad3 100644 --- a/tools/types/types.go +++ b/tools/variables/types.go @@ -1,4 +1,4 @@ -package types +package variables import "reflect" diff --git a/tools/variables/variables.go b/tools/variables/variables.go new file mode 100644 index 00000000..9d60dabf --- /dev/null +++ b/tools/variables/variables.go @@ -0,0 +1,37 @@ +package variables + +import ( + "errors" + "log" + "reflect" +) + +// Defined checks if variable is defined in a struct +func Defined(data interface{}, field string) bool { + t := reflect.Indirect(reflect.ValueOf(data)).Type() + + if t.Kind() != reflect.Struct { + log.Print("Non-struct type not allowed.") + return false + } + + _, b := t.FieldByName(field) + return b +} + +// Dict allows to send more than one variable into a template +func Dict(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, errors.New("invalid dict call") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, errors.New("dict keys must be strings") + } + dict[key] = values[i+1] + } + + return dict, nil +}