diff --git a/filenames/filenames.go b/filenames/filenames.go index a171d781..8bf8e001 100644 --- a/filenames/filenames.go +++ b/filenames/filenames.go @@ -9,29 +9,31 @@ 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 assets folder (default: Journey root folder) + AssetPath = 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(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(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(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("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 +52,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 +71,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 contentPath +} + +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 d9567b04..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. 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 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 457a662f..ad4539c6 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": @@ -98,15 +110,24 @@ 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) // 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 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 " + configuration.Config.HttpHostAndPort + "...") - http.ListenAndServe(configuration.Config.HttpHostAndPort, httpRouter) + log.Println("Starting http server on port " + httpPort + "...") + err := http.ListenAndServe(httpPort, httpRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTP server:", err) + } case "All": checkHttpsCertificates() httpsRouter := httptreemux.New() @@ -120,11 +141,19 @@ 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 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 " + configuration.Config.HttpHostAndPort + "...") - http.ListenAndServe(configuration.Config.HttpHostAndPort, httpRouter) + log.Println("Starting http server on port " + httpPort + "...") + 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 @@ -134,7 +163,10 @@ 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 + "...") + err := http.ListenAndServe(httpPort, httpRouter) + if err != nil { + log.Fatal("Error: Couldn't start the HTTP server:", err) + } } } 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 {