repos / pico

pico services - prose.sh, pastes.sh, imgs.sh, feeds.sh, pgs.sh
git clone https://github.com/picosh/pico.git

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
M lists/config.go
+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 }
M pastes/config.go
+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 }
M prose/config.go
+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 }
M wish/cms/config/config.go
+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 {
M wish/cms/db/db.go
+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)
M wish/cms/db/postgres/storage.go
+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 
M wish/cms/ui/account/create.go
+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 
M wish/cms/ui/username/username.go
+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