Add build option to embed static files in the built binary
This commit is contained in:
parent
4273413e98
commit
ff3dba9980
@ -4,8 +4,8 @@ set -x
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [[ -n "$1" ]]; then
|
if [[ -n "$1" ]]; then
|
||||||
go build -ldflags="-s -w -X main.version_string=$1" -o tw ./twitter
|
go build -ldflags="-s -w -X main.version_string=$1 -X webserver.is_production=true" -o tw ./twitter
|
||||||
else
|
else
|
||||||
go build -ldflags="-s -w" -o tw ./twitter
|
go build -ldflags="-s -w -X webserver.is_production=true" -o tw ./twitter
|
||||||
fi
|
fi
|
||||||
chmod +x tw
|
chmod +x tw
|
||||||
|
10
internal/webserver/files.go
Normal file
10
internal/webserver/files.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package webserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed "tpl" "static"
|
||||||
|
var embedded_files embed.FS
|
||||||
|
|
||||||
|
var use_embedded = false
|
@ -5,8 +5,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
|
||||||
"github.com/Masterminds/sprig/v3"
|
"github.com/Masterminds/sprig/v3"
|
||||||
@ -20,6 +23,32 @@ func panic_if(err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var this_dir string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_, this_file, _, _ := runtime.Caller(0) // `this_file` is absolute path to this source file
|
||||||
|
this_dir = path.Dir(this_file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func get_filepath(s string) string {
|
||||||
|
if use_embedded {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return path.Join(this_dir, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func glob(path string) []string {
|
||||||
|
var ret []string
|
||||||
|
var err error
|
||||||
|
if use_embedded {
|
||||||
|
ret, err = fs.Glob(embedded_files, get_filepath(path))
|
||||||
|
} else {
|
||||||
|
ret, err = filepath.Glob(get_filepath(path))
|
||||||
|
}
|
||||||
|
panic_if(err)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
// func (app *Application) error_400(w http.ResponseWriter) {
|
// func (app *Application) error_400(w http.ResponseWriter) {
|
||||||
// http.Error(w, "Bad Request", 400)
|
// http.Error(w, "Bad Request", 400)
|
||||||
// }
|
// }
|
||||||
@ -52,11 +81,7 @@ type TweetCollection interface {
|
|||||||
// Creates a template from the given template file using all the available partials.
|
// Creates a template from the given template file using all the available partials.
|
||||||
// Calls `app.buffered_render` to render the created template.
|
// Calls `app.buffered_render` to render the created template.
|
||||||
func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_file string, data TweetCollection) {
|
func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_file string, data TweetCollection) {
|
||||||
partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl"))
|
partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...)
|
||||||
panic_if(err)
|
|
||||||
tweet_partials, err := filepath.Glob(get_filepath("tpl/tweet_page_includes/*.tpl"))
|
|
||||||
panic_if(err)
|
|
||||||
partials = append(partials, tweet_partials...)
|
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{
|
Funcs: func_map(template.FuncMap{
|
||||||
@ -77,8 +102,7 @@ func (app *Application) buffered_render_tweet_page(w http.ResponseWriter, tpl_fi
|
|||||||
// Creates a template from the given template file using all the available partials.
|
// Creates a template from the given template file using all the available partials.
|
||||||
// Calls `app.buffered_render` to render the created template.
|
// Calls `app.buffered_render` to render the created template.
|
||||||
func (app *Application) buffered_render_basic_page(w http.ResponseWriter, tpl_file string, data interface{}) {
|
func (app *Application) buffered_render_basic_page(w http.ResponseWriter, tpl_file string, data interface{}) {
|
||||||
partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl"))
|
partials := glob("tpl/includes/*.tpl")
|
||||||
panic_if(err)
|
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}),
|
Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}),
|
||||||
@ -90,11 +114,7 @@ func (app *Application) buffered_render_basic_page(w http.ResponseWriter, tpl_fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_name string, data TweetCollection) {
|
func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_name string, data TweetCollection) {
|
||||||
partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl"))
|
partials := append(glob("tpl/includes/*.tpl"), glob("tpl/tweet_page_includes/*.tpl")...)
|
||||||
panic_if(err)
|
|
||||||
tweet_partials, err := filepath.Glob(get_filepath("tpl/tweet_page_includes/*.tpl"))
|
|
||||||
panic_if(err)
|
|
||||||
partials = append(partials, tweet_partials...)
|
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{
|
Funcs: func_map(template.FuncMap{
|
||||||
@ -113,8 +133,7 @@ func (app *Application) buffered_render_tweet_htmx(w http.ResponseWriter, tpl_na
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (app *Application) buffered_render_basic_htmx(w http.ResponseWriter, tpl_name string, data interface{}) {
|
func (app *Application) buffered_render_basic_htmx(w http.ResponseWriter, tpl_name string, data interface{}) {
|
||||||
partials, err := filepath.Glob(get_filepath("tpl/includes/*.tpl"))
|
partials := glob("tpl/includes/*.tpl")
|
||||||
panic_if(err)
|
|
||||||
|
|
||||||
r := renderer{
|
r := renderer{
|
||||||
Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}),
|
Funcs: func_map(template.FuncMap{"active_user": app.get_active_user}),
|
||||||
@ -147,7 +166,13 @@ type renderer struct {
|
|||||||
// Render the given template using a bytes.Buffer. This avoids the possibility of failing partway
|
// Render the given template using a bytes.Buffer. This avoids the possibility of failing partway
|
||||||
// through the rendering, and sending an imcomplete response with "Bad Request" or "Server Error" at the end.
|
// through the rendering, and sending an imcomplete response with "Bad Request" or "Server Error" at the end.
|
||||||
func (r renderer) BufferedRender(w io.Writer) {
|
func (r renderer) BufferedRender(w io.Writer) {
|
||||||
tpl, err := template.New("").Funcs(r.Funcs).ParseFiles(r.Filenames...)
|
var tpl *template.Template
|
||||||
|
var err error
|
||||||
|
if use_embedded {
|
||||||
|
tpl, err = template.New("").Funcs(r.Funcs).ParseFS(embedded_files, r.Filenames...)
|
||||||
|
} else {
|
||||||
|
tpl, err = template.New("").Funcs(r.Funcs).ParseFiles(r.Filenames...)
|
||||||
|
}
|
||||||
panic_if(err)
|
panic_if(err)
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -88,17 +87,6 @@ func get_default_user() scraper.User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var this_dir string
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
_, this_file, _, _ := runtime.Caller(0) // `this_file` is absolute path to this source file
|
|
||||||
this_dir = path.Dir(this_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
func get_filepath(s string) string {
|
|
||||||
return path.Join(this_dir, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manual router implementation.
|
// Manual router implementation.
|
||||||
// I don't like the weird matching behavior of http.ServeMux, and it's not hard to write by hand.
|
// I don't like the weird matching behavior of http.ServeMux, and it's not hard to write by hand.
|
||||||
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -110,7 +98,12 @@ func (app *Application) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
parts := strings.Split(r.URL.Path, "/")[1:]
|
parts := strings.Split(r.URL.Path, "/")[1:]
|
||||||
switch parts[0] {
|
switch parts[0] {
|
||||||
case "static":
|
case "static":
|
||||||
http.StripPrefix("/static", http.FileServer(http.Dir(get_filepath("static")))).ServeHTTP(w, r)
|
if use_embedded {
|
||||||
|
// Serve directly from the embedded files
|
||||||
|
http.FileServer(http.FS(embedded_files)).ServeHTTP(w, r)
|
||||||
|
} else {
|
||||||
|
http.StripPrefix("/static", http.FileServer(http.Dir(get_filepath("static")))).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
case "tweet":
|
case "tweet":
|
||||||
app.TweetDetail(w, r)
|
app.TweetDetail(w, r)
|
||||||
case "content":
|
case "content":
|
||||||
|
Loading…
x
Reference in New Issue
Block a user