mirror of
https://gitlab.crans.org/nounous/ghostream.git
synced 2024-12-22 13:52:19 +00:00
Restructure configuration
This commit is contained in:
parent
c799a5b613
commit
5ac336393b
11
auth/auth.go
Normal file
11
auth/auth.go
Normal file
@ -0,0 +1,11 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"gitlab.crans.org/nounous/ghostream/auth/ldap"
|
||||
)
|
||||
|
||||
// Options holds web package configuration
|
||||
type Options struct {
|
||||
Backend string
|
||||
LDAP ldap.Options
|
||||
}
|
7
auth/ldap/ldap.go
Normal file
7
auth/ldap/ldap.go
Normal file
@ -0,0 +1,7 @@
|
||||
package ldap
|
||||
|
||||
// Options holds web package configuration
|
||||
type Options struct {
|
||||
URI string
|
||||
UserDn string
|
||||
}
|
1
go.mod
1
go.mod
@ -5,4 +5,5 @@ go 1.13
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.7.1
|
||||
github.com/spf13/viper v1.7.1
|
||||
honnef.co/go/tools v0.0.1-2019.2.3
|
||||
)
|
||||
|
3
go.sum
3
go.sum
@ -11,6 +11,7 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
@ -296,6 +297,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -345,5 +347,6 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Cr@ns <roots@crans.org>
|
||||
* Authors : Alexandre Iooss <erdnaxe@crans.org>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Config holds app configuration
|
||||
type Config struct {
|
||||
AuthBackend string
|
||||
LDAP struct {
|
||||
URI string
|
||||
UserDn string
|
||||
}
|
||||
Prometheus struct {
|
||||
ListenAddress string
|
||||
}
|
||||
Site struct {
|
||||
ListenAddress string
|
||||
Name string
|
||||
Hostname string
|
||||
Favicon string
|
||||
WidgetURL string
|
||||
}
|
||||
}
|
||||
|
||||
// New configuration
|
||||
func New() (*Config, error) {
|
||||
// Load configuration from environnement variables
|
||||
// Replace "." to "_" for nested structs
|
||||
// e.g. GHOSTREAM_LDAP_URI will apply to Config.LDAP.URI
|
||||
viper.SetEnvPrefix("ghostream")
|
||||
replacer := strings.NewReplacer(".", "_")
|
||||
viper.SetEnvKeyReplacer(replacer)
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// Load configuration file if exists
|
||||
viper.SetConfigName("ghostream")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("$HOME/.ghostream")
|
||||
viper.AddConfigPath("/etc/ghostream")
|
||||
viper.AddConfigPath(".")
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
// Config file not found, ignore and use defaults
|
||||
log.Print(err)
|
||||
} else {
|
||||
// Config file was found but another error was produced
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
// Config loaded
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
}
|
||||
|
||||
// Define configuration default values
|
||||
viper.SetDefault("AuthBackend", "LDAP")
|
||||
viper.SetDefault("LDAP.URI", "ldap://127.0.0.1:389")
|
||||
viper.SetDefault("LDAP.UserDn", "cn=users,dc=example,dc=com")
|
||||
viper.SetDefault("Prometheus.ListenAddress", "0.0.0.0:2112")
|
||||
viper.SetDefault("Site.ListenAddress", "127.0.0.1:8080")
|
||||
viper.SetDefault("Site.Name", "Ghostream")
|
||||
viper.SetDefault("Site.Hostname", "localhost")
|
||||
viper.SetDefault("Site.Favicon", "/favicon.ico")
|
||||
|
||||
config := &Config{}
|
||||
err := viper.Unmarshal(config)
|
||||
return config, err
|
||||
}
|
@ -7,9 +7,13 @@ import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"gitlab.crans.org/nounous/ghostream/internal/config"
|
||||
)
|
||||
|
||||
// Options holds web package configuration
|
||||
type Options struct {
|
||||
ListenAddress string
|
||||
}
|
||||
|
||||
var (
|
||||
// ViewerServed is the total amount of viewer page served
|
||||
ViewerServed = promauto.NewCounter(prometheus.CounterOpts{
|
||||
@ -19,9 +23,9 @@ var (
|
||||
)
|
||||
|
||||
// ServeHTTP server that expose prometheus metrics
|
||||
func ServeHTTP(cfg *config.Config) {
|
||||
func ServeHTTP(cfg *Options) {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/metrics", promhttp.Handler())
|
||||
log.Printf("Monitoring listening on http://%s/", cfg.Prometheus.ListenAddress)
|
||||
log.Fatal(http.ListenAndServe(cfg.Prometheus.ListenAddress, mux))
|
||||
log.Printf("Monitoring HTTP server listening on %s", cfg.ListenAddress)
|
||||
log.Fatal(http.ListenAndServe(cfg.ListenAddress, mux))
|
||||
}
|
||||
|
58
main.go
58
main.go
@ -2,27 +2,73 @@ package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"gitlab.crans.org/nounous/ghostream/internal/config"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.crans.org/nounous/ghostream/auth"
|
||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||
"gitlab.crans.org/nounous/ghostream/web"
|
||||
)
|
||||
|
||||
func loadConfiguration() {
|
||||
// Load configuration from environnement variables
|
||||
// Replace "." to "_" for nested structs
|
||||
// e.g. GHOSTREAM_LDAP_URI will apply to Config.LDAP.URI
|
||||
viper.SetEnvPrefix("ghostream")
|
||||
replacer := strings.NewReplacer(".", "_")
|
||||
viper.SetEnvKeyReplacer(replacer)
|
||||
viper.AutomaticEnv()
|
||||
|
||||
// Load configuration file if exists
|
||||
viper.SetConfigName("ghostream")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath("$HOME/.ghostream")
|
||||
viper.AddConfigPath("/etc/ghostream")
|
||||
viper.AddConfigPath(".")
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||
// Config file not found, ignore and use defaults
|
||||
log.Print(err)
|
||||
} else {
|
||||
// Config file was found but another error was produced
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
// Config loaded
|
||||
log.Printf("Using config file: %s", viper.ConfigFileUsed())
|
||||
}
|
||||
|
||||
// Define configuration default values
|
||||
viper.SetDefault("Auth.Backend", "LDAP")
|
||||
viper.SetDefault("Auth.LDAP.URI", "ldap://127.0.0.1:389")
|
||||
viper.SetDefault("Auth.LDAP.UserDn", "cn=users,dc=example,dc=com")
|
||||
viper.SetDefault("Monitoring.ListenAddress", ":2112")
|
||||
viper.SetDefault("Web.ListenAddress", "127.0.0.1:8080")
|
||||
viper.SetDefault("Web.Name", "Ghostream")
|
||||
viper.SetDefault("Web.Hostname", "localhost")
|
||||
viper.SetDefault("Web.Favicon", "/favicon.ico")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Load configuration
|
||||
cfg, err := config.New()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
loadConfiguration()
|
||||
cfg := struct {
|
||||
Auth auth.Options
|
||||
Monitoring monitoring.Options
|
||||
Web web.Options
|
||||
}{}
|
||||
if err := viper.Unmarshal(&cfg); err != nil {
|
||||
log.Fatalln("Failed to load settings", err)
|
||||
}
|
||||
|
||||
// Start web server routine
|
||||
go func() {
|
||||
web.ServeHTTP(cfg)
|
||||
web.ServeHTTP(&cfg.Web)
|
||||
}()
|
||||
|
||||
// Start monitoring server routine
|
||||
go func() {
|
||||
monitoring.ServeHTTP(cfg)
|
||||
monitoring.ServeHTTP(&cfg.Monitoring)
|
||||
}()
|
||||
|
||||
// Wait for routines
|
||||
|
@ -3,7 +3,7 @@
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{if .Path}}{{.Path}} - {{end}}{{.Cfg.Site.Name}}</title>
|
||||
<title>{{if .Path}}{{.Path}} - {{end}}{{.Cfg.Name}}</title>
|
||||
<link rel="stylesheet" href="static/style.css">
|
||||
<link rel="shortcut icon" href="static/favicon.ico">
|
||||
</head>
|
||||
|
@ -1,8 +1,8 @@
|
||||
{{define "index"}}
|
||||
<div style="max-width:720px;margin:0 auto; padding: 1rem">
|
||||
<h1>{{.Cfg.Site.Name}}</h1>
|
||||
<h1>{{.Cfg.Name}}</h1>
|
||||
<p>
|
||||
{{.Cfg.Site.Name}} est un service maintenu par le
|
||||
{{.Cfg.Name}} est un service maintenu par le
|
||||
<a href="https://crans.org/">Crans</a> permettant de diffuser
|
||||
un contenu vidéo. Il a pour but d'être utilisé pour diffuser
|
||||
des séminaires ou évènements.
|
||||
@ -21,7 +21,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<b>Serveur :</b>
|
||||
<code>rtmps://{{.Cfg.Site.Hostname}}:1935/stream</code>,
|
||||
<code>rtmps://{{.Cfg.Hostname}}:1935/stream</code>,
|
||||
</li>
|
||||
<li>
|
||||
<b>Clé de stream :</b>
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
<p>
|
||||
Votre stream sera alors disponible sur
|
||||
<code>https://{{.Cfg.Site.Hostname}}/IDENTIFIANT</code>.
|
||||
<code>https://{{.Cfg.Hostname}}/IDENTIFIANT</code>.
|
||||
</p>
|
||||
|
||||
<h3>Avec FFmpeg</h3>
|
||||
@ -49,7 +49,7 @@
|
||||
<code>
|
||||
ffmpeg -re -i mavideo.webm -vcodec libx264 -vprofile baseline
|
||||
-acodec aac -strict -2 -f flv
|
||||
rtmps://{{.Cfg.Site.Hostname}}:1935/stream/IDENTIFIANT?pass=MOT_DE_PASSE
|
||||
rtmps://{{.Cfg.Hostname}}:1935/stream/IDENTIFIANT?pass=MOT_DE_PASSE
|
||||
</code>
|
||||
</p>
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
<path fill-rule="evenodd" d="M1.5 13A1.5 1.5 0 0 0 3 14.5h8a1.5 1.5 0 0 0 1.5-1.5V9a.5.5 0 0 0-1 0v4a.5.5 0 0 1-.5.5H3a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 0 0-1H3A1.5 1.5 0 0 0 1.5 5v8zm7-11a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0V2.5H9a.5.5 0 0 1-.5-.5z"/>
|
||||
<path fill-rule="evenodd" d="M14.354 1.646a.5.5 0 0 1 0 .708l-8 8a.5.5 0 0 1-.708-.708l8-8a.5.5 0 0 1 .708 0z"/>
|
||||
</svg>
|
||||
<code>rtmps://{{.Cfg.Site.Hostname}}:1935/play/{{.Path}}</code>
|
||||
<code>rtmps://{{.Cfg.Hostname}}:1935/play/{{.Path}}</code>
|
||||
<a href="#" id="chatToggle" title="Cacher/Afficher le chat">»</a>
|
||||
</p>
|
||||
</div>
|
||||
@ -43,7 +43,7 @@ player = OvenPlayer.create("player", {
|
||||
expandFullScreenUI: true,
|
||||
sources: [
|
||||
{
|
||||
"file": "wss://{{.Cfg.Site.Hostname}}/play/{{.Path}}",
|
||||
"file": "wss://{{.Cfg.Hostname}}/play/{{.Path}}",
|
||||
"type": "webrtc",
|
||||
"label": "WebRTC Source"
|
||||
}
|
||||
|
26
web/web.go
26
web/web.go
@ -6,19 +6,27 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"gitlab.crans.org/nounous/ghostream/internal/config"
|
||||
"gitlab.crans.org/nounous/ghostream/internal/monitoring"
|
||||
)
|
||||
|
||||
// Options holds web package configuration
|
||||
type Options struct {
|
||||
ListenAddress string
|
||||
Name string
|
||||
Hostname string
|
||||
Favicon string
|
||||
WidgetURL string
|
||||
}
|
||||
|
||||
// Preload templates
|
||||
var templates = template.Must(template.ParseGlob("web/template/*.tmpl"))
|
||||
|
||||
// Handle site index and viewer pages
|
||||
func viewerHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config) {
|
||||
func viewerHandler(w http.ResponseWriter, r *http.Request, cfg *Options) {
|
||||
// Data for template
|
||||
data := struct {
|
||||
Path string
|
||||
Cfg *config.Config
|
||||
Cfg *Options
|
||||
}{Path: r.URL.Path[1:], Cfg: cfg}
|
||||
|
||||
// FIXME validation on path: https://golang.org/doc/articles/wiki/#tmp_11
|
||||
@ -35,7 +43,7 @@ func viewerHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config) {
|
||||
}
|
||||
|
||||
// Auth incoming stream
|
||||
func streamAuthHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config) {
|
||||
func streamAuthHandler(w http.ResponseWriter, r *http.Request, cfg *Options) {
|
||||
// FIXME POST request only with "name" and "pass"
|
||||
// if name or pass missing => 400 Malformed request
|
||||
// else login in against LDAP or static users
|
||||
@ -44,7 +52,7 @@ func streamAuthHandler(w http.ResponseWriter, r *http.Request, cfg *config.Confi
|
||||
|
||||
// Handle static files
|
||||
// We do not use http.FileServer as we do not want directory listing
|
||||
func staticHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config) {
|
||||
func staticHandler(w http.ResponseWriter, r *http.Request, cfg *Options) {
|
||||
path := "./web/" + r.URL.Path
|
||||
if f, err := os.Stat(path); err == nil && !f.IsDir() {
|
||||
http.ServeFile(w, r, path)
|
||||
@ -54,19 +62,19 @@ func staticHandler(w http.ResponseWriter, r *http.Request, cfg *config.Config) {
|
||||
}
|
||||
|
||||
// Closure to pass configuration
|
||||
func makeHandler(fn func(http.ResponseWriter, *http.Request, *config.Config), cfg *config.Config) http.HandlerFunc {
|
||||
func makeHandler(fn func(http.ResponseWriter, *http.Request, *Options), cfg *Options) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
fn(w, r, cfg)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP server
|
||||
func ServeHTTP(cfg *config.Config) {
|
||||
func ServeHTTP(cfg *Options) {
|
||||
// Set up HTTP router and server
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", makeHandler(viewerHandler, cfg))
|
||||
mux.HandleFunc("/rtmp/auth", makeHandler(streamAuthHandler, cfg))
|
||||
mux.HandleFunc("/static/", makeHandler(staticHandler, cfg))
|
||||
log.Printf("Listening on http://%s/", cfg.Site.ListenAddress)
|
||||
log.Fatal(http.ListenAndServe(cfg.Site.ListenAddress, mux))
|
||||
log.Printf("HTTP server listening on %s", cfg.ListenAddress)
|
||||
log.Fatal(http.ListenAndServe(cfg.ListenAddress, mux))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user