349 lines
7.0 KiB
Go
349 lines
7.0 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/gorilla/mux"
|
|
_ "github.com/mattn/go-sqlite3"
|
|
librss "github.com/mmcdole/gofeed"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type FeedManager struct {
|
|
DBPath string
|
|
}
|
|
|
|
func (f *FeedManager) createDB() (bool, error) {
|
|
db, err := sql.Open("sqlite3", "./database.db")
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer db.Close()
|
|
|
|
createStmts := [2]string{`
|
|
create table feeds (
|
|
Id INTEGER PRIMARY KEY,
|
|
Title TEXT,
|
|
Description TEXT,
|
|
Link TEXT,
|
|
FeedLink TEXT,
|
|
Updated TEXT,
|
|
UpdatedParsed TEXT,
|
|
Published TEXT,
|
|
PublishedParsed TEXT,
|
|
Author TEXT,
|
|
Language TEXT,
|
|
Image TEXT,
|
|
Copyright TEXT,
|
|
Generator TEXT,
|
|
Categories TEXT,
|
|
DublinCoreExt TEXT,
|
|
ITunesExt TEXT,
|
|
Extensions TEXT,
|
|
Custom TEXT,
|
|
Items TEXT,
|
|
FeedType TEXT,
|
|
FeedVersion TEXT
|
|
);`,
|
|
`
|
|
create table items (
|
|
GUID TEXT PRIMARY KEY,
|
|
Feed INTEGER NOT NULL,
|
|
Title TEXT,
|
|
Description TEXT,
|
|
Content TEXT,
|
|
Link TEXT,
|
|
Updated TEXT,
|
|
UpdatedParsed TEXT,
|
|
Published TEXT,
|
|
PublishedParsed TEXT,
|
|
Author TEXT,
|
|
Image TEXT,
|
|
Categories TEXT,
|
|
Enclosures TEXT,
|
|
DublinCoreExt TEXT,
|
|
ITunesExt TEXT,
|
|
Extensions TEXT,
|
|
Custom TEXT,
|
|
|
|
FOREIGN KEY(Feed) REFERENCES feeds(Id)
|
|
)
|
|
`}
|
|
|
|
for _, stmt := range createStmts {
|
|
_, err = db.Exec(stmt)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (f *FeedManager) ExecDB(query string, parameters ...interface{}) (*sql.Result, error) {
|
|
db, err := sql.Open("sqlite3", f.DBPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer db.Close()
|
|
|
|
stmt, err := db.Prepare(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result, err := stmt.Exec(parameters...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &result, nil
|
|
}
|
|
|
|
func (f *FeedManager) QueryDB(query string, parameters ...interface{}) (*sql.Rows, error) {
|
|
db, err := sql.Open("sqlite3", f.DBPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer db.Close()
|
|
|
|
stmt, err := db.Prepare(query)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result, err := stmt.Query(parameters...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (f *FeedManager) AddFeed(url string) (*sql.Result, error) {
|
|
feed, err := f.DownloadFeed(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
query := `insert into feeds (title, description, link, feedLink, updated, updatedParsed,
|
|
published, publishedParsed) values(?, ?, ?, ?, ?, ?, ?, ?);`
|
|
return f.ExecDB(query, feed.Title, feed.Description, feed.Link, feed.FeedLink, feed.Updated, feed.UpdatedParsed,
|
|
feed.Published, feed.PublishedParsed)
|
|
}
|
|
|
|
func (f *FeedManager) RemoveFeed(url string) (*sql.Result, error) {
|
|
query := "delete from feeds where link = ?;"
|
|
return f.ExecDB(query, url)
|
|
}
|
|
|
|
func (f *FeedManager) DownloadFeed(url string) (*librss.Feed, error) {
|
|
fp := librss.NewParser()
|
|
feed, err := fp.ParseURL(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return feed, nil
|
|
}
|
|
|
|
func (f *FeedManager) GetFeeds() (map[int]librss.Feed, error) {
|
|
feeds := make(map[int]librss.Feed)
|
|
|
|
rows, err := f.QueryDB("select id, title, feedlink, link from feeds;")
|
|
if err != nil {
|
|
return feeds, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var link, title, feedLink string
|
|
var id int
|
|
err = rows.Scan(&id, &title, &feedLink, &link)
|
|
if err != nil {
|
|
return feeds, err
|
|
}
|
|
|
|
feeds[id] = librss.Feed{Link: link, FeedLink: feedLink, Title: title}
|
|
}
|
|
|
|
return feeds, nil
|
|
}
|
|
|
|
func (f *FeedManager) Sync() error {
|
|
feeds, err := f.GetFeeds()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for id, feed := range feeds {
|
|
res, err := f.DownloadFeed(feed.Link)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, item := range res.Items {
|
|
_, err := f.ExecDB("insert into items (feed, Title, Description, Content, Link, Author, GUID) values (?, ?, ?, ?, ?, ?, ?)",
|
|
id, item.Title, item.Description, item.Content, item.Link, item.Author, item.GUID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func HomeHandler(w http.ResponseWriter, r *http.Request) {
|
|
//fmt.Fprintf(w, "hello")
|
|
http.ServeFile(w, r, "./ui/index.html")
|
|
}
|
|
|
|
func GetFeedsHandler(w http.ResponseWriter, r *http.Request) {
|
|
defaultDBPath := "./database.db"
|
|
feedManager := FeedManager{DBPath: defaultDBPath}
|
|
|
|
feeds, err := feedManager.GetFeeds()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
feedList := make([]librss.Feed, 0)
|
|
for _, feed := range feeds {
|
|
feedList = append(feedList, feed)
|
|
}
|
|
|
|
json, err := json.Marshal(feedList)
|
|
fmt.Fprintf(w, string(json))
|
|
}
|
|
|
|
func PostFeed(w http.ResponseWriter, r *http.Request) {
|
|
defaultDBPath := "./database.db"
|
|
feedManager := FeedManager{DBPath: defaultDBPath}
|
|
|
|
new_feed_url := r.FormValue("new_feed_url")
|
|
|
|
if new_feed_url == "" {
|
|
w.WriteHeader(500)
|
|
}
|
|
|
|
feeds, err := feedManager.GetFeeds()
|
|
if err != nil {
|
|
log.Print(err)
|
|
w.WriteHeader(500)
|
|
return
|
|
}
|
|
|
|
for _, feed := range feeds {
|
|
if feed.FeedLink == new_feed_url {
|
|
log.Print("Not adding feed ", new_feed_url, " because it already exists")
|
|
w.WriteHeader(500)
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Print("Adding new feed : ", new_feed_url)
|
|
|
|
_, err = feedManager.AddFeed(r.FormValue("new_feed_url"))
|
|
if err != nil {
|
|
fmt.Fprintf(w, "ERROR")
|
|
}
|
|
fmt.Fprintf(w, "OK")
|
|
}
|
|
|
|
func (f *FeedManager) GetItems(feedId int) ([]librss.Item, error) {
|
|
rows, err := f.QueryDB("select title, description from items where feed=?;", feedId)
|
|
if err != nil {
|
|
log.Print(err)
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
items := make([]librss.Item, 0)
|
|
for rows.Next() {
|
|
var title, description string
|
|
err = rows.Scan(&title, &description)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, librss.Item{Title: title, Description: description})
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
func GetItemsHandler(w http.ResponseWriter, r *http.Request) {
|
|
defaultDBPath := "./database.db"
|
|
feedManager := FeedManager{DBPath: defaultDBPath}
|
|
|
|
vars := mux.Vars(r)
|
|
feedId, err := strconv.Atoi(vars["feedId"])
|
|
if err != nil {
|
|
log.Print(err)
|
|
return
|
|
}
|
|
|
|
items, err := feedManager.GetItems(feedId)
|
|
if err != nil {
|
|
log.Print(err)
|
|
return
|
|
}
|
|
|
|
json, err := json.Marshal(items)
|
|
fmt.Fprintf(w, string(json))
|
|
}
|
|
|
|
func main() {
|
|
defaultDBPath := "./database.db"
|
|
|
|
feedManager := FeedManager{DBPath: defaultDBPath}
|
|
|
|
if _, err := os.Stat(defaultDBPath); os.IsNotExist(err) {
|
|
feedManager.createDB()
|
|
_, err := feedManager.AddFeed("https://me.wavrant.xyz/feed/1/")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
_, err = feedManager.AddFeed("https://www.lemonde.fr/rss/une.xml")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = feedManager.Sync()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
} else if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
router := mux.NewRouter()
|
|
|
|
router.HandleFunc("/", HomeHandler)
|
|
|
|
router.PathPrefix("/static/").Handler(
|
|
http.StripPrefix(
|
|
"/static/", http.FileServer(http.Dir("./ui/static"))))
|
|
|
|
router.HandleFunc("/api/feeds/", GetFeedsHandler)
|
|
router.HandleFunc("/api/feeds/{feedId:[0-9]+}/items", GetItemsHandler)
|
|
router.HandleFunc("/api/feeds/new/", PostFeed).Methods("POST")
|
|
|
|
http.Handle("/", router)
|
|
|
|
server := &http.Server{
|
|
Handler: router,
|
|
Addr: "127.0.0.1:8000",
|
|
WriteTimeout: 10 * time.Second,
|
|
ReadTimeout: 10 * time.Second,
|
|
}
|
|
|
|
log.Print("Listening on 127.0.0.1:8000")
|
|
log.Fatal(server.ListenAndServe())
|
|
}
|