Compare commits

..

No commits in common. "main" and "v0.0.5" have entirely different histories.
main ... v0.0.5

17 changed files with 32 additions and 508 deletions

View File

@ -1,36 +0,0 @@
run-name: ${{ github.actor }} is testing
on: ["push"]
jobs:
Release:
runs-on: docker
#container:
# image: goreleaser/goreleaser
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
#- run: goreleaser release --skip-publish --snapshot --rm-dist --debug
- name: Setup Go environment
uses: https://github.com/actions/setup-go@v2.1.3
with:
go-version: 1.20
- name: Run GoReleaser
uses: https://github.com/goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
version: latest
args: release --skip-publish --snapshot --rm-dist --debug --clean
env:
GORELEASER_FORCE_TOKEN: "gitea"
- name: Copy deb to publish folder
run: mkdir publish; cp dist/*deb publish/last.deb
- name: Upload to s3
uses: https://github.com/shallwefootball/s3-upload-action@master
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: 'kekscloud-releases'
source_dir: 'publish/'
destination_dir: 'http-server-status/'
endpoint: 's3.eu-central-003.backblazeb2.com'

View File

@ -1,40 +0,0 @@
run-name: ${{ github.actor }} is testing
on:
push:
tags:
- '*'
jobs:
Release:
runs-on: docker
#container:
# image: goreleaser/goreleaser
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
#- run: goreleaser release --skip-publish --snapshot --rm-dist --debug
- name: Setup Go environment
uses: https://github.com/actions/setup-go@v2.1.3
with:
go-version: 1.20
- name: Run GoReleaser
uses: https://github.com/goreleaser/goreleaser-action@v4
with:
# either 'goreleaser' (default) or 'goreleaser-pro'
distribution: goreleaser
version: latest
args: release --clean
env:
GORELEASER_FORCE_TOKEN: "gitea"
GITEA_TOKEN: "${{ secrets.GITEA }}"
- name: Copy deb to publish folder
run: mkdir publish; cp dist/*deb publish/stable.deb; cp dist/*deb publish/
- name: Upload to s3
uses: https://github.com/shallwefootball/s3-upload-action@master
with:
aws_key_id: ${{ secrets.AWS_KEY_ID }}
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
aws_bucket: 'kekscloud-releases'
source_dir: 'publish/'
destination_dir: 'http-server-status/'
endpoint: 's3.eu-central-003.backblazeb2.com'

View File

@ -15,8 +15,11 @@ builds:
#- darwin
goarch:
- amd64
ldflags:
- -X main.version={{ .Version }}
archives:
- replacements:
darwin: Darwin
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
changelog:
@ -46,7 +49,4 @@ nfpms:
- src: init/http-server-status.service
dst: /etc/systemd/system/http-server-status.service
- src: config.yml
dst: /etc/http-server-status/config.yml.sample
scripts:
postinstall: "scripts/postinstall.sh"
preinstall: "scripts/preinstall.sh"
dst: /etc/http-server-status/config.yml.sample

View File

@ -1,32 +1,19 @@
# HTTP Server Status
Status Page with your Server informations (HDD usage, ram usage, load, systemd) which returns a http Status 500 if a limit is reached. With this Service you can monitor your Server with a external tool like uptimerobot.com.
Status Page for your VM or HDD Server to montiro with an external tool like uptimerobot.com
# Installation
## Dowanload URLs:
* Last Dev Build: `https://kekscloud-releases.s3.eu-central-003.backblazeb2.com/http-server-status/last.deb`
* Last Release Build: `https://kekscloud-releases.s3.eu-central-003.backblazeb2.com/http-server-status/stable.deb`
* Special Version: `https://kekscloud-releases.s3.eu-central-003.backblazeb2.com/http-server-status/http-server-status_1.0.7_linux_amd64.deb` (Bigger than 1.0.7)
To Install the Server Montiroing download the last .deb File from the Release page or the links above and save it as http-server-status.deb on your server. Than install the package with dpkg
To Install the Server Montiroing download the last .deb File from the Release page and save it as http-server-status.deb on your server. Than install the package with dpkg
```
dpkg -i http-server-status.deb
```
# Config
Copy the config sample and edit the config.
```bigquery
cp /etc/http-server-status/config.yml.sample /etc/http-server-status/config.yml
nano /etc/http-server-status/config.yml
```
# Run
After this start the server
```

View File

@ -1,6 +1,7 @@
package main
import (
"fmt"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
"http-server-status/internal/pkg/checks"
@ -14,21 +15,15 @@ type Config struct {
HTTP struct{
Listen string `yaml:"listen"`
} `yaml:"http"`
Auth struct{
Enabled bool `yaml:"enabled"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} `yaml:"auth"`
Checks struct{
HDD checks.HDDConfig `yaml:"hdd"`
Load checks.LoadConfig `yaml:"load"`
Memory checks.MemoryConfig `yaml:"memory"`
Systemd checks.SystemdConf `yaml:"systemd"`
Version checks.VersionConfig `yaml:"version"`
} `yaml:"checks"`
}
func readConfig() {
fmt.Println(os.Args)
filename := "/etc/http-server-status/config.yml"
if len(os.Args) > 1 {
filename = os.Args[1]
@ -44,6 +39,4 @@ func readConfig() {
if err != nil {
log.Fatal().Err(err).Msg("Cant parse yaml file")
}
c.Checks.Version.Version = version
}

View File

@ -1,27 +1,12 @@
http:
listen: ":3003"
auth:
enabled: false
username: test
password: test
checks:
hdd:
enabled: true
max_percent: 80
max_percent: 100
load:
enabled: true
max_load_1: 10
max_load_5: 8
max_load_15: 5
memory:
enabled: true
max: 80
max_swap: 80
systemd:
enabled: true
services:
- sshd
version:
enabled: true
max: 70

View File

@ -1,11 +1,11 @@
[Unit]
Description=HTTP Status Server
Description=Go Mail Admin
After=syslog.target
[Service]
Type=simple
User=http-server-status
ExecStart=/usr/bin/http-server-status
User=root
ExecStart=/usr/local/bin/http-server-status
SyslogIdentifier=http-server-status
StandardOutput=syslog
StandardError=syslog

View File

@ -1,5 +0,0 @@
URL=`python3 -c 'import urllib.request;import json;f = urllib.request.urlopen("https://git.keks.cloud/api/v1/repos/kekskurse/http-server-status/releases?limit=1");d = json.loads(f.read().decode("utf-8"));
for asset in d[0]["assets"]: x = asset["browser_download_url"] if asset["name"][-4:] == ".deb" else ""; print(x, end="");'`
wget -O hss.deb $URL
dpkg -i hss.deb
rm hss.deb

View File

@ -1,12 +1,12 @@
package checks
import (
"github.com/rs/zerolog/log"
"github.com/shirou/gopsutil/disk"
)
type HDDConfig struct {
MaxPercent int `yaml:"max_percent"`
Enabled bool `yaml:"enabled"`
}
type HDD struct {
@ -14,11 +14,9 @@ type HDD struct {
}
func (h HDD) Execute() (ok bool, data interface{}, err error) {
if h.Config.Enabled == false {
return true, nil, nil
}
success := true
parts, _ := disk.Partitions(false)
parts, _ := disk.Partitions(true)
log.Debug().Int("max_percent", h.Config.MaxPercent).Msg("Execute HDD Check")
usage := make(map[string]float64)
for _, p := range parts {
device := p.Mountpoint
@ -34,5 +32,5 @@ func (h HDD) Execute() (ok bool, data interface{}, err error) {
}
func (h HDD) Name() string {
return "DiscSpace"
return "Disc space"
}

View File

@ -8,7 +8,6 @@ type LoadConfig struct {
MaxLoad1 float64 `yaml:"max_load_1"`
MaxLoad5 float64 `yaml:"max_load_5"`
MaxLoad15 float64 `yaml:"max_load_15"`
Enabled bool `yaml:"enabled"`
}
type Load struct {
@ -16,9 +15,6 @@ type Load struct {
}
func (h Load) Execute() (ok bool, data interface{}, err error) {
if h.Config.Enabled == false {
return true, nil, nil
}
load, err := loadavg.Get()
if load.Loadavg1 > h.Config.MaxLoad1 {
return false,load, nil
@ -36,5 +32,5 @@ func (h Load) Execute() (ok bool, data interface{}, err error) {
}
func (h Load) Name() string {
return "SystemLoad"
return "System Load"
}

View File

@ -1,14 +1,11 @@
package checks
import (
"fmt"
"github.com/mackerelio/go-osstat/memory"
)
type MemoryConfig struct {
Max float64 `yaml:"max"`
MaxSwap float64 `yaml:"max_swap"`
Enabled bool `yaml:"enabled"`
}
type Memory struct {
@ -16,32 +13,15 @@ type Memory struct {
}
func (h Memory) Execute() (ok bool, data interface{}, err error) {
if h.Config.Enabled == false {
return true, nil, nil
}
memory, err := memory.Get()
fmt.Println(memory)
p := float64(100) / float64(memory.Total) * float64(memory.Used)
ps := float64(100) / float64(memory.SwapTotal) * float64(memory.SwapUsed)
res := make(map[string]interface{})
res["row"] = memory
res["ram"] = p
res["swap"] = ps
if memory.SwapTotal == 0 {
res["swap"] = 100
}
fmt.Println(ps)
if p > h.Config.Max {
return false, res, nil
}
if ps > h.Config.MaxSwap {
return false, res, nil
return false, memory, nil
}
return true, res,nil
return true, memory,nil
}
func (h Memory) Name() string {
return "MemoryUsage"
return "Memory usage"
}

View File

@ -1,63 +0,0 @@
package checks
import (
"github.com/rs/zerolog/log"
"os/exec"
"strings"
)
type SystemdConf struct {
Services []string `yaml:"services"`
Enabled bool `yaml:"enabled"`
}
type Systemd struct {
Config SystemdConf
}
func (h Systemd) Execute() (ok bool, data interface{}, err error) {
if h.Config.Enabled == false {
return true, nil, nil
}
success := true
servicelist := make(map[string]bool)
for _, service := range h.Config.Services {
res, err := h.getStatus(service)
if err != nil {
return false, nil, err
}
if res == false {
success = false
}
servicelist[service] = res
}
return success, servicelist,nil
}
func (h Systemd) Name() string {
return "SystemdStatus"
}
func (h Systemd) getStatus(name string) (bool, error) {
cmd := exec.Command("systemctl", "check", name)
out, err := cmd.CombinedOutput()
if err != nil {
log.Debug().Err(err).Msg("Error in systemd communication")
if exitErr, ok := err.(*exec.ExitError); ok {
if exitErr.ExitCode() == 3 {
return false, nil
}
}
return false, err
}
s := string(out)
s = strings.Trim(s, "\n")
if s == "active" {
return true, nil
}
return false, nil
}

View File

@ -1,45 +0,0 @@
package checks
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type VersionConfig struct {
Enabled bool `yaml:"enabled"`
Version string
}
type Version struct {
Config VersionConfig
}
func (h Version) Execute() (ok bool, data interface{}, err error) {
if h.Config.Enabled == false {
return true, nil, nil
}
resp, err := http.Get("https://git.keks.cloud/api/v1/repos/kekskurse/http-server-status/releases?limit=1")
defer resp.Body.Close()
var g []struct {
TagName string `json:"tag_name"`
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, nil, err
}
json.Unmarshal(body, &g)
if g[0].TagName == fmt.Sprintf("v%v", h.Config.Version) {
return true, map[string]string{"version": g[0].TagName}, nil
}
return false, map[string]string{"version": g[0].TagName}, nil
}
func (h Version) Name() string {
return "Version"
}

72
main.go
View File

@ -1,74 +1,27 @@
package main
import (
_ "embed"
"encoding/json"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
checks "http-server-status/internal/pkg/checks"
"net/http"
"os"
"sync"
"text/template"
)
//go:embed template/index.html
var s string
var (
version = "development"
)
var checkList []checks.Check
func init() {
readConfig()
pp := log.Output(zerolog.ConsoleWriter{Out: os.Stdout})
multi := zerolog.MultiLevelWriter(pp)
log.Logger = zerolog.New(multi).With().Timestamp().Caller().Logger()
checkList = append(checkList, checks.HDD{c.Checks.HDD}, checks.Memory{Config: c.Checks.Memory}, checks.Load{Config: c.Checks.Load}, checks.Systemd{Config: c.Checks.Systemd}, checks.Version{Config: c.Checks.Version})
log.Debug().Int("max_percent", c.Checks.HDD.MaxPercent).Msg("HDD CHECK")
checkList = append(checkList, checks.HDD{c.Checks.HDD}, checks.Memory{Config: c.Checks.Memory}, checks.Load{Config: c.Checks.Load})
}
func auth(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if c.Auth.Enabled {
user, pass, _ := r.BasicAuth()
if !check(user, pass) {
w.Header().Set("WWW-Authenticate", `Basic realm="MY REALM"`)
http.Error(w, "Unauthorized.", 401)
return
}
}
fn(w, r)
}
}
func check(u, p string) bool {
if u == c.Auth.Username && p == c.Auth.Password {
return true
}
return false
}
func main() {
http.HandleFunc("/", auth(func(writer http.ResponseWriter, request *http.Request) {
res, gloableRes := checkSystem()
if gloableRes == false {
writer.WriteHeader(http.StatusInternalServerError)
}
t, err := template.New("todos").Parse(s)
if err != nil {
panic(err)
}
t.Execute(writer, map[string]interface{}{"checks":res, "version": version})
//writer.Write([]byte(s))
}))
http.HandleFunc("/data.json", auth(handler))
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/data.json", http.StatusSeeOther)
})
http.HandleFunc("/data.json", handler)
err := http.ListenAndServe(c.HTTP.Listen, nil)
log.Fatal().Err(err).Msg("Shutdown")
}
@ -84,7 +37,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
resJson, err := json.Marshal(httpResposne)
if err != nil {
log.Fatal().Err(err).Interface("httpResponse", ResultReturn{}).Msg("Check Error")
log.Fatal().Err(err).Msg("Check Error")
}
if gloableRes {
w.Write(resJson)
@ -94,7 +47,6 @@ func handler(w http.ResponseWriter, r *http.Request) {
}
type ResultReturn struct {
Success bool `json:"success"`
Data interface{} `json:"data"`
@ -104,26 +56,18 @@ func checkSystem() (map[string]ResultReturn, bool) {
globaleResult := true
wg := sync.WaitGroup{}
res := make(map[string]ResultReturn)
mutex := sync.Mutex{}
for _, c := range checkList {
wg.Add(1)
go func(check checks.Check) {
defer wg.Done()
s, data, err := check.Execute()
if err != nil {
log.Error().Err(err).Msg("Cant execute check")
return
}
log.Debug().Str("check", check.Name()).Bool("status", s).Interface("Data", data).Msg("Executed Check, got Results")
s, data, _ := check.Execute()
if s == false {
globaleResult = false
}
mutex.Lock()
res[check.Name()] = ResultReturn{
Success: s,
Data: data,
}
mutex.Unlock()
}(c)
}
wg.Wait()

View File

@ -1,12 +1,2 @@
if [ -f "/etc/http-server-status/config.yml" ]; then
echo "Config exists"
else
cp /etc/http-server-status/config.yml.sample /etc/http-server-status/config.yml
fi
systemctl daemon-reload
STATUS="$(systemctl is-active tomcat.service)"
if [ "${STATUS}" = "active" ]; then
systemctl restart http-server-status
echo "Restart Service"
fi
systemctl restart http-server-status

View File

@ -1,4 +0,0 @@
#/bin/sh
if ! id -u http-server-status > /dev/null 2>&1; then
adduser --system http-server-status
fi

View File

@ -1,156 +0,0 @@
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTTP Status Page</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-F3w7mX95PdgyTmZZMECAngseQB83DfGTowi0iMjiWaeVhAn4FJkqJByhZMI3AhiU" crossorigin="anonymous">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<h1>HTTP Status Page</h1>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="card">
<div class="card-header {{if .checks.DiscSpace.Success }}bg-success{{else}}bg-danger{{end}}" id="hddtitle">
Check HDD
</div>
<div class="card-body" id="hddpercent">
<table class="table">
{{range $key, $val := .checks.DiscSpace.Data}}
<tr>
<td style="width: 20%">{{ $key }}</td>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: {{ $val }}%;" aria-valuenow="{{ $val }}" aria-valuemin="0" aria-valuemax="100">{{ $val }}%</div>
</div>
</td>
</tr>
{{end }}
</table>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header {{if .checks.MemoryUsage.Success }}bg-success{{else}}bg-danger{{end}}" id="ramtitle">
Check RAM
</div>
<div class="card-body">
{{ if .checks.MemoryUsage.Data }}
<table class="table">
<tr>
<th style="width: 20%">Ram</th>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: {{ .checks.MemoryUsage.Data.ram }}%;" aria-valuenow="{{ .checks.MemoryUsage.Data.ram }}" aria-valuemin="0" aria-valuemax="100">{{ .checks.MemoryUsage.Data.ram }}%</div>
</div>
</td>
</tr>
<tr>
<th style="width: 20%">Swap</th>
<td>
<div class="progress">
<div class="progress-bar" role="progressbar" style="width: {{ .checks.MemoryUsage.Data.swap }}%;" aria-valuenow="{{ .checks.MemoryUsage.Data.swap }}" aria-valuemin="0" aria-valuemax="100">{{ .checks.MemoryUsage.Data.swap }}%</div>
</div>
</td>
</tr>
</table>
{{ end }}
</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 20px;">
<div class="col-md-6">
<div class="card">
<div class="card-header {{if .checks.SystemLoad.Success }}bg-success{{else}}bg-danger{{end}}" id="loadtitle">
Check Load
</div>
<div class="card-body">
{{ if .checks.SystemLoad.Data }}
<table class="table">
<tr>
<th style="width: 20%">1</th>
<td>{{ .checks.SystemLoad.Data.Loadavg1 }}</td>
</tr>
<tr>
<th style="width: 20%">5</th>
<td>{{ .checks.SystemLoad.Data.Loadavg5 }}</td>
</tr>
<tr>
<th style="width: 20%">15</th>
<td>{{ .checks.SystemLoad.Data.Loadavg15 }}</td>
</tr>
</table>
{{ end }}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header {{if .checks.SystemdStatus.Success }}bg-success{{else}}bg-danger{{end}}" id="systemdtitle">
Systemd Check
</div>
<div class="card-body">
<table class="table">
{{range $key, $val := .checks.SystemdStatus.Data}}
<tr>
<td>{{ $key }}</td>
<td>
{{ $val }}
</td>
</tr>
{{end }}
</table>
</div>
</div>
</div>
</div>
<div class="row" style="margin-top: 20px;">
<div class="col-md-6">
<div class="card">
<div class="card-header {{if .checks.Version.Success }}bg-success{{else}}bg-danger{{end}}" id="loadtitle">
Version
</div>
<div class="card-body">
{{ if .checks.Version.Data }}
Newest Version: {{ .checks.Version.Data.version }}
{{ end }}
</div>
</div>
</div>
<div class="col-md-6">
</div>
</div>
</div>
</div>
<div class="container">
<footer class="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top">
<div class="col-md-4 d-flex align-items-center">
<a href="/" class="mb-3 me-2 mb-md-0 text-muted text-decoration-none lh-1">
<svg class="bi" width="30" height="24"><use xlink:href="#bootstrap"/></svg>
</a>
<span class="text-muted">Version: {{ .version }}</span>
</div>
<ul class="nav col-md-4 justify-content-end list-unstyled d-flex">
<li><a href="https://git.keks.cloud/kekskurse/http-server-status">Git/Source Code</a></li>
</ul>
</footer>
</div>
</body>
</html>