diff --git a/Makefile b/Makefile index ceb0eb3..af1cca1 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ PHONEY: serve # SQLite Commands sql-cli: - sqlite3 $(SQL_DATABASE) -cmd ".headers on" -cmd ".mode column" -cmd ".tables" + sqlite3 $(SQL_DATABASE) -cmd ".headers on" -cmd ".mode box" -cmd ".tables" init-db: run-migrate sqlite3 $(SQL_DATABASE) "PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL;" diff --git a/README.md b/README.md index a3e6226..d693c85 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ An example web application in Golang. - https://lets-go.alexedwards.net/sample/02.09-serving-static-files.html ## Project Structure +Following https://go.dev/doc/modules/layout#server-project the implementation + Loosely inspired by the organization of [WTFDial](https://github.com/benbjohnson/wtf?tab=readme-ov-file#project-structure), - Application domain types reside in the project root (User, UserService, etc) - Implementations of the application domain reside in the subpackages `sqlite`, `http`, etc. @@ -89,3 +90,24 @@ Warning: http.ServeFile() does not automatically sanitize the file path. If you ## Databases The Let's-go book calls for MySQL. We use [go-sqlite3](ggithub.com/mattn/go-sqlite3) and [go-migrate]() tool to manage migrations instead. Use `make check-system-deps` to validate all tools for this repository are installed. + + +## Managing Dependencies + +To upgrade to latest minor/patch version of a package in your go mod.go you can use the `-u` flag: + +``` +go get -u github.com/foo/bar +``` + +To update to a specific package + +``` +go get -u github.com/foo/bar@v2.0.0 +``` + +To remove the package you can use `go mod tidy` if all references have been removed, or: + +``` +go get github.com/foo/bar@none +``` \ No newline at end of file diff --git a/cmd/ratchetd/main.go b/cmd/ratchetd/main.go index 898765a..c8c918c 100644 --- a/cmd/ratchetd/main.go +++ b/cmd/ratchetd/main.go @@ -7,6 +7,7 @@ import ( "net/http" "os" + rdb "git.runcible.io/learning/ratchet/internal/database" "git.runcible.io/learning/ratchet/internal/logging" "git.runcible.io/learning/ratchet/internal/server" // "git.runcible.io/learning/ratchet" @@ -23,13 +24,23 @@ func main() { // Parse command line options addr := flag.String("addr", "0.0.0.0", "HTTP network address") port := flag.String("port", "5001", "HTTP port") - logLevel := flag.String("logging", "INFO", "Logging Level. Valid values [INFO, DEBUG, WARN, ERROR].") + logLevel := flag.String("logging", "DEBUG", "Logging Level. Valid values [INFO, DEBUG, WARN, ERROR].") + dbPath := flag.String("database", "./ratchet.db", "A path to a sqlite3 database") // must call parse or all values will be the defaults flag.Parse() // DEPENDENCY INJECTION FOR HANDLERS // Setup Logging logger := logging.InitLogging(*logLevel) + // Setup DB Connection Pool + db, err := rdb.OpenSqlite3DB(*dbPath) + + if err != nil { + slog.Error(err.Error()) + os.Exit(1) + } + // Close db connection before exiting main. + defer db.Close() // Propagate build information to root package to share globally // ratchet.Version = strings.TrimPrefix(version, "") @@ -41,7 +52,7 @@ func main() { slog.Info(fmt.Sprintf("Listening on http://%s:%s", *addr, *port)) //log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%s", *addr, *port), server)) // there is no log.Fatal equivalent. This is an approximation of the behavior - err := http.ListenAndServe(fmt.Sprintf("%s:%s", *addr, *port), server) + err = http.ListenAndServe(fmt.Sprintf("%s:%s", *addr, *port), server) slog.Error(err.Error()) os.Exit(1) diff --git a/go.mod b/go.mod index 6c5902a..c8971a2 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module git.runcible.io/learning/ratchet go 1.23.3 -require github.com/mattn/go-sqlite3 v1.14.24 // indirect +require github.com/mattn/go-sqlite3 v1.14.24 diff --git a/internal/database/database.go b/internal/database/database.go new file mode 100644 index 0000000..b0a765e --- /dev/null +++ b/internal/database/database.go @@ -0,0 +1,30 @@ +package database + +import ( + "database/sql" + "fmt" + "log/slog" + + _ "github.com/mattn/go-sqlite3" +) + +// OpenSqlite3DB is a wrapper +// +// TODO wtf dail uses context.Background(). Look into it more +func OpenSqlite3DB(dbPath string) (*sql.DB, error) { + full_database_path := "file:" + dbPath + "?cache=shared" + + slog.Debug(fmt.Sprintf("Using database path: %s", full_database_path)) + + db, err := sql.Open("sqlite3", full_database_path) + if err != nil { + return nil, fmt.Errorf("failed to open: %s", full_database_path) + } + + err = db.Ping() + if err != nil { + db.Close() + return nil, err + } + return db, nil +}