Skip to content

Commit 3c426e9

Browse files
authored
Allow hosting OpenBooks server at a subpath. (#34)
* feature: Add support for hosting at sub path. - The subpath is set via environment variable or cli argument. - Updated readme files to show it's used. * fix: minor wording change
1 parent ab8a7ce commit 3c426e9

12 files changed

+111
-56
lines changed

.dockerignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
**/node_modules/
22
**/build/
3+
**/dist/
4+
**/github/
35
openbooks.exe
6+
openbooks
47
*.exe

Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ COPY --from=build /go/src/openbooks .
2121

2222
EXPOSE 80
2323
VOLUME [ "/books" ]
24+
ENV BASE_PATH=/
2425

2526
ENTRYPOINT ["./openbooks", "server", "--dir", "/books", "--port", "80"]

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ Openbooks allows you to download ebooks from irc.irchighway.net quickly and easi
2424
- Config to perist all eBook files to disk
2525
- `docker run -p 8080:80 -v /home/evan/Downloads/books:/books evanbuss/openbooks --persist`
2626

27+
### Setting the Base Path
28+
29+
OpenBooks server doesn't have to be hosted at the root of your webserver. The basepath value allows you to host it behind a reverse proxy. The base path value must have opening and closing forward slashes (default "/").
30+
31+
- Docker
32+
- `docker run -p 8080:80 -e BASE_PATH=/openbooks/ evanbuss/openbooks`
33+
- Binary
34+
- `./openbooks server --basepath /openbooks/`
35+
2736
## Development
2837

2938
### Install the dependencies

cmd/server.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ func init() {
2222
serverCmd.Flags().StringP("port", "p", "5228", "Set the local network port for browser mode.")
2323
serverCmd.Flags().StringP("dir", "d", os.TempDir(), "The directory where eBooks are saved when persist enabled.")
2424
serverCmd.Flags().Bool("persist", false, "Persist eBooks in 'dir'. Default is to delete after sending.")
25+
serverCmd.Flags().String("basepath", "/", `Base path where the application is accessible. For example "/openbooks/".`)
2526
}
2627

27-
// TODO: Something is broken with this new setup...
2828
var serverCmd = &cobra.Command{
2929
Use: "server",
3030
Short: "Run OpenBooks in server mode.",
@@ -36,6 +36,15 @@ var serverCmd = &cobra.Command{
3636
port, _ := cmd.Flags().GetString("port")
3737
dir, _ := cmd.Flags().GetString("dir")
3838
persist, _ := cmd.Flags().GetBool("persist")
39+
basepath, _ := cmd.Flags().GetString("basepath")
40+
41+
// If cli flag isn't set (default value) check for the presence of an
42+
// environment variable and use it if found.
43+
if basepath == cmd.Flag("basepath").DefValue {
44+
if envPath, present := os.LookupEnv("BASE_PATH"); present {
45+
basepath = envPath
46+
}
47+
}
3948

4049
config := server.Config{
4150
Log: log,
@@ -44,6 +53,7 @@ var serverCmd = &cobra.Command{
4453
Port: port,
4554
DownloadDir: dir,
4655
Persist: persist,
56+
Basepath: basepath,
4757
}
4858

4959
server.Start(config)

docker.md

+6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616

1717
`docker run -d -p 8080:80 evanbuss/openbooks --name my_irc_name`
1818

19+
### Host at a sub path behind a reverse proxy
20+
21+
`docker run -d -p 8080:80 -e BASE_PATH=/openbooks/ evanbuss/openbooks`
22+
1923
## Arguments
2024

2125
```
@@ -38,6 +42,8 @@ services:
3842
restart: unless-stopped
3943
container_name: OpenBooks
4044
command: --persist
45+
environment:
46+
- BASE_PATH=/openbooks/
4147
image: evan-buss/openbooks:latest
4248
4349
volumes:

server/app/package-lock.json

+14-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/app/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
},
2020
"devDependencies": {
2121
"@types/lodash": "^4.14.172",
22-
"@types/node": "^16.6.2",
23-
"@types/react": "^17.0.18",
22+
"@types/node": "^16.7.1",
23+
"@types/react": "^17.0.19",
2424
"@types/react-dom": "^17.0.9",
2525
"@types/styled-components": "^5.1.12",
2626
"@vitejs/plugin-react-refresh": "^1.3.6",

server/app/src/state/store.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import serverReducer from "./serverSlice";
66
import stateReducer from "./stateSlice";
77
import { enableMapSet } from "immer";
88

9-
const wsProtocol = window.location.protocol === "https:" ? "wss://" : "ws://";
10-
const wsHost = import.meta.env.DEV ? "localhost:5228" : window.location.host;
11-
const wsUrl = `${wsProtocol}${wsHost}/ws`;
9+
const websocketURL = new URL(window.location.href + "ws")
10+
if (websocketURL.protocol.startsWith("https")) {
11+
websocketURL.protocol = websocketURL.protocol.replace("https", "wss");
12+
} else {
13+
websocketURL.protocol = websocketURL.protocol.replace("http", "ws");
14+
}
1215

1316
enableMapSet();
1417

@@ -18,7 +21,7 @@ export const store = configureStore({
1821
history: historyReducer,
1922
servers: serverReducer
2023
},
21-
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(websocketConn(wsUrl))
24+
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(websocketConn(websocketURL.href))
2225
});
2326

2427
const saveState = (key: string, state: any): void => {

server/app/vite.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import reactRefresh from '@vitejs/plugin-react-refresh'
33

44
// https://vitejs.dev/config/
55
export default defineConfig({
6-
plugins: [reactRefresh()]
6+
plugins: [reactRefresh()],
7+
base: "./"
78
})

server/routes.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package server
2+
3+
import (
4+
"embed"
5+
"fmt"
6+
"io/fs"
7+
"log"
8+
"net/http"
9+
"path"
10+
)
11+
12+
//go:embed app/dist
13+
var reactClient embed.FS
14+
15+
func registerRoutes(hub *Hub) {
16+
basePath := getBaseRoute()
17+
fmt.Printf("Base Path: %s\n", basePath)
18+
19+
http.Handle(basePath, serveStaticFiles(basePath, "app/dist"))
20+
21+
http.HandleFunc(path.Join(basePath, "ws"), func(w http.ResponseWriter, r *http.Request) {
22+
serveWs(hub, w, r)
23+
})
24+
25+
http.HandleFunc(path.Join(basePath, "connections"), func(w http.ResponseWriter, r *http.Request) {
26+
fmt.Fprintf(w, "There are currently %d active connections.", *numConnections)
27+
})
28+
}
29+
30+
func serveStaticFiles(basePath, assetPath string) http.Handler {
31+
// update the embedded file system's tree so that index.html is at the root
32+
app, err := fs.Sub(reactClient, assetPath)
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
37+
// strip the predefined base path and serve the static file
38+
return http.StripPrefix(basePath, http.FileServer(http.FS(app)))
39+
}
40+
41+
func getBaseRoute() string {
42+
cleaned := path.Clean(config.Basepath)
43+
if cleaned == "/" {
44+
return cleaned
45+
}
46+
return cleaned + "/"
47+
}

server/server.go

+6-33
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,16 @@
11
package server
22

33
import (
4-
"embed"
54
"fmt"
65
"log"
76
"net/http"
8-
"net/url"
97
"os"
108
"os/signal"
9+
"path"
1110
"syscall"
1211
"time"
1312
)
1413

15-
//go:embed app/dist
16-
var reactClient embed.FS
17-
1814
// Config contains settings for server
1915
type Config struct {
2016
Log bool
@@ -23,6 +19,7 @@ type Config struct {
2319
UserName string
2420
Persist bool
2521
DownloadDir string
22+
Basepath string
2623
}
2724

2825
var config Config
@@ -45,38 +42,14 @@ func Start(conf Config) {
4542
os.Exit(1)
4643
}()
4744

48-
http.Handle("/", AddRoutePrefix("/app/dist/", http.FileServer(http.FS(reactClient))))
49-
50-
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
51-
serveWs(hub, w, r)
52-
})
53-
54-
http.HandleFunc("/connections", func(w http.ResponseWriter, r *http.Request) {
55-
fmt.Fprintf(w, "There are currently %d active connections.", *numConnections)
56-
})
45+
registerRoutes(hub)
5746

5847
if config.OpenBrowser {
59-
openbrowser("http://127.0.0.1:" + config.Port + "/")
48+
browserUrl := "http://127.0.0.1:" + path.Join(config.Port+config.Basepath)
49+
fmt.Println(browserUrl)
50+
openbrowser(browserUrl)
6051
}
6152

6253
log.Printf("OpenBooks is listening on port %v", config.Port)
6354
log.Fatal(http.ListenAndServe(":"+config.Port, nil))
6455
}
65-
66-
// AddRoutePrefix adds a prefix to the request.
67-
func AddRoutePrefix(prefix string, h http.Handler) http.Handler {
68-
if prefix == "" {
69-
return h
70-
}
71-
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
72-
p := prefix + r.URL.Path
73-
rp := prefix + r.URL.RawPath
74-
r2 := new(http.Request)
75-
*r2 = *r
76-
r2.URL = new(url.URL)
77-
*r2.URL = *r.URL
78-
r2.URL.Path = p
79-
r2.URL.RawPath = rp
80-
h.ServeHTTP(w, r2)
81-
})
82-
}

todo

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77
- Server / Title - Search Text Box
88
- Format - Find unique formats and have clickable options
99
- Use new Pulsar component instead of custom version
10-
- Make the "connected" indicator actually work...
10+
- Make the "connected" indicator actually work...
11+
- Show IRC name in web interface
12+
- Show raw IRC logs in web interface

0 commit comments

Comments
 (0)