From 8730a3c04883c49b7b62718f78bf7edaa65e635c Mon Sep 17 00:00:00 2001 From: Kai H Date: Thu, 7 May 2015 23:30:17 +0200 Subject: [PATCH 1/5] Fixed: Possible duplicate user name or slug. --- server/admin.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/admin.go b/server/admin.go index 99db4032..ab80f4fd 100644 --- a/server/admin.go +++ b/server/admin.go @@ -652,6 +652,24 @@ func patchApiUserHandler(w http.ResponseWriter, r *http.Request, _ map[string]st if json.Slug == "" { json.Slug = tempUser.Slug } + // Check if new name is already taken + if json.Name != string(tempUser.Name) { + _, err = database.RetrieveUserByName([]byte(json.Name)) + if err == nil { + // The new user name is already taken. Assign the old name. + // TODO: Return error that will be displayed in the admin interface. + json.Name = string(tempUser.Name) + } + } + // Check if new slug is already taken + if json.Slug != tempUser.Slug { + _, err = database.RetrieveUserBySlug(json.Slug) + if err == nil { + // The new user slug is already taken. Assign the old slug. + // TODO: Return error that will be displayed in the admin interface. + json.Slug = tempUser.Slug + } + } user := structure.User{Id: json.Id, Name: []byte(json.Name), Slug: json.Slug, Email: []byte(json.Email), Image: []byte(json.Image), Cover: []byte(json.Cover), Bio: []byte(json.Bio), Website: []byte(json.Website), Location: []byte(json.Location)} err = methods.UpdateUser(&user, userId) if err != nil { From 447057ea64965dfe1b16bca49f82fe8f2ad9a198 Mon Sep 17 00:00:00 2001 From: Kai H Date: Sat, 9 May 2015 15:56:57 +0200 Subject: [PATCH 2/5] Updated flags help text. --- flags/flags.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flags/flags.go b/flags/flags.go index d9567b04..8124d7b1 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -20,7 +20,7 @@ func init() { func parseFlags() { // Check if a custom content path has been provided by the user - flag.StringVar(&CustomPath, "custom-path", "", "Specify a custom path to store content files. Note: Journey needs read and write access to that path. Example: -custom-path=/absolute/path/to/custom/folder") - flag.BoolVar(&IsInDevMode, "dev", false, "Set this flag to put Journey in developer mode. Features of developer mode: Themes will be recompiled immediately after changes to the files.") + flag.StringVar(&CustomPath, "custom-path", "", "Specify a custom path to store content files. Note: Journey needs read and write access to that path. A theme folder needs to be present in the custon path under content/themes. Example: -custom-path=/absolute/path/to/custom/folder") + flag.BoolVar(&IsInDevMode, "dev", false, "Set this flag to put Journey in developer mode. Features of developer mode: Themes and plugins will be recompiled immediately after changes to the files. The log is output to the console instead of log.txt") flag.Parse() } From 162289eaf50d89795468931785eccdd421aa60da Mon Sep 17 00:00:00 2001 From: Kai H Date: Mon, 11 May 2015 13:53:45 +0200 Subject: [PATCH 3/5] HTTPS redirection to admin interface when using "HttpsUsage":"AdminOnly" --- main.go | 1 + 1 file changed, 1 insertion(+) diff --git a/main.go b/main.go index 457a662f..d642ccfc 100644 --- a/main.go +++ b/main.go @@ -98,6 +98,7 @@ func main() { server.InitializePages(httpsRouter) // Admin as https and http redirect // Add redirection to http router + httpRouter.GET("/admin/", httpsRedirect) httpRouter.GET("/admin/*path", httpsRedirect) // Add routes to https router server.InitializeAdmin(httpsRouter) From 7bf7027f494a862b6b137e38759af9139dd13fba Mon Sep 17 00:00:00 2001 From: Kai H Date: Tue, 12 May 2015 14:04:41 +0200 Subject: [PATCH 4/5] Added: Flag to set the log file output and location. Added: Flag to override the HTTP/HTTPS ports. Changed: More in the description. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed: The log is now output to the console by default. To output it to a file, the “-log” command line option needs to be used. - Changed: The working directory is no longer automatically set to the Journey directory. --- filenames/filenames.go | 57 ++++++++++++++++++++++++------------------ flags/flags.go | 14 +++++++++-- main.go | 44 ++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/filenames/filenames.go b/filenames/filenames.go index a171d781..3aeaea9a 100644 --- a/filenames/filenames.go +++ b/filenames/filenames.go @@ -9,29 +9,30 @@ import ( ) var ( - // Initialization of the working directory - needed to load relative assets - _ = initializeWorkingDirectory() + // Determine the path the Journey executable is in - needed to load relative assets + ExecutablePath = determineExecutablePath() + + // Determine the path the the content folder + ContentPath = determineContentPath() // For assets that are created, changed, our user-provided while running journey - ConfigFilename = filepath.Join(flags.CustomPath, "config.json") - LogFilename = filepath.Join(flags.CustomPath, "log.txt") - DatabaseFilepath = filepath.Join(flags.CustomPath, "content", "data") - DatabaseFilename = filepath.Join(flags.CustomPath, "content", "data", "journey.db") - ThemesFilepath = filepath.Join(flags.CustomPath, "content", "themes") - ImagesFilepath = filepath.Join(flags.CustomPath, "content", "images") - ContentFilepath = filepath.Join(flags.CustomPath, "content") - PluginsFilepath = filepath.Join(flags.CustomPath, "content", "plugins") - PagesFilepath = filepath.Join(flags.CustomPath, "content", "pages") + ConfigFilename = filepath.Join(ExecutablePath, "config.json") + DatabaseFilepath = filepath.Join(ContentPath, "data") + DatabaseFilename = filepath.Join(ContentPath, "data", "journey.db") + ThemesFilepath = filepath.Join(ContentPath, "themes") + ImagesFilepath = filepath.Join(ContentPath, "images") + PluginsFilepath = filepath.Join(ContentPath, "plugins") + PagesFilepath = filepath.Join(ContentPath, "pages") // For https - HttpsFilepath = filepath.Join(flags.CustomPath, "content", "https") - HttpsCertFilename = filepath.Join(flags.CustomPath, "content", "https", "cert.pem") - HttpsKeyFilename = filepath.Join(flags.CustomPath, "content", "https", "key.pem") + HttpsFilepath = filepath.Join(ContentPath, "https") + HttpsCertFilename = filepath.Join(ContentPath, "https", "cert.pem") + HttpsKeyFilename = filepath.Join(ContentPath, "https", "key.pem") //For built-in files (e.g. the admin interface) - AdminFilepath = filepath.Join("built-in", "admin") - PublicFilepath = filepath.Join("built-in", "public") - HbsFilepath = filepath.Join("built-in", "hbs") + AdminFilepath = filepath.Join(ExecutablePath, "built-in", "admin") + PublicFilepath = filepath.Join(ExecutablePath, "built-in", "public") + HbsFilepath = filepath.Join(ExecutablePath, "built-in", "hbs") // For handlebars (this is a url string) JqueryFilename = "/public/jquery/jquery.js" @@ -50,7 +51,7 @@ func init() { // Create content directories if they are not created already err := createDirectories() if err != nil { - log.Fatalln("Error: Couldn't create directories: " + err.Error()) + log.Fatal("Error: Couldn't create directories:", err) } } @@ -69,13 +70,21 @@ func createDirectories() error { return nil } -func initializeWorkingDirectory() error { - // Set working directory to the path this executable is in +func determineContentPath() string { + contentPath := "" + if flags.CustomPath != "" { + contentPath = flags.CustomPath + } else { + contentPath = determineExecutablePath() + } + return filepath.Join(contentPath, "content") +} + +func determineExecutablePath() string { + // Get the path this executable is located in executablePath, err := osext.ExecutableFolder() if err != nil { - log.Fatal("Error: Couldn't determine working directory: " + err.Error()) - return err + log.Fatal("Error: Couldn't determine what directory this executable is in:", err) } - os.Chdir(executablePath) - return nil + return executablePath } diff --git a/flags/flags.go b/flags/flags.go index 8124d7b1..2badc4e9 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -6,8 +6,11 @@ import ( ) var ( + Log = "" CustomPath = "" IsInDevMode = false + HttpPort = "" + HttpsPort = "" ) func init() { @@ -19,8 +22,15 @@ func init() { } func parseFlags() { + // Check if the log should be output to a file + flag.StringVar(&Log, "log", "", "Use this option to save to log output to a file. Note: Journey needs create, read, and write access to that file. Example: -log=path/to/log.txt") // Check if a custom content path has been provided by the user - flag.StringVar(&CustomPath, "custom-path", "", "Specify a custom path to store content files. Note: Journey needs read and write access to that path. A theme folder needs to be present in the custon path under content/themes. Example: -custom-path=/absolute/path/to/custom/folder") - flag.BoolVar(&IsInDevMode, "dev", false, "Set this flag to put Journey in developer mode. Features of developer mode: Themes and plugins will be recompiled immediately after changes to the files. The log is output to the console instead of log.txt") + flag.StringVar(&CustomPath, "custom-path", "", "Specify a custom path to store content files. Note: Journey needs read and write access to that path. A theme folder needs to be located in the custon path under content/themes. Example: -custom-path=/absolute/path/to/custom/folder") + // Check if the dvelopment mode flag was provided by the user + flag.BoolVar(&IsInDevMode, "dev", false, "Use this flag flag to put Journey in developer mode. Features of developer mode: Themes and plugins will be recompiled immediately after changes to the files. Example: -dev") + // Check if the http port that was set in the config was overridden by the user + flag.StringVar(&HttpPort, "http-port", "", "Use this option to override the HTTP port that was set in the config.json. Example: -http-port=8080") + // Check if the http port that was set in the config was overridden by the user + flag.StringVar(&HttpsPort, "https-port", "", "Use this option to override the HTTPS port that was set in the config.json. Example: -https-port=8081") flag.Parse() } diff --git a/main.go b/main.go index d642ccfc..d2bee16a 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ import ( "net/http" "os" "runtime" + "strings" ) func httpsRedirect(w http.ResponseWriter, r *http.Request, _ map[string]string) { @@ -42,9 +43,9 @@ func main() { // GOMAXPROCS - Maybe not needed runtime.GOMAXPROCS(runtime.NumCPU()) - // Write log to file if Journey is not in dev mode - if !flags.IsInDevMode { - logFile, err := os.OpenFile(filenames.LogFilename, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) + // Write log to file if the log flag was provided + if flags.Log != "" { + logFile, err := os.OpenFile(flags.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatal("Error: Couldn't open log file: " + err.Error()) } @@ -57,21 +58,21 @@ func main() { // Database err = database.Initialize() if err != nil { - log.Fatal("Error: Couldn't initialize database: " + err.Error()) + log.Fatal("Error: Couldn't initialize database:", err) return } // Global blog data err = methods.GenerateBlog() if err != nil { - log.Fatal("Error: Couldn't generate blog data: " + err.Error()) + log.Fatal("Error: Couldn't generate blog data:", err) return } // Templates err = templates.Generate() if err != nil { - log.Fatal("Error: Couldn't compile templates: " + err.Error()) + log.Fatal("Error: Couldn't compile templates:", err) return } @@ -84,6 +85,17 @@ func main() { } // HTTP(S) Server + httpPort := configuration.Config.HttpHostAndPort + httpsPort := configuration.Config.HttpsHostAndPort + // Check if HTTP/HTTPS flags were provided + if flags.HttpPort != "" { + components := strings.SplitAfterN(httpPort, ":", 2) + httpPort = components[0] + flags.HttpPort + } + if flags.HttpsPort != "" { + components := strings.SplitAfterN(httpsPort, ":", 2) + httpsPort = components[0] + flags.HttpsPort + } // Determine the kind of https support (as set in the config.json) switch configuration.Config.HttpsUsage { case "AdminOnly": @@ -103,11 +115,11 @@ func main() { // Add routes to https router server.InitializeAdmin(httpsRouter) // Start https server - log.Println("Starting https server on port " + configuration.Config.HttpsHostAndPort + "...") - go http.ListenAndServeTLS(configuration.Config.HttpsHostAndPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + log.Println("Starting https server on port " + httpsPort + "...") + go http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) // Start http server - log.Println("Starting http server on port " + configuration.Config.HttpHostAndPort + "...") - http.ListenAndServe(configuration.Config.HttpHostAndPort, httpRouter) + log.Println("Starting http server on port " + httpPort + "...") + http.ListenAndServe(httpPort, httpRouter) case "All": checkHttpsCertificates() httpsRouter := httptreemux.New() @@ -121,11 +133,11 @@ func main() { httpRouter.GET("/", httpsRedirect) httpRouter.GET("/*path", httpsRedirect) // Start https server - log.Println("Starting https server on port " + configuration.Config.HttpsHostAndPort + "...") - go http.ListenAndServeTLS(configuration.Config.HttpsHostAndPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + log.Println("Starting https server on port " + httpsPort + "...") + go http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) // Start http server - log.Println("Starting http server on port " + configuration.Config.HttpHostAndPort + "...") - http.ListenAndServe(configuration.Config.HttpHostAndPort, httpRouter) + log.Println("Starting http server on port " + httpPort + "...") + http.ListenAndServe(httpPort, httpRouter) default: // This is configuration.HttpsUsage == "None" httpRouter := httptreemux.New() // Blog and pages as http @@ -135,7 +147,7 @@ func main() { server.InitializeAdmin(httpRouter) // Start http server log.Println("Starting server without HTTPS support. Please enable HTTPS in " + filenames.ConfigFilename + " to improve security.") - log.Println("Starting http server on port " + configuration.Config.HttpHostAndPort + "...") - http.ListenAndServe(configuration.Config.HttpHostAndPort, httpRouter) + log.Println("Starting http server on port " + httpPort + "...") + http.ListenAndServe(httpPort, httpRouter) } } From 48983c9d72eb7d45487c697d202997c687a14c7b Mon Sep 17 00:00:00 2001 From: Kai H Date: Tue, 12 May 2015 14:25:54 +0200 Subject: [PATCH 5/5] Added: Errors while starting the HTTP/HTTPS server are now logged. --- filenames/filenames.go | 27 ++++++++++++++------------- main.go | 29 ++++++++++++++++++++++++----- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/filenames/filenames.go b/filenames/filenames.go index 3aeaea9a..8bf8e001 100644 --- a/filenames/filenames.go +++ b/filenames/filenames.go @@ -12,22 +12,23 @@ var ( // Determine the path the Journey executable is in - needed to load relative assets ExecutablePath = determineExecutablePath() - // Determine the path the the content folder - ContentPath = determineContentPath() + // Determine the path the the assets folder (default: Journey root folder) + AssetPath = determineContentPath() // For assets that are created, changed, our user-provided while running journey - ConfigFilename = filepath.Join(ExecutablePath, "config.json") - DatabaseFilepath = filepath.Join(ContentPath, "data") - DatabaseFilename = filepath.Join(ContentPath, "data", "journey.db") - ThemesFilepath = filepath.Join(ContentPath, "themes") - ImagesFilepath = filepath.Join(ContentPath, "images") - PluginsFilepath = filepath.Join(ContentPath, "plugins") - PagesFilepath = filepath.Join(ContentPath, "pages") + ConfigFilename = filepath.Join(AssetPath, "config.json") + ContentFilepath = filepath.Join(AssetPath, "content") + DatabaseFilepath = filepath.Join(ContentFilepath, "data") + DatabaseFilename = filepath.Join(ContentFilepath, "data", "journey.db") + ThemesFilepath = filepath.Join(ContentFilepath, "themes") + ImagesFilepath = filepath.Join(ContentFilepath, "images") + PluginsFilepath = filepath.Join(ContentFilepath, "plugins") + PagesFilepath = filepath.Join(ContentFilepath, "pages") // For https - HttpsFilepath = filepath.Join(ContentPath, "https") - HttpsCertFilename = filepath.Join(ContentPath, "https", "cert.pem") - HttpsKeyFilename = filepath.Join(ContentPath, "https", "key.pem") + HttpsFilepath = filepath.Join(ContentFilepath, "https") + HttpsCertFilename = filepath.Join(ContentFilepath, "https", "cert.pem") + HttpsKeyFilename = filepath.Join(ContentFilepath, "https", "key.pem") //For built-in files (e.g. the admin interface) AdminFilepath = filepath.Join(ExecutablePath, "built-in", "admin") @@ -77,7 +78,7 @@ func determineContentPath() string { } else { contentPath = determineExecutablePath() } - return filepath.Join(contentPath, "content") + return contentPath } func determineExecutablePath() string { diff --git a/main.go b/main.go index d2bee16a..ad4539c6 100644 --- a/main.go +++ b/main.go @@ -116,10 +116,18 @@ func main() { server.InitializeAdmin(httpsRouter) // Start https server log.Println("Starting https server on port " + httpsPort + "...") - go http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + go func() { + err := http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTPS server:", err) + } + }() // Start http server log.Println("Starting http server on port " + httpPort + "...") - http.ListenAndServe(httpPort, httpRouter) + err := http.ListenAndServe(httpPort, httpRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTP server:", err) + } case "All": checkHttpsCertificates() httpsRouter := httptreemux.New() @@ -134,10 +142,18 @@ func main() { httpRouter.GET("/*path", httpsRedirect) // Start https server log.Println("Starting https server on port " + httpsPort + "...") - go http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + go func() { + err := http.ListenAndServeTLS(httpsPort, filenames.HttpsCertFilename, filenames.HttpsKeyFilename, httpsRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTPS server:", err) + } + }() // Start http server log.Println("Starting http server on port " + httpPort + "...") - http.ListenAndServe(httpPort, httpRouter) + err := http.ListenAndServe(httpPort, httpRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTP server:", err) + } default: // This is configuration.HttpsUsage == "None" httpRouter := httptreemux.New() // Blog and pages as http @@ -148,6 +164,9 @@ func main() { // Start http server log.Println("Starting server without HTTPS support. Please enable HTTPS in " + filenames.ConfigFilename + " to improve security.") log.Println("Starting http server on port " + httpPort + "...") - http.ListenAndServe(httpPort, httpRouter) + err := http.ListenAndServe(httpPort, httpRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTP server:", err) + } } }