tulip/cmd/red-tulip/serve.go

144 lines
3.8 KiB
Go
Raw Normal View History

2023-09-06 22:20:09 +01:00
package main
import (
"context"
"crypto/rand"
"database/sql"
"encoding/json"
"errors"
"flag"
"fmt"
"github.com/1f349/mjwt"
2023-09-06 22:20:09 +01:00
"github.com/1f349/tulip/database"
2023-10-09 20:31:41 +01:00
"github.com/1f349/tulip/mail/templates"
2024-01-29 10:44:45 +00:00
"github.com/1f349/tulip/red-pages"
"github.com/1f349/tulip/red-server"
2023-09-06 22:20:09 +01:00
"github.com/1f349/violet/utils"
"github.com/MrMelon54/exit-reload"
"github.com/google/subcommands"
_ "github.com/mattn/go-sqlite3"
"log"
"os"
"path/filepath"
)
type serveCmd struct{ configPath string }
func (s *serveCmd) Name() string { return "serve" }
func (s *serveCmd) Synopsis() string { return "Serve user authentication service" }
func (s *serveCmd) SetFlags(f *flag.FlagSet) {
f.StringVar(&s.configPath, "conf", "", "/path/to/config.json : path to the config file")
}
func (s *serveCmd) Usage() string {
return `serve [-conf <config file>]
Serve user authentication service using information from the config file
`
}
func (s *serveCmd) Execute(_ context.Context, _ *flag.FlagSet, _ ...any) subcommands.ExitStatus {
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Starting...")
2023-09-06 22:20:09 +01:00
if s.configPath == "" {
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Error: config flag is missing")
2023-09-06 22:20:09 +01:00
return subcommands.ExitUsageError
}
openConf, err := os.Open(s.configPath)
if err != nil {
if os.IsNotExist(err) {
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Error: missing config file")
2023-09-06 22:20:09 +01:00
} else {
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Error: open config file: ", err)
2023-09-06 22:20:09 +01:00
}
return subcommands.ExitFailure
}
2024-01-29 10:44:45 +00:00
var config red_server.Conf
2023-09-06 22:20:09 +01:00
err = json.NewDecoder(openConf).Decode(&config)
if err != nil {
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Error: invalid config file: ", err)
2023-09-06 22:20:09 +01:00
return subcommands.ExitFailure
}
configPathAbs, err := filepath.Abs(s.configPath)
if err != nil {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed to get absolute config path")
2023-09-06 22:20:09 +01:00
}
wd := filepath.Dir(configPathAbs)
normalLoad(config, wd)
return subcommands.ExitSuccess
}
2024-01-29 10:44:45 +00:00
func normalLoad(startUp red_server.Conf, wd string) {
signingKey, err := mjwt.NewMJwtSignerFromFileOrCreate(startUp.OtpIssuer, filepath.Join(wd, "tulip.key.pem"), rand.Reader, 4096)
if err != nil {
log.Fatal("[Tulip] Failed to open signing key file:", err)
}
2023-09-06 22:20:09 +01:00
2024-01-29 10:44:45 +00:00
db, err := database.Open(filepath.Join(wd, "red-red-tulip.db.sqlite"))
2023-09-06 22:20:09 +01:00
if err != nil {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed to open database:", err)
2023-09-06 22:20:09 +01:00
}
2024-01-29 10:44:45 +00:00
log.Println("[RedTulip] Checking database contains at least one user")
2023-09-06 22:20:09 +01:00
if err := checkDbHasUser(db); err != nil {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed check:", err)
2023-09-06 22:20:09 +01:00
}
2024-01-29 10:44:45 +00:00
if err = red_pages.LoadPages(wd); err != nil {
log.Fatal("[RedTulip] Failed to load page templates:", err)
2023-10-09 20:31:41 +01:00
}
if err := templates.LoadMailTemplates(wd); err != nil {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed to load mail templates:", err)
2023-10-09 20:31:41 +01:00
}
2024-01-29 10:44:45 +00:00
srv := red_server.NewHttpServer(startUp, db, signingKey)
log.Printf("[RedTulip] Starting HTTP red-server on '%s'\n", srv.Addr)
2023-09-06 22:20:09 +01:00
go utils.RunBackgroundHttp("HTTP", srv)
2024-01-29 10:44:45 +00:00
exit_reload.ExitReload("RedTulip", func() {}, func() {
// stop http red-server
2023-09-08 00:30:03 +01:00
_ = srv.Close()
_ = db.Close()
2023-09-06 22:20:09 +01:00
})
}
func genHmacKey() []byte {
a := make([]byte, 32)
n, err := rand.Reader.Read(a)
if err != nil {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed to generate HMAC key")
2023-09-06 22:20:09 +01:00
}
if n != 32 {
2024-01-29 10:44:45 +00:00
log.Fatal("[RedTulip] Failed to generate HMAC key")
2023-09-06 22:20:09 +01:00
}
return a
}
func checkDbHasUser(db *database.DB) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("failed to start transaction: %w", err)
}
2023-09-08 00:30:03 +01:00
defer tx.Rollback()
2023-09-06 22:20:09 +01:00
if err := tx.HasUser(); err != nil {
if errors.Is(err, sql.ErrNoRows) {
2023-10-10 18:06:43 +01:00
_, err := tx.InsertUser("Admin", "admin", "admin", "admin@localhost", false, database.RoleAdmin, false)
2023-09-06 22:20:09 +01:00
if err != nil {
return fmt.Errorf("failed to add user: %w", err)
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
// continue normal operation now
return nil
} else {
return fmt.Errorf("failed to check if table has a user: %w", err)
}
}
return nil
}