+6
-5
appview/config/config.go
+6
-5
appview/config/config.go
···
10
)
11
12
type CoreConfig struct {
13
-
CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"`
14
-
DbPath string `env:"DB_PATH, default=appview.db"`
15
-
ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"`
16
-
AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"`
17
-
Dev bool `env:"DEV, default=false"`
18
}
19
20
type OAuthConfig struct {
···
10
)
11
12
type CoreConfig struct {
13
+
CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"`
14
+
DbPath string `env:"DB_PATH, default=appview.db"`
15
+
ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"`
16
+
AppviewHost string `env:"APPVIEW_HOST, default=https://tangled.sh"`
17
+
Dev bool `env:"DEV, default=false"`
18
+
DisallowedNicknamesFile string `env:"DISALLOWED_NICKNAMES_FILE"`
19
}
20
21
type OAuthConfig struct {
+1
-1
appview/pages/templates/user/completeSignup.html
+1
-1
appview/pages/templates/user/completeSignup.html
+73
-15
appview/signup/signup.go
+73
-15
appview/signup/signup.go
···
1
package signup
2
3
import (
4
"fmt"
5
"log/slog"
6
"net/http"
7
8
"github.com/go-chi/chi/v5"
9
"github.com/posthog/posthog-go"
···
18
)
19
20
type Signup struct {
21
-
config *config.Config
22
-
db *db.DB
23
-
cf *dns.Cloudflare
24
-
posthog posthog.Client
25
-
xrpc *xrpcclient.Client
26
-
idResolver *idresolver.Resolver
27
-
pages *pages.Pages
28
-
l *slog.Logger
29
}
30
31
func New(cfg *config.Config, database *db.DB, pc posthog.Client, idResolver *idresolver.Resolver, pages *pages.Pages, l *slog.Logger) *Signup {
···
37
l.Warn("failed to create cloudflare client, signup will be disabled", "error", err)
38
}
39
}
40
41
return &Signup{
42
-
config: cfg,
43
-
db: database,
44
-
posthog: pc,
45
-
idResolver: idResolver,
46
-
cf: cf,
47
-
pages: pages,
48
-
l: l,
49
}
50
}
51
52
func (s *Signup) Router() http.Handler {
53
r := chi.NewRouter()
54
r.Post("/", s.signup)
···
128
129
if !userutil.IsValidSubdomain(username) {
130
s.pages.Notice(w, "signup-error", "Invalid username. Username must be 4–63 characters, lowercase letters, digits, or hyphens, and can't start or end with a hyphen.")
131
return
132
}
133
···
1
package signup
2
3
import (
4
+
"bufio"
5
"fmt"
6
"log/slog"
7
"net/http"
8
+
"os"
9
+
"strings"
10
11
"github.com/go-chi/chi/v5"
12
"github.com/posthog/posthog-go"
···
21
)
22
23
type Signup struct {
24
+
config *config.Config
25
+
db *db.DB
26
+
cf *dns.Cloudflare
27
+
posthog posthog.Client
28
+
xrpc *xrpcclient.Client
29
+
idResolver *idresolver.Resolver
30
+
pages *pages.Pages
31
+
l *slog.Logger
32
+
disallowedNicknames map[string]bool
33
}
34
35
func New(cfg *config.Config, database *db.DB, pc posthog.Client, idResolver *idresolver.Resolver, pages *pages.Pages, l *slog.Logger) *Signup {
···
41
l.Warn("failed to create cloudflare client, signup will be disabled", "error", err)
42
}
43
}
44
+
45
+
disallowedNicknames := loadDisallowedNicknames(cfg.Core.DisallowedNicknamesFile, l)
46
47
return &Signup{
48
+
config: cfg,
49
+
db: database,
50
+
posthog: pc,
51
+
idResolver: idResolver,
52
+
cf: cf,
53
+
pages: pages,
54
+
l: l,
55
+
disallowedNicknames: disallowedNicknames,
56
}
57
}
58
59
+
func loadDisallowedNicknames(filepath string, logger *slog.Logger) map[string]bool {
60
+
disallowed := make(map[string]bool)
61
+
62
+
if filepath == "" {
63
+
logger.Debug("no disallowed nicknames file configured")
64
+
return disallowed
65
+
}
66
+
67
+
file, err := os.Open(filepath)
68
+
if err != nil {
69
+
logger.Warn("failed to open disallowed nicknames file", "file", filepath, "error", err)
70
+
return disallowed
71
+
}
72
+
defer file.Close()
73
+
74
+
scanner := bufio.NewScanner(file)
75
+
lineNum := 0
76
+
for scanner.Scan() {
77
+
lineNum++
78
+
line := strings.TrimSpace(scanner.Text())
79
+
if line == "" || strings.HasPrefix(line, "#") {
80
+
continue // skip empty lines and comments
81
+
}
82
+
83
+
nickname := strings.ToLower(line)
84
+
if userutil.IsValidSubdomain(nickname) {
85
+
disallowed[nickname] = true
86
+
} else {
87
+
logger.Warn("invalid nickname format in disallowed nicknames file",
88
+
"file", filepath, "line", lineNum, "nickname", nickname)
89
+
}
90
+
}
91
+
92
+
if err := scanner.Err(); err != nil {
93
+
logger.Error("error reading disallowed nicknames file", "file", filepath, "error", err)
94
+
}
95
+
96
+
logger.Info("loaded disallowed nicknames", "count", len(disallowed), "file", filepath)
97
+
return disallowed
98
+
}
99
+
100
+
// isNicknameAllowed checks if a nickname is allowed (not in the disallowed list)
101
+
func (s *Signup) isNicknameAllowed(nickname string) bool {
102
+
return !s.disallowedNicknames[strings.ToLower(nickname)]
103
+
}
104
+
105
func (s *Signup) Router() http.Handler {
106
r := chi.NewRouter()
107
r.Post("/", s.signup)
···
181
182
if !userutil.IsValidSubdomain(username) {
183
s.pages.Notice(w, "signup-error", "Invalid username. Username must be 4–63 characters, lowercase letters, digits, or hyphens, and can't start or end with a hyphen.")
184
+
return
185
+
}
186
+
187
+
if !s.isNicknameAllowed(username) {
188
+
s.pages.Notice(w, "signup-error", "This username is not available. Please choose a different one.")
189
return
190
}
191