func main() { confpath := flag.String("config", "", "readeef config path") flag.Parse() cfg, err := readeef.ReadConfig(*confpath) if err != nil { exitWithError(fmt.Sprintf("Error reading config from path '%s': %v", *confpath, err)) } if cfg.SearchIndex.BlevePath == "" { exitWithError("No bleve-path in search-index section of the config") } logger := readeef.NewLogger(cfg) repo, err := repo.New(cfg.DB.Driver, cfg.DB.Connect, logger) if err != nil { exitWithError(fmt.Sprintf("Error connecting to database: %v", err)) } si, err := readeef.NewSearchIndex(repo, cfg, logger) if err != nil { exitWithError(fmt.Sprintf("Error creating search index: %v", err)) } logger.Infoln("Getting all articles") if err := si.IndexAllArticles(); err != nil { exitWithError(fmt.Sprintf("Error indexing all articles: %v", err)) } }
func main() { confpath := flag.String("config", "", "readeef config path") flag.Parse() cfg, err := readeef.ReadConfig(*confpath) if err != nil { exitWithError(fmt.Sprintf("Error reading config from path '%s': %v", *confpath, err)) } logger := readeef.NewLogger(cfg) repo, err := repo.New(cfg.DB.Driver, cfg.DB.Connect, logger) if err != nil { exitWithError(fmt.Sprintf("Error connecting to database: %v", err)) } var sp content.SearchProvider switch cfg.Content.SearchProvider { case "elastic": if sp, err = search.NewElastic(cfg.Content.ElasticURL, cfg.Content.SearchBatchSize, logger); err != nil { exitWithError(fmt.Sprintf("Error initializing Elastic search: %v\n", err)) } case "bleve": fallthrough default: if sp, err = search.NewBleve(cfg.Content.BlevePath, cfg.Content.SearchBatchSize, logger); err != nil { exitWithError(fmt.Sprintf("Error initializing Bleve search: %v\n", err)) } } logger.Infoln("Getting all articles") if err := sp.IndexAllFeeds(repo); err != nil { exitWithError(fmt.Sprintf("Error indexing all articles: %v", err)) } }
func main() { confpath := flag.String("config", "", "config path") flag.Parse() cfg, err := readeef.ReadConfig(*confpath) if err != nil { exitWithError(fmt.Sprintf("Error reading config from path '%s': %v", *confpath, err)) } logger := readeef.NewLogger(cfg) repo, err := repo.New(cfg.DB.Driver, cfg.DB.Connect, logger) if err != nil { exitWithError(fmt.Sprintf("Error connecting to database: %v", err)) } switch flag.Arg(0) { case "add": if flag.NArg() != 3 { exitWithError("Not enough arguments for 'add' command. Login and password must be specified") } login := flag.Arg(1) pass := flag.Arg(2) u := repo.User() i := u.Data() i.Login = data.Login(login) i.Active = true u.Data(i) u.Password(pass, []byte(cfg.Auth.Secret)) u.Update() if u.HasErr() { exitWithError(fmt.Sprintf("Error setting password for user '%s': %v", login, u.Err())) } case "remove": if flag.NArg() != 2 { exitWithError("Not enough arguments for 'remove' command. Login must be specified") } login := flag.Arg(1) u := repo.UserByLogin(data.Login(login)) u.Delete() if u.HasErr() { exitWithError(fmt.Sprintf("Error getting user '%s' from the database: %v", login, u.Err())) } case "get": if flag.NArg() != 3 { exitWithError("Not enough arguments for 'get' command. Login and user property must be specified") } login := flag.Arg(1) prop := flag.Arg(2) u := repo.UserByLogin(data.Login(login)) if repo.HasErr() { exitWithError(fmt.Sprintf("Error getting user '%s' from the database: %v", login, repo.Err())) } lowerProp := strings.ToLower(prop) switch lowerProp { case "firstname", "first_name": fmt.Printf("%s\n", u.Data().FirstName) case "lastname", "last_name": fmt.Printf("%s\n", u.Data().LastName) case "email": fmt.Printf("%s\n", u.Data().Email) case "hashtype", "hash_type": fmt.Printf("%s\n", u.Data().HashType) case "salt": fmt.Printf("%v\n", u.Data().Salt) case "hash": fmt.Printf("%v\n", u.Data().Hash) case "md5api", "md5_api": fmt.Printf("%v\n", u.Data().MD5API) case "admin": fmt.Printf("%v\n", u.Data().Admin) case "active": fmt.Printf("%v\n", u.Data().Active) default: exitWithError(fmt.Sprintf("Unknown user property '%s'", prop)) } case "set": if flag.NArg() != 4 { exitWithError("Not enough arguments for 'set' command. Login, user property, and value must be specified") } login := flag.Arg(1) prop := flag.Arg(2) val := flag.Arg(3) u := repo.UserByLogin(data.Login(login)) if repo.HasErr() { exitWithError(fmt.Sprintf("Error getting user '%s' from the database: %v", login, repo.Err())) } in := u.Data() lowerProp := strings.ToLower(prop) switch lowerProp { case "firstname", "first_name": in.FirstName = val case "lastname", "last_name": in.LastName = val case "email": in.Email = val case "password": u.Password(val, []byte(cfg.Auth.Secret)) case "admin", "active": enabled := false if val == "1" || val == "true" || val == "on" { enabled = true } if lowerProp == "admin" { in.Admin = enabled } else { in.Active = enabled } default: exitWithError(fmt.Sprintf("Unknown user property '%s'", prop)) } u.Update() if u.HasErr() { exitWithError(fmt.Sprintf("Error updating the user database record for '%s': %v", login, u.Err())) } case "list": users := repo.AllUsers() if repo.HasErr() { exitWithError(fmt.Sprintf("Error getting users from the database: %v", repo.Err())) } for _, u := range users { fmt.Printf("%s\n", u.Data().Login) } case "list-detailed": users := repo.AllUsers() if repo.HasErr() { exitWithError(fmt.Sprintf("Error getting users from the database: %v", repo.Err())) } for _, u := range users { in := u.Data() fmt.Printf("Login: %s", in.Login) if in.FirstName != "" { fmt.Printf(", first name: %s", in.FirstName) } if in.LastName != "" { fmt.Printf(", last name: %s", in.LastName) } if in.Email != "" { fmt.Printf(", email: %s", in.Email) } if in.HashType != "" { fmt.Printf(", has type: %s", in.HashType) } fmt.Printf("\n") } default: exitWithError(fmt.Sprintf("Unknown command '%s'", flag.Arg(0))) } }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, logger webfw.Logger) error { repo, err := repo.New(config.DB.Driver, config.DB.Connect, logger) if err != nil { return err } capabilities := capabilities{ I18N: len(dispatcher.Config.I18n.Languages) > 1, Popularity: len(config.Popularity.Providers) > 0, } var ap []content.ArticleProcessor for _, p := range config.Content.ArticleProcessors { switch p { case "relative-url": ap = append(ap, contentProcessor.NewRelativeUrl(logger)) case "proxy-http": template := config.Content.ProxyHTTPURLTemplate if template != "" { p, err := contentProcessor.NewProxyHTTP(logger, template) if err != nil { return fmt.Errorf("Error initializing Proxy HTTP article processor: %v", err) } ap = append(ap, p) capabilities.ProxyHTTP = true } case "insert-thumbnail-target": ap = append(ap, contentProcessor.NewInsertThumbnailTarget(logger)) } } repo.ArticleProcessors(ap) if err := initAdminUser(repo, []byte(config.Auth.Secret)); err != nil { return err } mw := make([]string, 0, len(dispatcher.Config.Dispatcher.Middleware)) for _, m := range dispatcher.Config.Dispatcher.Middleware { switch m { case "I18N", "Static", "Url", "Sitemap": case "Session": if capabilities.ProxyHTTP { mw = append(mw, m) } default: mw = append(mw, m) } } dispatcher.Config.Dispatcher.Middleware = mw dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) dispatcher.Context.SetGlobal(readeef.CtxKey("repo"), repo) fm := readeef.NewFeedManager(repo, config, logger) var processors []parser.Processor for _, p := range config.FeedParser.Processors { switch p { case "relative-url": processors = append(processors, processor.NewRelativeUrl(logger)) case "proxy-http": template := config.FeedParser.ProxyHTTPURLTemplate if template != "" { p, err := processor.NewProxyHTTP(logger, template) if err != nil { return fmt.Errorf("Error initializing Proxy HTTP processor: %v", err) } processors = append(processors, p) capabilities.ProxyHTTP = true } case "cleanup": processors = append(processors, processor.NewCleanup(logger)) case "top-image-marker": processors = append(processors, processor.NewTopImageMarker(logger)) } } fm.ParserProcessors(processors) var sp content.SearchProvider switch config.Content.SearchProvider { case "elastic": if sp, err = search.NewElastic(config.Content.ElasticURL, config.Content.SearchBatchSize, logger); err != nil { logger.Printf("Error initializing Elastic search: %v\n", err) } case "bleve": fallthrough default: if sp, err = search.NewBleve(config.Content.BlevePath, config.Content.SearchBatchSize, logger); err != nil { logger.Printf("Error initializing Bleve search: %v\n", err) } } if sp != nil { if sp.IsNewIndex() { go func() { sp.IndexAllFeeds(repo) }() } } var ce content.Extractor switch config.Content.Extractor { case "readability": if ce, err = extractor.NewReadability(config.Content.ReadabilityKey); err != nil { return fmt.Errorf("Error initializing Readability extractor: %v\n", err) } case "goose": fallthrough default: if ce, err = extractor.NewGoose(dispatcher.Config.Renderer.Dir); err != nil { return fmt.Errorf("Error initializing Goose extractor: %v\n", err) } } if ce != nil { capabilities.Extractor = true } var t content.Thumbnailer switch config.Content.Thumbnailer { case "extract": if t, err = thumbnailer.NewExtract(ce, logger); err != nil { return fmt.Errorf("Error initializing Extract thumbnailer: %v\n", err) } case "description": fallthrough default: t = thumbnailer.NewDescription(logger) } monitors := []content.FeedMonitor{monitor.NewUnread(repo, logger)} for _, m := range config.FeedManager.Monitors { switch m { case "index": if sp != nil { monitors = append(monitors, monitor.NewIndex(sp, logger)) capabilities.Search = true } case "thumbnailer": if t != nil { monitors = append(monitors, monitor.NewThumbnailer(t, logger)) } } } webSocket := NewWebSocket(fm, sp, ce, capabilities) dispatcher.Handle(webSocket) monitors = append(monitors, webSocket) if config.Hubbub.CallbackURL != "" { hubbub := readeef.NewHubbub(repo, config, logger, dispatcher.Pattern, fm.RemoveFeedChannel()) if err := hubbub.InitSubscriptions(); err != nil { return fmt.Errorf("Error initializing hubbub subscriptions: %v", err) } hubbub.FeedMonitors(monitors) fm.Hubbub(hubbub) } fm.FeedMonitors(monitors) fm.Start() nonce := readeef.NewNonce() controllers := []webfw.Controller{ NewAuth(capabilities), NewFeed(fm, sp), NewArticle(config, ce), NewUser(), NewUserSettings(), NewNonce(nonce), } if fm.Hubbub() != nil { controllers = append(controllers, NewHubbubController(fm.Hubbub(), config.Hubbub.RelativePath, fm.AddFeedChannel(), fm.RemoveFeedChannel())) } for _, e := range config.API.Emulators { switch e { case "tt-rss": controllers = append(controllers, NewTtRss(fm, sp)) case "fever": controllers = append(controllers, NewFever()) } } for _, c := range controllers { dispatcher.Handle(c) } middleware.InitializeDefault(dispatcher) dispatcher.RegisterMiddleware(readeef.Auth{Pattern: dispatcher.Pattern, Nonce: nonce, IgnoreURLPrefix: config.Auth.IgnoreURLPrefix}) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") go func() { for { select { case <-time.After(5 * time.Minute): nonce.Clean(45 * time.Second) } } }() return nil }
func RegisterControllers(config readeef.Config, dispatcher *webfw.Dispatcher, logger webfw.Logger) error { repo, err := repo.New(config.DB.Driver, config.DB.Connect, logger) if err != nil { return err } if err := initAdminUser(repo, []byte(config.Auth.Secret)); err != nil { return err } dispatcher.Context.SetGlobal(readeef.CtxKey("config"), config) dispatcher.Context.SetGlobal(context.BaseCtxKey("readeefConfig"), config) dispatcher.Context.SetGlobal(readeef.CtxKey("repo"), repo) um := &readeef.UpdateFeedReceiverManager{} fm := readeef.NewFeedManager(repo, config, logger, um) if config.Hubbub.CallbackURL != "" { hubbub := readeef.NewHubbub(repo, config, logger, dispatcher.Pattern, fm.RemoveFeedChannel(), fm.AddFeedChannel(), um) if err := hubbub.InitSubscriptions(); err != nil { return fmt.Errorf("Error initializing hubbub subscriptions: %v", err) } fm.SetHubbub(hubbub) dispatcher.Handle(readeef.NewHubbubController(hubbub)) } var si readeef.SearchIndex if config.SearchIndex.BlevePath != "" { var err error si, err = readeef.NewSearchIndex(repo, config, logger) if err != nil { return fmt.Errorf("Error initializing bleve search: %v", err) } if si.IsNewIndex() { go func() { si.IndexAllArticles() }() } fm.SetSearchIndex(si) } fm.Start() nonce := readeef.NewNonce() var patternController webfw.PatternController var multiPatternController webfw.MultiPatternController patternController = NewAuth() dispatcher.Handle(patternController) multiPatternController = NewFeed(fm) dispatcher.Handle(multiPatternController) multiPatternController = NewArticle(config) dispatcher.Handle(multiPatternController) if config.SearchIndex.BlevePath != "" { patternController = NewSearch(si) dispatcher.Handle(patternController) } multiPatternController = NewUser() dispatcher.Handle(multiPatternController) patternController = NewUserSettings() dispatcher.Handle(patternController) patternController = NewNonce(nonce) dispatcher.Handle(patternController) if config.API.Fever { patternController = NewFever(fm) dispatcher.Handle(patternController) } webSocket := NewWebSocket(fm, si) dispatcher.Handle(webSocket) um.AddUpdateReceiver(webSocket) middleware.InitializeDefault(dispatcher) dispatcher.RegisterMiddleware(readeef.Auth{Pattern: dispatcher.Pattern, Nonce: nonce, IgnoreURLPrefix: config.Auth.IgnoreURLPrefix}) dispatcher.Renderer = renderer.NewRenderer(dispatcher.Config.Renderer.Dir, dispatcher.Config.Renderer.Base) dispatcher.Renderer.Delims("{%", "%}") go func() { for { select { case <-time.After(5 * time.Minute): nonce.Clean(45 * time.Second) } } }() return nil }