Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PathPrefix for karmada dashboard #97

Merged
merged 4 commits into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/web/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Options struct {
I18nDir string
EnableApiProxy bool
ApiProxyEndpoint string
DashboardConfigPath string
}

func NewOptions() *Options {
Expand All @@ -34,4 +35,5 @@ func (o *Options) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&o.I18nDir, "i18n-dir", "./i18n", "directory to serve i18n files")
fs.BoolVar(&o.EnableApiProxy, "enable-api-proxy", true, "whether enable proxy to karmada-dashboard-api, if set true, all requests with /api prefix will be proxyed to karmada-dashboard-api.karmada-system.svc.cluster.local")
fs.StringVar(&o.ApiProxyEndpoint, "api-proxy-endpoint", "http://karmada-dashboard-api.karmada-system.svc.cluster.local:8000", "karmada-dashboard-api endpoint")
fs.StringVar(&o.DashboardConfigPath, "dashboard-config-path", "./config/dashboard-config.yaml", "path to dashboard config file")
}
17 changes: 12 additions & 5 deletions cmd/web/app/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/karmada-io/dashboard/cmd/api/app/router"
"github.com/karmada-io/dashboard/cmd/web/app/options"
"github.com/karmada-io/dashboard/pkg/config"
"github.com/karmada-io/dashboard/pkg/environment"
"github.com/karmada-io/karmada/pkg/sharedcli/klogflag"
"github.com/spf13/cobra"
Expand All @@ -17,9 +18,10 @@ import (
"net/url"
"os"
"path"
"strings"
)

// NewApiCommand creates a *cobra.Command object with default parameters
// NewWebCommand creates a *cobra.Command object with default parameters
func NewWebCommand(ctx context.Context) *cobra.Command {
opts := options.NewOptions()
cmd := &cobra.Command{
Expand Down Expand Up @@ -60,7 +62,7 @@ func NewWebCommand(ctx context.Context) *cobra.Command {

func run(ctx context.Context, opts *options.Options) error {
klog.InfoS("Starting Karmada Dashboard API", "version", environment.Version)

config.InitDashboardConfigFromMountFile(opts.DashboardConfigPath)
serve(opts)
select {
case <-ctx.Done():
Expand All @@ -72,27 +74,31 @@ func run(ctx context.Context, opts *options.Options) error {
func serve(opts *options.Options) {
insecureAddress := fmt.Sprintf("%s:%d", opts.InsecureBindAddress, opts.InsecurePort)
klog.V(1).InfoS("Listening and serving on", "address", insecureAddress)
pathPrefix := config.GetDashboardConfig().PathPrefix
klog.V(1).Infof("PathPrefix is:%s", pathPrefix)
go func() {
r := router.Router()
r.StaticFS("/static", http.Dir(opts.StaticDir))
g := r.Group(pathPrefix)
g.StaticFS("/static", http.Dir(opts.StaticDir))
if opts.EnableApiProxy {
// https://karmada-apiserver.karmada-system.svc.cluster.local:5443
r.Any("/api/*path", func(c *gin.Context) {
g.Any("/api/*path", func(c *gin.Context) {
remote, _ := url.Parse(opts.ApiProxyEndpoint)
proxy := httputil.NewSingleHostReverseProxy(remote)
proxy.Director = func(req *http.Request) {
req.Header = c.Request.Header
req.Host = remote.Host
req.URL.Scheme = remote.Scheme
req.URL.Host = remote.Host
req.URL.Path = strings.TrimPrefix(req.URL.Path, pathPrefix)
}
proxy.ServeHTTP(c.Writer, c.Request)
})
}
// TODO:
// currently we only mock the return i18n json, this feature will be implemented by ospp2024
// https://summer-ospp.ac.cn/org/prodetail/245c40338?lang=zh&list=pro
r.GET("/i18n/*path", func(c *gin.Context) {
g.GET("/i18n/*path", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{})
})
r.NoRoute(func(c *gin.Context) {
Expand All @@ -103,6 +109,7 @@ func serve(opts *options.Options) {
buff, readAllErr := io.ReadAll(f)
if readAllErr == nil {
indexHtml = string(buff)
indexHtml = strings.ReplaceAll(indexHtml, "{{PathPrefix}}", pathPrefix)
}
}
c.Header("Content-Type", "text/html; charset=utf-8")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ require (
github.com/samber/lo v1.39.0
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.28.5
k8s.io/apimachinery v0.28.5
k8s.io/client-go v0.28.5
Expand Down Expand Up @@ -103,7 +104,6 @@ require (
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.28.5 // indirect
k8s.io/apiserver v0.28.5 // indirect
k8s.io/cli-runtime v0.28.5 // indirect
Expand Down
21 changes: 21 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func GetDashboardConfig() DashboardConfig {
DockerRegistries: dashboardConfig.DockerRegistries,
ChartRegistries: dashboardConfig.ChartRegistries,
MenuConfigs: dashboardConfig.MenuConfigs,
PathPrefix: dashboardConfig.PathPrefix,
}
}

Expand All @@ -110,3 +111,23 @@ func UpdateDashboardConfig(k8sClient kubernetes.Interface, newDashboardConfig Da
}
return nil
}

func InitDashboardConfigFromMountFile(mountPath string) error {
_, err := os.Stat(mountPath)
if os.IsNotExist(err) {
return fmt.Errorf("%s not exist", mountPath)
}
content, err := os.ReadFile(mountPath)
if err != nil {
return err
}

var tmpConfig DashboardConfig
if err = yaml.Unmarshal(content, &tmpConfig); err != nil {
klog.Errorf("Failed to unmarshal from content %v", err)
return err
} else {
dashboardConfig = tmpConfig
return nil
}
}
1 change: 1 addition & 0 deletions pkg/config/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ type DashboardConfig struct {
DockerRegistries []DockerRegistry `yaml:"docker_registries" json:"docker_registries"`
ChartRegistries []ChartRegistry `yaml:"chart_registries" json:"chart_registries"`
MenuConfigs []MenuConfig `yaml:"menu_configs" json:"menu_configs"`
PathPrefix string `yaml:"path_prefix" json:"path_prefix"`
}
6 changes: 6 additions & 0 deletions ui/apps/dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<script>
// assume that __path_prefix__ in the form of '/xxx/aaa'
// start with slash and not end with slash
window.__path_prefix__ = '{{PathPrefix}}'
window.__dynamic_base__ = window.__path_prefix__ + '/static'
</script>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions ui/apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"vite": "^5.2.0",
"vite-plugin-dynamic-base": "^1.1.0",
"vite-plugin-svgr": "^4.2.0"
}
}
5 changes: 4 additions & 1 deletion ui/apps/dashboard/src/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { createBrowserRouter, RouterProvider } from 'react-router-dom';

import { routes } from './route.tsx';
import { routerBase } from '@/services/base';

const router = createBrowserRouter(routes);
const router = createBrowserRouter(routes, {
basename: routerBase,
});
export default function Router() {
return <RouterProvider router={router} />;
}
14 changes: 11 additions & 3 deletions ui/apps/dashboard/src/services/base.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import axios from 'axios';
import _ from 'lodash';

let pathPrefix = window.__path_prefix__ || '';
if (!pathPrefix.startsWith('/')) {
pathPrefix = '/' + pathPrefix;
}
if (!pathPrefix.endsWith('/')) {
pathPrefix = pathPrefix + '/';
}
export const routerBase = pathPrefix;
const baseURL: string = _.join([pathPrefix, 'api/v1'], '');

const baseURL: string = '/api/v1';
export const karmadaClient = axios.create({
baseURL,
});
Expand Down Expand Up @@ -76,5 +86,3 @@ export const extractPropagationPolicy = (r: { objectMeta: ObjectMeta }) => {
}
return r?.objectMeta?.annotations?.[propagationpolicyKey];
};


3 changes: 3 additions & 0 deletions ui/apps/dashboard/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
/// <reference types="vite/client" />
interface Window {
__path_prefix__: string;
}
10 changes: 9 additions & 1 deletion ui/apps/dashboard/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@ import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';
import { dynamicBase } from 'vite-plugin-dynamic-base';

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
base: process.env.NODE_ENV === 'development' ? '' : '/static',
plugins: [react(), svgr()],
plugins: [
react(),
svgr(),
dynamicBase({
publicPath: 'window.__dynamic_base__',
transformIndexHtml: true,
}),
],
resolve: {
alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }],
},
Expand Down
Loading