Follow the leader with help from a server
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

handlers.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. // NGnius 2020-01-30
  2. package main
  3. import (
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "strings"
  11. )
  12. const (
  13. defaultCorsHeader = "*"
  14. defaultPassword = ""
  15. )
  16. var (
  17. corsHeader string
  18. password string
  19. reuseTokens bool
  20. )
  21. func boardHandler(w http.ResponseWriter, r *http.Request) {
  22. w.Header().Add("Content-Type", "application/json")
  23. w.Header().Add("Access-Control-Allow-Origin", corsHeader)
  24. if r.Method != "GET" {
  25. //w.WriteHeader(405)
  26. errorResponse(405, "Non-GET method not allowed at this endpoint", w, r)
  27. return
  28. }
  29. args := r.URL.Query()
  30. // check args pre-conditions
  31. if !checkArgExists(args, "board", w) {
  32. errorResponse(400, "Missing required 'board' URL parameter", w, r)
  33. return
  34. }
  35. board := args.Get("board")
  36. if !checkArgExists(args, "count", w) || !checkArgInt(args, "count", w, 0) {
  37. //w.WriteHeader(400)
  38. errorResponse(400, "Missing required 'count' integer URL parameter", w, r)
  39. return
  40. }
  41. count, _ := strconv.Atoi(args.Get("count"))
  42. if !checkArgExists(args, "start", w) || !checkArgInt(args, "start", w, 0) {
  43. //w.WriteHeader(400)
  44. errorResponse(400, "Missing required 'start' integer URL parameter", w, r)
  45. return
  46. }
  47. start, _ := strconv.Atoi(args.Get("start"))
  48. // execute query
  49. result := NewResult("", r.URL.String())
  50. b, err := boardByName(board)
  51. //b, ok := boards[board]
  52. if err != nil {
  53. fmt.Println(err)
  54. //w.WriteHeader(404)
  55. errorResponse(404, "Board could not be retrieved: "+err.Error(), w, r)
  56. return
  57. }
  58. bEntries, loadErr := b.SomeEntries(int64(start), int64(start+count))
  59. if loadErr != nil {
  60. fmt.Println(loadErr)
  61. //w.WriteHeader(404)
  62. errorResponse(404, "Board entries could not be retrieved: "+loadErr.Error(), w, r)
  63. return
  64. }
  65. for _, entry := range bEntries {
  66. item, entryErr := entry.JsonObject()
  67. if entryErr != nil {
  68. fmt.Println(entryErr)
  69. }
  70. result.Items = append(result.Items, &item)
  71. }
  72. result.Query = fmt.Sprintf("load board[name: %s] from %d to %d", board, start, count+start)
  73. result.Complete()
  74. data, err := json.Marshal(result)
  75. if err != nil {
  76. //w.WriteHeader(500)
  77. errorResponse(500, "Unable to convert result into JSON: "+err.Error(), w, r)
  78. return
  79. }
  80. w.Write(data)
  81. }
  82. func playerHandler(w http.ResponseWriter, r *http.Request) {
  83. w.Header().Add("Content-Type", "application/json")
  84. w.Header().Add("Access-Control-Allow-Origin", corsHeader)
  85. if r.Method != "GET" {
  86. //w.WriteHeader(405)
  87. errorResponse(405, "Non-GET method not allowed at this endpoint", w, r)
  88. return
  89. }
  90. args := r.URL.Query()
  91. // check args
  92. if !checkArgExists(args, "id", w) || !checkArgInt(args, "id", w, 1) {
  93. errorResponse(400, "Missing required 'id' integer URL parameter", w, r)
  94. return
  95. }
  96. id, _ := strconv.Atoi(args.Get("id"))
  97. entry_count := 0
  98. if checkArgExists(args, "entries", w) && checkArgInt(args, "entries", w, 0) {
  99. entry_count, _ = strconv.Atoi(args.Get("entries"))
  100. }
  101. // retrieve player
  102. result := NewResult("", r.URL.String())
  103. player := &Player{ID: int64(id)}
  104. loadErr := player.Load()
  105. if loadErr != nil {
  106. fmt.Println(loadErr)
  107. //w.WriteHeader(404)
  108. errorResponse(404, "Player could not be retrieved: "+loadErr.Error(), w, r)
  109. return
  110. }
  111. tempJsonObj, _ := player.JsonObject()
  112. pJsonObj := tempJsonObj.(PlayerJSON)
  113. if entry_count > 0 {
  114. entries, loadErr := player.SomeEntries(int64(entry_count))
  115. if loadErr == nil {
  116. for _, e := range entries {
  117. eJsonObj, entryErr := e.JsonObject()
  118. if entryErr != nil {
  119. fmt.Println(entryErr)
  120. }
  121. pJsonObj.Entries = append(pJsonObj.Entries, eJsonObj.(EntryJSON))
  122. }
  123. }
  124. }
  125. result.Items = []interface{}{pJsonObj}
  126. result.Query = fmt.Sprintf("load player[id: %d]", player.ID)
  127. result.Complete()
  128. data, err := json.Marshal(result)
  129. if err != nil {
  130. //w.WriteHeader(500)
  131. errorResponse(500, "Unable convert result to JSON: "+err.Error(), w, r)
  132. return
  133. }
  134. w.Write(data)
  135. }
  136. func newEntryHandler(w http.ResponseWriter, r *http.Request) {
  137. w.Header().Add("Content-Type", "application/json")
  138. w.Header().Add("Access-Control-Allow-Origin", corsHeader)
  139. if r.Method != "POST" {
  140. //w.WriteHeader(405)
  141. errorResponse(405, "Non-POST method not allowed at this endpoint", w, r)
  142. return
  143. }
  144. // check for API token
  145. tokenHeader := r.Header.Get("Authorization")
  146. tokenSplit := strings.Split(tokenHeader, " ")
  147. if tokenHeader == "" || len(tokenSplit) != 2 || tokenSplit[1] == "" {
  148. errorResponse(401, "Missing or invalid authorization header", w, r)
  149. return
  150. }
  151. key, keyErr := keyByToken(tokenSplit[1])
  152. if keyErr != nil || key.ID == 0 {
  153. errorResponse(401, "Invalid token", w, r)
  154. return
  155. }
  156. if !key.IsEnabled() {
  157. errorResponse(403, "Invalid token", w, r)
  158. return
  159. }
  160. data, readErr := ioutil.ReadAll(r.Body)
  161. if readErr != nil {
  162. fmt.Println(readErr)
  163. //w.WriteHeader(500)
  164. errorResponse(500, "Unable to read HTTP request body: "+readErr.Error(), w, r)
  165. return
  166. }
  167. newEntry, jsonErr := UnmarshalNewEntryJSON(data)
  168. if jsonErr != nil {
  169. //w.WriteHeader(400)
  170. errorResponse(400, "Unable to convert request to JSON: "+jsonErr.Error(), w, r)
  171. return
  172. }
  173. if password != "" && newEntry.Password != password {
  174. errorResponse(403, "Invalid password", w, r)
  175. return
  176. }
  177. if newEntry.PlayerID != key.Player && !key.IsMultiuser() {
  178. errorResponse(403, "Invalid authorization for player", w, r)
  179. return
  180. }
  181. sqlErr := newEntrySql(newEntry.Score, newEntry.PlayerID, newEntry.BoardID)
  182. if sqlErr != nil {
  183. fmt.Println(sqlErr)
  184. //w.WriteHeader(500)
  185. errorResponse(500, "Entry could not be created: "+sqlErr.Error(), w, r)
  186. return
  187. }
  188. //w.WriteHeader(204)
  189. errorResponse(200, "New entry created", w, r)
  190. }
  191. func newKeyHandler(w http.ResponseWriter, r *http.Request) {
  192. w.Header().Add("Content-Type", "application/json")
  193. w.Header().Add("Access-Control-Allow-Origin", corsHeader)
  194. if r.Method != "POST" {
  195. errorResponse(405, "Non-POST method not allowed at this endpoint", w, r)
  196. return
  197. }
  198. data, readErr := ioutil.ReadAll(r.Body)
  199. if readErr != nil {
  200. fmt.Println(readErr)
  201. errorResponse(500, "Unable to read HTTP request body: "+readErr.Error(), w, r)
  202. return
  203. }
  204. result := NewResult("", r.URL.String())
  205. newKey, jsonErr := UnmarshalNewKeyJSON(data)
  206. if jsonErr != nil {
  207. errorResponse(400, "Unable to convert request to JSON: "+jsonErr.Error(), w, r)
  208. return
  209. }
  210. var key *Key
  211. var sqlErr error
  212. if reuseTokens {
  213. key, sqlErr = keyByPlayer(newKey.PlayerID)
  214. }
  215. if (sqlErr != nil && reuseTokens) || !reuseTokens {
  216. key, sqlErr = newKeySql(newKey.PlayerID, newKey.PlayerName)
  217. }
  218. if sqlErr != nil {
  219. fmt.Println(sqlErr)
  220. errorResponse(500, "Key could not be created: "+sqlErr.Error(), w, r)
  221. return
  222. }
  223. jsonObj, _ := key.JsonObject()
  224. result.Items = []interface{}{jsonObj}
  225. result.Query = fmt.Sprintf("new key[player id: %d]", newKey.PlayerID)
  226. result.Complete()
  227. data, err := json.Marshal(result)
  228. if err != nil {
  229. errorResponse(500, "Unable convert result to JSON: "+err.Error(), w, r)
  230. return
  231. }
  232. w.Write(data)
  233. }
  234. func exampleHandler(w http.ResponseWriter, r *http.Request) {
  235. // useless function please ignore
  236. }
  237. // utility functions
  238. func checkArgExists(values url.Values, key string, w http.ResponseWriter) (ok bool) {
  239. ok = values.Get(key) != ""
  240. return
  241. }
  242. func checkArgInt(values url.Values, key string, w http.ResponseWriter, min int) (ok bool) {
  243. intVal, err := strconv.Atoi(values.Get(key))
  244. ok = err == nil && intVal >= min
  245. return
  246. }
  247. func errorResponse(statusCode int, reason string, w http.ResponseWriter, r *http.Request) {
  248. w.WriteHeader(statusCode)
  249. query := "error"
  250. if statusCode == 200 {
  251. query = "success"
  252. }
  253. errorRes := NewResult(query, r.URL.String())
  254. errorRes.Items = append(errorRes.Items, ErrorJSON{Reason: reason, StatusCode: statusCode})
  255. errorRes.StatusCode = statusCode
  256. data, _ := json.Marshal(errorRes)
  257. w.Write(data)
  258. }