From efcb1aeda3f710254933c7fa14e487289f7cea94 Mon Sep 17 00:00:00 2001 From: velvettear Date: Fri, 24 Jun 2022 15:31:48 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + .vscode/launch.json | 17 ++++ .vscode/settings.json | 5 + LICENSE.md | 20 ++++ README.md | 12 +++ config/config.go | 43 +++++++++ go.mod | 3 + index.html | 165 ++++++++++++++++++++++++++++++++ log/log.go | 63 ++++++++++++ main.go | 129 +++++++++++++++++++++++++ util/date/date.go | 64 +++++++++++++ util/environment/environment.go | 33 +++++++ 12 files changed, 555 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 index.html create mode 100644 log/log.go create mode 100644 main.go create mode 100644 util/date/date.go create mode 100644 util/environment/environment.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e40a4e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__debug_bin \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7c6193c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "go-scan", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/main.go", + "env": { + "GO_SCAN_SCANNER": "utsushi:esci:usb:/sys/devices/pci0000:00/0000:00:14.0/usb1/1-5/1-5:1.0", + "GO_SCAN_OUTPUTDIRECTORY": "/tmp", + "GO_SCAN_ARGUMENTS": "--jpeg-quality 100 --mode Color --scan-area ISO/A4/Portrait --resolution 600" + } + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0b83fca --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "go.delveConfig": { + "debugAdapter": "dlv-dap", + } +} \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d342365 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ +# MIT License +**Copyright (c) 2022 Daniel Sommer \** + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.** \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..498b402 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# go-scan + +a simple http server serving a page to execute scans + + +## environment variables + +- `GO_SCAN_ADDRESS`: listen address of the http server +- `GO_SCAN_PORT`: port of the http server +- `GO_SCAN_SCANNER`: name of the scanner to use +- `GO_SCAN_OUTPUTDIRECTORY`: path to the output directory for scans +- `GO_SCAN_ARGUMENTS`: extra arguments to pass to the scanimage command \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..6762c90 --- /dev/null +++ b/config/config.go @@ -0,0 +1,43 @@ +package config + +import ( + "os" + "strings" + "velvettear/go-scan/util/environment" +) + +// exported functions +func New() Config { + config := Config{ + ServerConfig: serverConfig{ + Address: environment.New("GO_SCAN_ADDRESS", "0.0.0.0").Value(), + Port: environment.New("GO_SCAN_PORT", "9000").Value(), + }, + ScannerConfig: scannerConfig{ + Scanner: environment.New("GO_SCAN_SCANNER", "").Value(), + OutputDirectory: environment.New("GO_SCAN_OUTPUTDIRECTORY", os.TempDir()).Value(), + Arguments: environment.New("GO_SCAN_ARGUMENTS", "").Value(), + }, + } + if !strings.HasSuffix(config.ScannerConfig.OutputDirectory, "/") { + config.ScannerConfig.OutputDirectory = config.ScannerConfig.OutputDirectory + "/" + } + return config +} + +// structs +type Config struct { + ServerConfig serverConfig + ScannerConfig scannerConfig +} + +type serverConfig struct { + Port string + Address string +} + +type scannerConfig struct { + Scanner string + OutputDirectory string + Arguments string +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3aff4f7 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module velvettear/go-scan + +go 1.18 \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..b83255b --- /dev/null +++ b/index.html @@ -0,0 +1,165 @@ + + + + go-scan + + + + +
+
+

go-scan

+
+
+ +
+
+ filename: + +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000..9ce4b46 --- /dev/null +++ b/log/log.go @@ -0,0 +1,63 @@ +package log + +import ( + "fmt" + "velvettear/go-scan/util/date" +) + +// exported functions +func Debug(logMessage string, logExtras ...string) { + trace(0, logMessage, logExtras...) +} + +func Info(logMessage string, logExtras ...string) { + trace(1, logMessage, logExtras...) +} + +func Warning(logMessage string, logExtras ...string) { + trace(2, logMessage, logExtras...) +} + +func Error(logMessage string, logExtras ...string) { + trace(3, logMessage, logExtras...) +} + +// unexported functions +func trace(logLevel int, logMessage string, logExtras ...string) { + if len(logMessage) == 0 { + return + } + extras := "" + for index := 0; index < len(logExtras); index++ { + tmp := logExtras[index] + if len(tmp) == 0 { + continue + } + if index > 0 { + extras += " | " + } + extras += logExtras[index] + } + if len(extras) > 0 { + logMessage = logMessage + " (" + extras + ")" + } + fmt.Println(buildLogMessage(getPrefixForLogLevel(logLevel), logMessage)) +} + +func getPrefixForLogLevel(loglevel int) string { + switch loglevel { + case 3: + return "error" + case 2: + return "warning" + case 1: + return "info" + default: + return "debug" + } +} + +func buildLogMessage(prefix string, message string) string { + timestamp := date.New() + return timestamp.GetFormattedDate() + " [" + prefix + "]" + " > " + message +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..da791b8 --- /dev/null +++ b/main.go @@ -0,0 +1,129 @@ +package main + +import ( + _ "embed" + "encoding/json" + "fmt" + "net/http" + "os/exec" + "strconv" + "strings" + "time" + "velvettear/go-scan/config" + "velvettear/go-scan/log" + "velvettear/go-scan/util/date" +) + +//go:embed index.html +var html []byte + +var configuration config.Config + +func main() { + configuration = config.New() + startServer() +} + +func startServer() { + serverAddress := configuration.ServerConfig.Address + ":" + configuration.ServerConfig.Port + http.HandleFunc("/", handleHTTPRequests) + log.Info("starting server '" + serverAddress + "'...") + error := http.ListenAndServe(serverAddress, nil) + if error != nil { + log.Error("an error occured starting the server", error.Error()) + } +} + +func handleHTTPRequests(writer http.ResponseWriter, request *http.Request) { + if strings.ToLower(request.Method) == "post" { + result := handleScan(request) + writer.Header().Set("Content-Type", "application/json") + json, _ := json.Marshal(result) + writer.Write(json) + return + } + writer.Write(html) +} + +func handleScan(request *http.Request) *result { + log.Info("starting scan...") + timestamp := date.Milliseconds() + + var parameters requestParameters + parseErr := json.NewDecoder(request.Body).Decode(¶meters) + if parseErr != nil { + log.Error("error parsing request parameters", parseErr.Error()) + } + scanName := generateScanName(parameters.Filename) + args := []string{} + if len(configuration.ScannerConfig.Scanner) > 0 { + args = append(args, "-d") + args = append(args, configuration.ScannerConfig.Scanner) + } + args = append(args, "-o") + args = append(args, scanName) + extraArgs := strings.Split(configuration.ScannerConfig.Arguments, " ") + for index := 0; index < len(extraArgs); index++ { + args = append(args, extraArgs[index]) + } + log.Debug("executing command 'scanimage'...", args...) + scanCmd := exec.Command( + "scanimage", + args..., + ) + _, err := scanCmd.Output() + if err != nil { + log.Error("an error occured executing the scan", err.Error()) + result := &result{ + State: "error", + Message: err.Error(), + } + return result + } + result := &result{ + State: "ok", + Message: scanName, + } + log.Info("finished scan after "+strconv.Itoa(date.GetTimeDifference(timestamp))+"ms", scanName) + return result +} + +func generateScanName(filename string) string { + if len(filename) == 0 { + day := fmt.Sprint(time.Now().Day()) + if len(day) < 2 { + day = "0" + day + } + month := fmt.Sprint(int(time.Now().Month())) + if len(month) < 2 { + month = "0" + month + } + hour := fmt.Sprint(time.Now().Hour()) + if len(hour) < 2 { + hour = "0" + hour + } + minute := fmt.Sprint(time.Now().Minute()) + if len(minute) < 2 { + minute = "0" + minute + } + second := fmt.Sprint(time.Now().Second()) + if len(second) < 2 { + second = "0" + second + } + filename = day + month + fmt.Sprint(time.Now().Year()) + "-" + hour + minute + second + } + if !strings.HasSuffix(filename, ".png") { + filename += ".png" + } + return configuration.ScannerConfig.OutputDirectory + filename +} + +// structs +type result struct { + State string + Message string +} + +type requestParameters struct { + Filename string +} diff --git a/util/date/date.go b/util/date/date.go new file mode 100644 index 0000000..aa4a28a --- /dev/null +++ b/util/date/date.go @@ -0,0 +1,64 @@ +package date + +import ( + "fmt" + "time" +) + +// exported functions +func New() Date { + now := time.Now() + day := fmt.Sprint(now.Day()) + if len(day) < 2 { + day = "0" + day + } + month := fmt.Sprint(int(now.Month())) + if len(month) < 2 { + month = "0" + month + } + year := fmt.Sprint(now.Year()) + hour := fmt.Sprint(now.Hour()) + if len(hour) < 2 { + hour = "0" + hour + } + minute := fmt.Sprint(now.Minute()) + if len(minute) < 2 { + minute = "0" + minute + } + second := fmt.Sprint(now.Second()) + if len(second) < 2 { + second = "0" + second + } + return Date{ + Day: day, + Month: month, + Year: year, + Hour: hour, + Minute: minute, + Second: second, + GetFormattedDate: func() string { + return day + "." + month + "." + year + " " + hour + ":" + minute + ":" + second + }, + } +} + +func Milliseconds() int { + return int(time.Now().UnixMilli()) +} + +func GetTimeDifference(timestamp int) int { + return Milliseconds() - timestamp +} + +// structs +type getFormattedDate func() string + +type Date struct { + Day string + Month string + Year string + Hour string + Minute string + Second string + GetFormattedDate getFormattedDate +} diff --git a/util/environment/environment.go b/util/environment/environment.go new file mode 100644 index 0000000..50ee3c9 --- /dev/null +++ b/util/environment/environment.go @@ -0,0 +1,33 @@ +package environment + +import ( + "os" +) + +// exported functions +func New(name string, defaultValue string) EnvironmentVariable { + environmentVariable := EnvironmentVariable{ + Key: name, + Default: defaultValue, + Value: func() string { + if len(name) == 0 { + return defaultValue + } + value := os.Getenv(name) + if len(value) == 0 { + return defaultValue + } + return value + }, + } + return environmentVariable +} + +// structs +type getValue func() string + +type EnvironmentVariable struct { + Key string + Default string + Value getValue +}