mirror of
https://github.com/miniflux/v2.git
synced 2025-08-16 18:01:37 +00:00
For small fixed-size structures, it's better to use a slice of values, instead of a slice of pointers to values: they're stored contiguously and thus can be iterated on quickly by the CPU, and it does remove an indirection per object every time the GC kicks in.
119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package storage // import "miniflux.app/v2/internal/storage"
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"miniflux.app/v2/internal/crypto"
|
|
"miniflux.app/v2/internal/model"
|
|
)
|
|
|
|
var ErrAPIKeyNotFound = fmt.Errorf("store: API Key not found")
|
|
|
|
// APIKeyExists checks if an API Key with the same description exists.
|
|
func (s *Storage) APIKeyExists(userID int64, description string) bool {
|
|
var result bool
|
|
query := `SELECT true FROM api_keys WHERE user_id=$1 AND lower(description)=lower($2) LIMIT 1`
|
|
s.db.QueryRow(query, userID, description).Scan(&result)
|
|
return result
|
|
}
|
|
|
|
// SetAPIKeyUsedTimestamp updates the last used date of an API Key.
|
|
func (s *Storage) SetAPIKeyUsedTimestamp(userID int64, token string) error {
|
|
query := `UPDATE api_keys SET last_used_at=now() WHERE user_id=$1 and token=$2`
|
|
_, err := s.db.Exec(query, userID, token)
|
|
if err != nil {
|
|
return fmt.Errorf(`store: unable to update last used date for API key: %v`, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// APIKeys returns all API Keys that belongs to the given user.
|
|
func (s *Storage) APIKeys(userID int64) (model.APIKeys, error) {
|
|
query := `
|
|
SELECT
|
|
id, user_id, token, description, last_used_at, created_at
|
|
FROM
|
|
api_keys
|
|
WHERE
|
|
user_id=$1
|
|
ORDER BY description ASC
|
|
`
|
|
rows, err := s.db.Query(query, userID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`store: unable to fetch API Keys: %v`, err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
apiKeys := make(model.APIKeys, 0)
|
|
for rows.Next() {
|
|
var apiKey model.APIKey
|
|
if err := rows.Scan(
|
|
&apiKey.ID,
|
|
&apiKey.UserID,
|
|
&apiKey.Token,
|
|
&apiKey.Description,
|
|
&apiKey.LastUsedAt,
|
|
&apiKey.CreatedAt,
|
|
); err != nil {
|
|
return nil, fmt.Errorf(`store: unable to fetch API Key row: %v`, err)
|
|
}
|
|
|
|
apiKeys = append(apiKeys, apiKey)
|
|
}
|
|
|
|
return apiKeys, nil
|
|
}
|
|
|
|
// CreateAPIKey inserts a new API key.
|
|
func (s *Storage) CreateAPIKey(userID int64, description string) (*model.APIKey, error) {
|
|
query := `
|
|
INSERT INTO api_keys
|
|
(user_id, token, description)
|
|
VALUES
|
|
($1, $2, $3)
|
|
RETURNING
|
|
id, user_id, token, description, last_used_at, created_at
|
|
`
|
|
var apiKey model.APIKey
|
|
err := s.db.QueryRow(
|
|
query,
|
|
userID,
|
|
crypto.GenerateRandomStringHex(32),
|
|
description,
|
|
).Scan(
|
|
&apiKey.ID,
|
|
&apiKey.UserID,
|
|
&apiKey.Token,
|
|
&apiKey.Description,
|
|
&apiKey.LastUsedAt,
|
|
&apiKey.CreatedAt,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(`store: unable to create API Key: %v`, err)
|
|
}
|
|
|
|
return &apiKey, nil
|
|
}
|
|
|
|
// DeleteAPIKey deletes an API Key.
|
|
func (s *Storage) DeleteAPIKey(userID, keyID int64) error {
|
|
result, err := s.db.Exec(`DELETE FROM api_keys WHERE id = $1 AND user_id = $2`, keyID, userID)
|
|
if err != nil {
|
|
return fmt.Errorf(`store: unable to delete this API Key: %v`, err)
|
|
}
|
|
|
|
count, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf(`store: unable to delete this API Key: %v`, err)
|
|
}
|
|
|
|
if count == 0 {
|
|
return ErrAPIKeyNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|