diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5fa2459 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.idea +keys +nkey.sh +users +bin +db.sql +*crt +*key +Makefile2 +*~ diff --git a/config/cfg.go b/config/cfg.go index 78a57c1..0ad327e 100644 --- a/config/cfg.go +++ b/config/cfg.go @@ -3,33 +3,21 @@ package config import "os" type ( - DBCfg struct { - DatabaseURL string - } - - ServerCfg struct { - SessionKey string - Env string - CertFile string - CertKeyFile string - } - Config struct { - DB DBCfg - Server ServerCfg + DatabaseURL string + SessionKey string + Env string + CertFile string + KeyFile string } ) func New() *Config { return &Config{ - DB: DBCfg{ - DatabaseURL: os.Getenv("DATABASE_URL"), - }, - Server: ServerCfg{ - SessionKey: os.Getenv("SESSION_KEY"), - Env: os.Getenv("ENV"), - CertFile: os.Getenv("CERT_FILE"), - CertKeyFile: os.Getenv("CERT_KEY_FILE"), - }, + DatabaseURL: os.Getenv("DATABASE_URL"), + SessionKey: os.Getenv("SESSION_KEY"), + Env: os.Getenv("ENV"), + CertFile: os.Getenv("CERT_FILE"), + KeyFile: os.Getenv("CERT_KEY_FILE"), } } diff --git a/generate.go b/generate.go index 5185c35..2149b0d 100644 --- a/generate.go +++ b/generate.go @@ -81,5 +81,6 @@ func generateMap(target string, pkg string, mapName string, srcFiles []string) { func main() { generateMap(path.Join("storage", "sql.go"), "storage", "SqlMap", glob("storage/sql/*.sql")) - //generateMap(path.Join("template", "html.go"), "template", "TplMap", glob("template/html/*.html")) + generateMap(path.Join("web", "handler", "html.go"), "handler", "TplMap", glob("web/handler/html/*.html")) + generateMap(path.Join("web", "handler", "common.go"), "handler", "TplCommonMap", glob("web/handler/html/common/*.html")) } diff --git a/go.mod b/go.mod index 76f93a0..dd86997 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,8 @@ module status go 1.16 require ( + github.com/gorilla/mux v1.8.0 + github.com/gorilla/sessions v1.2.1 github.com/lib/pq v1.10.4 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 ) diff --git a/go.sum b/go.sum index 233fb43..709d15c 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI= diff --git a/main.go b/main.go index 8b60401..200c4ba 100644 --- a/main.go +++ b/main.go @@ -5,17 +5,17 @@ import ( "log" "status/config" "status/storage" + "status/web" ) func main() { cfg := config.New() - db, err := storage.InitDB(cfg.DB) + db, err := storage.InitDB(cfg.DatabaseURL) if err != nil { log.Fatal(err) } - storage.New(db) - //data := storage.New(db) - //log.Fatal( - // server.Serve(data, cfg), - //) + data := storage.New(db) + log.Fatal( + web.Serve(data, cfg), + ) } diff --git a/storage/db.go b/storage/db.go index a3a11a1..fd877fb 100644 --- a/storage/db.go +++ b/storage/db.go @@ -3,11 +3,10 @@ package storage import ( "database/sql" _ "github.com/lib/pq" - "status/config" ) -func InitDB(cfg config.DBCfg) (*sql.DB, error) { - db, err := sql.Open("postgres", cfg.DatabaseURL) +func InitDB(databaseURL string) (*sql.DB, error) { + db, err := sql.Open("postgres", databaseURL) if err != nil { return db, err } diff --git a/storage/status.go b/storage/status.go index 7e2d21a..28b0f4e 100644 --- a/storage/status.go +++ b/storage/status.go @@ -55,27 +55,27 @@ func (s *Storage) StatusById(id int64) (model.Status, error) { return status, err } -func (s *Storage) StatusByUsername(user string, perPage int, page int64) ([]model.Status, bool, error) { +func (s *Storage) StatusByUsername(user string, perPage int, page int64) ([]model.Status, error) { rows, err := s.db.Query(statusQueryBuilder{ where: `author = $1`, limit: strconv.Itoa(perPage + 1), offset: `$2`, }.build(), user, page*int64(perPage)) if err != nil { - return nil, false, err + return nil, err } var statuses []model.Status for rows.Next() { post, err := s.populateStatus(rows) if err != nil { - return statuses, false, err + return statuses, err } statuses = append(statuses, post) } if len(statuses) > perPage { - return statuses[0:perPage], true, err + return statuses[0:perPage], err } - return statuses, false, err + return statuses, err } func (s *Storage) Statuses(page int64, perPage int) ([]model.Status, bool, error) { diff --git a/web/handler/common.go b/web/handler/common.go new file mode 100644 index 0000000..7771ded --- /dev/null +++ b/web/handler/common.go @@ -0,0 +1,22 @@ +// Code generated by go generate; DO NOT EDIT. + +package handler + +var TplCommonMap = map[string]string{ + "layout": `{{ define "layout" }} + + +
+ + + +This is the index
+{{ end }}`, +} diff --git a/web/handler/html/common/layout.html b/web/handler/html/common/layout.html new file mode 100644 index 0000000..887e31b --- /dev/null +++ b/web/handler/html/common/layout.html @@ -0,0 +1,16 @@ +{{ define "layout" }} + + + + + + +This is the index
+{{ end }} \ No newline at end of file diff --git a/web/handler/index_show.go b/web/handler/index_show.go new file mode 100644 index 0000000..317ff4c --- /dev/null +++ b/web/handler/index_show.go @@ -0,0 +1,22 @@ +package handler + +import ( + "net/http" +) + +type Update struct { + UpdatedAgo string + Author string +} + +func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) { + // user, _ := h.sess.Get(r) + + //h.renderLayout(w, "index", map[string]interface{}{ + // "Pages": pages, + // "Files": files, + // "News": news, + //}, user) + + h.renderLayout(w, "index", map[string]interface{}{}, "") +} diff --git a/web/handler/tpl.go b/web/handler/tpl.go new file mode 100644 index 0000000..9463b30 --- /dev/null +++ b/web/handler/tpl.go @@ -0,0 +1,34 @@ +package handler + +import ( + "html/template" + "io" +) + +var views = make(map[string]*template.Template) + +func (h *Handler) initTpl() { + commonTemplates := "" + for _, content := range TplCommonMap { + commonTemplates += content + } + + for name, content := range TplMap { + views[name] = template.Must(template.New("main").Parse(commonTemplates + content)) + } +} + +func (h *Handler) renderLayout(w io.Writer, view string, params map[string]interface{}, user string) { + data := make(map[string]interface{}) + if params != nil { + for k, v := range params { + data[k] = v + } + } + data["logged"] = user + views[view].ExecuteTemplate(w, "layout", data) +} + +func (h *Handler) view(view string) *template.Template { + return views[view] +} diff --git a/web/session/session.go b/web/session/session.go new file mode 100644 index 0000000..a78a624 --- /dev/null +++ b/web/session/session.go @@ -0,0 +1,58 @@ +package session + +import ( + "errors" + "github.com/gorilla/sessions" + "net/http" + "status/storage" +) + +const cookieName = "status" + +type Session struct { + Store *sessions.CookieStore + Storage *storage.Storage +} + +func New(key string, storage *storage.Storage) *Session { + store := sessions.NewCookieStore([]byte(key)) + store.Options = &sessions.Options{ + HttpOnly: true, + MaxAge: 86400 * 30, + } + return &Session{ + Store: store, + Storage: storage, + } +} + +func (s *Session) Delete(w http.ResponseWriter, r *http.Request) error { + session, err := s.Store.Get(r, cookieName) + if err != nil { + return err + } + session.Options.MaxAge = -1 + err = session.Save(r, w) + return err +} + +func (s *Session) Save(r *http.Request, w http.ResponseWriter, name string) error { + session, _ := s.Store.Get(r, cookieName) + session.Values["name"] = name + return session.Save(r, w) +} + +func (s *Session) Get(r *http.Request) (string, error) { + session, err := s.Store.Get(r, cookieName) + if err != nil { + return "", err + } + name, ok := session.Values["name"].(string) + if name == "" || !ok { + return "", errors.New("error extracting session") + } + if ok := s.Storage.UserExists(name); !ok { + return "", errors.New("user doesn't exit") + } + return name, nil +} diff --git a/web/web.go b/web/web.go new file mode 100644 index 0000000..34473e6 --- /dev/null +++ b/web/web.go @@ -0,0 +1,47 @@ +package web + +import ( + "fmt" + "log" + "net/http" + "status/config" + "status/storage" + "status/web/handler" + "status/web/session" +) + +func httpToHTTPSHandler() *http.ServeMux { + handleRedirect := func(w http.ResponseWriter, r *http.Request) { + newURI := "https://" + r.Host + r.URL.String() + http.Redirect(w, r, newURI, http.StatusFound) + } + mux := &http.ServeMux{} + mux.HandleFunc("/", handleRedirect) + return mux +} + +func Serve(data *storage.Storage, cfg *config.Config) error { + var err error + sess := session.New(cfg.SessionKey, data) + s, err := handler.New(cfg, sess, data) + if err != nil { + log.Fatal(err) + } + switch cfg.Env { + case "PROD": + go func() { + fmt.Printf("Starting HTTP server on :443\n") + err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, s) + if err != nil { + log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err) + } + }() + fmt.Printf("Starting HTTP to HTTPS server on :80\n") + err = http.ListenAndServe(":80", httpToHTTPSHandler()) + break + default: + fmt.Printf("Starting HTTP server on port 8000\n") + err = http.ListenAndServe(":8000", s) + } + return err +}