Skip to content

Commit

Permalink
Merge pull request #376 from synyx/bundle-assets
Browse files Browse the repository at this point in the history
Bundle assets / Docker Image
  • Loading branch information
fheft authored Nov 3, 2024
2 parents f41fb23 + 8dea061 commit ca563d0
Show file tree
Hide file tree
Showing 17 changed files with 78 additions and 33 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*
!pkg/web/public/
!bin/
!examples/
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ coverage.html
coverage.out
dist/
node_modules/
public/js/
public/css/
pkg/web/public/js/
pkg/web/public/css/
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM gcr.io/distroless/static-debian12

USER 1000

COPY bin/tales-server /
COPY examples/ /Tales

VOLUME /Tales

EXPOSE 3000

ENTRYPOINT ["/tales-server"]
CMD ["-bind", "127.0.0.1:3000", "-projects", "/Tales"]
13 changes: 9 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ BINARIES=${SERVER_BINARY} ${MIGRATE_BINARY}
coverage coverage-go coverage-js \
lint lint-go lint-js \
test test-go test-js \
run dist
run dist docker

all: build

Expand Down Expand Up @@ -80,13 +80,13 @@ test-js:
npm run test

run:
${SERVER_BINARY} -resources public/
${SERVER_BINARY} -resources pkg/web/public

dist: dist-go dist-js

dist-go: tales-server.zip

tales-server.zip: bin/* public/*
tales-server.zip: bin/* pkg/web/public/*
mkdir -p dist/tales-server
cp -r bin public dist/tales-server/
if which zip; then \
Expand All @@ -98,10 +98,15 @@ tales-server.zip: bin/* public/*
dist-js:
npm run dist

CONTAINER_BUILDER := $(shell which docker 2>/dev/null || which podman 2>/dev/null)
docker:
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS) -extldflags=-static" -o bin/tales-server $(PKG)/cmd/tales-server
${CONTAINER_BUILDER} build -t tales .

clean: clean-go clean-js

clean-go:
rm -rf coverage.out coverage.html ${BINARIES}

clean-js:
rm -rf dist/main.{js,js.map} public/js/tales.{js,js.map} public/js/viewer.{js,js.map}
rm -rf dist/main.{js,js.map} pkg/web/public/js/tales.{js,js.map} pkg/web/public/js/viewer.{js,js.map}
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Build Status](https://github.com/synyx/tales/workflows/Tales%20CI/badge.svg)](https://github.com/synyx/tales/actions?query=workflow%3A%22Tales+CI%22)
[![Build Status](https://github.com/synyx/tales/workflows/Tales%20CI/badge.svg)](https://github.com/synyx/tales/actions?query=workflow%3A%22Tales+CI%22) [![Demo](https://img.shields.io/badge/demo-instance-blue)](https://tales-demo.synyx.codes/)

# Tales

Expand Down Expand Up @@ -44,7 +44,7 @@ Storyline of your Tale. **Chapters** can further structure your Sections, making

## Prerequisites

All you need is a computer running Windows, Linux or MacOS – and the image with your Tale! :)
All you need is a computer running Windows, Linux or macOS – and the image with your Tale! :)
Tales supports all kinds of image formats, from simple JPEGs to sophisticated vector graphics like SVGs.


Expand Down Expand Up @@ -76,6 +76,7 @@ A video tutorial is coming soon!
Some examples of already existing Tales:

- [What is Tales: A Tale about Tales](examples/A_Tale_about_Tales.html)
See [Demo Instance](https://tales-demo.synyx.codes) on how it is built.

# Contributing

Expand Down
36 changes: 20 additions & 16 deletions cmd/tales-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"flag"
"io/fs"
"log"
"net/http"
"os"
Expand All @@ -19,6 +20,7 @@ import (
var (
resourcesDir string
projectsDir string
bindAddr string
)

func init() {
Expand All @@ -28,36 +30,37 @@ func init() {
func main() {
flag.StringVar(&resourcesDir, "resources", "", "path to public resources")
flag.StringVar(&projectsDir, "projects", defaultProjectsDir(), "path to projects")
flag.StringVar(&bindAddr, "bind", "127.0.0.1:3000", "HTTP server address")
flag.Parse()

if resourcesDir == "" {
flag.Usage()
os.Exit(1)
}

log.Printf("Starting tales-server %s (%s)",
buildinfo.Version,
buildinfo.FormattedGitSHA())

var err error
resourcesDir, err = filepath.Abs(resourcesDir)
projectsDir, err := filepath.Abs(projectsDir)
if err != nil {
log.Fatal(err)
}

projectsDir, err = filepath.Abs(projectsDir)
if err != nil {
log.Fatal(err)
}
log.Printf("Projects directory is at \"%s\"", projectsDir)

shutdownTimeout := 5 * time.Second
var resourceFS fs.FS
if resourcesDir != "" {
resourcesDir, err := filepath.Abs(resourcesDir)
if err != nil {
log.Fatal(err)
}

log.Printf("Projects directory is at \"%s\"", projectsDir)
log.Printf("Resources directory is at \"%s\"", resourcesDir)
log.Printf("Resources directory is at \"%s\"", resourcesDir)
resourceFS = os.DirFS(resourcesDir)
} else {
resourceFS, _ = fs.Sub(web.EmbeddedResources, "public")
log.Println("Using embedded resources")
}

server := http.Server{
Handler: web.NewServer(projectsDir, resourcesDir),
Addr: "127.0.0.1:3000",
Handler: web.NewServer(projectsDir, resourceFS),
Addr: bindAddr,
WriteTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
}
Expand All @@ -72,6 +75,7 @@ func main() {

waitForInterrupt()

shutdownTimeout := 5 * time.Second
log.Printf("Shutting down... (will timeout in %v)", shutdownTimeout)

ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"postinstall": "electron-builder install-app-deps",
"scripts": {
"build": "rollup -c && npm run build:css",
"build:css": "postcss src/css/*.css --dir public/css/",
"build:css": "postcss src/css/*.css --dir pkg/web/public/css/",
"dist": "electron-builder --publish=never",
"format": "prettier --write 'src/js/**/*.js' 'src/css/**/*.css'",
"lint": "eslint src/js",
Expand All @@ -18,7 +18,7 @@
"preversion": "npm test && npm run lint",
"test": "jest",
"watch": "rollup -c -w",
"watch:css": "postcss src/css/*.css --dir public/css/ -w"
"watch:css": "postcss src/css/*.css --dir pkg/web/public/css/ -w"
},
"repository": {
"type": "git",
Expand Down
File renamed without changes.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes.
File renamed without changes.
8 changes: 8 additions & 0 deletions pkg/web/resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package web

import "embed"

// EmbeddedResources holds static web server content.
//
//go:embed public
var EmbeddedResources embed.FS
11 changes: 6 additions & 5 deletions pkg/web/server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package web

import (
"io/fs"
"net/http"

"synyx.de/tales/pkg/project"
Expand All @@ -17,7 +18,7 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

// NewServer creates a http.Handler ready to handle tales requests.
func NewServer(projectsDir, resourcesDir string) http.Handler {
func NewServer(projectsDir string, resourcesDir fs.FS) http.Handler {
r := http.NewServeMux()
repository := &project.FilesystemRepository{
ProjectDir: projectsDir,
Expand All @@ -27,9 +28,9 @@ func NewServer(projectsDir, resourcesDir string) http.Handler {
repository: repository,
}

fs := http.FileServer(http.Dir(projectsDir))
r.Handle("GET /editor/", http.StripPrefix("/editor", fs))
r.Handle("GET /presenter/", http.StripPrefix("/presenter", fs))
projectFileServer := http.FileServer(http.Dir(projectsDir))
r.Handle("GET /editor/", http.StripPrefix("/editor", projectFileServer))
r.Handle("GET /presenter/", http.StripPrefix("/presenter", projectFileServer))

r.HandleFunc("GET /api/tales/", s.listProjects)
r.HandleFunc("POST /api/tales/", s.createProject)
Expand All @@ -38,7 +39,7 @@ func NewServer(projectsDir, resourcesDir string) http.Handler {
r.HandleFunc("DELETE /api/tales/{slug}", s.deleteProject)
r.HandleFunc("PUT /api/tales/{slug}/image", s.saveProjectImage)

r.Handle("GET /", http.FileServer(http.Dir(resourcesDir)))
r.Handle("GET /", http.FileServer(http.FS(resourcesDir)))

return s
}
12 changes: 10 additions & 2 deletions pkg/web/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package web
import (
"bytes"
"encoding/json"
"io/fs"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -23,7 +24,7 @@ type TestClient struct {
func NewTestClient(t *testing.T) *TestClient {
dir, err := os.MkdirTemp("", "tales-test")
assert.NoError(t, err)
handler := NewServer(dir, "")
handler := NewServer(dir, emptyFS{})
repo := &project.FilesystemRepository{
ProjectDir: dir,
}
Expand Down Expand Up @@ -59,6 +60,13 @@ func (tc *TestClient) Cleanup() {

func TestServer_ServeHTTP(t *testing.T) {
t.Run("http handler", func(t *testing.T) {
assert.Implements(t, new(http.Handler), NewServer("", ""))
assert.Implements(t, new(http.Handler), NewServer("", emptyFS{}))
})
}

type emptyFS struct {
}

func (f emptyFS) Open(_ string) (fs.File, error) {
return nil, fs.ErrNotExist
}
1 change: 1 addition & 0 deletions public

0 comments on commit ca563d0

Please sign in to comment.