This commit is contained in:
parent
508f7bf4b7
commit
360ee1c5fe
12 changed files with 110 additions and 8 deletions
6
main.go
6
main.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"embed"
|
"embed"
|
||||||
|
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/dashboard"
|
||||||
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/sample"
|
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/sample"
|
||||||
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/user"
|
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/user"
|
||||||
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/webpage"
|
"git.keks.cloud/kekskurse/go-sample-webpage/pkg/webpage"
|
||||||
|
@ -21,12 +22,13 @@ var webserver embed.FS
|
||||||
var migrationFS embed.FS
|
var migrationFS embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
|
||||||
config := webpage.WebPageConfig{
|
config := webpage.WebPageConfig{
|
||||||
Templates: webserver,
|
Templates: webserver,
|
||||||
Static: webserver,
|
Static: webserver,
|
||||||
Migrations: migrationFS,
|
Migrations: migrationFS,
|
||||||
|
Token: tokenAuth,
|
||||||
Bootstrap: func(router chi.Router, template func(http.Handler) http.Handler) {
|
Bootstrap: func(router chi.Router, template func(http.Handler) http.Handler) {
|
||||||
tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
|
|
||||||
db, err := sqlx.Open("mysql", "root:test@tcp(localhost:3306)/test?multiStatements=true")
|
db, err := sqlx.Open("mysql", "root:test@tcp(localhost:3306)/test?multiStatements=true")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -40,6 +42,8 @@ func main() {
|
||||||
|
|
||||||
sample.Register(router)
|
sample.Register(router)
|
||||||
user.Register(router, template, tokenAuth, uc, d, userconfig)
|
user.Register(router, template, tokenAuth, uc, d, userconfig)
|
||||||
|
|
||||||
|
dashboard.Register(router, template)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
pkg/dashboard/dashboard.go
Normal file
32
pkg/dashboard/dashboard.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package dashboard
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
|
"github.com/unrolled/render"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// content holds our static web server content.
|
||||||
|
//go:embed templates/*
|
||||||
|
var webserver embed.FS
|
||||||
|
|
||||||
|
var ren *render.Render
|
||||||
|
|
||||||
|
func Register(router chi.Router, template func(http.Handler) http.Handler) {
|
||||||
|
ren = render.New(render.Options{
|
||||||
|
//Layout: "layout",
|
||||||
|
FileSystem: &render.EmbedFileSystem{
|
||||||
|
FS: webserver,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Group(func(r chi.Router) {
|
||||||
|
r.Use(template)
|
||||||
|
r.Get("/dashboard", sampleCall)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func sampleCall(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ren.HTML(w, http.StatusOK, "dashboard", map[string]string {"title": "World"})
|
||||||
|
}
|
|
@ -25,5 +25,5 @@ func Register(router chi.Router) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sampleCall(w http.ResponseWriter, r *http.Request) {
|
func sampleCall(w http.ResponseWriter, r *http.Request) {
|
||||||
ren.HTML(w, http.StatusOK, "ssp", map[string]string {"title": "World"})
|
ren.HTML(w, http.StatusOK, "dashboard", map[string]string {"title": "World"})
|
||||||
}
|
}
|
1
pkg/sample/templates/dashboard.tmpl
Normal file
1
pkg/sample/templates/dashboard.tmpl
Normal file
|
@ -0,0 +1 @@
|
||||||
|
BLABLA <b>Something</b>
|
|
@ -70,6 +70,7 @@ func Register(router chi.Router, template func(http.Handler) http.Handler, token
|
||||||
r.Get("/register", registerForm)
|
r.Get("/register", registerForm)
|
||||||
r.Post("/register", register)
|
r.Post("/register", register)
|
||||||
r.Get("/logout", logout)
|
r.Get("/logout", logout)
|
||||||
|
r.Get("/validate/{mail}/{token}", validateMail)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.Get("/captcha/{id}.png", captchaImage)
|
router.Get("/captcha/{id}.png", captchaImage)
|
||||||
|
@ -95,6 +96,27 @@ func Register(router chi.Router, template func(http.Handler) http.Handler, token
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateMail(w http.ResponseWriter, r *http.Request) {
|
||||||
|
mailinput := chi.URLParam(r, "mail")
|
||||||
|
token := chi.URLParam(r, "token")
|
||||||
|
|
||||||
|
mailbytes, _ := base64.StdEncoding.DecodeString(mailinput)
|
||||||
|
mail := string(mailbytes)
|
||||||
|
|
||||||
|
res, err := uc.validateMail(mail, token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == false{
|
||||||
|
w.Write([]byte("Invalide or alrady used link"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte("Account is activated, you can login now"))
|
||||||
|
}
|
||||||
|
|
||||||
func captchaImage(w http.ResponseWriter, r *http.Request) {
|
func captchaImage(w http.ResponseWriter, r *http.Request) {
|
||||||
id := chi.URLParam(r, "id")
|
id := chi.URLParam(r, "id")
|
||||||
w.Header().Set("Content-Type", "image/png")
|
w.Header().Set("Content-Type", "image/png")
|
||||||
|
@ -132,6 +154,7 @@ func login(w http.ResponseWriter, r *http.Request) {
|
||||||
expiration := time.Now().Add(365 * 24 * time.Hour)
|
expiration := time.Now().Add(365 * 24 * time.Hour)
|
||||||
cookie := http.Cookie{Name: "jwt",Value:tokenstring,Expires:expiration}
|
cookie := http.Cookie{Name: "jwt",Value:tokenstring,Expires:expiration}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
http.Redirect(w, r, "/dashboard", 301)
|
||||||
w.Write([]byte("Login ok"))
|
w.Write([]byte("Login ok"))
|
||||||
} else {
|
} else {
|
||||||
w.Write([]byte("Login failed"))
|
w.Write([]byte("Login failed"))
|
||||||
|
|
|
@ -14,6 +14,7 @@ type UserClient interface {
|
||||||
register(username, password, email string) (bool, error)
|
register(username, password, email string) (bool, error)
|
||||||
login(username, password string, requiredMailValidation bool) (bool, error)
|
login(username, password string, requiredMailValidation bool) (bool, error)
|
||||||
getMailValidationToken(email string, forceRecreate bool) (string, error)
|
getMailValidationToken(email string, forceRecreate bool) (string, error)
|
||||||
|
validateMail(email, token string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserClientMemory struct {
|
type UserClientMemory struct {
|
||||||
|
@ -26,6 +27,9 @@ func GetUserMemmoryClient() *UserClientMemory {
|
||||||
uc.users["admin"]="password"
|
uc.users["admin"]="password"
|
||||||
return &uc
|
return &uc
|
||||||
}
|
}
|
||||||
|
func (uc *UserClientMemory) validateMail(email, token string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (uc *UserClientMemory) register(username, password, email string) (bool, error) {
|
func (uc *UserClientMemory) register(username, password, email string) (bool, error) {
|
||||||
if _, ok := uc.users[username]; ok {
|
if _, ok := uc.users[username]; ok {
|
||||||
|
@ -130,6 +134,25 @@ func (uc UserClientSql) getMailValidationToken(email string, forceRecreate bool)
|
||||||
//token = fmt.Sprintf("%v/%v", base64.StdEncoding.EncodeToString([]byte(email)), token)
|
//token = fmt.Sprintf("%v/%v", base64.StdEncoding.EncodeToString([]byte(email)), token)
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
func (uc *UserClientSql) validateMail(email, token string) (bool, error) {
|
||||||
|
user := User{}
|
||||||
|
err := uc.db.Get(&user, "SELECT * FROM `user` WHERE `mail` = ?", email)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.MailValidationCode != nil && *user.MailValidationCode == token {
|
||||||
|
_, err = uc.db.NamedExec("UPDATE `user` SET `mailValidationCode` = NULL, `mailValidate` = 1 WHERE id = :id LIMIT 1", map[string]interface{}{
|
||||||
|
"id": user.Id,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func (uc UserClientSql)hashAndSalt(pwd []byte) string {
|
func (uc UserClientSql)hashAndSalt(pwd []byte) string {
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package webpage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/go-chi/jwtauth/v5"
|
||||||
"github.com/unrolled/render"
|
"github.com/unrolled/render"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
@ -41,6 +42,15 @@ func TemplateMiddelware(next http.Handler) http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.buf.Reset()
|
rw.buf.Reset()
|
||||||
render.HTML(w, http.StatusOK, "sub", template.HTML(string(content)))
|
|
||||||
|
|
||||||
|
_, claims, _ := jwtauth.FromContext(r.Context())
|
||||||
|
|
||||||
|
loggedin := false
|
||||||
|
if claims["username"] != nil {
|
||||||
|
loggedin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
render.HTML(w, http.StatusOK, "sub", map[string]interface{}{"loggedin": loggedin, "content": template.HTML(string(content))})
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"embed"
|
"embed"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/chi/v5/middleware"
|
"github.com/go-chi/chi/v5/middleware"
|
||||||
|
"github.com/go-chi/jwtauth/v5"
|
||||||
"github.com/golang-migrate/migrate/v4"
|
"github.com/golang-migrate/migrate/v4"
|
||||||
"github.com/golang-migrate/migrate/v4/database/mysql"
|
"github.com/golang-migrate/migrate/v4/database/mysql"
|
||||||
"github.com/golang-migrate/migrate/v4/source/iofs"
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
@ -20,6 +21,7 @@ type WebPageConfig struct {
|
||||||
Templates embed.FS
|
Templates embed.FS
|
||||||
Static embed.FS
|
Static embed.FS
|
||||||
Migrations embed.FS
|
Migrations embed.FS
|
||||||
|
Token *jwtauth.JWTAuth
|
||||||
Bootstrap func(router chi.Router, middlewares func(http.Handler) http.Handler)
|
Bootstrap func(router chi.Router, middlewares func(http.Handler) http.Handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +69,8 @@ func runWebpage() error {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
r.Use(middleware.Logger)
|
r.Use(middleware.Logger)
|
||||||
|
r.Use(jwtauth.Verifier(config.Token))
|
||||||
|
|
||||||
|
|
||||||
r.Handle("/static/*", http.FileServer(http.FS(config.Static)))
|
r.Handle("/static/*", http.FileServer(http.FS(config.Static)))
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
|
|
|
@ -44,16 +44,23 @@
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav me-auto">
|
<ul class="navbar-nav me-auto">
|
||||||
|
{{if .loggedin }}
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Account</a>
|
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">Account</a>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<a class="dropdown-item" href="/login">Login</a>
|
|
||||||
<a class="dropdown-item" href="/register">Register</a>
|
|
||||||
<a class="dropdown-item" href="/me">Me</a>
|
<a class="dropdown-item" href="/me">Me</a>
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item" href="/logout">Logout</a>
|
<a class="dropdown-item" href="/logout">Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
{{ else }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/register">Register</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/login">Login</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{{ . }}
|
{{ .content }}
|
2
vendor/github.com/dchest/captcha/captcha.go
generated
vendored
2
vendor/github.com/dchest/captcha/captcha.go
generated
vendored
|
@ -50,7 +50,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -137,7 +136,6 @@ func Verify(id string, digits []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
reald := globalStore.Get(id, true)
|
reald := globalStore.Get(id, true)
|
||||||
fmt.Println(reald)
|
|
||||||
if reald == nil {
|
if reald == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue