1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-08-01 17:38:37 +00:00

Add Google Reader API implementation (experimental)

Co-authored-by: Sebastian Kempken <sebastian@kempken.io>
Co-authored-by: Gergan Penkov <gergan@gmail.com>
Co-authored-by: Dave Marquard <dave@marquard.org>
Co-authored-by: Moritz Fago <4459068+MoritzFago@users.noreply.github.com>
This commit is contained in:
Gergan Penkov 2022-01-03 04:45:12 +01:00 committed by GitHub
parent 2935aaef45
commit 4b6e46d9ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1923 additions and 36 deletions

View file

@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"github.com/lib/pq"
"miniflux.app/model"
)
@ -215,3 +216,51 @@ func (s *Storage) RemoveCategory(userID, categoryID int64) error {
return nil
}
// delete the given categories, replacing those categories with the user's first
// category on affected feeds
func (s *Storage) RemoveAndReplaceCategoriesByName(userid int64, titles []string) error {
tx, err := s.db.Begin()
if err != nil {
return errors.New("unable to begin transaction")
}
titleParam := pq.Array(titles)
var count int
query := "SELECT count(*) FROM categories WHERE user_id = $1 and title != ANY($2)"
err = tx.QueryRow(query, userid, titleParam).Scan(&count)
if err != nil {
tx.Rollback()
return errors.New("unable to retrieve category count")
}
if count < 1 {
tx.Rollback()
return errors.New("at least 1 category must remain after deletion")
}
query = `
WITH d_cats AS (SELECT id FROM categories WHERE user_id = $1 AND title = ANY($2))
UPDATE feeds
SET category_id =
(SELECT id
FROM categories
WHERE user_id = $1 AND id NOT IN (SELECT id FROM d_cats)
ORDER BY title ASC
LIMIT 1)
WHERE user_id = $1 AND category_id IN (SELECT id FROM d_cats)
`
_, err = tx.Exec(query, userid, titleParam)
if err != nil {
tx.Rollback()
return fmt.Errorf("unable to replace categories: %v", err)
}
query = "DELETE FROM categories WHERE user_id = $1 AND title = ANY($2)"
_, err = tx.Exec(query, userid, titleParam)
if err != nil {
tx.Rollback()
return fmt.Errorf("unable to delete categories: %v", err)
}
tx.Commit()
return nil
}

View file

@ -371,6 +371,26 @@ func (s *Storage) SetEntriesStatusCount(userID int64, entryIDs []int64, status s
return visible, nil
}
// SetEntriesBookmarked update the bookmarked state for the given list of entries.
func (s *Storage) SetEntriesBookmarkedState(userID int64, entryIDs []int64, starred bool) error {
query := `UPDATE entries SET starred=$1, changed_at=now() WHERE user_id=$2 AND id=ANY($3)`
result, err := s.db.Exec(query, starred, userID, pq.Array(entryIDs))
if err != nil {
return fmt.Errorf(`store: unable to update the bookmarked state %v: %v`, entryIDs, err)
}
count, err := result.RowsAffected()
if err != nil {
return fmt.Errorf(`store: unable to update these entries %v: %v`, entryIDs, err)
}
if count == 0 {
return errors.New(`store: nothing has been updated`)
}
return nil
}
// ToggleBookmark toggles entry bookmark value.
func (s *Storage) ToggleBookmark(userID int64, entryID int64) error {
query := `UPDATE entries SET starred = NOT starred, changed_at=now() WHERE user_id=$1 AND id=$2`

View file

@ -8,6 +8,7 @@ import (
"database/sql"
"fmt"
"golang.org/x/crypto/bcrypt"
"miniflux.app/model"
)
@ -19,6 +20,14 @@ func (s *Storage) HasDuplicateFeverUsername(userID int64, feverUsername string)
return result
}
// HasDuplicateGoogleReaderUsername checks if another user have the same googlereader username.
func (s *Storage) HasDuplicateGoogleReaderUsername(userID int64, googleReaderUsername string) bool {
query := `SELECT true FROM integrations WHERE user_id != $1 AND googlereader_username=$2`
var result bool
s.db.QueryRow(query, userID, googleReaderUsername).Scan(&result)
return result
}
// UserByFeverToken returns a user by using the Fever API token.
func (s *Storage) UserByFeverToken(token string) (*model.User, error) {
query := `
@ -42,6 +51,57 @@ func (s *Storage) UserByFeverToken(token string) (*model.User, error) {
}
}
// GoogleReaderUserCheckPassword validates the hashed password.
func (s *Storage) GoogleReaderUserCheckPassword(username, password string) error {
var hash string
query := `
SELECT
googlereader_password
FROM integrations
WHERE
integrations.googlereader_enabled='t' AND integrations.googlereader_username=$1
`
err := s.db.QueryRow(query, username).Scan(&hash)
if err == sql.ErrNoRows {
return fmt.Errorf(`store: unable to find this user: %s`, username)
} else if err != nil {
return fmt.Errorf(`store: unable to fetch user: %v`, err)
}
if err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)); err != nil {
return fmt.Errorf(`store: invalid password for "%s" (%v)`, username, err)
}
return nil
}
// GoogleReaderUserGetIntegration returns part of the google reader parts of the integration struct.
func (s *Storage) GoogleReaderUserGetIntegration(username string) (*model.Integration, error) {
var integration model.Integration
query := `
SELECT
user_id,
googlereader_enabled,
googlereader_username,
googlereader_password
FROM integrations
WHERE
integrations.googlereader_enabled='t' AND integrations.googlereader_username=$1
`
err := s.db.QueryRow(query, username).Scan(&integration.UserID, &integration.GoogleReaderEnabled, &integration.GoogleReaderUsername, &integration.GoogleReaderPassword)
if err == sql.ErrNoRows {
return &integration, fmt.Errorf(`store: unable to find this user: %s`, username)
} else if err != nil {
return &integration, fmt.Errorf(`store: unable to fetch user: %v`, err)
}
return &integration, nil
}
// Integration returns user integration settings.
func (s *Storage) Integration(userID int64) (*model.Integration, error) {
query := `
@ -57,6 +117,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
fever_enabled,
fever_username,
fever_token,
googlereader_enabled,
googlereader_username,
googlereader_password,
wallabag_enabled,
wallabag_url,
wallabag_client_id,
@ -90,6 +153,9 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
&integration.FeverEnabled,
&integration.FeverUsername,
&integration.FeverToken,
&integration.GoogleReaderEnabled,
&integration.GoogleReaderUsername,
&integration.GoogleReaderPassword,
&integration.WallabagEnabled,
&integration.WallabagURL,
&integration.WallabagClientID,
@ -118,7 +184,13 @@ func (s *Storage) Integration(userID int64) (*model.Integration, error) {
// UpdateIntegration saves user integration settings.
func (s *Storage) UpdateIntegration(integration *model.Integration) error {
query := `
var err error
if integration.GoogleReaderPassword != "" {
integration.GoogleReaderPassword, err = hashPassword(integration.GoogleReaderPassword)
if err != nil {
return err
}
query := `
UPDATE
integrations
SET
@ -144,41 +216,116 @@ func (s *Storage) UpdateIntegration(integration *model.Integration) error {
pocket_enabled=$20,
pocket_access_token=$21,
pocket_consumer_key=$22,
telegram_bot_enabled=$23,
telegram_bot_token=$24,
telegram_bot_chat_id=$25
googlereader_enabled=$23,
googlereader_username=$24,
googlereader_password=$25,
telegram_bot_enabled=$26,
telegram_bot_token=$27,
telegram_bot_chat_id=$28
WHERE
user_id=$26
user_id=$29
`
_, err := s.db.Exec(
query,
integration.PinboardEnabled,
integration.PinboardToken,
integration.PinboardTags,
integration.PinboardMarkAsUnread,
integration.InstapaperEnabled,
integration.InstapaperUsername,
integration.InstapaperPassword,
integration.FeverEnabled,
integration.FeverUsername,
integration.FeverToken,
integration.WallabagEnabled,
integration.WallabagURL,
integration.WallabagClientID,
integration.WallabagClientSecret,
integration.WallabagUsername,
integration.WallabagPassword,
integration.NunuxKeeperEnabled,
integration.NunuxKeeperURL,
integration.NunuxKeeperAPIKey,
integration.PocketEnabled,
integration.PocketAccessToken,
integration.PocketConsumerKey,
integration.TelegramBotEnabled,
integration.TelegramBotToken,
integration.TelegramBotChatID,
integration.UserID,
)
_, err = s.db.Exec(
query,
integration.PinboardEnabled,
integration.PinboardToken,
integration.PinboardTags,
integration.PinboardMarkAsUnread,
integration.InstapaperEnabled,
integration.InstapaperUsername,
integration.InstapaperPassword,
integration.FeverEnabled,
integration.FeverUsername,
integration.FeverToken,
integration.WallabagEnabled,
integration.WallabagURL,
integration.WallabagClientID,
integration.WallabagClientSecret,
integration.WallabagUsername,
integration.WallabagPassword,
integration.NunuxKeeperEnabled,
integration.NunuxKeeperURL,
integration.NunuxKeeperAPIKey,
integration.PocketEnabled,
integration.PocketAccessToken,
integration.PocketConsumerKey,
integration.GoogleReaderEnabled,
integration.GoogleReaderUsername,
integration.GoogleReaderPassword,
integration.TelegramBotEnabled,
integration.TelegramBotToken,
integration.TelegramBotChatID,
integration.UserID,
)
} else {
query := `
UPDATE
integrations
SET
pinboard_enabled=$1,
pinboard_token=$2,
pinboard_tags=$3,
pinboard_mark_as_unread=$4,
instapaper_enabled=$5,
instapaper_username=$6,
instapaper_password=$7,
fever_enabled=$8,
fever_username=$9,
fever_token=$10,
wallabag_enabled=$11,
wallabag_url=$12,
wallabag_client_id=$13,
wallabag_client_secret=$14,
wallabag_username=$15,
wallabag_password=$16,
nunux_keeper_enabled=$17,
nunux_keeper_url=$18,
nunux_keeper_api_key=$19,
pocket_enabled=$20,
pocket_access_token=$21,
pocket_consumer_key=$22,
googlereader_enabled=$23,
googlereader_username=$24,
googlereader_password=$25,
telegram_bot_enabled=$26,
telegram_bot_token=$27,
telegram_bot_chat_id=$28
WHERE
user_id=$29
`
_, err = s.db.Exec(
query,
integration.PinboardEnabled,
integration.PinboardToken,
integration.PinboardTags,
integration.PinboardMarkAsUnread,
integration.InstapaperEnabled,
integration.InstapaperUsername,
integration.InstapaperPassword,
integration.FeverEnabled,
integration.FeverUsername,
integration.FeverToken,
integration.WallabagEnabled,
integration.WallabagURL,
integration.WallabagClientID,
integration.WallabagClientSecret,
integration.WallabagUsername,
integration.WallabagPassword,
integration.NunuxKeeperEnabled,
integration.NunuxKeeperURL,
integration.NunuxKeeperAPIKey,
integration.PocketEnabled,
integration.PocketAccessToken,
integration.PocketConsumerKey,
integration.GoogleReaderEnabled,
integration.GoogleReaderUsername,
integration.GoogleReaderPassword,
integration.TelegramBotEnabled,
integration.TelegramBotToken,
integration.TelegramBotChatID,
integration.UserID,
)
}
if err != nil {
return fmt.Errorf(`store: unable to update integration row: %v`, err)