Session repository and community portal for Meditation Assistant
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

298 lines
6.5 KiB

package main
import (
"encoding/json"
"fmt"
"html"
"log"
"net/http"
"strconv"
"strings"
"time"
)
var updateCommunity = make(chan struct{})
func handleMediNET(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = r.ParseForm()
// Authenticate (signin)
// Send key, client will then call (connect)
token := r.FormValue("token")
if token != "" {
a, err := db.authenticate(token)
if err != nil {
log.Printf("ERROR! failed to authenticate token: %s", err)
return
} else if a == nil {
log.Printf("ERROR! failed to retrieve authenticated account")
return
}
go trackActiveAccount(a.ID)
w.Header().Set("x-MediNET", "connected")
w.Header().Set("x-MediNET-Key", a.Key)
return
}
key := r.URL.Query().Get("x")
if key == "" {
key = r.FormValue("x")
}
a, err := db.getAccount(key)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
if a == nil {
w.Header().Set("x-MediNET", "signin")
logDebugf("Asking to sign in %s %q", key, html.EscapeString(r.URL.RequestURI()))
return
}
go trackActiveAccount(a.ID)
data := make(map[string]interface{})
data["status"] = "success"
action := r.FormValue("action")
tz, err := time.LoadLocation(r.URL.Query().Get("tz"))
if err != nil {
tz, err = time.LoadLocation("UTC")
if err != nil {
log.Printf("ERROR! %v", err)
return
}
}
apiver, err := strconv.Atoi(r.URL.Query().Get("v"))
if err != nil {
apiver = 0
}
appver := 0
streakbuffer := -1
av := r.URL.Query().Get("av")
match := regexpMarket.FindStringSubmatch(av)
if len(match) == 2 {
match := regexpNumbers.FindAllString(av[0:len(av)-len(match[1])], -1)
if match != nil {
appver, err = strconv.Atoi(strings.Join(match, ""))
if err != nil {
appver = 0
}
}
} else {
av = "x-" + av
}
appmarket := r.URL.Query().Get("am")
sb := r.URL.Query().Get("buf")
if sb != "" {
streakbuffer, err = strconv.Atoi(sb)
if err != nil {
streakbuffer = -1
}
}
if streakbuffer >= 0 {
if a.StreakBuffer != streakbuffer {
a.StreakBuffer = streakbuffer
go db.updateStreakBuffer(a.ID, streakbuffer)
}
}
// TODO: read from announcement table on successful connect
//data["announce"] = "First line\n\nSecond line"
switch action {
case "deletesession":
data["result"] = "notdeleted"
st := r.FormValue("session")
if st == "" {
break
}
started, err := strconv.Atoi(st)
if err != nil || started == 0 {
log.Printf("failed to read session started when deleting session: %s", err)
return
}
deleted, err := db.deleteSession(started, a.ID)
if err != nil {
log.Printf("ERROR! %v", err)
return
} else if deleted {
data["result"] = "deleted"
}
case "downloadsessions":
sessions, err := db.getAllSessions(a.ID)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
data["downloadsessions"] = sessions
case "uploadsessions":
data["result"] = "corrupt"
u := r.FormValue("uploadsessions")
if u == "" {
break
}
postsession := r.FormValue("postsession")
updateSessionStarted, _ := strconv.Atoi(r.FormValue("editstarted"))
var uploadsessions []session
err = json.Unmarshal([]byte(u), &uploadsessions)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
data["result"] = "uploaded"
uploaded := false
sessionsuploaded := 0
for _, session := range uploadsessions {
uploaded, err = db.addSession(session, updateSessionStarted, a.ID, av, appmarket)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
if uploaded {
sessionsuploaded++
}
}
data["sessionsuploaded"] = sessionsuploaded
if sessionsuploaded > 0 {
started := 0
streakday := 0
for _, session := range uploadsessions {
if session.Started > started {
started = session.Started
if action != "editposting" {
streakday = session.StreakDay
}
}
}
t := time.Now().In(tz)
if beforeWindowStart(t, streakbuffer) {
t = t.AddDate(0, 0, -1)
}
t = atWindowStart(t, streakbuffer)
if int64(started) >= t.Unix() { // A session was recorded after the start of today's streak window
streak, err := db.calculateStreak(a.ID, a.StreakBuffer, tz)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
logDebugf("NEW SESSION %v - CALCULATED: %d, SUBMITTED: %d", t, streak, streakday)
if streak < streakday {
streak = streakday
} else if streak > streakday {
err = db.setSessionStreakDay(started, streak, a.ID)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
}
err = db.setStreak(streak, a.ID, a.StreakBuffer, tz)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
}
stats.SessionsPosted++
if streakday > stats.TopStreak {
stats.TopStreak = streakday
}
}
if postsession != "" {
if sessionsuploaded > 0 {
data["result"] = "posted"
} else {
data["result"] = "alreadyposted"
}
}
}
w.Header().Set("x-MediNET", "connected")
// Send streak
if action == "connect" || action == "downloadsessions" || action == "uploadsessions" {
streakday, streakend, topstreak, err := db.getStreak(a.ID)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
w.Header().Set("x-MediNET-Streak", fmt.Sprintf("%d,%d", streakday, streakend))
w.Header().Set("x-MediNET-MaxStreak", fmt.Sprintf("%d", topstreak))
}
err = json.NewEncoder(w).Encode(data)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
j, err := json.Marshal(data)
if err != nil {
log.Printf("ERROR! %v", err)
return
}
logDebugf("App: %d API: %d Action: %s - %q", appver, apiver, action, html.EscapeString(r.URL.RequestURI()))
logDebugf("Account ID: %d, JSON: %s", a.ID, string(j))
if action == "uploadsessions" || action == "deletesession" {
updateCommunity <- struct{}{}
}
}
func initWeb() {
http.HandleFunc("/om/sessions", func(w http.ResponseWriter, r *http.Request) {
// TODO
})
http.HandleFunc("/om/account", func(w http.ResponseWriter, r *http.Request) {
// TODO
})
http.HandleFunc("/om/forum", func(w http.ResponseWriter, r *http.Request) {
// TODO
})
http.HandleFunc("/om/groups", func(w http.ResponseWriter, r *http.Request) {
// TODO
})
http.HandleFunc("/om/status", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "MediNET ONLINE<br><br>Accounts: %d (%d new)<br>Sessions: %d<br>Top streak: %d", len(stats.ActiveAccounts), stats.AccountsCreated, stats.SessionsPosted, stats.TopStreak)
})
http.HandleFunc("/om", handleMediNET)
go handleUpdateCommunity()
updateCommunity <- struct{}{}
}
func listenWeb() {
log.Fatal(http.ListenAndServe(config.Om, nil))
}