commit 623fc4f97059ac6770d3b47f378f799388a0da2c Author: velvettear Date: Mon Jul 4 10:02:06 2022 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eeb2383 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__debug_bin +go-scan \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1dfe2f1 --- /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/scans", + "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..bea5fd4 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# go-http-server + +a simple http server (template) written in go + +## environment variables + +- `GO_HTTP_ADDRESS`: listen address of the http server +- `GO_HTTP_PORT`: port of the http server + +## build process + +- `git clone https://git.velvettear.de/velvettear/go-http-server.git` +- `cd go-http-server` +- `go build` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c2c7d15 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module velvettear/go-http-server + +go 1.18 \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..a9305b3 --- /dev/null +++ b/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "embed" + _ "embed" + "velvettear/go-http-server/src/config" + "velvettear/go-http-server/src/http" +) + +//go:embed static +var files embed.FS + +var configuration config.Config + +func main() { + configuration = config.New() + server := http.Server(configuration.ServerConfig.Address, configuration.ServerConfig.Port, files) + server.Run() +} diff --git a/src/config/config.go b/src/config/config.go new file mode 100644 index 0000000..4d7b535 --- /dev/null +++ b/src/config/config.go @@ -0,0 +1,26 @@ +package config + +import ( + "velvettear/go-http-server/src/util/environment" +) + +// exported functions +func New() Config { + config := Config{ + ServerConfig: serverConfig{ + Address: environment.New("GO_HTTP_ADDRESS", "0.0.0.0").Value(), + Port: environment.New("GO_HTTP_PORT", "9000").Value(), + }, + } + return config +} + +// structs +type Config struct { + ServerConfig serverConfig +} + +type serverConfig struct { + Port string + Address string +} diff --git a/src/http/contentTypes.go b/src/http/contentTypes.go new file mode 100644 index 0000000..c1ad355 --- /dev/null +++ b/src/http/contentTypes.go @@ -0,0 +1,157 @@ +package http + +import "strings" + +func getContentType(file string) string { + parts := strings.Split(file, ".") + extension := parts[len(parts)-1] + switch extension { + case ".aac": + return "audio/aac" + case "abw": + return "application/x-abiword" + case "arc": + return "application/x-freearc" + case "avif": + return "image/avif" + case "avi": + return "video/x-msvideo" + case "azw": + return "application/vnd.amazon.ebook" + case "bin": + return "application/octet-stream" + case "bmp": + return "image/bmp" + case "bz": + return "application/x-bzip" + case "bz2": + return "application/x-bzip2" + case "cda": + return "application/x-cdf" + case "csh": + return "application/x-csh" + case "css": + return "text/css" + case "csv": + return "text/csv" + case "doc": + return "application/msword" + case "docx": + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + case "eot": + return "application/vnd.ms-fontobject" + case "epub": + return "application/epub+zip" + case "gz": + return "application/gzip" + case "gif": + return "image/gif" + case "htm": + case "html": + return "text/html" + case "ico": + return "image/vnd.microsoft.icon" + case "ics": + return "text/calendar" + case "jar": + return "application/java-archive" + case "jpeg": + case "jpg": + return "image/jpeg" + case "js": + return "text/javascript" + case "json": + return "application/json" + case "jsonld": + return "application/ld+json" + case "mid": + case "midi": + return "audio/midi audio/x-midi" + case "mjs": + return "text/javascript" + case "mp3": + return "audio/mpeg" + case "mp4": + return "video/mp4" + case "mpeg": + return "video/mpeg" + case "mpkg": + return "application/vnd.apple.installer+xml" + case "odp": + return "application/vnd.oasis.opendocument.presentation" + case "ods": + return "application/vnd.oasis.opendocument.spreadsheet" + case "odt": + return "application/vnd.oasis.opendocument.text" + case "oga": + return "audio/ogg" + case "ogv": + return "video/ogg" + case "ogx": + return "application/ogg" + case "opus": + return "audio/opus" + case "otf": + return "font/otf" + case "png": + return "image/png" + case "pdf": + return "application/pdf" + case "php": + return "application/x-httpd-php" + case "ppt": + return "application/vnd.ms-powerpoint" + case "pptx": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation" + case "rar": + return "application/vnd.rar" + case "rtf": + return "application/rtf" + case "sh": + return "application/x-sh" + case "svg": + return "image/svg+xml" + case "swf": + return "application/x-shockwave-flash" + case "tar": + return "application/x-tar" + case "tif": + case "tiff": + return "image/tiff" + case "ts": + return "video/mp2t" + case "ttf": + return "font/ttf" + case "txt": + return "text/plain" + case "vsd": + return "application/vnd.visio" + case "wav": + return "audio/wav" + case "weba": + return "audio/webm" + case "webm": + return "video/webm" + case "webp": + return "image/webp" + case "woff": + return "font/woff" + case "woff2": + return "font/woff2" + case "xhtml": + return "application/xhtml+xml" + case "xls": + return "application/vnd.ms-excel" + case "xlsx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + case "xml": + return "application/xml" + case "xul": + return "application/vnd.mozilla.xul+xml" + case "zip": + return "application/zip" + case "7z": + return "application/x-7z-compressed" + } + return "" +} diff --git a/src/http/server.go b/src/http/server.go new file mode 100644 index 0000000..ed6fe52 --- /dev/null +++ b/src/http/server.go @@ -0,0 +1,63 @@ +package http + +import ( + "embed" + "net/http" + "strings" + "velvettear/go-http-server/src/log" +) + +// struct +type server struct { + address string + port string + files embed.FS +} + +// "constructor" +func Server(address string, port string, files embed.FS) server { + server := server{ + address: address, + port: port, + files: files, + } + return server +} + +// exported functions +func (server server) Run() { + if len(server.address) == 0 || len(server.port) == 0 { + log.Fatal("could not start the server, address or port is not set") + } + serverAddress := server.address + ":" + server.port + http.HandleFunc("/", server.serveStaticFiles) + log.Info("starting server '" + serverAddress + "'...") + err := http.ListenAndServe(serverAddress, nil) + if err != nil { + log.Fatal("an error occured starting the server", err.Error()) + } +} + +// unexported functions +func (server server) serveStaticFiles(writer http.ResponseWriter, request *http.Request) { + requestedFile := strings.TrimPrefix(request.URL.Path, "/") + switch requestedFile { + case "favicon.ico": + requestedFile = "img/" + requestedFile + case "": + requestedFile = "html/index.html" + } + requestedFile = "static/" + requestedFile + bytes, err := server.files.ReadFile(requestedFile) + if err != nil { + log.Error("an error occured serving the static file '"+requestedFile+"'", err.Error()) + writer.WriteHeader(404) + return + } + contentType := getContentType(requestedFile) + if len(contentType) > 0 { + writer.Header().Set("Content-Type", contentType) + } + log.Debug("serving file '" + requestedFile + "'...") + writer.Write(bytes) +} diff --git a/src/log/log.go b/src/log/log.go new file mode 100644 index 0000000..bf16647 --- /dev/null +++ b/src/log/log.go @@ -0,0 +1,71 @@ +package log + +import ( + "fmt" + "os" + "velvettear/go-http-server/src/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...) +} + +func Fatal(logMessage string, logExtras ...string) { + trace(4, logMessage, logExtras...) + os.Exit(1) +} + +// 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 4: + return "fatal" + 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/src/util/date/date.go b/src/util/date/date.go new file mode 100644 index 0000000..fa506a9 --- /dev/null +++ b/src/util/date/date.go @@ -0,0 +1,64 @@ +package date + +import ( + "fmt" + "time" +) + +// struct(s) +type getFormattedDate func() string + +type Date struct { + Day string + Month string + Year string + Hour string + Minute string + Second string + GetFormattedDate getFormattedDate +} + +// 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 +} diff --git a/src/util/environment/environment.go b/src/util/environment/environment.go new file mode 100644 index 0000000..50ee3c9 --- /dev/null +++ b/src/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 +} diff --git a/static/css/styles.css b/static/css/styles.css new file mode 100644 index 0000000..8795661 --- /dev/null +++ b/static/css/styles.css @@ -0,0 +1,53 @@ +#content { + flex: 1 0 auto; +} + +#footer { + display: flex; + flex-direction: row; + justify-content: center; + flex-shrink: 0; + padding: 8px; + background-color: #4e4e4e; + font-weight: 600; + align-items: center; + transition: background-color 0.5s; + border-top: 1px solid #6e6e6e; +} + +#footer:hover { + background-color: #3e3e3e; + cursor: pointer; +} + +#footer img { + margin-left: 8px; + margin-right: 8px; +} + +html, +body { + height: 100%; +} + +body { + font-family: 'Fira Code', monospace !important; + color: #e1e1e1; + background-color: #1e1e1e; + margin: 0; + padding: 0; + text-align: center; + display: flex; + flex-direction: column; +} + +h1 { + font-weight: 900; + font-size: 5rem; + color: #f1f1f1; +} + +a { + text-decoration: none; + color: #f1f1f1; +} \ No newline at end of file diff --git a/static/fonts/firacode.ttf b/static/fonts/firacode.ttf new file mode 100644 index 0000000..f75b2a2 Binary files /dev/null and b/static/fonts/firacode.ttf differ diff --git a/static/html/index.html b/static/html/index.html new file mode 100644 index 0000000..8b4561d --- /dev/null +++ b/static/html/index.html @@ -0,0 +1,25 @@ + + + + go-http-server + + + + + +
+
+

go-http-server

+
+
+ + + + + + + \ No newline at end of file diff --git a/static/img/favicon.ico b/static/img/favicon.ico new file mode 100644 index 0000000..9b4a744 Binary files /dev/null and b/static/img/favicon.ico differ diff --git a/static/img/heart.svg b/static/img/heart.svg new file mode 100644 index 0000000..5abc1ab --- /dev/null +++ b/static/img/heart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..e69de29 diff --git a/systemd/go-http-server.env b/systemd/go-http-server.env new file mode 100644 index 0000000..1a27600 --- /dev/null +++ b/systemd/go-http-server.env @@ -0,0 +1,2 @@ +GO_HTTP_ADDRESS="0.0.0.0" +GO_HTTP_PORT="9000" \ No newline at end of file diff --git a/systemd/go-http-server.service b/systemd/go-http-server.service new file mode 100644 index 0000000..37dea17 --- /dev/null +++ b/systemd/go-http-server.service @@ -0,0 +1,12 @@ +[Unit] +Description=go-http-server + +[Service] +Type=simple +User=root +Group=root +EnvironmentFile=/opt/go-http-server/go-http-server.env +ExecStart=/opt/go-http-server/go-http-server + +[Install] +WantedBy=multi-user.target \ No newline at end of file