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/pkg/components/auth" "git.sr.ht/~gabrielgio/img/pkg/components/filesystem" "git.sr.ht/~gabrielgio/img/pkg/components/media" "git.sr.ht/~gabrielgio/img/pkg/database/localfs" "git.sr.ht/~gabrielgio/img/pkg/database/sql" "git.sr.ht/~gabrielgio/img/pkg/ext" "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") // 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.ServeFiles("/static/{filepath:*}", "./static") r.NotFound = ext.NotFoundHTML authMiddleware := ext.NewAuthMiddleware(hexKey, logger.WithField("context", "auth")) logMiddleware := ext.NewLogMiddleare(logger.WithField("context", "http")) extRouter := ext.NewRouter(r) extRouter.AddMiddleware(logMiddleware.HTTP) extRouter.AddMiddleware(authMiddleware.LoggedIn) extRouter.AddMiddleware(ext.HTML) scheduler := worker.NewScheduler(10) // repository var ( userRepository = sql.NewUserRepository(db) settingsRepository = sql.NewSettingsRespository(db) fileSystemRepository = localfs.NewFileSystemRepository(*root) mediaRepository = sql.NewMediaRepository(db) ) //TODO: remove later userRepository.EnsureAdmin(context.Background()) // controller var ( userController = auth.NewController(userRepository, hexKey) fileSystemController = filesystem.NewController(fileSystemRepository) ) // view for _, v := range []view.View{ view.NewAuthView(userController), view.NewFileSystemView(*fileSystemController, settingsRepository), view.NewSettingsView(settingsRepository), view.NewMediaView(mediaRepository), } { v.SetMyselfIn(extRouter) } // worker var ( serverWorker = worker.NewServerWorker(&fasthttp.Server{Handler: r.Handler}) fileScanner = worker.NewFileScanner(*root, mediaRepository) exifScanner = worker.NewEXIFScanner(mediaRepository) ) pool := worker.NewWorkerPool() pool.AddWorker("http server", serverWorker) pool.AddWorker("exif scanner", worker.NewWorkerFromListProcessor[*media.Media](exifScanner, scheduler)) pool.AddWorker("file scanner", worker.NewWorkerFromChanProcessor[string](fileScanner, scheduler)) ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() pool.Start(ctx) pool.Wait() } 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") } }