// Removes an existing Website from the database.
func ApiWebsitesDelete(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	value := ps.ByName("url")

	// Simple Validation
	if value == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid value.")
		return
	}

	// Remove Check-Results from Database
	db := lib.GetDatabase()
	res, err := db.Exec("DELETE c FROM checks c INNER JOIN websites w ON c.websiteId = w.id WHERE w.url = ?;", value)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to delete Check-Results: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Remove Notifications from Database
	res, err = db.Exec("DELETE n FROM notifications n INNER JOIN websites w ON n.websiteId = w.id WHERE w.url = ?;", value)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to delete Notifications: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Remove Website from Database
	db = lib.GetDatabase()
	res, err = db.Exec("DELETE FROM websites WHERE url = ?;", value)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to delete Website: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Check if exactly one Website has been deleted
	rowsAffected, _ := res.RowsAffected()
	if rowsAffected == 1 {
		SendJsonMessage(w, http.StatusOK, true, "")
	} else {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Could not delete Website.")
	}
}
// Sets an existing Website's notification-preferences.
func ApiWebsitePutNotifications(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	url := ps.ByName("url")
	pushbulletKey := r.Form.Get("pushbulletKey")
	email := r.Form.Get("email")
	telegramId := r.Form.Get("telegramId")

	// Simple Validation
	if url == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid value.")
		return
	}

	// Check for existing settings
	var (
		cPushbulletKey string
		cEmail         string
		cTelegramId    string
	)
	db := lib.GetDatabase()
	err := db.QueryRow("SELECT pushbulletKey, email, telegramId FROM notifications, websites WHERE notifications.websiteId = websites.id AND url = ?;", url).Scan(&cPushbulletKey, &cEmail, &cTelegramId)
	if err != nil {
		if err == sql.ErrNoRows {
			// no settings found --> Insert
			var id int
			err := db.QueryRow("SELECT id FROM websites WHERE url = ?;", url).Scan(&id)
			if err != nil {
				SendJsonMessage(w, http.StatusNotFound, false, "Unable to process your Request: Could not find Website.")
				return
			}

			_, err = db.Exec("INSERT INTO notifications (websiteId, pushbulletKey, email, telegramId) VALUES (?, ?, ?, ?);", id, pushbulletKey, email, telegramId)
			if err != nil {
				logging.MustGetLogger("").Error("Unable to insert Website's notification settings: ", err)
				SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
				return
			}
		} else {
			logging.MustGetLogger("").Error("Unable to get Website's notification settings: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
			return
		}
	} else {
		// existing settings found --> Update
		_, err = db.Exec("UPDATE notifications, websites SET pushbulletKey = ?, email = ?, telegramId = ? WHERE notifications.websiteId = websites.id AND url = ?;", pushbulletKey, email, telegramId, url)
		if err != nil {
			logging.MustGetLogger("").Error("Unable to update Website's notification settings: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
			return
		}
	}

	SendJsonMessage(w, http.StatusOK, true, "")
}
// Updates the application's check-interval in the database.
func ApiSettingsInterval(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	temp := r.Form.Get("interval")
	value, err := strconv.Atoi(temp)

	// Simple Validation
	if err != nil || value < 10 || value > 600 {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid value between 10 and 600 seconds.")
		return
	}

	// Update Database-Row
	db := lib.GetDatabase()
	_, err = db.Exec("UPDATE settings SET value = ? WHERE name = 'interval';", value)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to change Interval: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Update Configuration
	lib.GetConfiguration().Dynamic.Interval = value
	SendJsonMessage(w, http.StatusOK, true, "")
}
// Get an existing Website's notification-preferences.
func ApiWebsitesGetNotifications(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	value := ps.ByName("url")

	// Simple Validation
	if value == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid value.")
		return
	}

	// Get existing settings
	var (
		cPushbulletKey string
		cEmail         string
		cTelegramId    string
	)
	db := lib.GetDatabase()
	var resp WebsiteNotificationsResponse
	err := db.QueryRow("SELECT pushbulletKey, email, telegramId FROM notifications, websites WHERE notifications.websiteId = websites.id AND url = ?;", value).Scan(&cPushbulletKey, &cEmail, &cTelegramId)
	if err != nil {
		if err == sql.ErrNoRows {
			// Check if Website exists
			var id int
			err := db.QueryRow("SELECT id FROM websites WHERE url = ?;", value).Scan(&id)
			if err != nil {
				SendJsonMessage(w, http.StatusNotFound, false, "Unable to process your Request: Could not find Website.")
				return
			}
			resp = WebsiteNotificationsResponse{true, Notifications{"", "", ""}}
		} else {
			logging.MustGetLogger("").Error("Unable to get Website's notification settings: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
			return
		}
	} else {
		resp = WebsiteNotificationsResponse{true, Notifications{cPushbulletKey, cEmail, cTelegramId}}
	}

	// Send Response
	responseBytes, err := json.Marshal(resp)
	if err != nil {
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(responseBytes)
}
Esempio n. 5
0
// UpAndRunning2 Main - The application's entrance-point
func main() {
	// Logger
	lib.SetupLogger()

	// Welcome
	logging.MustGetLogger("").Info("Welcome to UpAndRunning2 v" + VERSION + " [" + goVersion + "@" + goArch + "]!")

	// Config
	lib.ReadConfigurationFromFile("config/local.json")
	lib.SetStaticConfiguration(lib.StaticConfiguration{VERSION, goVersion, goArch})

	// Database
	lib.OpenDatabase(lib.GetConfiguration().Database)

	// Config (again)
	lib.ReadConfigurationFromDatabase(lib.GetDatabase())

	// Admin-User
	admin := lib.Admin{}
	admin.Init()

	// Session-Management
	lib.InitSessionManagement()

	// Additional Libraries
	goreq.SetConnectTimeout(5 * time.Second)
	lib.InitHttpStatusCodeMap()
	go lib.RunTelegramBot()

	// Start Checking and Serving
	checkAllSites()
	startCheckTimer()
	startCleaningTimer()
	serveRequests()

	lib.GetDatabase().Close()
}
// Edits an existing Website in the database.
func ApiWebsitesEdit(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	oldUrl := ps.ByName("url")
	name := r.Form.Get("name")
	protocol := r.Form.Get("protocol")
	url := r.Form.Get("url")
	method := r.Form.Get("checkMethod")

	// Simple Validation
	if oldUrl == "" || name == "" || protocol == "" || url == "" || method == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit valid values.")
		return
	}
	if protocol != "http" && protocol != "https" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid protocol.")
		return
	}
	if !govalidator.IsURL(protocol + "://" + url) {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid url.")
		return
	}
	if method != "HEAD" && method != "GET" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid check method.")
		return
	}

	// Update Database
	db := lib.GetDatabase()
	res, err := db.Exec("UPDATE websites SET name = ?, protocol = ?, url = ?, checkMethod = ? WHERE url = ?;", name, protocol, url, method, oldUrl)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to edit Website: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Check if exactly one Website has been edited
	rowsAffected, _ := res.RowsAffected()
	if rowsAffected == 1 {
		SendJsonMessage(w, http.StatusOK, true, "")
	} else {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Could not edit Website.")
	}
}
Esempio n. 7
0
// Checks all enabled Websites
func checkAllSites() {
	// Check for internet-connection
	if !lib.GetConfiguration().Application.RunCheckIfOffline {
		res, err := goreq.Request{Uri: "https://google.com", Method: "HEAD", UserAgent: "UpAndRunning2 (https://github.com/MarvinMenzerath/UpAndRunning2)", MaxRedirects: 1, Timeout: 5 * time.Second}.Do()
		if err != nil {
			logging.MustGetLogger("").Warning("Did not check Websites because of missing internet-connection: ", err)
			return
		} else {
			if res.StatusCode != 200 {
				logging.MustGetLogger("").Warning("Did not check Websites because of missing internet-connection.")
				res.Body.Close()
				return
			}
		}
	}

	// Query the Database
	db := lib.GetDatabase()
	rows, err := db.Query("SELECT id, protocol, url, checkMethod FROM websites WHERE enabled = 1;")
	if err != nil {
		logging.MustGetLogger("").Error("Unable to fetch Websites: ", err)
		return
	}
	defer rows.Close()

	// Check every Website
	count := 0
	for rows.Next() {
		var website lib.Website
		err = rows.Scan(&website.Id, &website.Protocol, &website.Url, &website.CheckMethod)
		if err != nil {
			logging.MustGetLogger("").Error("Unable to read Website-Row: ", err)
			return
		}
		go website.RunCheck(false)
		count++
		time.Sleep(time.Millisecond * 200)
	}

	// Check for Errors
	err = rows.Err()
	if err != nil {
		logging.MustGetLogger("").Error("Unable to read Website-Rows: ", err)
		return
	}

	logging.MustGetLogger("").Info("Checked " + strconv.Itoa(count) + " active Websites.")
}
// Sets an existing Website to visible / invisible in the database.
func ApiWebsitesVisibility(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	value := ps.ByName("url")
	visible := r.Form.Get("visible")

	// Simple Validation
	if value == "" || visible == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit valid values.")
		return
	}

	var visibilityValue int
	if visible == "true" {
		visibilityValue = 1
	} else if visible == "false" {
		visibilityValue = 0
	} else {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit valid values.")
		return
	}

	// Update Database-Row
	db := lib.GetDatabase()
	res, err := db.Exec("UPDATE websites SET visible = ? WHERE url = ?;", visibilityValue, value)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to set Website's visibility: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	// Check if exactly one Website is affected
	rowsAffected, _ := res.RowsAffected()
	if rowsAffected == 1 {
		SendJsonMessage(w, http.StatusOK, true, "")
	} else {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Could not set Website's visibility.")
	}
}
// Inserts a new Website into the database.
func ApiWebsitesAdd(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	name := r.Form.Get("name")
	protocol := r.Form.Get("protocol")
	url := ps.ByName("url")
	method := r.Form.Get("checkMethod")

	// Simple Validation
	if name == "" || protocol == "" || url == "" || method == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit valid values.")
		return
	}
	if protocol != "http" && protocol != "https" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid protocol.")
		return
	}
	if !govalidator.IsURL(protocol + "://" + url) {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid url.")
		return
	}
	if method != "HEAD" && method != "GET" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid check method.")
		return
	}

	// Insert into Database
	db := lib.GetDatabase()
	_, err := db.Exec("INSERT INTO websites (name, protocol, url, checkMethod) VALUES (?, ?, ?, ?);", name, protocol, url, method)
	if err != nil {
		logging.MustGetLogger("").Error("Unable to add Website: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
		return
	}

	SendJsonMessage(w, http.StatusOK, true, "")
}
// Triggers a check of all enabled Websites.
func ApiWebsiteCheck(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Get data from Request
	r.ParseForm()
	url := ps.ByName("url")

	// Simple Validation
	if url == "" {
		SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Submit a valid value.")
		return
	}

	// Query the Database
	db := lib.GetDatabase()
	var website lib.Website
	err := db.QueryRow("SELECT id, protocol, url, checkMethod FROM websites WHERE url = ?;", url).Scan(&website.Id, &website.Protocol, &website.Url, &website.CheckMethod)

	if err != nil {
		if err == sql.ErrNoRows {
			SendJsonMessage(w, http.StatusNotFound, false, "Unable to process your Request: Could not find Website.")
			return
		} else {
			logging.MustGetLogger("").Error("Unable to get Website's notification settings: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request: "+err.Error())
			return
		}
	} else {
		// Run the requested check
		logging.MustGetLogger("").Info("Checking requested Website (" + website.Url + ").")
		website.RunCheck(false)
		SendJsonMessage(w, http.StatusOK, true, "")
	}
}
// Returns a AdminWebsiteResponse containing all Websites as AdminWebsite.
func ApiWebsitesDetailed(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if !lib.IsLoggedIn(r) {
		SendJsonMessage(w, http.StatusUnauthorized, false, "Unauthorized.")
		return
	}

	// Query the Database for basic data
	db := lib.GetDatabase()
	rows, err := db.Query("SELECT id, name, enabled, visible, protocol, url, checkMethod FROM websites ORDER BY name;")
	if err != nil {
		logging.MustGetLogger("").Error("Unable to fetch Websites: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	defer rows.Close()

	// Add every Website
	websites := []DetailedWebsite{}
	var (
		id            int
		name          string
		enabled       bool
		visible       bool
		protocol      string
		url           string
		checkMethod   string
		statusCode    string
		statusText    string
		responseTime  int
		time          string
		notifications EnabledNotifications
	)

	for rows.Next() {
		err = rows.Scan(&id, &name, &enabled, &visible, &protocol, &url, &checkMethod)
		if err != nil {
			logging.MustGetLogger("").Error("Unable to read Website-Data-Row: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		// Query the database for status data
		err = db.QueryRow("SELECT statusCode, statusText, responseTime, time FROM checks WHERE websiteId = ? ORDER BY id DESC LIMIT 1;", id).Scan(&statusCode, &statusText, &responseTime, &time)
		switch {
		case err == sql.ErrNoRows:
			statusCode = "0"
			statusText = "unknown"
			time = "0000-00-00 00:00:00"
		case err != nil:
			logging.MustGetLogger("").Error("Unable to fetch Website's status: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		// Query the database for enabled notifications
		var (
			tmpPushbullet string
			tmpEmail      string
			tmpTelegram   string
		)
		err = db.QueryRow("SELECT pushbulletKey, email, telegramId FROM notifications WHERE websiteId = ?;", id).Scan(&tmpPushbullet, &tmpEmail, &tmpTelegram)
		switch {
		case err == sql.ErrNoRows:
			notifications.Pushbullet = false
			notifications.Email = false
			notifications.Telegram = false
		case err != nil:
			logging.MustGetLogger("").Error("Unable to fetch Website's status: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		if tmpPushbullet != "" {
			notifications.Pushbullet = true
		} else {
			notifications.Pushbullet = false
		}
		if tmpEmail != "" {
			notifications.Email = true
		} else {
			notifications.Email = false
		}
		if tmpTelegram != "" {
			notifications.Telegram = true
		} else {
			notifications.Telegram = false
		}

		websites = append(websites, DetailedWebsite{id, name, enabled, visible, protocol, url, checkMethod, statusCode + " - " + statusText, strconv.Itoa(responseTime) + " ms", time, notifications})
	}

	// Send Response
	responseBytes, err := json.Marshal(DetailedWebsiteResponse{true, websites})
	if err != nil {
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(responseBytes)
}
// Returns a ResultsResponse containing an array of WebsiteCheckResults.
func ApiWebsitesResults(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	// Get limit-parameter from Request
	limit := 100
	limitString := r.URL.Query().Get("limit")
	if len(limitString) != 0 {
		parsedLimit, err := strconv.Atoi(limitString)
		if err != nil {
			SendJsonMessage(w, http.StatusBadRequest, false, "Unable to parse given limit-parameter.")
			return
		}
		if parsedLimit > 9999 {
			SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Limit has to be less than 10000.")
			return
		}
		limit = parsedLimit
	}

	// Get offset-parameter from Request
	offset := 0
	offsetString := r.URL.Query().Get("offset")
	if len(offsetString) != 0 {
		parsedOffset, err := strconv.Atoi(offsetString)
		if err != nil {
			SendJsonMessage(w, http.StatusBadRequest, false, "Unable to parse given offset-parameter.")
			return
		}
		if parsedOffset > 9999 {
			SendJsonMessage(w, http.StatusBadRequest, false, "Unable to process your Request: Offset has to be less than 10000.")
			return
		}
		offset = parsedOffset
	}

	enabledWebsitesOnly := "AND websites.enabled = 1"
	if lib.IsLoggedIn(r) {
		enabledWebsitesOnly = ""
	}

	// Query the Database
	db := lib.GetDatabase()
	rows, err := db.Query("SELECT statusCode, statusText, responseTime, time FROM checks, websites WHERE checks.websiteId = websites.id AND websites.url = ? "+enabledWebsitesOnly+" ORDER BY time DESC LIMIT ? OFFSET ?;", ps.ByName("url"), limit, offset)
	switch {
	case err == sql.ErrNoRows:
		SendJsonMessage(w, http.StatusNotFound, false, "Unable to find any data matching the given url.")
		return
	case err != nil:
		logging.MustGetLogger("").Error("Unable to fetch Results: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	defer rows.Close()

	// Add every Result
	results := []WebsiteCheckResult{}
	var (
		statusCode   string
		statusText   string
		responseTime int
		time         string
	)
	for rows.Next() {
		err = rows.Scan(&statusCode, &statusText, &responseTime, &time)
		if err != nil {
			logging.MustGetLogger("").Error("Unable to read Result-Row: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		results = append(results, WebsiteCheckResult{statusCode + " - " + statusText, strconv.Itoa(responseTime) + " ms", time})
	}

	if len(results) == 0 {
		SendJsonMessage(w, http.StatusNotFound, false, "Unable to find any data matching the given url.")
		return
	}

	// Check for Errors
	err = rows.Err()
	if err != nil {
		logging.MustGetLogger("").Error("Unable to read Result-Rows: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}

	// Send Response
	responseBytes, err := json.Marshal(ResultsResponse{true, results})
	if err != nil {
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(responseBytes)
}
// Returns a StatusResponse containing all the Website's important data if the Website is enabled.
func ApiWebsitesStatus(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	var (
		id                   int
		name                 string
		protocol             string
		url                  string
		statusCode           string
		statusText           string
		responseTime         int
		time                 string
		lastFailStatusCode   string
		lastFailStatusText   string
		lastFailResponseTime int
		lastFailTime         string
		ups                  int
		totalChecks          int
	)

	enabledWebsitesOnly := "AND websites.enabled = 1"
	if lib.IsLoggedIn(r) {
		enabledWebsitesOnly = ""
	}

	// Query the Database for basic data and the last successful check
	db := lib.GetDatabase()
	err := db.QueryRow("SELECT websites.id, websites.name, websites.protocol, websites.url, checks.statusCode, checks.statusText, checks.responseTime, checks.time FROM checks, websites WHERE checks.websiteId = websites.id AND websites.url = ? "+enabledWebsitesOnly+" ORDER BY checks.id DESC LIMIT 1;", ps.ByName("url")).Scan(&id, &name, &protocol, &url, &statusCode, &statusText, &responseTime, &time)
	switch {
	case err == sql.ErrNoRows:
		SendJsonMessage(w, http.StatusNotFound, false, "Unable to find any data matching the given url.")
		return
	case err != nil:
		logging.MustGetLogger("").Error("Unable to fetch Website-Status: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}

	// Query the Database for the last unsuccessful check
	err = db.QueryRow("SELECT checks.statusCode, checks.statusText, checks.responseTime, checks.time FROM checks, websites WHERE checks.websiteId = websites.id AND (checks.statusCode NOT LIKE '2%' AND checks.statusCode NOT LIKE '3%') AND websites.url = ? ORDER BY checks.id DESC LIMIT 1;", ps.ByName("url")).Scan(&lastFailStatusCode, &lastFailStatusText, &lastFailResponseTime, &lastFailTime)
	switch {
	case err == sql.ErrNoRows:
		lastFailStatusCode = "0"
		lastFailStatusText = "unknown"
		lastFailResponseTime = 0
		lastFailTime = "0000-00-00 00:00:00"
	case err != nil:
		logging.MustGetLogger("").Error("Unable to fetch Website-Status: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}

	// Query the Database for the amount of (successful / total) checks
	err = db.QueryRow("SELECT (SELECT COUNT(checks.id) FROM checks, websites WHERE checks.websiteId = websites.id AND (checks.statusCode LIKE '2%' OR checks.statusCode LIKE '3%') AND websites.url = ?) AS ups, (SELECT COUNT(checks.id) FROM checks, websites WHERE checks.websiteId = websites.id AND websites.url = ?) AS total FROM checks LIMIT 1;", ps.ByName("url"), ps.ByName("url")).Scan(&ups, &totalChecks)
	switch {
	case err == sql.ErrNoRows:
		logging.MustGetLogger("").Error("Unable to fetch Website-Status: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	case err != nil:
		logging.MustGetLogger("").Error("Unable to fetch Website-Status: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}

	// Build Response
	responseJson := StatusResponse{true, WebsiteData{id, name, protocol + "://" + url}, WebsiteAvailability{ups, totalChecks - ups, totalChecks, strconv.FormatFloat((float64(ups)/float64(totalChecks))*100, 'f', 2, 64) + "%"}, WebsiteCheckResult{statusCode + " - " + statusText, strconv.Itoa(responseTime) + " ms", time}, WebsiteCheckResult{lastFailStatusCode + " - " + lastFailStatusText, strconv.Itoa(lastFailResponseTime) + " ms", lastFailTime}}

	// Send Response
	responseBytes, err := json.Marshal(responseJson)
	if err != nil {
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(responseBytes)
}
// Returns a WebsiteResponse containing all publicly visible Websites as BasicWebsite.
func ApiWebsites(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if lib.IsLoggedIn(r) {
		// Send a more detailed version if the user is logged in
		ApiWebsitesDetailed(w, r, ps)
		return
	}

	// Query the Database for basic data
	db := lib.GetDatabase()
	rows, err := db.Query("SELECT id, name, protocol, url FROM websites WHERE enabled = 1 AND visible = 1 ORDER BY name;")
	if err != nil {
		logging.MustGetLogger("").Error("Unable to fetch Websites: ", err)
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	defer rows.Close()

	// Add every Website
	websites := []BasicWebsite{}
	var (
		id           int
		name         string
		protocol     string
		url          string
		statusCode   string
		statusText   string
		responseTime int
	)

	for rows.Next() {
		err = rows.Scan(&id, &name, &protocol, &url)
		if err != nil {
			logging.MustGetLogger("").Error("Unable to read Website-Data-Row: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		// Query the database for status data
		err = db.QueryRow("SELECT statusCode, statusText, responseTime FROM checks WHERE websiteId = ? ORDER BY id DESC LIMIT 1;", id).Scan(&statusCode, &statusText, &responseTime)
		switch {
		case err == sql.ErrNoRows:
			statusCode = "0"
			statusText = "unknown"
		case err != nil:
			logging.MustGetLogger("").Error("Unable to fetch Website-Status: ", err)
			SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
			return
		}

		websites = append(websites, BasicWebsite{name, protocol, url, statusCode + " - " + statusText, strconv.Itoa(responseTime) + " ms"})
	}

	// Send Response
	responseBytes, err := json.Marshal(WebsiteResponse{true, websites})
	if err != nil {
		SendJsonMessage(w, http.StatusInternalServerError, false, "Unable to process your Request.")
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(responseBytes)
}