|
|
@ -3,41 +3,57 @@ package main |
|
|
|
import ( |
|
|
|
"database/sql" |
|
|
|
"fmt" |
|
|
|
"time" |
|
|
|
|
|
|
|
// "github.com/joho/godotenv"
|
|
|
|
"html/template" |
|
|
|
"log" |
|
|
|
"net/http" |
|
|
|
"os" |
|
|
|
"strconv" |
|
|
|
|
|
|
|
_ "github.com/go-sql-driver/mysql" |
|
|
|
) |
|
|
|
|
|
|
|
//Global db object
|
|
|
|
var db *sql.DB |
|
|
|
|
|
|
|
// Check errors and fail if they're bad
|
|
|
|
func check(err error) { |
|
|
|
// Used in writing out errors when they occur
|
|
|
|
type errorMessage struct { |
|
|
|
StatusCode int |
|
|
|
Msg string |
|
|
|
} |
|
|
|
|
|
|
|
// Writes the given error message in the template. If not, just writes the message raw
|
|
|
|
func writeError(w http.ResponseWriter, e errorMessage) { |
|
|
|
// Try to use error template
|
|
|
|
w.WriteHeader(e.StatusCode) |
|
|
|
t := template.Must(template.New("error.html").ParseFiles("html/error.html")) |
|
|
|
err := t.Execute(w, e) |
|
|
|
// If something goes wrong just tell them the error in an ugly way
|
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
// Can't tell the user since the writer already wrote out
|
|
|
|
log.Println(err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Get all the currently available hooks
|
|
|
|
func getHooks() []Hook { |
|
|
|
func getHooks(w http.ResponseWriter) []Hook { |
|
|
|
// Get all hooks from db
|
|
|
|
rows, err := db.Query("SELECT ID FROM HOOKS") |
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
} |
|
|
|
defer rows.Close() |
|
|
|
|
|
|
|
// Look through hooks
|
|
|
|
var hooks []Hook |
|
|
|
for rows.Next() { |
|
|
|
var hook Hook |
|
|
|
err = rows.Scan(&hook.Name) |
|
|
|
// Hook doesn't have a name (This shouldn't really happen)
|
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
} |
|
|
|
hooks = append(hooks, hook) |
|
|
|
} |
|
|
@ -46,75 +62,122 @@ func getHooks() []Hook { |
|
|
|
|
|
|
|
// Show the index
|
|
|
|
func index(w http.ResponseWriter, r *http.Request) { |
|
|
|
// Log request to this url
|
|
|
|
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()) |
|
|
|
// Serve index template
|
|
|
|
t := template.Must(template.New("index.html").ParseFiles("html/index.html")) |
|
|
|
err := t.Execute(w, getHooks(w)) |
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
} |
|
|
|
// Can't find the page
|
|
|
|
} else { |
|
|
|
http.ServeFile(w, r, "html/404.html") |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusNotFound, |
|
|
|
Msg: "Not found", |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
//Run a particular hook (will probably come up with a better solution)
|
|
|
|
// 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
|
|
|
|
// Handles requests to /hook/
|
|
|
|
func hookHandler(w http.ResponseWriter, r *http.Request) { |
|
|
|
// Log request to this url
|
|
|
|
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") |
|
|
|
if r.URL.String() == "/hook/" { |
|
|
|
switch r.Method { |
|
|
|
// Display all hooks
|
|
|
|
case http.MethodGet: |
|
|
|
showHooks(w) |
|
|
|
// Method unsupported
|
|
|
|
default: |
|
|
|
log.Println("y tho") |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusMethodNotAllowed, |
|
|
|
Msg: "Method Unsupported", |
|
|
|
}) |
|
|
|
} |
|
|
|
return |
|
|
|
} |
|
|
|
hook := Hook{ |
|
|
|
Name: name, |
|
|
|
params: nil, |
|
|
|
actions: nil, |
|
|
|
|
|
|
|
// Hook name
|
|
|
|
name := r.URL.String()[len("/hook/"):] |
|
|
|
|
|
|
|
switch r.Method { |
|
|
|
// Handle the hook
|
|
|
|
case http.MethodGet, http.MethodPost: |
|
|
|
// See if the hook exists
|
|
|
|
row := db.QueryRow("SELECT EXISTS(SELECT id FROM HOOKS WHERE HOOKS.id = ?)", name) |
|
|
|
var res int |
|
|
|
row.Scan(&res) |
|
|
|
// Hook doesn't exist
|
|
|
|
if res == 0 { |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusNotFound, |
|
|
|
Msg: "Not Found", |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Construct the hook from the results
|
|
|
|
hook := Hook { |
|
|
|
Name: name, |
|
|
|
params: nil, |
|
|
|
actions: nil, |
|
|
|
} |
|
|
|
|
|
|
|
// Run the user's hook
|
|
|
|
runHook(w, r, hook) |
|
|
|
|
|
|
|
// Create / Update the hook
|
|
|
|
case http.MethodPut: |
|
|
|
createHook(w, name) |
|
|
|
|
|
|
|
// Delete the hook
|
|
|
|
case http.MethodDelete: |
|
|
|
deleteHook(w, name) |
|
|
|
|
|
|
|
// Method unsupported
|
|
|
|
default: |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusMethodNotAllowed, |
|
|
|
Msg: "Method Not Allowed", |
|
|
|
}) |
|
|
|
} |
|
|
|
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() |
|
|
|
func showHooks(w http.ResponseWriter) { |
|
|
|
hooks := getHooks(w) |
|
|
|
allHooks := "" |
|
|
|
for _, h := range hooks { |
|
|
|
allHooks += h.Name + "\n" |
|
|
|
if hooks != nil { |
|
|
|
for _, h := range hooks { |
|
|
|
allHooks += h.Name + "\n" |
|
|
|
} |
|
|
|
} else { |
|
|
|
allHooks = "Nothing Found\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/"):] |
|
|
|
func createHook(w http.ResponseWriter, name string) { |
|
|
|
statement, err := db.Prepare("INSERT IGNORE INTO HOOKS(id) VALUES(?)") |
|
|
|
// Shouldn't really happen, but incase the insert goes wrong
|
|
|
|
if err != nil { |
|
|
|
log.Fatal(err) |
|
|
|
fmt.Fprintln(w, err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
defer statement.Close() |
|
|
@ -122,141 +185,48 @@ func createHook(w http.ResponseWriter, r *http.Request) { |
|
|
|
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/"):] |
|
|
|
// Delete a hook
|
|
|
|
func deleteHook(w http.ResponseWriter, name string) { |
|
|
|
statement, err := db.Prepare("DELETE FROM HOOKS where ID=(?)") |
|
|
|
//Shouldn't ever error
|
|
|
|
if err != nil { |
|
|
|
log.Println(err) |
|
|
|
fmt.Fprintln(w, err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
defer statement.Close() |
|
|
|
|
|
|
|
// Get results
|
|
|
|
res, err := statement.Exec(name) |
|
|
|
// Shouldn't ever error
|
|
|
|
if err != nil { |
|
|
|
log.Println(err) |
|
|
|
fmt.Fprintln(w, err) |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: err.Error(), |
|
|
|
}) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Probably also shouldn't error
|
|
|
|
n, err := res.RowsAffected() |
|
|
|
if err != nil { |
|
|
|
log.Println(err) |
|
|
|
fmt.Fprintln(w, err) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// Nothing was deleted
|
|
|
|
if n == 0 { |
|
|
|
http.ServeFile(w, r, "html/404.html") |
|
|
|
writeError(w, errorMessage { |
|
|
|
StatusCode: http.StatusInternalServerError, |
|
|
|
Msg: "Hook was not deleted", |
|
|
|
}) |
|
|
|
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)) |
|
|
|
} |
|
|
|
fmt.Fprintln(w, name) |
|
|
|
} |