@@ -3,11 +3,12 @@ package main
33import (
44 "errors"
55 "fmt"
6+ "os"
67 "strings"
78
89 "charm.land/huh/v2"
910 "github.com/tinyauthapp/paerser/cli"
10- "github.com/tinyauthapp/tinyauth/internal/utils/logger "
11+ "github.com/tinyauthapp/tinyauth/internal/model "
1112 "golang.org/x/crypto/bcrypt"
1213)
1314
@@ -34,62 +35,107 @@ func createUserCmd() *cli.Command {
3435 & cli.FlagLoader {},
3536 }
3637
37- return & cli.Command {
38+ cmd := & cli.Command {
3839 Name : "create" ,
3940 Description : "Create a user" ,
4041 Configuration : tCfg ,
4142 Resources : loaders ,
42- Run : func (_ []string ) error {
43- log := logger .NewLogger ().WithSimpleConfig ()
44- log .Init ()
45-
46- if tCfg .Interactive {
47- form := huh .NewForm (
48- huh .NewGroup (
49- huh .NewInput ().Title ("Username" ).Value (& tCfg .Username ).Validate ((func (s string ) error {
50- if s == "" {
51- return errors .New ("username cannot be empty" )
52- }
53- return nil
54- })),
55- huh .NewInput ().Title ("Password" ).Value (& tCfg .Password ).Validate ((func (s string ) error {
56- if s == "" {
57- return errors .New ("password cannot be empty" )
58- }
59- return nil
60- })),
61- huh .NewSelect [bool ]().Title ("Format the output for Docker?" ).Options (huh .NewOption ("Yes" , true ), huh .NewOption ("No" , false )).Value (& tCfg .Docker ),
62- ),
63- )
64-
65- theme := new (themeBase )
66- err := form .WithTheme (theme ).Run ()
67-
68- if err != nil {
69- return fmt .Errorf ("failed to run interactive prompt: %w" , err )
70- }
71- }
43+ }
7244
73- if tCfg .Username == "" || tCfg .Password == "" {
74- return errors .New ("username and password cannot be empty" )
45+ cmd .Run = func (_ []string ) error {
46+ if tCfg .Interactive {
47+ form := huh .NewForm (
48+ huh .NewGroup (
49+ huh .NewInput ().Title ("Username" ).Value (& tCfg .Username ).Validate (func (s string ) error {
50+ if s == "" {
51+ return errors .New ("username cannot be empty" )
52+ }
53+ if strings .Contains (s , ":" ) {
54+ return errors .New ("username cannot contain ':'" )
55+ }
56+ return nil
57+ }),
58+ huh .NewInput ().Title ("Password" ).Value (& tCfg .Password ).Validate (func (s string ) error {
59+ if s == "" {
60+ return errors .New ("password cannot be empty" )
61+ }
62+ return nil
63+ }),
64+ huh .NewSelect [bool ]().Title ("Format the output for Docker?" ).Options (huh .NewOption ("Yes" , true ), huh .NewOption ("No" , false )).Value (& tCfg .Docker ),
65+ ),
66+ )
67+
68+ theme := new (themeBase )
69+
70+ err := form .WithTheme (theme ).Run ()
71+ if err != nil {
72+ return fmt .Errorf ("failed to run interactive prompt: %w" , err )
7573 }
74+ }
7675
77- log .App .Info ().Str ("username" , tCfg .Username ).Msg ("Creating user" )
76+ if tCfg .Username == "" || tCfg .Password == "" {
77+ cmd .PrintHelp (os .Stdout )
78+ return errors .New ("username and password cannot be empty" )
79+ }
7880
79- passwd , err := bcrypt .GenerateFromPassword ([]byte (tCfg .Password ), bcrypt .DefaultCost )
80- if err != nil {
81- return fmt .Errorf ("failed to hash password: %w" , err )
82- }
81+ if strings .Contains (tCfg .Username , ":" ) {
82+ return errors .New ("username cannot contain ':'" )
83+ }
8384
84- // If docker format is enabled, escape the dollar sign
85- passwdStr := string (passwd )
86- if tCfg .Docker {
87- passwdStr = strings .ReplaceAll (passwdStr , "$" , "$$" )
88- }
85+ passwd , err := bcrypt .GenerateFromPassword ([]byte (tCfg .Password ), bcrypt .DefaultCost )
86+ if err != nil {
87+ return fmt .Errorf ("failed to hash password: %w" , err )
88+ }
89+
90+ // Only the docker compose output needs $ escaped, the raw hash is correct everywhere else
91+ passwdStr := string (passwd )
92+ outputStr := passwdStr
93+
94+ if tCfg .Docker {
95+ outputStr = strings .ReplaceAll (passwdStr , "$" , "$$" )
96+ }
97+
98+ user := fmt .Sprintf ("%s:%s" , tCfg .Username , passwdStr )
99+ escapedUser := fmt .Sprintf ("%s:%s" , tCfg .Username , outputStr )
89100
90- log . App . Info (). Str ( "user" , fmt . Sprintf ( "%s:%s" , tCfg . Username , passwdStr )). Msg ( "User created" )
101+ buf := strings. Builder {}
91102
92- return nil
93- },
103+ // header
104+ fmt .Fprintf (& buf , "Created user '%s'.\n \n " , tCfg .Username )
105+
106+ // environment variable
107+ fmt .Fprint (& buf , "Environment variable:\n \n " )
108+ renderToBuf (& buf , []kv {
109+ {"TINYAUTH_AUTH_USERS" , escapedUser },
110+ }, "=" )
111+
112+ // cli flags
113+ fmt .Fprint (& buf , "\n CLI flags:\n \n " )
114+ renderToBuf (& buf , []kv {
115+ {"--auth.users" , user },
116+ }, "=" )
117+
118+ // yaml config
119+ fmt .Fprint (& buf , "\n YAML config:\n \n " )
120+
121+ err = renderYamlToBuf (& buf , & model.Config {
122+ Auth : model.AuthConfig {
123+ Users : []string {user },
124+ },
125+ })
126+
127+ if err != nil {
128+ return fmt .Errorf ("failed to render yaml config: %w" , err )
129+ }
130+
131+ buf .WriteString ("\n " )
132+
133+ // footer
134+ fmt .Fprint (& buf , "Use your config option of choice to add the user to Tinyauth and then restart." )
135+
136+ fmt .Println (buf .String ())
137+ return nil
94138 }
139+
140+ return cmd
95141}
0 commit comments