mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
Define base for assets and support for sub path (#1247)
* Added correct mime type to files * Define Base for Assets Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> * lint Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> * Make things relative Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> * hop styling Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com> Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -185,7 +185,8 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
|
|||||||
responseWriter.WriteHeader(500)
|
responseWriter.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
targetURL.Path = strings.Replace(req.URL.Path, fmt.Sprintf("/api/proxy/%s/%s", tenant.Namespace, tenant.Name), "", -1)
|
tenantBase := fmt.Sprintf("/api/proxy/%s/%s", tenant.Namespace, tenant.Name)
|
||||||
|
targetURL.Path = strings.Replace(req.URL.Path, tenantBase, "", -1)
|
||||||
|
|
||||||
proxiedCookie := &http.Cookie{
|
proxiedCookie := &http.Cookie{
|
||||||
Name: "token",
|
Name: "token",
|
||||||
@@ -207,8 +208,17 @@ func serveProxy(responseWriter http.ResponseWriter, req *http.Request) {
|
|||||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
// are we proxying something with cp=y? (console proxy) then add cpb (console proxy base) so the console
|
||||||
|
// on the other side updates the <base href="" /> to this value overriding sub path or root
|
||||||
|
if v := req.URL.Query().Get("cp"); v == "y" {
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("cpb", tenantBase)
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
}
|
||||||
// copy query params
|
// copy query params
|
||||||
targetURL.RawQuery = req.URL.Query().Encode()
|
targetURL.RawQuery = req.URL.Query().Encode()
|
||||||
|
|
||||||
proxRequest, err := http.NewRequest(req.Method, targetURL.String(), req.Body)
|
proxRequest, err := http.NewRequest(req.Method, targetURL.String(), req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
|
|||||||
@@ -2,51 +2,52 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<base href="/" />
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||||
<meta
|
<meta
|
||||||
name="theme-color"
|
|
||||||
content="#081C42"
|
content="#081C42"
|
||||||
media="(prefers-color-scheme: light)"
|
media="(prefers-color-scheme: light)"
|
||||||
|
name="theme-color"
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
name="theme-color"
|
|
||||||
content="#081C42"
|
content="#081C42"
|
||||||
media="(prefers-color-scheme: dark)"
|
media="(prefers-color-scheme: dark)"
|
||||||
|
name="theme-color"
|
||||||
/>
|
/>
|
||||||
<meta name="description" content="MinIO Console" />
|
<meta content="MinIO Console" name="description" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link href="%PUBLIC_URL%/styles/root-styles.css" rel="stylesheet" />
|
<link href="%PUBLIC_URL%/styles/root-styles.css" rel="stylesheet" />
|
||||||
<link
|
<link
|
||||||
|
href="%PUBLIC_URL%/apple-icon-180x180.png"
|
||||||
rel="apple-touch-icon"
|
rel="apple-touch-icon"
|
||||||
sizes="180x180"
|
sizes="180x180"
|
||||||
href="%PUBLIC_URL%/apple-icon-180x180.png"
|
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="32x32"
|
|
||||||
href="%PUBLIC_URL%/favicon-32x32.png"
|
href="%PUBLIC_URL%/favicon-32x32.png"
|
||||||
|
rel="icon"
|
||||||
|
sizes="32x32"
|
||||||
|
type="image/png"
|
||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
sizes="96x96"
|
|
||||||
href="%PUBLIC_URL%/favicon-96x96.png"
|
href="%PUBLIC_URL%/favicon-96x96.png"
|
||||||
/>
|
|
||||||
<link
|
|
||||||
rel="icon"
|
rel="icon"
|
||||||
|
sizes="96x96"
|
||||||
type="image/png"
|
type="image/png"
|
||||||
sizes="16x16"
|
|
||||||
href="%PUBLIC_URL%/favicon-16x16.png"
|
|
||||||
/>
|
/>
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<link
|
<link
|
||||||
rel="mask-icon"
|
href="%PUBLIC_URL%/favicon-16x16.png"
|
||||||
href="%PUBLIC_URL%/safari-pinned-tab.svg"
|
rel="icon"
|
||||||
|
sizes="16x16"
|
||||||
|
type="image/png"
|
||||||
|
/>
|
||||||
|
<link href="%PUBLIC_URL%/manifest.json" rel="manifest" />
|
||||||
|
<link
|
||||||
color="#3a4e54"
|
color="#3a4e54"
|
||||||
|
href="%PUBLIC_URL%/safari-pinned-tab.svg"
|
||||||
|
rel="mask-icon"
|
||||||
/>
|
/>
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
@@ -71,22 +72,22 @@
|
|||||||
"
|
"
|
||||||
cx="44"
|
cx="44"
|
||||||
cy="44"
|
cy="44"
|
||||||
r="20.2"
|
|
||||||
fill="none"
|
fill="none"
|
||||||
|
r="20.2"
|
||||||
stroke-width="3.6"
|
stroke-width="3.6"
|
||||||
></circle>
|
></circle>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
If you open it directly in the browser, you will see an empty page.
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import { connect } from "react-redux";
|
|||||||
import { AppState } from "./store";
|
import { AppState } from "./store";
|
||||||
import {
|
import {
|
||||||
consoleOperatorMode,
|
consoleOperatorMode,
|
||||||
userLoggedIn,
|
|
||||||
setDistributedMode,
|
setDistributedMode,
|
||||||
|
userLoggedIn,
|
||||||
} from "./actions";
|
} from "./actions";
|
||||||
import api from "./common/api";
|
import api from "./common/api";
|
||||||
import { saveSessionResponse } from "./screens/Console/actions";
|
import { saveSessionResponse } from "./screens/Console/actions";
|
||||||
|
|||||||
@@ -17,12 +17,14 @@
|
|||||||
import request from "superagent";
|
import request from "superagent";
|
||||||
import get from "lodash/get";
|
import get from "lodash/get";
|
||||||
import { clearSession } from "../utils";
|
import { clearSession } from "../utils";
|
||||||
import { baseUrl } from "../../history";
|
|
||||||
import { ErrorResponseHandler } from "../types";
|
import { ErrorResponseHandler } from "../types";
|
||||||
|
|
||||||
export class API {
|
export class API {
|
||||||
invoke(method: string, url: string, data?: object) {
|
invoke(method: string, url: string, data?: object) {
|
||||||
const targetURL = `${baseUrl}${url}`.replace(/\/\//g, "/");
|
let targetURL = url;
|
||||||
|
if (targetURL[0] === "/") {
|
||||||
|
targetURL = targetURL.substr(1);
|
||||||
|
}
|
||||||
return request(method, targetURL)
|
return request(method, targetURL)
|
||||||
.send(data)
|
.send(data)
|
||||||
.then((res) => res.body)
|
.then((res) => res.body)
|
||||||
|
|||||||
@@ -3,13 +3,4 @@ import { BrowserHistoryBuildOptions } from "history/createBrowserHistory";
|
|||||||
|
|
||||||
let browserHistoryOpts: BrowserHistoryBuildOptions = {};
|
let browserHistoryOpts: BrowserHistoryBuildOptions = {};
|
||||||
|
|
||||||
export let baseUrl = "";
|
|
||||||
|
|
||||||
if (`${window.location.pathname}`.startsWith("/api/proxy/")) {
|
|
||||||
// grab from api to the tenant name (/api/proxy/namespace/tenant)
|
|
||||||
const urlParts = `${window.location.pathname}`.split("/").slice(0, 5);
|
|
||||||
browserHistoryOpts.basename = urlParts.join("/");
|
|
||||||
baseUrl = `${urlParts.join("/")}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default createBrowserHistory(browserHistoryOpts);
|
export default createBrowserHistory(browserHistoryOpts);
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ import RewindEnable from "./RewindEnable";
|
|||||||
|
|
||||||
import DeleteMultipleObjects from "./DeleteMultipleObjects";
|
import DeleteMultipleObjects from "./DeleteMultipleObjects";
|
||||||
import PreviewFileModal from "../Preview/PreviewFileModal";
|
import PreviewFileModal from "../Preview/PreviewFileModal";
|
||||||
import { baseUrl } from "../../../../../../history";
|
|
||||||
import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
|
import ScreenTitle from "../../../../Common/ScreenTitle/ScreenTitle";
|
||||||
import AddFolderIcon from "../../../../../../icons/AddFolderIcon";
|
import AddFolderIcon from "../../../../../../icons/AddFolderIcon";
|
||||||
import HistoryIcon from "../../../../../../icons/HistoryIcon";
|
import HistoryIcon from "../../../../../../icons/HistoryIcon";
|
||||||
@@ -597,7 +596,7 @@ const ListObjects = ({
|
|||||||
}
|
}
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let files = e.target.files;
|
let files = e.target.files;
|
||||||
let uploadUrl = `${baseUrl}/api/v1/buckets/${bucketName}/objects/upload`;
|
let uploadUrl = `api/v1/buckets/${bucketName}/objects/upload`;
|
||||||
if (encodedPath !== "") {
|
if (encodedPath !== "") {
|
||||||
uploadUrl = `${uploadUrl}?prefix=${encodedPath}`;
|
uploadUrl = `${uploadUrl}?prefix=${encodedPath}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ const PageHeader = ({
|
|||||||
return (
|
return (
|
||||||
<Grid
|
<Grid
|
||||||
container
|
container
|
||||||
className={classes.headerContainer}
|
className={`${classes.headerContainer} page-header`}
|
||||||
direction="row"
|
direction="row"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -95,9 +95,7 @@ const Dashboard = React.lazy(() => import("./Dashboard/Dashboard"));
|
|||||||
const Account = React.lazy(() => import("./Account/Account"));
|
const Account = React.lazy(() => import("./Account/Account"));
|
||||||
const Users = React.lazy(() => import("./Users/Users"));
|
const Users = React.lazy(() => import("./Users/Users"));
|
||||||
const Groups = React.lazy(() => import("./Groups/Groups"));
|
const Groups = React.lazy(() => import("./Groups/Groups"));
|
||||||
const ConfigurationMain = React.lazy(
|
|
||||||
() => import("./Configurations/ConfigurationMain")
|
|
||||||
);
|
|
||||||
const TenantDetails = React.lazy(
|
const TenantDetails = React.lazy(
|
||||||
() => import("./Tenants/TenantDetails/TenantDetails")
|
() => import("./Tenants/TenantDetails/TenantDetails")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -461,7 +461,7 @@ const Menu = ({
|
|||||||
component: NavLink,
|
component: NavLink,
|
||||||
to: "/license",
|
to: "/license",
|
||||||
name: "License",
|
name: "License",
|
||||||
icon: <LicenseIcon />,
|
icon: LicenseIcon,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...documentation,
|
...documentation,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { Theme } from "@mui/material/styles";
|
|||||||
import createStyles from "@mui/styles/createStyles";
|
import createStyles from "@mui/styles/createStyles";
|
||||||
import withStyles from "@mui/styles/withStyles";
|
import withStyles from "@mui/styles/withStyles";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { CircularProgress, IconButton } from "@mui/material";
|
import { Box, CircularProgress, IconButton } from "@mui/material";
|
||||||
import PageHeader from "../../../Common/PageHeader/PageHeader";
|
import PageHeader from "../../../Common/PageHeader/PageHeader";
|
||||||
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
|
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
|
||||||
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
import ExitToAppIcon from "@mui/icons-material/ExitToApp";
|
||||||
@@ -46,8 +46,8 @@ const styles = (theme: Theme) =>
|
|||||||
divContainer: {
|
divContainer: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 77,
|
top: 80,
|
||||||
height: "calc(100vh - 77px)",
|
height: "calc(100vh - 81px)",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
},
|
},
|
||||||
loader: {
|
loader: {
|
||||||
@@ -55,6 +55,11 @@ const styles = (theme: Theme) =>
|
|||||||
margin: "auto",
|
margin: "auto",
|
||||||
marginTop: 80,
|
marginTop: 80,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pageHeader: {
|
||||||
|
borderBottom: "1px solid #dedede",
|
||||||
|
},
|
||||||
|
|
||||||
...containerForHeader(theme.spacing(4)),
|
...containerForHeader(theme.spacing(4)),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -66,72 +71,76 @@ const Hop = ({ classes, match }: IHopSimple) => {
|
|||||||
const consoleFrame = React.useRef<HTMLIFrameElement>(null);
|
const consoleFrame = React.useRef<HTMLIFrameElement>(null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<Fragment>
|
||||||
<PageHeader
|
<Box className={classes.pageHeader}>
|
||||||
label={
|
<PageHeader
|
||||||
<Fragment>
|
label={
|
||||||
<Link to={"/tenants"} className={classes.breadcrumLink}>
|
<Fragment>
|
||||||
Tenants
|
<Link to={"/tenants"} className={classes.breadcrumLink}>
|
||||||
</Link>
|
Tenants
|
||||||
{` > `}
|
</Link>
|
||||||
<Link
|
{` > `}
|
||||||
to={`/namespaces/${tenantNamespace}/tenants/${tenantName}`}
|
<Link
|
||||||
className={classes.breadcrumLink}
|
to={`/namespaces/${tenantNamespace}/tenants/${tenantName}`}
|
||||||
>
|
className={classes.breadcrumLink}
|
||||||
{match.params["tenantName"]}
|
>
|
||||||
</Link>
|
{match.params["tenantName"]}
|
||||||
{` > Management`}
|
</Link>
|
||||||
</Fragment>
|
{` > Management`}
|
||||||
}
|
</Fragment>
|
||||||
actions={
|
}
|
||||||
<React.Fragment>
|
actions={
|
||||||
<IconButton
|
<React.Fragment>
|
||||||
color="primary"
|
<IconButton
|
||||||
aria-label="Refresh List"
|
color="primary"
|
||||||
component="span"
|
aria-label="Refresh List"
|
||||||
onClick={() => {
|
component="span"
|
||||||
if (
|
onClick={() => {
|
||||||
consoleFrame !== null &&
|
if (
|
||||||
consoleFrame.current !== null &&
|
consoleFrame !== null &&
|
||||||
consoleFrame.current.contentDocument !== null
|
consoleFrame.current !== null &&
|
||||||
) {
|
consoleFrame.current.contentDocument !== null
|
||||||
const loc =
|
) {
|
||||||
consoleFrame.current.contentDocument.location.toString();
|
const loc =
|
||||||
|
consoleFrame.current.contentDocument.location.toString();
|
||||||
|
|
||||||
let add = "&";
|
let add = "&";
|
||||||
|
|
||||||
if (loc.indexOf("?") < 0) {
|
if (loc.indexOf("?") < 0) {
|
||||||
add = `?`;
|
add = `?`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loc.indexOf("cp=y") < 0) {
|
||||||
|
const next = `${loc}${add}cp=y`;
|
||||||
|
consoleFrame.current.contentDocument.location.replace(
|
||||||
|
next
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
consoleFrame.current.contentDocument.location.reload();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
if (loc.indexOf("cp=y") < 0) {
|
size="large"
|
||||||
const next = `${loc}${add}cp=y`;
|
>
|
||||||
consoleFrame.current.contentDocument.location.replace(next);
|
<RefreshIcon />
|
||||||
} else {
|
</IconButton>
|
||||||
consoleFrame.current.contentDocument.location.reload();
|
<IconButton
|
||||||
}
|
color="primary"
|
||||||
}
|
aria-label="Refresh List"
|
||||||
}}
|
component="span"
|
||||||
size="large"
|
onClick={() => {
|
||||||
>
|
history.push(
|
||||||
<RefreshIcon />
|
`/namespaces/${tenantNamespace}/tenants/${tenantName}`
|
||||||
</IconButton>
|
);
|
||||||
<IconButton
|
}}
|
||||||
color="primary"
|
size="large"
|
||||||
aria-label="Refresh List"
|
>
|
||||||
component="span"
|
<ExitToAppIcon />
|
||||||
onClick={() => {
|
</IconButton>
|
||||||
history.push(
|
</React.Fragment>
|
||||||
`/namespaces/${tenantNamespace}/tenants/${tenantName}`
|
}
|
||||||
);
|
/>
|
||||||
}}
|
</Box>
|
||||||
size="large"
|
|
||||||
>
|
|
||||||
<ExitToAppIcon />
|
|
||||||
</IconButton>
|
|
||||||
</React.Fragment>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className={classes.divContainer}>
|
<div className={classes.divContainer}>
|
||||||
{loading && (
|
{loading && (
|
||||||
<div className={classes.loader}>
|
<div className={classes.loader}>
|
||||||
@@ -148,7 +157,7 @@ const Hop = ({ classes, match }: IHopSimple) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,14 +21,16 @@ package restapi
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/klauspost/compress/gzhttp"
|
"github.com/klauspost/compress/gzhttp"
|
||||||
@@ -50,6 +52,9 @@ var additionalServerFlags = struct {
|
|||||||
CertsDir string `long:"certs-dir" description:"path to certs directory" env:"CONSOLE_CERTS_DIR"`
|
CertsDir string `long:"certs-dir" description:"path to certs directory" env:"CONSOLE_CERTS_DIR"`
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
|
var subPath = "/"
|
||||||
|
var subPathOnce sync.Once
|
||||||
|
|
||||||
func configureFlags(api *operations.ConsoleAPI) {
|
func configureFlags(api *operations.ConsoleAPI) {
|
||||||
api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
|
api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{
|
||||||
{
|
{
|
||||||
@@ -251,8 +256,6 @@ func FileServerMiddleware(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var reHrefIndex = regexp.MustCompile(`(?m)((href|src)="(.\/).*?")`)
|
|
||||||
|
|
||||||
type notFoundRedirectRespWr struct {
|
type notFoundRedirectRespWr struct {
|
||||||
http.ResponseWriter // We embed http.ResponseWriter
|
http.ResponseWriter // We embed http.ResponseWriter
|
||||||
status int
|
status int
|
||||||
@@ -274,9 +277,15 @@ func (w *notFoundRedirectRespWr) Write(p []byte) (int, error) {
|
|||||||
|
|
||||||
func handleSPA(w http.ResponseWriter, r *http.Request) {
|
func handleSPA(w http.ResponseWriter, r *http.Request) {
|
||||||
basePath := "/"
|
basePath := "/"
|
||||||
// For SPA mode we will replace relative paths with absolute unless we receive query param cp=y
|
// For SPA mode we will replace root base with a sub path if configured unless we received cp=y and cpb=/NEW/BASE
|
||||||
if v := r.URL.Query().Get("cp"); v == "y" {
|
if v := r.URL.Query().Get("cp"); v == "y" {
|
||||||
basePath = "./"
|
if base := r.URL.Query().Get("cpb"); base != "" {
|
||||||
|
// make sure the subpath has a trailing slash
|
||||||
|
if !strings.HasSuffix(base, "/") {
|
||||||
|
base = fmt.Sprintf("%s/", base)
|
||||||
|
}
|
||||||
|
basePath = base
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
indexPage, err := portal_ui.GetStaticAssets().Open("build/index.html")
|
indexPage, err := portal_ui.GetStaticAssets().Open("build/index.html")
|
||||||
@@ -291,16 +300,21 @@ func handleSPA(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if basePath != "./" {
|
// if we have a seeded basePath. This should override CONSOLE_SUBPATH every time, thus the `if else`
|
||||||
indexPageStr := string(indexPageBytes)
|
if basePath != "/" {
|
||||||
for _, match := range reHrefIndex.FindAllStringSubmatch(indexPageStr, -1) {
|
indexPageBytes = replaceBaseInIndex(indexPageBytes, basePath)
|
||||||
toReplace := strings.Replace(match[1], match[3], basePath, 1)
|
// if we have a custom subpath replace it in
|
||||||
indexPageStr = strings.Replace(indexPageStr, match[1], toReplace, 1)
|
} else if getSubPath() != "/" {
|
||||||
}
|
indexPageBytes = replaceBaseInIndex(indexPageBytes, getSubPath())
|
||||||
indexPageBytes = []byte(indexPageStr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
mimeType := mimedb.TypeByExtension(filepath.Ext(r.URL.Path))
|
||||||
|
|
||||||
|
if mimeType == "application/octet-stream" {
|
||||||
|
mimeType = "text/html"
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", mimeType)
|
||||||
http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(indexPageBytes))
|
http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(indexPageBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,3 +349,24 @@ func configureServer(s *http.Server, _, _ string) {
|
|||||||
// Turn-off random logging by Go net/http
|
// Turn-off random logging by Go net/http
|
||||||
s.ErrorLog = log.New(&nullWriter{}, "", 0)
|
s.ErrorLog = log.New(&nullWriter{}, "", 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getSubPath() string {
|
||||||
|
subPathOnce.Do(func() {
|
||||||
|
if v := os.Getenv("CONSOLE_SUBPATH"); v != "" {
|
||||||
|
// make sure the subpath has a trailing slash
|
||||||
|
if !strings.HasSuffix(v, "/") {
|
||||||
|
v = fmt.Sprintf("%s/", v)
|
||||||
|
}
|
||||||
|
subPath = v
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return subPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceBaseInIndex(indexPageBytes []byte, basePath string) []byte {
|
||||||
|
indexPageStr := string(indexPageBytes)
|
||||||
|
newBase := fmt.Sprintf("<base href=\"%s\"/>", basePath)
|
||||||
|
indexPageStr = strings.Replace(indexPageStr, "<base href=\"/\"/>", newBase, 1)
|
||||||
|
indexPageBytes = []byte(indexPageStr)
|
||||||
|
return indexPageBytes
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user