From 3fbcfdcb5dbaaa6d6244c464e88339582a01617f Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Mon, 28 Sep 2020 13:03:57 -0500 Subject: [PATCH] Commiting changes long overdue --- assets.go | 67 ++++++++++++++++++++------------ endpoints_public.go | 24 ------------ endpoints_rss.go | 73 +++++++++++++++++++++++++++++++++++ endpoints_user.go | 16 ++++++++ main.go | 1 + model.go | 30 ++++++++++++-- model_feeds.go | 32 ++++++++------- model_sitedata.go | 8 ++-- model_user.go | 36 ++++++++--------- page_data.go | 8 ++++ templates/rss_feed.xml | 20 ++++++++++ templates/user_dashboard.html | 2 + 12 files changed, 229 insertions(+), 88 deletions(-) create mode 100644 endpoints_rss.go create mode 100644 templates/rss_feed.xml diff --git a/assets.go b/assets.go index 1879a8b..64bfe34 100644 --- a/assets.go +++ b/assets.go @@ -475,40 +475,57 @@ ZfY39XJelrPeb2dTP1e8O5VLPwIAAP//0xnP9ywDAAA= `, }, + "/templates/rss_feed.xml": { + local: "templates/rss_feed.xml", + size: 700, + modtime: 1547486966, + compressed: ` +H4sIAAAAAAAC/2xSTY+bMBC951dMEbdt7bTqaev1alvUU9VDlZyiHAyeEivGIH/QShb/fQUG8qGcsGfe +e543D/b6v9HQo3WqNS/ZZ7LNXvmGWecuxS9km/ENAKtOwhjU4xmAeeU18j+qLJWHn4iS0VRKba3MmZ+8 +754ptROIlEFrtFUr8V9rz45UbUNRKk9jJDtsOi08FsILsndoyT4oOQyMTkJJU6KrrOq8ag0fX4S/rYXH +ZIfWiAZHgWvWPJswdRA1cjSfgmN0vS9t578HpWUhPPJ7/bUzDXcDnRfjNf+6ZXT8TpUYwQpTI+TqI+Q9 +PL/AreToxcEwJP6Hw4/ibfd2yOAJ8p4Ul/HhCbLjkWcJpzw26YE1jRjznvyefV+lAcC6UM528p78Es7v +OzmbWFoLtA5KJpxrg61wGL6lmw71cr5VmBgLfQrsOre75Cb+6OpxOPTiK0ZAI9NiGF1/P0atc3zzHgAA +//+UZXAKvAIAAA== +`, + }, + "/templates/user_dashboard.html": { local: "templates/user_dashboard.html", - size: 45, - modtime: 1547160534, + size: 97, + modtime: 1547486320, compressed: ` -H4sIAAAAAAAC/7JJVMgoSk2zVdIvLU4t0k9LTU0pVrJzTcksUQguTSpOLsosKMnMzyu20U+04wIEAAD/ -/2HSavctAAAA +H4sIAAAAAAAC/7JJVMgoSk2zVdIvLU4t0k9LTU0pVrJzTcksUQguTSpOLsosKMnMzyu20U+04+JCqC4q +LtavrtYLSc0tyEksSXVJLEnUCy3NTKmtVbILCg5WcEtNTQHrAQQAAP//EcPbEmEAAAA= `, }, "/templates/user_feeds.html": { local: "templates/user_feeds.html", - size: 3588, - modtime: 1547234296, + size: 4065, + modtime: 1547235825, compressed: ` -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 +UuK/debJVdLmyWLWftT+EwAA///sH8RP4Q8AAA== `, }, diff --git a/endpoints_public.go b/endpoints_public.go index 28b98cf..5f45e6d 100644 --- a/endpoints_public.go +++ b/endpoints_public.go @@ -1,10 +1,7 @@ package main import ( - "fmt" "net/http" - - "github.com/gorilla/mux" ) func handleRequest(w http.ResponseWriter, req *http.Request) { @@ -19,24 +16,3 @@ func handleRequest(w http.ResponseWriter, req *http.Request) { handleUserLoginForm(page) } } - -func handleRssFeed(w http.ResponseWriter, req *http.Request) { - vars := mux.Vars(req) - var uid string - var uidOk bool - if uid, uidOk = vars["uid"]; !uidOk { - userError(w) - return - } - w.Header().Set("Content-Type", "application/xml") - v, err := buildRssFeed(uid) - if err != nil { - userError(w) - return - } - fmt.Fprint(w, v) -} - -func buildRssFeed(uid string) (string, error) { - return "", nil -} diff --git a/endpoints_rss.go b/endpoints_rss.go new file mode 100644 index 0000000..d955b19 --- /dev/null +++ b/endpoints_rss.go @@ -0,0 +1,73 @@ +package main + +import ( + "net/http" + "strings" + "time" + + "github.com/gorilla/mux" +) + +func handleRssFeed(w http.ResponseWriter, req *http.Request) { + page := initPageData(w, req) + + vars := mux.Vars(req) + var uid string + var uidOk bool + if uid, uidOk = vars["uid"]; !uidOk { + userError(w) + return + } + w.Header().Set("Content-Type", "application/xml") + var slug string + var slugOk bool + slug, slugOk = vars["slug"] + if !slugOk { + // Print the user's entire feed + buildUserRssFeed(page, uid) + } else { + // Print the feed for a specific slug + buildSlugRssFeed(page, uid, slug) + } +} + +type rssPageData struct { + User *User + Feeds []Feed + BuildDate time.Time +} + +func buildUserRssFeed(page *pageData, uid string) { + var err error + rpd := new(rssPageData) + rpd.BuildDate = time.Now() + rpd.User, err = m.GetUser(uid) + if err != nil { + userError(page.session.w) + return + } + for _, v := range rpd.User.SubSlugs { + pts := strings.Split(v, ";") + fd, err := m.GetFeed(pts[0], pts[1]) + if err != nil { + userError(page.session.w) + return + } + rpd.Feeds = append(rpd.Feeds, *fd) + } + page.TemplateData = rpd + page.showRss() +} + +func buildSlugRssFeed(page *pageData, uid, slug string) { + var err error + rpd := new(rssPageData) + rpd.BuildDate = time.Now() + rpd.User, err = m.GetUser(uid) + if err != nil { + userError(page.session.w) + return + } + page.TemplateData = rpd + page.showRss() +} diff --git a/endpoints_user.go b/endpoints_user.go index 3e89756..36ecbf4 100644 --- a/endpoints_user.go +++ b/endpoints_user.go @@ -75,6 +75,17 @@ func doLogin(uid, password string) error { func handleUserDashboard(page *pageData) { page.SubTitle = "dashboard" + var err error + id, err := page.session.getStringValue("id") + if err != nil { + userError(page.session.w) + return + } + page.TemplateData, err = m.GetUser(id) + if err != nil { + userError(page.session.w) + return + } page.show("user_dashboard.html") } @@ -120,6 +131,11 @@ func handleUpdateUserSubs(page *pageData) { u, err = m.GetUser(id) u.SubSlugs = userSubs m.SaveUser(u) + for k := range m.Users { + if m.Users[k].Uuid == id { + m.Users[k].SubSlugs = userSubs + } + } redirect("/user/dashboard", page.session.w, page.session.req) } diff --git a/main.go b/main.go index efd0147..98acb6f 100644 --- a/main.go +++ b/main.go @@ -72,6 +72,7 @@ func main() { pub.HandleFunc("/api/comics/{cid}/{function}", handleApiComicsCall) */ pub.HandleFunc("/rss/{uid}", handleRssFeed) + pub.HandleFunc("/rss/{uid}/{slug}", handleRssFeed) pub.HandleFunc("/user/{function}", handleUserRequest) http.Handle("/", r) diff --git a/model.go b/model.go index e032645..31e0d16 100644 --- a/model.go +++ b/model.go @@ -10,8 +10,10 @@ import ( ) type model struct { + dbOpen int bolt *boltease.DB dbFileName string + dbChanged bool Users []User FeedSources []FeedSource @@ -48,10 +50,10 @@ func NewModel() (*model, error) { func (m *model) initDB() error { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() if err = m.bolt.MkBucketPath([]string{"site"}); err != nil { return err @@ -65,9 +67,31 @@ func (m *model) initDB() error { return nil } +func (m *model) openDB() error { + m.dbOpen = m.dbOpen + 1 + if m.dbOpen > 1 { + return nil + } + var err error + if err = m.openDB(); err != nil { + return err + } + return nil +} + +func (m *model) closeDB() { + m.dbOpen = m.dbOpen - 1 + if m.dbOpen == 0 { + m.closeDB() + } +} + func (m *model) saveChanges() { + fmt.Println("Saving Site to DB") m.Site.SaveToDB() - //m.SaveAllFeeds(m.Feeds) + //fmt.Println("Saving Feeds to DB") + //m.SaveAllFeeds(m.FeedSources) + fmt.Println("Saving Users to DB") m.SaveAllUsers(m.Users) } diff --git a/model_feeds.go b/model_feeds.go index 99b1e35..b111531 100644 --- a/model_feeds.go +++ b/model_feeds.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "plugin" "time" ) @@ -91,7 +92,7 @@ func NewFeed(s, n, a, source string) *Feed { } func (f *Feed) GetBucket() []string { - return []string{"feed", f.Source, f.Slug} + return []string{"feeds", f.Source, f.Slug} } func (f *Feed) Update() error { @@ -146,10 +147,10 @@ func (f *Feed) GetRssItem() string { // DB Function to save a feed func (m *model) SaveFeed(f *Feed) error { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() bkt := f.GetBucket() if err = m.bolt.MkBucketPath(bkt); err != nil { return err @@ -180,10 +181,10 @@ func (m *model) SaveFeed(f *Feed) error { // DB Function to get a feed func (m *model) GetFeed(source, slug string) (*Feed, error) { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return nil, err } - defer m.bolt.CloseDB() + defer m.closeDB() ret := new(Feed) ret.Source = source ret.Slug = slug @@ -210,14 +211,17 @@ func (m *model) LoadFeeds() error { } // Save all feeds to the DB -func (m *model) SaveAllFeeds(feeds []Feed) { +func (m *model) SaveAllFeeds(feedSources []FeedSource) { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return } - defer m.bolt.CloseDB() - for i := range feeds { - m.SaveFeed(&feeds[i]) + defer m.closeDB() + for _, v := range feedSources { + for j := range v.Feeds { + fmt.Printf("Saving Feed to DB (%s;%s)\n", v.Feeds[j].Source, v.Feeds[j].Slug) + m.SaveFeed(v.Feeds[j]) + } } } @@ -225,10 +229,10 @@ func (m *model) SaveAllFeeds(feeds []Feed) { func (m *model) GetAllFeeds() []Feed { var ret []Feed var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return ret } - defer m.bolt.CloseDB() + defer m.closeDB() var srcs []string bkt := []string{"feeds"} if srcs, err = m.bolt.GetBucketList(bkt); err != nil { @@ -253,10 +257,10 @@ func (m *model) GetAllFeeds() []Feed { // Delete a feed from the DB func (m *model) DeleteFeed(slug string) error { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() return m.bolt.DeleteBucket([]string{"feeds"}, slug) } diff --git a/model_sitedata.go b/model_sitedata.go index 0a8a6b1..d012518 100644 --- a/model_sitedata.go +++ b/model_sitedata.go @@ -42,10 +42,10 @@ func NewSiteData(m *model) *SiteData { // load the site data out of the database // If fields don't exist in the DB, don't clobber what is already in s func (s *SiteData) LoadFromDB() error { - if err := s.m.bolt.OpenDB(); err != nil { + if err := s.m.openDB(); err != nil { return err } - defer s.m.bolt.CloseDB() + defer s.m.closeDB() if title, _ := s.m.bolt.GetValue(s.mPath, "title"); strings.TrimSpace(title) != "" { s.Title = title @@ -78,10 +78,10 @@ func (s *SiteData) NeedsSave() bool { func (s *SiteData) SaveToDB() error { s.lastSave = time.Now() var err error - if err = s.m.bolt.OpenDB(); err != nil { + if err = s.m.openDB(); err != nil { return err } - defer s.m.bolt.CloseDB() + defer s.m.closeDB() if err = s.m.bolt.SetValue(s.mPath, "title", s.Title); err != nil { return err diff --git a/model_user.go b/model_user.go index eb7d7a5..e256401 100644 --- a/model_user.go +++ b/model_user.go @@ -40,10 +40,10 @@ func (u *User) UpdateFeed() error { func (m *model) SaveUser(u *User) error { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() bkt := []string{"users", u.Uuid} if err = m.bolt.MkBucketPath(bkt); err != nil { return err @@ -73,10 +73,10 @@ func (m *model) SaveUser(u *User) error { func (m *model) isValidUser(uid string) bool { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return false } - defer m.bolt.CloseDB() + defer m.closeDB() ret := new(User) bkt := []string{"users", uid} ret.Uuid = uid @@ -105,10 +105,10 @@ func (m *model) GetUser(uid string) (*User, error) { return nil, errors.New("No user id given") } var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return nil, err } - defer m.bolt.CloseDB() + defer m.closeDB() ret := new(User) bkt := []string{"users", uid} ret.Uuid = uid @@ -128,10 +128,10 @@ func (m *model) GetUser(uid string) (*User, error) { func (m *model) SaveAllUsers(users []User) { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return } - defer m.bolt.CloseDB() + defer m.closeDB() for i := range users { m.SaveUser(&users[i]) } @@ -145,10 +145,10 @@ func (m *model) LoadUsers() error { func (m *model) GetAllUsers() []User { var err error var ret []User - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return ret } - defer m.bolt.CloseDB() + defer m.closeDB() uids := m.GetUserIdList() for _, uid := range uids { @@ -161,10 +161,10 @@ func (m *model) GetAllUsers() []User { func (m *model) GetUserByName(nm string) (*User, error) { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return nil, err } - defer m.bolt.CloseDB() + defer m.closeDB() usrids := m.GetUserIdList() for i := range usrids { bkt := []string{"users", usrids[i]} @@ -180,10 +180,10 @@ func (m *model) GetUserByName(nm string) (*User, error) { func (m *model) GetUserIdList() []string { var ret []string var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return ret } - defer m.bolt.CloseDB() + defer m.closeDB() bkt := []string{"users"} ret, _ = m.bolt.GetBucketList(bkt) return ret @@ -201,10 +201,10 @@ func (m *model) updateUserPassword(uid, password string) error { return err } - if err := m.bolt.OpenDB(); err != nil { + if err := m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() usrPath := []string{"users", uid} return m.bolt.SetValue(usrPath, "password", string(cryptPw)) @@ -213,10 +213,10 @@ func (m *model) updateUserPassword(uid, password string) error { // Is the uid and pw given valid? func (m *model) checkCredentials(uid, pw string) error { var err error - if err = m.bolt.OpenDB(); err != nil { + if err = m.openDB(); err != nil { return err } - defer m.bolt.CloseDB() + defer m.closeDB() var uPw string usrPath := []string{"users", uid} diff --git a/page_data.go b/page_data.go index 41a9e02..0347ae9 100644 --- a/page_data.go +++ b/page_data.go @@ -38,3 +38,11 @@ func (p *pageData) show(tmplName string) error { } return nil } + +func (p *pageData) showRss() error { + if err := outputTemplate("rss_feed.xml", p, p.session.w); err != nil { + fmt.Printf("%s\n", err) + return err + } + return nil +} diff --git a/templates/rss_feed.xml b/templates/rss_feed.xml new file mode 100644 index 0000000..e1d3708 --- /dev/null +++ b/templates/rss_feed.xml @@ -0,0 +1,20 @@ + + + + Ribbit Feed + http://ribbit.bullercodeworks.com/edit/{{.TemplateData.User.Uuid}} + Feed for {{.TemplateData.User.Username}} + en-us + {{.TemplateData.BuildDate}} + 40 + {{ range $i, $v := .TemplateData.Feeds }} + + {{$v.Name}} + {{$v.LastUpdate}} + {{$v.Source}};{{$v.Slug}};{{$v.LastUpdate}} + + + + {{ end }} + + diff --git a/templates/user_dashboard.html b/templates/user_dashboard.html index c1cb451..921e256 100644 --- a/templates/user_dashboard.html +++ b/templates/user_dashboard.html @@ -1 +1,3 @@ Edit Subscriptions + +RSS Feed