lens @ 1ae70dbd9124675d4a510954619b01edd5f1f6c3

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