Restructure project directories

This commit is contained in:
Radhi Fadlillah 2019-05-21 10:31:40 +07:00
parent 1b75fc1ead
commit 1e099fbd27
23 changed files with 375 additions and 2 deletions

5
.gitignore vendored
View File

@ -1,3 +1,6 @@
# Exclude config file
.vscode/
*.toml
*.toml
# Exclude executable file
/shiori*

2
go.mod
View File

@ -3,6 +3,8 @@ module github.com/go-shiori/shiori
go 1.12
require (
github.com/jmoiron/sqlx v1.2.0
github.com/sirupsen/logrus v1.4.2
github.com/spf13/cobra v0.0.4
google.golang.org/appengine v1.6.0 // indirect
)

12
go.sum
View File

@ -7,12 +7,21 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@ -35,9 +44,12 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -0,0 +1,13 @@
package cmd
import "github.com/spf13/cobra"
func accountAddCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "add username",
Short: "Create new account",
Args: cobra.ExactArgs(1),
}
return cmd
}

View File

@ -0,0 +1,17 @@
package cmd
import "github.com/spf13/cobra"
func accountDeleteCmd() *cobra.Command {
cmd := &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.",
}
cmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and delete ALL accounts")
return cmd
}

View File

@ -0,0 +1,16 @@
package cmd
import "github.com/spf13/cobra"
func accountPrintCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "print",
Short: "Print the saved accounts",
Args: cobra.NoArgs,
Aliases: []string{"list", "ls"},
}
cmd.Flags().StringP("search", "s", "", "Search accounts by username")
return cmd
}

20
internal/cmd/account.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import (
"github.com/spf13/cobra"
)
func accountCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "account",
Short: "Manage account for accessing web interface",
}
cmd.AddCommand(
accountAddCmd(),
accountPrintCmd(),
accountDeleteCmd(),
)
return cmd
}

20
internal/cmd/add.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import (
"github.com/spf13/cobra"
)
func addCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "add url",
Short: "Bookmark the specified URL",
Args: cobra.ExactArgs(1),
}
cmd.Flags().StringP("title", "i", "", "Custom title for this bookmark.")
cmd.Flags().StringP("excerpt", "e", "", "Custom excerpt for this bookmark.")
cmd.Flags().StringSliceP("tags", "t", []string{}, "Comma-separated tags for this bookmark.")
cmd.Flags().BoolP("offline", "o", false, "Save bookmark without fetching data from internet.")
return cmd
}

21
internal/cmd/delete.go Normal file
View File

@ -0,0 +1,21 @@
package cmd
import (
"github.com/spf13/cobra"
)
func deleteCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "delete [indices]",
Short: "Delete the saved bookmarks",
Long: "Delete bookmarks. " +
"When a record is deleted, the last record is moved to the removed index. " +
"Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " +
"If no arguments, ALL records will be deleted.",
Aliases: []string{"rm"},
}
cmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and delete ALL bookmarks")
return cmd
}

15
internal/cmd/export.go Normal file
View File

@ -0,0 +1,15 @@
package cmd
import (
"github.com/spf13/cobra"
)
func exportCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "export target-file",
Short: "Export bookmarks into HTML file in Netscape Bookmark format",
Args: cobra.ExactArgs(1),
}
return cmd
}

15
internal/cmd/import.go Normal file
View File

@ -0,0 +1,15 @@
package cmd
import "github.com/spf13/cobra"
func importCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "import source-file",
Short: "Import bookmarks from HTML file in Netscape Bookmark format",
Args: cobra.ExactArgs(1),
}
cmd.Flags().BoolP("generate-tag", "t", false, "Auto generate tag from bookmark's category")
return cmd
}

22
internal/cmd/open.go Normal file
View File

@ -0,0 +1,22 @@
package cmd
import (
"github.com/spf13/cobra"
)
func openCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "open [indices]",
Short: "Open the saved bookmarks",
Long: "Open bookmarks in browser. " +
"Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), " +
"hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " +
"If no arguments, ALL bookmarks will be opened.",
}
cmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and open ALL bookmarks")
cmd.Flags().BoolP("cache", "c", false, "Open the bookmark's cache in text-only mode")
cmd.Flags().Bool("trim-space", false, "Trim all spaces and newlines from the bookmark's cache")
return cmd
}

15
internal/cmd/pocket.go Normal file
View File

@ -0,0 +1,15 @@
package cmd
import (
"github.com/spf13/cobra"
)
func pocketCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "pocket source-file",
Short: "Import bookmarks from Pocket's exported HTML file",
Args: cobra.ExactArgs(1),
}
return cmd
}

20
internal/cmd/print.go Normal file
View File

@ -0,0 +1,20 @@
package cmd
import "github.com/spf13/cobra"
func printCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "print [indices]",
Short: "Print the saved bookmarks",
Long: "Show the saved bookmarks by its DB index. " +
"Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), " +
"hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " +
"If no arguments, all records with actual index from database are shown.",
Aliases: []string{"list", "ls"},
}
cmd.Flags().BoolP("json", "j", false, "Output data in JSON format")
cmd.Flags().BoolP("index-only", "i", false, "Only print the index of bookmarks")
return cmd
}

29
internal/cmd/root.go Normal file
View File

@ -0,0 +1,29 @@
package cmd
import (
"github.com/spf13/cobra"
)
// ShioriCmd returns the root command for shiori
func ShioriCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "shiori",
Short: "Simple command-line bookmark manager built with Go",
}
rootCmd.AddCommand(
accountCmd(),
addCmd(),
printCmd(),
searchCmd(),
updateCmd(),
deleteCmd(),
openCmd(),
importCmd(),
exportCmd(),
pocketCmd(),
serveCmd(),
)
return rootCmd
}

22
internal/cmd/search.go Normal file
View File

@ -0,0 +1,22 @@
package cmd
import "github.com/spf13/cobra"
func searchCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "search keyword",
Short: "Search bookmarks by submitted keyword",
Long: "Search bookmarks by looking for matching keyword in bookmark's title and content. " +
"If no keyword submitted, print all saved bookmarks. " +
"Search results will be different depending on DBMS that used by shiori :\n" +
"- sqlite3, search works using fts4 method: https://www.sqlite.org/fts3.html.\n" +
"- mysql or mariadb, search works using natural language mode: https://dev.mysql.com/doc/refman/5.5/en/fulltext-natural-language.html.",
Args: cobra.MaximumNArgs(1),
}
cmd.Flags().BoolP("json", "j", false, "Output data in JSON format")
cmd.Flags().BoolP("index-only", "i", false, "Only print the index of bookmarks")
cmd.Flags().StringSliceP("tags", "t", []string{}, "Search bookmarks with specified tag(s)")
return cmd
}

19
internal/cmd/serve.go Normal file
View File

@ -0,0 +1,19 @@
package cmd
import (
"github.com/spf13/cobra"
)
func serveCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "serve",
Short: "Serve web interface for managing bookmarks",
Long: "Run a simple annd performant web server which " +
"serves the site for managing bookmarks. If --port " +
"flag is not used, it will use port 8080 by default.",
}
cmd.Flags().IntP("port", "p", 8080, "Port that used by server")
return cmd
}

27
internal/cmd/update.go Normal file
View File

@ -0,0 +1,27 @@
package cmd
import "github.com/spf13/cobra"
func updateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update [indices]",
Short: "Update the saved bookmarks",
Long: "Update fields of an existing bookmark. " +
"Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), " +
"hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " +
"If no arguments, ALL bookmarks will be updated. Update works differently depending on the flags:\n" +
"- If indices are passed without any flags (--url, --title, --tag and --excerpt), read the URLs from DB and update titles from web.\n" +
"- If --url is passed (and --title is omitted), update the title from web using the URL. While using this flag, update only accept EXACTLY one index.\n" +
"While updating bookmark's tags, you can use - to remove tag (e.g. -nature to remove nature tag from this bookmark).",
}
cmd.Flags().StringP("url", "u", "", "New URL for this bookmark.")
cmd.Flags().StringP("title", "i", "", "New title for this bookmark.")
cmd.Flags().StringP("excerpt", "e", "", "New excerpt for this bookmark.")
cmd.Flags().StringSliceP("tags", "t", []string{}, "Comma-separated tags for this bookmark.")
cmd.Flags().BoolP("offline", "o", false, "Update bookmark without fetching data from internet.")
cmd.Flags().BoolP("yes", "y", false, "Skip confirmation prompt and update ALL bookmarks")
cmd.Flags().Bool("dont-overwrite", false, "Don't overwrite existing metadata. Useful when only want to update bookmark's content.")
return cmd
}

View File

@ -0,0 +1,5 @@
package database
// DB is interface for accessing and manipulating data in database.
type DB interface {
}

View File

@ -0,0 +1,11 @@
package database
import (
"github.com/jmoiron/sqlx"
)
// SQLiteDatabase is implementation of Database interface
// for connecting to SQLite3 database.
type SQLiteDatabase struct {
sqlx.DB
}

40
internal/model/model.go Normal file
View File

@ -0,0 +1,40 @@
package model
// Tag is the tag for a bookmark.
type Tag struct {
ID int `db:"id" json:"id"`
Name string `db:"name" json:"name"`
NBookmarks int `db:"n_bookmarks" json:"nBookmarks"`
Deleted bool `json:"-"`
}
// Bookmark is the record for an URL.
type Bookmark struct {
ID int `db:"id" json:"id"`
URL string `db:"url" json:"url"`
Title string `db:"title" json:"title"`
ImageURL string `db:"image_url" json:"imageURL"`
Excerpt string `db:"excerpt" json:"excerpt"`
Author string `db:"author" json:"author"`
MinReadTime int `db:"min_read_time" json:"minReadTime"`
MaxReadTime int `db:"max_read_time" json:"maxReadTime"`
Modified string `db:"modified" json:"modified"`
Content string `db:"content" json:"-"`
HTML string `db:"html" json:"html,omitempty"`
HasContent bool `db:"has_content" json:"hasContent"`
Tags []Tag `json:"tags"`
}
// Account is person that allowed to access web interface.
type Account struct {
ID int `db:"id" json:"id"`
Username string `db:"username" json:"username"`
Password string `db:"password" json:"password"`
}
// LoginRequest is request from user to access web interface.
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
Remember bool `json:"remember"`
}

View File

@ -0,0 +1 @@
package webserver

10
main.go
View File

@ -1,5 +1,13 @@
package main
import (
"github.com/go-shiori/shiori/internal/cmd"
"github.com/sirupsen/logrus"
)
func main() {
//
shioriCmd := cmd.ShioriCmd()
if err := shioriCmd.Execute(); err != nil {
logrus.Fatalln(err)
}
}