package main import ( "errors" "fmt" "log" "net/http" "os" "path/filepath" "strconv" "time" "arimelody-web/admin" "arimelody-web/api" "arimelody-web/global" "arimelody-web/templates" "arimelody-web/view" "github.com/jmoiron/sqlx" _ "github.com/lib/pq" ) const DEFAULT_PORT int64 = 8080 func main() { // initialise database connection if env := os.Getenv("ARIMELODY_DB_HOST"); env != "" { global.Config.DB.Host = env } if env := os.Getenv("ARIMELODY_DB_NAME"); env != "" { global.Config.DB.Name = env } if env := os.Getenv("ARIMELODY_DB_USER"); env != "" { global.Config.DB.User = env } if env := os.Getenv("ARIMELODY_DB_PASS"); env != "" { global.Config.DB.Pass = env } if global.Config.DB.Host == "" { fmt.Fprintf(os.Stderr, "FATAL: db.host not provided! Exiting...\n") os.Exit(1) } if global.Config.DB.Name == "" { fmt.Fprintf(os.Stderr, "FATAL: db.name not provided! Exiting...\n") os.Exit(1) } if global.Config.DB.User == "" { fmt.Fprintf(os.Stderr, "FATAL: db.user not provided! Exiting...\n") os.Exit(1) } if global.Config.DB.Pass == "" { fmt.Fprintf(os.Stderr, "FATAL: db.pass not provided! Exiting...\n") os.Exit(1) } var err error global.DB, err = sqlx.Connect( "postgres", fmt.Sprintf( "host=%s user=%s dbname=%s password='%s' sslmode=disable", global.Config.DB.Host, global.Config.DB.User, global.Config.DB.Name, global.Config.DB.Pass, ), ) if err != nil { fmt.Fprintf(os.Stderr, "FATAL: Unable to create database connection pool: %v\n", err) os.Exit(1) } global.DB.SetConnMaxLifetime(time.Minute * 3) global.DB.SetMaxOpenConns(10) global.DB.SetMaxIdleConns(10) defer global.DB.Close() // start the web server! mux := createServeMux() port, err := strconv.ParseInt(os.Getenv("ARIMELODY_PORT"), 10, 0) if err != nil { port = DEFAULT_PORT } fmt.Printf("Now serving at http://127.0.0.1:%d\n", port) log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), global.HTTPLog(mux))) } func createServeMux() *http.ServeMux { mux := http.NewServeMux() mux.Handle("/admin/", http.StripPrefix("/admin", admin.Handler())) mux.Handle("/api/", http.StripPrefix("/api", api.Handler())) mux.Handle("/music/", http.StripPrefix("/music", view.MusicHandler())) mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler(filepath.Join(global.Config.DataDirectory, "uploads")))) mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" || r.URL.Path == "/index.html" { err := templates.Pages["index"].Execute(w, nil) if err != nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } return } staticHandler("public").ServeHTTP(w, r) })) return mux } func staticHandler(directory string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { info, err := os.Stat(filepath.Join(directory, filepath.Clean(r.URL.Path))) // does the file exist? if err != nil { if errors.Is(err, os.ErrNotExist) { http.NotFound(w, r) return } } // is thjs a directory? (forbidden) if info.IsDir() { http.NotFound(w, r) return } http.FileServer(http.Dir(directory)).ServeHTTP(w, r) }) }