forked from tildetown/town
add welcome message
parent
9444954bc6
commit
6a1fcbcf32
|
@ -4,26 +4,22 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/mail"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.tilde.town/tildetown/town/invites"
|
"git.tilde.town/tildetown/town/invites"
|
||||||
"git.tilde.town/tildetown/town/sshkey"
|
|
||||||
"git.tilde.town/tildetown/town/stats"
|
"git.tilde.town/tildetown/town/stats"
|
||||||
"github.com/AlecAivazis/survey/v2"
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO move magic key machine to static page
|
||||||
|
// TODO link to code of conduct as part of the form and ask if they agree
|
||||||
|
|
||||||
//go:embed welcome.txt
|
//go:embed welcome.txt
|
||||||
var welcomeArt string
|
var welcomeArt string
|
||||||
|
|
||||||
// TODO move magic key machine to static page
|
|
||||||
|
|
||||||
type newUserData struct {
|
type newUserData struct {
|
||||||
Username string
|
Username string
|
||||||
DisplayName string
|
DisplayName string
|
||||||
|
@ -31,148 +27,12 @@ type newUserData struct {
|
||||||
PubKey string
|
PubKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func surveyIconSet(icons *survey.IconSet) {
|
func defaultStyle() lipgloss.Style {
|
||||||
icons.Question.Text = "~"
|
return lipgloss.NewStyle().
|
||||||
icons.Question.Format = "magenta:b"
|
Foreground(lipgloss.AdaptiveColor{
|
||||||
}
|
Light: "#7D19BD",
|
||||||
|
Dark: "#E0B0FF",
|
||||||
func promptCode() (code string, err error) {
|
})
|
||||||
err = survey.AskOne(&survey.Input{
|
|
||||||
Message: "invite code?",
|
|
||||||
}, &code,
|
|
||||||
survey.WithValidator(survey.Required),
|
|
||||||
survey.WithIcons(surveyIconSet))
|
|
||||||
code = strings.TrimSpace(code)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func confirmContinue() (conf bool, err error) {
|
|
||||||
err = survey.AskOne(
|
|
||||||
&survey.Confirm{
|
|
||||||
Message: "Does the above look ok?",
|
|
||||||
}, &conf,
|
|
||||||
survey.WithValidator(survey.Required),
|
|
||||||
survey.WithIcons(surveyIconSet))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type asker struct {
|
|
||||||
UserData *newUserData
|
|
||||||
Style lipgloss.Style
|
|
||||||
Invite invites.Invite
|
|
||||||
TownData stats.TildeData
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *asker) Ask() (err error) {
|
|
||||||
// TODO somehow un and email getting set to "" but pubkey works fine?
|
|
||||||
|
|
||||||
if err = a.promptUsername(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.promptEmail(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = a.promptKey(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := a.Style.SetString(
|
|
||||||
fmt.Sprintf(`ok! your account is about to be created with the following details:
|
|
||||||
|
|
||||||
username: %s
|
|
||||||
email: %s
|
|
||||||
pubkey: %s`, a.UserData.Username, a.UserData.Email, a.UserData.PubKey)).Bold(true).MaxWidth(80)
|
|
||||||
|
|
||||||
fmt.Println(s)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *asker) promptUsername() (err error) {
|
|
||||||
// copied from /etc/adduser.conf
|
|
||||||
usernameRE := regexp.MustCompile(`^[a-z][-a-z0-9_]*$`)
|
|
||||||
err = survey.AskOne(
|
|
||||||
&survey.Input{
|
|
||||||
Message: "desired username?",
|
|
||||||
Default: a.UserData.Username,
|
|
||||||
}, &a.UserData.Username,
|
|
||||||
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 a.TownData.Users {
|
|
||||||
if v.Username == un {
|
|
||||||
return fmt.Errorf("username '%s' is already in use", un)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *asker) promptEmail() (err error) {
|
|
||||||
err = survey.AskOne(
|
|
||||||
&survey.Input{
|
|
||||||
Message: "e-mail (for account recovery only)?",
|
|
||||||
Default: a.UserData.Email,
|
|
||||||
}, &a.UserData.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
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *asker) promptKey() (err error) {
|
|
||||||
err = survey.AskOne(
|
|
||||||
&survey.Input{
|
|
||||||
Message: "SSH public key?",
|
|
||||||
Default: a.UserData.PubKey,
|
|
||||||
}, &a.UserData.PubKey,
|
|
||||||
survey.WithValidator(survey.Required),
|
|
||||||
survey.WithIcons(surveyIconSet),
|
|
||||||
survey.WithValidator(func(v interface{}) error {
|
|
||||||
key := v.(string)
|
|
||||||
valid, err := sshkey.ValidKey(key)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to validate key: %w", err)
|
|
||||||
}
|
|
||||||
if !valid {
|
|
||||||
return errors.New("that doesn't seem like a valid SSH key. try another public key?")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func _main() error {
|
func _main() error {
|
||||||
|
@ -181,13 +41,7 @@ func _main() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s := lipgloss.NewStyle().
|
s := defaultStyle().SetString(welcomeArt)
|
||||||
Foreground(lipgloss.AdaptiveColor{
|
|
||||||
Light: "#7D19BD",
|
|
||||||
Dark: "#E0B0FF",
|
|
||||||
})
|
|
||||||
|
|
||||||
s = s.SetString(welcomeArt)
|
|
||||||
fmt.Println(s)
|
fmt.Println(s)
|
||||||
|
|
||||||
code, err := promptCode()
|
code, err := promptCode()
|
||||||
|
@ -220,7 +74,7 @@ func _main() error {
|
||||||
UserData: data,
|
UserData: data,
|
||||||
Invite: *invite,
|
Invite: *invite,
|
||||||
TownData: townData,
|
TownData: townData,
|
||||||
Style: s,
|
Style: defaultStyle(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.Ask(); err != nil {
|
if err = a.Ask(); err != nil {
|
||||||
|
@ -252,11 +106,30 @@ Please email root@tilde.town and paste the following error:
|
||||||
|
|
||||||
%s
|
%s
|
||||||
|
|
||||||
Your invite code has not been marked as used and you're welcome to try again, though if there is a system issue you might need to wait for word from an admin.`, err.Error()))
|
Your invite code has not been marked as used and you're welcome to try again,
|
||||||
|
though if there is a system issue you might need to wait for word from an admin.`, err.Error()))
|
||||||
fmt.Println(s)
|
fmt.Println(s)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s = s.SetString(fmt.Sprintf(`OK! your user account has been created.
|
||||||
|
|
||||||
|
welcome, ~%[1]s <3
|
||||||
|
|
||||||
|
This program is going to exit and you are now free to ssh to town as yourself:
|
||||||
|
|
||||||
|
ssh %[1]s@tilde.town
|
||||||
|
|
||||||
|
if your public key isn't found by ssh, you'll need to explain to ssh how to find it with:
|
||||||
|
|
||||||
|
ssh -i "replace with path to public key file" %[1]s@tilde.town
|
||||||
|
|
||||||
|
for help with ssh, see: https://tilde.town/wiki/getting-started/ssh.html
|
||||||
|
|
||||||
|
if you end up very stuck, you can email root@tilde.town .`, data.Username))
|
||||||
|
|
||||||
|
fmt.Println(s)
|
||||||
|
|
||||||
// TODO mark invite as used
|
// TODO mark invite as used
|
||||||
// TODO add user to town.db
|
// TODO add user to town.db
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue