Email: orginize maddy code structure
diff --git a/apps/maddy/web/.gitignore b/apps/maddy/web/.gitignore
new file mode 100644
index 0000000..797200e
--- /dev/null
+++ b/apps/maddy/web/.gitignore
@@ -0,0 +1 @@
+maddy-web
diff --git a/apps/maddy/web/Dockerfile b/apps/maddy/web/Dockerfile
new file mode 100644
index 0000000..8c057e1
--- /dev/null
+++ b/apps/maddy/web/Dockerfile
@@ -0,0 +1,4 @@
+FROM giolekva/maddy:v0.4.4 AS maddy
+
+COPY maddy-web /usr/bin
+RUN chmod +x /usr/bin/maddy-web
diff --git a/apps/maddy/web/Makefile b/apps/maddy/web/Makefile
new file mode 100644
index 0000000..7c054fc
--- /dev/null
+++ b/apps/maddy/web/Makefile
@@ -0,0 +1,17 @@
+clean:
+ rm -f maddy-web
+
+build: clean
+ go build -o maddy-web *.go
+
+image: build
+ docker build --tag=giolekva/maddy-web:latest .
+
+push: image
+ docker push giolekva/maddy-web:latest
+
+push_arm64: export GOOS=linux
+push_arm64: export GOARCH=arm64
+push_arm64: export CGO_ENABLED=0
+push_arm64: export GO111MODULE=on
+push_arm64: push
diff --git a/apps/maddy/web/go.mod b/apps/maddy/web/go.mod
new file mode 100644
index 0000000..4b7d8aa
--- /dev/null
+++ b/apps/maddy/web/go.mod
@@ -0,0 +1,3 @@
+module github.com/giolekva/pcloud/apps/maddy/web
+
+go 1.16
diff --git a/apps/maddy/web/main.go b/apps/maddy/web/main.go
new file mode 100644
index 0000000..d4bced0
--- /dev/null
+++ b/apps/maddy/web/main.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "bufio"
+ "embed"
+ "flag"
+ "fmt"
+ "html/template"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os/exec"
+)
+
+var port = flag.Int("port", 8080, "Port to listen on.")
+var maddyConfig = flag.String("maddy-config", "", "Path to the Maddy configuration file.")
+
+//go:embed templates/*
+var tmpls embed.FS
+
+type Templates struct {
+ Index *template.Template
+}
+
+func ParseTemplates(fs embed.FS) (*Templates, error) {
+ index, err := template.ParseFS(fs, "templates/index.html")
+ if err != nil {
+ return nil, err
+ }
+ return &Templates{index}, nil
+}
+
+type MaddyManager struct {
+ configPath string
+}
+
+func (m MaddyManager) ListAccounts() ([]string, error) {
+ cmd := exec.Command("maddyctl", "-config", m.configPath, "creds", "list")
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ if err := cmd.Start(); err != nil {
+ return nil, err
+ }
+ scanner := bufio.NewScanner(stdout)
+ accts := make([]string, 0)
+ for scanner.Scan() {
+ acct := scanner.Text()
+ if len(acct) == 0 {
+ continue
+ }
+ accts = append(accts, acct)
+ }
+ return accts, nil
+
+}
+
+func (m MaddyManager) CreateAccount(username, password string) error {
+ cmd := exec.Command("maddyctl", "-config", m.configPath, "creds", "create", username)
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+ if err := cmd.Start(); err != nil {
+ return err
+ }
+ go func() {
+ defer stdin.Close()
+ io.WriteString(stdin, password)
+ }()
+ if err := cmd.Wait(); err != nil {
+ return err
+ }
+ // Create IMAP
+ cmd = exec.Command("maddyctl", "-config", m.configPath, "imap-acct", "create", username)
+ return cmd.Run()
+}
+
+type MaddyHandler struct {
+ mgr MaddyManager
+ tmpls *Templates
+}
+
+func (h *MaddyHandler) handleListAccounts(w http.ResponseWriter, r *http.Request) {
+ accts, err := h.mgr.ListAccounts()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if err := h.tmpls.Index.Execute(w, accts); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func (h *MaddyHandler) handleCreateAccount(w http.ResponseWriter, r *http.Request) {
+ if err := r.ParseForm(); err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ err := h.mgr.CreateAccount(r.FormValue("username"), r.FormValue("password"))
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ http.Redirect(w, r, "/", http.StatusSeeOther)
+}
+
+func main() {
+ flag.Parse()
+ t, err := ParseTemplates(tmpls)
+ if err != nil {
+ log.Fatal(err)
+ }
+ mgr := MaddyManager{
+ configPath: *maddyConfig,
+ }
+ handler := MaddyHandler{
+ mgr: mgr,
+ tmpls: t,
+ }
+ http.HandleFunc("/", handler.handleListAccounts)
+ http.HandleFunc("/create", handler.handleCreateAccount)
+ fmt.Printf("Starting HTTP server on port: %d\n", *port)
+ fmt.Printf("Maddy config: %s\n", *maddyConfig)
+ if cfg, err := ioutil.ReadFile(*maddyConfig); err != nil {
+ log.Fatal(err)
+ } else {
+ log.Print(string(cfg))
+ }
+ log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
+
+}
diff --git a/apps/maddy/web/templates/index.html b/apps/maddy/web/templates/index.html
new file mode 100644
index 0000000..a5fcb97
--- /dev/null
+++ b/apps/maddy/web/templates/index.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <title>Manage email accounts</title>
+</head>
+<body>
+ <form action="/create" method="POST">
+ <label for="username">Username:</label><br />
+ <input type="text" name="username" /><br />
+ <label for="password">Last name:</label><br />
+ <input type="password" name="password" /><br />
+ <input type="submit" value="Create New Account" />
+ </form>
+ <table>
+ <tr>
+ <th>Account</th>
+ <th>Delete</th>
+ </tr>
+ {{range .}}
+ <tr>
+ <td>
+ {{.}}
+ </td>
+ <td>
+ Delete
+ </td>
+ </tr>
+ {{end}}
+ </table>
+</body>
+</html>
+