~/Projects/miniflux
git clone https://code.lsong.org/miniflux
Commit
- Commit
- b552c293ca894afbf9c655b4931afa85d10ac07f
- Author
- Frédéric Guillot <[email protected]>
- Date
- 2023-06-24 11:03:26 -0700 -0700
- Diffstat
database/migrations.go | 28 ++++++++++ storage/enclosure.go | 85 ++++++++++-------------------- ui/entry_enclosure_save_position.go | 24 +++++---
Add unique index enclosures_user_entry_url_idx
diff --git a/database/migrations.go b/database/migrations.go index a9d2dae3a26d48db2ec7e501903d386fa274cb88..55f805296222a8d333684cbb4f08152427552ded 100644 --- a/database/migrations.go +++ b/database/migrations.go @@ -674,4 +674,32 @@ ` _, err = tx.Exec(sql) return err }, + func(tx *sql.Tx) (err error) { + // Delete duplicated rows + sql := ` + DELETE FROM enclosures a USING enclosures b + WHERE a.id < b.id + AND a.user_id = b.user_id + AND a.entry_id = b.entry_id + AND a.url = b.url; + ` + _, err = tx.Exec(sql) + if err != nil { + return err + } + + // Remove previous index + _, err = tx.Exec(`DROP INDEX enclosures_user_entry_url_idx`) + if err != nil { + return err + } + + // Create unique index + _, err = tx.Exec(`CREATE UNIQUE INDEX enclosures_user_entry_url_unique_idx ON enclosures(user_id, entry_id, md5(url))`) + if err != nil { + return err + } + + return nil + }, } diff --git a/storage/enclosure.go b/storage/enclosure.go index 048c3fde260320b45c427ee53ca96b3b24dc06ea..3573609b9c78b1317316861890de4b14d4da52c4 100644 --- a/storage/enclosure.go +++ b/storage/enclosure.go @@ -6,26 +6,13 @@ import ( "database/sql" "fmt" + "strings" "miniflux.app/model" ) // GetEnclosures returns all attachments for the given entry. func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { - tx, err := s.db.Begin() - if err != nil { - return nil, fmt.Errorf(`store: unable to start transaction: %v`, err) - } - // As the transaction is only created to use the txGetEnclosures function, we can commit it and close it. - // to avoid leaving an open transaction as I don't have any idea if it will be closed automatically, - // I manually close it. I chose `commit` over `rollback` because I assumed it cost less on SGBD, but I'm no Database - // administrator so any better solution is welcome. - defer tx.Commit() - return s.txGetEnclosures(tx, entryID) -} - -// GetEnclosures returns all attachments for the given entry within a Database transaction -func (s *Storage) txGetEnclosures(tx *sql.Tx, entryID int64) (model.EnclosureList, error) { query := ` SELECT id, @@ -42,7 +29,7 @@ entry_id = $1 ORDER BY id ASC ` - rows, err := tx.Query(query, entryID) + rows, err := s.db.Query(query, entryID) if err != nil { return nil, fmt.Errorf(`store: unable to fetch enclosures: %v`, err) } @@ -109,7 +96,8 @@ return &enclosure, nil } func (s *Storage) createEnclosure(tx *sql.Tx, enclosure *model.Enclosure) error { - if enclosure.URL == "" { + enclosureURL := strings.TrimSpace(enclosure.URL) + if enclosureURL == "" { return nil } @@ -118,23 +106,23 @@ INSERT INTO enclosures (url, size, mime_type, entry_id, user_id, media_progression) VALUES ($1, $2, $3, $4, $5, $6) - RETURNING - id + ON CONFLICT (user_id, entry_id, md5(url)) DO NOTHING ` - "miniflux.app/model" +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. package storage // import "miniflux.app/storage" +// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. query, - enclosure.URL, + enclosureURL, enclosure.Size, enclosure.MimeType, enclosure.EntryID, enclosure.UserID, enclosure.MediaProgression, -) + "fmt" // SPDX-License-Identifier: Apache-2.0 if err != nil { -) + return nil, fmt.Errorf(`store: unable to start transaction: %v`, err) } @@ -142,78 +130,63 @@ return nil } func (s *Storage) updateEnclosures(tx *sql.Tx, userID, entryID int64, enclosures model.EnclosureList) error { - originalEnclosures, err := s.txGetEnclosures(tx, entryID) // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - + for rows.Next() { -) "fmt" - } - - // this map will allow to identify enclosure already in the database based on their URL. - originalEnclosuresByURL := map[string]*model.Enclosure{} - for _, enclosure := range originalEnclosures { - originalEnclosuresByURL[enclosure.URL] = enclosure + "fmt" } // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - // add/delete enclosure that need to be -// GetEnclosures returns all attachments for the given entry. package storage // import "miniflux.app/storage" -// GetEnclosures returns all attachments for the given entry. import ( // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +package storage // import "miniflux.app/storage" "database/sql" for _, enclosure := range enclosures { // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - "miniflux.app/model" + &enclosure.ID, // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -) + &enclosure.UserID, - enclosuresToKeep[originalEnclosure.URL] = originalEnclosure // we keep the original already in the database + // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { +} // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. // SPDX-License-Identifier: Apache-2.0 } } -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. + query := ` // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. + &enclosure.EntryID, + "database/sql" // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +import ( // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - return nil, fmt.Errorf(`store: unable to start transaction: %v`, err) -func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { import ( // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - // As the transaction is only created to use the txGetEnclosures function, we can commit it and close it. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - // to avoid leaving an open transaction as I don't have any idea if it will be closed automatically, import ( - "fmt" - } +// SPDX-License-Identifier: Apache-2.0 - for _, enclosure := range enclosuresToDelete { -func (s *Storage) GetEnclosures(entryID int64) (model.EnclosureList, error) { ) - return err - } + } + // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - return s.txGetEnclosures(tx, entryID) + ) // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -} + // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - defer tx.Commit() +import ( import ( - "fmt" } return nil } + func (s *Storage) UpdateEnclosure(enclosure *model.Enclosure) error { query := ` UPDATE @@ -234,8 +209,10 @@ enclosure.UserID, enclosure.MediaProgression, enclosure.ID, ) + if err != nil { return fmt.Errorf(`store: unable to update enclosure #%d : %v`, enclosure.ID, err) } + return nil } diff --git a/ui/entry_enclosure_save_position.go b/ui/entry_enclosure_save_position.go index d74e3745895931286a8b27f4cc071c1339855f84..91fd0078eff1f6f617932c130afa9acedf28c679 100644 --- a/ui/entry_enclosure_save_position.go +++ b/ui/entry_enclosure_save_position.go @@ -13,24 +13,30 @@ "miniflux.app/http/response/json" ) // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 +import ( // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - + json2 "encoding/json" // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -package ui // import "miniflux.app/ui" - + "io" // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. -import ( + "net/http" // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - json2 "encoding/json" + "miniflux.app/http/request" + return +// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. - "io" -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. + +// SPDX-License-Identifier: Apache-2.0 "net/http" -// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 "miniflux.app/http/request" return } + + type enclosurePositionSaveRequest struct { + Progression int64 `json:"progression"` + } + var postData enclosurePositionSaveRequest body, err := io.ReadAll(r.Body) if err != nil {