lens @ cff5600c8abebd1ce988b2185c07e998c4a1d483

  1package main
  2
  3import (
  4	"context"
  5	"encoding/hex"
  6	"errors"
  7	"os"
  8	"os/signal"
  9
 10	"github.com/fasthttp/router"
 11	"github.com/sirupsen/logrus"
 12	flag "github.com/spf13/pflag"
 13	"github.com/valyala/fasthttp"
 14	"gorm.io/driver/mysql"
 15	"gorm.io/driver/postgres"
 16	"gorm.io/driver/sqlite"
 17	"gorm.io/gorm"
 18
 19	"git.sr.ht/~gabrielgio/img/pkg/components/auth"
 20	"git.sr.ht/~gabrielgio/img/pkg/components/filesystem"
 21	"git.sr.ht/~gabrielgio/img/pkg/components/media"
 22	"git.sr.ht/~gabrielgio/img/pkg/database/localfs"
 23	"git.sr.ht/~gabrielgio/img/pkg/database/sql"
 24	"git.sr.ht/~gabrielgio/img/pkg/ext"
 25	"git.sr.ht/~gabrielgio/img/pkg/view"
 26	"git.sr.ht/~gabrielgio/img/pkg/worker"
 27)
 28
 29func main() {
 30	var (
 31		key      = flag.String("aes-key", "", "AES key, either 16, 24, or 32 bytes string to select AES-128, AES-192, or AES-256")
 32		dbType   = flag.String("db-type", "sqlite", "Database to be used. Choose either mysql, psql or sqlite")
 33		dbCon    = flag.String("db-con", "main.db", "Database string connection for given database type. Ref: https://gorm.io/docs/connecting_to_the_database.html")
 34		logLevel = flag.String("log-level", "error", "Log level: Choose either trace, debug, info, warning, error, fatal or panic")
 35
 36		// TODO: this will later be replaced by user specific root folder
 37		root = flag.String("root", "", "root folder for the whole application. All the workers will use it as working directory")
 38	)
 39
 40	flag.Parse()
 41
 42	l, err := logrus.ParseLevel(*logLevel)
 43	if err != nil {
 44		panic("failed to parse log level" + err.Error())
 45	}
 46	logger := logrus.New()
 47	logger.SetLevel(l)
 48
 49	d, err := OpenDatabase(*dbType, *dbCon)
 50	if err != nil {
 51		panic("failed to parse database strings" + err.Error())
 52	}
 53
 54	db, err := gorm.Open(d, &gorm.Config{
 55		Logger: ext.Wraplog(logger.WithField("context", "sql")),
 56	})
 57	if err != nil {
 58		panic("failed to connect database: " + err.Error())
 59	}
 60
 61	if err = sql.Migrate(db); err != nil {
 62		panic("failed to migrate database: " + err.Error())
 63	}
 64
 65	hexKey, err := hex.DecodeString(*key)
 66	if err != nil {
 67		panic("failed to decode key database: " + err.Error())
 68	}
 69
 70	r := router.New()
 71	r.ServeFiles("/static/{filepath:*}", "./static")
 72	r.NotFound = ext.NotFoundHTML
 73
 74	authMiddleware := ext.NewAuthMiddleware(hexKey, logger.WithField("context", "auth"))
 75	logMiddleware := ext.NewLogMiddleare(logger.WithField("context", "http"))
 76
 77	extRouter := ext.NewRouter(r)
 78	extRouter.AddMiddleware(logMiddleware.HTTP)
 79	extRouter.AddMiddleware(authMiddleware.LoggedIn)
 80	extRouter.AddMiddleware(ext.HTML)
 81
 82	scheduler := worker.NewScheduler(10)
 83
 84	// repository
 85	var (
 86		userRepository       = sql.NewUserRepository(db)
 87		settingsRepository   = sql.NewSettingsRespository(db)
 88		fileSystemRepository = localfs.NewFileSystemRepository(*root)
 89		mediaRepository      = sql.NewMediaRepository(db)
 90	)
 91
 92	//TODO: remove later
 93	userRepository.EnsureAdmin(context.Background())
 94
 95	// controller
 96	var (
 97		userController       = auth.NewController(userRepository, hexKey)
 98		fileSystemController = filesystem.NewController(fileSystemRepository)
 99	)
100
101	// view
102	for _, v := range []view.View{
103		view.NewAuthView(userController),
104		view.NewFileSystemView(*fileSystemController, settingsRepository),
105		view.NewSettingsView(settingsRepository),
106		view.NewMediaView(mediaRepository),
107	} {
108		v.SetMyselfIn(extRouter)
109	}
110
111	// worker
112	var (
113		serverWorker = worker.NewServerWorker(&fasthttp.Server{Handler: r.Handler})
114		fileScanner  = worker.NewFileScanner(*root, mediaRepository)
115		exifScanner  = worker.NewEXIFScanner(mediaRepository)
116	)
117
118	pool := worker.NewWorkerPool()
119	pool.AddWorker("http server", serverWorker)
120	pool.AddWorker("exif scanner", worker.NewWorkerFromListProcessor[*media.Media](exifScanner, scheduler))
121	pool.AddWorker("file scanner", worker.NewWorkerFromChanProcessor[string](fileScanner, scheduler))
122
123	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
124	defer stop()
125
126	pool.Start(ctx)
127	pool.Wait()
128}
129
130func OpenDatabase(dbType string, dbConn string) (gorm.Dialector, error) {
131	switch dbType {
132	case "sqlite":
133		return sqlite.Open(dbConn), nil
134	case "psql":
135		return postgres.Open(dbConn), nil
136	case "mysql":
137		return mysql.Open(dbConn), nil
138	default:
139		return nil, errors.New("No valid db type given")
140	}
141}