mirror of
https://github.com/thomiceli/opengist.git
synced 2025-01-09 10:02:39 +00:00
Added email and gravatar to user config
This commit is contained in:
parent
858ee3e70a
commit
7b27db8849
6 changed files with 107 additions and 23 deletions
|
@ -10,6 +10,8 @@ type User struct {
|
||||||
Password string
|
Password string
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
CreatedAt int64
|
CreatedAt int64
|
||||||
|
Email string
|
||||||
|
MD5Hash string // for gravatar, if no Email is specified, the value is random
|
||||||
|
|
||||||
Gists []Gist `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"`
|
Gists []Gist `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;foreignKey:UserID"`
|
||||||
SSHKeys []SSHKey `gorm:"foreignKey:UserID"`
|
SSHKeys []SSHKey `gorm:"foreignKey:UserID"`
|
||||||
|
@ -92,6 +94,10 @@ func (user *User) Create() error {
|
||||||
return db.Create(&user).Error
|
return db.Create(&user).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (user *User) Update() error {
|
||||||
|
return db.Save(&user).Error
|
||||||
|
}
|
||||||
|
|
||||||
func (user *User) Delete() error {
|
func (user *User) Delete() error {
|
||||||
return db.Delete(&user).Error
|
return db.Delete(&user).Error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"opengist/internal/models"
|
"opengist/internal/models"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sshKeys(ctx echo.Context) error {
|
func userSettings(ctx echo.Context) error {
|
||||||
user := getUserLogged(ctx)
|
user := getUserLogged(ctx)
|
||||||
|
|
||||||
keys, err := models.GetSSHKeysByUserID(user.ID)
|
keys, err := models.GetSSHKeysByUserID(user.ID)
|
||||||
|
@ -17,14 +21,48 @@ func sshKeys(ctx echo.Context) error {
|
||||||
return errorRes(500, "Cannot get SSH keys", err)
|
return errorRes(500, "Cannot get SSH keys", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setData(ctx, "email", user.Email)
|
||||||
setData(ctx, "sshKeys", keys)
|
setData(ctx, "sshKeys", keys)
|
||||||
setData(ctx, "htmlTitle", "Manage SSH keys")
|
setData(ctx, "htmlTitle", "Settings")
|
||||||
return html(ctx, "ssh_keys.html")
|
return html(ctx, "settings.html")
|
||||||
|
}
|
||||||
|
|
||||||
|
func emailProcess(ctx echo.Context) error {
|
||||||
|
user := getUserLogged(ctx)
|
||||||
|
email := ctx.FormValue("email")
|
||||||
|
var hash string
|
||||||
|
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
|
if email == "" {
|
||||||
|
// generate random md5 string
|
||||||
|
hash = fmt.Sprintf("%x", md5.Sum([]byte(time.Now().String())))
|
||||||
|
} else {
|
||||||
|
hash = fmt.Sprintf("%x", md5.Sum([]byte(strings.ToLower(strings.TrimSpace(email)))))
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Email = email
|
||||||
|
user.MD5Hash = hash
|
||||||
|
|
||||||
|
if err := user.Update(); err != nil {
|
||||||
|
return errorRes(500, "Cannot update email", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addFlash(ctx, "Email updated", "success")
|
||||||
|
return redirect(ctx, "/settings")
|
||||||
|
}
|
||||||
|
|
||||||
|
func accountDeleteProcess(ctx echo.Context) error {
|
||||||
|
user := getUserLogged(ctx)
|
||||||
|
|
||||||
|
if err := user.Delete(); err != nil {
|
||||||
|
return errorRes(500, "Cannot delete this user", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect(ctx, "/all")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshKeysProcess(ctx echo.Context) error {
|
func sshKeysProcess(ctx echo.Context) error {
|
||||||
setData(ctx, "htmlTitle", "Manage SSH keys")
|
|
||||||
|
|
||||||
user := getUserLogged(ctx)
|
user := getUserLogged(ctx)
|
||||||
|
|
||||||
var dto = new(models.SSHKeyDTO)
|
var dto = new(models.SSHKeyDTO)
|
||||||
|
@ -34,7 +72,7 @@ func sshKeysProcess(ctx echo.Context) error {
|
||||||
|
|
||||||
if err := ctx.Validate(dto); err != nil {
|
if err := ctx.Validate(dto); err != nil {
|
||||||
addFlash(ctx, validationMessages(&err), "error")
|
addFlash(ctx, validationMessages(&err), "error")
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
||||||
key := dto.ToSSHKey()
|
key := dto.ToSSHKey()
|
||||||
|
|
||||||
|
@ -43,7 +81,7 @@ func sshKeysProcess(ctx echo.Context) error {
|
||||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
|
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(key.Content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
addFlash(ctx, "Invalid SSH key", "error")
|
addFlash(ctx, "Invalid SSH key", "error")
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
sha := sha256.Sum256(pubKey.Marshal())
|
sha := sha256.Sum256(pubKey.Marshal())
|
||||||
|
@ -54,7 +92,7 @@ func sshKeysProcess(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
addFlash(ctx, "SSH key added", "success")
|
addFlash(ctx, "SSH key added", "success")
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshKeysDelete(ctx echo.Context) error {
|
func sshKeysDelete(ctx echo.Context) error {
|
||||||
|
@ -62,13 +100,13 @@ func sshKeysDelete(ctx echo.Context) error {
|
||||||
keyId, err := strconv.Atoi(ctx.Param("id"))
|
keyId, err := strconv.Atoi(ctx.Param("id"))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := models.GetSSHKeyByID(uint(keyId))
|
key, err := models.GetSSHKeyByID(uint(keyId))
|
||||||
|
|
||||||
if err != nil || key.UserID != user.ID {
|
if err != nil || key.UserID != user.ID {
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := key.Delete(); err != nil {
|
if err := key.Delete(); err != nil {
|
||||||
|
@ -76,5 +114,5 @@ func sshKeysDelete(ctx echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
addFlash(ctx, "SSH key deleted", "success")
|
addFlash(ctx, "SSH key deleted", "success")
|
||||||
return redirect(ctx, "/ssh-keys")
|
return redirect(ctx, "/settings")
|
||||||
}
|
}
|
|
@ -100,6 +100,9 @@ func Start() {
|
||||||
"slug": func(s string) string {
|
"slug": func(s string) string {
|
||||||
return strings.Trim(re.ReplaceAllString(strings.ToLower(s), "-"), "-")
|
return strings.Trim(re.ReplaceAllString(strings.ToLower(s), "-"), "-")
|
||||||
},
|
},
|
||||||
|
"avatarUrl": func(user *models.User) string {
|
||||||
|
return "https://www.gravatar.com/avatar/" + user.MD5Hash + "?d=identicon&s=200"
|
||||||
|
},
|
||||||
}).ParseGlob("templates/*/*.html")),
|
}).ParseGlob("templates/*/*.html")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,9 +147,11 @@ func Start() {
|
||||||
g1.POST("/login", processLogin)
|
g1.POST("/login", processLogin)
|
||||||
g1.GET("/logout", logout)
|
g1.GET("/logout", logout)
|
||||||
|
|
||||||
g1.GET("/ssh-keys", sshKeys, logged)
|
g1.GET("/settings", userSettings, logged)
|
||||||
g1.POST("/ssh-keys", sshKeysProcess, logged)
|
g1.POST("/settings/email", emailProcess, logged)
|
||||||
g1.DELETE("/ssh-keys/:id", sshKeysDelete, logged)
|
g1.DELETE("/settings/account", accountDeleteProcess, logged)
|
||||||
|
g1.POST("/settings/ssh-keys", sshKeysProcess, logged)
|
||||||
|
g1.DELETE("/settings/ssh-keys/:id", sshKeysDelete, logged)
|
||||||
|
|
||||||
g2 := g1.Group("/admin")
|
g2 := g1.Group("/admin")
|
||||||
{
|
{
|
||||||
|
|
|
@ -148,7 +148,7 @@ func validateReservedKeywords(fl validator.FieldLevel) bool {
|
||||||
name := fl.Field().String()
|
name := fl.Field().String()
|
||||||
|
|
||||||
restrictedNames := map[string]struct{}{}
|
restrictedNames := map[string]struct{}{}
|
||||||
for _, restrictedName := range []string{"register", "login", "logout", "ssh-keys", "admin", "all"} {
|
for _, restrictedName := range []string{"register", "login", "logout", "config", "admin", "all"} {
|
||||||
restrictedNames[restrictedName] = struct{}{}
|
restrictedNames[restrictedName] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
templates/base/base_header.html
vendored
4
templates/base/base_header.html
vendored
|
@ -58,7 +58,7 @@
|
||||||
{{ if .userLogged.IsAdmin }}
|
{{ if .userLogged.IsAdmin }}
|
||||||
<a href="/admin" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Admin</a>
|
<a href="/admin" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Admin</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="/ssh-keys" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">SSH Keys</a>
|
<a href="/settings" class="hidden sm:block text-slate-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Config</a>
|
||||||
|
|
||||||
<a href="/logout" id="logged-button" class="inline-flex text-slate-300 hover:bg-rose-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
<a href="/logout" id="logged-button" class="inline-flex text-slate-300 hover:bg-rose-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">
|
||||||
<p class="text-slate-300 mr-1 username">{{ .userLogged.Username }}</p>
|
<p class="text-slate-300 mr-1 username">{{ .userLogged.Username }}</p>
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
<a href="/all" class="bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium" aria-current="page">All</a>
|
<a href="/all" class="bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium" aria-current="page">All</a>
|
||||||
{{ if .userLogged }}
|
{{ if .userLogged }}
|
||||||
<a href="/{{ .userLogged.Username }}" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">My gists</a>
|
<a href="/{{ .userLogged.Username }}" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">My gists</a>
|
||||||
<a href="/ssh-keys" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">SSH Keys</a>
|
<a href="/settings" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Config</a>
|
||||||
|
|
||||||
{{ if .userLogged.IsAdmin }}
|
{{ if .userLogged.IsAdmin }}
|
||||||
<a href="/admin" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Admin</a>
|
<a href="/admin" class="text-slate-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Admin</a>
|
||||||
|
|
|
@ -2,19 +2,54 @@
|
||||||
<div class="py-10">
|
<div class="py-10">
|
||||||
<header class="pb-4">
|
<header class="pb-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold leading-tight">SSH Keys</h1>
|
<h1 class="text-2xl font-bold leading-tight">Settings</h1>
|
||||||
<p class="text-sm text-gray-400 italic">Used only to push gists using Git via SSH</p>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div >
|
<div class="space-y-4">
|
||||||
<div class="sm:grid grid-cols-2 gap-x-4 md:gap-x-16">
|
<div class="sm:grid grid-cols-2 gap-x-4 md:gap-x-16">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="bg-gray-900 rounded-md border border-1 border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
<div class="bg-gray-900 rounded-md border border-1 border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
<h2 class="text-md font-bold text-slate-300 mb-4">
|
<h2 class="text-md font-bold text-slate-300">
|
||||||
|
Email
|
||||||
|
</h2>
|
||||||
|
<h3 class="text-sm text-gray-400 italic mb-4">
|
||||||
|
Used for commits and Gravatar
|
||||||
|
</h3>
|
||||||
|
<form class="space-y-6" action="/settings/email" method="post">
|
||||||
|
<div>
|
||||||
|
<div class="mt-1">
|
||||||
|
<input id="email" name="email" value="{{ .userLogged.Email }}" type="email" required autocomplete="off" class="bg-gray-800 appearance-none block w-full px-3 py-2 border border-gray-700 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent border-gray-700 text-sm font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500">Set email</button>
|
||||||
|
{{ .csrfHtml }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="bg-gray-900 rounded-md border border-1 border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
|
<h2 class="text-md font-bold text-slate-300">
|
||||||
|
Delete account
|
||||||
|
</h2>
|
||||||
|
<form class="space-y-6" action="/settings/account" method="post" onsubmit="return confirm('Are you sure you want to delete your account ?')">
|
||||||
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
|
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent border-gray-700 text-sm font-medium rounded-md shadow-sm text-white bg-rose-600 hover:bg-rose-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 mt-2">Delete account</button>
|
||||||
|
{{ .csrfHtml }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sm:grid grid-cols-2 gap-x-4 md:gap-x-16">
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="bg-gray-900 rounded-md border border-1 border-gray-700 py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
|
<h2 class="text-md font-bold text-slate-300">
|
||||||
Add SSH Key
|
Add SSH Key
|
||||||
</h2>
|
</h2>
|
||||||
<form class="space-y-6" action="#" method="post">
|
<h3 class="text-sm text-gray-400 italic mb-4">
|
||||||
|
Used only to push gists using Git via SSH
|
||||||
|
</h3>
|
||||||
|
<form class="space-y-6" action="/settings/ssh-keys" method="post">
|
||||||
<div>
|
<div>
|
||||||
<label for="title" class="block text-sm font-medium text-slate-300"> Title </label>
|
<label for="title" class="block text-sm font-medium text-slate-300"> Title </label>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
|
@ -53,7 +88,7 @@
|
||||||
<p class="text-xs text-gray-500 line-clamp-2">Last used <span class="moment-timestamp">{{ .LastUsedAt }}</span></p>
|
<p class="text-xs text-gray-500 line-clamp-2">Last used <span class="moment-timestamp">{{ .LastUsedAt }}</span></p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
<form action="/ssh-keys/{{.ID}}" method="post" class="inline-block" onsubmit="return confirm('Confirm deletion of SSH key')">
|
<form action="/settings/ssh-keys/{{.ID}}" method="post" class="inline-block" onsubmit="return confirm('Confirm deletion of SSH key')">
|
||||||
<input type="hidden" name="_method" value="DELETE">
|
<input type="hidden" name="_method" value="DELETE">
|
||||||
{{ $.csrfHtml }}
|
{{ $.csrfHtml }}
|
||||||
|
|
Loading…
Reference in a new issue