Add Traefik Hub button and deprecate Pilot
Co-authored-by: Tom Moulard <tom.moulard@traefik.io>
This commit is contained in:
parent
aa0b5466a9
commit
91f4ccf087
14 changed files with 21 additions and 250 deletions
|
@ -216,7 +216,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||
}
|
||||
|
||||
if staticConfiguration.Pilot != nil {
|
||||
version.PilotEnabled = staticConfiguration.Pilot.Dashboard
|
||||
log.WithoutContext().Warn("Traefik Pilot is deprecated and will be removed soon. Please check our Blog for migration instructions later this year.")
|
||||
}
|
||||
|
||||
// Plugins
|
||||
|
|
|
@ -468,3 +468,8 @@ This option available in the ForwardAuth middleware, as well as in the HTTP, Con
|
|||
### Consul Enterprise Namespaces
|
||||
|
||||
In `v2.8`, the `namespace` option of Consul and Consul Catalog providers is deprecated, please use the `namespaces` options instead.
|
||||
|
||||
### Traefik Pilot
|
||||
|
||||
In `v2.8`, the `pilot.token` and `pilot.dashboard` options are deprecated.
|
||||
Please check our Blog for migration instructions later this year.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package static
|
||||
|
||||
// Pilot Configuration related to Traefik Pilot.
|
||||
// Deprecated.
|
||||
type Pilot struct {
|
||||
Token string `description:"Traefik Pilot token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"`
|
||||
Dashboard bool `description:"Enable Traefik Pilot in the dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty"`
|
||||
|
|
|
@ -78,6 +78,7 @@ type Configuration struct {
|
|||
|
||||
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
|
||||
|
||||
// Deprecated.
|
||||
Pilot *Pilot `description:"Traefik Pilot configuration." json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty" export:"true"`
|
||||
|
||||
Hub *hub.Provider `description:"Traefik Hub configuration." json:"hub,omitempty" toml:"hub,omitempty" yaml:"hub,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
|
|
|
@ -194,8 +194,6 @@ func (c *client) SendInstanceInfo(ctx context.Context, pilotMetrics []metrics.Pi
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to create UUID: %w", err)
|
||||
}
|
||||
|
||||
version.UUID = c.uuid
|
||||
}
|
||||
|
||||
info := instanceInfo{
|
||||
|
|
|
@ -23,9 +23,6 @@ var (
|
|||
// StartDate holds the start date of traefik.
|
||||
StartDate = time.Now()
|
||||
// UUID instance uuid.
|
||||
UUID string
|
||||
// PilotEnabled activate integration of pilot into the dashboard.
|
||||
PilotEnabled bool
|
||||
)
|
||||
|
||||
// Handler expose version routes.
|
||||
|
@ -46,11 +43,9 @@ func (v Handler) Append(router *mux.Router) {
|
|||
UUID string `json:"uuid,omitempty"`
|
||||
PilotEnabled bool `json:"pilotEnabled"`
|
||||
}{
|
||||
Version: Version,
|
||||
Codename: Codename,
|
||||
StartDate: StartDate,
|
||||
UUID: UUID,
|
||||
PilotEnabled: PilotEnabled,
|
||||
Version: Version,
|
||||
Codename: Codename,
|
||||
StartDate: StartDate,
|
||||
}
|
||||
|
||||
if err := templatesRenderer.JSON(response, http.StatusOK, v); err != nil {
|
||||
|
|
|
@ -2,8 +2,6 @@ FROM node:14.16
|
|||
# Current Active LTS release according to (https://nodejs.org/en/about/releases/)
|
||||
|
||||
ENV WEBUI_DIR /src/webui
|
||||
ARG ARG_PLATFORM_URL=https://pilot.traefik.io
|
||||
ENV PLATFORM_URL=${ARG_PLATFORM_URL}
|
||||
RUN mkdir -p $WEBUI_DIR
|
||||
|
||||
COPY package.json $WEBUI_DIR/
|
||||
|
|
|
@ -118,13 +118,11 @@ module.exports = function (ctx) {
|
|||
env: process.env.APP_ENV === 'development'
|
||||
? { // staging:
|
||||
APP_ENV: JSON.stringify(process.env.APP_ENV),
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api'),
|
||||
PLATFORM_URL: JSON.stringify(process.env.PLATFORM_URL || 'https://pilot.traefik.io')
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api')
|
||||
}
|
||||
: { // production:
|
||||
APP_ENV: JSON.stringify(process.env.APP_ENV),
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api'),
|
||||
PLATFORM_URL: JSON.stringify(process.env.PLATFORM_URL || 'https://pilot.traefik.io')
|
||||
APP_API: JSON.stringify(process.env.APP_API || '/api')
|
||||
},
|
||||
uglifyOptions: {
|
||||
compress: {
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
<template>
|
||||
<div id="q-app">
|
||||
<router-view />
|
||||
<platform-panel
|
||||
v-if="pilotEnabled" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { APP } from './_helpers/APP'
|
||||
import PlatformPanel from './components/platform/PlatformPanel'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
PlatformPanel
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('core', { coreVersion: 'version' }),
|
||||
pilotEnabled () {
|
||||
return this.coreVersion.pilotEnabled
|
||||
}
|
||||
...mapGetters('core', { coreVersion: 'version' })
|
||||
},
|
||||
beforeCreate () {
|
||||
// Set vue instance
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const APP = {
|
||||
config: {
|
||||
env: process.env.APP_ENV,
|
||||
apiUrl: process.env.APP_API,
|
||||
platformUrl: process.env.PLATFORM_URL
|
||||
apiUrl: process.env.APP_API
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
</q-tabs>
|
||||
<div class="right-menu">
|
||||
<q-tabs>
|
||||
<q-btn type="a" href="https://hub.traefik.io/" target="_blank" flat no-caps label="Go to Hub Dashboard →" class="btn-menu btn-hub" />
|
||||
<q-btn @click="$q.dark.toggle()" stretch flat no-caps icon="invert_colors" :label="`${$q.dark.isActive ? 'Light' : 'Dark'} theme`" class="btn-menu" />
|
||||
<q-btn stretch flat icon="eva-question-mark-circle-outline">
|
||||
<q-menu anchor="bottom left" auto-close>
|
||||
|
@ -43,8 +44,6 @@
|
|||
</q-menu>
|
||||
</q-btn>
|
||||
</q-tabs>
|
||||
<platform-auth-state
|
||||
v-if="pilotEnabled" />
|
||||
</div>
|
||||
</q-toolbar>
|
||||
</div>
|
||||
|
@ -60,12 +59,10 @@
|
|||
|
||||
<script>
|
||||
import config from '../../../package'
|
||||
import PlatformAuthState from '../platform/PlatformAuthState'
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'NavBar',
|
||||
components: { PlatformAuthState },
|
||||
computed: {
|
||||
...mapGetters('core', { coreVersion: 'version' }),
|
||||
version () {
|
||||
|
@ -74,9 +71,6 @@ export default {
|
|||
? this.coreVersion.Version
|
||||
: this.coreVersion.Version.substring(0, 7)
|
||||
},
|
||||
pilotEnabled () {
|
||||
return this.coreVersion.pilotEnabled
|
||||
},
|
||||
parsedVersion () {
|
||||
if (!this.version) {
|
||||
return 'master'
|
||||
|
@ -159,6 +153,11 @@ export default {
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-hub {
|
||||
color: #0e204c;
|
||||
background: #deea48;
|
||||
}
|
||||
|
||||
.q-item {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<template>
|
||||
<q-btn color="accent" class="btn" @click="$emit('click', $event)" :bind="$attrs">{{label}}</q-btn>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'ConnectButton',
|
||||
props: ['label']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.btn {
|
||||
font-family: 'Nunito', 'Roboto', sans-serif;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
text-transform: inherit;
|
||||
}
|
||||
</style>
|
|
@ -1,82 +0,0 @@
|
|||
<template>
|
||||
<div class="iframe-wrapper" v-if="isOnline">
|
||||
<iframe
|
||||
id="platform-auth-state"
|
||||
:src="iFrameUrl"
|
||||
v-if="renderIrame"
|
||||
v-resize="resizeOpts"
|
||||
height="64px"
|
||||
frameBorder="0"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import qs from 'query-string'
|
||||
import '../../_directives/resize'
|
||||
|
||||
export default {
|
||||
name: 'PlatformPanel',
|
||||
data () {
|
||||
return {
|
||||
renderIrame: true,
|
||||
resizeOpts: {
|
||||
log: false,
|
||||
onMessage: ({ iframe, message }) => {
|
||||
if (typeof message === 'string') {
|
||||
// 1st condition for backward compatibility
|
||||
if (message === 'open:profile') {
|
||||
this.openPlatform('/')
|
||||
} else if (message.includes('open:')) {
|
||||
this.openPlatform(message.split('open:')[1])
|
||||
} else if (message === 'logout') {
|
||||
this.closePlatform()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getInstanceInfos()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('platform', { isPlatformOpen: 'isOpen', platformPath: 'path' }),
|
||||
...mapGetters('core', { instanceInfos: 'version' }),
|
||||
isOnline () {
|
||||
return window.navigator.onLine
|
||||
},
|
||||
iFrameUrl () {
|
||||
const instanceInfos = JSON.stringify(this.instanceInfos)
|
||||
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=${this.platformPath}`
|
||||
|
||||
return qs.stringifyUrl({ url: `${this.platformUrl}/partials/auth-state`, query: { authRedirectUrl, instanceInfos } })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('platform', { openPlatform: 'open' }, { closePlatform: 'close' }),
|
||||
...mapActions('core', { getInstanceInfos: 'getVersion' })
|
||||
},
|
||||
watch: {
|
||||
isPlatformOpen (isOpen, wasOpen) {
|
||||
if (!isOpen && wasOpen) {
|
||||
this.renderIrame = false
|
||||
this.$nextTick().then(() => {
|
||||
this.renderIrame = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../../css/sass/variables";
|
||||
|
||||
#platform-auth-state {
|
||||
width: 1px;
|
||||
min-width: 296px;
|
||||
}
|
||||
</style>
|
|
@ -1,112 +0,0 @@
|
|||
<template>
|
||||
<side-panel
|
||||
:isOpen="isPlatformOpen"
|
||||
@onClose="closePlatform()"
|
||||
v-if="isOnline"
|
||||
>
|
||||
<div class="iframe-wrapper">
|
||||
<iframe
|
||||
id="platform-iframe"
|
||||
:src="iFrameUrl"
|
||||
v-resize="resizeOpts"
|
||||
style="position: relative; height: 100%; width: 100%;"
|
||||
frameBorder="0"
|
||||
scrolling="yes"
|
||||
@load="onIFrameLoad"
|
||||
/>
|
||||
</div>
|
||||
</side-panel>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import qs from 'query-string'
|
||||
import SidePanel from '../_commons/SidePanel'
|
||||
import Helps from '../../_helpers/Helps'
|
||||
import '../../_directives/resize'
|
||||
|
||||
export default {
|
||||
name: 'PlatformPanel',
|
||||
components: {
|
||||
SidePanel
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
resizeOpts: {
|
||||
log: false,
|
||||
resize: false,
|
||||
scrolling: true,
|
||||
onMessage: ({ iframe, message }) => {
|
||||
if (typeof message === 'string') {
|
||||
// 1st condition for backward compatibility
|
||||
if (message === 'open:profile') {
|
||||
this.openPlatform('/')
|
||||
} else if (message.includes('open:')) {
|
||||
this.openPlatform(message.split('open:')[1])
|
||||
} else if (message === 'logout') {
|
||||
this.closePlatform()
|
||||
}
|
||||
} else if (message.type) {
|
||||
switch (message.type) {
|
||||
case 'copy-to-clipboard':
|
||||
navigator.clipboard.writeText(message.value)
|
||||
break
|
||||
}
|
||||
} else if (message.isAuthenticated) {
|
||||
this.isAuthenticated = message.isAuthenticated
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.getInstanceInfos()
|
||||
},
|
||||
computed: {
|
||||
...mapGetters('platform', { isPlatformOpen: 'isOpen', platformPath: 'path' }),
|
||||
...mapGetters('core', { instanceInfos: 'version' }),
|
||||
iFrameUrl () {
|
||||
const instanceInfos = JSON.stringify(this.instanceInfos)
|
||||
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=${this.platformPath}`
|
||||
|
||||
return qs.stringifyUrl({ url: `${this.platformUrl}${this.platformPath}`, query: { authRedirectUrl, instanceInfos } })
|
||||
},
|
||||
isOnline () {
|
||||
return window.navigator.onLine
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('platform', { openPlatform: 'open', closePlatform: 'close' }),
|
||||
...mapActions('core', { getInstanceInfos: 'getVersion' })
|
||||
},
|
||||
watch: {
|
||||
$route (to, from) {
|
||||
const wasOpen = from.query && from.query.platform
|
||||
const isOpen = to.query && to.query.platform
|
||||
|
||||
if (!wasOpen && isOpen) {
|
||||
this.openPlatform(to.query.platform)
|
||||
}
|
||||
},
|
||||
isPlatformOpen (newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
document.querySelector('body').style.overflow = newValue ? 'hidden' : 'visible'
|
||||
|
||||
this.$router.push({
|
||||
path: this.$route.path,
|
||||
query: Helps.removeEmptyObjects({
|
||||
...this.$route.query,
|
||||
platform: this.platformPath ? this.platformPath : undefined
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.iframe-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue