parent
70bf57dba3
commit
61a2113d9d
6 changed files with 261 additions and 238 deletions
@ -0,0 +1,171 @@ |
||||
// TODO:
|
||||
// * Board UI?
|
||||
package main |
||||
|
||||
import ( |
||||
"bytes" |
||||
"crypto/ed25519" |
||||
"errors" |
||||
"flag" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"os" |
||||
"os/user" |
||||
"path/filepath" |
||||
"time" |
||||
|
||||
"github.com/llimllib/springer/client" |
||||
) |
||||
|
||||
func fileExists(name string) bool { |
||||
if _, err := os.Stat(name); errors.Is(err, os.ErrNotExist) { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
func getKeys(folder string) (ed25519.PublicKey, ed25519.PrivateKey) { |
||||
// get the expected public key file and private key file paths
|
||||
var pubfile, privfile string |
||||
if len(folder) == 0 { |
||||
user, err := user.Current() |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
configPath := os.Getenv("XDG_CONFIG_HOME") |
||||
if configPath == "" { |
||||
configPath = filepath.Join(user.HomeDir, ".config", "spring83") |
||||
} |
||||
|
||||
if err = os.MkdirAll(configPath, os.ModePerm); err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
pubfile = filepath.Join(configPath, "key.pub") |
||||
privfile = filepath.Join(configPath, "key.priv") |
||||
} else { |
||||
pubfile = filepath.Join(folder, "key.pub") |
||||
privfile = filepath.Join(folder, "key.priv") |
||||
} |
||||
|
||||
// try to load the public and private key files as ed25519 keys
|
||||
var pubkey ed25519.PublicKey |
||||
var privkey ed25519.PrivateKey |
||||
var err error |
||||
if fileExists(pubfile) && fileExists(privfile) { |
||||
pubkey, err = ioutil.ReadFile(pubfile) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
privkey, err = ioutil.ReadFile(privfile) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} else { |
||||
fmt.Println("Generating valid key. This will take a minute") |
||||
pubkey, privkey = client.ValidKey() |
||||
|
||||
os.WriteFile(pubfile, pubkey, 0666) |
||||
os.WriteFile(privfile, privkey, 0600) |
||||
} |
||||
|
||||
return pubkey, privkey |
||||
} |
||||
|
||||
func getBody(inputFile string) []byte { |
||||
var body []byte |
||||
var err error |
||||
if inputFile == "-" { |
||||
body, err = ioutil.ReadAll(os.Stdin) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} else { |
||||
body, err = ioutil.ReadFile(inputFile) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
} |
||||
|
||||
// Prepend a time element. Maybe we should check to see if it's already
|
||||
// been provided?
|
||||
timeElt := []byte(fmt.Sprintf("<time datetime=\"%s\">", time.Now().UTC().Format(time.RFC3339))) |
||||
body = append(timeElt, body...) |
||||
|
||||
if len(body) == 0 { |
||||
panic(fmt.Errorf("input required")) |
||||
} |
||||
if len(body) > 2217 { |
||||
panic(fmt.Errorf("input body too long")) |
||||
} |
||||
|
||||
return body |
||||
} |
||||
|
||||
func sendBody(server string, body []byte, pubkey ed25519.PublicKey, privkey ed25519.PrivateKey) { |
||||
client := &http.Client{} |
||||
|
||||
url := fmt.Sprintf("%s/%x", server, pubkey) |
||||
req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(body)) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
sig := ed25519.Sign(privkey, body) |
||||
req.Header.Set("Authorization", fmt.Sprintf("Spring-83 Signature=%x", sig)) |
||||
|
||||
dt := time.Now().Format(time.RFC1123) |
||||
req.Header.Set("If-Unmodified-Since", dt) |
||||
|
||||
resp, err := client.Do(req) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
defer resp.Body.Close() |
||||
|
||||
responseBody, err := ioutil.ReadAll(resp.Body) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
|
||||
if resp.StatusCode != 200 { |
||||
fmt.Printf("%s: %s\n", resp.Status, responseBody) |
||||
} |
||||
} |
||||
|
||||
func usage() { |
||||
fmt.Print(`springer [-file=filename] [-key=keyfolder] server |
||||
|
||||
Send a spring83 board to a server |
||||
|
||||
flags: |
||||
-file=filename |
||||
if present, a file to send to the server instead of accepting bytes on |
||||
stdin |
||||
-key=keyfolder |
||||
a folder the program should use for finding your public and private |
||||
keys. It will expect there to be two files, one called "key.pub" and |
||||
another called "key.priv" which are your public and private keys, |
||||
respectively |
||||
`) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
func main() { |
||||
inputFile := flag.String("file", "-", "The file to send to the server") |
||||
keyFolder := flag.String("key", "", "A folder to check for key.pub and key.priv") |
||||
flag.Usage = usage |
||||
flag.Parse() |
||||
|
||||
if len(flag.Args()) < 1 { |
||||
usage() |
||||
} |
||||
server := flag.Args()[0] |
||||
|
||||
pubkey, privkey := getKeys(*keyFolder) |
||||
|
||||
sendBody(server, getBody(*inputFile), pubkey, privkey) |
||||
} |
@ -0,0 +1,67 @@ |
||||
package main |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/http" |
||||
"os" |
||||
"time" |
||||
|
||||
_ "github.com/mattn/go-sqlite3" |
||||
"github.com/rs/zerolog" |
||||
"github.com/rs/zerolog/log" |
||||
|
||||
"github.com/llimllib/springer/server" |
||||
) |
||||
|
||||
// getenv returns the environment variable given by the key if present,
|
||||
// otherwise it returns adefault
|
||||
func getenv(key string, adefault string) string { |
||||
val, ok := os.LookupEnv(key) |
||||
if !ok { |
||||
return adefault |
||||
} |
||||
return val |
||||
} |
||||
|
||||
func initLog() zerolog.Logger { |
||||
logLevel := getenv("LOG_LEVEL", "info") |
||||
level, err := zerolog.ParseLevel(logLevel) |
||||
if err != nil { |
||||
log.Panic().Err(err).Msg("") |
||||
} |
||||
|
||||
log := zerolog.New(os.Stderr).With().Timestamp().Logger().Level(level) |
||||
if getenv("PRETTY_LOGGING", "") != "" { |
||||
log = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}) |
||||
} |
||||
|
||||
return log |
||||
} |
||||
|
||||
func main() { |
||||
log := initLog() |
||||
db := server.InitDB(log) |
||||
server.InitRuntimeValues(log) |
||||
server.InitCleaner(db, log) |
||||
|
||||
spring83 := server.NewSpring83Server(db, log) |
||||
|
||||
host := getenv("HOST", "") |
||||
port := getenv("PORT", "8000") |
||||
addr := fmt.Sprintf("%s:%s", host, port) |
||||
timeoutMsg := "Request timed out" |
||||
|
||||
srv := &http.Server{ |
||||
Addr: addr, |
||||
ReadTimeout: 1 * time.Second, |
||||
WriteTimeout: 1 * time.Second, |
||||
IdleTimeout: 30 * time.Second, |
||||
ReadHeaderTimeout: 2 * time.Second, |
||||
Handler: server.RequestLogger(log, http.TimeoutHandler(spring83, 2*time.Second, timeoutMsg)), |
||||
} |
||||
|
||||
log.Info().Str("addr", addr).Msg("starting helloserver on") |
||||
if err := srv.ListenAndServe(); err != http.ErrServerClosed { |
||||
log.Err(err).Msg("Error received from server") |
||||
} |
||||
} |
@ -1,3 +1,3 @@ |
||||
server/main.go server/templates/** { |
||||
daemon: go run ./server |
||||
daemon: go run ./cmd/springerd |
||||
} |
||||
|
Loading…
Reference in new issue