forked from tildetown/town
174 lines
3.6 KiB
Go
174 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/mail"
|
|
"os"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"git.tilde.town/tildetown/town/invites"
|
|
"git.tilde.town/tildetown/town/stats"
|
|
"github.com/AlecAivazis/survey/v2"
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
_ "embed"
|
|
)
|
|
|
|
//go:embed welcome.txt
|
|
var welcomeArt string
|
|
|
|
// TODO move magic key machine to static page
|
|
|
|
type newUserData struct {
|
|
Username string
|
|
DisplayName string
|
|
Email string
|
|
PubKey string
|
|
}
|
|
|
|
func surveyIconSet(icons *survey.IconSet) {
|
|
icons.Question.Text = "~"
|
|
icons.Question.Format = "magenta:b"
|
|
}
|
|
|
|
func promptCode() (code string, err error) {
|
|
codePrompt := &survey.Input{
|
|
Message: "invite code?",
|
|
}
|
|
err = survey.AskOne(codePrompt, &code,
|
|
survey.WithValidator(survey.Required),
|
|
survey.WithIcons(surveyIconSet))
|
|
code = strings.TrimSpace(code)
|
|
return
|
|
}
|
|
|
|
func promptUsername(townData stats.TildeData) (un string, err error) {
|
|
usernameRE := regexp.MustCompile(`^[a-z][-a-z0-9_]*$`)
|
|
unPrompt := &survey.Input{
|
|
Message: "desired username?",
|
|
}
|
|
err = survey.AskOne(unPrompt, &un,
|
|
survey.WithValidator(survey.Required),
|
|
survey.WithIcons(surveyIconSet),
|
|
survey.WithValidator(func(val interface{}) error {
|
|
un := val.(string)
|
|
if len(un) > 32 {
|
|
return fmt.Errorf("username '%s' is too long", un)
|
|
}
|
|
|
|
return nil
|
|
}),
|
|
survey.WithValidator(func(val interface{}) error {
|
|
un := val.(string)
|
|
if !usernameRE.MatchString(un) {
|
|
return errors.New("usernames must start with a letter and only contain letters, nubers, - or _")
|
|
}
|
|
|
|
return nil
|
|
}),
|
|
survey.WithValidator(func(val interface{}) error {
|
|
un := val.(string)
|
|
for _, v := range townData.Users {
|
|
if v.Username == un {
|
|
return fmt.Errorf("username '%s' is already in use", un)
|
|
}
|
|
}
|
|
return nil
|
|
}))
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func promptEmail(defaultEmail string) (email string, err error) {
|
|
emailPrompt := &survey.Input{
|
|
Message: "e-mail (for account recovery only)?",
|
|
Default: defaultEmail,
|
|
}
|
|
err = survey.AskOne(emailPrompt, &email,
|
|
survey.WithValidator(survey.Required),
|
|
survey.WithIcons(surveyIconSet),
|
|
survey.WithValidator(func(val interface{}) error {
|
|
email := val.(string)
|
|
_, err := mail.ParseAddress(email)
|
|
if err != nil {
|
|
return fmt.Errorf("'%s' doesn't look like an email: %w", email, err)
|
|
}
|
|
|
|
if !strings.Contains(email, ".") {
|
|
return fmt.Errorf("'%s' doesn't look like an email: domain not fully qualified", email)
|
|
}
|
|
|
|
return nil
|
|
}))
|
|
|
|
return "", nil
|
|
}
|
|
|
|
func _main() error {
|
|
townData, err := stats.Stats()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
inviteDB, err := invites.ConnectDB()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data := &newUserData{}
|
|
|
|
s := lipgloss.NewStyle().
|
|
Foreground(lipgloss.AdaptiveColor{
|
|
Light: "#7D19BD",
|
|
Dark: "#E0B0FF",
|
|
})
|
|
|
|
s = s.SetString(welcomeArt)
|
|
fmt.Println(s)
|
|
|
|
code, err := promptCode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
invite, err := invites.Get(inviteDB, code)
|
|
if err != nil {
|
|
return fmt.Errorf("could not look up invite code: %w", err)
|
|
}
|
|
|
|
if invite.Used {
|
|
return errors.New("that invite code has already been used.")
|
|
}
|
|
|
|
s = s.SetString("thanks!! just gotta collect some information now and then your account will be ready.")
|
|
|
|
fmt.Println(s)
|
|
|
|
data.Username, err = promptUsername(townData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
data.Email, err = promptEmail(invite.Email)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO collect public key
|
|
// TODO have enough to make account; can now do that
|
|
// TODO assuming account creation succeeded, mark invite as used
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
// TODO friendlier error handling
|
|
err := _main()
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|