diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 059839b..3721113 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -5,6 +5,7 @@ package cmd import ( "git.giftfish.de/ston1th/godrop/v2" "git.giftfish.de/ston1th/gowiki/pkg/core" + "git.giftfish.de/ston1th/gowiki/pkg/db" "git.giftfish.de/ston1th/gowiki/pkg/log" "git.giftfish.de/ston1th/gowiki/pkg/server" "github.com/urfave/cli" @@ -18,13 +19,18 @@ import ( const ( defDataDir = "/var/lib/gowiki" - defListen = "127.0.0.1:8090" + defListen = "127.0.0.1:8080" defLogFile = "gowiki.log" defRunUser = "wiki" defRunGroup = "wiki" ) +var ( + conf core.Config + dumpFile string +) + func initServer(conf core.Config) (err error) { dropCfg := godrop.Config{ User: conf.User, @@ -70,11 +76,6 @@ func initServer(conf core.Config) (err error) { return } -var ( - verbose bool - conf core.Config -) - func Run(version string) { app := cli.NewApp() app.Name = "gowiki" @@ -95,31 +96,67 @@ func Run(version string) { }, { Name: "dump", - Usage: "create a json dump of all articles in the data directory (dump is written to stdout) gowiki needs to be stopped", + Usage: "create a json dump of all articles in the data directory (gowiki needs to be stopped)", Flags: defaultFlags(dumpRestoreFlags()), Action: func(c *cli.Context) error { - if err := os.Chdir(conf.DataDir); err != nil { + var ( + file = os.Stdout + err error + ) + if dumpFile != "-" { + file, err = os.OpenFile(dumpFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640) + if err != nil { + stdlog.Fatal("dump: ", err) + } + } + if err = os.Chdir(conf.DataDir); err != nil { + stdlog.Fatal("dump: ", err) + } + DB, err := db.New(conf.DataDir) + if err != nil { + stdlog.Fatal("dump: ", err) + } + if err = DB.Dump(file); err != nil { + stdlog.Fatal("dump: ", err) + } + file.Close() + err = DB.Close() + if err != nil { stdlog.Fatal("dump: ", err) } - //TODO - //if err := db.Dump(blevePath, verbose); err != nil { - // stdlog.Fatal("dump: ", err) - //} return nil }, }, { Name: "restore", - Usage: "restore a json dump to the data directory (dump is read from stdin) gowiki needs to be stopped", + Usage: "restore a json dump to the data directory (gowiki needs to be stopped)", Flags: defaultFlags(dumpRestoreFlags()), Action: func(c *cli.Context) error { - if err := os.Chdir(conf.DataDir); err != nil { + var ( + file = os.Stdin + err error + ) + if dumpFile != "-" { + file, err = os.Open(dumpFile) + if err != nil { + stdlog.Fatal("dump: ", err) + } + } + if err = os.Chdir(conf.DataDir); err != nil { + stdlog.Fatal("restore: ", err) + } + DB, err := db.New(conf.DataDir) + if err != nil { + stdlog.Fatal("restore: ", err) + } + if err = DB.Restore(file); err != nil { + stdlog.Fatal("restore: ", err) + } + file.Close() + err = DB.Close() + if err != nil { stdlog.Fatal("restore: ", err) } - //TODO - //if err := db.Restore(blevePath, verbose); err != nil { - // stdlog.Fatal("restore: ", err) - //} return nil }, }, @@ -178,10 +215,11 @@ func serverFlags() []cli.Flag { func dumpRestoreFlags() []cli.Flag { return []cli.Flag{ - cli.BoolFlag{ - Name: "verbose, v", - Usage: "enable verbose mode", - Destination: &verbose, + cli.StringFlag{ + Name: "file, f", + Value: "-", + Usage: "file to read/write dump (default stdin/stdout)", + Destination: &dumpFile, }, } } diff --git a/pkg/db/db.go b/pkg/db/db.go index 937f07d..92634ea 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -7,7 +7,9 @@ import ( "git.giftfish.de/ston1th/gowiki/pkg/cache" "git.giftfish.de/ston1th/gowiki/pkg/core" "git.giftfish.de/ston1th/gowiki/pkg/index" + "git.giftfish.de/ston1th/gowiki/pkg/render" "git.giftfish.de/ston1th/gowiki/pkg/store" + "io" ) const ( @@ -61,10 +63,30 @@ func New(datadir string) (db *DB, err error) { return } -func (db *DB) Close() error { - err := db.store.Close() +func (db *DB) Dump(w io.Writer) error { + return db.store.Dump(w) +} + +func (db *DB) Restore(r io.Reader) (err error) { + err = db.store.Restore(r) if err != nil { - return err + return + } + err = db.store.ForEach(func(k string, _ []byte) error { + if trim, ok := hasTrimPrefix(k, pagePrefix); ok { + p := new(core.Page) + db.store.Get(pagePrefix+trim, p) + db.Index.Add(p.Title, p.StoreTitle, render.Render(p)) + } + return nil + }) + return +} + +func (db *DB) Close() (err error) { + err = db.store.Close() + if err != nil { + return } return db.Index.Close() } diff --git a/pkg/db/dump.go b/pkg/db/dump.go deleted file mode 100644 index 8fb7230..0000000 --- a/pkg/db/dump.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 2018 Marius Schellenberger - -package db - -/*import ( - "encoding/json" - "fmt" - "os" - - "git.giftfish.de/ston1th/gowiki/pkg/core" -) - -type DBDump struct { - Pages []DumpPage `json:"pages"` - Snippets []Snippet `json:"snippets"` - Users []User `json:"users"` -} - -type DumpPage struct { - Title string `json:"title"` - LinkTitle string `json:"link_title"` - MD string `json:"md"` - Created string `json:"created"` - Updated string `json:"updated"` - Public bool `json:"public"` -} - -func verboseLog(prefix, title string, v bool) { - if v { - fmt.Fprintln(os.Stderr, prefix, title) - } -} - -func Dump(path string, v bool) (err error) { - bs, err := NewBoltStore() - if err != nil { - return - } - var dump DBDump - //TODO bs.GetAllPages - var pages []*core.Page - for _, p := range pages { - if err != nil { - return - } - verboseLog("dump:", p.Title, v) - dump.Pages = append(dump.Pages, DumpPage{ - Title: p.Title, - LinkTitle: p.LinkTitle, - MD: p.MD, - Created: p.Created, - Updated: p.Updated, - Public: p.Public, - }) - } - //TODO snippets - //verboseLog("dump:", "snippets", v) - verboseLog("dump:", "users", v) - dump.Users, err = bs.DumpUsers() - if err != nil { - return - } - return json.NewEncoder(os.Stdout).Encode(dump) -} - -func Restore(path string, v bool) (err error) { - i, err := NewIndex(path) - if err != nil { - return - } - defer i.Close() - bs, err := NewBoltStore() - if err != nil { - return - } - var dump DBDump - err = json.NewDecoder(os.Stdin).Decode(&dump) - if err != nil { - return - } - for _, art := range dump.Articles { - verboseLog("restore:", art.Title, v) - err = i.Restore(&art) - if err != nil { - return - } - } - //TODO snippets - //verboseLog("restore:", "snippets", v) - verboseLog("restore:", "users", v) - err = bs.RestoreUsers(dump.Users) - return -}*/ diff --git a/pkg/db/page.go b/pkg/db/page.go index 1ef130d..fc46be8 100644 --- a/pkg/db/page.go +++ b/pkg/db/page.go @@ -25,7 +25,7 @@ func (db *DB) GetAllPages(username string) (pages core.Pages) { if trim == core.IndexURI { return nil } - p, err := db.GetPage(trim, username) + p, err := db.getPage(trim, username) if err == nil { pages = append(pages, *p) } @@ -36,11 +36,12 @@ func (db *DB) GetAllPages(username string) (pages core.Pages) { return } -func (db *DB) GetPage(title, username string) (p *core.Page, err error) { - p = db.cache.Get(title) +func (db *DB) GetPage(section, title, username string) (p *core.Page, err error) { + st := render.StoreTitle(section, title) + p = db.cache.Get(st) if p == nil { - p, err = db.getPage(title, username) - db.cache.Add(title, p) + p, err = db.getPage(st, username) + db.cache.Add(st, p) return } if !core.ReadPerm(username, p) { @@ -74,7 +75,7 @@ func (db *DB) CreatePage(title, section, markdown, username string, p core.Permi return } } - st := render.StoreTitle(section, title) + st := render.StoreTitle(section, render.Title(title)) if _, err = db.getPage(st, username); err != nil && err != store.ErrKeyNotFound { return } @@ -130,7 +131,7 @@ func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permi return } - err = db.Index.Add(title, st, search) + err = db.Index.Add(page.Title, st, search) if err != nil { return } diff --git a/pkg/db/user.go b/pkg/db/user.go index 97e0f4b..a655112 100644 --- a/pkg/db/user.go +++ b/pkg/db/user.go @@ -8,6 +8,7 @@ import ( "git.giftfish.de/ston1th/gowiki/pkg/log" "git.giftfish.de/ston1th/jwt/v3" "golang.org/x/crypto/bcrypt" + "regexp" "sort" "strconv" "strings" @@ -19,26 +20,21 @@ var ( errUserNotFound = dbErr("user not found") errUserExists = dbErr("user already exist") errUserReserved = dbErr("username is reserved") + + userRe = regexp.MustCompile("[^a-zA-Z0-9]+") ) -func (db *DB) DumpUsers() (users core.Users, err error) { - db.store.ForEach(func(k string, _ []byte) error { +func (db *DB) GetUsers() (users core.Users, err error) { + err = db.store.ForEach(func(k string, _ []byte) error { if trim, ok := hasTrimPrefix(k, userPrefix); ok { u, err := db.GetUser(trim) + u.Password = "" if err == nil { users = append(users, u) } } return nil }) - return -} - -func (db *DB) GetUsers() (users core.Users, err error) { - users, err = db.DumpUsers() - for i := range users { - users[i].Password = "" - } sort.Sort(users) return } @@ -58,31 +54,8 @@ func (db *DB) UserExists(username string) error { return db.store.Get(userPrefix+username, nil) } -// TODO -/*func (bs *BoltStore) RestoreUsers(users []User) error { - return bs.db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte(userTable)) - err := b.ForEach(func(k, v []byte) error { - return b.Delete(k) - }) - if err != nil { - return err - } - for _, u := range users { - v, err := bs.Marshal(u) - if err != nil { - return err - } - err = b.Put([]byte(u.Username), v) - if err != nil { - return err - } - } - return nil - }) -}*/ - func (db *DB) CreateUser(username, password string, admin bool) error { + username = userRe.ReplaceAllString(username, "") if reservedUser(username) { return errUserReserved } diff --git a/pkg/render/render.go b/pkg/render/render.go index 89f33f4..9be13b4 100644 --- a/pkg/render/render.go +++ b/pkg/render/render.go @@ -30,7 +30,7 @@ var ( func Render(p *core.Page) string { rend := blackfriday.MarkdownCommon([]byte(p.Markdown)) - index, html := buildIndex(string(rend)) + index, html := buildTOC(string(rend)) html = strings.Replace(html, "