Skip to content

Commit be1c5cf

Browse files
committed
add MVP gui.
1 parent 968ac68 commit be1c5cf

File tree

9 files changed

+527
-18
lines changed

9 files changed

+527
-18
lines changed

cmd/serve.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,17 @@ func init() {
1515
Use: "serve",
1616
Short: "Start HTTP server.",
1717
Run: func(cmd *cobra.Command, args []string) {
18-
err := internal.Serve(config)
18+
if config.Gui.Enabled {
19+
go func() {
20+
// TODO: Shutdown.
21+
err := internal.ServeGui(config)
22+
if err != nil {
23+
log.Panic().Err(err).Msg("unable to start gui command")
24+
}
25+
}()
26+
}
27+
28+
err := internal.ServeApp(config)
1929
if err != nil {
2030
log.Panic().Err(err).Msg("unable to start serve command")
2131
}

config.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,10 @@ redis:
101101
password: ""
102102
# Redis database.
103103
database: 0
104+
105+
# (MVP) gui administation, very basic.
106+
gui:
107+
# If GUI should be enabled.
108+
enabled: false
109+
# GUI bind address.
110+
bind: "127.0.0.1:8081"

internal/config/config.go

Lines changed: 89 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,18 @@ type App struct {
3333
Target string
3434
Bind string
3535
Proxy bool
36-
Users map[string]string // username:password
37-
Emails []string // list of emails or @domains
38-
Roles map[string]string // username/email:role
36+
37+
// username:password
38+
Users map[string]string
39+
usersFile string
40+
41+
// list of emails or @domains
42+
Emails []string
43+
emailsFile string
44+
45+
// username/email:role
46+
Roles map[string]string
47+
rolesFile string
3948

4049
RedirectAllowlist []url.URL
4150

@@ -205,9 +214,9 @@ func (c *App) Set() {
205214
emails := viper.GetStringSlice("app.emails")
206215

207216
// load emails from a file
208-
emailsFile := viper.GetString("app.emails_file")
209-
if emailsFile != "" {
210-
emailsBytes, err := os.ReadFile(emailsFile)
217+
c.emailsFile = viper.GetString("app.emails_file")
218+
if c.emailsFile != "" {
219+
emailsBytes, err := os.ReadFile(c.emailsFile)
211220
if err != nil {
212221
log.Panic().Err(err).Msgf("error opening emails file")
213222
}
@@ -220,9 +229,9 @@ func (c *App) Set() {
220229
users := viper.GetStringSlice("app.users")
221230

222231
// load users from a file
223-
usersFile := viper.GetString("app.users_file")
224-
if usersFile != "" {
225-
usersBytes, err := os.ReadFile(usersFile)
232+
c.usersFile = viper.GetString("app.users_file")
233+
if c.usersFile != "" {
234+
usersBytes, err := os.ReadFile(c.usersFile)
226235
if err != nil {
227236
log.Panic().Err(err).Msgf("error opening users file")
228237
}
@@ -235,9 +244,9 @@ func (c *App) Set() {
235244
roles := viper.GetStringSlice("app.roles")
236245

237246
// load roles from a file
238-
rolesFile := viper.GetString("app.roles_file")
239-
if rolesFile != "" {
240-
rolesBytes, err := os.ReadFile(rolesFile)
247+
c.rolesFile = viper.GetString("app.roles_file")
248+
if c.rolesFile != "" {
249+
rolesBytes, err := os.ReadFile(c.rolesFile)
241250
if err != nil {
242251
log.Panic().Err(err).Msgf("error opening roles file")
243252
}
@@ -324,6 +333,46 @@ func (c *App) Set() {
324333
c.Expiration.Session = time.Duration(viper.GetInt64("app.expiration.session")) * time.Second
325334
}
326335

336+
func (c *App) SaveEmails() error {
337+
// if there is no emails file, we cannot save anything
338+
if c.emailsFile == "" {
339+
return fmt.Errorf("no emails file specified")
340+
}
341+
342+
payload := []byte(strings.Join(c.Emails, "\n"))
343+
return os.WriteFile(c.emailsFile, payload, 0644)
344+
}
345+
346+
func (c *App) SaveUsers() error {
347+
// if there is no users file, we cannot save anything
348+
if c.usersFile == "" {
349+
return fmt.Errorf("no users file specified")
350+
}
351+
352+
users := []string{}
353+
for username, secret := range c.Users {
354+
users = append(users, fmt.Sprintf("%s:%s", username, secret))
355+
}
356+
357+
payload := []byte(strings.Join(users, "\n"))
358+
return os.WriteFile(c.usersFile, payload, 0644)
359+
}
360+
361+
func (c *App) SaveRoles() error {
362+
// if there is no roles file, we cannot save anything
363+
if c.rolesFile == "" {
364+
return fmt.Errorf("no roles file specified")
365+
}
366+
367+
roles := []string{}
368+
for user, role := range c.Roles {
369+
roles = append(roles, fmt.Sprintf("%s=%s", user, role))
370+
}
371+
372+
payload := []byte(strings.Join(roles, "\n"))
373+
return os.WriteFile(c.rolesFile, payload, 0644)
374+
}
375+
327376
//
328377
// tmpl
329378
//
@@ -492,3 +541,31 @@ func (c *Redis) Set() {
492541
c.Password = viper.GetString("redis.password")
493542
c.Database = viper.GetInt("redis.database")
494543
}
544+
545+
//
546+
// gui
547+
//
548+
549+
type Gui struct {
550+
Enabled bool
551+
Bind string
552+
}
553+
554+
func (Gui) Init(cmd *cobra.Command) error {
555+
cmd.PersistentFlags().Bool("gui.enabled", false, "If GUI should be enabled.")
556+
if err := viper.BindPFlag("gui.enabled", cmd.PersistentFlags().Lookup("gui.enabled")); err != nil {
557+
return err
558+
}
559+
560+
cmd.PersistentFlags().String("gui.bind", "127.0.0.1:8081", "Address, where is HTTP server listening.")
561+
if err := viper.BindPFlag("gui.bind", cmd.PersistentFlags().Lookup("gui.bind")); err != nil {
562+
return err
563+
}
564+
565+
return nil
566+
}
567+
568+
func (c *Gui) Set() {
569+
c.Enabled = viper.GetBool("gui.enabled")
570+
c.Bind = viper.GetString("gui.bind")
571+
}

internal/config/serve.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type Serve struct {
1010
Email Email
1111
Cookie Cookie
1212
Redis Redis
13+
Gui Gui
1314
}
1415

1516
func (c *Serve) Init(cmd *cobra.Command) error {
@@ -33,6 +34,10 @@ func (c *Serve) Init(cmd *cobra.Command) error {
3334
return err
3435
}
3536

37+
if err := c.Gui.Init(cmd); err != nil {
38+
return err
39+
}
40+
3641
return nil
3742
}
3843

@@ -42,4 +47,5 @@ func (c *Serve) Set() {
4247
c.Email.Set()
4348
c.Cookie.Set()
4449
c.Redis.Set()
50+
c.Gui.Set()
4551
}

internal/gui/emails.gohtml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>email-auth</title>
6+
<style>
7+
body {
8+
font-family: Arial, Helvetica, sans-serif;
9+
}
10+
form {
11+
display: inline-block;
12+
}
13+
</style>
14+
</head>
15+
<body>
16+
<h1> emails </h1>
17+
<a href="./">back</a>
18+
<br>
19+
<br>
20+
<form action="./email" method="post">
21+
<input type="text" name="email" placeholder="email or @domain.com">
22+
<input type="hidden" name="action" value="add">
23+
<input type="submit" value="add email">
24+
</form>
25+
<ul>
26+
{{ range $email := .Emails }}
27+
<li>
28+
{{ $email }} &bull; <form action="./email" method="post">
29+
<input type="hidden" name="email" value="{{ $email }}">
30+
<input type="hidden" name="action" value="remove">
31+
<input type="submit" value="remove">
32+
</form>
33+
</li>
34+
{{ end }}
35+
</ul>
36+
</body>

0 commit comments

Comments
 (0)