Captain Hook - Web hook 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.

236 lines
5.3 KiB

  1. package main
  2. import (
  3. "database/sql"
  4. "encoding/json"
  5. "fmt"
  6. "html/template"
  7. "log"
  8. "net/http"
  9. "os"
  10. "strings"
  11. _ "github.com/go-sql-driver/mysql"
  12. )
  13. //Global db object
  14. var db *sql.DB
  15. // Used in writing out errors when they occur
  16. type errorMessage struct {
  17. StatusCode int
  18. Msg string
  19. }
  20. // Writes the given error message in the template. If not, just writes the message raw
  21. func writeError(w http.ResponseWriter, e errorMessage) {
  22. // Try to use error template
  23. w.WriteHeader(e.StatusCode)
  24. t := template.Must(template.New("error.html").ParseFiles("html/error.html"))
  25. err := t.Execute(w, e)
  26. // If something goes wrong just tell them the error in an ugly way
  27. if err != nil {
  28. // Can't tell the user since the writer already wrote out
  29. log.Println(err)
  30. }
  31. }
  32. // Get all the currently available hooks
  33. func getHooks(w http.ResponseWriter) []Hook {
  34. // Get all hooks from db
  35. rows, err := db.Query("SELECT ID FROM Hooks")
  36. if err != nil {
  37. logError(w, err)
  38. }
  39. defer rows.Close()
  40. // Look through hooks
  41. hooks := make([]Hook, 0)
  42. for rows.Next() {
  43. var hook Hook
  44. err = rows.Scan(&hook.Name)
  45. // Hook doesn't have a name (This shouldn't really happen)
  46. if err != nil {
  47. logError(w, err)
  48. }
  49. hooks = append(hooks, hook)
  50. }
  51. return hooks
  52. }
  53. // Run a particular hook (will probably come up with a better solution)
  54. func runHook(w http.ResponseWriter, r *http.Request, hook Hook) {
  55. b, err := json.Marshal(hook)
  56. if err != nil {
  57. logError(w, err)
  58. } else {
  59. fmt.Fprint(w, string(b))
  60. }
  61. }
  62. func loadTemplate(w http.ResponseWriter, r *http.Request, path string, name string, arg interface{}) {
  63. // Log request to this url
  64. log.Println(r.URL.String(), r.Method)
  65. if r.URL.String() == path {
  66. // Serve index template
  67. t := template.Must(template.New(name+".html").ParseFiles("html/"+name+".html"))
  68. err := t.Execute(w, arg)
  69. if err != nil {
  70. // writeError(w, errorMessage{
  71. // StatusCode: http.StatusInternalServerError,
  72. // Msg: "Failed to Load Page",
  73. // })
  74. }
  75. // Can't find the page
  76. } else {
  77. writeError(w, errorMessage{
  78. StatusCode: http.StatusNotFound,
  79. Msg: "Not Found",
  80. })
  81. }
  82. }
  83. // Show the index
  84. func index(w http.ResponseWriter, r *http.Request) {
  85. u := r.URL.String()
  86. // Some bad path to start
  87. path := "/bad"
  88. // In case people think this is actually static html
  89. if u == "/" || u == "/index.html" || u == "/index.htm" || u == "/index" {
  90. loadTemplate(w, r, u, "index", getHooks(w))
  91. // Catch those trying to cheat and go to /bad
  92. } else if u == "/bad" {
  93. path = "/very_bad"
  94. } else {
  95. loadTemplate(w, r, path, "index", getHooks(w))
  96. }
  97. }
  98. // Show the configuration page
  99. func configureHook(w http.ResponseWriter, r *http.Request, hook Hook) {
  100. loadTemplate(w, r, r.URL.String(),"configure", hook)
  101. }
  102. // Handles requests to /hook/
  103. func hookHandler(w http.ResponseWriter, r *http.Request) {
  104. // Log request to this url
  105. log.Println(r.URL.String(), r.Method)
  106. if r.URL.String() == "/hook/" {
  107. switch r.Method {
  108. // Display all hooks
  109. case http.MethodGet:
  110. showHooks(w)
  111. // Method unsupported
  112. default:
  113. writeError(w, errorMessage{
  114. StatusCode: http.StatusMethodNotAllowed,
  115. Msg: "Method Unsupported",
  116. })
  117. }
  118. return
  119. }
  120. // Hook name
  121. name := r.URL.String()[len("/hook/"):]
  122. nameSplit := strings.Split(name, "/")
  123. name = strings.ToLower(nameSplit[0])
  124. switch r.Method {
  125. // Handle the hook
  126. case http.MethodGet, http.MethodPost:
  127. // See if the hook exists
  128. row := db.QueryRow("SELECT EXISTS(SELECT id FROM Hooks WHERE Hooks.id = ?)", name)
  129. var res int
  130. row.Scan(&res)
  131. // Hook doesn't exist
  132. if res == 0 {
  133. writeError(w, errorMessage{
  134. StatusCode: http.StatusNotFound,
  135. Msg: "Not Found",
  136. })
  137. return
  138. }
  139. // Construct the hook from the results
  140. hook := Hook{
  141. Name: name,
  142. conditions: nil,
  143. }
  144. // Check to see if it's configure web page instead of API endpoint
  145. if len(nameSplit) == 2 && nameSplit[1] == "configure" {
  146. configureHook(w, r, hook)
  147. } else {
  148. // Run the user's hook
  149. runHook(w, r, hook)
  150. }
  151. // Create / Update the hook
  152. case http.MethodPut:
  153. createHook(w, name)
  154. // Delete the hook
  155. case http.MethodDelete:
  156. deleteHook(w, name)
  157. // Method unsupported
  158. default:
  159. writeError(w, errorMessage{
  160. StatusCode: http.StatusMethodNotAllowed,
  161. Msg: "Method Not Allowed",
  162. })
  163. }
  164. }
  165. // Shows all of the hooks
  166. func showHooks(w http.ResponseWriter) {
  167. hooks := getHooks(w)
  168. b, err := json.Marshal(hooks)
  169. if err != nil {
  170. logError(w, err)
  171. } else {
  172. fmt.Fprint(w, string(b))
  173. }
  174. }
  175. // Creates a new hook
  176. func createHook(w http.ResponseWriter, name string) {
  177. statement, err := db.Prepare("INSERT IGNORE INTO Hooks(id) VALUES(?)")
  178. // Shouldn't really happen, but incase the insert goes wrong
  179. if err != nil {
  180. logError(w, err)
  181. os.Exit(1)
  182. return
  183. }
  184. defer statement.Close()
  185. statement.Exec(name)
  186. fmt.Fprintln(w, name)
  187. }
  188. // Delete a hook
  189. func deleteHook(w http.ResponseWriter, name string) {
  190. statement, err := db.Prepare("DELETE FROM Hooks WHERE ID=(?)")
  191. //Shouldn't ever error
  192. if err != nil {
  193. logError(w, err)
  194. return
  195. }
  196. defer statement.Close()
  197. // Get results
  198. res, err := statement.Exec(name)
  199. // Shouldn't ever error
  200. if err != nil {
  201. logError(w, err)
  202. return
  203. }
  204. // Probably also shouldn't error
  205. _, err = res.RowsAffected()
  206. if err != nil {
  207. logError(w, err)
  208. return
  209. }
  210. fmt.Fprintln(w, name)
  211. }