commit c79f191be541a29dfafc0dd0b9799641918c79f1
parent cf473e898b4b52c2e21f840ef20f891b8b115878
Author: weetat <wtenison@protonmail.com>
Date: Wed, 12 Feb 2025 20:50:31 +0000
Add HTTPS server with YAML config and templates
Read filesystem and server settings from the YAML files in sys/, check
that the TLS cert and key exist, and serve /web/ as static files. The /
handler collects the .html files under the web root and renders
index.html from them.
The run target now uses sudo so the server can bind a privileged port.
Diffstat:
5 files changed, 185 insertions(+), 4 deletions(-)
diff --git a/Makefile b/Makefile
@@ -5,6 +5,6 @@ build:
go build -o ./bin/$(PROJECT_NAME) ./src/$(PROJECT_NAME)
run:
- ./bin/$(PROJECT_NAME)
+ sudo ./bin/$(PROJECT_NAME)
all: build run
\ No newline at end of file
diff --git a/go.mod b/go.mod
@@ -2,4 +2,4 @@ module miki
go 1.23.2
-require github.com/goccy/go-yaml v1.15.17 // indirect
+require github.com/goccy/go-yaml v1.15.17
diff --git a/src/miki/miki.go b/src/miki/miki.go
@@ -1,5 +1,175 @@
package main
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+ "regexp"
+
+ "github.com/goccy/go-yaml"
+)
+
+type ResourceSignature struct {
+ directory string // The root directoy of the resource.
+ dependencyDepth int // The folder search depth of the resource. -1=inf, 0=single, >0=n-depth
+ dependencyPattern *regexp.Regexp // The pattern of valid resource dependency files.
+}
+
+func MakeResourceSig(dir string, depth int, pattern *regexp.Regexp) (ResourceSignature, error) {
+ if dir == "" || depth < -1 || pattern == nil {
+ return ResourceSignature{}, fmt.Errorf("__ERROR:\tResource:Make(%s, %d, %s)", dir, depth, pattern)
+ }
+
+ if _, _err := os.Stat(dir); _err != nil {
+ return ResourceSignature{}, _err
+ }
+
+ return ResourceSignature{dir, depth, pattern}, nil
+}
+
+type ResourcePackage struct {
+ directory string // The root directory of the resource.
+ dependencyFiles []string // The set of valid resource files.
+ dependencyData IResourceData // Any relevant resource data.
+}
+
+func MakeResourcePkg(resSig ResourceSignature) (ResourcePackage, error) {
+
+ // Check and collect resource signature directory files.
+ _files, _err := os.ReadDir(resSig.directory)
+ if _err != nil {
+ return ResourcePackage{}, _err
+ }
+
+ _resPkg := ResourcePackage{
+ directory: resSig.directory,
+ dependencyFiles: []string{},
+ dependencyData: nil,
+ }
+
+ _resFileDirDeps := []string{}
+
+ // Collete all valid files and sub directory files.
+ for _, _f := range _files {
+
+ if _f.IsDir() && (resSig.dependencyDepth > 0 || resSig.dependencyDepth == -1) {
+ // Recurse into valid directory files.
+
+ _resDirSig, _err := MakeResourceSig(_f.Name(), resSig.dependencyDepth, resSig.dependencyPattern)
+ if _err != nil {
+ return ResourcePackage{}, _err
+ }
+
+ _resDirPkg, _err := MakeResourcePkg(_resDirSig)
+ if _err != nil {
+ return ResourcePackage{}, _err
+ }
+
+ _resFileDirDeps = append(_resFileDirDeps, _resDirPkg.dependencyFiles...)
+
+ } else if resSig.dependencyPattern != nil && resSig.dependencyPattern.MatchString(_f.Name()) {
+ // Add files that match pattern to dependency list.
+ _resPkg.dependencyFiles = append(_resPkg.dependencyFiles, filepath.Join(_resPkg.directory, _f.Name()))
+ } else if resSig.dependencyPattern == nil {
+ // Add any file in directory to dependency list.
+ _resPkg.dependencyFiles = append(_resPkg.dependencyFiles, filepath.Join(_resPkg.directory, _f.Name()))
+ }
+ }
+
+ // Add any sub directory file dependencies.
+ _resPkg.dependencyFiles = append(_resPkg.dependencyFiles, _resFileDirDeps...)
+
+ return _resPkg, nil
+}
+
+type IResourceData interface {
+}
+
func main() {
+ fmt.Println("MIKI")
+
+ _fsBuffer := bytes.NewBufferString("filesysteminfo: *fs\n")
+
+ var _fs struct {
+ FileSystemInfo struct {
+ RootDir string
+ }
+ }
+
+ decoder := yaml.NewDecoder(_fsBuffer, yaml.ReferenceDirs("sys"))
+ if err := decoder.Decode(&_fs); err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Printf("%+v\n", _fs)
+
+ _siBuffer := bytes.NewBufferString("serverinfo: *si\n")
+
+ var _si struct {
+ ServerInfo struct {
+ Port string
+ SslPath string
+ CertFile string
+ KeyFile string
+ }
+ }
+
+ decoder = yaml.NewDecoder(_siBuffer, yaml.ReferenceDirs("sys"))
+ if err := decoder.Decode(&_si); err != nil {
+ fmt.Println(err.Error())
+ return
+ }
+ fmt.Printf("%+v\n", _si)
+
+ _tls_cert_path := filepath.Join(_si.ServerInfo.SslPath, _si.ServerInfo.CertFile)
+ if _, err := os.Stat(_tls_cert_path); err != nil {
+ log.Panicf("__PANIC\tmain:%s", err)
+ }
+
+ _tls_key_path := filepath.Join(_si.ServerInfo.SslPath, _si.ServerInfo.KeyFile)
+ if _, err := os.Stat(_tls_key_path); err != nil {
+ log.Panicf("__PANIC\tmain:%s", err)
+ }
+
+ _mux := http.NewServeMux()
+ _mux.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("/home/miki/"))))
+ _mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+
+ _r, _err := MakeResourceSig(_fs.FileSystemInfo.RootDir+"/", -1, regexp.MustCompile("^([a-zA-Z0-9.]+).html$"))
+ if _err != nil {
+ fmt.Printf("_ERROR\tmain():HandleFunc(\"/\"):MakeResourceSig():%s\n", _err)
+ http.NotFound(w, r)
+ return
+ }
+
+ _rPkg, _err := MakeResourcePkg(_r)
+ if _err != nil {
+ fmt.Printf("_ERROR\tmain():HandleFunc(\"/\"):MakeResourcePkg():%s\n", _err)
+ http.NotFound(w, r)
+ return
+ }
+
+ _tmpl, _err := template.ParseFiles(_rPkg.dependencyFiles...)
+ if _err != nil {
+ fmt.Printf("_ERROR\tmain():HandleFunc(\"/\"):MakeResourcePkg():%s\n", _err)
+ http.Error(w, "_ERROR:HandleFunc(\"/\"):template.ParseFiles()", 500)
+ return
+ }
+
+ if _err = _tmpl.ExecuteTemplate(w, "index.html", _rPkg.dependencyData); _err != nil {
+ fmt.Printf("_ERROR\tmain():HandleFunc(\"/\"):MakeResourcePkg():%s\n", _err)
+ http.Error(w, "_ERROR:HandleFunc(\"/\"):template.ExecuteTemplate()", 500)
+ return
+ }
+
+ fmt.Printf("INFO:\tmain():mux:Resource(%+v)\n", _rPkg)
+ })
-}
-\ No newline at end of file
+ if _err := http.ListenAndServeTLS(":"+_si.ServerInfo.Port, _tls_cert_path, _tls_key_path, _mux); _err != nil {
+ fmt.Printf("_ERROR\tmain():ListenAndServeTLS():%s\n", _err)
+ }
+}
diff --git a/web/blank.html b/web/blank.html
diff --git a/web/index.html b/web/index.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Miki</title>
+ <meta name="viewport" charset="utf-8" content="width=device-width, initial-scale=1.0"/>
+ </head>
+
+ <body>
+ <h1>HELLO WORLD</h1>
+ </body>
+</html>
+\ No newline at end of file