156 lines
4.1 KiB
Go
156 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"io/fs"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
|
|
userctx "git.kealoha.me/lks/lenslocked/context"
|
|
ctrlrs "git.kealoha.me/lks/lenslocked/controllers"
|
|
"git.kealoha.me/lks/lenslocked/migrations"
|
|
"git.kealoha.me/lks/lenslocked/models"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/gorilla/csrf"
|
|
"github.com/joho/godotenv"
|
|
"github.com/pressly/goose/v3"
|
|
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
_ "github.com/jackc/pgx/v4/stdlib"
|
|
)
|
|
|
|
const DEBUG bool = true
|
|
|
|
type config struct {
|
|
Postgres string
|
|
Email struct {
|
|
Host string
|
|
Port int
|
|
Username, Pass, Sender string
|
|
}
|
|
Csrf struct {
|
|
Key []byte
|
|
Secure bool
|
|
}
|
|
Server struct {
|
|
Address string
|
|
}
|
|
}
|
|
|
|
func loadConfig() (config, error) {
|
|
var cfg config
|
|
cfg.Csrf.Secure = !DEBUG
|
|
|
|
err := godotenv.Load()
|
|
if err != nil {
|
|
fmt.Println("Warning: Could not load a .env file")
|
|
}
|
|
|
|
cfg.Csrf.Key = []byte(os.Getenv("LENSLOCKED_CSRF_KEY"))
|
|
if len(cfg.Csrf.Key) < 32 {
|
|
return cfg, fmt.Errorf("Error: no or bad csrf protection key\nPlease set the LENSLOCKED_CSRF_KEY env var to a key at least 32 characters long.")
|
|
}
|
|
|
|
cfg.Postgres = os.Getenv("LENSLOCKED_DB_STRING")
|
|
|
|
cfg.Email.Host = os.Getenv("LENSLOCKED_EMAIL_HOST")
|
|
cfg.Email.Username = os.Getenv("LENSLOCKED_EMAIL_USERNAME")
|
|
cfg.Email.Pass = os.Getenv("LENSLOCKED_EMAIL_PASSWORD")
|
|
cfg.Email.Sender = os.Getenv("LENSLOCKED_EMAIL_FROM")
|
|
cfg.Email.Port, err = strconv.Atoi(os.Getenv("LENSLOCKED_EMAIL_PORT"))
|
|
if err != nil {
|
|
fmt.Println("Warning: Invalid STMP port set in LENSLOCKED_EMAIL_PORT. Using port 587")
|
|
cfg.Email.Port = 587
|
|
}
|
|
|
|
cfg.Server.Address = os.Getenv("LENSLOCKED_ADDRESS")
|
|
if cfg.Server.Address == "" {
|
|
if DEBUG {
|
|
cfg.Server.Address = ":3000"
|
|
} else {
|
|
return cfg, fmt.Errorf("No server address set\nPlease set the LENSLOCKED_ADDRESS env var to the servers address")
|
|
}
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/html; charset=utf8")
|
|
w.WriteHeader(http.StatusNotFound)
|
|
fmt.Fprint(w, "404 page not found")
|
|
}
|
|
|
|
func ConnectDB(dbstr string) *sql.DB {
|
|
db, err := sql.Open("pgx", dbstr)
|
|
if err != nil {
|
|
panic(fmt.Sprint("Error connecting to database: %w", err))
|
|
}
|
|
err = db.Ping()
|
|
if err != nil {
|
|
panic(fmt.Sprint("Error connecting to database: %w", err))
|
|
}
|
|
return db
|
|
}
|
|
func MigrateDB(db *sql.DB, subfs fs.FS) error {
|
|
goose.SetBaseFS(subfs)
|
|
defer func() { goose.SetBaseFS(nil) }()
|
|
err := goose.SetDialect("postgres")
|
|
if err != nil {
|
|
return fmt.Errorf("Migrate: %w", err)
|
|
}
|
|
err = goose.Up(db, ".")
|
|
if err != nil {
|
|
return fmt.Errorf("Migrate: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
cfg, err := loadConfig()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
db := ConnectDB(cfg.Postgres)
|
|
defer db.Close()
|
|
err = MigrateDB(db, migrations.FS)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
userService := models.UserService{DB: db}
|
|
sessionService := models.SessionService{DB: db}
|
|
emailService := models.NewEmailService(cfg.Email.Host, cfg.Email.Port, cfg.Email.Username, cfg.Email.Pass, cfg.Email.Sender)
|
|
var usersCtrlr ctrlrs.Users = ctrlrs.Default(&userService, &sessionService, emailService)
|
|
|
|
umw := userctx.UserMiddleware{SS: &sessionService}
|
|
|
|
r := chi.NewRouter()
|
|
|
|
r.Use(middleware.Logger)
|
|
r.Use(csrf.Protect(cfg.Csrf.Key, csrf.Secure(cfg.Csrf.Secure)))
|
|
r.Use(umw.SetUser)
|
|
|
|
r.Get("/", ctrlrs.StaticController("home.gohtml", "tailwind.gohtml"))
|
|
r.Get("/contact", ctrlrs.StaticController("contact.gohtml", "tailwind.gohtml"))
|
|
r.Get("/faq", ctrlrs.FAQ("faq.gohtml", "tailwind.gohtml"))
|
|
|
|
r.Get("/signup", usersCtrlr.GetSignup)
|
|
r.Post("/signup", usersCtrlr.PostSignup)
|
|
r.Get("/signin", usersCtrlr.GetSignin)
|
|
r.Post("/signin", usersCtrlr.PostSignin)
|
|
r.Post("/signout", usersCtrlr.GetSignout)
|
|
r.Get("/forgot-pw", usersCtrlr.GetForgotPassword)
|
|
r.Post("/forgot-pw", usersCtrlr.PostForgotPassword)
|
|
r.Get("/reset-pw", usersCtrlr.GetResetPass)
|
|
r.Post("/reset-pw", usersCtrlr.PostResetPass)
|
|
|
|
r.Get("/user", umw.RequireUserfn(usersCtrlr.CurrentUser))
|
|
r.NotFound(notFoundHandler)
|
|
|
|
fmt.Printf("Starting the server on %s...\n", cfg.Server.Address)
|
|
http.ListenAndServe(cfg.Server.Address, r)
|
|
}
|