Example #1
0
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))
	}
}
Example #2
0
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))
	}
}
Example #3
0
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)))
	}
}
Example #4
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
}
Example #5
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
	}

	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
}