2018-10-25 15:51:47 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2019-07-08 14:51:44 +02:00
|
|
|
"errors"
|
2018-10-25 15:51:47 +02:00
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2019-07-08 14:51:44 +02:00
|
|
|
"os"
|
2019-06-26 12:52:47 +02:00
|
|
|
"strings"
|
2019-07-04 11:17:33 +02:00
|
|
|
"time"
|
2018-10-25 15:51:47 +02:00
|
|
|
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
"github.com/knadh/goyesql"
|
|
|
|
"github.com/knadh/listmonk/models"
|
2019-06-26 12:52:47 +02:00
|
|
|
"github.com/lib/pq"
|
|
|
|
uuid "github.com/satori/go.uuid"
|
2018-10-25 15:51:47 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// install runs the first time setup of creating and
|
|
|
|
// migrating the database and creating the super user.
|
2019-11-30 12:25:14 +01:00
|
|
|
func install(app *App, qMap goyesql.Queries, prompt bool) {
|
2019-01-03 12:18:47 +01:00
|
|
|
fmt.Println("")
|
2019-06-26 12:52:47 +02:00
|
|
|
fmt.Println("** First time installation **")
|
|
|
|
fmt.Printf("** IMPORTANT: This will wipe existing listmonk tables and types in the DB '%s' **",
|
2019-06-26 13:23:23 +02:00
|
|
|
ko.String("db.database"))
|
2019-01-03 12:18:47 +01:00
|
|
|
fmt.Println("")
|
2018-10-25 15:51:47 +02:00
|
|
|
|
2019-11-30 12:25:14 +01:00
|
|
|
if prompt {
|
|
|
|
var ok string
|
|
|
|
fmt.Print("Continue (y/n)? ")
|
|
|
|
if _, err := fmt.Scanf("%s", &ok); err != nil {
|
|
|
|
logger.Fatalf("Error reading value from terminal: %v", err)
|
|
|
|
}
|
|
|
|
if strings.ToLower(ok) != "y" {
|
|
|
|
fmt.Println("Installation cancelled.")
|
|
|
|
return
|
|
|
|
}
|
2018-10-25 15:51:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Migrate the tables.
|
2019-06-26 12:52:47 +02:00
|
|
|
err := installMigrate(app.DB, app)
|
2018-10-25 15:51:47 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample list.
|
2020-02-09 07:15:01 +01:00
|
|
|
var (
|
|
|
|
defList int
|
|
|
|
optinList int
|
|
|
|
)
|
|
|
|
if err := q.CreateList.Get(&defList,
|
2018-10-25 15:51:47 +02:00
|
|
|
uuid.NewV4().String(),
|
|
|
|
"Default list",
|
2020-02-09 07:15:01 +01:00
|
|
|
models.ListTypePrivate,
|
2019-12-01 13:18:36 +01:00
|
|
|
models.ListOptinSingle,
|
2018-10-25 15:51:47 +02:00
|
|
|
pq.StringArray{"test"},
|
|
|
|
); err != nil {
|
2019-12-01 13:18:36 +01:00
|
|
|
logger.Fatalf("Error creating list: %v", err)
|
2018-10-25 15:51:47 +02:00
|
|
|
}
|
|
|
|
|
2020-02-09 07:15:01 +01:00
|
|
|
if err := q.CreateList.Get(&optinList, uuid.NewV4().String(),
|
|
|
|
"Opt-in list",
|
|
|
|
models.ListTypePublic,
|
|
|
|
models.ListOptinDouble,
|
|
|
|
pq.StringArray{"test"},
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating list: %v", err)
|
|
|
|
}
|
|
|
|
|
2018-10-25 15:51:47 +02:00
|
|
|
// Sample subscriber.
|
|
|
|
if _, err := q.UpsertSubscriber.Exec(
|
|
|
|
uuid.NewV4(),
|
2019-07-04 11:17:33 +02:00
|
|
|
"john@example.com",
|
|
|
|
"John Doe",
|
|
|
|
`{"type": "known", "good": true, "city": "Bengaluru"}`,
|
2020-02-09 07:15:01 +01:00
|
|
|
pq.Int64Array{int64(defList)},
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating subscriber: %v", err)
|
|
|
|
}
|
|
|
|
if _, err := q.UpsertSubscriber.Exec(
|
|
|
|
uuid.NewV4(),
|
|
|
|
"anon@example.com",
|
|
|
|
"Anon Doe",
|
|
|
|
`{"type": "unknown", "good": true, "city": "Bengaluru"}`,
|
|
|
|
pq.Int64Array{int64(optinList)},
|
2018-10-25 15:51:47 +02:00
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("Error creating subscriber: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Default template.
|
2019-07-04 11:03:03 +02:00
|
|
|
tplBody, err := ioutil.ReadFile("email-templates/default.tpl")
|
2018-10-25 15:51:47 +02:00
|
|
|
if err != nil {
|
2018-10-29 10:50:11 +01:00
|
|
|
tplBody = []byte(tplTag)
|
2018-10-25 15:51:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var tplID int
|
|
|
|
if err := q.CreateTemplate.Get(&tplID,
|
|
|
|
"Default template",
|
|
|
|
string(tplBody),
|
|
|
|
); err != nil {
|
2019-07-04 11:17:33 +02:00
|
|
|
logger.Fatalf("error creating default template: %v", err)
|
2018-10-25 15:51:47 +02:00
|
|
|
}
|
|
|
|
if _, err := q.SetDefaultTemplate.Exec(tplID); err != nil {
|
2019-07-04 11:17:33 +02:00
|
|
|
logger.Fatalf("error setting default template: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sample campaign.
|
|
|
|
sendAt := time.Now()
|
|
|
|
sendAt.Add(time.Minute * 43200)
|
|
|
|
if _, err := q.CreateCampaign.Exec(uuid.NewV4(),
|
2020-02-09 07:15:01 +01:00
|
|
|
models.CampaignTypeRegular,
|
2019-07-04 11:17:33 +02:00
|
|
|
"Test campaign",
|
|
|
|
"Welcome to listmonk",
|
2019-07-16 16:21:41 +02:00
|
|
|
"No Reply <noreply@yoursite.com>",
|
2019-07-04 11:17:33 +02:00
|
|
|
`<h3>Hi {{ .Subscriber.FirstName }}!</h3>
|
|
|
|
This is a test e-mail campaign. Your second name is {{ .Subscriber.LastName }} and you are from {{ .Subscriber.Attribs.city }}.`,
|
|
|
|
"richtext",
|
|
|
|
sendAt,
|
|
|
|
pq.StringArray{"test-campaign"},
|
|
|
|
"email",
|
|
|
|
1,
|
|
|
|
pq.Int64Array{1},
|
|
|
|
); err != nil {
|
|
|
|
logger.Fatalf("error creating sample campaign: %v", err)
|
2018-10-25 15:51:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
logger.Printf("Setup complete")
|
2019-07-15 17:06:08 +02:00
|
|
|
logger.Printf(`Run the program and access the dashboard at %s`, ko.String("app.address"))
|
2018-10-25 15:51:47 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// installMigrate executes the SQL schema and creates the necessary tables and types.
|
2019-01-03 12:18:47 +01:00
|
|
|
func installMigrate(db *sqlx.DB, app *App) error {
|
|
|
|
q, err := app.FS.Read("/schema.sql")
|
2018-10-25 15:51:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = db.Query(string(q))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2019-07-08 14:51:44 +02:00
|
|
|
|
|
|
|
func newConfigFile() error {
|
|
|
|
if _, err := os.Stat("config.toml"); !os.IsNotExist(err) {
|
|
|
|
return errors.New("config.toml exists. Remove it to generate a new one")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the static file system into which all
|
|
|
|
// required static assets (.sql, .js files etc.) are loaded.
|
|
|
|
fs, err := initFileSystem(os.Args[0])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := fs.Read("config.toml.sample")
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error reading sample config (is binary stuffed?): %v", err)
|
|
|
|
}
|
|
|
|
|
2019-10-25 07:41:47 +02:00
|
|
|
return ioutil.WriteFile("config.toml", b, 0644)
|
2019-07-08 14:51:44 +02:00
|
|
|
}
|