Add email

This commit is contained in:
m15o 2021-11-28 15:31:51 +01:00
parent ecf4203f39
commit 0b08befaa8
16 changed files with 111 additions and 36 deletions

View file

@ -50,10 +50,6 @@ dd {
margin-bottom: 1em;
}
.profile-picture {
float: right;
}
nav {
margin-bottom: 1em;
}

8
go.mod
View file

@ -3,11 +3,13 @@ module status
go 1.16
require (
github.com/fogleman/gg v1.3.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/gorilla/feeds v1.1.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/sessions v1.2.1
github.com/kr/pretty v0.3.0 // indirect
github.com/lib/pq v1.10.4
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
golang.org/x/image v0.0.0-20211028202545-6944b10bf410
)

14
go.sum
View file

@ -1,15 +1,27 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
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/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 h1:/pEO3GD/ABYAjuakUS6xSEmmlyVS4kxBNkeA9tLJiTI=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
@ -21,3 +33,5 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=

View file

@ -15,6 +15,7 @@ type User struct {
About string
Style string
Picture string
Email string
CreatedAt time.Time
}

View file

@ -7,7 +7,7 @@ import (
"strconv"
)
const schemaVersion = 6
const schemaVersion = 7
func Migrate(db *sql.DB) {
var currentVersion int

View file

@ -38,4 +38,6 @@ alter column about TYPE TEXT;`,
add column style TEXT not null DEFAULT '';`,
"schema_version_6": `alter table users
add column picture varchar(500) not null DEFAULT '';`,
"schema_version_7": `alter table users
add column email varchar(500) not null DEFAULT '';`,
}

View file

@ -0,0 +1,2 @@
alter table users
add column email varchar(500) not null DEFAULT '';

View file

@ -4,10 +4,10 @@ import (
"status/model"
)
const queryFindName = `SELECT name, hash, created_at, homepage, about, style, picture FROM users WHERE name=lower($1);`
const queryFindName = `SELECT name, hash, created_at, homepage, about, style, picture, email FROM users WHERE name=lower($1);`
func (s *Storage) queryUser(q string, params ...interface{}) (user model.User, err error) {
err = s.db.QueryRow(q, params...).Scan(&user.Name, &user.Hash, &user.CreatedAt, &user.Homepage, &user.About, &user.Style, &user.Picture)
err = s.db.QueryRow(q, params...).Scan(&user.Name, &user.Hash, &user.CreatedAt, &user.Homepage, &user.About, &user.Style, &user.Picture, &user.Email)
return
}
@ -77,11 +77,11 @@ func (s *Storage) DeleteUser(username string) error {
return err
}
func (s *Storage) UpdateSettings(username, homepage, about, style, picture string) error {
stmt, err := s.db.Prepare(`UPDATE users SET homepage = $1, about = $2, style = $3, picture = $4 WHERE name = $5;`)
func (s *Storage) UpdateSettings(username, homepage, about, style, picture, email string) error {
stmt, err := s.db.Prepare(`UPDATE users SET homepage = $1, about = $2, style = $3, picture = $4, email = $5 WHERE name = $6;`)
if err != nil {
return err
}
_, err = stmt.Exec(homepage, about, style, picture, username)
_, err = stmt.Exec(homepage, about, style, picture, email, username)
return err
}

View file

@ -9,6 +9,7 @@ type SettingsForm struct {
About string
Style string
Picture string
Email string
}
func NewSettingsForm(r *http.Request) *SettingsForm {
@ -17,5 +18,6 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
About: r.FormValue("about"),
Style: r.FormValue("style"),
Picture: r.FormValue("picture"),
Email: r.FormValue("email"),
}
}

View file

@ -63,7 +63,6 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
sess: sess,
}
h.initTpl()
router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)
@ -80,8 +79,10 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
router.HandleFunc("/edit", h.updateStatus).Methods(http.MethodPost)
router.HandleFunc("/manage", h.showManageView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet)
router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet)
router.PathPrefix("/assets/").Handler(
http.StripPrefix("/assets/",

View file

@ -179,12 +179,6 @@ var TplMap = map[string]string{
<input type="submit" value="Submit">
</form>
</section>
{{ end }}`,
"remove_success": `{{ define "content" }}
<section>
{{ template "flash" .flash }}
<a href="/">Back home</a>
</section>
{{ end }}`,
"settings": `{{ define "content" }}
<section>
@ -196,22 +190,27 @@ var TplMap = map[string]string{
<form action="/settings-update" method="post">
<div class="field">
<label for="homepage">homepage</label>
<input type="text" name="homepage" id="homepage" value="{{ .Homepage }}" autocomplete="off"/>
<input type="text" name="homepage" id="homepage" value="{{ .User.Homepage }}" autocomplete="off"/>
</div>
<div class="field">
<label for="email">Email</label>
<input type="text" name="email" id="email" value="{{ .User.Email }}" autocomplete="off"/>
</div>
<div class="field">
<label for="picture">picture URL</label>
<input type="text" name="picture" id="picture" value="{{ .Picture }}" autocomplete="off"/>
<input type="text" name="picture" id="picture" value="{{ .User.Picture }}" autocomplete="off"/>
</div>
<div class="field">
<label for="about">about</label>
<textarea name="about" id="about" rows="20">{{ .About }}</textarea>
<textarea name="about" id="about" rows="20">{{ .User.About }}</textarea>
</div>
<div class="field">
<label for="style">style</label>
<textarea name="style" id="style" rows="20">{{ .Style }}</textarea>
<textarea name="style" id="style" rows="20">{{ .User.Style }}</textarea>
</div>
<input type="submit" value="Submit">
</form>
@ -224,11 +223,13 @@ var TplMap = map[string]string{
{{ define "content" }}
<div class="cols">
<section>
<img src="{{ .picture }}" width="100" class="profile-picture"/>
<h2>{{ .user }}</h2>
<img src="{{ .picture }}" width="100" class="profile-picture"/>
<dl>
<dt>Homepage</dt>
<dd><a href="{{ .homepage }}" target="_blank">{{ .homepage }}</a></dd>
<dt>Email</dt>
<dd><a href="{{ .email }}" target="_blank">{{ .email }}</a></dd>
<dt>About</dt>
<dd>{{ .about }}</dd>
</dl>

View file

@ -8,22 +8,27 @@
<form action="/settings-update" method="post">
<div class="field">
<label for="homepage">homepage</label>
<input type="text" name="homepage" id="homepage" value="{{ .Homepage }}" autocomplete="off"/>
<input type="text" name="homepage" id="homepage" value="{{ .User.Homepage }}" autocomplete="off"/>
</div>
<div class="field">
<label for="email">Email</label>
<input type="text" name="email" id="email" value="{{ .User.Email }}" autocomplete="off"/>
</div>
<div class="field">
<label for="picture">picture URL</label>
<input type="text" name="picture" id="picture" value="{{ .Picture }}" autocomplete="off"/>
<input type="text" name="picture" id="picture" value="{{ .User.Picture }}" autocomplete="off"/>
</div>
<div class="field">
<label for="about">about</label>
<textarea name="about" id="about" rows="20">{{ .About }}</textarea>
<textarea name="about" id="about" rows="20">{{ .User.About }}</textarea>
</div>
<div class="field">
<label for="style">style</label>
<textarea name="style" id="style" rows="20">{{ .Style }}</textarea>
<textarea name="style" id="style" rows="20">{{ .User.Style }}</textarea>
</div>
<input type="submit" value="Submit">
</form>

View file

@ -5,11 +5,13 @@
{{ define "content" }}
<div class="cols">
<section>
<img src="{{ .picture }}" width="100" class="profile-picture"/>
<h2>{{ .user }}</h2>
<img src="{{ .picture }}" width="100" class="profile-picture"/>
<dl>
<dt>Homepage</dt>
<dd><a href="{{ .homepage }}" target="_blank">{{ .homepage }}</a></dd>
<dt>Email</dt>
<dd><a href="{{ .email }}" target="_blank">{{ .email }}</a></dd>
<dt>About</dt>
<dd>{{ .about }}</dd>
</dl>

View file

@ -24,10 +24,7 @@ func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
}
session.Save(r, w)
h.renderLayout(w, "settings", map[string]interface{}{
"flash": flash,
"About": user.About,
"Homepage": user.Homepage,
"Style": user.Style,
"Picture": user.Picture,
"flash": flash,
"User": user,
}, username)
}

View file

@ -13,7 +13,7 @@ func (h *Handler) updateSettings(w http.ResponseWriter, r *http.Request) {
return
}
f := form.NewSettingsForm(r)
if err := h.storage.UpdateSettings(user, f.Homepage, f.About, f.Style, f.Picture); err != nil {
if err := h.storage.UpdateSettings(user, f.Homepage, f.About, f.Style, f.Picture, f.Email); err != nil {
serverError(w, err)
return
}

View file

@ -2,9 +2,11 @@ package handler
import (
"encoding/json"
"fmt"
"github.com/fogleman/gg"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"github.com/gorilla/feeds"
"github.com/gorilla/mux"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/goregular"
@ -14,6 +16,7 @@ import (
"image/color"
"image/draw"
"strconv"
"time"
//"image/png"
"net/http"
@ -77,6 +80,7 @@ func (h *Handler) showUserView(w http.ResponseWriter, r *http.Request) {
"homepage": user.Homepage,
"about": template.HTML(user.About),
"picture": user.Picture,
"email": user.Email,
"style": template.CSS(user.Style),
"showMore": showMore,
"page": page,
@ -197,3 +201,49 @@ func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request
//avatar := createAvatar(text)
//png.Encode(w, avatar)
}
func (h *Handler) showAtomView(w http.ResponseWriter, r *http.Request) {
username := mux.Vars(r)["user"]
user, err := h.storage.UserByName(username)
if err != nil {
notFound(w)
return
}
now := time.Now()
feed := &feeds.Feed{
Title: user.Name,
Link: &feeds.Link{Href: fmt.Sprintf("https://status.cafe/users/%s", username)}, // TODO change the scheme?
Author: &feeds.Author{Name: user.Name, Email: "todo@todo.com"}, // TODO EMAIL
Created: now,
}
statuses, _, err := h.storage.StatusByUsername(user.Name, 20, 0)
if err != nil {
serverError(w, err)
return
}
for _, status := range statuses {
if err != nil {
serverError(w, err)
return
}
feed.Items = append(feed.Items, &feeds.Item{
Title: status.Content,
Link: &feeds.Link{Href: fmt.Sprintf("https://status.cafe/users/%s/%d", username, status.Id)},
Author: &feeds.Author{Name: user.Name},
Content: status.Content,
Created: status.CreatedAt,
})
}
atom, err := feed.ToAtom()
if err != nil {
serverError(w, err)
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Content-Type", "application/atom+xml")
w.Write([]byte(atom))
}