feat: allow homepage customization
Some checks reported warnings
arcad/arcast/pipeline/head This commit is unstable

This commit is contained in:
2024-04-26 12:10:59 +02:00
parent 35585959f5
commit 768393adc8
18 changed files with 376 additions and 243 deletions

View File

@ -0,0 +1,64 @@
{{ define "base" }}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/logo.png" />
<title>Ready to cast !</title>
<link rel="stylesheet" href="style.css" />
{{ block "head" .
}}{{
end
}}
</head>
<body>
<div class="container">
<div class="panel">
<div id="icon"></div>
{{ block "message" . }}
<h2 id="title" class="text-centered">Ready to cast !</h2>
{{ end }}
{{ block "info" . }}
<p><b>Instance ID</b></p>
<p class="text-small text-centered">
<code>{{ .ID }}</code>
</p>
<p><b>Addresses</b></p>
<ul class="text-italic text-small text-centered">
{{ $port := .Port }}
{{
range.IPs
}}
<li>
<code>{{ . }}:{{ $port }}</code>
</li>
{{
end
}}
</ul>
{{ end }}
{{if .Apps }}
{{ block "apps" . }}
<p><b>Apps</b></p>
<ul class="text-italic text-small text-centered">
{{ $tlsPort := .TLSPort }}
{{
range.IPs
}}
<li>
<a href="https://{{ . }}:{{ $tlsPort }}/apps">
https://{{ . }}:{{ $tlsPort }}/apps
</a>
</li>
{{
end
}}
</ul>
{{ end }}
{{ end }}
</div>
</div>
</body>
</html>
{{ end }}

View File

@ -0,0 +1,3 @@
{{ define "index" }}
{{ template "base" . }}
{{ end }}

BIN
pkg/server/embed/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

121
pkg/server/embed/style.css Normal file
View File

@ -0,0 +1,121 @@
html {
box-sizing: border-box;
font-size: 16px;
width: 100%;
height: 100%;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
width: 100%;
height: 100%;
background: rgb(76, 96, 188);
background: linear-gradient(
415deg,
rgba(4, 168, 243, 1),
rgb(76, 136, 188, 1),
rgba(76, 96, 188, 1),
rgb(115, 76, 188, 1),
rgb(87, 76, 188, 1)
);
background-size: 400% 400%;
animation: gradient 15s ease infinite;
}
@keyframes gradient {
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
*,
*:before,
*:after {
box-sizing: inherit;
}
ol,
ul {
list-style: none;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
margin: 0;
padding: 0;
font-weight: normal;
}
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.panel {
display: block;
background-color: #fff;
border-radius: 15px;
box-shadow: 10px 10px 10px #33333361;
position: relative;
padding: 50px 30px 30px 30px;
min-width: 50%;
color: #333;
}
#title {
margin: 10px 0px 20px 0px;
}
#icon {
width: 100px;
aspect-ratio: 1/1;
background-size: contain;
background-position: center center;
background-image: url("logo.png");
position: absolute;
left: 50%;
margin-left: -50px;
margin-top: -100px;
background-repeat: no-repeat;
}
.panel p,
.panel ul {
margin-top: 10px;
}
.text-centered {
text-align: center;
}
.text-italic {
font-style: italic;
}
.text-small {
font-size: 0.8em;
}
.mt {
margin-top: 1em;
display: block;
}

90
pkg/server/fs.go Normal file
View File

@ -0,0 +1,90 @@
package server
import (
"embed"
"html/template"
"io/fs"
"net/http"
"os"
"strings"
"forge.cadoles.com/arcad/arcast/pkg/network"
"github.com/dschmidt/go-layerfs"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
_ "embed"
)
var (
//go:embed embed/**
embedFS embed.FS
)
func (s *Server) initLayeredFS() error {
layers := make([]fs.FS, 0)
if s.upperLayerDir != "" {
upperLayer := os.DirFS(s.upperLayerDir)
layers = append(layers, upperLayer)
}
baseLayer, err := fs.Sub(embedFS, "embed")
if err != nil {
return errors.WithStack(err)
}
layers = append(layers, baseLayer)
s.layeredFS = layerfs.New(layers...)
return nil
}
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if strings.HasPrefix(path, "/_templates") {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
http.ServeFileFS(w, r, s.layeredFS, path)
}
func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
type templateData struct {
IPs []string
Port int
TLSPort int
ID string
Apps bool
}
ips, err := network.GetLANIPv4Addrs()
if err != nil {
logger.Error(r.Context(), "could not retrieve lan ip addresses", logger.CapturedE(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
d := templateData{
ID: s.instanceID,
IPs: ips,
Port: s.port,
TLSPort: s.tlsPort,
Apps: s.appsEnabled,
}
templates, err := template.New("").ParseFS(s.layeredFS, "_partials/*.gohtml", "_templates/*.gohtml")
if err != nil {
logger.Error(r.Context(), "could not parse template", logger.CapturedE(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
if err := templates.ExecuteTemplate(w, "index", d); err != nil {
logger.Error(r.Context(), "could not render index page", logger.CapturedE(errors.WithStack(err)))
}
}

View File

@ -4,7 +4,6 @@ import (
"context"
"crypto/tls"
"fmt"
"html/template"
"net"
"net/http"
"strconv"
@ -14,25 +13,8 @@ import (
"github.com/go-chi/cors"
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
_ "embed"
)
var (
//go:embed templates/idle.html.gotmpl
rawIdleTemplate []byte
idleTemplate *template.Template
)
func init() {
tmpl, err := template.New("").Parse(string(rawIdleTemplate))
if err != nil {
panic(errors.Wrap(err, "could not parse idle template"))
}
idleTemplate = tmpl
}
func (s *Server) startWebServers(ctx context.Context) error {
router := chi.NewRouter()
@ -50,7 +32,6 @@ func (s *Server) startWebServers(ctx context.Context) error {
}))
}
router.Get("/", s.handleHome)
router.Get("/api/v1/info", s.handleInfo)
router.Post("/api/v1/cast", s.handleCast)
router.Delete("/api/v1/cast", s.handleReset)
@ -63,6 +44,9 @@ func (s *Server) startWebServers(ctx context.Context) error {
router.Handle("/api/v1/broadcast/{channelID}", http.HandlerFunc(s.handleBroadcast))
}
router.Get("/", s.handleIndex)
router.Get("/*", s.handleStatic)
if err := s.startHTTPServer(ctx, router); err != nil {
return errors.WithStack(err)
}
@ -193,32 +177,3 @@ func (s *Server) getAllowedOrigins() ([]string, error) {
return allowedOrigins, nil
}
func (s *Server) handleHome(w http.ResponseWriter, r *http.Request) {
type templateData struct {
IPs []string
Port int
TLSPort int
ID string
Apps bool
}
ips, err := network.GetLANIPv4Addrs()
if err != nil {
logger.Error(r.Context(), "could not retrieve lan ip addresses", logger.CapturedE(errors.WithStack(err)))
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
d := templateData{
ID: s.instanceID,
IPs: ips,
Port: s.port,
TLSPort: s.tlsPort,
Apps: s.appsEnabled,
}
if err := idleTemplate.Execute(w, d); err != nil {
logger.Error(r.Context(), "could not render idle page", logger.CapturedE(errors.WithStack(err)))
}
}

View File

@ -28,6 +28,7 @@ type Options struct {
DefaultApp string
AllowedOrigins []string
Apps []App
UpperLayerDir string
}
type OptionFunc func(opts *Options)
@ -42,6 +43,7 @@ func NewOptions(funcs ...OptionFunc) *Options {
DefaultApp: "",
AllowedOrigins: make([]string, 0),
Apps: make([]App, 0),
UpperLayerDir: "",
}
for _, fn := range funcs {
@ -105,6 +107,12 @@ func WithServiceDiscoveryEnabled(enabled bool) OptionFunc {
}
}
func WithUpperLayerDir(dir string) OptionFunc {
return func(opts *Options) {
opts.UpperLayerDir = dir
}
}
func NewRandomInstanceID() string {
return newRandomInstanceID()
}

View File

@ -3,6 +3,7 @@ package server
import (
"context"
"crypto/tls"
"io/fs"
"forge.cadoles.com/arcad/arcast/pkg/browser"
"github.com/gorilla/websocket"
@ -32,10 +33,19 @@ type Server struct {
ctx context.Context
cancel context.CancelFunc
upgrader websocket.Upgrader
layeredFS fs.FS
upperLayerDir string
}
func (s *Server) Start() error {
serverCtx, cancelServer := context.WithCancel(context.Background())
ctx := context.Background()
if err := s.initLayeredFS(); err != nil {
return errors.WithStack(err)
}
serverCtx, cancelServer := context.WithCancel(ctx)
s.cancel = cancelServer
s.ctx = serverCtx
@ -92,6 +102,7 @@ func New(browser browser.Browser, funcs ...OptionFunc) *Server {
defaultApp: opts.DefaultApp,
apps: opts.Apps,
serviceDiscoveryEnabled: opts.EnableServiceDiscovery,
upperLayerDir: opts.UpperLayerDir,
}
server.upgrader = websocket.Upgrader{

File diff suppressed because one or more lines are too long