Add email
This commit is contained in:
parent
ecf4203f39
commit
0b08befaa8
16 changed files with 111 additions and 36 deletions
|
|
@ -50,10 +50,6 @@ dd {
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.profile-picture {
|
||||
float: right;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
|
|
|||
8
go.mod
8
go.mod
|
|
@ -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
14
go.sum
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type User struct {
|
|||
About string
|
||||
Style string
|
||||
Picture string
|
||||
Email string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strconv"
|
||||
)
|
||||
|
||||
const schemaVersion = 6
|
||||
const schemaVersion = 7
|
||||
|
||||
func Migrate(db *sql.DB) {
|
||||
var currentVersion int
|
||||
|
|
|
|||
|
|
@ -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 '';`,
|
||||
}
|
||||
|
|
|
|||
2
storage/sql/schema_version_7.sql
Normal file
2
storage/sql/schema_version_7.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
alter table users
|
||||
add column email varchar(500) not null DEFAULT '';
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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/",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue