Manage Onboarding decision via DB flag (#4926)
* WIP add onboarding flag to db * dev build * fix onboarding telem call
This commit is contained in:
parent
88459ce2d2
commit
54e0cde56f
2
.github/workflows/dev-build.yaml
vendored
2
.github/workflows/dev-build.yaml
vendored
@ -6,7 +6,7 @@ concurrency:
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['4855-thinking-block-toggle'] # put your current branch to create a build. Core team only.
|
branches: ['onboarding-flag'] # put your current branch to create a build. Core team only.
|
||||||
paths-ignore:
|
paths-ignore:
|
||||||
- '**.md'
|
- '**.md'
|
||||||
- 'cloud-deployments/*'
|
- 'cloud-deployments/*'
|
||||||
|
|||||||
@ -19,27 +19,18 @@ function useIsAuthenticated() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const validateSession = async () => {
|
const validateSession = async () => {
|
||||||
const {
|
const onboardingComplete = await System.isOnboardingComplete();
|
||||||
MultiUserMode,
|
const { MultiUserMode, RequiresAuth } = await System.keys();
|
||||||
RequiresAuth,
|
|
||||||
LLMProvider = null,
|
|
||||||
VectorDB = null,
|
|
||||||
} = await System.keys();
|
|
||||||
|
|
||||||
setMultiUserMode(MultiUserMode);
|
setMultiUserMode(MultiUserMode);
|
||||||
|
|
||||||
// Check for the onboarding redirect condition
|
// Check for the onboarding redirect condition
|
||||||
if (
|
if (onboardingComplete === false) {
|
||||||
!MultiUserMode &&
|
|
||||||
!RequiresAuth && // Not in Multi-user AND no password set.
|
|
||||||
!LLMProvider &&
|
|
||||||
!VectorDB
|
|
||||||
) {
|
|
||||||
setShouldRedirectToOnboarding(true);
|
setShouldRedirectToOnboarding(true);
|
||||||
setIsAuthed(true);
|
setIsAuthed(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Single User mode without password - no auth required
|
||||||
if (!MultiUserMode && !RequiresAuth) {
|
if (!MultiUserMode && !RequiresAuth) {
|
||||||
setIsAuthed(true);
|
setIsAuthed(true);
|
||||||
return;
|
return;
|
||||||
@ -58,6 +49,7 @@ function useIsAuthenticated() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Multi-user mode checks
|
||||||
const localUser = localStorage.getItem(AUTH_USER);
|
const localUser = localStorage.getItem(AUTH_USER);
|
||||||
const localAuthToken = localStorage.getItem(AUTH_TOKEN);
|
const localAuthToken = localStorage.getItem(AUTH_TOKEN);
|
||||||
if (!localUser || !localAuthToken) {
|
if (!localUser || !localAuthToken) {
|
||||||
|
|||||||
16
frontend/src/hooks/useOnboardingComplete.js
Normal file
16
frontend/src/hooks/useOnboardingComplete.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import System from "@/models/system";
|
||||||
|
import paths from "@/utils/paths";
|
||||||
|
|
||||||
|
export default function useRedirectToHomeOnOnboardingComplete() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
useEffect(() => {
|
||||||
|
async function checkOnboardingComplete() {
|
||||||
|
const onboardingComplete = await System.isOnboardingComplete();
|
||||||
|
if (onboardingComplete === false) return;
|
||||||
|
navigate(paths.home());
|
||||||
|
}
|
||||||
|
checkOnboardingComplete();
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
@ -32,6 +32,32 @@ const System = {
|
|||||||
.then((res) => res.vectorCount)
|
.then((res) => res.vectorCount)
|
||||||
.catch(() => 0);
|
.catch(() => 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the onboarding is complete.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
isOnboardingComplete: async function () {
|
||||||
|
return await fetch(`${API_BASE}/onboarding`)
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Could not find onboarding information.");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((res) => res.onboardingComplete)
|
||||||
|
.catch(() => false);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Marks the onboarding as complete.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
markOnboardingComplete: async function () {
|
||||||
|
return await fetch(`${API_BASE}/onboarding`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: baseHeaders(),
|
||||||
|
})
|
||||||
|
.then((res) => res.ok)
|
||||||
|
.catch(() => false);
|
||||||
|
},
|
||||||
keys: async function () {
|
keys: async function () {
|
||||||
return await fetch(`${API_BASE}/setup-complete`)
|
return await fetch(`${API_BASE}/setup-complete`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import AnythingLLMLogo from "@/media/logo/anything-llm.png";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { useTheme } from "@/hooks/useTheme";
|
import { useTheme } from "@/hooks/useTheme";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
import useRedirectToHomeOnOnboardingComplete from "@/hooks/useOnboardingComplete";
|
||||||
|
|
||||||
const IMG_SRCSET = {
|
const IMG_SRCSET = {
|
||||||
light: {
|
light: {
|
||||||
@ -21,6 +22,7 @@ const IMG_SRCSET = {
|
|||||||
|
|
||||||
export default function OnboardingHome() {
|
export default function OnboardingHome() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useRedirectToHomeOnOnboardingComplete();
|
||||||
const { theme } = useTheme();
|
const { theme } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const srcSet = IMG_SRCSET?.[theme] || IMG_SRCSET.default;
|
const srcSet = IMG_SRCSET?.[theme] || IMG_SRCSET.default;
|
||||||
|
|||||||
@ -337,11 +337,18 @@ export default function LLMPreference({
|
|||||||
fetchKeys();
|
fetchKeys();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleForward() {
|
async function handleForward() {
|
||||||
|
try {
|
||||||
|
await System.markOnboardingComplete();
|
||||||
|
console.log("Onboarding complete");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Onboarding complete failed", error);
|
||||||
|
} finally {
|
||||||
if (hiddenSubmitButtonRef.current) {
|
if (hiddenSubmitButtonRef.current) {
|
||||||
hiddenSubmitButtonRef.current.click();
|
hiddenSubmitButtonRef.current.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleBack() {
|
function handleBack() {
|
||||||
navigate(paths.onboarding.home());
|
navigate(paths.onboarding.home());
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { ArrowLeft, ArrowRight } from "@phosphor-icons/react";
|
import { ArrowLeft, ArrowRight } from "@phosphor-icons/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
|
import useRedirectToHomeOnOnboardingComplete from "@/hooks/useOnboardingComplete";
|
||||||
import Home from "./Home";
|
import Home from "./Home";
|
||||||
import LLMPreference from "./LLMPreference";
|
import LLMPreference from "./LLMPreference";
|
||||||
import UserSetup from "./UserSetup";
|
import UserSetup from "./UserSetup";
|
||||||
@ -18,6 +19,7 @@ const OnboardingSteps = {
|
|||||||
export default OnboardingSteps;
|
export default OnboardingSteps;
|
||||||
|
|
||||||
export function OnboardingLayout({ children }) {
|
export function OnboardingLayout({ children }) {
|
||||||
|
useRedirectToHomeOnOnboardingComplete();
|
||||||
const [header, setHeader] = useState({
|
const [header, setHeader] = useState({
|
||||||
title: "",
|
title: "",
|
||||||
description: "",
|
description: "",
|
||||||
|
|||||||
@ -80,6 +80,26 @@ function systemEndpoints(app) {
|
|||||||
response.sendStatus(200).end();
|
response.sendStatus(200).end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get("/onboarding", async (_, response) => {
|
||||||
|
try {
|
||||||
|
const results = await SystemSettings.isOnboardingComplete();
|
||||||
|
response.status(200).json({ onboardingComplete: results });
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message, e);
|
||||||
|
response.sendStatus(500).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post("/onboarding", [validatedRequest], async (_, response) => {
|
||||||
|
try {
|
||||||
|
await SystemSettings.markOnboardingComplete();
|
||||||
|
response.sendStatus(200).end();
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e.message, e);
|
||||||
|
response.sendStatus(500).end();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
app.get("/setup-complete", async (_, response) => {
|
app.get("/setup-complete", async (_, response) => {
|
||||||
try {
|
try {
|
||||||
const results = await SystemSettings.currentSettings();
|
const results = await SystemSettings.currentSettings();
|
||||||
|
|||||||
@ -71,9 +71,6 @@ function workspaceEndpoints(app) {
|
|||||||
},
|
},
|
||||||
user?.id
|
user?.id
|
||||||
);
|
);
|
||||||
if (onboardingComplete === true)
|
|
||||||
await Telemetry.sendTelemetry("onboarding_complete");
|
|
||||||
|
|
||||||
response.status(200).json({ workspace, message });
|
response.status(200).json({ workspace, message });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message, e);
|
console.error(e.message, e);
|
||||||
|
|||||||
@ -20,7 +20,7 @@ const SystemSettings = {
|
|||||||
/** A default system prompt that is used when no other system prompt is set or available to the function caller. */
|
/** A default system prompt that is used when no other system prompt is set or available to the function caller. */
|
||||||
saneDefaultSystemPrompt:
|
saneDefaultSystemPrompt:
|
||||||
"Given the following conversation, relevant context, and a follow up question, reply with an answer to the current question the user is asking. Return only your response to the question given the above information following the users instructions as needed.",
|
"Given the following conversation, relevant context, and a follow up question, reply with an answer to the current question the user is asking. Return only your response to the question given the above information following the users instructions as needed.",
|
||||||
protectedFields: ["multi_user_mode", "hub_api_key"],
|
protectedFields: ["multi_user_mode", "hub_api_key", "onboarding_complete"],
|
||||||
publicFields: [
|
publicFields: [
|
||||||
"footer_data",
|
"footer_data",
|
||||||
"support_email",
|
"support_email",
|
||||||
@ -411,6 +411,28 @@ const SystemSettings = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isOnboardingComplete: async function () {
|
||||||
|
try {
|
||||||
|
const setting = await this.get({ label: "onboarding_complete" });
|
||||||
|
return setting?.value === "true";
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
markOnboardingComplete: async function () {
|
||||||
|
try {
|
||||||
|
await this._updateSettings({ onboarding_complete: true });
|
||||||
|
const { Telemetry } = require("./telemetry");
|
||||||
|
await Telemetry.sendTelemetry("onboarding_complete");
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
currentLogoFilename: async function () {
|
currentLogoFilename: async function () {
|
||||||
try {
|
try {
|
||||||
const setting = await this.get({ label: "logo_filename" });
|
const setting = await this.get({ label: "logo_filename" });
|
||||||
|
|||||||
@ -4,6 +4,7 @@ const { EncryptionManager } = require("../EncryptionManager");
|
|||||||
const { CommunicationKey } = require("../comKey");
|
const { CommunicationKey } = require("../comKey");
|
||||||
const setupTelemetry = require("../telemetry");
|
const setupTelemetry = require("../telemetry");
|
||||||
const eagerLoadContextWindows = require("./eagerLoadContextWindows");
|
const eagerLoadContextWindows = require("./eagerLoadContextWindows");
|
||||||
|
const markOnboarded = require("./markOnboarded");
|
||||||
|
|
||||||
// Testing SSL? You can make a self signed certificate and point the ENVs to that location
|
// Testing SSL? You can make a self signed certificate and point the ENVs to that location
|
||||||
// make a directory in server called 'sslcert' - cd into it
|
// make a directory in server called 'sslcert' - cd into it
|
||||||
@ -28,6 +29,7 @@ function bootSSL(app, port = 3001) {
|
|||||||
|
|
||||||
server
|
server
|
||||||
.listen(port, async () => {
|
.listen(port, async () => {
|
||||||
|
await markOnboarded();
|
||||||
await setupTelemetry();
|
await setupTelemetry();
|
||||||
new CommunicationKey(true);
|
new CommunicationKey(true);
|
||||||
new EncryptionManager();
|
new EncryptionManager();
|
||||||
@ -58,6 +60,7 @@ function bootHTTP(app, port = 3001) {
|
|||||||
|
|
||||||
app
|
app
|
||||||
.listen(port, async () => {
|
.listen(port, async () => {
|
||||||
|
await markOnboarded();
|
||||||
await setupTelemetry();
|
await setupTelemetry();
|
||||||
new CommunicationKey(true);
|
new CommunicationKey(true);
|
||||||
new EncryptionManager();
|
new EncryptionManager();
|
||||||
|
|||||||
52
server/utils/boot/markOnboarded.js
Normal file
52
server/utils/boot/markOnboarded.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
const { SystemSettings } = require("../../models/systemSettings");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the onboarding as completed for legacy users prior to this change where onboarding is now a flag in the DB.
|
||||||
|
* This is a legacy patch to ensure that existing users are not redirected to the onboarding page who have been using the app for a while.
|
||||||
|
*/
|
||||||
|
async function markOnboarded() {
|
||||||
|
try {
|
||||||
|
const onboardingStatus = await SystemSettings.isOnboardingComplete();
|
||||||
|
if (onboardingStatus === true) return;
|
||||||
|
|
||||||
|
// Check if the server is already onboarded by the old way of checking if the server in any way has been setup.
|
||||||
|
// If it is, then we can mark the onboarding as complete in the DB to persist this
|
||||||
|
const alreadyOnboarded = await isLegacyOnboarded();
|
||||||
|
if (alreadyOnboarded === true) {
|
||||||
|
console.log(
|
||||||
|
"\x1b[33m[ONBOARDING PATCH]\x1b[0m Legacy instance is already onboarded, marking onboarding as complete. You will not see this message again."
|
||||||
|
);
|
||||||
|
await SystemSettings.markOnboardingComplete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
"\x1b[31m[ONBOARDING PATCH]\x1b[0m Error marking onboarding as complete",
|
||||||
|
e.message,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the server is already onboarded by the old way of checking if the server in any way has been setup.
|
||||||
|
* @returns {Promise<boolean>}
|
||||||
|
*/
|
||||||
|
async function isLegacyOnboarded() {
|
||||||
|
// LLM Provider is set, so we can assume onboarding is complete since this is default null in SystemSettings.js
|
||||||
|
if (!!process.env.LLM_PROVIDER) return true;
|
||||||
|
|
||||||
|
// Vector DB is set, so we can assume onboarding is complete since this is default null in SystemSettings.js (default is lancedb in frontend)
|
||||||
|
if (!!process.env.VECTOR_DB) return true;
|
||||||
|
|
||||||
|
// Check if the AUTH_TOKEN/JWT_SECRET is set, so we can assume onboarding is complete since this is default null in SystemSettings.js
|
||||||
|
if (!!process.env.AUTH_TOKEN || !!process.env.JWT_SECRET) return true;
|
||||||
|
|
||||||
|
// Check multi-user mode is enabled, if it is, then they are already using the app.
|
||||||
|
if ((await SystemSettings.isMultiUserMode()) === true) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = markOnboarded;
|
||||||
Loading…
Reference in New Issue
Block a user