feat: support Userpilot reports
Some checks failed
default / default (push) Has been cancelled
default / e2e-backups (push) Has been cancelled
default / e2e-cluster-import (push) Has been cancelled
default / e2e-forced-removal (push) Has been cancelled
default / e2e-omni-upgrade (push) Has been cancelled
default / e2e-scaling (push) Has been cancelled
default / e2e-short (push) Has been cancelled
default / e2e-short-secureboot (push) Has been cancelled
default / e2e-templates (push) Has been cancelled
default / e2e-upgrades (push) Has been cancelled
default / e2e-workload-proxy (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled
e2e-workload-proxy-cron / default (push) Has been cancelled
e2e-upgrades-cron / default (push) Has been cancelled
e2e-templates-cron / default (push) Has been cancelled
e2e-short-secureboot-cron / default (push) Has been cancelled
e2e-short-cron / default (push) Has been cancelled
e2e-scaling-cron / default (push) Has been cancelled
e2e-omni-upgrade-cron / default (push) Has been cancelled
e2e-forced-removal-cron / default (push) Has been cancelled
e2e-cluster-import-cron / default (push) Has been cancelled
e2e-backups-cron / default (push) Has been cancelled
Lock old issues / action (push) Has been cancelled

Omni now has `--user-pilot-app-token` flag. When set it will enable
`Userpilot` integration in the UI.

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
This commit is contained in:
Artem Chernyshev 2025-08-26 18:22:54 +03:00
parent 9f0f15aafb
commit de144e3f6b
No known key found for this signature in database
GPG Key ID: 9BAC0E08F5067BB8
18 changed files with 809 additions and 319 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1023,6 +1023,13 @@ message FeaturesConfigSpec {
// ImageFactoryBaseUrl is the base URL of the image factory.
string image_factory_base_url = 5;
// UserPilotSettings enables UserPilot on the frontend side.
UserPilotSettings user_pilot_settings = 6;
}
message UserPilotSettings {
string app_token = 1;
}
message EtcdBackupSettings {

View File

@ -1708,6 +1708,7 @@ func (m *FeaturesConfigSpec) CloneVT() *FeaturesConfigSpec {
r.EmbeddedDiscoveryService = m.EmbeddedDiscoveryService
r.AuditLogEnabled = m.AuditLogEnabled
r.ImageFactoryBaseUrl = m.ImageFactoryBaseUrl
r.UserPilotSettings = m.UserPilotSettings.CloneVT()
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
@ -1719,6 +1720,23 @@ func (m *FeaturesConfigSpec) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *UserPilotSettings) CloneVT() *UserPilotSettings {
if m == nil {
return (*UserPilotSettings)(nil)
}
r := new(UserPilotSettings)
r.AppToken = m.AppToken
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
}
return r
}
func (m *UserPilotSettings) CloneMessageVT() proto.Message {
return m.CloneVT()
}
func (m *EtcdBackupSettings) CloneVT() *EtcdBackupSettings {
if m == nil {
return (*EtcdBackupSettings)(nil)
@ -4925,6 +4943,9 @@ func (this *FeaturesConfigSpec) EqualVT(that *FeaturesConfigSpec) bool {
if this.ImageFactoryBaseUrl != that.ImageFactoryBaseUrl {
return false
}
if !this.UserPilotSettings.EqualVT(that.UserPilotSettings) {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
@ -4935,6 +4956,25 @@ func (this *FeaturesConfigSpec) EqualMessageVT(thatMsg proto.Message) bool {
}
return this.EqualVT(that)
}
func (this *UserPilotSettings) EqualVT(that *UserPilotSettings) bool {
if this == that {
return true
} else if this == nil || that == nil {
return false
}
if this.AppToken != that.AppToken {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
func (this *UserPilotSettings) EqualMessageVT(thatMsg proto.Message) bool {
that, ok := thatMsg.(*UserPilotSettings)
if !ok {
return false
}
return this.EqualVT(that)
}
func (this *EtcdBackupSettings) EqualVT(that *EtcdBackupSettings) bool {
if this == that {
return true
@ -10727,6 +10767,16 @@ func (m *FeaturesConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if m.UserPilotSettings != nil {
size, err := m.UserPilotSettings.MarshalToSizedBufferVT(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
i--
dAtA[i] = 0x32
}
if len(m.ImageFactoryBaseUrl) > 0 {
i -= len(m.ImageFactoryBaseUrl)
copy(dAtA[i:], m.ImageFactoryBaseUrl)
@ -10777,6 +10827,46 @@ func (m *FeaturesConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *UserPilotSettings) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *UserPilotSettings) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *UserPilotSettings) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.AppToken) > 0 {
i -= len(m.AppToken)
copy(dAtA[i:], m.AppToken)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AppToken)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *EtcdBackupSettings) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
@ -14824,6 +14914,24 @@ func (m *FeaturesConfigSpec) SizeVT() (n int) {
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
if m.UserPilotSettings != nil {
l = m.UserPilotSettings.SizeVT()
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *UserPilotSettings) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.AppToken)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -27499,6 +27607,125 @@ func (m *FeaturesConfigSpec) UnmarshalVT(dAtA []byte) error {
}
m.ImageFactoryBaseUrl = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field UserPilotSettings", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.UserPilotSettings == nil {
m.UserPilotSettings = &UserPilotSettings{}
}
if err := m.UserPilotSettings.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *UserPilotSettings) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: UserPilotSettings: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: UserPilotSettings: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field AppToken", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.AppToken = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])

View File

@ -26,6 +26,7 @@ type CurrentUserSpec struct {
state protoimpl.MessageState `protogen:"open.v1"`
Identity string `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"`
Role string `protobuf:"bytes,3,opt,name=role,proto3" json:"role,omitempty"`
UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -74,6 +75,13 @@ func (x *CurrentUserSpec) GetRole() string {
return ""
}
func (x *CurrentUserSpec) GetUserId() string {
if x != nil {
return x.UserId
}
return ""
}
type PermissionsSpec struct {
state protoimpl.MessageState `protogen:"open.v1"`
CanReadClusters bool `protobuf:"varint,1,opt,name=can_read_clusters,json=canReadClusters,proto3" json:"can_read_clusters,omitempty"`
@ -466,10 +474,11 @@ var File_omni_specs_virtual_proto protoreflect.FileDescriptor
const file_omni_specs_virtual_proto_rawDesc = "" +
"\n" +
"\x18omni/specs/virtual.proto\x12\x05specs\"G\n" +
"\x18omni/specs/virtual.proto\x12\x05specs\"`\n" +
"\x0fCurrentUserSpec\x12\x1a\n" +
"\bidentity\x18\x01 \x01(\tR\bidentity\x12\x12\n" +
"\x04role\x18\x03 \x01(\tR\x04roleJ\x04\b\x02\x10\x03\"\xdb\x04\n" +
"\x04role\x18\x03 \x01(\tR\x04role\x12\x17\n" +
"\auser_id\x18\x04 \x01(\tR\x06userIdJ\x04\b\x02\x10\x03\"\xdb\x04\n" +
"\x0fPermissionsSpec\x12*\n" +
"\x11can_read_clusters\x18\x01 \x01(\bR\x0fcanReadClusters\x12.\n" +
"\x13can_create_clusters\x18\x02 \x01(\bR\x11canCreateClusters\x12(\n" +

View File

@ -7,6 +7,7 @@ message CurrentUserSpec {
string identity = 1;
reserved 2;
string role = 3;
string user_id = 4;
}
message PermissionsSpec {

View File

@ -27,6 +27,7 @@ func (m *CurrentUserSpec) CloneVT() *CurrentUserSpec {
r := new(CurrentUserSpec)
r.Identity = m.Identity
r.Role = m.Role
r.UserId = m.UserId
if len(m.unknownFields) > 0 {
r.unknownFields = make([]byte, len(m.unknownFields))
copy(r.unknownFields, m.unknownFields)
@ -166,6 +167,9 @@ func (this *CurrentUserSpec) EqualVT(that *CurrentUserSpec) bool {
if this.Role != that.Role {
return false
}
if this.UserId != that.UserId {
return false
}
return string(this.unknownFields) == string(that.unknownFields)
}
@ -387,6 +391,13 @@ func (m *CurrentUserSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.UserId) > 0 {
i -= len(m.UserId)
copy(dAtA[i:], m.UserId)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.UserId)))
i--
dAtA[i] = 0x22
}
if len(m.Role) > 0 {
i -= len(m.Role)
copy(dAtA[i:], m.Role)
@ -851,6 +862,10 @@ func (m *CurrentUserSpec) SizeVT() (n int) {
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
l = len(m.UserId)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
@ -1090,6 +1105,38 @@ func (m *CurrentUserSpec) UnmarshalVT(dAtA []byte) error {
}
m.Role = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field UserId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.UserId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])

View File

@ -138,6 +138,7 @@ func newCommand() *cobra.Command {
rootCmd.Flags().StringVar(&cmdConfig.Account.ID, "account-id", cmdConfig.Account.ID, "instance account ID, should never be changed.")
rootCmd.Flags().StringVar(&cmdConfig.Account.Name, "name", cmdConfig.Account.Name, "instance user-facing name.")
rootCmd.Flags().StringVar(&cmdConfig.Account.UserPilot.AppToken, "user-pilot-app-token", cmdConfig.Account.UserPilot.AppToken, "user pilot app token.")
defineServiceFlags()
defineAuthFlags()

View File

@ -21,6 +21,7 @@
"monaco-editor": "^0.52.2",
"monaco-yaml": "^5.3.1",
"pluralize": "^8.0.0",
"userpilot": "^1.4.1",
"uuid": "^11.1.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1",
@ -1572,6 +1573,36 @@
"vue": "^3.2.26"
}
},
"node_modules/@ndhoule/each": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@ndhoule/each/-/each-2.0.1.tgz",
"integrity": "sha512-wHuJw6x+rF6Q9Skgra++KccjBozCr9ymtna0FhxmV/8xT/hZ2ExGYR8SV8prg8x4AH/7mzDYErNGIVHuzHeybw==",
"license": "MIT",
"dependencies": {
"@ndhoule/keys": "^2.0.0"
}
},
"node_modules/@ndhoule/includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@ndhoule/includes/-/includes-2.0.1.tgz",
"integrity": "sha512-Q8zN6f3yIhxgBwZ5ldLozHqJlc/fRQ5+hFFsPMFeC9SJvz0nq8vG9hoRXL1c1iaNFQd7yAZIy2igQpERoFqxqg==",
"license": "MIT",
"dependencies": {
"@ndhoule/each": "^2.0.1"
}
},
"node_modules/@ndhoule/keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@ndhoule/keys/-/keys-2.0.0.tgz",
"integrity": "sha512-vtCqKBC1Av6dsBA8xpAO+cgk051nfaI+PnmTZep2Px0vYrDvpUmLxv7z40COlWH5yCpu3gzNhepk+02yiQiZNw==",
"license": "MIT"
},
"node_modules/@ndhoule/pick": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@ndhoule/pick/-/pick-2.0.0.tgz",
"integrity": "sha512-xkYtpf1pRd8egwvl5tJcdGu+GBd6ZZH3S/zoIQ9txEI+pHF9oTIlxMC9G4CB3sRugAeLgu8qYJGl3tnxWq74Qw==",
"license": "MIT"
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -3937,6 +3968,11 @@
"dev": true,
"license": "MIT"
},
"node_modules/component-indexof": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw=="
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@ -5488,6 +5524,15 @@
"dev": true,
"license": "ISC"
},
"node_modules/is": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/is/-/is-3.3.2.tgz",
"integrity": "sha512-a2xr4E3s1PjDS8ORcGgXpWx6V+liNs+O3JRD2mb9aeugD7rtkkZ0zgLdYgw0tWsKhsdiezGYptSiMlVazCBTuQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/is-arguments": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
@ -6608,6 +6653,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/obj-case": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/obj-case/-/obj-case-0.2.1.tgz",
"integrity": "sha512-PquYBBTy+Y6Ob/O2574XHhDtHJlV1cJHMCgW+rDRc9J5hhmRelJB3k5dTK/3cVmFVtzvAKuENeuLpoyTzMzkOg==",
"license": "MIT"
},
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@ -8321,6 +8372,19 @@
"node": ">= 0.4"
}
},
"node_modules/userpilot": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/userpilot/-/userpilot-1.4.1.tgz",
"integrity": "sha512-I6tYwPCbOAhqZsdIDWmOdxHKtDvxnCLa1cIO4Lcy/qZG1kXhTiHYaoHA58xpbh6I1wmkOn3hbkIs71f+/JRvWg==",
"license": "MIT",
"dependencies": {
"@ndhoule/includes": "^2.0.1",
"@ndhoule/pick": "^2.0.0",
"component-indexof": "0.0.3",
"is": "^3.1.0",
"obj-case": "^0.2.0"
}
},
"node_modules/util": {
"version": "0.12.5",
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",

View File

@ -28,6 +28,7 @@
"monaco-editor": "^0.52.2",
"monaco-yaml": "^5.3.1",
"pluralize": "^8.0.0",
"userpilot": "^1.4.1",
"uuid": "^11.1.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1",

View File

@ -671,6 +671,11 @@ export type FeaturesConfigSpec = {
embedded_discovery_service?: boolean
audit_log_enabled?: boolean
image_factory_base_url?: string
user_pilot_settings?: UserPilotSettings
}
export type UserPilotSettings = {
app_token?: string
}
export type EtcdBackupSettings = {

View File

@ -6,6 +6,7 @@
export type CurrentUserSpec = {
identity?: string
role?: string
user_id?: string
}
export type PermissionsSpec = {

View File

@ -27,6 +27,8 @@ import {
VirtualNamespace,
} from '@/api/resources'
import { initializeUserPilot } from './features'
export const currentUser: Ref<Resource<CurrentUserSpec> | undefined> = ref()
export const permissions: Ref<Resource<PermissionsSpec> | undefined> = ref()
@ -166,6 +168,8 @@ const refreshCurrentUser = async () => {
},
withRuntime(Runtime.Omni),
)
await initializeUserPilot(currentUser.value)
} catch {
currentUser.value = undefined
}

View File

@ -3,6 +3,7 @@
// Use of this software is governed by the Business Source License
// included in the LICENSE file.
import { Userpilot } from 'userpilot'
import type { Ref } from 'vue'
import { computed, ref } from 'vue'
@ -10,7 +11,8 @@ import { Runtime } from '@/api/common/omni.pb'
import type { Resource } from '@/api/grpc'
import { ResourceService } from '@/api/grpc'
import type { FeaturesConfigSpec } from '@/api/omni/specs/omni.pb'
import { withRuntime } from '@/api/options'
import type { CurrentUserSpec } from '@/api/omni/specs/virtual.pb'
import { withAbortController, withRuntime } from '@/api/options'
import { DefaultNamespace, FeaturesConfigID, FeaturesConfigType } from '@/api/resources'
import Watch from '@/api/watch'
@ -31,6 +33,40 @@ export const setupWorkloadProxyingEnabledFeatureWatch = (): Ref<boolean> => {
})
}
let userPilotInitialized = false
let userPilotInitializeAbortController: AbortController | null = null
export const initializeUserPilot = async (user: Resource<CurrentUserSpec>) => {
if (!userPilotInitialized) {
userPilotInitializeAbortController?.abort()
userPilotInitializeAbortController = new AbortController()
const featuresConfig = await ResourceService.Get<Resource<FeaturesConfigSpec>>(
{
type: FeaturesConfigType,
namespace: DefaultNamespace,
id: FeaturesConfigID,
},
withRuntime(Runtime.Omni),
withAbortController(userPilotInitializeAbortController),
)
const token = featuresConfig.spec?.user_pilot_settings?.app_token
if (!token) {
return
}
Userpilot.initialize(token)
userPilotInitialized = true
}
Userpilot.identify(user.spec.user_id!, {
role: user.spec.role!,
})
}
let cachedFeaturesConfig: Resource<FeaturesConfigSpec> | undefined
export const embeddedDiscoveryServiceFeatureAvailable = async (): Promise<boolean> => {

View File

@ -4,6 +4,7 @@
// included in the LICENSE file.
import { authGuard } from '@auth0/auth0-vue'
import { Userpilot } from 'userpilot'
import type { RouteLocation, RouteLocationRaw, RouteMeta, RouteRecordRaw } from 'vue-router'
import { createRouter, createWebHistory } from 'vue-router'
@ -462,6 +463,10 @@ router.beforeEach((to) => {
return true
})
router.afterEach(() => {
Userpilot.reload()
})
const modals = {
reboot: NodeReboot,
shutdown: NodeShutdown,

View File

@ -185,6 +185,17 @@ func (v *State) currentUser(ctx context.Context) (*virtual.CurrentUser, error) {
user.Metadata().SetVersion(version)
identity, err := safe.ReaderGetByID[*authres.Identity](ctx, v.PrimaryState, identityVal.Identity)
if err != nil {
if state.IsNotFoundError(err) {
return user, nil
}
return nil, err
}
user.TypedSpec().Value.UserId = identity.TypedSpec().Value.UserId
return user, nil
}

View File

@ -110,8 +110,8 @@ func (handler *StaticHandler) serveFile(w http.ResponseWriter, r *http.Request,
} else {
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
w.Header().Set("Content-Security-Policy", "default-src 'self'; img-src * data: ; "+
";connect-src 'self' https://*.auth0.com ;font-src 'self' data: "+
w.Header().Set("Content-Security-Policy", "default-src 'self' https://*.userpilot.io; img-src * data: ; "+
";connect-src 'self' https://*.auth0.com https://*.userpilot.io wss://*.userpilot.io ;font-src 'self' data: "+
";style-src 'self' 'unsafe-inline' https://fonts.googleapis.com data: ;upgrade-insecure-requests;"+
";frame-src https://*.auth0.com",
)

View File

@ -297,6 +297,16 @@ type Account struct {
// Omni will use to present some information to the user.
// Name can be changed at any time.
Name string `yaml:"name" validate:"required"`
// UserPilot configuration.
UserPilot UserPilot `yaml:"userPilot"`
}
// UserPilot describes user pilot credentials.
// If not set it is disabled.
type UserPilot struct {
// AppToken is the token used to report metrics to the userpilot service.
AppToken string `yaml:"appToken"`
}
// Registries configures docker registries to be used for the Talos and Kubernetes images.

View File

@ -34,6 +34,9 @@ func UpdateResources(ctx context.Context, st state.State, logger *zap.Logger) er
res.TypedSpec().Value.AuditLogEnabled = config.Config.Logs.Audit.Path != ""
res.TypedSpec().Value.ImageFactoryBaseUrl = config.Config.Registries.ImageFactoryBaseURL
res.TypedSpec().Value.UserPilotSettings = &specs.UserPilotSettings{
AppToken: config.Config.Account.UserPilot.AppToken,
}
return nil
}