- commit
- 5c65c99
- parent
- 141b0dd
- author
- Eric Bower
- date
- 2022-08-03 14:26:55 +0000 UTC
feat: allow registration flag I made it so when a user presses 'esc' in the account creation screen it will quit the app instead of going to the main cms screen. Also added better error handling during account creation. Implements: https://todo.sr.ht/~erock/pico.sh/21 Fixes: https://todo.sr.ht/~erock/pico.sh/41 Fixes: https://todo.sr.ht/~erock/pico.sh/1
8 files changed,
+89,
-66
+13,
-11
1@@ -14,6 +14,7 @@ func NewConfigSite() *shared.ConfigSite {
2 customdomains := shared.GetEnv("LISTS_CUSTOMDOMAINS", "0")
3 port := shared.GetEnv("LISTS_WEB_PORT", "3000")
4 protocol := shared.GetEnv("LISTS_PROTOCOL", "https")
5+ allowRegister := shared.GetEnv("LISTS_ALLOW_REGISTER", "1")
6 dbURL := shared.GetEnv("DATABASE_URL", "")
7 subdomainsEnabled := false
8 if subdomains == "1" {
9@@ -35,17 +36,18 @@ func NewConfigSite() *shared.ConfigSite {
10 SubdomainsEnabled: subdomainsEnabled,
11 CustomdomainsEnabled: customdomainsEnabled,
12 ConfigCms: config.ConfigCms{
13- Domain: domain,
14- Email: email,
15- Port: port,
16- Protocol: protocol,
17- DbURL: dbURL,
18- Description: "A microblog for your lists.",
19- IntroText: intro,
20- Space: "lists",
21- AllowedExt: []string{".txt"},
22- HiddenPosts: []string{"_header.txt", "_readme.txt"},
23- Logger: shared.CreateLogger(),
24+ Domain: domain,
25+ Email: email,
26+ Port: port,
27+ Protocol: protocol,
28+ DbURL: dbURL,
29+ Description: "A microblog for your lists.",
30+ IntroText: intro,
31+ Space: "lists",
32+ AllowedExt: []string{".txt"},
33+ HiddenPosts: []string{"_header.txt", "_readme.txt"},
34+ Logger: shared.CreateLogger(),
35+ AllowRegister: allowRegister == "1",
36 },
37 }
38 }
+11,
-9
1@@ -15,6 +15,7 @@ func NewConfigSite() *shared.ConfigSite {
2 port := shared.GetEnv("PASTES_WEB_PORT", "3000")
3 dbURL := shared.GetEnv("DATABASE_URL", "")
4 protocol := shared.GetEnv("PASTES_PROTOCOL", "https")
5+ allowRegister := shared.GetEnv("PASTES_ALLOW_REGISTER", "1")
6 subdomainsEnabled := false
7 if subdomains == "1" {
8 subdomainsEnabled = true
9@@ -35,15 +36,16 @@ func NewConfigSite() *shared.ConfigSite {
10 SubdomainsEnabled: subdomainsEnabled,
11 CustomdomainsEnabled: customdomainsEnabled,
12 ConfigCms: config.ConfigCms{
13- Domain: domain,
14- Port: port,
15- Protocol: protocol,
16- Email: email,
17- DbURL: dbURL,
18- Description: "a pastebin for hackers.",
19- IntroText: intro,
20- Space: "pastes",
21- Logger: shared.CreateLogger(),
22+ Domain: domain,
23+ Port: port,
24+ Protocol: protocol,
25+ Email: email,
26+ DbURL: dbURL,
27+ Description: "a pastebin for hackers.",
28+ IntroText: intro,
29+ Space: "pastes",
30+ Logger: shared.CreateLogger(),
31+ AllowRegister: allowRegister == "1",
32 },
33 }
34 }
+13,
-11
1@@ -14,6 +14,7 @@ func NewConfigSite() *shared.ConfigSite {
2 customdomains := shared.GetEnv("PROSE_CUSTOMDOMAINS", "0")
3 port := shared.GetEnv("PROSE_WEB_PORT", "3000")
4 protocol := shared.GetEnv("PROSE_PROTOCOL", "https")
5+ allowRegister := shared.GetEnv("PROSE_ALLOW_REGISTER", "1")
6 dbURL := shared.GetEnv("DATABASE_URL", "")
7 subdomainsEnabled := false
8 if subdomains == "1" {
9@@ -35,17 +36,18 @@ func NewConfigSite() *shared.ConfigSite {
10 SubdomainsEnabled: subdomainsEnabled,
11 CustomdomainsEnabled: customdomainsEnabled,
12 ConfigCms: config.ConfigCms{
13- Domain: domain,
14- Email: email,
15- Port: port,
16- Protocol: protocol,
17- DbURL: dbURL,
18- Description: "a blog platform for hackers.",
19- IntroText: intro,
20- Space: "prose",
21- AllowedExt: []string{".md"},
22- HiddenPosts: []string{"_readme.md", "_styles.css"},
23- Logger: shared.CreateLogger(),
24+ Domain: domain,
25+ Email: email,
26+ Port: port,
27+ Protocol: protocol,
28+ DbURL: dbURL,
29+ Description: "a blog platform for hackers.",
30+ IntroText: intro,
31+ Space: "prose",
32+ AllowedExt: []string{".md"},
33+ HiddenPosts: []string{"_readme.md", "_styles.css"},
34+ Logger: shared.CreateLogger(),
35+ AllowRegister: allowRegister == "1",
36 },
37 }
38 }
+12,
-11
1@@ -10,17 +10,18 @@ type ConfigURL interface {
2 }
3
4 type ConfigCms struct {
5- Domain string
6- Port string
7- Email string
8- Protocol string
9- DbURL string
10- Description string
11- IntroText string
12- Space string
13- AllowedExt []string
14- HiddenPosts []string
15- Logger *zap.SugaredLogger
16+ Domain string
17+ Port string
18+ Email string
19+ Protocol string
20+ DbURL string
21+ Description string
22+ IntroText string
23+ Space string
24+ AllowedExt []string
25+ HiddenPosts []string
26+ Logger *zap.SugaredLogger
27+ AllowRegister bool
28 }
29
30 func NewConfigCms() *ConfigCms {
+4,
-2
1@@ -6,7 +6,9 @@ import (
2 "time"
3 )
4
5-var ErrNameTaken = errors.New("name taken")
6+var ErrNameTaken = errors.New("username has already been claimed")
7+var ErrNameDenied = errors.New("username is on the denylist")
8+var ErrNameInvalid = errors.New("username has invalid characters in it")
9
10 type PublicKey struct {
11 ID string `json:"id"`
12@@ -100,7 +102,7 @@ type DB interface {
13 FindUserForNameAndKey(name string, key string) (*User, error)
14 FindUserForKey(name string, key string) (*User, error)
15 FindUser(userID string) (*User, error)
16- ValidateName(name string) bool
17+ ValidateName(name string) (bool, error)
18 SetUserName(userID string, name string) error
19
20 FindPosts() ([]*Post, error)
+12,
-7
1@@ -4,6 +4,7 @@ import (
2 "context"
3 "database/sql"
4 "errors"
5+ "fmt"
6 "math"
7 "strings"
8 "time"
9@@ -337,17 +338,20 @@ func (me *PsqlDB) FindUser(userID string) (*db.User, error) {
10 return user, nil
11 }
12
13-func (me *PsqlDB) ValidateName(name string) bool {
14+func (me *PsqlDB) ValidateName(name string) (bool, error) {
15 lower := strings.ToLower(name)
16 if slices.Contains(db.DenyList, lower) {
17- return false
18+ return false, fmt.Errorf("%s is invalid: %w", lower, db.ErrNameDenied)
19 }
20 v := db.NameValidator.MatchString(lower)
21 if !v {
22- return false
23+ return false, fmt.Errorf("%s is invalid: %w", lower, db.ErrNameInvalid)
24 }
25 user, _ := me.FindUserForName(lower)
26- return user == nil
27+ if user == nil {
28+ return true, nil
29+ }
30+ return false, fmt.Errorf("%s is invalid: %w", lower, db.ErrNameTaken)
31 }
32
33 func (me *PsqlDB) FindUserForName(name string) (*db.User, error) {
34@@ -376,11 +380,12 @@ func (me *PsqlDB) FindUserForNameAndKey(name string, key string) (*db.User, erro
35
36 func (me *PsqlDB) SetUserName(userID string, name string) error {
37 lowerName := strings.ToLower(name)
38- if !me.ValidateName(lowerName) {
39- return errors.New("name is already taken")
40+ valid, err := me.ValidateName(lowerName)
41+ if !valid {
42+ return err
43 }
44
45- _, err := me.Db.Exec(sqlUpdateUserName, lowerName, userID)
46+ _, err = me.Db.Exec(sqlUpdateUserName, lowerName, userID)
47 return err
48 }
49
+14,
-9
1@@ -1,6 +1,7 @@
2 package account
3
4 import (
5+ "errors"
6 "fmt"
7 "strings"
8
9@@ -134,12 +135,9 @@ func Update(msg tea.Msg, m CreateModel) (CreateModel, tea.Cmd) {
10 switch msg := msg.(type) {
11 case tea.KeyMsg:
12 switch msg.Type {
13- case tea.KeyCtrlC: // quit
14+ case tea.KeyCtrlC, tea.KeyEscape:
15 m.Quit = true
16 return m, nil
17- case tea.KeyEscape: // exit this mini-app
18- m.Done = true
19- return m, nil
20
21 default:
22 // Ignore keys if we're submitting
23@@ -240,6 +238,10 @@ func Update(msg tea.Msg, m CreateModel) (CreateModel, tea.Cmd) {
24
25 // View renders current view from the model.
26 func View(m CreateModel) string {
27+ if !m.cfg.AllowRegister {
28+ return "Registration is closed for this service. Press 'esc' to exit."
29+ }
30+
31 s := fmt.Sprintf("%s\n\n%s\n\n", m.cfg.Description, m.cfg.IntroText)
32 s += "Enter a username\n\n"
33 s += m.input.View() + "\n\n"
34@@ -288,10 +290,15 @@ func createAccount(m CreateModel) tea.Cmd {
35 return NameInvalidMsg{}
36 }
37
38+ valid, err := m.dbpool.ValidateName(m.newName)
39 // Validate before resetting the session to potentially save some
40 // network traffic and keep things feeling speedy.
41- if !m.dbpool.ValidateName(m.newName) {
42- return NameInvalidMsg{}
43+ if !valid {
44+ if errors.Is(err, db.ErrNameTaken) {
45+ return NameTakenMsg{}
46+ } else {
47+ return NameInvalidMsg{}
48+ }
49 }
50
51 user, err := registerUser(m)
52@@ -300,9 +307,7 @@ func createAccount(m CreateModel) tea.Cmd {
53 }
54
55 err = m.dbpool.SetUserName(user.ID, m.newName)
56- if err == db.ErrNameTaken {
57- return NameTakenMsg{}
58- } else if err != nil {
59+ if err != nil {
60 return errMsg{err}
61 }
62
+10,
-6
1@@ -1,6 +1,7 @@
2 package username
3
4 import (
5+ "errors"
6 "fmt"
7 "strings"
8
9@@ -262,16 +263,19 @@ func spinnerView(m Model) string {
10 // Attempt to update the username on the server.
11 func setName(m Model) tea.Cmd {
12 return func() tea.Msg {
13+ valid, err := m.dbpool.ValidateName(m.newName)
14 // Validate before resetting the session to potentially save some
15 // network traffic and keep things feeling speedy.
16- if !m.dbpool.ValidateName(m.newName) {
17- return NameInvalidMsg{}
18+ if !valid {
19+ if errors.Is(err, db.ErrNameTaken) {
20+ return NameTakenMsg{}
21+ } else {
22+ return NameInvalidMsg{}
23+ }
24 }
25
26- err := m.dbpool.SetUserName(m.user.ID, m.newName)
27- if err == db.ErrNameTaken {
28- return NameTakenMsg{}
29- } else if err != nil {
30+ err = m.dbpool.SetUserName(m.user.ID, m.newName)
31+ if err != nil {
32 return errMsg{err}
33 }
34