diff --git a/.gitignore b/.gitignore index 5703cb8..f1acf32 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ static/bundle.js static/codemirror static/react-widgets node_modules -cusip_list.csv -security_templates.go +internal/handlers/cusip_list.csv +internal/handlers/security_templates.go diff --git a/Makefile b/Makefile index 1e27874..db16d03 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ JS_SOURCES = $(wildcard js/*.js) $(wildcard js/*/*.js) -all: static/bundle.js static/react-widgets static/codemirror/codemirror.css security_templates.go +all: static/bundle.js static/react-widgets static/codemirror/codemirror.css node_modules: npm install @@ -15,10 +15,4 @@ static/codemirror/codemirror.css: node_modules/codemirror/lib/codemirror.js node mkdir -p static/codemirror cp node_modules/codemirror/lib/codemirror.css static/codemirror/codemirror.css -security_templates.go: cusip_list.csv - ./scripts/gen_security_list.py > security_templates.go - -cusip_list.csv: - ./scripts/gen_cusip_csv.sh > cusip_list.csv - .PHONY = all diff --git a/README.md b/README.md index 6ca6933..e545373 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,8 @@ You'll then want to build everything (the Golang and Javascript portions) using something like: $ export GOPATH=`pwd` - $ go get -v github.com/aclindsa/moneygo + $ go get -d github.com/aclindsa/moneygo + $ go generate -v github.com/aclindsa/moneygo/internal/handlers $ go generate -v github.com/aclindsa/moneygo $ go install -v github.com/aclindsa/moneygo diff --git a/db.go b/db.go deleted file mode 100644 index 60505e9..0000000 --- a/db.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "database/sql" - "fmt" - _ "github.com/go-sql-driver/mysql" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" - "gopkg.in/gorp.v1" -) - -func initDB(cfg *Config) (*gorp.DbMap, error) { - db, err := sql.Open(cfg.MoneyGo.DBType.String(), cfg.MoneyGo.DSN) - if err != nil { - return nil, err - } - - var dialect gorp.Dialect - if cfg.MoneyGo.DBType == SQLite { - dialect = gorp.SqliteDialect{} - } else if cfg.MoneyGo.DBType == MySQL { - dialect = gorp.MySQLDialect{ - Engine: "InnoDB", - Encoding: "UTF8", - } - } else if cfg.MoneyGo.DBType == Postgres { - dialect = gorp.PostgresDialect{} - } else { - return nil, fmt.Errorf("Don't know gorp dialect to go with '%s' DB type", cfg.MoneyGo.DBType.String()) - } - - dbmap := &gorp.DbMap{Db: db, Dialect: dialect} - dbmap.AddTableWithName(User{}, "users").SetKeys(true, "UserId") - dbmap.AddTableWithName(Session{}, "sessions").SetKeys(true, "SessionId") - dbmap.AddTableWithName(Account{}, "accounts").SetKeys(true, "AccountId") - dbmap.AddTableWithName(Security{}, "securities").SetKeys(true, "SecurityId") - dbmap.AddTableWithName(Transaction{}, "transactions").SetKeys(true, "TransactionId") - dbmap.AddTableWithName(Split{}, "splits").SetKeys(true, "SplitId") - dbmap.AddTableWithName(Price{}, "prices").SetKeys(true, "PriceId") - dbmap.AddTableWithName(Report{}, "reports").SetKeys(true, "ReportId") - - err = dbmap.CreateTablesIfNotExists() - if err != nil { - return nil, err - } - - return dbmap, nil -} diff --git a/config.go b/internal/config/config.go similarity index 95% rename from config.go rename to internal/config/config.go index 3938d71..5d71d7c 100644 --- a/config.go +++ b/internal/config/config.go @@ -1,4 +1,4 @@ -package main +package config import ( "errors" @@ -59,7 +59,7 @@ type Config struct { MoneyGo MoneyGo } -func readConfig(filename string) (*Config, error) { +func ReadConfig(filename string) (*Config, error) { cfg := Config{ MoneyGo: MoneyGo{ Fcgi: false, diff --git a/internal/db/db.go b/internal/db/db.go new file mode 100644 index 0000000..988fd82 --- /dev/null +++ b/internal/db/db.go @@ -0,0 +1,45 @@ +package db + +import ( + "database/sql" + "fmt" + "github.com/aclindsa/moneygo/internal/config" + "github.com/aclindsa/moneygo/internal/handlers" + _ "github.com/go-sql-driver/mysql" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + "gopkg.in/gorp.v1" +) + +func GetDbMap(db *sql.DB, cfg *config.Config) (*gorp.DbMap, error) { + var dialect gorp.Dialect + if cfg.MoneyGo.DBType == config.SQLite { + dialect = gorp.SqliteDialect{} + } else if cfg.MoneyGo.DBType == config.MySQL { + dialect = gorp.MySQLDialect{ + Engine: "InnoDB", + Encoding: "UTF8", + } + } else if cfg.MoneyGo.DBType == config.Postgres { + dialect = gorp.PostgresDialect{} + } else { + return nil, fmt.Errorf("Don't know gorp dialect to go with '%s' DB type", cfg.MoneyGo.DBType.String()) + } + + dbmap := &gorp.DbMap{Db: db, Dialect: dialect} + dbmap.AddTableWithName(handlers.User{}, "users").SetKeys(true, "UserId") + dbmap.AddTableWithName(handlers.Session{}, "sessions").SetKeys(true, "SessionId") + dbmap.AddTableWithName(handlers.Account{}, "accounts").SetKeys(true, "AccountId") + dbmap.AddTableWithName(handlers.Security{}, "securities").SetKeys(true, "SecurityId") + dbmap.AddTableWithName(handlers.Transaction{}, "transactions").SetKeys(true, "TransactionId") + dbmap.AddTableWithName(handlers.Split{}, "splits").SetKeys(true, "SplitId") + dbmap.AddTableWithName(handlers.Price{}, "prices").SetKeys(true, "PriceId") + dbmap.AddTableWithName(handlers.Report{}, "reports").SetKeys(true, "ReportId") + + err := dbmap.CreateTablesIfNotExists() + if err != nil { + return nil, err + } + + return dbmap, nil +} diff --git a/internal/handlers/Makefile b/internal/handlers/Makefile new file mode 100644 index 0000000..6708f20 --- /dev/null +++ b/internal/handlers/Makefile @@ -0,0 +1,9 @@ +all: security_templates.go + +security_templates.go: cusip_list.csv scripts/gen_security_list.py + ./scripts/gen_security_list.py > security_templates.go + +cusip_list.csv: + ./scripts/gen_cusip_csv.sh > cusip_list.csv + +.PHONY = all diff --git a/accounts.go b/internal/handlers/accounts.go similarity index 99% rename from accounts.go rename to internal/handlers/accounts.go index e038322..e6b6ee2 100644 --- a/accounts.go +++ b/internal/handlers/accounts.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "encoding/json" diff --git a/accounts_lua.go b/internal/handlers/accounts_lua.go similarity index 99% rename from accounts_lua.go rename to internal/handlers/accounts_lua.go index 52f9095..928b0ba 100644 --- a/accounts_lua.go +++ b/internal/handlers/accounts_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "context" diff --git a/balance_lua.go b/internal/handlers/balance_lua.go similarity index 99% rename from balance_lua.go rename to internal/handlers/balance_lua.go index 8e9ae8b..2c017fc 100644 --- a/balance_lua.go +++ b/internal/handlers/balance_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "github.com/yuin/gopher-lua" diff --git a/date_lua.go b/internal/handlers/date_lua.go similarity index 99% rename from date_lua.go rename to internal/handlers/date_lua.go index 437ae7b..80b9f7b 100644 --- a/date_lua.go +++ b/internal/handlers/date_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "github.com/yuin/gopher-lua" diff --git a/errors.go b/internal/handlers/errors.go similarity index 97% rename from errors.go rename to internal/handlers/errors.go index 7cd9277..e475014 100644 --- a/errors.go +++ b/internal/handlers/errors.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "encoding/json" diff --git a/gnucash.go b/internal/handlers/gnucash.go similarity index 99% rename from gnucash.go rename to internal/handlers/gnucash.go index 2665194..24f6b7c 100644 --- a/gnucash.go +++ b/internal/handlers/gnucash.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "bufio" diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go new file mode 100644 index 0000000..985d8d5 --- /dev/null +++ b/internal/handlers/handlers.go @@ -0,0 +1,31 @@ +package handlers + +import ( + "gopkg.in/gorp.v1" + "net/http" +) + +// Create a closure over db, allowing the handlers to look like a +// http.HandlerFunc +type DB = gorp.DbMap +type DBHandler func(http.ResponseWriter, *http.Request, *DB) + +func DBHandlerFunc(h DBHandler, db *DB) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + h(w, r, db) + } +} + +func GetHandler(db *DB) *http.ServeMux { + servemux := http.NewServeMux() + servemux.HandleFunc("/session/", DBHandlerFunc(SessionHandler, db)) + servemux.HandleFunc("/user/", DBHandlerFunc(UserHandler, db)) + servemux.HandleFunc("/security/", DBHandlerFunc(SecurityHandler, db)) + servemux.HandleFunc("/securitytemplate/", SecurityTemplateHandler) + servemux.HandleFunc("/account/", DBHandlerFunc(AccountHandler, db)) + servemux.HandleFunc("/transaction/", DBHandlerFunc(TransactionHandler, db)) + servemux.HandleFunc("/import/gnucash", DBHandlerFunc(GnucashImportHandler, db)) + servemux.HandleFunc("/report/", DBHandlerFunc(ReportHandler, db)) + + return servemux +} diff --git a/imports.go b/internal/handlers/imports.go similarity index 99% rename from imports.go rename to internal/handlers/imports.go index acd9f8c..28a5922 100644 --- a/imports.go +++ b/internal/handlers/imports.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "encoding/json" diff --git a/ofx.go b/internal/handlers/ofx.go similarity index 99% rename from ofx.go rename to internal/handlers/ofx.go index d5a5e82..67556e5 100644 --- a/ofx.go +++ b/internal/handlers/ofx.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "errors" diff --git a/prices.go b/internal/handlers/prices.go similarity index 99% rename from prices.go rename to internal/handlers/prices.go index 2ed133a..42fa512 100644 --- a/prices.go +++ b/internal/handlers/prices.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "gopkg.in/gorp.v1" diff --git a/prices_lua.go b/internal/handlers/prices_lua.go similarity index 99% rename from prices_lua.go rename to internal/handlers/prices_lua.go index 9f763c6..0c3fe89 100644 --- a/prices_lua.go +++ b/internal/handlers/prices_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "github.com/yuin/gopher-lua" diff --git a/reports.go b/internal/handlers/reports.go similarity index 99% rename from reports.go rename to internal/handlers/reports.go index 325e613..f42d00f 100644 --- a/reports.go +++ b/internal/handlers/reports.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "context" diff --git a/reports/asset_allocation.lua b/internal/handlers/reports/asset_allocation.lua similarity index 100% rename from reports/asset_allocation.lua rename to internal/handlers/reports/asset_allocation.lua diff --git a/reports/monthly_cash_flow.lua b/internal/handlers/reports/monthly_cash_flow.lua similarity index 100% rename from reports/monthly_cash_flow.lua rename to internal/handlers/reports/monthly_cash_flow.lua diff --git a/reports/monthly_expenses.lua b/internal/handlers/reports/monthly_expenses.lua similarity index 100% rename from reports/monthly_expenses.lua rename to internal/handlers/reports/monthly_expenses.lua diff --git a/reports/monthly_net_worth.lua b/internal/handlers/reports/monthly_net_worth.lua similarity index 100% rename from reports/monthly_net_worth.lua rename to internal/handlers/reports/monthly_net_worth.lua diff --git a/reports/monthly_net_worth_change.lua b/internal/handlers/reports/monthly_net_worth_change.lua similarity index 100% rename from reports/monthly_net_worth_change.lua rename to internal/handlers/reports/monthly_net_worth_change.lua diff --git a/reports/quarterly_net_worth.lua b/internal/handlers/reports/quarterly_net_worth.lua similarity index 100% rename from reports/quarterly_net_worth.lua rename to internal/handlers/reports/quarterly_net_worth.lua diff --git a/reports/years_income.lua b/internal/handlers/reports/years_income.lua similarity index 100% rename from reports/years_income.lua rename to internal/handlers/reports/years_income.lua diff --git a/reports_lua.go b/internal/handlers/reports_lua.go similarity index 99% rename from reports_lua.go rename to internal/handlers/reports_lua.go index a272393..2904a2b 100644 --- a/reports_lua.go +++ b/internal/handlers/reports_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "github.com/yuin/gopher-lua" diff --git a/scripts/gen_cusip_csv.sh b/internal/handlers/scripts/gen_cusip_csv.sh similarity index 100% rename from scripts/gen_cusip_csv.sh rename to internal/handlers/scripts/gen_cusip_csv.sh diff --git a/scripts/gen_security_list.py b/internal/handlers/scripts/gen_security_list.py similarity index 99% rename from scripts/gen_security_list.py rename to internal/handlers/scripts/gen_security_list.py index c8e7ea3..49450d6 100755 --- a/scripts/gen_security_list.py +++ b/internal/handlers/scripts/gen_security_list.py @@ -104,7 +104,7 @@ def main(): currency_list = get_currency_list() cusip_list = get_cusip_list('cusip_list.csv') - print("package main\n") + print("package handlers\n") print("var SecurityTemplates = []Security{") print(currency_list.unicode()) print(cusip_list.unicode()) diff --git a/securities.go b/internal/handlers/securities.go similarity index 99% rename from securities.go rename to internal/handlers/securities.go index bd4d148..73ee50e 100644 --- a/securities.go +++ b/internal/handlers/securities.go @@ -1,4 +1,6 @@ -package main +package handlers + +//go:generate make import ( "encoding/json" diff --git a/securities_lua.go b/internal/handlers/securities_lua.go similarity index 99% rename from securities_lua.go rename to internal/handlers/securities_lua.go index dbd394f..a13614b 100644 --- a/securities_lua.go +++ b/internal/handlers/securities_lua.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "context" diff --git a/sessions.go b/internal/handlers/sessions.go similarity index 99% rename from sessions.go rename to internal/handlers/sessions.go index d3df3ae..732085d 100644 --- a/sessions.go +++ b/internal/handlers/sessions.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "crypto/rand" diff --git a/transactions.go b/internal/handlers/transactions.go similarity index 99% rename from transactions.go rename to internal/handlers/transactions.go index 8ea2214..72c5ab2 100644 --- a/transactions.go +++ b/internal/handlers/transactions.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "encoding/json" diff --git a/users.go b/internal/handlers/users.go similarity index 99% rename from users.go rename to internal/handlers/users.go index 83e7b77..471dfcc 100644 --- a/users.go +++ b/internal/handlers/users.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "crypto/sha256" diff --git a/util.go b/internal/handlers/util.go similarity index 96% rename from util.go rename to internal/handlers/util.go index 1627920..3579a13 100644 --- a/util.go +++ b/internal/handlers/util.go @@ -1,4 +1,4 @@ -package main +package handlers import ( "fmt" diff --git a/main.go b/main.go index 0ffb0e9..65583c6 100644 --- a/main.go +++ b/main.go @@ -3,8 +3,11 @@ package main //go:generate make import ( + "database/sql" "flag" - "gopkg.in/gorp.v1" + "github.com/aclindsa/moneygo/internal/config" + "github.com/aclindsa/moneygo/internal/db" + "github.com/aclindsa/moneygo/internal/handlers" "log" "net" "net/http" @@ -15,19 +18,19 @@ import ( ) var configFile string -var config *Config +var cfg *config.Config func init() { var err error flag.StringVar(&configFile, "config", "/etc/moneygo/config.ini", "Path to config file") flag.Parse() - config, err = readConfig(configFile) + cfg, err = config.ReadConfig(configFile) if err != nil { log.Fatal(err) } - static_path := path.Join(config.MoneyGo.Basedir, "static") + static_path := path.Join(cfg.MoneyGo.Basedir, "static") // Ensure base directory is valid dir_err_str := "The base directory doesn't look like it contains the " + @@ -46,57 +49,48 @@ func init() { log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) } -func rootHandler(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, path.Join(config.MoneyGo.Basedir, "static/index.html")) -} +type FileHandler func(http.ResponseWriter, *http.Request, string) -func staticHandler(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, path.Join(config.MoneyGo.Basedir, r.URL.Path)) -} - -// Create a closure over db, allowing the handlers to look like a -// http.HandlerFunc -type DB = gorp.DbMap -type DBHandler func(http.ResponseWriter, *http.Request, *DB) - -func DBHandlerFunc(h DBHandler, db *DB) http.HandlerFunc { +func FileHandlerFunc(h FileHandler, basedir string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - h(w, r, db) + h(w, r, basedir) } } -func GetHandler(db *DB) http.Handler { - servemux := http.NewServeMux() - servemux.HandleFunc("/", rootHandler) - servemux.HandleFunc("/static/", staticHandler) - servemux.HandleFunc("/session/", DBHandlerFunc(SessionHandler, db)) - servemux.HandleFunc("/user/", DBHandlerFunc(UserHandler, db)) - servemux.HandleFunc("/security/", DBHandlerFunc(SecurityHandler, db)) - servemux.HandleFunc("/securitytemplate/", SecurityTemplateHandler) - servemux.HandleFunc("/account/", DBHandlerFunc(AccountHandler, db)) - servemux.HandleFunc("/transaction/", DBHandlerFunc(TransactionHandler, db)) - servemux.HandleFunc("/import/gnucash", DBHandlerFunc(GnucashImportHandler, db)) - servemux.HandleFunc("/report/", DBHandlerFunc(ReportHandler, db)) +func rootHandler(w http.ResponseWriter, r *http.Request, basedir string) { + http.ServeFile(w, r, path.Join(basedir, "static/index.html")) +} - return servemux +func staticHandler(w http.ResponseWriter, r *http.Request, basedir string) { + http.ServeFile(w, r, path.Join(basedir, r.URL.Path)) } func main() { - database, err := initDB(config) + database, err := sql.Open(cfg.MoneyGo.DBType.String(), cfg.MoneyGo.DSN) if err != nil { log.Fatal(err) } - handler := GetHandler(database) + defer database.Close() - listener, err := net.Listen("tcp", ":"+strconv.Itoa(config.MoneyGo.Port)) + dbmap, err := db.GetDbMap(database, cfg) if err != nil { log.Fatal(err) } - log.Printf("Serving on port %d out of directory: %s", config.MoneyGo.Port, config.MoneyGo.Basedir) - if config.MoneyGo.Fcgi { - fcgi.Serve(listener, handler) + // Get ServeMux for API and add our own handlers for files + servemux := handlers.GetHandler(dbmap) + servemux.HandleFunc("/", FileHandlerFunc(rootHandler, cfg.MoneyGo.Basedir)) + servemux.HandleFunc("/static/", FileHandlerFunc(staticHandler, cfg.MoneyGo.Basedir)) + + listener, err := net.Listen("tcp", ":"+strconv.Itoa(cfg.MoneyGo.Port)) + if err != nil { + log.Fatal(err) + } + + log.Printf("Serving on port %d out of directory: %s", cfg.MoneyGo.Port, cfg.MoneyGo.Basedir) + if cfg.MoneyGo.Fcgi { + fcgi.Serve(listener, servemux) } else { - http.Serve(listener, handler) + http.Serve(listener, servemux) } }