feat: init commit

This commit is contained in:
Louis Dalibard 2024-04-01 21:09:41 +02:00
parent d899ee0b08
commit d8e89bd5db
17 changed files with 447 additions and 0 deletions

5
assets/banner.txt Normal file
View File

@ -0,0 +1,5 @@
__ ______
/ /______ ____ / / / /
/ //_/ __ \/ __ \/ / / /
/ ,< / /_/ / / / /_/_/_/
/_/|_|\____/_/ /_(_|_|_)

67
auth/auth.go Normal file
View File

@ -0,0 +1,67 @@
package auth
import (
"kon/config"
"kon/utils"
"log"
"sync"
"time"
"github.com/dchest/uniuri"
"github.com/golang-jwt/jwt/v5"
)
var Hashes = map[string]time.Time{}
var HashesMutex = sync.RWMutex{}
func CreateToken() string {
token := jwt.NewWithClaims(jwt.SigningMethodHS256,
jwt.MapClaims{
"reqHash": uniuri.New(),
"exp": time.Now().Add(time.Hour * 24).Unix(),
})
tokenString, err := token.SignedString(config.AuthSecret)
utils.CheckError(err)
return tokenString
}
func VerifyToken(tokenString string) bool {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return config.AuthSecret, nil
})
if err != nil {
return false
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
_, does_claim_have_req_hash := claims["reqHash"]
if does_claim_have_req_hash {
HashesMutex.RLock()
val, exists := Hashes[claims["reqHash"].(string)]
HashesMutex.RUnlock()
if exists {
if time.Since(val) > 48*time.Hour {
HashesMutex.Lock()
Hashes[claims["reqHash"].(string)] = time.Now()
HashesMutex.Unlock()
return true
} else {
return false
}
} else {
HashesMutex.Lock()
Hashes[claims["reqHash"].(string)] = time.Now()
HashesMutex.Unlock()
return true
}
} else {
return false
}
} else {
log.Printf("Invalid JWT Token")
return false
}
}

14
banner/banner.go Normal file
View File

@ -0,0 +1,14 @@
package banner
import (
"kon/constants"
"github.com/fatih/color"
)
const Banner = " __ ______\n / /______ ____ / / / /\n / //_/ __ \\/ __ \\/ / / / \n / ,< / /_/ / / / /_/_/_/ \n /_/|_|\\____/_/ /_(_|_|_)"
func FormatBanner() string {
d := color.New(color.FgHiWhite, color.Bold)
return d.Sprint(Banner) + " " + constants.VersionString
}

98
client/client.go Normal file
View File

@ -0,0 +1,98 @@
package client
import (
"encoding/json"
"fmt"
"io"
"kon/auth"
"kon/banner"
"kon/client/state"
"kon/config"
"kon/constants"
"kon/pon/paths/status"
"kon/utils"
"net/http"
"net/url"
"sort"
"time"
"github.com/fatih/color"
)
func Status() {
for host := range config.Config.Hosts {
state.State.Mutex.Lock()
state.State.Machines[host] = state.Machine{
State: state.Working,
JobStart: time.Now(),
}
state.State.Mutex.Unlock()
go StatusThread(host)
}
DisplayState()
}
func StatusThread(host string) {
base, err := url.Parse("http://" + config.Config.Hosts[host] + constants.HostEndpoint)
utils.CheckError(err)
base.Path += "status"
params := url.Values{}
params.Add("token", auth.CreateToken())
base.RawQuery = params.Encode()
resp, err := http.Get(base.String())
if utils.JobFailed(err, host) {
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if utils.JobFailed(err, host) {
return
}
var status status.Status
err = json.Unmarshal(body, &status)
if utils.JobFailed(err, host) {
return
}
state.State.Mutex.Lock()
state.State.Machines[host] = state.Machine{
State: state.Completed,
JobStart: state.State.Machines[host].JobStart,
JobEnd: time.Now(),
Message: fmt.Sprintf("[RAM: %0.2f%%] [Uptime: %s]", float64(status.MemUsed)/float64(status.MemTotal)*100.0, time.Duration(float64(status.Uptime)*float64(time.Second))),
}
state.State.Mutex.Unlock()
}
func DisplayState() {
finished := false
for !finished {
finished = true
currentString := banner.FormatBanner() + "\n\n"
state.State.Mutex.RLock()
keys := make([]string, 0, len(state.State.Machines))
for key := range state.State.Machines {
keys = append(keys, key)
}
sort.Strings(keys)
for _, host := range keys {
line := ""
switch state.State.Machines[host].State {
case state.Working:
finished = false
d := color.New(color.FgHiYellow, color.Bold)
line = d.Sprintf(" - [✸] %-15s %15s\n", host, time.Since(state.State.Machines[host].JobStart))
case state.Error:
d := color.New(color.FgHiRed, color.Bold)
line = d.Sprintf(" - [✗] %-15s %15s %s\n", host, state.State.Machines[host].JobEnd.Sub(state.State.Machines[host].JobStart), state.State.Machines[host].Message)
case state.Completed:
d := color.New(color.FgHiGreen, color.Bold)
line = d.Sprintf(" - [✔] %-15s %15s %s\n", host, state.State.Machines[host].JobEnd.Sub(state.State.Machines[host].JobStart), state.State.Machines[host].Message)
}
currentString += line
}
state.State.Mutex.RUnlock()
print("\033[H\033[2J")
println(currentString)
}
println("")
}

28
client/state/state.go Normal file
View File

@ -0,0 +1,28 @@
package state
import (
"sync"
"time"
)
type StateStruct struct {
Machines map[string]Machine
Mutex sync.RWMutex
}
type Machine struct {
State int
JobStart time.Time
JobEnd time.Time
Message string
}
const (
Working = iota
Completed
Error
)
var State StateStruct = StateStruct{
Machines: map[string]Machine{},
}

20
config/config.go Normal file
View File

@ -0,0 +1,20 @@
package config
type ConfigStruct struct {
Hosts map[string]string
}
var Config = ConfigStruct{
Hosts: map[string]string{
"alpha": "127.0.0.1",
"beta": "127.0.0.1",
"gamma": "127.0.0.1",
"delta": "127.0.0.1",
"epsilon": "127.0.0.1",
"zeta": "127.0.0.1",
"eta": "127.0.0.1",
"omega": "127.0.0.1",
},
}
var AuthSecret = []byte("ChangeMe!")

5
constants/constants.go Normal file
View File

@ -0,0 +1,5 @@
package constants
const VersionString = "v0.0.1"
const HostEndpoint = ":3000"

25
go.mod Normal file
View File

@ -0,0 +1,25 @@
module kon
go 1.22.1
require (
github.com/dchest/uniuri v1.2.0
github.com/fatih/color v1.16.0
github.com/gofiber/fiber/v2 v2.52.4
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/mackerelio/go-osstat v0.2.4
)
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/klauspost/compress v1.17.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.18.0 // indirect
)

35
go.sum Normal file
View File

@ -0,0 +1,35 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/dchest/uniuri v1.2.0 h1:koIcOUdrTIivZgSLhHQvKgqdWZq5d7KdMEWF1Ud6+5g=
github.com/dchest/uniuri v1.2.0/go.mod h1:fSzm4SLHzNZvWLvWJew423PhAzkpNQYq+uNLq4kxhkY=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/gofiber/fiber/v2 v2.52.4 h1:P+T+4iK7VaqUsq2PALYEfBBo6bJZ4q3FP8cZ84EggTM=
github.com/gofiber/fiber/v2 v2.52.4/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs=
github.com/mackerelio/go-osstat v0.2.4/go.mod h1:Zy+qzGdZs3A9cuIqmgbJvwbmLQH9dJvtio5ZjJTbdlQ=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

5
help/help.go Normal file
View File

@ -0,0 +1,5 @@
package help
const Help = `
Commands:
- pon: start server`

BIN
kon Executable file

Binary file not shown.

31
main.go Normal file
View File

@ -0,0 +1,31 @@
package main
import (
"fmt"
"kon/banner"
"kon/client"
"kon/help"
"kon/pon"
"log"
"os"
)
func main() {
args := os.Args
fmt.Println(banner.FormatBanner())
if len(args) == 1 {
fmt.Println(help.Help)
} else if len(args) == 2 {
switch args[1] {
case "pon":
pon.StartServer()
case "status":
client.Status()
default:
log.Fatal("Cannot find command.")
}
} else {
log.Fatal("Too many arguments.")
}
}

View File

@ -0,0 +1,12 @@
package middlewares
import (
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
)
func Init(app *fiber.App) {
app.Use(logger.New(logger.Config{
TimeFormat: "2006-01-02 15:04:05",
}))
}

11
pon/paths/paths.go Normal file
View File

@ -0,0 +1,11 @@
package paths
import (
"kon/pon/paths/status"
"github.com/gofiber/fiber/v2"
)
func Init(app *fiber.App) {
app.Get("/status", status.HandleFunc)
}

View File

@ -0,0 +1,47 @@
package status
import (
"encoding/json"
"kon/auth"
"kon/utils"
"os"
"github.com/mackerelio/go-osstat/memory"
"github.com/mackerelio/go-osstat/uptime"
"github.com/gofiber/fiber/v2"
)
type Status struct {
Hostname string
MemUsed uint64
MemTotal uint64
Uptime uint64
}
func GetStatus() Status {
hostname, err := os.Hostname()
utils.CheckError(err)
memory, err := memory.Get()
utils.CheckError(err)
uptime, err := uptime.Get()
utils.CheckError(err)
return Status{
Hostname: hostname,
MemUsed: memory.Used,
MemTotal: memory.Total,
Uptime: uint64(uptime) / 1000000000,
}
}
func HandleFunc(c *fiber.Ctx) error {
queryValue := c.Query("token")
if auth.VerifyToken(queryValue) {
statusJSON, err := json.Marshal(GetStatus())
utils.CheckError(err)
return c.SendString(string(statusJSON))
} else {
return c.SendString("no")
}
}

16
pon/pon.go Normal file
View File

@ -0,0 +1,16 @@
package pon
import (
"kon/constants"
"kon/pon/middlewares"
"kon/pon/paths"
"github.com/gofiber/fiber/v2"
)
func StartServer() {
app := fiber.New()
middlewares.Init(app)
paths.Init(app)
app.Listen(constants.HostEndpoint)
}

28
utils/utils.go Normal file
View File

@ -0,0 +1,28 @@
package utils
import (
"kon/client/state"
"log"
"time"
)
func CheckError(err error) {
if err != nil {
log.Fatal(err)
}
}
func JobFailed(err error, host string) bool {
if err != nil {
state.State.Mutex.Lock()
state.State.Machines[host] = state.Machine{
State: state.Error,
Message: err.Error(),
JobStart: state.State.Machines[host].JobStart,
JobEnd: time.Now(),
}
state.State.Mutex.Unlock()
return true
}
return false
}