2018-10-25 13:51:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"regexp"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"github.com/lib/pq"
|
|
|
|
uuid "github.com/satori/go.uuid"
|
|
|
|
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
"github.com/knadh/goyesql"
|
|
|
|
"github.com/knadh/listmonk/models"
|
|
|
|
"github.com/spf13/viper"
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"golang.org/x/crypto/ssh/terminal"
|
|
|
|
)
|
|
|
|
|
|
|
|
// install runs the first time setup of creating and
|
|
|
|
// migrating the database and creating the super user.
|
|
|
|
func install(app *App, qMap goyesql.Queries) {
|
|
|
|
var (
|
|
|
|
email, pw, pw2 []byte
|
|
|
|
err error
|
|
|
|
|
|
|
|
// Pseudo e-mail validation using Regexp, well ...
|
|
|
|
emRegex, _ = regexp.Compile("(.+?)@(.+?)")
|
|
|
|
)
|
|
|
|
|
|
|
|
fmt.Println("** First time installation. **")
|
|
|
|
fmt.Println("** IMPORTANT: This will wipe existing listmonk tables and types. **")
|
|
|
|
fmt.Println("\n")
|
|
|
|
|
|
|
|
for len(email) == 0 {
|
|
|
|
fmt.Print("Enter the superadmin login e-mail: ")
|
|
|
|
if _, err = fmt.Scanf("%s", &email); err != nil {
|
|
|
|
logger.Fatalf("Error reading e-mail from the terminal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !emRegex.Match(email) {
|
|
|
|
logger.Println("Please enter a valid e-mail")
|
|
|
|
email = []byte{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for len(pw) < 8 {
|
|
|
|
fmt.Print("Enter the superadmin password (min 8 chars): ")
|
|
|
|
if pw, err = terminal.ReadPassword(int(syscall.Stdin)); err != nil {
|
|
|
|
logger.Fatalf("Error reading password from the terminal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("")
|
|
|
|
if len(pw) < 8 {
|
|
|
|
logger.Println("Password should be min 8 characters")
|
|
|
|
pw = []byte{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for len(pw2) < 8 {
|
|
|
|
fmt.Print("Repeat the superadmin password: ")
|
|
|
|
if pw2, err = terminal.ReadPassword(int(syscall.Stdin)); err != nil {
|
|
|
|
logger.Fatalf("Error reading password from the terminal: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Println("")
|
|
|
|
if len(pw2) < 8 {
|
|
|
|
logger.Println("Password should be min 8 characters")
|
|
|
|
pw2 = []byte{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate.
|
|
|
|
if !bytes.Equal(pw, pw2) {
|
|
|
|
logger.Fatalf("Passwords don't match")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hash the password.
|
|
|
|
hash, err := bcrypt.GenerateFromPassword(pw, bcrypt.DefaultCost)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatalf("Error hashing password: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Migrate the tables.
|
|
|
|
err = installMigrate(app.DB)
|
|
|
|
if err != nil {
|
|
|
|
logger.Fatalf("Error migrating DB schema: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the queries.
|
|
|
|
var q Queries
|
|
|
|
if err := scanQueriesToStruct(&q, qMap, app.DB.Unsafe()); err != nil {
|
|
|
|
logger.Fatalf("error loading SQL queries: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the superadmin user.
|
|
|
|
if _, err := q.CreateUser.Exec(
|
|
|
|
string(email),
|
|
|
|
models.UserTypeSuperadmin, // name
|
|
|
|
string(hash),
|
|
|
|
models.UserTypeSuperadmin,
|
|
|
|
models.UserStatusEnabled,
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating superadmin user: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample list.
|
|
|
|
var listID int
|
|
|
|
if err := q.CreateList.Get(&listID,
|
|
|
|
uuid.NewV4().String(),
|
|
|
|
"Default list",
|
|
|
|
models.ListTypePublic,
|
|
|
|
pq.StringArray{"test"},
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating superadmin user: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample subscriber.
|
|
|
|
name := bytes.Split(email, []byte("@"))
|
|
|
|
if _, err := q.UpsertSubscriber.Exec(
|
|
|
|
uuid.NewV4(),
|
|
|
|
email,
|
|
|
|
bytes.Title(name[0]),
|
|
|
|
models.SubscriberStatusEnabled,
|
|
|
|
`{"type": "known", "good": true}`,
|
|
|
|
true,
|
|
|
|
pq.Int64Array{int64(listID)},
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating subscriber: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default template.
|
|
|
|
tplBody, err := ioutil.ReadFile("default-template.html")
|
|
|
|
if err != nil {
|
2018-10-29 09:50:11 +00:00
|
|
|
tplBody = []byte(tplTag)
|
2018-10-25 13:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var tplID int
|
|
|
|
if err := q.CreateTemplate.Get(&tplID,
|
|
|
|
"Default template",
|
|
|
|
string(tplBody),
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating default template: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := q.SetDefaultTemplate.Exec(tplID); err != nil {
|
|
|
|
logger.Fatalf("Error setting default template: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.Printf("Setup complete")
|
|
|
|
logger.Printf(`Run the program and login with the username "superadmin" and your password at %s`,
|
|
|
|
viper.GetString("server.address"))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// installMigrate executes the SQL schema and creates the necessary tables and types.
|
|
|
|
func installMigrate(db *sqlx.DB) error {
|
|
|
|
q, err := ioutil.ReadFile("schema.sql")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = db.Query(string(q))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|