forked from knadh/listmonk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
149 lines (131 loc) · 4.09 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package main
import (
"fmt"
"html/template"
"log"
"os"
"strings"
"time"
"github.com/jmoiron/sqlx"
"github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file"
"github.com/knadh/koanf/providers/posflag"
"github.com/knadh/listmonk/internal/manager"
"github.com/knadh/listmonk/internal/media"
"github.com/knadh/listmonk/internal/messenger"
"github.com/knadh/listmonk/internal/subimporter"
"github.com/knadh/stuffbin"
flag "github.com/spf13/pflag"
)
// App contains the "global" components that are
// passed around, especially through HTTP handlers.
type App struct {
fs stuffbin.FileSystem
db *sqlx.DB
queries *Queries
constants *constants
manager *manager.Manager
importer *subimporter.Importer
messenger messenger.Messenger
media media.Store
notifTpls *template.Template
log *log.Logger
}
var (
// Global logger.
lo = log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile)
// Global configuration reader.
ko = koanf.New(".")
buildString string
)
func init() {
f := flag.NewFlagSet("config", flag.ContinueOnError)
f.Usage = func() {
// Register --help handler.
fmt.Println(f.FlagUsages())
os.Exit(0)
}
// Register the commandline flags.
f.StringSlice("config", []string{"config.toml"},
"path to one or more config files (will be merged in order)")
f.Bool("install", false, "run first time installation")
f.Bool("version", false, "current version of the build")
f.Bool("new-config", false, "generate sample config file")
f.String("static-dir", "", "(optional) path to directory with static files")
f.Bool("yes", false, "assume 'yes' to prompts, eg: during --install")
if err := f.Parse(os.Args[1:]); err != nil {
lo.Fatalf("error loading flags: %v", err)
}
// Display version.
if v, _ := f.GetBool("version"); v {
fmt.Println(buildString)
os.Exit(0)
}
// Generate new config.
if ok, _ := f.GetBool("new-config"); ok {
if err := newConfigFile(); err != nil {
lo.Println(err)
os.Exit(1)
}
lo.Println("generated config.toml. Edit and run --install")
os.Exit(0)
}
// Load config files.
cFiles, _ := f.GetStringSlice("config")
for _, f := range cFiles {
lo.Printf("reading config: %s", f)
if err := ko.Load(file.Provider(f), toml.Parser()); err != nil {
if os.IsNotExist(err) {
lo.Fatal("config file not found. If there isn't one yet, run --new-config to generate one.")
}
lo.Fatalf("error loadng config from file: %v.", err)
}
}
// Load environment variables and merge into the loaded config.
if err := ko.Load(env.Provider("LISTMONK_", ".", func(s string) string {
return strings.Replace(strings.ToLower(
strings.TrimPrefix(s, "LISTMONK_")), "__", ".", -1)
}), nil); err != nil {
lo.Fatalf("error loading config from env: %v", err)
}
if err := ko.Load(posflag.Provider(f, ".", ko), nil); err != nil {
lo.Fatalf("error loading config: %v", err)
}
}
func main() {
// Initialize the DB and the filesystem that are required by the installer
// and the app.
var (
fs = initFS(ko.String("static-dir"))
db = initDB()
)
defer db.Close()
// Installer mode? This runs before the SQL queries are loaded and prepared
// as the installer needs to work on an empty DB.
if ko.Bool("install") {
install(db, fs, !ko.Bool("yes"))
return
}
// Initialize the main app controller that wraps all of the app's
// components. This is passed around HTTP handlers.
app := &App{
fs: fs,
db: db,
constants: initConstants(),
media: initMediaStore(),
log: lo,
}
_, app.queries = initQueries(queryFilePath, db, fs, true)
app.manager = initCampaignManager(app.queries, app.constants, app)
app.importer = initImporter(app.queries, db, app)
app.messenger = initMessengers(app.manager)
app.notifTpls = initNotifTemplates("/email-templates/*.html", fs, app.constants)
// Start the campaign workers. The campaign batches (fetch from DB, push out
// messages) get processed at the specified interval.
go app.manager.Run(time.Second * 5)
app.manager.SpawnWorkers()
// Start and run the app server.
initHTTPServer(app)
}