mirror of
https://github.com/go-shiori/shiori.git
synced 2025-09-06 21:14:47 +08:00
Create cmd for managing accounts
This commit is contained in:
parent
d74d545b49
commit
b555d1b5a4
5 changed files with 205 additions and 4 deletions
128
cmd/account.go
Normal file
128
cmd/account.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
accountCmd = &cobra.Command{
|
||||
Use: "account",
|
||||
Short: "Manage account for accessing web interface.",
|
||||
}
|
||||
|
||||
addAccountCmd = &cobra.Command{
|
||||
Use: "add username",
|
||||
Short: "Create new account.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
username := args[0]
|
||||
|
||||
fmt.Println("Username: " + username)
|
||||
fmt.Print("Password: ")
|
||||
|
||||
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
err = addAccount(username, string(bytePassword))
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
printAccountCmd = &cobra.Command{
|
||||
Use: "print",
|
||||
Short: "Print the saved accounts.",
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
keyword, _ := cmd.Flags().GetString("search")
|
||||
err := printAccounts(keyword)
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
deleteAccountCmd = &cobra.Command{
|
||||
Use: "delete [usernames]",
|
||||
Short: "Delete the saved accounts.",
|
||||
Long: "Delete accounts. " +
|
||||
"Accepts space-separated list of usernames. " +
|
||||
"If no arguments, all records will be deleted.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Read flags
|
||||
skipConfirmation, _ := cmd.Flags().GetBool("yes")
|
||||
|
||||
// If no arguments, confirm to user
|
||||
if len(args) == 0 && !skipConfirmation {
|
||||
confirmDelete := ""
|
||||
fmt.Print("Remove ALL accounts? (y/n): ")
|
||||
fmt.Scanln(&confirmDelete)
|
||||
|
||||
if confirmDelete != "y" {
|
||||
fmt.Println("No accounts deleted")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err := DB.DeleteAccounts(args...)
|
||||
if err != nil {
|
||||
cError.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("Accounts has been deleted")
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Create flags
|
||||
printAccountCmd.Flags().StringP("search", "s", "", "Search accounts by username")
|
||||
deleteAccountCmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and delete ALL accounts")
|
||||
|
||||
accountCmd.AddCommand(addAccountCmd)
|
||||
accountCmd.AddCommand(printAccountCmd)
|
||||
accountCmd.AddCommand(deleteAccountCmd)
|
||||
rootCmd.AddCommand(accountCmd)
|
||||
}
|
||||
|
||||
func addAccount(username, password string) error {
|
||||
if username == "" {
|
||||
return fmt.Errorf("Username must not empty")
|
||||
}
|
||||
|
||||
if len(password) < 8 {
|
||||
return fmt.Errorf("Password must be at least 8 characters")
|
||||
}
|
||||
|
||||
err := DB.CreateAccount(username, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printAccounts(keyword string) error {
|
||||
accounts, err := DB.GetAccounts(keyword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, account := range accounts {
|
||||
cIndex.Print("- ")
|
||||
fmt.Println(account.Username)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -84,7 +84,7 @@ func addBookmark(url, title, excerpt string, tags []string, offline bool) (book
|
|||
}
|
||||
|
||||
// Save to database
|
||||
bookmark.ID, err = DB.SaveBookmark(bookmark)
|
||||
bookmark.ID, err = DB.CreateBookmark(bookmark)
|
||||
if err != nil {
|
||||
return book, err
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
// Database is interface for manipulating data in database.
|
||||
type Database interface {
|
||||
// SaveBookmark saves new bookmark to database.
|
||||
SaveBookmark(bookmark model.Bookmark) (int64, error)
|
||||
CreateBookmark(bookmark model.Bookmark) (int64, error)
|
||||
|
||||
// GetBookmarks fetch list of bookmarks based on submitted indices.
|
||||
GetBookmarks(options GetBookmarksOptions, indices ...string) ([]model.Bookmark, error)
|
||||
|
@ -21,6 +21,15 @@ type Database interface {
|
|||
|
||||
// UpdateBookmarks updates the saved bookmark in database.
|
||||
UpdateBookmarks(bookmarks []model.Bookmark) error
|
||||
|
||||
// CreateAccount creates new account in database
|
||||
CreateAccount(username, password string) error
|
||||
|
||||
// GetAccounts fetch list of accounts in database
|
||||
GetAccounts(keyword string) ([]model.Account, error)
|
||||
|
||||
// DeleteAccounts removes all record with matching usernames
|
||||
DeleteAccounts(usernames ...string) error
|
||||
}
|
||||
|
||||
type GetBookmarksOptions struct {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/RadhiFadlillah/shiori/model"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -75,8 +76,8 @@ func OpenSQLiteDatabase() (*SQLiteDatabase, error) {
|
|||
return &SQLiteDatabase{*db}, err
|
||||
}
|
||||
|
||||
// SaveBookmark saves new bookmark to database. Returns new ID and error if any happened.
|
||||
func (db *SQLiteDatabase) SaveBookmark(bookmark model.Bookmark) (bookmarkID int64, err error) {
|
||||
// CreateBookmark saves new bookmark to database. Returns new ID and error if any happened.
|
||||
func (db *SQLiteDatabase) CreateBookmark(bookmark model.Bookmark) (bookmarkID int64, err error) {
|
||||
// Check URL and title
|
||||
if bookmark.URL == "" {
|
||||
return -1, fmt.Errorf("URL must not empty")
|
||||
|
@ -536,3 +537,59 @@ func (db *SQLiteDatabase) UpdateBookmarks(bookmarks []model.Bookmark) (err error
|
|||
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateAccount saves new account to database. Returns new ID and error if any happened.
|
||||
func (db *SQLiteDatabase) CreateAccount(username, password string) (err error) {
|
||||
// Hash password with bcrypt
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Insert account to database
|
||||
_, err = db.Exec(`INSERT INTO account
|
||||
(username, password) VALUES (?, ?)`,
|
||||
username, hashedPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAccounts fetch list of accounts in database
|
||||
func (db *SQLiteDatabase) GetAccounts(keyword string) ([]model.Account, error) {
|
||||
query := `SELECT id, username, password FROM account`
|
||||
args := []interface{}{}
|
||||
if keyword != "" {
|
||||
query += ` WHERE username LIKE ?`
|
||||
args = append(args, "%"+keyword+"%")
|
||||
}
|
||||
query += ` ORDER BY username`
|
||||
|
||||
accounts := []model.Account{}
|
||||
err := db.Select(&accounts, query, args...)
|
||||
return accounts, err
|
||||
}
|
||||
|
||||
// DeleteAccounts removes all record with matching usernames
|
||||
func (db *SQLiteDatabase) DeleteAccounts(usernames ...string) error {
|
||||
// Prepare where clause
|
||||
args := []interface{}{}
|
||||
whereClause := " WHERE 1"
|
||||
|
||||
if len(usernames) > 0 {
|
||||
whereClause = " WHERE username IN ("
|
||||
for _, username := range usernames {
|
||||
args = append(args, username)
|
||||
whereClause += "?,"
|
||||
}
|
||||
|
||||
whereClause = whereClause[:len(whereClause)-1]
|
||||
whereClause += ")"
|
||||
}
|
||||
|
||||
// Delete usernames
|
||||
_,err := db.Exec(`DELETE FROM account `+whereClause, args...)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -24,3 +24,10 @@ type Bookmark struct {
|
|||
HTML template.HTML `db:"html" json:"-"`
|
||||
Tags []Tag `json:"tags"`
|
||||
}
|
||||
|
||||
// Account is account for accessing bookmarks from web interface
|
||||
type Account struct {
|
||||
ID int64 `db:"id" json:"id"`
|
||||
Username string `db:"username" json:"username"`
|
||||
Password string `db:"password" json:"password"`
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue