mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-19 18:05:41 -05:00
refactor(ui): posthog as composable and option for ui telemetry
relate to kestra-io/kestra-ee#4831
This commit is contained in:
@@ -235,6 +235,9 @@ kestra:
|
||||
traces:
|
||||
root: DISABLED
|
||||
|
||||
ui-anonymous-usage-report:
|
||||
enabled: true
|
||||
|
||||
anonymous-usage-report:
|
||||
enabled: true
|
||||
uri: https://api.kestra.io/v1/reports/server-events
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import VueTour from "./components/onboarding/VueTour.vue";
|
||||
import DefaultLayout from "override/components/layout/DefaultLayout.vue";
|
||||
import DocIdDisplay from "./components/DocIdDisplay.vue";
|
||||
import posthog from "posthog-js";
|
||||
import "@kestra-io/ui-libs/style.css";
|
||||
|
||||
import {useApiStore} from "./stores/api";
|
||||
@@ -25,6 +24,7 @@
|
||||
import {useLayoutStore} from "./stores/layout";
|
||||
import {useCoreStore} from "./stores/core";
|
||||
import {useDocStore} from "./stores/doc";
|
||||
import {initPostHogForSetup} from "./composables/usePosthog";
|
||||
import {useMiscStore} from "override/stores/misc";
|
||||
import {useExecutionsStore} from "./stores/executions";
|
||||
import * as BasicAuth from "./utils/basicAuth";
|
||||
@@ -116,63 +116,10 @@
|
||||
uid: uid,
|
||||
});
|
||||
|
||||
const apiConfig = await this.apiStore.loadConfig();
|
||||
this.initStats(apiConfig, config, uid);
|
||||
await initPostHogForSetup(config);
|
||||
|
||||
return config;
|
||||
},
|
||||
initStats(apiConfig, config, uid) {
|
||||
if (this.miscStore.configs["isAnonymousUsageEnabled"] === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only run posthog in production
|
||||
if (import.meta.env.MODE === "production") {
|
||||
posthog.init(
|
||||
apiConfig.posthog.token,
|
||||
{
|
||||
api_host: apiConfig.posthog.apiHost,
|
||||
ui_host: "https://eu.posthog.com",
|
||||
capture_pageview: false,
|
||||
capture_pageleave: true,
|
||||
autocapture: false,
|
||||
}
|
||||
)
|
||||
|
||||
posthog.register_once(this.statsGlobalData(config, uid));
|
||||
|
||||
if (!posthog.get_property("__alias")) {
|
||||
posthog.alias(apiConfig.id);
|
||||
}
|
||||
}
|
||||
|
||||
let surveyVisible = false;
|
||||
window.addEventListener("PHSurveyShown", () => {
|
||||
surveyVisible = true;
|
||||
});
|
||||
|
||||
window.addEventListener("PHSurveyClosed", () => {
|
||||
surveyVisible = false;
|
||||
})
|
||||
|
||||
window.addEventListener("KestraRouterAfterEach", () => {
|
||||
if (surveyVisible) {
|
||||
window.dispatchEvent(new Event("PHSurveyClosed"))
|
||||
surveyVisible = false;
|
||||
}
|
||||
})
|
||||
},
|
||||
statsGlobalData(config, uid) {
|
||||
return {
|
||||
from: "APP",
|
||||
iid: config.uuid,
|
||||
uid: uid,
|
||||
app: {
|
||||
version: config.version,
|
||||
type: "OSS"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
import MailChecker from "mailchecker"
|
||||
import {useMiscStore} from "override/stores/misc"
|
||||
import {useSurveySkip} from "../../composables/useSurveyData"
|
||||
import {initPostHogForSetup, trackSetupEvent} from "../../utils/setupPosthog"
|
||||
import {initPostHogForSetup, trackSetupEvent} from "../../composables/usePosthog"
|
||||
|
||||
import Cogs from "vue-material-design-icons/Cogs.vue"
|
||||
import AccountPlus from "vue-material-design-icons/AccountPlus.vue"
|
||||
@@ -312,9 +312,9 @@
|
||||
const setupConfigurationLines = computed<ConfigLine[]>(() => {
|
||||
if (!setupConfiguration.value) return []
|
||||
const configs = miscStore.configs
|
||||
|
||||
|
||||
const basicAuthValue = activeStep.value >= 1 || configs?.isBasicAuthInitialized
|
||||
|
||||
|
||||
return [
|
||||
{name: "repository", icon: Database, value: setupConfiguration.value.repositoryType || "NOT SETUP"},
|
||||
{name: "queue", icon: CurrentDc, value: setupConfiguration.value.queueType || "NOT SETUP"},
|
||||
@@ -420,9 +420,9 @@
|
||||
user_email: userFormData.value.username
|
||||
}, userFormData.value)
|
||||
|
||||
|
||||
|
||||
localStorage.setItem("basicAuthUserCreated", "true")
|
||||
|
||||
|
||||
nextStep()
|
||||
} catch (error: any) {
|
||||
trackSetupEvent("setup_flow:account_creation_failed", {
|
||||
|
||||
@@ -16,17 +16,31 @@ interface UserFormData {
|
||||
|
||||
interface Config {
|
||||
isAnonymousUsageEnabled?: boolean
|
||||
isUiAnonymousUsageEnabled?: boolean
|
||||
uuid?: string
|
||||
version?: string
|
||||
edition?: string
|
||||
}
|
||||
|
||||
function statsGlobalData(config: Config, uid: string): any {
|
||||
return {
|
||||
from: "APP",
|
||||
iid: config.uuid,
|
||||
uid: uid,
|
||||
app: {
|
||||
version: config.version,
|
||||
type: config.edition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function initPostHogForSetup(config: Config): Promise<void> {
|
||||
try {
|
||||
if (!config?.isAnonymousUsageEnabled) return
|
||||
if (!config.isUiAnonymousUsageEnabled) return
|
||||
|
||||
const apiStore = useApiStore()
|
||||
const apiConfig = await apiStore.loadConfig()
|
||||
|
||||
|
||||
if (!apiConfig?.posthog?.token || (window as any).posthog?.__loaded) return
|
||||
|
||||
const uid = getUID()
|
||||
@@ -42,22 +56,40 @@ export async function initPostHogForSetup(config: Config): Promise<void> {
|
||||
autocapture: false,
|
||||
})
|
||||
|
||||
posthog.register_once(statsGlobalData(config, uid));
|
||||
|
||||
if (!posthog.get_property("__alias")) {
|
||||
posthog.alias(apiConfig.id)
|
||||
}
|
||||
|
||||
let surveyVisible = false;
|
||||
window.addEventListener("PHSurveyShown", () => {
|
||||
surveyVisible = true;
|
||||
});
|
||||
|
||||
window.addEventListener("PHSurveyClosed", () => {
|
||||
surveyVisible = false;
|
||||
})
|
||||
|
||||
window.addEventListener("KestraRouterAfterEach", () => {
|
||||
if (surveyVisible) {
|
||||
window.dispatchEvent(new Event("PHSurveyClosed"))
|
||||
surveyVisible = false;
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize PostHog:", error)
|
||||
}
|
||||
}
|
||||
|
||||
export function trackSetupEvent(
|
||||
eventName: string,
|
||||
additionalData: Record<string, any>,
|
||||
eventName: string,
|
||||
additionalData: Record<string, any>,
|
||||
userFormData: UserFormData
|
||||
): void {
|
||||
const miscStore = useMiscStore()
|
||||
const uid = getUID()
|
||||
|
||||
|
||||
if (!miscStore.configs?.isAnonymousUsageEnabled || !uid) return
|
||||
|
||||
const userInfo = userFormData.firstName ? {
|
||||
@@ -44,56 +44,59 @@ import java.util.Optional;
|
||||
public class MiscController {
|
||||
@Inject
|
||||
protected ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@Inject
|
||||
VersionProvider versionProvider;
|
||||
|
||||
|
||||
@Inject
|
||||
DashboardRepositoryInterface dashboardRepository;
|
||||
|
||||
|
||||
@Inject
|
||||
ExecutionRepositoryInterface executionRepository;
|
||||
|
||||
|
||||
@Inject
|
||||
InstanceService instanceService;
|
||||
|
||||
|
||||
@Inject
|
||||
FeatureUsageReport featureUsageReport;
|
||||
|
||||
|
||||
@Inject
|
||||
BasicAuthService basicAuthService;
|
||||
|
||||
|
||||
@Inject
|
||||
Optional<TemplateRepositoryInterface> templateRepository;
|
||||
|
||||
|
||||
@Inject
|
||||
NamespaceUtils namespaceUtils;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.anonymous-usage-report.enabled}")
|
||||
protected Boolean isAnonymousUsageEnabled;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.ui-anonymous-usage-report.enabled}")
|
||||
protected Boolean isUiAnonymousUsageEnabled;
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.environment.name}")
|
||||
@Nullable
|
||||
protected String environmentName;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.environment.color}")
|
||||
@Nullable
|
||||
protected String environmentColor;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.url}")
|
||||
@Nullable
|
||||
protected String kestraUrl;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.server.preview.initial-rows:100}")
|
||||
private Integer initialPreviewRows;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.server.preview.max-rows:5000}")
|
||||
private Integer maxPreviewRows;
|
||||
|
||||
|
||||
@io.micronaut.context.annotation.Value("${kestra.hidden-labels.prefixes:}")
|
||||
private List<String> hiddenLabelsPrefixes;
|
||||
|
||||
|
||||
|
||||
|
||||
@Get("/configs")
|
||||
@ExecuteOn(TaskExecutors.IO)
|
||||
@Operation(tags = {"Misc"}, summary = "Retrieve the instance configuration.", description = "Global endpoint available to all users.")
|
||||
@@ -101,12 +104,14 @@ public class MiscController {
|
||||
Configuration.ConfigurationBuilder<?, ?> builder = Configuration
|
||||
.builder()
|
||||
.uuid(instanceService.fetch())
|
||||
.edition(Edition.OSS)
|
||||
.version(versionProvider.getVersion())
|
||||
.commitId(versionProvider.getRevision())
|
||||
.commitDate(versionProvider.getDate())
|
||||
.isCustomDashboardsEnabled(dashboardRepository.isEnabled())
|
||||
.isTaskRunEnabled(executionRepository.isTaskRunEnabled())
|
||||
.isAnonymousUsageEnabled(this.isAnonymousUsageEnabled)
|
||||
.isUiAnonymousUsageEnabled(this.isUiAnonymousUsageEnabled)
|
||||
.isTemplateEnabled(templateRepository.isPresent())
|
||||
.preview(Preview.builder()
|
||||
.initial(this.initialPreviewRows)
|
||||
@@ -118,7 +123,7 @@ public class MiscController {
|
||||
.resourceToFilters(QueryFilter.Resource.asResourceList())
|
||||
.hiddenLabelsPrefixes(hiddenLabelsPrefixes)
|
||||
.url(kestraUrl);
|
||||
|
||||
|
||||
if (this.environmentName != null || this.environmentColor != null) {
|
||||
builder.environment(
|
||||
Environment.builder()
|
||||
@@ -127,10 +132,15 @@ public class MiscController {
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
public enum Edition {
|
||||
OSS,
|
||||
EE
|
||||
}
|
||||
|
||||
@Get("/{tenant}/usages/all")
|
||||
@ExecuteOn(TaskExecutors.IO)
|
||||
@Operation(tags = {"Misc"}, summary = "Retrieve instance usage information")
|
||||
@@ -142,7 +152,7 @@ public class MiscController {
|
||||
.executions(event.getExecutions())
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Post(uri = "/{tenant}/basicAuth")
|
||||
@ExecuteOn(TaskExecutors.IO)
|
||||
@Operation(tags = {"Misc"}, summary = "Configure basic authentication for the instance.", description = "Sets up basic authentication credentials.")
|
||||
@@ -150,73 +160,78 @@ public class MiscController {
|
||||
@RequestBody(description = "") @Body BasicAuthCredentials basicAuthCredentials
|
||||
) {
|
||||
basicAuthService.save(basicAuthCredentials.getUid(), new BasicAuthService.BasicAuthConfiguration(basicAuthCredentials.getUsername(), basicAuthCredentials.getPassword()));
|
||||
|
||||
|
||||
return HttpResponse.noContent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Get("/basicAuthValidationErrors")
|
||||
@ExecuteOn(TaskExecutors.IO)
|
||||
@Operation(tags = {"Misc"}, summary = "Retrieve the instance configuration.", description = "Global endpoint available to all users.")
|
||||
public List<String> getBasicAuthConfigErrors() {
|
||||
return basicAuthService.validationErrors();
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public static class Configuration {
|
||||
String uuid;
|
||||
|
||||
|
||||
String version;
|
||||
|
||||
|
||||
Edition edition;
|
||||
|
||||
String commitId;
|
||||
|
||||
|
||||
ZonedDateTime commitDate;
|
||||
|
||||
|
||||
@JsonInclude
|
||||
Boolean isCustomDashboardsEnabled;
|
||||
|
||||
|
||||
@JsonInclude
|
||||
Boolean isTaskRunEnabled;
|
||||
|
||||
|
||||
@JsonInclude
|
||||
Boolean isAnonymousUsageEnabled;
|
||||
|
||||
|
||||
@JsonInclude
|
||||
Boolean isUiAnonymousUsageEnabled;
|
||||
|
||||
@JsonInclude
|
||||
Boolean isTemplateEnabled;
|
||||
|
||||
|
||||
Environment environment;
|
||||
|
||||
|
||||
String url;
|
||||
|
||||
|
||||
Preview preview;
|
||||
|
||||
|
||||
String systemNamespace;
|
||||
|
||||
|
||||
List<String> hiddenLabelsPrefixes;
|
||||
// List of filter by component
|
||||
List<QueryFilter.ResourceField> resourceToFilters;
|
||||
|
||||
|
||||
Boolean isAiEnabled;
|
||||
|
||||
|
||||
Boolean isBasicAuthInitialized;
|
||||
}
|
||||
|
||||
|
||||
@Value
|
||||
@Builder(toBuilder = true)
|
||||
public static class Environment {
|
||||
String name;
|
||||
String color;
|
||||
}
|
||||
|
||||
|
||||
@Value
|
||||
@Builder(toBuilder = true)
|
||||
public static class Preview {
|
||||
Integer initial;
|
||||
Integer max;
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class BasicAuthCredentials {
|
||||
@@ -224,7 +239,7 @@ public class MiscController {
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
||||
|
||||
|
||||
@SuperBuilder(toBuilder = true)
|
||||
@Getter
|
||||
public static class ApiUsage {
|
||||
|
||||
Reference in New Issue
Block a user