diff --git a/manifest.json b/manifest.json
index 4d019834..e0a0a9f5 100644
--- a/manifest.json
+++ b/manifest.json
@@ -6,5 +6,5 @@
"description": "An AI copilot for your Second Brain",
"author": "Khoj Inc.",
"authorUrl": "https://github.com/khoj-ai",
- "isDesktopOnly": true
+ "isDesktopOnly": false
}
diff --git a/prod.Dockerfile b/prod.Dockerfile
index a935f3c6..693a3a8b 100644
--- a/prod.Dockerfile
+++ b/prod.Dockerfile
@@ -20,7 +20,7 @@ COPY . .
RUN apt install vim -y
# Set the PYTHONPATH environment variable in order for it to find the Django app.
-ENV PYTHONPATH=/app/src/khoj:$PYTHONPATH
+ENV PYTHONPATH=/app/src:$PYTHONPATH
# Run the Application
# There are more arguments required for the application to run,
diff --git a/src/interface/desktop/chat.html b/src/interface/desktop/chat.html
index 21acc4a4..391160fc 100644
--- a/src/interface/desktop/chat.html
+++ b/src/interface/desktop/chat.html
@@ -120,7 +120,8 @@
// Create a new div for the chat message text and append it to the chat message
let chatMessageText = document.createElement('div');
chatMessageText.className = `chat-message-text ${by}`;
- chatMessageText.innerHTML = formattedMessage;
+ let textNode = document.createTextNode(formattedMessage);
+ chatMessageText.appendChild(textNode);
chatMessage.appendChild(chatMessageText);
// Append annotations div to the chat message
diff --git a/src/interface/desktop/search.html b/src/interface/desktop/search.html
index aa8aa662..ce368b9b 100644
--- a/src/interface/desktop/search.html
+++ b/src/interface/desktop/search.html
@@ -112,14 +112,14 @@
} else if (
item.additional.file.endsWith(".md") ||
item.additional.file.endsWith(".markdown") ||
- (item.additional.file.includes("issues") && item.additional.file.includes("github.com")) ||
- (item.additional.file.includes("commit") && item.additional.file.includes("github.com"))
+ (item.additional.file.includes("issues") && item.additional.source === "github") ||
+ (item.additional.file.includes("commit") && item.additional.source === "github")
)
{
html += render_markdown(query, [item]);
} else if (item.additional.file.endsWith(".pdf")) {
html += render_pdf(query, [item]);
- } else if (item.additional.file.includes("notion.so")) {
+ } else if (item.additional.source == "notion") {
html += `
`;
} else if (item.additional.file.endsWith(".html")) {
html += render_html(query, [item]);
diff --git a/src/interface/obsidian/manifest.json b/src/interface/obsidian/manifest.json
index 4d019834..e0a0a9f5 100644
--- a/src/interface/obsidian/manifest.json
+++ b/src/interface/obsidian/manifest.json
@@ -6,5 +6,5 @@
"description": "An AI copilot for your Second Brain",
"author": "Khoj Inc.",
"authorUrl": "https://github.com/khoj-ai",
- "isDesktopOnly": true
+ "isDesktopOnly": false
}
diff --git a/src/khoj/app/settings.py b/src/khoj/app/settings.py
index 721bcc87..427706e8 100644
--- a/src/khoj/app/settings.py
+++ b/src/khoj/app/settings.py
@@ -14,7 +14,7 @@ from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
-BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
+BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
@@ -24,15 +24,15 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
SECRET_KEY = os.getenv("KHOJ_DJANGO_SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = os.getenv("KHOJ_DEBUG", "False") == "True"
+DEBUG = os.getenv("KHOJ_DEBUG") == "True"
-ALLOWED_HOSTS = [".khoj.dev", "localhost", "127.0.0.1", "[::1]", "beta.khoj.dev"]
+# All Subdomains of KHOJ_DOMAIN are trusted
+KHOJ_DOMAIN = os.getenv("KHOJ_DOMAIN", "khoj.dev")
+ALLOWED_HOSTS = [f".{KHOJ_DOMAIN}", "localhost", "127.0.0.1", "[::1]"]
CSRF_TRUSTED_ORIGINS = [
- "https://app.khoj.dev",
- "https://beta.khoj.dev",
- "https://khoj.dev",
- "https://*.khoj.dev",
+ f"https://*.{KHOJ_DOMAIN}",
+ f"https://{KHOJ_DOMAIN}",
]
COOKIE_SAMESITE = "None"
@@ -40,8 +40,8 @@ if DEBUG:
SESSION_COOKIE_DOMAIN = "localhost"
CSRF_COOKIE_DOMAIN = "localhost"
else:
- SESSION_COOKIE_DOMAIN = "khoj.dev"
- CSRF_COOKIE_DOMAIN = "khoj.dev"
+ SESSION_COOKIE_DOMAIN = KHOJ_DOMAIN
+ CSRF_COOKIE_DOMAIN = KHOJ_DOMAIN
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
@@ -143,7 +143,7 @@ USE_TZ = True
# https://docs.djangoproject.com/en/4.2/howto/static-files/
STATIC_ROOT = BASE_DIR / "static"
-STATICFILES_DIRS = [BASE_DIR / "src/khoj/interface/web"]
+STATICFILES_DIRS = [BASE_DIR / "interface/web"]
STATIC_URL = "/static/"
# Default primary key field type
diff --git a/src/khoj/interface/web/search.html b/src/khoj/interface/web/search.html
index 5331ea92..d3ddb595 100644
--- a/src/khoj/interface/web/search.html
+++ b/src/khoj/interface/web/search.html
@@ -112,14 +112,14 @@
} else if (
item.additional.file.endsWith(".md") ||
item.additional.file.endsWith(".markdown") ||
- (item.additional.file.includes("issues") && item.additional.file.includes("github.com")) ||
- (item.additional.file.includes("commit") && item.additional.file.includes("github.com"))
+ (item.additional.file.includes("issues") && item.additional.source === "github") ||
+ (item.additional.file.includes("commit") && item.additional.source === "github")
)
{
html += render_markdown(query, [item]);
} else if (item.additional.file.endsWith(".pdf")) {
html += render_pdf(query, [item]);
- } else if (item.additional.file.includes("notion.so")) {
+ } else if (item.additional.source === "notion") {
html += ``;
} else if (item.additional.file.endsWith(".html")) {
html += render_html(query, [item]);
diff --git a/src/khoj/main.py b/src/khoj/main.py
index c5e1f277..c99ef1a1 100644
--- a/src/khoj/main.py
+++ b/src/khoj/main.py
@@ -3,6 +3,8 @@
"""
# Standard Packages
+from contextlib import redirect_stdout
+import io
import os
import sys
import locale
@@ -33,10 +35,14 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "khoj.app.settings")
django.setup()
# Initialize Django Database
-call_command("migrate", "--noinput")
+db_migrate_output = io.StringIO()
+with redirect_stdout(db_migrate_output):
+ call_command("migrate", "--noinput")
# Initialize Django Static Files
-call_command("collectstatic", "--noinput")
+collectstatic_output = io.StringIO()
+with redirect_stdout(collectstatic_output):
+ call_command("collectstatic", "--noinput")
# Initialize the Application Server
app = FastAPI()
@@ -45,9 +51,16 @@ app = FastAPI()
django_app = get_asgi_application()
# Add CORS middleware
+KHOJ_DOMAIN = os.getenv("KHOJ_DOMAIN", "app.khoj.dev")
app.add_middleware(
CORSMiddleware,
- allow_origins=["app://obsidian.md", "http://localhost:*", "https://app.khoj.dev/*", "app://khoj.dev"],
+ allow_origins=[
+ "app://obsidian.md",
+ "http://localhost:*",
+ "http://127.0.0.1:*",
+ f"https://{KHOJ_DOMAIN}",
+ "app://khoj.dev",
+ ],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
@@ -79,14 +92,16 @@ def run(should_start_server=True):
args = cli(state.cli_args)
set_state(args)
- logger.info(f"🚒 Initializing Khoj v{state.khoj_version}")
-
# Set Logging Level
if args.verbose == 0:
logger.setLevel(logging.INFO)
elif args.verbose >= 1:
logger.setLevel(logging.DEBUG)
+ logger.info(f"🚒 Initializing Khoj v{state.khoj_version}")
+ logger.info(f"📦 Initializing DB:\n{db_migrate_output.getvalue().strip()}")
+ logger.debug(f"🌍 Initializing Web Client:\n{collectstatic_output.getvalue().strip()}")
+
initialization()
# Create app directory, if it doesn't exist
@@ -107,10 +122,10 @@ def run(should_start_server=True):
# Mount Django and Static Files
app.mount("/server", django_app, name="server")
- static_dir = "static"
+ static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")
if not os.path.exists(static_dir):
os.mkdir(static_dir)
- app.mount(f"/{static_dir}", StaticFiles(directory=static_dir), name=static_dir)
+ app.mount(f"/static", StaticFiles(directory=static_dir), name=static_dir)
# Configure Middleware
configure_middleware(app)
diff --git a/src/khoj/search_type/image_search.py b/src/khoj/search_type/image_search.py
index 8c0a3cdb..fd5107ad 100644
--- a/src/khoj/search_type/image_search.py
+++ b/src/khoj/search_type/image_search.py
@@ -12,7 +12,6 @@ from sentence_transformers import SentenceTransformer, util
from PIL import Image
from tqdm import trange
import torch
-from khoj.utils import state
# Internal Packages
from khoj.utils.helpers import get_absolute_path, get_from_dict, resolve_absolute_path, load_model, timer
@@ -26,9 +25,6 @@ logger = logging.getLogger(__name__)
def initialize_model(search_config: ImageSearchConfig):
- # Initialize Model
- torch.set_num_threads(4)
-
# Convert model directory to absolute path
search_config.model_directory = resolve_absolute_path(search_config.model_directory)
diff --git a/src/khoj/search_type/text_search.py b/src/khoj/search_type/text_search.py
index a78ce522..ca0ac6d3 100644
--- a/src/khoj/search_type/text_search.py
+++ b/src/khoj/search_type/text_search.py
@@ -147,6 +147,7 @@ def collate_results(hits, dedupe=True):
"score": hit.distance,
"corpus_id": str(hit.corpus_id),
"additional": {
+ "source": hit.file_source,
"file": hit.file_path,
"compiled": hit.compiled,
"heading": hit.heading,
@@ -169,6 +170,7 @@ def deduplicated_search_responses(hits: List[SearchResponse]):
"score": hit.score,
"corpus_id": hit.corpus_id,
"additional": {
+ "source": hit.additional["source"],
"file": hit.additional["file"],
"compiled": hit.additional["compiled"],
"heading": hit.additional["heading"],
diff --git a/src/khoj/utils/rawconfig.py b/src/khoj/utils/rawconfig.py
index 4c97aedd..d36a36ff 100644
--- a/src/khoj/utils/rawconfig.py
+++ b/src/khoj/utils/rawconfig.py
@@ -72,6 +72,9 @@ class ImageSearchConfig(ConfigBase):
encoder_type: Optional[str] = None
model_directory: Optional[Path] = None
+ class Config:
+ protected_namespaces = ()
+
class SearchConfig(ConfigBase):
image: Optional[ImageSearchConfig] = None