diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/greenlight.iml b/.idea/greenlight.iml new file mode 100644 index 0000000..25ed3f6 --- /dev/null +++ b/.idea/greenlight.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..31e1ebc --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..475c31e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/cmd/api/healthcheck.go b/cmd/api/healthcheck.go new file mode 100644 index 0000000..c96a662 --- /dev/null +++ b/cmd/api/healthcheck.go @@ -0,0 +1,23 @@ +package main + +import ( + "fmt" + "net/http" +) + +/* +Declare a handler which writes a plain-text response with information about the +application status, operating environment and version +*/ +/* +Tips: The important thing to point out here is that healthcheckHandler is implemented as a method +on our application struct. This is an effective and idiomatic way to make dependencies available +to our handlers without resorting to global variables or closures - any dependency that the +healthcheckHandler needs can simply be included as a field in the application struct when we +initialize it in main() +*/ +func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "status: available") + fmt.Fprintf(w, "environment: %s\n", app.config.env) + fmt.Fprintf(w, "version: %s\n", version) +} diff --git a/cmd/api/main.go b/cmd/api/main.go new file mode 100644 index 0000000..ad26af5 --- /dev/null +++ b/cmd/api/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "flag" + "fmt" + "log/slog" + "net/http" + "os" + "time" +) + +/* +Declare a string containing the application version number. +Later we'll generate this automatically at build time, but for now we'll just store the +version number as a hard-coded global constant. +*/ +const version = "1.0.0" + +/* +Define a config struct to hold all the configuration settings for our application. +For now, the only configuration settings will be the network port that we want the server to listen on, +and the name of the current operating environment for the application (development, staging, production, etc.) +We'll read in the configuration settings from command-line flags when the application starts. +*/ +type config struct { + port int + env string +} + +/* +Define an application struct to hold the dependencies for our HTTP handlers, helpers, +and middleware. +*/ +type application struct { + config config + logger *slog.Logger +} + +func main() { + // Declare an instance of the config struct. + var cfg config + + /* + Read the value of the port and env command-line flags into the config struct. + We default to using the port number 4000 and the environment "development" if no corresponding flags are provided. + */ + flag.IntVar(&cfg.port, "port", 4000, "API server port") + flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production") + flag.Parse() + + // Initialize a new structured logger which writes log entries to standard out stream. + logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + + // Declare an instance of the application struct, containing the config struct and the logger. + app := &application{ + config: cfg, + logger: logger, + } + + /* + Declare a new servemux and add a /v1/healthcheck route which dispatches requests + to the healthcheckHandler method. + */ + mux := http.NewServeMux() + mux.HandleFunc("/v1/healthcheck", app.healthcheckHandler) + + /* + Declare a HTTP server which listens on the port provided in the config struct, + uses the servemux we created above as the handler, has some sensible timeout + settings and writes any log messages to the structured logger at Error level. + */ + srv := &http.Server{ + Addr: fmt.Sprintf(":%d", cfg.port), + Handler: mux, + IdleTimeout: time.Minute, + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), + } + + // Start the HTTP server. + logger.Info("starting server", "addr", srv.Addr, "env", cfg.env) + + err := srv.ListenAndServe() + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } +}