diff --git a/README.md b/README.md index bac18f67..01a9d070 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ In separate terminal windows from project root: - `yarn dev:workers` - `cd document-processor && flask run --host '0.0.0.0' --port 8888` -On first boot of the system you will be prompted to login. Consult the `backend/.env.development` and set or use the `SYS_EMAIL` and `SYS_PASSWORD` values. Once your new account is setup the root credentials will no longer work and you can use your admin account. +On first boot and visiting of the homepage, you will be automatically redirected to create your primary admin account, organization, and database connection. ## Contributing - create issue diff --git a/backend/.env.example b/backend/.env.example index de4981c1..4ee543b0 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,9 +1,4 @@ SERVER_PORT=3001 - -# temp Root account login -SYS_EMAIL=root@vectoradmin.com -SYS_PASSWORD=password - JWT_SECRET="YoUr_RaNd0M_sTr1nG" # STORAGE_DIR= DATABASE_CONNECTION_STRING="postgresql://user:password@host:port/database" diff --git a/backend/endpoints/auth.js b/backend/endpoints/auth.js index 1464a7a7..28970be6 100644 --- a/backend/endpoints/auth.js +++ b/backend/endpoints/auth.js @@ -10,6 +10,36 @@ const bcrypt = require("bcrypt"); function authenticationEndpoints(app) { if (!app) return; + app.get("/auth/auto-onboard", async (_, response) => { + try { + const completeSetup = (await User.count({ role: "admin" })) > 0; + if (completeSetup) { + response.status(200).json({ completed: true }); + return; + } + + const onboardingUser = await User.get({ role: "root" }); + if (!onboardingUser) { + response.status(200).json({ completed: true }); + return; + } + + await Telemetry.sendTelemetry("onboarding_complete"); // Have to send here since we have no other hooks. + response.status(200).json({ + valid: true, + user: onboardingUser, + token: makeJWT( + { id: onboardingUser.id, email: onboardingUser.email }, + "1hr" + ), + message: null, + }); + } catch (e) { + console.log(e.message, e); + response.sendStatus(500).end(); + } + }); + app.post("/auth/login", async (request, response) => { try { const { email, password } = reqBody(request); diff --git a/backend/utils/boot/index.js b/backend/utils/boot/index.js index 4adfba95..f7e488ed 100644 --- a/backend/utils/boot/index.js +++ b/backend/utils/boot/index.js @@ -48,8 +48,8 @@ async function systemInit() { const completeSetup = (await User.count({ role: "admin" })) > 0; if (completeSetup) return; - process.env.SYS_EMAIL = process.env.SYS_EMAIL ?? "root@vectoradmin.com"; - process.env.SYS_PASSWORD = process.env.SYS_PASSWORD ?? "password"; + process.env.SYS_EMAIL = "root@vectoradmin.com"; + process.env.SYS_PASSWORD = "password"; const existingRootUser = await User.get({ email: process.env.SYS_EMAIL, diff --git a/docker/.env.example b/docker/.env.example index 708510b2..ff8d11d1 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -2,9 +2,6 @@ SERVER_PORT=3001 # STORAGE_DIR="./backend/storage" JWT_SECRET="your-random-string-here" -SYS_EMAIL="root@example.com" -SYS_PASSWORD="hunter2" - INNGEST_EVENT_KEY="background_workers" INNGEST_SIGNING_KEY="random-string-goes-here" INNGEST_LANDING_PAGE="true" diff --git a/docker/DOCKER.md b/docker/DOCKER.md index 025e70fb..8e408329 100644 --- a/docker/DOCKER.md +++ b/docker/DOCKER.md @@ -19,8 +19,6 @@ Run this command first to get a dockerized Postgres container running: `docker run -d -p 3001:3001 \ -e SERVER_PORT="3001" \ -e JWT_SECRET="your-random-string-here" \ --e SYS_EMAIL="root@vectoradmin.com" \ --e SYS_PASSWORD="password" \ -e INNGEST_EVENT_KEY="background_workers" \ -e INNGEST_SIGNING_KEY="random-string-goes-here" \ -e INNGEST_LANDING_PAGE="true" \ @@ -36,8 +34,6 @@ mintplexlabs/vectoradmin` - Edit `.env` file and update the variables. **please** update all of the following: ```shell JWT_SECRET="some-random-string" -SYS_EMAIL="root@vectoradmin.com" -SYS_PASSWORD="password" DATABASE_CONNECTION_STRING="postgresql://vectoradmin:password@host.docker.internal:5433/vdbms" # Valid PG Connection string. INNGEST_SIGNING_KEY="some-random-string" ``` @@ -46,7 +42,7 @@ INNGEST_SIGNING_KEY="some-random-string" ## How to use the user interface and login for the first time. - To access the full application, visit `http://localhost:3001` in your browser. -- You first login will require you to use the `SYS_EMAIL` and `SYS_PASSWORD` set via ENV during build or run. After onboarding this login will be permanently disabled. +- You will automatically be redirected into onboarding to create your primary admin account, organization, and vector database connection. # Connecting to a Vector Database diff --git a/frontend/src/models/user.ts b/frontend/src/models/user.ts index 4f1bd94d..50015337 100644 --- a/frontend/src/models/user.ts +++ b/frontend/src/models/user.ts @@ -1,7 +1,32 @@ -import { API_BASE } from '../utils/constants'; +import { API_BASE, COMPLETE_ONBOARDING } from '../utils/constants'; import { baseHeaders } from '../utils/request'; const User = { + autoOnboard: async (): Promise<{ + user: object | null; + token: string | null; + }> => { + if (!!window.localStorage.getItem(COMPLETE_ONBOARDING)) + return { user: null, token: null }; + return fetch(`${API_BASE}/auth/auto-onboard`, { + method: 'GET', + cache: 'no-cache', + }) + .then((res) => res.json()) + .then((res) => { + // If special key for `completed` is set, we will stop checking this for the client. + if (res.hasOwnProperty('completed')) { + window.localStorage.setItem(COMPLETE_ONBOARDING, 'true'); + return { user: null, token: null }; + } + + return res; + }) + .catch((e) => { + console.error(e); + return { user: null, token: null }; + }); + }, login: async (email: string, password: string) => { let error; const { user, valid, token } = await fetch(`${API_BASE}/auth/login`, { diff --git a/frontend/src/pages/Authentication/SignIn.tsx b/frontend/src/pages/Authentication/SignIn.tsx index 2fe7f379..542f0b20 100644 --- a/frontend/src/pages/Authentication/SignIn.tsx +++ b/frontend/src/pages/Authentication/SignIn.tsx @@ -8,6 +8,7 @@ import User from '../../models/user'; import { APP_NAME, STORE_TOKEN, STORE_USER } from '../../utils/constants'; import paths from '../../utils/paths'; import validateSessionTokenForUser from '../../utils/session'; +import System from '../../models/system'; type IStages = 'loading' | 'failed' | 'success' | 'ready'; type FormTypes = { @@ -69,8 +70,23 @@ const SignIn = () => { return false; } window.location.replace(paths.dashboard()); + return true; } - checkAuth(); + + async function autoOnboard() { + const { user, token } = await User.autoOnboard(); + if (!!token && !!user) { + window.localStorage.setItem(STORE_USER, JSON.stringify(user)); + window.localStorage.setItem(STORE_TOKEN, token); + window.location.replace(paths.onboardingSetup()); + } + return; + } + + checkAuth().then((res) => { + if (res) return; + autoOnboard(); + }); }, []); return ( diff --git a/frontend/src/utils/constants.ts b/frontend/src/utils/constants.ts index 10a720c8..1f75b256 100644 --- a/frontend/src/utils/constants.ts +++ b/frontend/src/utils/constants.ts @@ -3,6 +3,7 @@ export const APP_NAME = import.meta.env.VITE_APP_NAME || 'VDMS'; export const STORE_USER = 'vdms_user'; export const STORE_TOKEN = 'vdms_authToken'; export const COMPLETE_QUESTIONNAIRE = 'vectoradmin_completed_questionnaire'; +export const COMPLETE_ONBOARDING = 'vectoradmin_completed_onboarding'; export const SUPPORTED_VECTOR_DBS = [ 'pinecone', 'chroma',