package main import ( "context" "encoding/hex" "errors" "os" "os/signal" "github.com/fasthttp/router" "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" "github.com/valyala/fasthttp" "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" "gorm.io/gorm" "git.sr.ht/~gabrielgio/img" "git.sr.ht/~gabrielgio/img/pkg/database/localfs" "git.sr.ht/~gabrielgio/img/pkg/database/repository" "git.sr.ht/~gabrielgio/img/pkg/database/sql" "git.sr.ht/~gabrielgio/img/pkg/ext" "git.sr.ht/~gabrielgio/img/pkg/service" "git.sr.ht/~gabrielgio/img/pkg/view" "git.sr.ht/~gabrielgio/img/pkg/worker" ) func main() { var ( key = flag.String("aes-key", "", "AES key, either 16, 24, or 32 bytes string to select AES-128, AES-192, or AES-256") dbType = flag.String("db-type", "sqlite", "Database to be used. Choose either mysql, psql or sqlite") dbCon = flag.String("db-con", "main.db", "Database string connection for given database type. Ref: https://gorm.io/docs/connecting_to_the_database.html") logLevel = flag.String("log-level", "error", "Log level: Choose either trace, debug, info, warning, error, fatal or panic") schedulerCount = flag.Uint("scheduler-count", 10, "How many workers are created to process media files") cachePath = flag.String("cache-path", "", "Folder to store thumbnail image") // TODO: this will later be replaced by user specific root folder root = flag.String("root", "", "root folder for the whole application. All the workers will use it as working directory") ) flag.Parse() l, err := logrus.ParseLevel(*logLevel) if err != nil { panic("failed to parse log level" + err.Error()) } logger := logrus.New() logger.SetLevel(l) d, err := OpenDatabase(*dbType, *dbCon) if err != nil { panic("failed to parse database strings" + err.Error()) } db, err := gorm.Open(d, &gorm.Config{ Logger: ext.Wraplog(logger.WithField("context", "sql")), }) if err != nil { panic("failed to connect database: " + err.Error()) } if err = sql.Migrate(db); err != nil { panic("failed to migrate database: " + err.Error()) } hexKey, err := hex.DecodeString(*key) if err != nil { panic("failed to decode key database: " + err.Error()) } r := router.New() r.GET("/static/{filepath:*}", ext.FileServer(img.StaticFS, "static/")) // repository var ( userRepository = sql.NewUserRepository(db) settingsRepository = sql.NewSettingsRespository(db) fileSystemRepository = localfs.NewFileSystemRepository(*root) mediaRepository = sql.NewMediaRepository(db) ) // middleware var ( authMiddleware = ext.NewAuthMiddleware(hexKey, logger.WithField("context", "auth")) logMiddleware = ext.NewLogMiddleare(logger.WithField("context", "http")) initialMiddleware = ext.NewInitialSetupMiddleware(userRepository) ) extRouter := ext.NewRouter(r) extRouter.AddMiddleware(ext.HTML) extRouter.AddMiddleware(initialMiddleware.Check) extRouter.AddMiddleware(authMiddleware.LoggedIn) extRouter.AddMiddleware(logMiddleware.HTTP) scheduler := worker.NewScheduler(*schedulerCount) // controller var ( userController = service.NewAuthController(userRepository, userRepository, hexKey) fileSystemController = service.NewFileSystemController(fileSystemRepository) ) // view for _, v := range []view.View{ view.NewAuthView(userController), view.NewFileSystemView(*fileSystemController, settingsRepository), view.NewSettingsView(settingsRepository, userRepository), view.NewMediaView(mediaRepository), } { v.SetMyselfIn(extRouter) } // processors var ( fileScanner = worker.NewFileScanner(*root, mediaRepository) exifScanner = worker.NewEXIFScanner(mediaRepository) thumbnailScanner = worker.NewThumbnailScanner(*cachePath, mediaRepository) ) // worker var ( serverWorker = worker.NewServerWorker(&fasthttp.Server{Handler: r.Handler}) fileWorker = worker.NewWorkerFromChanProcessor[string]( fileScanner, scheduler, logrus.WithField("context", "file scanner"), ) exifWorker = worker.NewWorkerFromBatchProcessor[*repository.Media]( exifScanner, scheduler, logrus.WithField("context", "exif scanner"), ) thumbnailWorker = worker.NewWorkerFromBatchProcessor[*repository.Media]( thumbnailScanner, scheduler, logrus.WithField("context", "thumbnail scanner"), ) ) pool := worker.NewWorkerPool() pool.AddWorker("http server", serverWorker) pool.AddWorker("exif scanner", exifWorker) pool.AddWorker("file scanner", fileWorker) pool.AddWorker("thumbnail scanner", thumbnailWorker) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() pool.Start(ctx) } func OpenDatabase(dbType string, dbConn string) (gorm.Dialector, error) { switch dbType { case "sqlite": return sqlite.Open(dbConn), nil case "psql": return postgres.Open(dbConn), nil case "mysql": return mysql.Open(dbConn), nil default: return nil, errors.New("No valid db type given") } }