feat: add http server with a page serving service informations

This commit is contained in:
2023-09-20 21:54:08 -06:00
parent 56d7174b96
commit fdaffca43f
23 changed files with 605 additions and 135 deletions

1
http/assets/bulma-0.9.4.min.css vendored Normal file

File diff suppressed because one or more lines are too long

29
http/options.go Normal file
View File

@ -0,0 +1,29 @@
package http
import "log"
type Options struct {
Logger func(message string, args ...any)
CustomDir string `env:"CUSTOM_DIR"`
}
type OptionFunc func(*Options)
func DefaultOptions() *Options {
return &Options{
Logger: log.Printf,
CustomDir: "",
}
}
func WithLogger(logger func(message string, args ...any)) func(*Options) {
return func(opts *Options) {
opts.Logger = logger
}
}
func WithCustomDir(customDir string) func(*Options) {
return func(opts *Options) {
opts.CustomDir = customDir
}
}

130
http/server.go Normal file
View File

@ -0,0 +1,130 @@
package http
import (
"bytes"
"embed"
"html/template"
"io"
"io/fs"
"net"
"net/http"
"os"
"path/filepath"
_ "embed"
"github.com/dschmidt/go-layerfs"
"github.com/pkg/errors"
)
//go:embed templates
var embeddedTemplates embed.FS
//go:embed assets
var embeddedAssets embed.FS
type Server struct {
http *http.Server
opts *Options
templates template.Template
}
func (s *Server) serveHomepage(w http.ResponseWriter, r *http.Request) {
data := struct {
Title string
}{
Title: "Rebound",
}
s.renderTemplate(w, "index", data)
}
func (s *Server) Serve(l net.Listener) error {
templatesFilesystem, err := s.getCustomizedFilesystem(embeddedTemplates)
if err != nil {
return errors.WithStack(err)
}
if err := s.parseTemplates(templatesFilesystem); err != nil {
return errors.WithStack(err)
}
assetsFilesystem, err := s.getCustomizedFilesystem(embeddedAssets)
if err != nil {
return errors.WithStack(err)
}
mux := http.NewServeMux()
mux.Handle("/assets/", http.FileServer(http.FS(assetsFilesystem)))
mux.HandleFunc("/", s.serveHomepage)
httpServer := &http.Server{
Handler: mux,
}
s.http = httpServer
if err := s.http.Serve(l); err != nil {
return errors.WithStack(err)
}
return nil
}
func (s *Server) parseTemplates(fs fs.FS) error {
templates, err := template.ParseFS(fs, "templates/*.html")
if err != nil {
return errors.WithStack(err)
}
s.templates = *templates
return nil
}
func (s *Server) renderTemplate(w http.ResponseWriter, name string, data any) {
var buff bytes.Buffer
if err := s.templates.ExecuteTemplate(&buff, name, data); err != nil {
s.log("[ERROR] %+s", errors.WithStack(err))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if _, err := io.Copy(w, &buff); err != nil {
s.log("[ERROR] %+s", errors.WithStack(err))
}
}
func (s *Server) log(message string, args ...any) {
s.opts.Logger(message, args...)
}
func (s *Server) getCustomizedFilesystem(base fs.FS) (fs.FS, error) {
filesystems := []fs.FS{}
if s.opts.CustomDir != "" {
absPath, err := filepath.Abs(s.opts.CustomDir)
if err != nil {
return nil, errors.WithStack(err)
}
filesystems = append(filesystems, os.DirFS(absPath))
}
filesystems = append(filesystems, base)
return layerfs.New(filesystems...), nil
}
func NewServer(funcs ...OptionFunc) *Server {
opts := DefaultOptions()
for _, fn := range funcs {
fn(opts)
}
return &Server{
opts: opts,
}
}

View File

@ -0,0 +1 @@
{{ define "advertising" }}{{ end }}

View File

@ -0,0 +1,9 @@
{{ define "footer" }}
<footer class="footer">
<div class="container">
<div class="content has-text-centered">
Ce service est propulsé par Rebound, un logiciel libre diffusé sous licence <a href="#">AGPL-3.0</a>.
</div>
</div>
</footer>
{{ end }}

6
http/templates/head.html Normal file
View File

@ -0,0 +1,6 @@
{{ define "head" }}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ .Title }}</title>
<link rel="stylesheet" href="assets/bulma-0.9.4.min.css">
{{ end }}

31
http/templates/index.html Normal file
View File

@ -0,0 +1,31 @@
{{ define "index" }}
<!DOCTYPE html>
<html>
<head>
{{ template "head" }}
</head>
<body>
<section class="section">
<div class="container">
<h1 class="title is-size-1">
Besoin de vous échapper ?
</h1>
<p class="subtitle is-size-3">
Bienvenue sur <strong>Rebound</strong>!
</p>
<div class="content">
<p>Rebound est un serveur SSH permettant de créer des tunnels TCP/IP éphémères et privés entre 2 machines positionnées
derrière un <abbr title="Network Address Traversal">NAT</abbr>.</p>
<p>Pour l'utiliser <strong>un simple client SSH suffit !</strong></p>
<pre class="has-background-dark has-text-white-ter is-family-monospace">ssh -R 0:127.0.0.1:<span class="has-text-info">&lt;port&gt;</span> rebound@rebound.cadol.es</pre>
<p class="is-italic"><span class="has-text-info">&lt;port&gt;</span> est à remplacer par le port du service
s'exécutant sur votre machine en local.</span>
<p>Une fois connecté, suivez les instructions. 😉</p>
</div>
</div>
</section>
{{ template "advertising" . }}
{{ template "footer" . }}
</body>
</html>
{{ end }}