2020-11-06 17:45:31 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-02-20 14:42:45 +00:00
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
2020-11-06 17:45:31 +00:00
|
|
|
"log"
|
2021-02-20 14:42:45 +00:00
|
|
|
"net/http"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2021-03-03 17:19:55 +00:00
|
|
|
"github.com/go-chi/chi/v5"
|
2021-02-20 14:42:45 +00:00
|
|
|
"github.com/vcraescu/go-paginator"
|
2020-11-06 17:45:31 +00:00
|
|
|
)
|
|
|
|
|
2021-03-19 12:04:11 +00:00
|
|
|
const notificationsPath = "/notifications"
|
|
|
|
|
2021-02-20 14:42:45 +00:00
|
|
|
type notification struct {
|
|
|
|
ID int
|
|
|
|
Time int64
|
|
|
|
Text string
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) sendNotification(text string) {
|
2021-02-20 14:42:45 +00:00
|
|
|
n := ¬ification{
|
|
|
|
Time: time.Now().Unix(),
|
|
|
|
Text: text,
|
|
|
|
}
|
2021-06-06 12:39:42 +00:00
|
|
|
if err := a.db.saveNotification(n); err != nil {
|
2021-02-20 14:42:45 +00:00
|
|
|
log.Println("Failed to save notification:", err.Error())
|
|
|
|
}
|
2021-06-06 12:39:42 +00:00
|
|
|
if an := a.cfg.Notifications; an != nil {
|
2021-06-19 06:37:16 +00:00
|
|
|
err := a.send(an.Telegram, n.Text, "")
|
2021-06-14 19:34:29 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Println("Failed to send Telegram notification:", err.Error())
|
2020-11-06 17:45:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-20 14:42:45 +00:00
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (db *database) saveNotification(n *notification) error {
|
|
|
|
if _, err := db.exec("insert into notifications (time, text) values (@time, @text)", sql.Named("time", n.Time), sql.Named("text", n.Text)); err != nil {
|
2021-02-20 14:42:45 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (db *database) deleteNotification(id int) error {
|
|
|
|
_, err := db.exec("delete from notifications where id = @id", sql.Named("id", id))
|
2021-05-15 06:51:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-02-20 14:42:45 +00:00
|
|
|
type notificationsRequestConfig struct {
|
|
|
|
offset, limit int
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildNotificationsQuery(config *notificationsRequestConfig) (query string, args []interface{}) {
|
|
|
|
args = []interface{}{}
|
|
|
|
query = "select id, time, text from notifications order by id desc"
|
|
|
|
if config.limit != 0 || config.offset != 0 {
|
|
|
|
query += " limit @limit offset @offset"
|
|
|
|
args = append(args, sql.Named("limit", config.limit), sql.Named("offset", config.offset))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (db *database) getNotifications(config *notificationsRequestConfig) ([]*notification, error) {
|
2021-02-20 14:42:45 +00:00
|
|
|
notifications := []*notification{}
|
|
|
|
query, args := buildNotificationsQuery(config)
|
2021-06-06 12:39:42 +00:00
|
|
|
rows, err := db.query(query, args...)
|
2021-02-20 14:42:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for rows.Next() {
|
|
|
|
n := ¬ification{}
|
|
|
|
err = rows.Scan(&n.ID, &n.Time, &n.Text)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
notifications = append(notifications, n)
|
|
|
|
}
|
|
|
|
return notifications, nil
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (db *database) countNotifications(config *notificationsRequestConfig) (count int, err error) {
|
2021-02-20 14:42:45 +00:00
|
|
|
query, params := buildNotificationsQuery(config)
|
|
|
|
query = "select count(*) from (" + query + ")"
|
2021-06-06 12:39:42 +00:00
|
|
|
row, err := db.queryRow(query, params...)
|
2021-02-20 14:42:45 +00:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = row.Scan(&count)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
type notificationsPaginationAdapter struct {
|
|
|
|
config *notificationsRequestConfig
|
|
|
|
nums int64
|
2021-06-06 12:39:42 +00:00
|
|
|
db *database
|
2021-02-20 14:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *notificationsPaginationAdapter) Nums() (int64, error) {
|
|
|
|
if p.nums == 0 {
|
2021-06-06 12:39:42 +00:00
|
|
|
nums, _ := p.db.countNotifications(p.config)
|
2021-02-20 14:42:45 +00:00
|
|
|
p.nums = int64(nums)
|
|
|
|
}
|
|
|
|
return p.nums, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *notificationsPaginationAdapter) Slice(offset, length int, data interface{}) error {
|
|
|
|
modifiedConfig := *p.config
|
|
|
|
modifiedConfig.offset = offset
|
|
|
|
modifiedConfig.limit = length
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
notifications, err := p.db.getNotifications(&modifiedConfig)
|
2021-02-20 14:42:45 +00:00
|
|
|
reflect.ValueOf(data).Elem().Set(reflect.ValueOf(¬ifications).Elem())
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) notificationsAdmin(w http.ResponseWriter, r *http.Request) {
|
2021-03-22 07:20:56 +00:00
|
|
|
// Adapter
|
|
|
|
pageNoString := chi.URLParam(r, "page")
|
|
|
|
pageNo, _ := strconv.Atoi(pageNoString)
|
2021-06-06 12:39:42 +00:00
|
|
|
p := paginator.New(¬ificationsPaginationAdapter{config: ¬ificationsRequestConfig{}, db: a.db}, 10)
|
2021-03-22 07:20:56 +00:00
|
|
|
p.SetPage(pageNo)
|
|
|
|
var notifications []*notification
|
|
|
|
err := p.Results(¬ifications)
|
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-03-22 07:20:56 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// Navigation
|
|
|
|
var hasPrev, hasNext bool
|
|
|
|
var prevPage, nextPage int
|
|
|
|
var prevPath, nextPath string
|
|
|
|
hasPrev, _ = p.HasPrev()
|
|
|
|
if hasPrev {
|
|
|
|
prevPage, _ = p.PrevPage()
|
|
|
|
} else {
|
|
|
|
prevPage, _ = p.Page()
|
|
|
|
}
|
|
|
|
if prevPage < 2 {
|
|
|
|
prevPath = notificationsPath
|
|
|
|
} else {
|
|
|
|
prevPath = fmt.Sprintf("%s/page/%d", notificationsPath, prevPage)
|
|
|
|
}
|
|
|
|
hasNext, _ = p.HasNext()
|
|
|
|
if hasNext {
|
|
|
|
nextPage, _ = p.NextPage()
|
|
|
|
} else {
|
|
|
|
nextPage, _ = p.Page()
|
2021-02-20 14:42:45 +00:00
|
|
|
}
|
2021-03-22 07:20:56 +00:00
|
|
|
nextPath = fmt.Sprintf("%s/page/%d", notificationsPath, nextPage)
|
|
|
|
// Render
|
2021-06-06 12:39:42 +00:00
|
|
|
a.render(w, r, templateNotificationsAdmin, &renderData{
|
2021-03-22 07:20:56 +00:00
|
|
|
Data: map[string]interface{}{
|
|
|
|
"Notifications": notifications,
|
|
|
|
"HasPrev": hasPrev,
|
|
|
|
"HasNext": hasNext,
|
2021-06-10 20:09:50 +00:00
|
|
|
"Prev": prevPath,
|
|
|
|
"Next": nextPath,
|
2021-03-22 07:20:56 +00:00
|
|
|
},
|
|
|
|
})
|
2021-02-20 14:42:45 +00:00
|
|
|
}
|
2021-05-15 06:51:48 +00:00
|
|
|
|
2021-06-06 12:39:42 +00:00
|
|
|
func (a *goBlog) notificationsAdminDelete(w http.ResponseWriter, r *http.Request) {
|
2021-05-15 06:51:48 +00:00
|
|
|
id, err := strconv.Atoi(r.FormValue("notificationid"))
|
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
2021-05-15 06:51:48 +00:00
|
|
|
return
|
|
|
|
}
|
2021-06-06 12:39:42 +00:00
|
|
|
err = a.db.deleteNotification(id)
|
2021-05-15 06:51:48 +00:00
|
|
|
if err != nil {
|
2021-06-06 12:39:42 +00:00
|
|
|
a.serveError(w, r, err.Error(), http.StatusInternalServerError)
|
2021-05-15 06:51:48 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
http.Redirect(w, r, ".", http.StatusFound)
|
|
|
|
}
|