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.

264 lines
7.4KB

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