diff options
| -rw-r--r-- | _www/index.tmpl | 15 | ||||
| -rw-r--r-- | cmd/lilin/main.go | 46 | ||||
| -rw-r--r-- | lilin.go | 8 | ||||
| -rw-r--r-- | lilin_test.go | 14 | ||||
| -rw-r--r-- | reports.go | 10 | ||||
| -rw-r--r-- | server.go | 67 | ||||
| -rw-r--r-- | server_options.go | 4 | ||||
| -rw-r--r-- | service_report.go | 11 | ||||
| -rw-r--r-- | worker.go | 13 |
9 files changed, 138 insertions, 50 deletions
diff --git a/_www/index.tmpl b/_www/index.tmpl new file mode 100644 index 0000000..250843d --- /dev/null +++ b/_www/index.tmpl @@ -0,0 +1,15 @@ +<!doctype html> +<html lang="en-GB"> + <head> + <meta charset="utf-8" /> + <meta name="viewport" content="width=device-width" /> + <title>{{.Title}}</title> + </head> + <body> + {{range .Services}} + <div> + <div>{{.Name}}</div> + </div> + {{end}} + </body> +</html> diff --git a/cmd/lilin/main.go b/cmd/lilin/main.go new file mode 100644 index 0000000..21c40ec --- /dev/null +++ b/cmd/lilin/main.go @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-only + +package main + +import ( + "context" + "log" + "os" + "os/signal" + "time" + + "git.sr.ht/~shulhan/lilin" +) + +func main() { + var serverOpts = lilin.ServerOptions{ + BaseDir: `testdata`, + IsDevelopment: true, + } + + var server *lilin.Server + var err error + server, err = lilin.NewServer(serverOpts) + if err != nil { + log.Fatal(err) + } + + go func() { + err = server.ListenAndServe() + if err != nil { + log.Fatal(err) + } + }() + + var sigq = make(chan os.Signal, 1) + signal.Notify(sigq, os.Interrupt) + <-sigq + + var ctx = context.Background() + var timeout = 5 * time.Second + var cancelf context.CancelFunc + ctx, cancelf = context.WithTimeout(ctx, timeout) + defer cancelf() + _ = server.Shutdown(ctx) +} @@ -3,7 +3,13 @@ package lilin -import "time" +import ( + "embed" + "time" +) // defTimeout define default timeout for service and client connection. const defTimeout = 5 * time.Second + +//go:embed _www +var wwwFS embed.FS diff --git a/lilin_test.go b/lilin_test.go index fe951a0..3e1899d 100644 --- a/lilin_test.go +++ b/lilin_test.go @@ -14,7 +14,6 @@ import ( "git.sr.ht/~shulhan/lilin" "git.sr.ht/~shulhan/lilin/internal" "git.sr.ht/~shulhan/pakakeh.go/lib/net" - "git.sr.ht/~shulhan/pakakeh.go/lib/test" ) var client *lilin.Client @@ -104,16 +103,3 @@ func dummyHTTPService() { log.Fatal(err) } } - -func TestServer_handleServicesSummary(t *testing.T) { - var gotSummary []lilin.Service - var err error - - gotSummary, err = client.ServicesSummary() - if err != nil { - t.Fatal(err) - } - - var expSummary = []lilin.Service{{}, {}, {}} - test.Assert(t, `ServicesSummary`, expSummary, gotSummary) -} diff --git a/reports.go b/reports.go new file mode 100644 index 0000000..1ffbd43 --- /dev/null +++ b/reports.go @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-only + +package lilin + +// Reports contains the report for all services. +type Reports struct { + Services map[string]ServiceReport + Title string +} @@ -4,13 +4,13 @@ package lilin import ( + "bytes" "context" - "encoding/json" "fmt" + "html/template" + "log" "net/http" "time" - - libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http" ) const ( @@ -25,13 +25,21 @@ type Server struct { worker *worker Options ServerOptions + Reports Reports + + pageIndexTmpl *template.Template + pageIndexBody bytes.Buffer } +// NewServer create new server to serve the content of _www and HTTP APIs. func NewServer(opts ServerOptions) (srv *Server, err error) { var logp = `NewServer` srv = &Server{ Options: opts, + Reports: Reports{ + Services: map[string]ServiceReport{}, + }, } err = srv.Options.init() @@ -44,8 +52,23 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { return nil, fmt.Errorf(`%s: %w`, logp, err) } + for name := range srv.worker.Services { + srv.Reports.Services[name] = ServiceReport{ + Name: name, + } + } + + srv.pageIndexTmpl, err = template.ParseFS(wwwFS, `_www/index.tmpl`) + if err != nil { + return nil, err + } + err = srv.pageIndexTmpl.Execute(&srv.pageIndexBody, &srv.Reports) + if err != nil { + return nil, err + } + var mux = http.NewServeMux() - mux.HandleFunc(`GET `+pathAPIServicesSummary, srv.handleServicesSummary) + mux.Handle(`GET /`, srv) srv.httpd = &http.Server{ Addr: srv.Options.Address, @@ -58,6 +81,8 @@ func NewServer(opts ServerOptions) (srv *Server, err error) { // ListenAndServe start handling request on incoming connections. func (srv *Server) ListenAndServe() (err error) { + log.Printf(`lilin: starting HTTP server at %s`, srv.Options.Address) + err = srv.httpd.ListenAndServe() if err != nil { return fmt.Errorf(`ListenAndServe: %w`, err) @@ -75,27 +100,25 @@ func (srv *Server) Shutdown(ctx context.Context) (err error) { return nil } -func (srv *Server) handleServicesSummary(resw http.ResponseWriter, req *http.Request) { - var summary = srv.worker.summary() - var resp = libhttp.EndpointResponse{ - Data: summary, - } - var respBody []byte +func (srv *Server) ServeHTTP(resw http.ResponseWriter, req *http.Request) { var err error - respBody, err = json.Marshal(resp) - if err != nil { - goto fail + switch req.URL.Path { + case `/`, `/index.html`: + _, err = resw.Write(srv.pageIndexBody.Bytes()) + if err != nil { + srv.internalError(resw, err) + } + return } - resw.WriteHeader(http.StatusOK) - resw.Write(respBody) - return -fail: + http.Redirect(resw, req, `/`, http.StatusSeeOther) +} + +func (srv *Server) internalError(resw http.ResponseWriter, err error) { resw.WriteHeader(http.StatusInternalServerError) - respBody = []byte(fmt.Sprintf(responseTemplateJSON, - http.StatusInternalServerError, - `ERR_INTERNAL`, - err.Error())) - resw.Write(respBody) + _, err = resw.Write([]byte(err.Error())) + if err != nil { + log.Println(`internalError:`, err.Error()) + } } diff --git a/server_options.go b/server_options.go index 5197ffd..4a033fc 100644 --- a/server_options.go +++ b/server_options.go @@ -24,6 +24,10 @@ type ServerOptions struct { // The address to listen for HTTP server and APIs. Address string `ini:"server::address"` + + // IsDevelopment run the server in development mode with direct access + // to file system in _www instead of using [embed.FS]. + IsDevelopment bool } // init initialize the server by reading the configuration from diff --git a/service_report.go b/service_report.go new file mode 100644 index 0000000..2627bc2 --- /dev/null +++ b/service_report.go @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 M. Shulhan <ms@kilabit.info> +// SPDX-License-Identifier: GPL-3.0-only + +package lilin + +// ServiceReport contains the scan report for single service. +type ServiceReport struct { + Name string + Last ScanReport + History []ScanReport +} @@ -4,10 +4,8 @@ package lilin import ( - "maps" "os" "path/filepath" - "slices" "strings" "sync" @@ -85,14 +83,3 @@ func (wrk *worker) loadServiceDir(configDir string) (err error) { } return nil } - -// summary return all services status ordered by name. -func (wrk *worker) summary() (list []Service) { - wrk.Lock() - var keys = slices.Sorted(maps.Keys(wrk.Services)) - for _, name := range keys { - list = append(list, *wrk.Services[name]) - } - wrk.Unlock() - return list -} |
