Initial Commit
This commit is contained in:
parent
f34d2d90d0
commit
5b76117cf8
31
.gitignore
vendored
31
.gitignore
vendored
@ -1,26 +1,15 @@
|
||||
# ---> Go
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
||||
# Binary
|
||||
ribbit
|
||||
# Database
|
||||
ribbit.db
|
||||
|
||||
# Example Feeds
|
||||
rssfeed.xml
|
||||
|
||||
# vim-restconsole
|
||||
console.rest
|
||||
|
16
endpoints_api.go
Normal file
16
endpoints_api.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func handleApiCall(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
vars := mux.Vars(req)
|
||||
_ = vars
|
||||
|
||||
fmt.Fprint(w, req.Header)
|
||||
}
|
91
endpoints_api_comics.go
Normal file
91
endpoints_api_comics.go
Normal file
@ -0,0 +1,91 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func handleApiComicsCall(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
vars := mux.Vars(req)
|
||||
cid, cidok := vars["cid"]
|
||||
fn, fnok := vars["function"]
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
if !cidok {
|
||||
handleApiListComics(w)
|
||||
return
|
||||
}
|
||||
if !fnok {
|
||||
handleApiShowComic(cid, w)
|
||||
return
|
||||
}
|
||||
switch fn {
|
||||
case "search":
|
||||
handleApiSearchComic(cid, w)
|
||||
return
|
||||
|
||||
default:
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func handleApiListComics(w http.ResponseWriter) {
|
||||
var res []byte
|
||||
var err error
|
||||
if res, err = json.Marshal(m.Comics); err != nil {
|
||||
http.Error(w, "I did a bad", 500)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(res))
|
||||
}
|
||||
|
||||
func handleApiSearchComic(nm string, w http.ResponseWriter) {
|
||||
nm = strings.ToLower(nm)
|
||||
var cs []Comic
|
||||
for _, c := range m.Comics {
|
||||
if strings.Contains(strings.ToLower(c.Name), nm) {
|
||||
cs = append(cs, c)
|
||||
}
|
||||
}
|
||||
var res []byte
|
||||
var err error
|
||||
if res, err = json.Marshal(cs); err != nil {
|
||||
http.Error(w, "I did a bad", 500)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(res))
|
||||
}
|
||||
|
||||
func handleApiShowComic(cid string, w http.ResponseWriter) {
|
||||
var err error
|
||||
var c *Comic
|
||||
pts := strings.Split(cid, ";")
|
||||
if len(pts) != 2 {
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
if c, err = m.GetComic(pts[0], pts[1]); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
|
||||
var res []byte
|
||||
if res, err = json.Marshal(c); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(res))
|
||||
return
|
||||
}
|
122
endpoints_api_users.go
Normal file
122
endpoints_api_users.go
Normal file
@ -0,0 +1,122 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func handleApiUsersCall(w http.ResponseWriter, req *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
vars := mux.Vars(req)
|
||||
uid, uidok := vars["uid"]
|
||||
fn, fnok := vars["function"]
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
if !uidok {
|
||||
handleApiListUsers(w)
|
||||
return
|
||||
}
|
||||
switch fn {
|
||||
default:
|
||||
handleApiShowUser(uid, w)
|
||||
}
|
||||
|
||||
case "POST", "PUT":
|
||||
if !uidok {
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
if !fnok {
|
||||
// Creating a new user
|
||||
var u *User
|
||||
var err error
|
||||
if u, err = m.GetUserByName(uid); err == nil {
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
u = NewUser(uid)
|
||||
m.SaveUser(u)
|
||||
m.LoadUsers()
|
||||
handleApiShowUser(u.Uuid, w)
|
||||
return
|
||||
}
|
||||
switch fn {
|
||||
case "slugs":
|
||||
slug, slugok := vars["slug"]
|
||||
if !slugok {
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
handleApiSubUser(uid, slug, w)
|
||||
|
||||
default:
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
|
||||
default:
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func handleApiListUsers(w http.ResponseWriter) {
|
||||
var res []byte
|
||||
var err error
|
||||
if res, err = json.Marshal(m.Users); err != nil {
|
||||
http.Error(w, "I did a bad", 500)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(res))
|
||||
}
|
||||
|
||||
func handleApiShowUser(uid string, w http.ResponseWriter) {
|
||||
var err error
|
||||
var u *User
|
||||
if u, err = m.GetUser(uid); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
|
||||
var res []byte
|
||||
if res, err = json.Marshal(u); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, string(res))
|
||||
return
|
||||
}
|
||||
|
||||
func handleApiSubUser(uid string, slug string, w http.ResponseWriter) {
|
||||
fmt.Println("Sub User", uid, slug)
|
||||
var u *User
|
||||
var err error
|
||||
if u, err = m.GetUser(uid); err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
pts := strings.Split(slug, ";")
|
||||
if len(pts) != 2 {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
_, err = m.GetComic(pts[0], pts[1])
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
u.SubSlugs = append(u.SubSlugs, slug)
|
||||
err = m.SaveUser(u)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
m.LoadUsers()
|
||||
handleApiShowUser(u.Uuid, w)
|
||||
}
|
39
endpoints_public.go
Normal file
39
endpoints_public.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func handleRequest(w http.ResponseWriter, req *http.Request) {
|
||||
vars := mux.Vars(req)
|
||||
|
||||
var fOk, uidOk bool
|
||||
var f, uid string
|
||||
f, fOk = vars["function"]
|
||||
uid, uidOk = vars["uid"]
|
||||
if !fOk || !uidOk {
|
||||
// Not sure what you want me to do here, Hoss.
|
||||
http.Error(w, "You did a bad", 400)
|
||||
return
|
||||
}
|
||||
switch f {
|
||||
case "rss":
|
||||
handleRssFeed(uid, w)
|
||||
default:
|
||||
http.Error(w, "You did a bad", 400)
|
||||
}
|
||||
}
|
||||
|
||||
func handleRssFeed(uid string, w http.ResponseWriter) {
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
|
||||
v, err := buildRssFeed(uid)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 400)
|
||||
return
|
||||
}
|
||||
fmt.Fprint(w, v)
|
||||
}
|
68
helper_dilbert.go
Normal file
68
helper_dilbert.go
Normal file
@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func downloadDilbertList() []Comic {
|
||||
var ret []Comic
|
||||
ret = append(ret, *NewComic("dilbert", "Dilbert", "Scott Adams", "dilbert"))
|
||||
return ret
|
||||
}
|
||||
|
||||
func getDilbertRssItem(slug string) (string, error) {
|
||||
desc, err := getDilbertFeedDesc(time.Now())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
comic, err := m.GetComic(SRC_DILBERT, slug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
desc = "<![CDATA[" + desc + "]]>"
|
||||
ret := " <item>\n"
|
||||
ret += " <title>" + comic.Name + "</title>\n"
|
||||
ret += " <pubDate>" + comic.LastUpdate.Format(time.RFC1123Z) + "</pubDate>\n"
|
||||
ret += " <guid>dilbert;" + slug + ";" + comic.LastUpdate.Format(time.RFC1123Z) + "</guid>\n"
|
||||
ret += " <link>" + getDilbertComicUrl(time.Now()) + "</link>\n"
|
||||
ret += " <description>" + desc + "</description>\n"
|
||||
ret += " </item>\n"
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getDilbertComicUrl(date time.Time) string {
|
||||
return fmt.Sprintf(
|
||||
"http://dilbert.com/strip/%4d-%02d-%02d",
|
||||
date.Year(),
|
||||
date.Month(),
|
||||
date.Day(),
|
||||
)
|
||||
}
|
||||
|
||||
func getDilbertFeedDesc(date time.Time) (string, error) {
|
||||
res, err := http.Get(getDilbertComicUrl(date))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return "", errors.New(fmt.Sprintf("Status code error: %d %s", res.StatusCode, res.Status))
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Find the Picture
|
||||
src, exists := doc.Find("img.img-comic").Attr("src")
|
||||
if !exists {
|
||||
return "", errors.New("Couldn't find image source")
|
||||
}
|
||||
return "<img src=\"" + src + "\" />", nil
|
||||
}
|
98
helper_gocomics.go
Normal file
98
helper_gocomics.go
Normal file
@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func downloadGoComicsList() []Comic {
|
||||
var ret []Comic
|
||||
lstUrl := "http://www.gocomics.com/comics/a-to-z"
|
||||
res, err := http.Get(lstUrl)
|
||||
if err != nil {
|
||||
return ret
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return ret
|
||||
}
|
||||
doc.Find("a.amu-media-item-link").Each(func(i int, s *goquery.Selection) {
|
||||
// For each item found, get the band and title
|
||||
slug, exists := s.Attr("href")
|
||||
if exists {
|
||||
pts := strings.Split(slug, "/")
|
||||
if len(pts) > 2 {
|
||||
slug = pts[1]
|
||||
}
|
||||
name := s.Find("h4.media-heading").Text()
|
||||
author := s.Find("h6.media-subheading").Text()
|
||||
author = strings.TrimPrefix(author, "By ")
|
||||
ret = append(ret, *NewComic(slug, name, author, "gocomics"))
|
||||
}
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
func getGoComicsRssItem(slug string) (string, error) {
|
||||
desc, err := getGoComicsFeedDesc(slug, time.Now())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
comic, err := m.GetComic(SRC_GOCOMICS, slug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
desc = "<![CDATA[" + desc + "]]>"
|
||||
ret := " <item>\n"
|
||||
ret += " <title>" + comic.Name + "</title>\n"
|
||||
ret += " <pubDate>" + comic.LastUpdate.Format(time.RFC1123Z) + "</pubDate>\n"
|
||||
ret += " <guid>gocomics;" + slug + ";" + comic.LastUpdate.Format(time.RFC1123Z) + "</guid>\n"
|
||||
ret += " <link>" + getGoComicsComicUrl(slug, time.Now()) + "</link>\n"
|
||||
ret += " <description>" + desc + "</description>\n"
|
||||
ret += " </item>\n"
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getGoComicsComicUrl(slug string, date time.Time) string {
|
||||
return fmt.Sprintf(
|
||||
"http://www.gocomics.com/%s/%04d/%02d/%02d",
|
||||
slug,
|
||||
date.Year(),
|
||||
date.Month(),
|
||||
date.Day(),
|
||||
)
|
||||
}
|
||||
|
||||
func getGoComicsFeedDesc(slug string, date time.Time) (string, error) {
|
||||
res, err := http.Get(getGoComicsComicUrl(slug, date))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return "", errors.New(fmt.Sprintf("Status code error: %d %s", res.StatusCode, res.Status))
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Find the Picture
|
||||
src, exists := doc.Find("picture.item-comic-image>img").Attr("src")
|
||||
if !exists {
|
||||
return "", errors.New("Couldn't find image source")
|
||||
}
|
||||
return "<img src=\"" + src + "\" />", nil
|
||||
}
|
74
helper_xkcd.go
Normal file
74
helper_xkcd.go
Normal file
@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func downloadXKCDList() []Comic {
|
||||
var ret []Comic
|
||||
ret = append(ret, *NewComic("xkcd", "XKCD", "Randall Munroe", "xkcd"))
|
||||
return ret
|
||||
}
|
||||
|
||||
func getXKCDRssItem(slug string) (string, error) {
|
||||
desc, err := getXKCDFeedDesc(time.Now())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
comic, err := m.GetComic(SRC_XKCD, slug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
desc = "<![CDATA[" + desc + "]]>"
|
||||
ret := " <item>\n"
|
||||
ret += " <title>" + comic.Name + "</title>\n"
|
||||
ret += " <pubDate>" + comic.LastUpdate.Format(time.RFC1123Z) + "</pubDate>\n"
|
||||
ret += " <guid>xkcd;" + slug + ";" + comic.LastUpdate.Format(time.RFC1123Z) + "</guid>\n"
|
||||
ret += " <link>" + getXKCDComicUrl(time.Now()) + "</link>\n"
|
||||
ret += " <description>" + desc + "</description>\n"
|
||||
ret += " </item>\n"
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func getXKCDComicUrl(date time.Time) string {
|
||||
// TODO: Actually make this work correctly
|
||||
// Get the previous comic number
|
||||
// and find the next one
|
||||
return fmt.Sprintf(
|
||||
"http://xkcd.com/",
|
||||
)
|
||||
}
|
||||
|
||||
func getXKCDFeedDesc(date time.Time) (string, error) {
|
||||
res, err := http.Get(getXKCDComicUrl(date))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
return "", errors.New(fmt.Sprintf("Status code error: %d %s", res.StatusCode, res.Status))
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// Find the Picture
|
||||
sel := doc.Find("div#comic>img")
|
||||
src, exists := sel.Attr("src")
|
||||
if !exists {
|
||||
return "", errors.New("Couldn't find image source")
|
||||
}
|
||||
src = "https:" + src
|
||||
title, exists := sel.Attr("title")
|
||||
if !exists {
|
||||
title = ""
|
||||
}
|
||||
return "<img src=\"" + src + "\" /><p>" + title + "</p>", nil
|
||||
}
|
102
helpers.go
Normal file
102
helpers.go
Normal file
@ -0,0 +1,102 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SRC_GOCOMICS = "gocomics"
|
||||
SRC_DILBERT = "dilbert"
|
||||
SRC_XKCD = "xkcd"
|
||||
)
|
||||
|
||||
func downloadComicsList() []Comic {
|
||||
var ret []Comic
|
||||
ret = append(ret, downloadGoComicsList()...)
|
||||
ret = append(ret, downloadDilbertList()...)
|
||||
ret = append(ret, downloadXKCDList()...)
|
||||
return ret
|
||||
}
|
||||
|
||||
func getRssItem(source, slug string) (string, error) {
|
||||
switch source {
|
||||
case SRC_GOCOMICS:
|
||||
return getGoComicsRssItem(slug)
|
||||
case SRC_DILBERT:
|
||||
return getDilbertRssItem(slug)
|
||||
case SRC_XKCD:
|
||||
return getXKCDRssItem(slug)
|
||||
}
|
||||
return "", errors.New("Invalid source")
|
||||
}
|
||||
|
||||
func getComicUrl(source, slug string, dt time.Time) (string, error) {
|
||||
switch source {
|
||||
case SRC_GOCOMICS:
|
||||
return getGoComicsComicUrl(slug, dt), nil
|
||||
case SRC_DILBERT:
|
||||
return getDilbertComicUrl(dt), nil
|
||||
case SRC_XKCD:
|
||||
return getXKCDComicUrl(dt), nil
|
||||
}
|
||||
return "", errors.New("Invalid source")
|
||||
}
|
||||
|
||||
func getComicDesc(source, slug string, dt time.Time) (string, error) {
|
||||
switch source {
|
||||
case SRC_GOCOMICS:
|
||||
return getGoComicsFeedDesc(slug, dt)
|
||||
case SRC_DILBERT:
|
||||
return getDilbertFeedDesc(dt)
|
||||
case SRC_XKCD:
|
||||
return getXKCDFeedDesc(dt)
|
||||
}
|
||||
return "", errors.New("Unknown Comic Source")
|
||||
}
|
||||
|
||||
func buildRssFeed(uid string) (string, error) {
|
||||
var usr *User
|
||||
var err error
|
||||
if usr, err = m.GetUser(uid); err != nil {
|
||||
return "", err
|
||||
}
|
||||
output := []string{
|
||||
"<?xml version=\"1.0\"?>",
|
||||
"<rss version=\"2.0\">",
|
||||
" <channel>",
|
||||
" <title>BCW Comic Feed</title>",
|
||||
" <link>http://comics.bullercodeworks.com/edit/" + uid + "</link>",
|
||||
" <description>Comic feed for " + usr.Username + "</description>",
|
||||
" <language>en-us</language>",
|
||||
" <lastBuildDate>" + time.Now().Format(time.RFC1123) + "</lastBuildDate>",
|
||||
" <ttl>40</ttl>",
|
||||
}
|
||||
|
||||
//date := time.Now()
|
||||
for _, slug := range usr.SubSlugs {
|
||||
pts := strings.Split(slug, ";")
|
||||
if len(pts) != 2 {
|
||||
continue
|
||||
}
|
||||
if comic, err := m.GetComic(pts[0], pts[1]); err == nil {
|
||||
output = append(output, comic.GetRssItem())
|
||||
}
|
||||
}
|
||||
|
||||
output = append(output, []string{
|
||||
" </channel>",
|
||||
"</rss>",
|
||||
}...)
|
||||
return strings.Join(output, "\n"), nil
|
||||
}
|
||||
|
||||
func addStringIfUnique(st string, sl []string) []string {
|
||||
for i := range sl {
|
||||
if sl[i] == st {
|
||||
return sl
|
||||
}
|
||||
}
|
||||
return append(sl, st)
|
||||
}
|
169
main.go
Normal file
169
main.go
Normal file
@ -0,0 +1,169 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/justinas/alice"
|
||||
)
|
||||
|
||||
const AppName = "ribbit"
|
||||
const DbName = AppName + ".db"
|
||||
|
||||
var sessionStore *sessions.CookieStore
|
||||
var r *mux.Router
|
||||
var m *model
|
||||
|
||||
var scheduler *JobScheduler
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
if m, err = NewModel(); err != nil {
|
||||
errorExit("Unable to initialize Model: " + err.Error())
|
||||
}
|
||||
|
||||
if len(os.Args) > 2 {
|
||||
key, val := os.Args[1], os.Args[2]
|
||||
switch key {
|
||||
case "--add-user-sub":
|
||||
if len(os.Args) < 3 {
|
||||
errorExit("Usage: --add-user-sub <username> <comic-slug>\nFor a list of slugs, use --list-comics")
|
||||
}
|
||||
slug := os.Args[3]
|
||||
var u *User
|
||||
if u, err = m.GetUserByName(val); err != nil {
|
||||
errorExit("Couldn't find a user with the username " + val)
|
||||
}
|
||||
pts := strings.Split(slug, ";")
|
||||
if len(pts) != 2 {
|
||||
errorExit("Invalid slug given: " + slug)
|
||||
}
|
||||
_, err := m.GetComic(pts[0], pts[1])
|
||||
if err != nil {
|
||||
errorExit("Couldn't find comic with slug: " + slug)
|
||||
}
|
||||
fmt.Println(u.SubSlugs)
|
||||
fmt.Println(slug)
|
||||
u.SubSlugs = append(u.SubSlugs, slug)
|
||||
fmt.Println(u.SubSlugs)
|
||||
m.SaveUser(u)
|
||||
done()
|
||||
default:
|
||||
errorExit("Unknown argument")
|
||||
}
|
||||
} else if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "--test":
|
||||
//d, _ := getXKCDFeedDesc(time.Now())
|
||||
fmt.Println(buildRssFeed("09af5fda-43dc-416e-93ad-cc050e0c098a"))
|
||||
done()
|
||||
case "--list-comics":
|
||||
comics := m.GetAllComics()
|
||||
for _, c := range comics {
|
||||
fmt.Printf("[ %s;%s ] %s\n", c.Source, c.Slug, c.Name)
|
||||
}
|
||||
done()
|
||||
case "--update-feeds":
|
||||
fmt.Println("Updating User Feeds...")
|
||||
m.UpdateAllUserFeeds()
|
||||
fmt.Println("Done.")
|
||||
done()
|
||||
case "--update-comics":
|
||||
fmt.Println("Updating the Comics List...")
|
||||
comics := downloadComicsList()
|
||||
for _, c := range comics {
|
||||
fmt.Printf("Updating [ %s - %s, %s ]\n", c.Slug, c.Name, c.Artist)
|
||||
m.SaveComic(&c)
|
||||
}
|
||||
m.saveChanges()
|
||||
fmt.Println("Done.")
|
||||
|
||||
default:
|
||||
errorExit("Unknown argument")
|
||||
}
|
||||
}
|
||||
|
||||
r = mux.NewRouter()
|
||||
r.StrictSlash(true)
|
||||
//r.PathPrefix("/assets/").Handler(http.FileServer())
|
||||
|
||||
pub := r.PathPrefix("/").Subrouter()
|
||||
pub.HandleFunc("/", handleRequest)
|
||||
pub.HandleFunc("/api", handleApiCall)
|
||||
pub.HandleFunc("/api/users", handleApiUsersCall)
|
||||
pub.HandleFunc("/api/users/{uid}", handleApiUsersCall)
|
||||
pub.HandleFunc("/api/users/{uid}/{function}", handleApiUsersCall)
|
||||
pub.HandleFunc("/api/users/{uid}/{function}/{slug}", handleApiUsersCall)
|
||||
pub.HandleFunc("/api/comics", handleApiComicsCall)
|
||||
pub.HandleFunc("/api/comics/{cid}", handleApiComicsCall)
|
||||
pub.HandleFunc("/api/comics/{cid}/{function}", handleApiComicsCall)
|
||||
pub.HandleFunc("/{function}", handleRequest)
|
||||
pub.HandleFunc("/{function}/{uid}", handleRequest)
|
||||
pub.HandleFunc("/{function}/{uid}/{subfunc}", handleRequest)
|
||||
pub.HandleFunc("/{function}/{uid}/{subfunc}/{slug}", handleRequest)
|
||||
|
||||
http.Handle("/", r)
|
||||
chain := alice.New(loggingHandler).Then(r)
|
||||
|
||||
// Save changes to the DB every 5 minutes
|
||||
go func() {
|
||||
for {
|
||||
if m.Site.LastSave.IsZero() || (time.Now().Day() != m.Site.LastSave.Day() && time.Now().Hour() == 2) {
|
||||
fmt.Println("Updating GoComics List...")
|
||||
comics := downloadComicsList()
|
||||
for _, c := range comics {
|
||||
fmt.Printf("Updating [ %s - %s, %s ]\n", c.Slug, c.Name, c.Artist)
|
||||
m.SaveComic(&c)
|
||||
}
|
||||
fmt.Println("Updating User Feeds...")
|
||||
m.UpdateAllUserFeeds()
|
||||
m.saveChanges()
|
||||
fmt.Println("Done.")
|
||||
}
|
||||
time.Sleep(time.Minute)
|
||||
}
|
||||
}()
|
||||
|
||||
// Set up a channel to intercept Ctrl+C for graceful shutdowns
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
go func() {
|
||||
<-c
|
||||
// Save the changes when the app quits
|
||||
fmt.Println("\nFinishing up...")
|
||||
m.saveChanges()
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
fmt.Printf("Listening on port %d\n", m.Site.Port)
|
||||
log.Fatal(http.ListenAndServe("127.0.0.1:"+strconv.Itoa(m.Site.Port), chain))
|
||||
}
|
||||
|
||||
func loggingHandler(h http.Handler) http.Handler {
|
||||
return handlers.LoggingHandler(os.Stdout, h)
|
||||
}
|
||||
|
||||
func done() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func errorExit(msg string) {
|
||||
fmt.Println(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func assertError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
100
model.go
Normal file
100
model.go
Normal file
@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/br0xen/boltease"
|
||||
)
|
||||
|
||||
type model struct {
|
||||
bolt *boltease.DB
|
||||
dbFileName string
|
||||
|
||||
Users []User
|
||||
Comics []Comic
|
||||
|
||||
Site *SiteData
|
||||
}
|
||||
|
||||
func NewModel() (*model, error) {
|
||||
var err error
|
||||
m := new(model)
|
||||
m.dbFileName = DbName
|
||||
m.bolt, err = boltease.Create(m.dbFileName, 0600, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = m.initDB(); err != nil {
|
||||
return nil, errors.New("Unable to initialzie DB: " + err.Error())
|
||||
}
|
||||
m.LoadSiteData()
|
||||
if err = m.LoadUsers(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = m.LoadComics(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *model) initDB() error {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
|
||||
if err = m.bolt.MkBucketPath([]string{"site"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.MkBucketPath([]string{"users"}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.MkBucketPath([]string{"comics"}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) saveChanges() {
|
||||
m.Site.LastSave = time.Now()
|
||||
m.SaveSite()
|
||||
//m.SaveAllComics(m.Comics)
|
||||
m.SaveAllUsers(m.Users)
|
||||
}
|
||||
|
||||
func (m *model) UpdateAllUserFeeds() {
|
||||
var allSubs []string
|
||||
for _, usr := range m.Users {
|
||||
// Pull all user subs
|
||||
for _, sub := range usr.SubSlugs {
|
||||
allSubs = addStringIfUnique(sub, allSubs)
|
||||
}
|
||||
}
|
||||
// So we have allSubs which contains all subscribed comics for all users
|
||||
for _, sub := range allSubs {
|
||||
fmt.Println("Updating Comic: " + sub)
|
||||
pts := strings.Split(sub, ";")
|
||||
if len(pts) != 2 {
|
||||
continue
|
||||
}
|
||||
c, err := m.GetComic(pts[0], pts[1])
|
||||
if err != nil {
|
||||
fmt.Println(sub, ":", err)
|
||||
continue
|
||||
}
|
||||
if err = c.Update(); err != nil {
|
||||
fmt.Println(sub, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
if err = m.SaveComic(c); err != nil {
|
||||
fmt.Println(sub, ":", err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
194
model_comics.go
Normal file
194
model_comics.go
Normal file
@ -0,0 +1,194 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Comic struct {
|
||||
Name string
|
||||
Artist string
|
||||
Slug string
|
||||
Source string
|
||||
Desc string
|
||||
LastUpdate time.Time
|
||||
}
|
||||
|
||||
func NewComic(s, n, a, source string) *Comic {
|
||||
return &Comic{
|
||||
Name: n,
|
||||
Artist: a,
|
||||
Slug: s,
|
||||
Source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Comic) GetBucket() []string {
|
||||
return []string{"comics", c.Source, c.Slug}
|
||||
}
|
||||
|
||||
func (c *Comic) Update() error {
|
||||
dt := time.Now()
|
||||
desc, err := getComicDesc(c.Source, c.Slug, dt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if desc == c.Desc {
|
||||
return errors.New("Comic didn't change")
|
||||
}
|
||||
c.Desc = desc
|
||||
c.LastUpdate = dt
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Comic) GetUrl(dt time.Time) string {
|
||||
var v string
|
||||
var e error
|
||||
if v, e = getComicUrl(c.Source, c.Slug, dt); e != nil {
|
||||
return ""
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *Comic) GetDesc(dt time.Time) string {
|
||||
var v string
|
||||
var e error
|
||||
if v, e = getComicDesc(c.Source, c.Slug, dt); e != nil {
|
||||
return ""
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *Comic) GetRssItem() string {
|
||||
var v string
|
||||
var e error
|
||||
if v, e = getRssItem(c.Source, c.Slug); e != nil {
|
||||
return ""
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// DB Function to save a comic
|
||||
func (m *model) SaveComic(c *Comic) error {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
bkt := c.GetBucket()
|
||||
if err = m.bolt.MkBucketPath(bkt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "name", c.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "artist", c.Artist); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "desc", c.Desc); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetTimestamp(bkt, "lastupdate", c.LastUpdate); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DB Function to get a comic
|
||||
func (m *model) GetComic(source, slug string) (*Comic, error) {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
ret := new(Comic)
|
||||
ret.Source = source
|
||||
ret.Slug = slug
|
||||
bkt := ret.GetBucket()
|
||||
if ret.Name, err = m.bolt.GetValue(bkt, "name"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.Artist, err = m.bolt.GetValue(bkt, "artist"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.Desc, err = m.bolt.GetValue(bkt, "desc"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.LastUpdate, err = m.bolt.GetTimestamp(bkt, "lastupdate"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Load all comics into the model
|
||||
func (m *model) LoadComics() error {
|
||||
m.Comics = m.GetAllComics()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save all comics to the DB
|
||||
func (m *model) SaveAllComics(comics []Comic) {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
for i := range comics {
|
||||
m.SaveComic(&comics[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Get all comics from the db
|
||||
func (m *model) GetAllComics() []Comic {
|
||||
var ret []Comic
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return ret
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
var srcs []string
|
||||
bkt := []string{"comics"}
|
||||
if srcs, err = m.bolt.GetBucketList(bkt); err != nil {
|
||||
return ret
|
||||
}
|
||||
for _, src := range srcs {
|
||||
srcBkt := append(bkt, src)
|
||||
var slugs []string
|
||||
if slugs, err = m.bolt.GetBucketList(srcBkt); err != nil {
|
||||
return ret
|
||||
}
|
||||
for _, slg := range slugs {
|
||||
c, err := m.GetComic(src, slg)
|
||||
if err == nil {
|
||||
ret = append(ret, *c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Delete a comic from the DB
|
||||
func (m *model) DeleteComic(slug string) error {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
|
||||
return m.bolt.DeleteBucket([]string{"comics"}, slug)
|
||||
}
|
||||
|
||||
func (m *model) RemoveMissingComics(comics []Comic) {
|
||||
for _, c := range m.Comics {
|
||||
var fnd bool
|
||||
for _, nc := range comics {
|
||||
if nc.Slug == c.Slug {
|
||||
fnd = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !fnd {
|
||||
m.DeleteComic(c.Slug)
|
||||
}
|
||||
}
|
||||
}
|
86
model_site.go
Normal file
86
model_site.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
type SiteData struct {
|
||||
Title string
|
||||
Port int
|
||||
SessionName string
|
||||
ServerDir string
|
||||
SessionSecret string
|
||||
|
||||
LastSave time.Time
|
||||
}
|
||||
|
||||
func NewSiteData() *SiteData {
|
||||
ret := new(SiteData)
|
||||
ret.Title = "BCW Comic Feed"
|
||||
ret.Port = 8080
|
||||
ret.SessionName = "bcw-comic-feed"
|
||||
ret.ServerDir = "./"
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *model) LoadSiteData() {
|
||||
m.Site = m.GetSite()
|
||||
}
|
||||
|
||||
func (m *model) SaveSite() error {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
bkt := []string{"site"}
|
||||
if err = m.bolt.SetValue(bkt, "title", m.Site.Title); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetInt(bkt, "port", m.Site.Port); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "session-name", m.Site.SessionName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "session-secret", m.Site.SessionSecret); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "server-dir", m.Site.ServerDir); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetTimestamp(bkt, "last-save", m.Site.LastSave); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) GetSite() *SiteData {
|
||||
s := NewSiteData()
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return s
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
bkt := []string{"site"}
|
||||
var wrkStr string
|
||||
var wrkInt int
|
||||
var wrkTm time.Time
|
||||
if wrkStr, err = m.bolt.GetValue(bkt, "title"); err == nil {
|
||||
s.Title = wrkStr
|
||||
}
|
||||
if wrkInt, err = m.bolt.GetInt(bkt, "port"); err == nil {
|
||||
s.Port = wrkInt
|
||||
}
|
||||
if wrkStr, err = m.bolt.GetValue(bkt, "session-name"); err == nil {
|
||||
s.SessionName = wrkStr
|
||||
}
|
||||
if wrkStr, err = m.bolt.GetValue(bkt, "session-secret"); err == nil {
|
||||
s.SessionSecret = wrkStr
|
||||
}
|
||||
if wrkStr, err = m.bolt.GetValue(bkt, "server-dir"); err == nil {
|
||||
s.ServerDir = wrkStr
|
||||
}
|
||||
if wrkTm, err = m.bolt.GetTimestamp(bkt, "last-save"); err == nil {
|
||||
s.LastSave = wrkTm
|
||||
}
|
||||
return s
|
||||
}
|
143
model_user.go
Normal file
143
model_user.go
Normal file
@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Username string `json:username`
|
||||
Uuid string `json:uuid`
|
||||
SubSlugs []string `json:subs`
|
||||
}
|
||||
|
||||
func NewUser(un string) *User {
|
||||
u := new(User)
|
||||
u.Username = un
|
||||
u.Uuid = uuid.New()
|
||||
return u
|
||||
}
|
||||
|
||||
func (u *User) UpdateFeed() error {
|
||||
for _, slug := range u.SubSlugs {
|
||||
pts := strings.Split(slug, ";")
|
||||
if len(pts) != 2 {
|
||||
continue
|
||||
}
|
||||
if comic, err := m.GetComic(pts[0], pts[1]); err == nil {
|
||||
comic.Update()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) SaveUser(u *User) error {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
bkt := []string{"users", u.Uuid}
|
||||
if err = m.bolt.MkBucketPath(bkt); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = m.bolt.SetValue(bkt, "username", u.Username); err != nil {
|
||||
return err
|
||||
}
|
||||
var newSubs []string
|
||||
for _, v := range u.SubSlugs {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
newSubs = append(newSubs, v)
|
||||
}
|
||||
}
|
||||
slugs := strings.Join(newSubs, ",")
|
||||
if err = m.bolt.SetValue(bkt, "subs", slugs); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) GetUser(uid string) (*User, error) {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
ret := new(User)
|
||||
bkt := []string{"users", uid}
|
||||
ret.Uuid = uid
|
||||
if ret.Username, err = m.bolt.GetValue(bkt, "username"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var subs string
|
||||
if subs, err = m.bolt.GetValue(bkt, "subs"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.SubSlugs = strings.Split(subs, ",")
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (m *model) SaveAllUsers(users []User) {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
for i := range users {
|
||||
m.SaveUser(&users[i])
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) LoadUsers() error {
|
||||
m.Users = m.GetAllUsers()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *model) GetAllUsers() []User {
|
||||
var err error
|
||||
var ret []User
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return ret
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
|
||||
uids := m.GetUserIdList()
|
||||
for _, uid := range uids {
|
||||
if u, e := m.GetUser(uid); e == nil {
|
||||
ret = append(ret, *u)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *model) GetUserByName(nm string) (*User, error) {
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
usrids := m.GetUserIdList()
|
||||
for i := range usrids {
|
||||
bkt := []string{"users", usrids[i]}
|
||||
var tstuname string
|
||||
if tstuname, _ = m.bolt.GetValue(bkt, "username"); tstuname == nm {
|
||||
// Found it
|
||||
return m.GetUser(usrids[i])
|
||||
}
|
||||
}
|
||||
return nil, errors.New("No user with username " + nm + " found")
|
||||
}
|
||||
|
||||
func (m *model) GetUserIdList() []string {
|
||||
var ret []string
|
||||
var err error
|
||||
if err = m.bolt.OpenDB(); err != nil {
|
||||
return ret
|
||||
}
|
||||
defer m.bolt.CloseDB()
|
||||
bkt := []string{"users"}
|
||||
ret, _ = m.bolt.GetBucketList(bkt)
|
||||
return ret
|
||||
}
|
54
page_session.go
Normal file
54
page_session.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
)
|
||||
|
||||
// This is basically a convenience struct for
|
||||
// easier session management (hopefully ;)
|
||||
type pageSession struct {
|
||||
session *sessions.Session
|
||||
req *http.Request
|
||||
w http.ResponseWriter
|
||||
}
|
||||
|
||||
func (p *pageSession) getStringValue(key string) (string, error) {
|
||||
val := p.session.Values[key]
|
||||
var retVal string
|
||||
var ok bool
|
||||
if retVal, ok = val.(string); !ok {
|
||||
return "", fmt.Errorf("Unable to create string from %s", key)
|
||||
}
|
||||
return retVal, nil
|
||||
}
|
||||
|
||||
func (p *pageSession) setStringValue(key, val string) {
|
||||
p.session.Values[key] = val
|
||||
p.session.Save(p.req, p.w)
|
||||
}
|
||||
|
||||
func (p *pageSession) setFlashMessage(msg, status string) {
|
||||
p.setStringValue("flash_message", msg)
|
||||
p.setStringValue("flash_status", status)
|
||||
}
|
||||
|
||||
func (p *pageSession) getFlashMessage() (string, string) {
|
||||
var err error
|
||||
var msg, status string
|
||||
if msg, err = p.getStringValue("flash_message"); err != nil {
|
||||
return "", "hidden"
|
||||
}
|
||||
if status, err = p.getStringValue("flash_status"); err != nil {
|
||||
return "", "hidden"
|
||||
}
|
||||
p.setFlashMessage("", "hidden")
|
||||
return msg, status
|
||||
}
|
||||
|
||||
func (p *pageSession) expireSession() {
|
||||
p.session.Options.MaxAge = -1
|
||||
p.session.Save(p.req, p.w)
|
||||
}
|
51
scheduler.go
Normal file
51
scheduler.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
RESULT_OK = 0
|
||||
RESULT_ERR = 1
|
||||
)
|
||||
|
||||
type JobScheduler struct {
|
||||
Jobs []Job
|
||||
}
|
||||
|
||||
func NewJobScheduler() *JobScheduler {
|
||||
return new(JobScheduler)
|
||||
}
|
||||
|
||||
func (s *JobScheduler) AddJob(j *Job) {
|
||||
s.Jobs = append(s.Jobs, *j)
|
||||
}
|
||||
|
||||
func (s *JobScheduler) Run() {
|
||||
for _, j := range s.Jobs {
|
||||
if j.ShouldRunNow() {
|
||||
j.Run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
freq time.Duration
|
||||
lastRun time.Time
|
||||
lastResult int
|
||||
action func() int
|
||||
}
|
||||
|
||||
func NewJob(a func() int, f time.Duration) *Job {
|
||||
j := new(Job)
|
||||
j.action = a
|
||||
j.freq = f
|
||||
return j
|
||||
}
|
||||
|
||||
func (j *Job) ShouldRunNow() bool {
|
||||
return time.Now().After(j.lastRun.Add(j.freq))
|
||||
}
|
||||
|
||||
func (j *Job) Run() {
|
||||
j.lastResult = j.action()
|
||||
j.lastRun = time.Now()
|
||||
}
|
Loading…
Reference in New Issue
Block a user