Skip to content

Commit

Permalink
Added support for custom base href
Browse files Browse the repository at this point in the history
  • Loading branch information
l0ll098 committed Sep 21, 2021
1 parent 6d50176 commit 1db3252
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 13 deletions.
45 changes: 41 additions & 4 deletions cmd/webserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
Expand Down Expand Up @@ -117,16 +118,24 @@ func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

// get the volume of the absolute path and remove it
volume := filepath.VolumeName(path)
path = strings.Replace(path, volume, "", 1)
resourcePath := strings.Replace(path, volume, "", 1)

// prepend the path with the path to the static directory
path = filepath.Join(h.staticPath, path)
resourcePath = filepath.Join(h.staticPath, resourcePath)

baseHref, hasCustomBaseHref := os.LookupEnv("SERVER_BASE_HREF")

// check whether a file exists at the given path
_, err = os.Stat(path)
_, err = os.Stat(resourcePath)
if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))

if hasCustomBaseHref {
generateIndexFile(w, r, baseHref)
} else {
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
}

return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
Expand All @@ -135,6 +144,11 @@ func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}

if hasCustomBaseHref && (strings.HasSuffix(path, "index.html") || strings.HasSuffix(path, "/")) {
generateIndexFile(w, r, baseHref)
return
}

// otherwise, use http.FileServer to serve the static dir
noCache(http.StripPrefix("/", http.FileServer(http.Dir(h.staticPath)))).ServeHTTP(w, r)
}
Expand Down Expand Up @@ -335,6 +349,19 @@ func getVersionHandler(w http.ResponseWriter, r *http.Request) {
respondWithPlainString(w, 200, resp)
}

func generateIndexFile(w http.ResponseWriter, r *http.Request, baseHref string) {
path, _ := os.Getwd()
buf, err := ioutil.ReadFile(filepath.Join(path, "/web/dist/index.html"))
if err != nil {
respondWithError(w, 500, err.Error())
return
}

file := string(buf)
file = strings.Replace(file, `<base href="/">`, fmt.Sprintf(`<base href="%s">`, baseHref), 1)
respondWithHtml(w, 200, file)
}

func respondWithError(w http.ResponseWriter, code int, message string) {
respondWithJSON(w, code, map[string]string{"error": message})
}
Expand All @@ -360,6 +387,16 @@ func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
}
}

func respondWithHtml(w http.ResponseWriter, code int, payload string) {
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(code)
_, err := w.Write(([]byte(payload)))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

func noCache(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Delete any ETag headers that may have been set
Expand Down
19 changes: 11 additions & 8 deletions web/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { OverlayContainer } from '@angular/cdk/overlay';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { MonacoEditorModule, MonacoProviderService } from 'ng-monaco-editor';
import { OverlayContainer } from '@angular/cdk/overlay';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HttpInterceptorService } from './services/interceptor.service';

@NgModule({
declarations: [AppComponent],
Expand All @@ -20,10 +21,12 @@ import { OverlayContainer } from '@angular/cdk/overlay';
}),
],
providers: [
MonacoProviderService,
{
provide: MonacoProviderService,
useValue: undefined
},
provide: HTTP_INTERCEPTORS,
useClass: HttpInterceptorService,
multi: true
}
],
bootstrap: [AppComponent],
})
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/pages/pages.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<mat-toolbar class="mat-elevation-z5">
<img [src]="isLightMode() ? '/assets/images/logo.svg' : '/assets/images/logo-white.svg'" id="logo">
<img [src]="isLightMode() ? './assets/images/logo.svg' : './assets/images/logo-white.svg'" id="logo">
<h1 class="app-header">Dashboard</h1>

<span class="spacer"></span>
Expand Down
55 changes: 55 additions & 0 deletions web/src/app/services/interceptor.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { DOCUMENT } from '@angular/common';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class HttpInterceptorService implements HttpInterceptor {

constructor(
@Inject(DOCUMENT) private document: Document
) { }

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const baseHref = this.getBaseHref();

// If the base href value is not the default "/" and the requested resource is not an absolute URL,
// we have to adjust the requested resource URL, in order to account for the base href itself
if (baseHref !== '/' && !req.url.match(/http(?:s){0,1}:\/\//)) {
let url = req.url;
if (req.url.startsWith('/')) {
// If the requested URL starts with a slash, we will remove it
url = req.url.slice(1);
}

req = req.clone({
url: `${baseHref}${url}`
});
}

return next.handle(req);
}

/**
* An helper method to get base href. If, for whatever reason, we cannot find a
* "base" tag with an "href" attribute, this method will return "/".
* Result of this function is a string that will always end with a slash.
* @returns the base href
*/
private getBaseHref() {
const baseHrefTag = this.document.querySelector('base[href]');
if (baseHrefTag) {
let href = baseHrefTag.getAttribute('href') || '/';
if (!href.endsWith('/')) {
href += '/';
}

return href;
}

return '/';
}

}

0 comments on commit 1db3252

Please sign in to comment.