mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-21 14:12:42 +02:00
initial health check
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
FROM python:3.11-slim-bullseye
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler libprotobuf-dev libgoogle-perftools-dev libpq-dev build-essential \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler \
|
||||
libprotobuf-dev libgoogle-perftools-dev libpq-dev build-essential curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY ./requirements/default.txt /tmp/requirements.txt
|
||||
|
@@ -1,7 +1,8 @@
|
||||
FROM python:3.11-slim-bullseye
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler libprotobuf-dev libgoogle-perftools-dev libpq-dev build-essential cron \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler \
|
||||
libprotobuf-dev libgoogle-perftools-dev libpq-dev build-essential cron curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY ./requirements/default.txt /tmp/requirements.txt
|
||||
|
@@ -13,6 +13,7 @@ from danswer.configs.app_configs import WEB_DOMAIN
|
||||
from danswer.datastores.qdrant.indexing import list_collections
|
||||
from danswer.server.admin import router as admin_router
|
||||
from danswer.server.event_loading import router as event_processing_router
|
||||
from danswer.server.health import router as health_router
|
||||
from danswer.server.search_backend import router as backend_router
|
||||
from danswer.utils.logging import setup_logger
|
||||
from fastapi import FastAPI
|
||||
@@ -39,6 +40,7 @@ def get_application() -> FastAPI:
|
||||
application.include_router(backend_router)
|
||||
application.include_router(event_processing_router)
|
||||
application.include_router(admin_router)
|
||||
application.include_router(health_router)
|
||||
|
||||
application.include_router(
|
||||
fastapi_users.get_auth_router(auth_backend),
|
||||
|
@@ -21,7 +21,9 @@ from danswer.dynamic_configs.interface import ConfigNotFoundError
|
||||
from danswer.server.models import AuthStatus
|
||||
from danswer.server.models import AuthUrl
|
||||
from danswer.server.models import GDriveCallback
|
||||
from danswer.server.models import IndexAttemptRequest
|
||||
from danswer.server.models import IndexAttemptSnapshot
|
||||
from danswer.server.models import ListIndexAttemptsResponse
|
||||
from danswer.utils.logging import setup_logger
|
||||
from fastapi import APIRouter
|
||||
from fastapi import Depends
|
||||
@@ -69,11 +71,6 @@ def modify_slack_config(
|
||||
update_slack_config(slack_config)
|
||||
|
||||
|
||||
class IndexAttemptRequest(BaseModel):
|
||||
input_type: InputType = InputType.PULL
|
||||
connector_specific_config: dict[str, Any]
|
||||
|
||||
|
||||
@router.post("/connectors/{source}/index-attempt", status_code=201)
|
||||
def index(
|
||||
source: DocumentSource,
|
||||
@@ -100,10 +97,6 @@ def index(
|
||||
)
|
||||
|
||||
|
||||
class ListIndexAttemptsResponse(BaseModel):
|
||||
index_attempts: list[IndexAttemptSnapshot]
|
||||
|
||||
|
||||
@router.get("/connectors/{source}/index-attempt")
|
||||
def list_index_attempts(
|
||||
source: DocumentSource,
|
||||
|
10
backend/danswer/server/health.py
Normal file
10
backend/danswer/server/health.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from danswer.server.models import HealthCheckResponse
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/health")
|
||||
def healthcheck() -> HealthCheckResponse:
|
||||
return {"status": "ok"}
|
@@ -1,12 +1,18 @@
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from typing import Literal
|
||||
|
||||
from danswer.configs.constants import DocumentSource
|
||||
from danswer.connectors.models import InputType
|
||||
from danswer.datastores.interfaces import DatastoreFilter
|
||||
from danswer.db.models import IndexingStatus
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class HealthCheckResponse(BaseModel):
|
||||
status: Literal["ok"]
|
||||
|
||||
|
||||
class AuthStatus(BaseModel):
|
||||
authenticated: bool
|
||||
|
||||
@@ -51,6 +57,11 @@ class UserByEmail(BaseModel):
|
||||
user_email: str
|
||||
|
||||
|
||||
class IndexAttemptRequest(BaseModel):
|
||||
input_type: InputType = InputType.PULL
|
||||
connector_specific_config: dict[str, Any]
|
||||
|
||||
|
||||
class IndexAttemptSnapshot(BaseModel):
|
||||
connector_specific_config: dict[str, Any]
|
||||
status: IndexingStatus
|
||||
@@ -60,5 +71,5 @@ class IndexAttemptSnapshot(BaseModel):
|
||||
docs_indexed: int
|
||||
|
||||
|
||||
class ListWebsiteIndexAttemptsResponse(BaseModel):
|
||||
class ListIndexAttemptsResponse(BaseModel):
|
||||
index_attempts: list[IndexAttemptSnapshot]
|
||||
|
@@ -35,7 +35,7 @@ services:
|
||||
web_server:
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
dockerfile: Dockerfile.dev
|
||||
depends_on:
|
||||
- api_server
|
||||
restart: always
|
||||
@@ -43,7 +43,6 @@ services:
|
||||
- .env
|
||||
environment:
|
||||
- INTERNAL_URL=http://api_server:8080
|
||||
- NODE_ENV=development
|
||||
ports:
|
||||
- "3000:3000"
|
||||
relational_db:
|
||||
@@ -59,6 +58,8 @@ services:
|
||||
vector_db:
|
||||
image: qdrant/qdrant:v1.1.3
|
||||
restart: always
|
||||
ports:
|
||||
- "6333:6333"
|
||||
volumes:
|
||||
- qdrant_volume:/qdrant/storage
|
||||
volumes:
|
||||
|
@@ -33,7 +33,7 @@ services:
|
||||
web_server:
|
||||
build:
|
||||
context: ../web
|
||||
dockerfile: Dockerfile
|
||||
dockerfile: Dockerfile.prod
|
||||
depends_on:
|
||||
- api_server
|
||||
restart: always
|
||||
@@ -41,7 +41,6 @@ services:
|
||||
- .env
|
||||
environment:
|
||||
- INTERNAL_URL=http://api_server:8080
|
||||
- NODE_ENV=production
|
||||
relational_db:
|
||||
image: postgres:15.2-alpine
|
||||
restart: always
|
||||
|
32
web/Dockerfile.dev
Normal file
32
web/Dockerfile.dev
Normal file
@@ -0,0 +1,32 @@
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
|
||||
# Allow install without lockfile, so example works even without Node.js installed locally
|
||||
else echo "Warning: Lockfile not found. It is recommended to commit lockfiles to version control." && yarn install; \
|
||||
fi
|
||||
|
||||
COPY src ./src
|
||||
COPY public ./public
|
||||
COPY next.config.js .
|
||||
COPY tsconfig.json .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage. Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line to disable telemetry at run time
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
# Note: Don't expose ports here, Compose will handle that for us
|
||||
|
||||
# Start Next.js in development mode based on the preferred package manager
|
||||
CMD \
|
||||
if [ -f yarn.lock ]; then yarn dev; \
|
||||
elif [ -f package-lock.json ]; then npm run dev; \
|
||||
elif [ -f pnpm-lock.yaml ]; then pnpm dev; \
|
||||
else yarn dev; \
|
||||
fi
|
@@ -4,10 +4,14 @@ import * as Yup from "yup";
|
||||
import { IndexForm } from "@/components/admin/connectors/Form";
|
||||
import { GithubIcon } from "@/components/icons/icons";
|
||||
import { TextFormField } from "@/components/admin/connectors/Field";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="mx-auto">
|
||||
<div className="mb-4">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="border-solid border-gray-600 border-b mb-4 pb-2 flex">
|
||||
<GithubIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Github PRs</h1>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getDomain } from "@/lib/redirectSS";
|
||||
import { buildUrl } from "@/lib/userSS";
|
||||
import { buildUrl } from "@/lib/utilsSS";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const GET = async (request: NextRequest) => {
|
||||
|
@@ -16,6 +16,7 @@ import { LoadingAnimation } from "@/components/Loading";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Popup } from "@/components/admin/connectors/Popup";
|
||||
import { useState } from "react";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
export default function Page() {
|
||||
const router = useRouter();
|
||||
@@ -43,9 +44,14 @@ export default function Page() {
|
||||
} | null>(null);
|
||||
|
||||
const header = (
|
||||
<div className="border-solid border-gray-600 border-b mb-4 pb-2 flex">
|
||||
<GoogleDriveIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Google Drive</h1>
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="border-solid border-gray-600 border-b mb-4 pb-2 flex">
|
||||
<GoogleDriveIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Google Drive</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import { SlackConfig } from "../../../../components/admin/connectors/types";
|
||||
import { LoadingAnimation } from "@/components/Loading";
|
||||
import { InitialSetupForm } from "./InitialSetupForm";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
const MainSection = () => {
|
||||
// TODO: add back in once this is ready
|
||||
@@ -29,9 +30,7 @@ const MainSection = () => {
|
||||
</div>
|
||||
);
|
||||
} else if (error || !data) {
|
||||
return (
|
||||
<div className="mt-16">{`Error loading Slack config - ${error}`}</div>
|
||||
);
|
||||
return <div>{`Error loading Slack config - ${error}`}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -62,6 +61,9 @@ const MainSection = () => {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="mx-auto">
|
||||
<div className="mb-4">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="border-solid border-gray-600 border-b mb-4 pb-2 flex">
|
||||
<SlackIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Slack</h1>
|
||||
|
@@ -15,6 +15,7 @@ import {
|
||||
import { IndexForm } from "@/components/admin/connectors/Form";
|
||||
import { TextFormField } from "@/components/admin/connectors/Field";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
const COLUMNS = [
|
||||
{ header: "Base URL", key: "url" },
|
||||
@@ -54,6 +55,9 @@ export default function Web() {
|
||||
|
||||
return (
|
||||
<div className="mx-auto">
|
||||
<div className="mb-4">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
|
||||
<GlobeIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Web</h1>
|
||||
|
@@ -16,6 +16,7 @@ import { CheckCircle, XCircle } from "@phosphor-icons/react";
|
||||
import { submitIndexRequest } from "@/components/admin/connectors/Form";
|
||||
import { useState } from "react";
|
||||
import { Popup } from "@/components/admin/connectors/Popup";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
const getModifiedSource = (indexAttempt: IndexAttempt) => {
|
||||
return indexAttempt.source === "web"
|
||||
@@ -63,6 +64,9 @@ export default function Status() {
|
||||
return (
|
||||
<div className="mx-auto">
|
||||
{popup && <Popup message={popup.message} type={popup.type} />}
|
||||
<div className="mb-4">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="border-solid border-gray-600 border-b pb-2 mb-4 flex">
|
||||
<NotebookIcon size="32" />
|
||||
<h1 className="text-3xl font-bold pl-2">Indexing Status</h1>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { getDomain } from "@/lib/redirectSS";
|
||||
import { buildUrl } from "@/lib/userSS";
|
||||
import { buildUrl } from "@/lib/utilsSS";
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
export const GET = async (request: NextRequest) => {
|
||||
|
@@ -3,6 +3,7 @@ import { Header } from "@/components/Header";
|
||||
import { getCurrentUserSS } from "@/lib/userSS";
|
||||
import { redirect } from "next/navigation";
|
||||
import { DISABLE_AUTH } from "@/lib/constants";
|
||||
import { HealthCheckBanner } from "@/components/health/healthcheck";
|
||||
|
||||
export default async function Home() {
|
||||
let user = null;
|
||||
@@ -15,6 +16,9 @@ export default async function Home() {
|
||||
return (
|
||||
<>
|
||||
<Header user={user} />
|
||||
<div className="m-3">
|
||||
<HealthCheckBanner />
|
||||
</div>
|
||||
<div className="px-24 pt-10 flex flex-col items-center min-h-screen bg-gray-900 text-gray-100">
|
||||
<div className="max-w-[800px] w-full">
|
||||
<SearchSection />
|
||||
|
24
web/src/components/health/healthcheck.tsx
Normal file
24
web/src/components/health/healthcheck.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import { fetcher } from "@/lib/fetcher";
|
||||
import useSWR from "swr";
|
||||
|
||||
export const HealthCheckBanner = () => {
|
||||
const { error } = useSWR("/api/health", fetcher);
|
||||
if (!error) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-xs mx-auto bg-gradient-to-r from-red-900 to-red-700 p-2 rounded-sm border-hidden text-gray-300">
|
||||
<p className="font-bold pb-1">The backend is currently unavailable.</p>
|
||||
|
||||
<p className="px-1">
|
||||
If this is your initial setup or you just updated your Danswer
|
||||
deployment, this is likely because the backend is still starting up.
|
||||
Give it a minute or two, and then refresh the page. If that does not
|
||||
work, make sure the backend is setup and/or contact an administrator.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
@@ -1,13 +1,6 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { User } from "./types";
|
||||
import { INTERNAL_URL } from "./constants";
|
||||
|
||||
export const buildUrl = (path: string) => {
|
||||
if (path.startsWith("/")) {
|
||||
return `${INTERNAL_URL}${path}`;
|
||||
}
|
||||
return `${INTERNAL_URL}/${path}`;
|
||||
};
|
||||
import { buildUrl } from "./utilsSS";
|
||||
|
||||
export const getGoogleOAuthUrlSS = async (): Promise<string> => {
|
||||
const res = await fetch(buildUrl("/auth/google/authorize"));
|
||||
|
8
web/src/lib/utilsSS.ts
Normal file
8
web/src/lib/utilsSS.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { INTERNAL_URL } from "./constants";
|
||||
|
||||
export const buildUrl = (path: string) => {
|
||||
if (path.startsWith("/")) {
|
||||
return `${INTERNAL_URL}${path}`;
|
||||
}
|
||||
return `${INTERNAL_URL}/${path}`;
|
||||
};
|
Reference in New Issue
Block a user