131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
package api
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"pargar/backend/internal/models"
|
||
|
|
)
|
||
|
|
|
||
|
|
func (s *Server) handleListEvents(w http.ResponseWriter, r *http.Request) {
|
||
|
|
u := currentUser(r)
|
||
|
|
events, err := s.store.ListUpcomingEvents(r.Context(), u.ID)
|
||
|
|
if err != nil {
|
||
|
|
writeErr(w, http.StatusInternalServerError, "بارگذاری رویدادها ممکن نشد")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if events == nil {
|
||
|
|
events = []models.Event{}
|
||
|
|
}
|
||
|
|
writeJSON(w, http.StatusOK, map[string]any{"events": events})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleGetEvent(w http.ResponseWriter, r *http.Request) {
|
||
|
|
u := currentUser(r)
|
||
|
|
id := routeID(r, "id")
|
||
|
|
e, err := s.store.GetEvent(r.Context(), id)
|
||
|
|
if err != nil {
|
||
|
|
writeErr(w, http.StatusNotFound, "رویداد پیدا نشد")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
e.Rsvped = s.rsvped(r, e.ID, u.ID)
|
||
|
|
writeJSON(w, http.StatusOK, map[string]any{"event": e})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) rsvped(r *http.Request, eventID, userID int64) bool {
|
||
|
|
events, err := s.store.ListUpcomingEvents(r.Context(), userID)
|
||
|
|
if err != nil {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
for _, e := range events {
|
||
|
|
if e.ID == eventID {
|
||
|
|
return e.Rsvped
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleRsvp(w http.ResponseWriter, r *http.Request) {
|
||
|
|
u := currentUser(r)
|
||
|
|
id := routeID(r, "id")
|
||
|
|
if err := s.store.Rsvp(r.Context(), id, u.ID); err != nil {
|
||
|
|
writeErr(w, http.StatusInternalServerError, "ثبتنام در رویداد ممکن نشد")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
e, err := s.store.GetEvent(r.Context(), id)
|
||
|
|
if err == nil {
|
||
|
|
go func() {
|
||
|
|
_ = s.notify.Notify(context.Background(), u.ID, "event", "rsvp",
|
||
|
|
"ثبتنام رویداد تأیید شد", "شما در رویداد «"+e.Title+"» ثبتنام کردهاید. هنگام شروع به شما یادآوری میکنیم.", "/unwrap?tab=events", nil)
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "rsvped": true})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleUnrsvp(w http.ResponseWriter, r *http.Request) {
|
||
|
|
u := currentUser(r)
|
||
|
|
id := routeID(r, "id")
|
||
|
|
if err := s.store.Unrsvp(r.Context(), id, u.ID); err != nil {
|
||
|
|
writeErr(w, http.StatusInternalServerError, "لغو ثبتنام رویداد ممکن نشد")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
writeJSON(w, http.StatusOK, map[string]any{"ok": true, "rsvped": false})
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Server) handleEventICS(w http.ResponseWriter, r *http.Request) {
|
||
|
|
u := currentUser(r)
|
||
|
|
id := routeID(r, "id")
|
||
|
|
e, err := s.store.GetEvent(r.Context(), id)
|
||
|
|
if err != nil {
|
||
|
|
writeErr(w, http.StatusNotFound, "رویداد پیدا نشد")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
_ = u // any authenticated user may download
|
||
|
|
ics := buildICS(e, s.cfg.PublicURL)
|
||
|
|
w.Header().Set("Content-Type", "text/calendar; charset=utf-8")
|
||
|
|
w.Header().Set("Content-Disposition", "attachment; filename=event-"+fmt.Sprintf("%d", e.ID)+".ics")
|
||
|
|
_, _ = w.Write([]byte(ics))
|
||
|
|
}
|
||
|
|
|
||
|
|
func buildICS(e *models.Event, origin string) string {
|
||
|
|
f := func(t time.Time) string { return t.UTC().Format("20060102T150405Z") }
|
||
|
|
return fmt.Sprintf(`BEGIN:VCALENDAR
|
||
|
|
VERSION:2.0
|
||
|
|
PRODID:-//Pargar//Events//EN
|
||
|
|
CALSCALE:GREGORIAN
|
||
|
|
METHOD:PUBLISH
|
||
|
|
BEGIN:VEVENT
|
||
|
|
UID:%d@pargar
|
||
|
|
DTSTAMP:%s
|
||
|
|
DTSTART:%s
|
||
|
|
DTEND:%s
|
||
|
|
SUMMARY:%s
|
||
|
|
DESCRIPTION:%s
|
||
|
|
LOCATION:%s
|
||
|
|
END:VEVENT
|
||
|
|
END:VCALENDAR
|
||
|
|
`, e.ID, f(time.Now()), f(e.StartsAt), f(e.EndsAt),
|
||
|
|
escapeICS(e.Title), escapeICS(e.Description), escapeICS(e.ExternalURL))
|
||
|
|
}
|
||
|
|
|
||
|
|
func escapeICS(v string) string {
|
||
|
|
out := ""
|
||
|
|
for _, r := range v {
|
||
|
|
switch r {
|
||
|
|
case '\\':
|
||
|
|
out += "\\\\"
|
||
|
|
case ';':
|
||
|
|
out += "\\;"
|
||
|
|
case ',':
|
||
|
|
out += "\\,"
|
||
|
|
case '\n':
|
||
|
|
out += "\\n"
|
||
|
|
default:
|
||
|
|
out += string(r)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return out
|
||
|
|
}
|