diff --git a/main.go b/main.go index 38dd16d..af1645b 100644 --- a/main.go +++ b/main.go @@ -1,3 +1,59 @@ package main -func main() {} +import ( + "net/http" + + "github.com/Sirupsen/logrus" + "github.com/dalu/wiki/wiki" + "github.com/dalu/wiki/wiki/storage" + "github.com/dalu/wiki/wiki/storage/mongo" + "gopkg.in/mgo.v2" + "gopkg.in/mgo.v2/bson" +) + +func main() { + log := logrus.New() + ms, e := mgo.Dial("localhost") + if e != nil { + log.Fatal("could not connect to mongodb", e.Error()) + } + defer ms.Close() + + mongostore := mongo.New(ms, mongo.Config{Database: "wiki"}) + if e := initializeWiki(mongostore); e != nil { + log.Fatal("initializeWiki: ", e.Error()) + } + + mux := http.NewServeMux() + wikiHandler := wiki.NewWikiHandler(mongostore, log) + + mux.Handle(wiki.DefaultMountPath, http.StripPrefix(wiki.DefaultMountPath, wikiHandler)) + + log.Fatal(http.ListenAndServe(":8080", mux)) +} + +func initializeWiki(s storage.WikiStorage) error { + _, e := s.GetTermByName("") + if e != nil { + if e == mgo.ErrNotFound { + t1 := new(storage.Term) + t1.ID = bson.NewObjectId() + t1.Language = "en" //TODO: create a Main_Page for all languages + t1.Redirect = "Main_Page" + t2 := new(storage.Term) + t2.ID = bson.NewObjectId() + t2.Language = "en" + t2.Name = "Main Page" + t2.Slug = "Main_Page" + if e := s.CreateTerm(t1); e != nil { + return e + } + if e := s.CreateTerm(t2); e != nil { + return e + } + } else { + return e + } + } + return nil +} diff --git a/wiki/handler.go b/wiki/handler.go new file mode 100644 index 0000000..94448b6 --- /dev/null +++ b/wiki/handler.go @@ -0,0 +1,88 @@ +package wiki + +import ( + "encoding/json" + "net/http" + + "github.com/Sirupsen/logrus" + "github.com/dalu/wiki/wiki/storage" +) + +const DefaultMountPath = "/wiki/" + +const ( + methodGET = "GET" + methodPOST = "POST" +) + +type wikiHandler struct { + s storage.WikiStorage + l *logrus.Logger +} + +func NewWikiHandler(s storage.WikiStorage, l *logrus.Logger) *wikiHandler { + l.Level = logrus.DebugLevel + return &wikiHandler{ + s: s, + l: l, + } +} + +func (h *wikiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case methodGET: + h.handleGET(w, r) + case methodPOST: + h.handlePOST(w, r) + default: + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + } +} + +func (h *wikiHandler) handleGET(w http.ResponseWriter, r *http.Request) { + action := r.URL.Query().Get("action") + + switch action { + case "": + h.l.Debug("show hit") + h.show(w, r) + case "edit": + h.l.Debug("edit hit") + case "new": + h.l.Debug("new hit") + case "revision": + h.l.Debug("revision hit") + case "revisions": + h.l.Debug("revisions hit") + } +} + +func (h *wikiHandler) handlePOST(w http.ResponseWriter, r *http.Request) {} + +// ----- + +func (h *wikiHandler) show(w http.ResponseWriter, r *http.Request) { + term, e := h.s.GetTermBySlug(r.URL.Path) + if e != nil { + h.l.Error("wikihandler.show.GetTermBySlug:", e) + return + } + if term.Redirect != "" { + http.Redirect(w, r, DefaultMountPath+term.Redirect, http.StatusTemporaryRedirect) + return + } + if term.CurrentRevisionID == "" { + http.Redirect(w, r, DefaultMountPath+r.URL.Path+"?action=new", http.StatusTemporaryRedirect) + return + } + revision, e := h.s.GetRevisionByID(term.CurrentRevisionID) + if e != nil { + h.l.Error("wikihandler.show.GetRevisionByTermID:", e) + return + } + // temporary + if e := json.NewEncoder(w).Encode(revision); e != nil { + h.l.Error("wikihandler.show.NewEncoder:", e) + http.Error(w, e.Error(), http.StatusInternalServerError) + } +} diff --git a/wiki/storage/interface.go b/wiki/storage/interface.go index c954136..d35da88 100644 --- a/wiki/storage/interface.go +++ b/wiki/storage/interface.go @@ -1,12 +1,10 @@ package storage -type Storage interface { +type WikiStorage interface { TermStorage - ContentStorage - DiffStorage + RevisionStorage } - type TermStorage interface { CreateTerm(term *Term) error UpdateTerm(term *Term) error @@ -14,24 +12,16 @@ type TermStorage interface { GetTermByName(name string) (*Term, error) GetTermBySlug(slug string) (*Term, error) GetTermByID(id string) (*Term, error) - GetTerms() ([]Term, error) - GenerateSlug(name string) string + GetTerms() ([]*Term, error) } -type ContentStorage interface { - CreateContent(content *Content) error - UpdateContent(content *Content) error - RemoveContent(content *Content) error - GetContentByTermName(termName string) (*Content, error) - GetContentByTermID(termID string) (*Content, error) - GetContentByID(id string) (*Content, error) - GetContents() ([]Content, error) +type RevisionStorage interface { + CreateRevision(revision *Revision) error + RemoveRevision(revision *Revision) error + RemoveRevisionsByTermID(termID string) error + GetRevisionByTermName(termName string) (*Revision, error) + GetRevisionByTermSlug(termSlug string) (*Revision, error) + GetRevisionByTermID(termID string) (*Revision, error) + GetRevisionByID(id string) (*Revision, error) + GetRevisions() ([]*Revision, error) } - -type DiffStorage interface { - CreateDiff(diff *Diff) error - UpdateDiff(diff *Diff) error - RemoveDiff(diff *Diff) error - GetDiff(term string) (*Diff, error) - GetDiffs() ([]Diff, error) -} \ No newline at end of file diff --git a/wiki/storage/model.go b/wiki/storage/model.go index a631fa5..9de9c7d 100644 --- a/wiki/storage/model.go +++ b/wiki/storage/model.go @@ -1,21 +1,24 @@ package storage -import "gopkg.in/mgo.v2/bson" +import ( + "time" + + "gopkg.in/mgo.v2/bson" +) type Term struct { - ID bson.ObjectId `bson:"_id,omitempty"` - Slug string - Name string + ID bson.ObjectId `bson:"_id,omitempty"` + Slug string `bson:"slug"` + Language string `bson:"lang"` + Name string `bson:"name"` + CurrentRevisionID string `bson:"revision_id,omitempty"` + Redirect string `bson:"redirect,omitempty"` } -type Content struct { - ID bson.ObjectId `bson:"_id,omitempty"` - TermID string - Text string -} - -type Diff struct { - ID bson.ObjectId `bson:"_id,omitempty"` - ContentID string - +type Revision struct { + ID bson.ObjectId `bson:"_id,omitempty"` + TermID string `bson:"term_id"` + AuthorIP string `bson:"author_ip"` + PublishDate time.Time `bson:"date"` + Text string `bson:"text"` } diff --git a/wiki/storage/mongo/storage.go b/wiki/storage/mongo/storage.go index 4052eb2..6209b7f 100644 --- a/wiki/storage/mongo/storage.go +++ b/wiki/storage/mongo/storage.go @@ -9,71 +9,65 @@ import ( const ( databaseDefault = "wiki" termCollectionDefault = "term" - contentCollectionDefault = "content" - diffCollectionDefault = "diff" + RevisionCollectionDefault = "revision" ) type mongoStorage struct { ms *mgo.Session database string termCollection string - contentCollection string - diffCollection string + revisionCollection string } type Config struct { Database string TermCollection string - ContentCollection string - DiffCollection string + RevisionCollection string } -func New(ms *mgo.Session, config Config) *mongoStorage { +func New(ms *mgo.Session, config Config) mongoStorage { if config.Database == "" { config.Database = databaseDefault } if config.TermCollection == "" { config.TermCollection = termCollectionDefault } - if config.ContentCollection == "" { - config.ContentCollection = contentCollectionDefault + if config.RevisionCollection == "" { + config.RevisionCollection = RevisionCollectionDefault } - if config.DiffCollection == "" { - config.DiffCollection = diffCollectionDefault - } - return &mongoStorage{ + + return mongoStorage{ ms: ms.Clone(), database: config.Database, termCollection: config.TermCollection, - contentCollection: config.ContentCollection, - diffCollection: config.DiffCollection, + revisionCollection: config.RevisionCollection, } } -// Term +// --- Term --- -func (s *mongoStorage) CreateTerm(term *storage.Term) error { +func (s mongoStorage) CreateTerm(term *storage.Term) error { ms := s.ms.Copy() defer ms.Close() c := ms.DB(s.database).C(s.termCollection) return c.Insert(term) } -func (s *mongoStorage) UpdateTerm(term *storage.Term) error { +func (s mongoStorage) UpdateTerm(term *storage.Term) error { ms := s.ms.Copy() defer ms.Close() c := ms.DB(s.database).C(s.termCollection) return c.UpdateId(term.ID, term) } -func (s *mongoStorage) RemoveTerm(term *storage.Term) error { +func (s mongoStorage) RemoveTerm(term *storage.Term) error { ms := s.ms.Copy() defer ms.Close() c := ms.DB(s.database).C(s.termCollection) return c.RemoveId(term.ID) } -func (s *mongoStorage) GetTermByName(name string) (*storage.Term, error) { +func (s mongoStorage) GetTermByName(name string) (*storage.Term, error) { ms := s.ms.Copy() defer ms.Close() c := ms.DB(s.database).C(s.termCollection) @@ -84,98 +78,129 @@ func (s *mongoStorage) GetTermByName(name string) (*storage.Term, error) { return term, nil } -func (s *mongoStorage) GetTerms() ([]storage.Term, error) { +func (s mongoStorage) GetTermBySlug(slug string) (*storage.Term, error) { ms := s.ms.Copy() defer ms.Close() c := ms.DB(s.database).C(s.termCollection) - terms := []storage.Term{} + term := new(storage.Term) + if e := c.Find(bson.M{"slug": slug}).One(term); e != nil { + return nil, e + } + return term, nil +} + +func (s mongoStorage) GetTermByID(id string) (*storage.Term, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.termCollection) + term := new(storage.Term) + if e := c.FindId(bson.ObjectIdHex(id)).One(term); e != nil { + return nil, e + } + return term, nil +} + +func (s mongoStorage) GetTerms() ([]*storage.Term, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.termCollection) + terms := []*storage.Term{} if e := c.Find(nil).All(&terms); e != nil { return terms, e } return terms, nil } -func (s *mongoStorage) GetTermByID(id string) (*storage.Term, error) { - panic("implement me") -} +// --- Revision --- -func (s *mongoStorage) GetTermBySlug(slug string) (*storage.Term, error) { - panic("implement me") -} - -func (s *mongoStorage) GenerateSlug(name string) string { - panic("implement me") -} - - -// Content - -func (s *mongoStorage) CreateContent(content *storage.Content) error { +func (s mongoStorage) CreateRevision(Revision *storage.Revision) error { ms := s.ms.Copy() defer ms.Close() - c := ms.DB(s.database).C(s.contentCollection) - return c.Insert(content) + c := ms.DB(s.database).C(s.revisionCollection) + return c.Insert(Revision) } -func (s *mongoStorage) UpdateContent(content *storage.Content) error { +func (s mongoStorage) RemoveRevision(Revision *storage.Revision) error { ms := s.ms.Copy() defer ms.Close() - c := ms.DB(s.database).C(s.contentCollection) - return c.UpdateId(content.ID, content) + c := ms.DB(s.database).C(s.revisionCollection) + return c.RemoveId(Revision.ID) } -func (s *mongoStorage) RemoveContent(content *storage.Content) error { +func (s mongoStorage) RemoveRevisionsByTermID(termID string) error { ms := s.ms.Copy() defer ms.Close() - c := ms.DB(s.database).C(s.contentCollection) - return c.RemoveId(content.ID) + c := ms.DB(s.database).C(s.revisionCollection) + _, e := c.RemoveAll(bson.M{"term_id": termID}) + if e != nil { + return e + } + return nil } -func (s *mongoStorage) GetContentByTermName(termName string) (*storage.Content, error) { +func (s mongoStorage) GetRevisionByTermName(termName string) (*storage.Revision, error) { ms := s.ms.Copy() defer ms.Close() - c := ms.DB(s.database).C(s.contentCollection) + c := ms.DB(s.database).C(s.revisionCollection) term, e := s.GetTermByName(termName) if e != nil { return nil, e } - content := new(storage.Content) - if e := c.Find(bson.M{"term_id": term.ID.Hex()}).One(content); e != nil { + Revision := new(storage.Revision) + if e := c.Find(bson.M{"term_id": term.ID.Hex()}).One(Revision); e != nil { return nil, e } - return content, nil + return Revision, nil } -func (s *mongoStorage) GetContentByID(id string) (*storage.Content, error) { - panic("implement me") +func (s mongoStorage) GetRevisionByTermSlug(termSlug string) (*storage.Revision, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.revisionCollection) + term, e := s.GetTermByName(termSlug) + if e != nil { + return nil, e + } + Revision := new(storage.Revision) + if e := c.Find(bson.M{"term_id": term.ID.Hex()}).One(Revision); e != nil { + return nil, e + } + return Revision, nil } -func (s *mongoStorage) GetContentByTermID(termID string) (*storage.Content, error) { - panic("implement me") +func (s mongoStorage) GetRevisionByID(id string) (*storage.Revision, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.revisionCollection) + Revision := new(storage.Revision) + if e := c.FindId(bson.ObjectIdHex(id)).One(Revision); e != nil { + return nil, e + } + return Revision, nil } -func (s *mongoStorage) GetContents() ([]storage.Content, error) { - panic("implement me") +func (s mongoStorage) GetRevisionByTermID(termID string) (*storage.Revision, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.revisionCollection) + term, e := s.GetTermByID(termID) + if e != nil { + return nil, e + } + Revision := new(storage.Revision) + if e := c.Find(bson.M{"term_id": term.ID.Hex()}).One(Revision); e != nil { + return nil, e + } + return Revision, nil } -// Diff - -func (s *mongoStorage) CreateDiff(diff *storage.Diff) error { - panic("implement me") -} - -func (s *mongoStorage) UpdateDiff(diff *storage.Diff) error { - panic("implement me") -} - -func (s *mongoStorage) RemoveDiff(diff *storage.Diff) error { - panic("implement me") -} - -func (s *mongoStorage) GetDiff(term string) (*storage.Diff, error) { - panic("implement me") -} - -func (s *mongoStorage) GetDiffs() ([]storage.Diff, error) { - panic("implement me") +func (s mongoStorage) GetRevisions() ([]*storage.Revision, error) { + ms := s.ms.Copy() + defer ms.Close() + c := ms.DB(s.database).C(s.revisionCollection) + terms := []*storage.Revision{} + if e := c.Find(nil).All(&terms); e != nil { + return terms, e + } + return terms, nil } diff --git a/wiki/wiki.go b/wiki/wiki.go index 3e1ef4c..2d523f0 100644 --- a/wiki/wiki.go +++ b/wiki/wiki.go @@ -1 +1,5 @@ package wiki + +func generateSlug() {} + +func diff(text1, text2 string) {} \ No newline at end of file