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.
 
 
 
 
 

262 lines
5.8 KiB

package main
import (
"database/sql"
"fmt"
"time"
// "github.com/joho/godotenv"
"html/template"
"log"
"net/http"
"os"
"strconv"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
// Check errors and fail if they're bad
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
// Get all the currently available hooks
func getHooks() []Hook {
rows, err := db.Query("SELECT ID FROM HOOKS")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var hooks []Hook
for rows.Next() {
var hook Hook
err = rows.Scan(&hook.Name)
if err != nil {
log.Fatal(err)
}
hooks = append(hooks, hook)
}
return hooks
}
// Show the index
func index(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.String())
if r.URL.String() == "/" {
t := template.Must(template.New("index.tmpl").ParseFiles("html/index.tmpl"))
for i, j := range getHooks() {
log.Printf("%d: %s\n", i, j.Name)
}
err := t.Execute(w, getHooks())
if err != nil {
log.Fatal(err)
}
} else {
http.ServeFile(w, r, "html/404.html")
}
}
//Run a particular hook (will probably come up with a better solution)
func runHook(w http.ResponseWriter, r *http.Request, hook Hook) {
fmt.Fprintln(w, hook.Name)
}
// Handles all of the hooks
func hookHandler(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.String())
name := r.URL.String()[len("/hook/"):]
row := db.QueryRow("SELECT EXISTS(SELECT id FROM HOOKS WHERE HOOKS.id = ?)", name)
var res int
row.Scan(&res)
if res == 0 {
http.ServeFile(w, r, "html/404.html")
return
}
hook := Hook{
Name: name,
params: nil,
actions: nil,
}
runHook(w, r, hook)
}
// Shows all of the hooks
func showHooks(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.String())
if r.URL.String() != "/hooks/" {
http.ServeFile(w, r, "html/404.html")
return
}
hooks := getHooks()
allHooks := ""
for _, h := range hooks {
allHooks += h.Name + "\n"
}
fmt.Fprint(w, allHooks)
}
// Creates a new hook
func createHook(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.String())
// Doesn't actually work yet
if r.URL.String() == "/hook/create/" {
http.ServeFile(w, r, "html/create.html")
return
}
// Create a hook
name := r.URL.String()[len("/hook/create/"):]
statement, err := db.Prepare("INSERT IGNORE INTO HOOKS(id) VALUES(?)")
if err != nil {
log.Fatal(err)
fmt.Fprintln(w, err)
return
}
defer statement.Close()
statement.Exec(name)
fmt.Fprintln(w, name)
}
// Delete one hook
func deleteHook(w http.ResponseWriter, r *http.Request) {
log.Println(r.URL.String())
name := r.URL.String()[len("/hook/delete/"):]
statement, err := db.Prepare("DELETE FROM HOOKS where ID=(?)")
if err != nil {
log.Println(err)
fmt.Fprintln(w, err)
return
}
defer statement.Close()
res, err := statement.Exec(name)
if err != nil {
log.Println(err)
fmt.Fprintln(w, err)
return
}
n, err := res.RowsAffected()
if err != nil {
log.Println(err)
fmt.Fprintln(w, err)
return
}
if n == 0 {
http.ServeFile(w, r, "html/404.html")
return
}
fmt.Fprintln(w, name)
// log.Println(cap(Hooks))
// //Clear hooks (this is perfectly safe to do)
// Hooks = nil
}
// func init() {
// // Loads values from .env into the system
// if err := godotenv.Load(); err != nil {
// log.Println("No .env file found")
// }
// }
func main() {
// Check if a value exists and fail if it doesn't
checkExists := func(exists bool, msg string) {
if !exists {
log.Fatal(msg)
}
}
// Get user credentials
user, exists := os.LookupEnv("MYSQL_USER")
checkExists(exists, "Couldn't find database user")
password, exists := os.LookupEnv("MYSQL_PASSWORD")
checkExists(exists, "Couldn't find database password")
// Get database params
dbServer, exists := os.LookupEnv("MYSQL_SERVER")
checkExists(exists, "Couldn't find database server")
dbPort, exists := os.LookupEnv("MYSQL_PORT")
checkExists(exists, "Couldn't find database port")
dbName, exists := os.LookupEnv("MYSQL_DATABASE")
checkExists(exists, "Couldn't find database name")
connectionString := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s",
user,
password,
dbServer,
dbPort,
dbName,
)
// Check how many times to try the db before quitting
attemptsStr, exists := os.LookupEnv("DB_ATTEMPTS")
if !exists {
attemptsStr = "5"
}
attempts, err := strconv.Atoi(attemptsStr)
if err != nil {
attempts = 5
}
timeoutStr, exists := os.LookupEnv("DB_CONNECTION_TIMEOUT")
if !exists {
timeoutStr = "5"
}
timeout, err := strconv.Atoi(timeoutStr)
if err != nil {
timeout = 5
}
for i := 1; i <= attempts; i++ {
db, err = sql.Open("mysql", connectionString)
if err != nil && i != attempts {
log.Printf(
"WARNING: Could not connect to db on attempt %d. Trying again in %d seconds.\n",
attempts,
timeout,
)
} else if err != nil {
log.Fatalf("Could not connect to db after %d attempts\n", attempts)
}
time.Sleep(time.Duration(timeout) * time.Second)
}
log.Println("Connection to db succeeded!")
defer db.Close()
// Open the db and initialize the table if it doesn't exist
db, err = sql.Open("mysql", connectionString)
//// DEBUG: Making sure table is good
// if err := dropDB(db); err != nil {
// log.Fatal(err)
// }
// Create database if it doesn't exist
if err := createDB(db); err != nil {
log.Fatal(err)
}
// Get listen address and port
addr, exists := os.LookupEnv("LISTEN")
checkExists(exists, "Couldn't find listen address")
port, exists := os.LookupEnv("PORT")
checkExists(exists, "Couldn't find port")
// Handle hooks
http.HandleFunc("/hook/delete/", deleteHook)
http.HandleFunc("/hook/create/", createHook)
http.HandleFunc("/hook/", hookHandler)
http.HandleFunc("/hooks/", showHooks)
http.HandleFunc("/", index)
http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("html/css"))))
// Start the server
log.Println("Starting server")
log.Fatal(http.ListenAndServe(addr+":"+port, nil))
}