Backend GKE Deploy, access key for backend
- Configs and automation for deploying backend to GKE - First steps to adding an optional token check for requests to backend - First steps frontend sending optional token to backend when configured
This commit is contained in:
parent
0fedaef537
commit
e6c2042df6
17 changed files with 296 additions and 39 deletions
18
Makefile
Normal file
18
Makefile
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
.PHONY: run
|
||||||
|
run:
|
||||||
|
docker compose -f docker-compose.yaml up
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: rebuild-run
|
||||||
|
rebuild-run:
|
||||||
|
docker compose -f docker-compose.yaml up --build
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: run-app-only
|
||||||
|
run-app-only:
|
||||||
|
docker compose -f app-docker-compose.yaml up
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: rebuild-run-app-only
|
||||||
|
rebuild-run-app-only:
|
||||||
|
docker compose -f app-docker-compose.yaml up --build
|
20
README.md
20
README.md
|
@ -106,6 +106,26 @@ You need to edit the ports accordingly.
|
||||||
|
|
||||||
[](https://repocloud.io/details/?app_id=267)
|
[](https://repocloud.io/details/?app_id=267)
|
||||||
|
|
||||||
|
## Deploy Perplexica backend to Google GKE
|
||||||
|
|
||||||
|
0: Install `docker` and `terraform` (Process specific to your system)
|
||||||
|
1a: Copy the `sample.env` file to `.env`
|
||||||
|
1b: Copy the `deploy/gcp/sample.env` file to `deploy/gcp/.env`
|
||||||
|
2a: Fillout desired LLM provider access keys etc. in `.env`
|
||||||
|
- Note: you will have to comeback and edit this file again once you have the address of the K8s backend deploy
|
||||||
|
2b: Fillout the GCP info in `deploy/gcp/.env`
|
||||||
|
3: Edit `GCP_REPO` to the correct docker image repo path if you are using something other than Container registry
|
||||||
|
4: Edit the `PREFIX` if you would like images and GKE entities to be prefixed with something else
|
||||||
|
5: In `deploy/gcp` run `make init` to initialize terraform
|
||||||
|
6: Follow the normal Preplexica configuration steps outlined in the project readme
|
||||||
|
7: Auth docker with the appropriate credential for repo Ex. for `gcr.io` -> `gcloud auth configure-docker`
|
||||||
|
8: In `deploy/gcp` run `make build-deplpy` to build and push the project images to the repo, create a GKE cluster and deploy the app
|
||||||
|
9: Once deployed successfully edit the `.env` file in the root project folder and update the `REMOTE_BACKEND_ADDRESS` with the remote k8s deployment address and port
|
||||||
|
10: In root project folder run `make rebuild-run-app-only`
|
||||||
|
|
||||||
|
If you configured everything correctly frontend app will run locally and provide you with a local url to open it.
|
||||||
|
Now you can run queries against the remotely deployed backend from your local machine. :celebrate:
|
||||||
|
|
||||||
## Upcoming Features
|
## Upcoming Features
|
||||||
|
|
||||||
- [ ] Finalizing Copilot Mode
|
- [ ] Finalizing Copilot Mode
|
||||||
|
|
13
app-docker-compose.yaml
Normal file
13
app-docker-compose.yaml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
services:
|
||||||
|
perplexica-frontend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: app.dockerfile
|
||||||
|
args:
|
||||||
|
- SUPER_SECRET_KEY=${SUPER_SECRET_KEY}
|
||||||
|
- NEXT_PUBLIC_API_URL=http://${REMOTE_BACKEND_ADDRESS}/api
|
||||||
|
- NEXT_PUBLIC_WS_URL=ws://${REMOTE_BACKEND_ADDRESS}
|
||||||
|
expose:
|
||||||
|
- 3000
|
||||||
|
ports:
|
||||||
|
- 3000:3000
|
|
@ -2,8 +2,11 @@ FROM node:alpine
|
||||||
|
|
||||||
ARG NEXT_PUBLIC_WS_URL
|
ARG NEXT_PUBLIC_WS_URL
|
||||||
ARG NEXT_PUBLIC_API_URL
|
ARG NEXT_PUBLIC_API_URL
|
||||||
|
ARG SUPER_SECRET_KEY
|
||||||
|
|
||||||
ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
|
ENV NEXT_PUBLIC_WS_URL=${NEXT_PUBLIC_WS_URL}
|
||||||
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
||||||
|
ENV SUPER_SECRET_KEY=${SUPER_SECRET_KEY}
|
||||||
|
|
||||||
WORKDIR /home/perplexica
|
WORKDIR /home/perplexica
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
# USAGE:
|
# Adds all the deployment relevant sensitive information about project
|
||||||
# 0: Install `docker` and `terraform` (Process specific to your system)
|
|
||||||
# 1: Copy the sample.env file to .env
|
|
||||||
# 2: Fillout the GCP info in .env
|
|
||||||
# 3: Edit GCP_REPO to the correct docker image repo path if you are using something other than Container registry
|
|
||||||
# 4: Edit the PREFIX if you would like images and GKE entities to be prefixed with something else
|
|
||||||
# 5: Run `make init` to initialize terraform
|
|
||||||
# 6: Follow the normal Preplexica configuration steps outlined in the project readme
|
|
||||||
# 7: Run `make build-deplpy` to build and push the project images to the repo, create a GKE cluster and deploy the app
|
|
||||||
#
|
|
||||||
# NOTES/ WARNINGS:
|
|
||||||
# - The app endpoint is unsecured and exposed to the internet at large, you will need to implement your desired auth
|
|
||||||
# - No auto scaling is enabled for this cluster and deployments, edit the terraform files accordingly for that
|
|
||||||
include .env
|
include .env
|
||||||
|
|
||||||
|
# Adds secrets/ keys we have define for the project locally and deployment
|
||||||
|
include ../../.env
|
||||||
|
|
||||||
# Use `location-id-docker.pkg` for artifact registry Ex. west-1-docker.pkg
|
# Use `location-id-docker.pkg` for artifact registry Ex. west-1-docker.pkg
|
||||||
GCP_REPO=gcr.io
|
GCP_REPO=gcr.io
|
||||||
PREFIX=perplexica
|
PREFIX=perplexica
|
||||||
|
SEARCH_PORT=8080
|
||||||
|
BACKEND_PORT=3001
|
||||||
SEARCH_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-searxng:latest
|
SEARCH_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-searxng:latest
|
||||||
BACKEND_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-backend:latest
|
BACKEND_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-backend:latest
|
||||||
APP_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-app:latest
|
APP_IMAGE_TAG=$(GCP_REPO)/$(GCP_PROJECT_ID)/$(PREFIX)-app:latest
|
||||||
|
@ -38,8 +31,10 @@ show_config:
|
||||||
&& echo $(GCP_SERVICE_ACCOUNT_KEY_FILE) \
|
&& echo $(GCP_SERVICE_ACCOUNT_KEY_FILE) \
|
||||||
&& echo $(SEARCH_IMAGE_TAG) \
|
&& echo $(SEARCH_IMAGE_TAG) \
|
||||||
&& echo $(BACKEND_IMAGE_TAG) \
|
&& echo $(BACKEND_IMAGE_TAG) \
|
||||||
&& echo $(APP_IMAGE_TAG)
|
&& echo $(APP_IMAGE_TAG) \
|
||||||
|
&& echo $(SEARCH_PORT) \
|
||||||
|
&& echo $(BACKEND_PORT) \
|
||||||
|
&& echo $(OPENAI)
|
||||||
|
|
||||||
.PHONY: docker-build-push-searxng
|
.PHONY: docker-build-push-searxng
|
||||||
docker-build-push-searxng:
|
docker-build-push-searxng:
|
||||||
|
@ -49,14 +44,15 @@ docker-build-push-searxng:
|
||||||
|
|
||||||
.PHONY: docker-build-push-backend
|
.PHONY: docker-build-push-backend
|
||||||
docker-build-push-backend:
|
docker-build-push-backend:
|
||||||
cd ../../ && docker build -f ./backed.dockerfile -t $(BACKEND_IMAGE_TAG) . --platform="linux/amd64"
|
cd ../../ && docker build -f ./backend.dockerfile -t $(BACKEND_IMAGE_TAG) . --platform="linux/amd64"
|
||||||
docker push $(BACKEND_IMAGE_TAG)
|
docker push $(BACKEND_IMAGE_TAG)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: docker-build-push-app
|
.PHONY: docker-build-push-app
|
||||||
docker-build-push-app:
|
docker-build-push-app:
|
||||||
cd ../../ && docker build -f ./app.dockerfile -t $(APP_IMAGE_TAG) . --platform="linux/amd64"
|
#
|
||||||
docker push $(APP_IMAGE_TAG)
|
# cd ../../ && docker build -f ./app.dockerfile -t $(APP_IMAGE_TAG) . --platform="linux/amd64"
|
||||||
|
# docker push $(APP_IMAGE_TAG)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: init
|
.PHONY: init
|
||||||
|
@ -73,6 +69,9 @@ deploy:
|
||||||
&& export TF_VAR_search_image=$(SEARCH_IMAGE_TAG) \
|
&& export TF_VAR_search_image=$(SEARCH_IMAGE_TAG) \
|
||||||
&& export TF_VAR_backend_image=$(BACKEND_IMAGE_TAG) \
|
&& export TF_VAR_backend_image=$(BACKEND_IMAGE_TAG) \
|
||||||
&& export TF_VAR_app_image=$(APP_IMAGE_TAG) \
|
&& export TF_VAR_app_image=$(APP_IMAGE_TAG) \
|
||||||
|
&& export TF_VAR_search_port=$(SEARCH_PORT) \
|
||||||
|
&& export TF_VAR_backend_port=$(BACKEND_PORT) \
|
||||||
|
&& export TF_VAR_open_ai=$(OPENAI) \
|
||||||
&& terraform apply
|
&& terraform apply
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,9 @@ provider "kubernetes" {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#####################################################################################################
|
||||||
|
# SearXNG - Search engine deployment and service
|
||||||
|
#####################################################################################################
|
||||||
resource "kubernetes_deployment" "searxng" {
|
resource "kubernetes_deployment" "searxng" {
|
||||||
metadata {
|
metadata {
|
||||||
name = "searxng"
|
name = "searxng"
|
||||||
|
@ -60,7 +63,7 @@ resource "kubernetes_deployment" "searxng" {
|
||||||
image = var.search_image
|
image = var.search_image
|
||||||
name = "searxng-container"
|
name = "searxng-container"
|
||||||
port {
|
port {
|
||||||
container_port = 8080
|
container_port = var.search_port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,14 +83,88 @@ resource "kubernetes_service" "searxng_service" {
|
||||||
}
|
}
|
||||||
|
|
||||||
port {
|
port {
|
||||||
port = 8080
|
port = var.search_port
|
||||||
target_port = 8080
|
target_port = var.search_port
|
||||||
}
|
}
|
||||||
|
|
||||||
type = "LoadBalancer"
|
type = "LoadBalancer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#####################################################################################################
|
||||||
|
# Perplexica - backend deployment and service
|
||||||
|
#####################################################################################################
|
||||||
|
resource "kubernetes_deployment" "backend" {
|
||||||
|
metadata {
|
||||||
|
name = "backend"
|
||||||
|
labels = {
|
||||||
|
app = "backend"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec {
|
||||||
|
replicas = 1
|
||||||
|
selector {
|
||||||
|
match_labels = {
|
||||||
|
component = "backend"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template {
|
||||||
|
metadata {
|
||||||
|
labels = {
|
||||||
|
component = "backend"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spec {
|
||||||
|
container {
|
||||||
|
image = var.backend_image
|
||||||
|
name = "backend-container"
|
||||||
|
port {
|
||||||
|
container_port = var.backend_port
|
||||||
|
}
|
||||||
|
env {
|
||||||
|
# searxng service ip
|
||||||
|
name = "SEARXNG_API_URL"
|
||||||
|
value = "http://${kubernetes_service.searxng_service.status[0].load_balancer[0].ingress[0].ip}:${var.search_port}"
|
||||||
|
}
|
||||||
|
env {
|
||||||
|
# openai key
|
||||||
|
name = "OPENAI"
|
||||||
|
value = var.open_ai
|
||||||
|
}
|
||||||
|
env {
|
||||||
|
# port
|
||||||
|
name = "PORT"
|
||||||
|
value = var.backend_port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "kubernetes_service" "backend_service" {
|
||||||
|
metadata {
|
||||||
|
name = "backend-service"
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
spec {
|
||||||
|
selector = {
|
||||||
|
component = "backend"
|
||||||
|
}
|
||||||
|
|
||||||
|
port {
|
||||||
|
port = var.backend_port
|
||||||
|
target_port = var.backend_port
|
||||||
|
}
|
||||||
|
|
||||||
|
type = "LoadBalancer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################################################################################
|
||||||
|
# Variable and module definitions
|
||||||
|
#####################################################################################################
|
||||||
variable "project_id" {
|
variable "project_id" {
|
||||||
description = "The ID of the project in which the resources will be deployed."
|
description = "The ID of the project in which the resources will be deployed."
|
||||||
type = string
|
type = string
|
||||||
|
@ -113,7 +190,7 @@ variable "search_image" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "backed_image" {
|
variable "backend_image" {
|
||||||
description = "Tag for the Perplexica backend image"
|
description = "Tag for the Perplexica backend image"
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
@ -123,6 +200,21 @@ variable "app_image" {
|
||||||
type = string
|
type = string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "open_ai" {
|
||||||
|
description = "OPENAI access key"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "search_port" {
|
||||||
|
description = "Port for searxng service"
|
||||||
|
type = number
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "backend_port" {
|
||||||
|
description = "Port for backend service"
|
||||||
|
type = number
|
||||||
|
}
|
||||||
|
|
||||||
module "gke-cluster" {
|
module "gke-cluster" {
|
||||||
source = "./gke-cluster"
|
source = "./gke-cluster"
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,12 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: backend.dockerfile
|
dockerfile: backend.dockerfile
|
||||||
args:
|
args:
|
||||||
- SEARXNG_API_URL=http://searxng:8080
|
- SEARXNG_API_URL=null
|
||||||
|
environment:
|
||||||
|
SEARXNG_API_URL: "http://searxng:8080"
|
||||||
|
OPENAI: ${OPENAI}
|
||||||
|
GROQ: ${GROQ}
|
||||||
|
OLLAMA_API_URL: ${OLLAMA_API_URL}
|
||||||
depends_on:
|
depends_on:
|
||||||
- searxng
|
- searxng
|
||||||
expose:
|
expose:
|
||||||
|
@ -30,8 +35,9 @@ services:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: app.dockerfile
|
dockerfile: app.dockerfile
|
||||||
args:
|
args:
|
||||||
- NEXT_PUBLIC_API_URL=http://127.0.0.1:3001/api
|
- SUPER_SECRET_KEY=${SUPER_SECRET_KEY}
|
||||||
- NEXT_PUBLIC_WS_URL=ws://127.0.0.1:3001
|
- NEXT_PUBLIC_API_URL=http://${REMOTE_BACKEND_ADDRESS}/api
|
||||||
|
- NEXT_PUBLIC_WS_URL=ws://${REMOTE_BACKEND_ADDRESS}
|
||||||
depends_on:
|
depends_on:
|
||||||
- perplexica-backend
|
- perplexica-backend
|
||||||
expose:
|
expose:
|
||||||
|
|
20
sample.env
Normal file
20
sample.env
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Copy this file over to .env and fill in the desired config.
|
||||||
|
# .env will become available to docker compose and these values will be
|
||||||
|
# used when running docker compose up
|
||||||
|
|
||||||
|
# Edit to set OpenAI access key
|
||||||
|
OPENAI=ADD OPENAI KEY HERE
|
||||||
|
|
||||||
|
# Uncomment and edit to set GROQ access key
|
||||||
|
# GROQ: ${GROQ}
|
||||||
|
|
||||||
|
# Uncomment and edit to set OLLAMA Url
|
||||||
|
# OLLAMA_API_URL: ${OLLAMA_API_URL}
|
||||||
|
|
||||||
|
# Address and port of the remotely deployed Perplexica backend
|
||||||
|
REMOTE_BACKEND_ADDRESS=111.111.111.111:0000
|
||||||
|
|
||||||
|
# Uncomment and edit to configure backend to reject requests without token
|
||||||
|
# leave commented to have open access to all endpoints
|
||||||
|
# Secret key to "secure" backend
|
||||||
|
# SUPER_SECRET_KEY=THISISASUPERSECRETKEYSERIOUSLY
|
|
@ -3,7 +3,8 @@ import express from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import { getPort } from './config';
|
import { requireAccessKey } from './auth';
|
||||||
|
import { getAccessKey, getPort } from './config';
|
||||||
import logger from './utils/logger';
|
import logger from './utils/logger';
|
||||||
|
|
||||||
const port = getPort();
|
const port = getPort();
|
||||||
|
@ -23,6 +24,10 @@ app.get('/api', (_, res) => {
|
||||||
res.status(200).json({ status: 'ok' });
|
res.status(200).json({ status: 'ok' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (getAccessKey()) {
|
||||||
|
app.all('*', requireAccessKey);
|
||||||
|
};
|
||||||
|
|
||||||
server.listen(port, () => {
|
server.listen(port, () => {
|
||||||
logger.info(`Server is running on port ${port}`);
|
logger.info(`Server is running on port ${port}`);
|
||||||
});
|
});
|
||||||
|
|
18
src/auth.ts
Normal file
18
src/auth.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import {
|
||||||
|
getAccessKey,
|
||||||
|
} from '../config';
|
||||||
|
|
||||||
|
const requireAccessKey = (req, res, next) => {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
|
||||||
|
if (authHeader) {
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
|
||||||
|
if (token !== getAccessKey()) {
|
||||||
|
return res.sendStatus(403);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
res.sendStatus(401);
|
||||||
|
}
|
||||||
|
};
|
|
@ -8,6 +8,7 @@ interface Config {
|
||||||
GENERAL: {
|
GENERAL: {
|
||||||
PORT: number;
|
PORT: number;
|
||||||
SIMILARITY_MEASURE: string;
|
SIMILARITY_MEASURE: string;
|
||||||
|
SUPER_SECRET_KEY: string;
|
||||||
};
|
};
|
||||||
API_KEYS: {
|
API_KEYS: {
|
||||||
OPENAI: string;
|
OPENAI: string;
|
||||||
|
@ -28,18 +29,38 @@ const loadConfig = () =>
|
||||||
fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'),
|
fs.readFileSync(path.join(__dirname, `../${configFileName}`), 'utf-8'),
|
||||||
) as any as Config;
|
) as any as Config;
|
||||||
|
|
||||||
|
const loadEnv = () => {
|
||||||
|
return {
|
||||||
|
GENERAL: {
|
||||||
|
PORT: Number(process.env.PORT),
|
||||||
|
SIMILARITY_MEASURE: process.env.SIMILARITY_MEASURE,
|
||||||
|
SUPER_SECRET_KEY: process.env.SUPER_SECRET_KEY
|
||||||
|
},
|
||||||
|
API_KEYS: {
|
||||||
|
OPENAI: process.env.OPENAI,
|
||||||
|
GROQ: process.env.GROQ
|
||||||
|
},
|
||||||
|
API_ENDPOINTS: {
|
||||||
|
SEARXNG: process.env.SEARXNG_API_URL,
|
||||||
|
OLLAMA: process.env.OLLAMA_API_URL
|
||||||
|
}
|
||||||
|
} as Config;
|
||||||
|
};
|
||||||
|
|
||||||
export const getPort = () => loadConfig().GENERAL.PORT;
|
export const getPort = () => loadConfig().GENERAL.PORT;
|
||||||
|
|
||||||
|
export const getAccessKey = () => loadEnv().GENERAL.SUPER_SECRET_KEY || loadConfig().GENERAL.SUPER_SECRET_KEY;
|
||||||
|
|
||||||
export const getSimilarityMeasure = () =>
|
export const getSimilarityMeasure = () =>
|
||||||
loadConfig().GENERAL.SIMILARITY_MEASURE;
|
loadConfig().GENERAL.SIMILARITY_MEASURE;
|
||||||
|
|
||||||
export const getOpenaiApiKey = () => loadConfig().API_KEYS.OPENAI;
|
export const getOpenaiApiKey = () => loadEnv().API_KEYS.OPENAI || loadConfig().API_KEYS.OPENAI;
|
||||||
|
|
||||||
export const getGroqApiKey = () => loadConfig().API_KEYS.GROQ;
|
export const getGroqApiKey = () => loadEnv().API_KEYS.GROQ || loadConfig().API_KEYS.GROQ;
|
||||||
|
|
||||||
export const getSearxngApiEndpoint = () => loadConfig().API_ENDPOINTS.SEARXNG;
|
export const getSearxngApiEndpoint = () => loadEnv().API_ENDPOINTS.SEARXNG || loadConfig().API_ENDPOINTS.SEARXNG;
|
||||||
|
|
||||||
export const getOllamaApiEndpoint = () => loadConfig().API_ENDPOINTS.OLLAMA;
|
export const getOllamaApiEndpoint = () => loadEnv().API_ENDPOINTS.OLLAMA || loadConfig().API_ENDPOINTS.OLLAMA;
|
||||||
|
|
||||||
export const updateConfig = (config: RecursivePartial<Config>) => {
|
export const updateConfig = (config: RecursivePartial<Config>) => {
|
||||||
const currentConfig = loadConfig();
|
const currentConfig = loadConfig();
|
||||||
|
|
|
@ -6,6 +6,7 @@ import Navbar from './Navbar';
|
||||||
import Chat from './Chat';
|
import Chat from './Chat';
|
||||||
import EmptyChat from './EmptyChat';
|
import EmptyChat from './EmptyChat';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { clientFetch } from '@/lib/utils';
|
||||||
|
|
||||||
export type Message = {
|
export type Message = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -34,8 +35,8 @@ const useSocket = (url: string) => {
|
||||||
!embeddingModel ||
|
!embeddingModel ||
|
||||||
!embeddingModelProvider
|
!embeddingModelProvider
|
||||||
) {
|
) {
|
||||||
const providers = await fetch(
|
const providers = await clientFetch(
|
||||||
`${process.env.NEXT_PUBLIC_API_URL}/models`,
|
'/models',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||||
import Lightbox from 'yet-another-react-lightbox';
|
import Lightbox from 'yet-another-react-lightbox';
|
||||||
import 'yet-another-react-lightbox/styles.css';
|
import 'yet-another-react-lightbox/styles.css';
|
||||||
import { Message } from './ChatWindow';
|
import { Message } from './ChatWindow';
|
||||||
|
import { clientFetch } from '@/lib/utils';
|
||||||
|
|
||||||
type Image = {
|
type Image = {
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -33,8 +34,8 @@ const SearchImages = ({
|
||||||
const chatModelProvider = localStorage.getItem('chatModelProvider');
|
const chatModelProvider = localStorage.getItem('chatModelProvider');
|
||||||
const chatModel = localStorage.getItem('chatModel');
|
const chatModel = localStorage.getItem('chatModel');
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await clientFetch(
|
||||||
`${process.env.NEXT_PUBLIC_API_URL}/images`,
|
'/images',
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||||
import Lightbox, { GenericSlide, VideoSlide } from 'yet-another-react-lightbox';
|
import Lightbox, { GenericSlide, VideoSlide } from 'yet-another-react-lightbox';
|
||||||
import 'yet-another-react-lightbox/styles.css';
|
import 'yet-another-react-lightbox/styles.css';
|
||||||
import { Message } from './ChatWindow';
|
import { Message } from './ChatWindow';
|
||||||
|
import { clientFetch } from '@/lib/utils';
|
||||||
|
|
||||||
type Video = {
|
type Video = {
|
||||||
url: string;
|
url: string;
|
||||||
|
@ -46,8 +47,8 @@ const Searchvideos = ({
|
||||||
const chatModelProvider = localStorage.getItem('chatModelProvider');
|
const chatModelProvider = localStorage.getItem('chatModelProvider');
|
||||||
const chatModel = localStorage.getItem('chatModel');
|
const chatModel = localStorage.getItem('chatModel');
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await clientFetch(
|
||||||
`${process.env.NEXT_PUBLIC_API_URL}/videos`,
|
'/videos',
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Dialog, Transition } from '@headlessui/react';
|
import { Dialog, Transition } from '@headlessui/react';
|
||||||
import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react';
|
import { CloudUpload, RefreshCcw, RefreshCw } from 'lucide-react';
|
||||||
import React, { Fragment, useEffect, useState } from 'react';
|
import React, { Fragment, useEffect, useState } from 'react';
|
||||||
|
import { clientFetch } from '@/lib/utils';
|
||||||
|
|
||||||
interface SettingsType {
|
interface SettingsType {
|
||||||
chatModelProviders: {
|
chatModelProviders: {
|
||||||
|
@ -42,7 +43,7 @@ const SettingsDialog = ({
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
const fetchConfig = async () => {
|
const fetchConfig = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, {
|
const res = await clientFetch('/config', {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
|
@ -102,7 +103,7 @@ const SettingsDialog = ({
|
||||||
setIsUpdating(true);
|
setIsUpdating(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fetch(`${process.env.NEXT_PUBLIC_API_URL}/config`, {
|
await clientFetch('/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
20
ui/lib/config.ts
Normal file
20
ui/lib/config.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
interface Config {
|
||||||
|
GENERAL: {
|
||||||
|
SUPER_SECRET_KEY: string;
|
||||||
|
NEXT_PUBLIC_API_URL: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadEnv = () => {
|
||||||
|
return {
|
||||||
|
GENERAL: {
|
||||||
|
SUPER_SECRET_KEY: process.env.SUPER_SECRET_KEY!,
|
||||||
|
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL!,
|
||||||
|
NEXT_PUBLIC_WS_URL: process.env.NEXT_PUBLIC_WS_URL!
|
||||||
|
},
|
||||||
|
} as Config;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAccessKey = () => loadEnv().GENERAL.SUPER_SECRET_KEY;
|
||||||
|
|
||||||
|
export const getBackendURL = () => loadEnv().GENERAL.NEXT_PUBLIC_API_URL;
|
|
@ -1,5 +1,6 @@
|
||||||
import clsx, { ClassValue } from 'clsx';
|
import clsx, { ClassValue } from 'clsx';
|
||||||
import { twMerge } from 'tailwind-merge';
|
import { twMerge } from 'tailwind-merge';
|
||||||
|
import { getAccessKey, getBackendURL } from './config'
|
||||||
|
|
||||||
export const cn = (...classes: ClassValue[]) => twMerge(clsx(...classes));
|
export const cn = (...classes: ClassValue[]) => twMerge(clsx(...classes));
|
||||||
|
|
||||||
|
@ -19,3 +20,20 @@ export const formatTimeDifference = (date1: Date, date2: Date): string => {
|
||||||
else
|
else
|
||||||
return `${Math.floor(diffInSeconds / 31536000)} year${Math.floor(diffInSeconds / 31536000) !== 1 ? 's' : ''}`;
|
return `${Math.floor(diffInSeconds / 31536000)} year${Math.floor(diffInSeconds / 31536000) !== 1 ? 's' : ''}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const clientFetch = async (path: string, payload: any): Promise<any> => {
|
||||||
|
let headers = payload.headers;
|
||||||
|
const url = `${getBackendURL()}${path}`;
|
||||||
|
const secret_token = getAccessKey();
|
||||||
|
|
||||||
|
if (secret_token) {
|
||||||
|
if (headers == null) {
|
||||||
|
headers = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
headers['Authorization'] = `Bearer ${secret_token}`;
|
||||||
|
payload.headers = headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
return await fetch(url, payload);
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue