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.

289 lines
8.1KB

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