Add web package
This commit is contained in:
parent
7cd973f0a6
commit
8b054e454a
22 changed files with 466 additions and 37 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
.idea
|
||||||
|
keys
|
||||||
|
nkey.sh
|
||||||
|
users
|
||||||
|
bin
|
||||||
|
db.sql
|
||||||
|
*crt
|
||||||
|
*key
|
||||||
|
Makefile2
|
||||||
|
*~
|
||||||
|
|
@ -3,33 +3,21 @@ package config
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
type (
|
type (
|
||||||
DBCfg struct {
|
|
||||||
DatabaseURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
ServerCfg struct {
|
|
||||||
SessionKey string
|
|
||||||
Env string
|
|
||||||
CertFile string
|
|
||||||
CertKeyFile string
|
|
||||||
}
|
|
||||||
|
|
||||||
Config struct {
|
Config struct {
|
||||||
DB DBCfg
|
DatabaseURL string
|
||||||
Server ServerCfg
|
SessionKey string
|
||||||
|
Env string
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func New() *Config {
|
func New() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
DB: DBCfg{
|
DatabaseURL: os.Getenv("DATABASE_URL"),
|
||||||
DatabaseURL: os.Getenv("DATABASE_URL"),
|
SessionKey: os.Getenv("SESSION_KEY"),
|
||||||
},
|
Env: os.Getenv("ENV"),
|
||||||
Server: ServerCfg{
|
CertFile: os.Getenv("CERT_FILE"),
|
||||||
SessionKey: os.Getenv("SESSION_KEY"),
|
KeyFile: os.Getenv("CERT_KEY_FILE"),
|
||||||
Env: os.Getenv("ENV"),
|
|
||||||
CertFile: os.Getenv("CERT_FILE"),
|
|
||||||
CertKeyFile: os.Getenv("CERT_KEY_FILE"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,5 +81,6 @@ func generateMap(target string, pkg string, mapName string, srcFiles []string) {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
generateMap(path.Join("storage", "sql.go"), "storage", "SqlMap", glob("storage/sql/*.sql"))
|
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"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -3,6 +3,8 @@ module status
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
github.com/gorilla/sessions v1.2.1
|
||||||
github.com/lib/pq v1.10.4
|
github.com/lib/pq v1.10.4
|
||||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
||||||
)
|
)
|
||||||
|
|
|
||||||
6
go.sum
6
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 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
|
||||||
|
|
|
||||||
12
main.go
12
main.go
|
|
@ -5,17 +5,17 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"status/config"
|
"status/config"
|
||||||
"status/storage"
|
"status/storage"
|
||||||
|
"status/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cfg := config.New()
|
cfg := config.New()
|
||||||
db, err := storage.InitDB(cfg.DB)
|
db, err := storage.InitDB(cfg.DatabaseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
storage.New(db)
|
data := storage.New(db)
|
||||||
//data := storage.New(db)
|
log.Fatal(
|
||||||
//log.Fatal(
|
web.Serve(data, cfg),
|
||||||
// server.Serve(data, cfg),
|
)
|
||||||
//)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,10 @@ package storage
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"status/config"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitDB(cfg config.DBCfg) (*sql.DB, error) {
|
func InitDB(databaseURL string) (*sql.DB, error) {
|
||||||
db, err := sql.Open("postgres", cfg.DatabaseURL)
|
db, err := sql.Open("postgres", databaseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return db, err
|
return db, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,27 +55,27 @@ func (s *Storage) StatusById(id int64) (model.Status, error) {
|
||||||
return status, err
|
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{
|
rows, err := s.db.Query(statusQueryBuilder{
|
||||||
where: `author = $1`,
|
where: `author = $1`,
|
||||||
limit: strconv.Itoa(perPage + 1),
|
limit: strconv.Itoa(perPage + 1),
|
||||||
offset: `$2`,
|
offset: `$2`,
|
||||||
}.build(), user, page*int64(perPage))
|
}.build(), user, page*int64(perPage))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var statuses []model.Status
|
var statuses []model.Status
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
post, err := s.populateStatus(rows)
|
post, err := s.populateStatus(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return statuses, false, err
|
return statuses, err
|
||||||
}
|
}
|
||||||
statuses = append(statuses, post)
|
statuses = append(statuses, post)
|
||||||
}
|
}
|
||||||
if len(statuses) > perPage {
|
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) {
|
func (s *Storage) Statuses(page int64, perPage int) ([]model.Status, bool, error) {
|
||||||
|
|
|
||||||
22
web/handler/common.go
Normal file
22
web/handler/common.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
var TplCommonMap = map[string]string{
|
||||||
|
"layout": `{{ define "layout" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/style.css"/>
|
||||||
|
<title>Status</title>
|
||||||
|
{{ template "head" . }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ template "content" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
|
{{ define "head" }}{{ end }}`,
|
||||||
|
}
|
||||||
34
web/handler/form/editor.go
Normal file
34
web/handler/form/editor.go
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EditorForm struct {
|
||||||
|
Name string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (f *HomepageForm) Validate() error {
|
||||||
|
// if f.Password != f.Confirm {
|
||||||
|
// return errors.New("password doesn't match confirmation")
|
||||||
|
// }
|
||||||
|
// if len(f.Username) < 3 {
|
||||||
|
// return errors.New("username needs to be at least 3 characters")
|
||||||
|
// }
|
||||||
|
// match, _ := regexp.MatchString("^[a-z0-9-_]+$", f.Username)
|
||||||
|
// if !match {
|
||||||
|
// return errors.New("only lowercase letters and digits are accepted for username")
|
||||||
|
// }
|
||||||
|
// if len(f.Password) < 6 {
|
||||||
|
// return errors.New("password needs to be at least 6 characters")
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
//}
|
||||||
|
|
||||||
|
func NewEditorForm(r *http.Request) *EditorForm {
|
||||||
|
return &EditorForm{
|
||||||
|
Name: r.FormValue("name"),
|
||||||
|
Content: r.FormValue("content"),
|
||||||
|
}
|
||||||
|
}
|
||||||
15
web/handler/form/folder.go
Normal file
15
web/handler/form/folder.go
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FolderForm struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFolderForm(r *http.Request) *FolderForm {
|
||||||
|
return &FolderForm{
|
||||||
|
Name: r.FormValue("name"),
|
||||||
|
}
|
||||||
|
}
|
||||||
41
web/handler/form/homepage.go
Normal file
41
web/handler/form/homepage.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HomepageForm struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Confirm string
|
||||||
|
Key string
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *HomepageForm) Validate() error {
|
||||||
|
if f.Password != f.Confirm {
|
||||||
|
return errors.New("password doesn't match confirmation")
|
||||||
|
}
|
||||||
|
if len(f.Username) < 3 {
|
||||||
|
return errors.New("username needs to be at least 3 characters")
|
||||||
|
}
|
||||||
|
match, _ := regexp.MatchString("^[a-z0-9-_]+$", f.Username)
|
||||||
|
if !match {
|
||||||
|
return errors.New("only lowercase letters and digits are accepted for username")
|
||||||
|
}
|
||||||
|
if len(f.Password) < 6 {
|
||||||
|
return errors.New("password needs to be at least 6 characters")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHomepageForm(r *http.Request) *HomepageForm {
|
||||||
|
return &HomepageForm{
|
||||||
|
Username: r.FormValue("name"),
|
||||||
|
Password: r.FormValue("password"),
|
||||||
|
Confirm: r.FormValue("password-confirm"),
|
||||||
|
Key: r.FormValue("key"),
|
||||||
|
}
|
||||||
|
}
|
||||||
18
web/handler/form/login.go
Normal file
18
web/handler/form/login.go
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginForm struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Error string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginForm(r *http.Request) *LoginForm {
|
||||||
|
return &LoginForm{
|
||||||
|
Username: r.FormValue("name"),
|
||||||
|
Password: r.FormValue("password"),
|
||||||
|
}
|
||||||
|
}
|
||||||
26
web/handler/form/upload.go
Normal file
26
web/handler/form/upload.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package form
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploadForm struct {
|
||||||
|
Filename string
|
||||||
|
File io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUploadForm(r *http.Request) (*UploadForm, error) {
|
||||||
|
if err := r.ParseMultipartForm(2 << 20); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
file, handler, err := r.FormFile("file")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return &UploadForm{
|
||||||
|
Filename: handler.Filename,
|
||||||
|
File: file,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
78
web/handler/handler.go
Normal file
78
web/handler/handler.go
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"status/config"
|
||||||
|
"status/storage"
|
||||||
|
"status/web/session"
|
||||||
|
)
|
||||||
|
|
||||||
|
func serverError(w http.ResponseWriter, err error) {
|
||||||
|
log.Println("[server error]", err)
|
||||||
|
http.Error(w, fmt.Sprintf("server error: %s", err), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func notFound(w http.ResponseWriter) {
|
||||||
|
http.Error(w, "Page Not Found", http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unauthorized(w http.ResponseWriter) {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
cfg *config.Config
|
||||||
|
mux *mux.Router
|
||||||
|
storage *storage.Storage
|
||||||
|
sess *session.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (h *Handler) getUser(r *http.Request) (string, error) {
|
||||||
|
// user, err := h.sess.Get(r)
|
||||||
|
// if err != nil {
|
||||||
|
// return "", err
|
||||||
|
// }
|
||||||
|
// if h.cfg.Env != "PROD" {
|
||||||
|
// return user, err
|
||||||
|
// }
|
||||||
|
// // Removes "https://" from referer before checking.
|
||||||
|
// // Thank you crussel for this fix!
|
||||||
|
// if !strings.HasPrefix(r.Referer()[8:], h.cfg.Host) && !strings.HasPrefix(r.Referer()[8:], user+h.cfg.Host) {
|
||||||
|
// err = errors.New("wrong referer")
|
||||||
|
// }
|
||||||
|
// return user, err
|
||||||
|
//}
|
||||||
|
|
||||||
|
func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) {
|
||||||
|
router := mux.NewRouter()
|
||||||
|
h := &Handler{
|
||||||
|
cfg: cfg,
|
||||||
|
mux: router,
|
||||||
|
storage: data,
|
||||||
|
sess: sess,
|
||||||
|
}
|
||||||
|
h.initTpl()
|
||||||
|
|
||||||
|
// Index
|
||||||
|
router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
|
||||||
|
//router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
|
||||||
|
//router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)
|
||||||
|
//router.HandleFunc("/help", h.showHelpView).Methods(http.MethodGet)
|
||||||
|
//router.HandleFunc("/profile/{username}", h.showProfileView).Methods(http.MethodGet)
|
||||||
|
//router.HandleFunc("/register", h.handleRegister)
|
||||||
|
//router.HandleFunc("/editor", h.handleEditor)
|
||||||
|
//router.HandleFunc("/upload", h.upload)
|
||||||
|
//router.HandleFunc("/files", h.handleFiles)
|
||||||
|
//router.HandleFunc("/new-folder", h.handleNewFolder)
|
||||||
|
//router.HandleFunc("/new-file", h.handleNewFile)
|
||||||
|
//router.HandleFunc("/homepages", h.showHomepagesView).Methods(http.MethodGet)
|
||||||
|
//router.HandleFunc("/rename", h.handleRename)
|
||||||
|
//router.HandleFunc("/delete", h.handleDelete).Methods(http.MethodPost)
|
||||||
|
//router.HandleFunc("/logout", h.logout).Methods(http.MethodGet)
|
||||||
|
//router.PathPrefix("/").Handler(http.FileServer(http.Dir(cfg.AssetsDir)))
|
||||||
|
|
||||||
|
return router, nil
|
||||||
|
}
|
||||||
9
web/handler/html.go
Normal file
9
web/handler/html.go
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
var TplMap = map[string]string{
|
||||||
|
"index": `{{ define "content" }}
|
||||||
|
<p>This is the index</p>
|
||||||
|
{{ end }}`,
|
||||||
|
}
|
||||||
16
web/handler/html/common/layout.html
Normal file
16
web/handler/html/common/layout.html
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{{ define "layout" }}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/style.css"/>
|
||||||
|
<title>Status</title>
|
||||||
|
{{ template "head" . }}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ template "content" . }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{{ end }}
|
||||||
|
{{ define "head" }}{{ end }}
|
||||||
3
web/handler/html/index.html
Normal file
3
web/handler/html/index.html
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{{ define "content" }}
|
||||||
|
<p>This is the index</p>
|
||||||
|
{{ end }}
|
||||||
22
web/handler/index_show.go
Normal file
22
web/handler/index_show.go
Normal file
|
|
@ -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{}{}, "")
|
||||||
|
}
|
||||||
34
web/handler/tpl.go
Normal file
34
web/handler/tpl.go
Normal file
|
|
@ -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]
|
||||||
|
}
|
||||||
58
web/session/session.go
Normal file
58
web/session/session.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
47
web/web.go
Normal file
47
web/web.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue