mirror of
https://github.com/1f349/tulip.git
synced 2024-12-22 16:24:10 +00:00
Improve underlying types and start writing edit page
This commit is contained in:
parent
703f3d17cd
commit
ece74ea36a
@ -114,7 +114,7 @@ func checkDbHasUser(db *database.DB) error {
|
||||
}
|
||||
if err := tx.HasUser(); err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err := tx.InsertUser("admin", "admin", "admin@localhost")
|
||||
err := tx.InsertUser("Admin", "admin", "admin", "admin@localhost")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add user: %w", err)
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ func marshalValueOrNull(null bool, data any) ([]byte, error) {
|
||||
type NullStringScanner struct{ sql.NullString }
|
||||
|
||||
func (s *NullStringScanner) Null() bool { return !s.Valid }
|
||||
func (s *NullStringScanner) Scan(src any) error { return s.Scan(src) }
|
||||
func (s *NullStringScanner) Scan(src any) error { return s.NullString.Scan(src) }
|
||||
func (s NullStringScanner) MarshalJSON() ([]byte, error) {
|
||||
return marshalValueOrNull(s.Null(), s.String)
|
||||
}
|
||||
@ -75,6 +75,14 @@ func (l *LocationScanner) Scan(src any) error {
|
||||
return nil
|
||||
}
|
||||
func (l LocationScanner) MarshalJSON() ([]byte, error) { return json.Marshal(l.Location.String()) }
|
||||
func (l *LocationScanner) UnmarshalJSON(bytes []byte) error {
|
||||
var a string
|
||||
err := json.Unmarshal(bytes, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.Scan(a)
|
||||
}
|
||||
|
||||
type LocaleScanner struct{ language.Tag }
|
||||
|
||||
@ -90,8 +98,14 @@ func (l *LocaleScanner) Scan(src any) error {
|
||||
l.Tag = lang
|
||||
return nil
|
||||
}
|
||||
func (l LocaleScanner) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(l.Tag.String())
|
||||
func (l LocaleScanner) MarshalJSON() ([]byte, error) { return json.Marshal(l.Tag.String()) }
|
||||
func (l *LocaleScanner) UnmarshalJSON(bytes []byte) error {
|
||||
var a string
|
||||
err := json.Unmarshal(bytes, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.Scan(a)
|
||||
}
|
||||
|
||||
type PronounScanner struct{ pronouns.Pronoun }
|
||||
@ -109,3 +123,11 @@ func (p *PronounScanner) Scan(src any) error {
|
||||
return nil
|
||||
}
|
||||
func (p PronounScanner) MarshalJSON() ([]byte, error) { return json.Marshal(p.Pronoun.String()) }
|
||||
func (p *PronounScanner) UnmarshalJSON(bytes []byte) error {
|
||||
var a string
|
||||
err := json.Unmarshal(bytes, &a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.Scan(a)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"database/sql"
|
||||
"github.com/MrMelon54/pronouns"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/text/language"
|
||||
@ -27,79 +27,51 @@ type User struct {
|
||||
}
|
||||
|
||||
type UserPatch struct {
|
||||
Name NullStringScanner `json:"name"`
|
||||
Picture NullStringScanner `json:"picture"`
|
||||
Website NullStringScanner `json:"website"`
|
||||
Pronouns PronounScanner `json:"pronouns"`
|
||||
Birthdate NullDateScanner `json:"birthdate"`
|
||||
ZoneInfo *time.Location `json:"zoneinfo"`
|
||||
Locale *language.Tag `json:"locale"`
|
||||
Name string
|
||||
Picture string
|
||||
Website string
|
||||
Pronouns pronouns.Pronoun
|
||||
Birthdate sql.NullTime
|
||||
ZoneInfo *time.Location
|
||||
Locale language.Tag
|
||||
}
|
||||
|
||||
func (u *UserPatch) UnmarshalJSON(bytes []byte) error {
|
||||
var m struct {
|
||||
Name string `json:"name"`
|
||||
Picture string `json:"picture"`
|
||||
Website string `json:"website"`
|
||||
Pronouns string `json:"pronouns"`
|
||||
Birthdate string `json:"birthdate"`
|
||||
ZoneInfo string `json:"zoneinfo"`
|
||||
Locale string `json:"locale"`
|
||||
}
|
||||
err := json.Unmarshal(bytes, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Name = m.Name
|
||||
|
||||
// only parse the picture address if included
|
||||
if m.Picture != "" {
|
||||
u.Picture, err = url.Parse(m.Picture)
|
||||
func (u *UserPatch) ParseFromForm(v url.Values) (err error) {
|
||||
u.Name = v.Get("name")
|
||||
u.Picture = v.Get("picture")
|
||||
u.Website = v.Get("website")
|
||||
if v.Has("reset_pronouns") {
|
||||
u.Pronouns = pronouns.TheyThem
|
||||
} else {
|
||||
u.Pronouns, err = pronouns.FindPronoun(v.Get("pronouns"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// only parse the website address if included
|
||||
if m.Website != "" {
|
||||
u.Website, err = url.Parse(m.Website)
|
||||
if v.Has("reset_birthdate") {
|
||||
u.Birthdate = sql.NullTime{}
|
||||
} else {
|
||||
u.Birthdate = sql.NullTime{Valid: true}
|
||||
u.Birthdate.Time, err = time.Parse(time.DateOnly, v.Get("birthdate"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// only parse the pronouns if included
|
||||
if m.Pronouns != "" {
|
||||
u.Pronouns, err = pronouns.FindPronoun(m.Pronouns)
|
||||
if v.Has("reset_zoneinfo") {
|
||||
u.ZoneInfo = time.UTC
|
||||
} else {
|
||||
u.ZoneInfo, err = time.LoadLocation(v.Get("zoneinfo"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// only parse the birthdate if included
|
||||
if m.Birthdate != "" {
|
||||
u.Birthdate, err = time.Parse(time.DateOnly, m.Birthdate)
|
||||
if v.Has("reset_locale") {
|
||||
u.Locale = language.AmericanEnglish
|
||||
} else {
|
||||
u.Locale, err = language.Parse(v.Get("locale"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// only parse the zoneinfo if included
|
||||
if m.ZoneInfo != "" {
|
||||
u.ZoneInfo, err = time.LoadLocation(m.ZoneInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if m.Locale != "" {
|
||||
locale, err := language.Parse(m.Locale)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Locale = &locale
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ json.Unmarshaler = &UserPatch{}
|
||||
|
@ -1,77 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/MrMelon54/pronouns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"maps"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestUserPatch_UnmarshalJSON(t *testing.T) {
|
||||
const a = `{
|
||||
"name": "Test",
|
||||
"picture": "https://example.com/logo.png",
|
||||
"website": "https://example.com",
|
||||
"gender": "robot",
|
||||
"pronouns": "they/them",
|
||||
"birthdate": "3070-01-01",
|
||||
"zoneinfo": "Europe/London",
|
||||
"locale": "en-GB"
|
||||
}`
|
||||
var p UserPatch
|
||||
assert.NoError(t, json.Unmarshal([]byte(a), &p))
|
||||
assert.Equal(t, "Test", p.Name)
|
||||
assert.Equal(t, "https://example.com/logo.png", p.Picture.String())
|
||||
assert.Equal(t, "https://example.com", p.Website.String())
|
||||
assert.Equal(t, pronouns.TheyThem, p.Pronouns)
|
||||
assert.Equal(t, time.Date(3070, time.January, 1, 0, 0, 0, 0, time.UTC), p.Birthdate)
|
||||
location, err := time.LoadLocation("Europe/London")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, location, p.ZoneInfo)
|
||||
assert.Equal(t, "en-GB", p.Locale.String())
|
||||
}
|
||||
|
||||
func TestUserPatch_UnmarshalJSON2(t *testing.T) {
|
||||
var userModifyChecks = map[string]struct{ valid, invalid []string }{
|
||||
"picture": {valid: []string{"https://example.com/icon.png"}, invalid: []string{"%/icon.png"}},
|
||||
"website": {valid: []string{"https://example.com"}, invalid: []string{"%/example.com"}},
|
||||
"pronouns": {valid: []string{"he/him", "she/her"}, invalid: []string{"a/a"}},
|
||||
"birthdate": {valid: []string{"2023-08-07", "2023-01-01"}, invalid: []string{"2023-00-00", "hello"}},
|
||||
"zoneinfo": {
|
||||
valid: []string{"Europe/London", "Europe/Berlin", "America/Los_Angeles", "America/Edmonton", "America/Montreal"},
|
||||
invalid: []string{"Europe/York", "Canada/Edmonton", "hello"},
|
||||
},
|
||||
"locale": {valid: []string{"en-GB", "en-US", "zh-CN"}, invalid: []string{"en-YY"}},
|
||||
}
|
||||
m := map[string]string{
|
||||
"name": "Test",
|
||||
"picture": "https://example.com/logo.png",
|
||||
"website": "https://example.com",
|
||||
"gender": "robot",
|
||||
"pronouns": "they/them",
|
||||
"birthdate": "3070-01-01",
|
||||
"zoneinfo": "Europe/London",
|
||||
"locale": "en-GB",
|
||||
}
|
||||
for k, v := range userModifyChecks {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
m2 := maps.Clone(m)
|
||||
for _, i := range v.valid {
|
||||
m2[k] = i
|
||||
marshal, err := json.Marshal(m2)
|
||||
assert.NoError(t, err)
|
||||
var m3 UserPatch
|
||||
assert.NoError(t, json.Unmarshal(marshal, &m3))
|
||||
}
|
||||
for _, i := range v.invalid {
|
||||
m2[k] = i
|
||||
marshal, err := json.Marshal(m2)
|
||||
assert.NoError(t, err)
|
||||
var m3 UserPatch
|
||||
assert.Error(t, json.Unmarshal(marshal, &m3))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -4,13 +4,13 @@ CREATE TABLE IF NOT EXISTS users
|
||||
name TEXT NOT NULL,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
picture TEXT,
|
||||
website TEXT,
|
||||
picture TEXT DEFAULT "" NOT NULL,
|
||||
website TEXT DEFAULT "" NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
email_verified INTEGER DEFAULT 0 NOT NULL,
|
||||
pronouns TEXT DEFAULT "they/them" NOT NULL,
|
||||
birthdate DATE,
|
||||
zoneinfo TEXT DEFAULT "" NOT NULL,
|
||||
zoneinfo TEXT DEFAULT "UTC" NOT NULL,
|
||||
locale TEXT DEFAULT "en-US" NOT NULL,
|
||||
updated_at DATETIME,
|
||||
active INTEGER DEFAULT 1
|
||||
|
@ -32,12 +32,12 @@ func (t *Tx) HasUser() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tx) InsertUser(un, pw, email string) error {
|
||||
func (t *Tx) InsertUser(name, un, pw, email string) error {
|
||||
pwHash, err := password.HashPassword(pw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = t.tx.Exec(`INSERT INTO users (subject, username, password, email) VALUES (?, ?, ?, ?)`, uuid.NewString(), un, pwHash, email)
|
||||
_, err = t.tx.Exec(`INSERT INTO users (subject, name, username, password, email) VALUES (?, ?, ?, ?, ?)`, uuid.NewString(), name, un, pwHash, email)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -113,20 +113,20 @@ func (t *Tx) ChangeUserPassword(sub uuid.UUID, pwOld, pwNew string) error {
|
||||
func (t *Tx) ModifyUser(sub uuid.UUID, v *UserPatch) error {
|
||||
exec, err := t.tx.Exec(
|
||||
`UPDATE users
|
||||
SET name = ifnull(?, name),
|
||||
picture = ifnull(?, picture),
|
||||
website = ifnull(?, website),
|
||||
pronouns = ifnull(?, pronouns),
|
||||
birthdate = ifnull(?, birthdate),
|
||||
zoneinfo = ifnull(?, zoneinfo),
|
||||
locale = ifnull(?, locale),
|
||||
SET name = ?,
|
||||
picture = ?,
|
||||
website = ?,
|
||||
pronouns = ?,
|
||||
birthdate = ?,
|
||||
zoneinfo = ?,
|
||||
locale = ?,
|
||||
updated_at = ?
|
||||
WHERE subject = ?`,
|
||||
v.Name,
|
||||
stringify(v.Picture),
|
||||
stringify(v.Website),
|
||||
v.Picture,
|
||||
v.Website,
|
||||
v.Pronouns.String(),
|
||||
sql.NullTime{Time: v.Birthdate, Valid: !v.Birthdate.IsZero()},
|
||||
v.Birthdate,
|
||||
v.ZoneInfo.String(),
|
||||
v.Locale.String(),
|
||||
time.Now().Format(time.DateTime),
|
||||
@ -164,14 +164,3 @@ func (c *clientInfoDbOutput) GetDomain() string { return c.domain }
|
||||
func (c *clientInfoDbOutput) IsPublic() bool { return false }
|
||||
func (c *clientInfoDbOutput) GetUserID() string { return "" }
|
||||
func (c *clientInfoDbOutput) IsSSO() bool { return c.sso }
|
||||
|
||||
func stringify(stringer fmt.Stringer) sql.NullString {
|
||||
if stringer == nil {
|
||||
return sql.NullString{}
|
||||
}
|
||||
return emptyToNull(stringer.String())
|
||||
}
|
||||
|
||||
func emptyToNull(a string) sql.NullString {
|
||||
return sql.NullString{String: a, Valid: a != ""}
|
||||
}
|
||||
|
@ -44,12 +44,9 @@ func TestTx_ModifyUser(t *testing.T) {
|
||||
tx, err := d.Begin()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, tx.ModifyUser(u, &UserPatch{
|
||||
Name: "example",
|
||||
Picture: nil,
|
||||
Website: nil,
|
||||
Pronouns: pronouns.Pronoun{},
|
||||
Birthdate: time.Time{},
|
||||
ZoneInfo: nil,
|
||||
Locale: &language.Tag{},
|
||||
Name: "example",
|
||||
Pronouns: pronouns.TheyThem,
|
||||
ZoneInfo: time.UTC,
|
||||
Locale: language.AmericanEnglish,
|
||||
}))
|
||||
}
|
||||
|
66
pages/edit.go.html
Normal file
66
pages/edit.go.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>1f349 ID</title>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>1f349 ID</h1>
|
||||
</header>
|
||||
<main>
|
||||
<div>Logged in as: {{.User.Name}} ({{.User.Sub}})</div>
|
||||
<div>
|
||||
<form method="POST" action="/edit">
|
||||
<input type="hidden" name="nonce" value="{{.Nonce}}">
|
||||
<div>
|
||||
<label for="field_name">Name</label>
|
||||
<input type="text" name="name" id="field_name" value="{{.User.Name}}">
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_picture">Picture</label>
|
||||
<input type="text" name="picture" id="field_picture" value="{{.User.Picture}}">
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_website">Website</label>
|
||||
<input type="text" name="website" id="field_website" value="{{.User.Picture}}">
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_pronouns">Pronouns</label>
|
||||
<select name="pronouns" id="field_pronouns">
|
||||
<option value="they/them" selected>They/Them</option>
|
||||
<option value="he/him">He/Him</option>
|
||||
<option value="she/her">She/Her</option>
|
||||
<option value="it/its">It/Its</option>
|
||||
<option value="one/one's">One/One's</option>
|
||||
</select>
|
||||
<label>Reset? <input type="checkbox" name="reset_pronouns"></label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_birthdate">Birthdate</label>
|
||||
<input type="text" name="birthdate" id="field_birthdate" value="{{.User.Birthdate}}">
|
||||
<label>Reset? <input type="checkbox" name="reset_birthdate"></label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_zoneinfo">Time Zone</label>
|
||||
<input type="text" name="zoneinfo" id="field_zoneinfo" value="{{.User.ZoneInfo}}" list="list_zoneinfo">
|
||||
<datalist id="list_zoneinfo">
|
||||
<!-- Fill in -->
|
||||
<option value="Europe/London"></option>
|
||||
</datalist>
|
||||
<label>Reset? <input type="checkbox" name="reset_zoneinfo"></label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="field_locale">Language</label>
|
||||
<input type="text" name="locale" id="field_locale" value="{{.User.Locale}}" list="list_locale">
|
||||
<datalist id="list_locale">
|
||||
<!-- Fill in -->
|
||||
<option value="en-US"></option>
|
||||
</datalist>
|
||||
<label>Reset? <input type="checkbox" name="reset_zoneinfo"></label>
|
||||
</div>
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
@ -8,9 +8,13 @@
|
||||
<h1>1f349 ID</h1>
|
||||
</header>
|
||||
<main>
|
||||
<div>Logged in as: {{.User.Name}} ({{.User.ID}})</div>
|
||||
<div>Logged in as: {{.User.Name}} ({{.User.Sub}})</div>
|
||||
<div>
|
||||
<form method="POST" action="/logout"><input type="hidden" name="nonce" value="{{.Nonce}}">
|
||||
<button onclick="location.href='/edit'">Edit Profile</button>
|
||||
</div>
|
||||
<div>
|
||||
<form method="POST" action="/logout">
|
||||
<input type="hidden" name="nonce" value="{{.Nonce}}">
|
||||
<button type="submit">Log Out</button>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -6,7 +6,10 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (h *HttpServer) dbTx(rw http.ResponseWriter, action func(tx *database.Tx) error) bool {
|
||||
// DbTx wraps a database transaction with http error messages and a simple action
|
||||
// function. If the action function returns an error the transaction will be
|
||||
// rolled back. If there is no error then the transaction is committed.
|
||||
func (h *HttpServer) DbTx(rw http.ResponseWriter, action func(tx *database.Tx) error) bool {
|
||||
tx, err := h.db.Begin()
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to begin database transaction", http.StatusInternalServerError)
|
||||
|
@ -93,9 +93,6 @@ func NewHttpServer(listen, domain string, db *database.DB, privKey []byte, clien
|
||||
return "openid", nil
|
||||
})
|
||||
|
||||
newUserUuid := uuid.New()
|
||||
fmt.Println("New User Uuid:", newUserUuid.String())
|
||||
|
||||
r.GET("/.well-known/openid-configuration", func(rw http.ResponseWriter, req *http.Request, params httprouter.Params) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_, _ = rw.Write(openIdBytes)
|
||||
@ -115,16 +112,18 @@ func NewHttpServer(listen, domain string, db *database.DB, privKey []byte, clien
|
||||
return
|
||||
}
|
||||
|
||||
hs.dbTx(rw, func(tx *database.Tx) error {
|
||||
hs.DbTx(rw, func(tx *database.Tx) error {
|
||||
userWithName, err := tx.GetUserDisplayName(auth.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get user display name: %w", err)
|
||||
}
|
||||
_ = pages.RenderPageTemplate(rw, "index", map[string]any{
|
||||
if err := pages.RenderPageTemplate(rw, "index", map[string]any{
|
||||
"Auth": auth,
|
||||
"User": userWithName,
|
||||
"Nonce": lNonce,
|
||||
})
|
||||
}); err != nil {
|
||||
log.Printf("Failed to render page: edit: %s\n", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}))
|
||||
@ -152,13 +151,15 @@ func NewHttpServer(listen, domain string, db *database.DB, privKey []byte, clien
|
||||
}
|
||||
rw.Header().Set("Content-Type", "text/html")
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
_ = pages.RenderPageTemplate(rw, "login", nil)
|
||||
if err := pages.RenderPageTemplate(rw, "login", nil); err != nil {
|
||||
log.Printf("Failed to render page: edit: %s\n", err)
|
||||
}
|
||||
}))
|
||||
r.POST("/login", hs.OptionalAuthentication(func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
|
||||
un := req.FormValue("username")
|
||||
pw := req.FormValue("password")
|
||||
var userSub uuid.UUID
|
||||
if hs.dbTx(rw, func(tx *database.Tx) error {
|
||||
if hs.DbTx(rw, func(tx *database.Tx) error {
|
||||
loginUser, err := tx.CheckLogin(un, pw)
|
||||
if err != nil {
|
||||
if errors2.Is(err, sql.ErrNoRows) || errors2.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
||||
@ -207,13 +208,16 @@ func NewHttpServer(listen, domain string, db *database.DB, privKey []byte, clien
|
||||
}
|
||||
})
|
||||
r.GET("/edit", hs.RequireAuthentication("403 Forbidden", http.StatusForbidden, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
|
||||
begin, err := db.Begin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
user, err := begin.GetUser(auth.ID)
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to read user data", http.StatusInternalServerError)
|
||||
var user *database.User
|
||||
|
||||
if hs.DbTx(rw, func(tx *database.Tx) error {
|
||||
var err error
|
||||
user, err = tx.GetUser(auth.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read user data: %w", err)
|
||||
}
|
||||
return nil
|
||||
}) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -223,33 +227,31 @@ func NewHttpServer(listen, domain string, db *database.DB, privKey []byte, clien
|
||||
http.Error(rw, "Failed to save session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = pages.RenderPageTemplate(rw, "edit", map[string]any{
|
||||
if err := pages.RenderPageTemplate(rw, "edit", map[string]any{
|
||||
"User": user,
|
||||
"Nonce": lNonce,
|
||||
})
|
||||
}); err != nil {
|
||||
log.Printf("Failed to render page: edit: %s\n", err)
|
||||
}
|
||||
}))
|
||||
r.POST("/edit", hs.RequireAuthentication("403 Forbidden", http.StatusForbidden, func(rw http.ResponseWriter, req *http.Request, params httprouter.Params, auth UserAuth) {
|
||||
if req.ParseForm() != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// TODO: parse user patch from form
|
||||
req.Form.Get("")
|
||||
|
||||
var patch database.UserPatch
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
decoder.DisallowUnknownFields()
|
||||
err := decoder.Decode(&patch)
|
||||
err := patch.ParseFromForm(req.Form)
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
begin, err := db.Begin()
|
||||
if err != nil {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if begin.ModifyUser(auth.ID, &patch) != nil {
|
||||
http.Error(rw, "Failed to modify user info", http.StatusInternalServerError)
|
||||
if hs.DbTx(rw, func(tx *database.Tx) error {
|
||||
if err := tx.ModifyUser(auth.ID, &patch); err != nil {
|
||||
return fmt.Errorf("failed to modify user info: %w", err)
|
||||
}
|
||||
return nil
|
||||
}) {
|
||||
return
|
||||
}
|
||||
http.Redirect(rw, req, "/", http.StatusFound)
|
||||
|
Loading…
Reference in New Issue
Block a user