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.

278 lines
7.7KB

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