mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-06-15 14:28:32 +00:00
feat(opensearch): Add BASE_URL config to support reverse proxy deployments
This commit is contained in:
31
README.md
31
README.md
@ -140,6 +140,37 @@ For more details, check out the full documentation [here](https://github.com/Itz
|
|||||||
|
|
||||||
Perplexica runs on Next.js and handles all API requests. It works right away on the same network and stays accessible even with port forwarding.
|
Perplexica runs on Next.js and handles all API requests. It works right away on the same network and stays accessible even with port forwarding.
|
||||||
|
|
||||||
|
### Running Behind a Reverse Proxy
|
||||||
|
|
||||||
|
When running Perplexica behind a reverse proxy (like Nginx, Apache, or Traefik), follow these steps to ensure proper functionality:
|
||||||
|
|
||||||
|
1. **Configure the BASE_URL setting**:
|
||||||
|
- In `config.toml`, set the `BASE_URL` parameter under the `[GENERAL]` section to your public-facing URL (e.g., `https://perplexica.yourdomain.com`)
|
||||||
|
|
||||||
|
2. **Ensure proper headers forwarding**:
|
||||||
|
- Your reverse proxy should forward the following headers:
|
||||||
|
- `X-Forwarded-Host`
|
||||||
|
- `X-Forwarded-Proto`
|
||||||
|
- `X-Forwarded-Port` (if using non-standard ports)
|
||||||
|
|
||||||
|
3. **Example Nginx configuration**:
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name perplexica.yourdomain.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:3000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This ensures that OpenSearch descriptions, browser integrations, and all URLs work properly when accessing Perplexica through your reverse proxy.
|
||||||
|
|
||||||
## One-Click Deployment
|
## One-Click Deployment
|
||||||
|
|
||||||
[](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica)
|
[](https://usw.sealos.io/?openapp=system-template%3FtemplateName%3Dperplexica)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[GENERAL]
|
[GENERAL]
|
||||||
SIMILARITY_MEASURE = "cosine" # "cosine" or "dot"
|
SIMILARITY_MEASURE = "cosine" # "cosine" or "dot"
|
||||||
KEEP_ALIVE = "5m" # How long to keep Ollama models loaded into memory. (Instead of using -1 use "-1m")
|
KEEP_ALIVE = "5m" # How long to keep Ollama models loaded into memory. (Instead of using -1 use "-1m")
|
||||||
|
BASE_URL = "" # Optional. When set, overrides detected URL for OpenSearch and other public URLs
|
||||||
|
|
||||||
[MODELS.OPENAI]
|
[MODELS.OPENAI]
|
||||||
API_KEY = ""
|
API_KEY = ""
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
getAnthropicApiKey,
|
getAnthropicApiKey,
|
||||||
|
getBaseUrl,
|
||||||
getCustomOpenaiApiKey,
|
getCustomOpenaiApiKey,
|
||||||
getCustomOpenaiApiUrl,
|
getCustomOpenaiApiUrl,
|
||||||
getCustomOpenaiModelName,
|
getCustomOpenaiModelName,
|
||||||
@ -60,6 +61,7 @@ export const GET = async (req: Request) => {
|
|||||||
config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl();
|
config['customOpenaiApiUrl'] = getCustomOpenaiApiUrl();
|
||||||
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
config['customOpenaiApiKey'] = getCustomOpenaiApiKey();
|
||||||
config['customOpenaiModelName'] = getCustomOpenaiModelName();
|
config['customOpenaiModelName'] = getCustomOpenaiModelName();
|
||||||
|
config['baseUrl'] = getBaseUrl();
|
||||||
|
|
||||||
return Response.json({ ...config }, { status: 200 });
|
return Response.json({ ...config }, { status: 200 });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
|
import { getBaseUrl } from '@/lib/config';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
/**
|
||||||
// Get the host from the request
|
* Creates an OpenSearch XML response with the given origin URL
|
||||||
const url = new URL(request.url);
|
*/
|
||||||
const origin = url.origin;
|
function generateOpenSearchResponse(origin: string): NextResponse {
|
||||||
|
|
||||||
// Create the OpenSearch XML with the correct origin
|
|
||||||
const opensearchXml = `<?xml version="1.0" encoding="utf-8"?>
|
const opensearchXml = `<?xml version="1.0" encoding="utf-8"?>
|
||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">
|
||||||
<ShortName>Perplexica</ShortName>
|
<ShortName>Perplexica</ShortName>
|
||||||
@ -17,10 +16,48 @@ export async function GET(request: Request) {
|
|||||||
<Url type="application/opensearchdescription+xml" rel="self" template="${origin}/api/opensearch"/>
|
<Url type="application/opensearchdescription+xml" rel="self" template="${origin}/api/opensearch"/>
|
||||||
</OpenSearchDescription>`;
|
</OpenSearchDescription>`;
|
||||||
|
|
||||||
// Return the XML with the correct content type
|
|
||||||
return new NextResponse(opensearchXml, {
|
return new NextResponse(opensearchXml, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/opensearchdescription+xml',
|
'Content-Type': 'application/opensearchdescription+xml',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
// Check if a BASE_URL is explicitly configured
|
||||||
|
const configBaseUrl = getBaseUrl();
|
||||||
|
|
||||||
|
// If BASE_URL is configured, use it, otherwise detect from request
|
||||||
|
if (configBaseUrl) {
|
||||||
|
// Remove any trailing slashes for consistency
|
||||||
|
let origin = configBaseUrl.replace(/\/+$/, '');
|
||||||
|
return generateOpenSearchResponse(origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect the correct origin, taking into account reverse proxy headers
|
||||||
|
const url = new URL(request.url);
|
||||||
|
let origin = url.origin;
|
||||||
|
|
||||||
|
// Extract headers
|
||||||
|
const headers = Object.fromEntries(request.headers);
|
||||||
|
|
||||||
|
// Check for X-Forwarded-Host and related headers to handle reverse proxies
|
||||||
|
if (headers['x-forwarded-host']) {
|
||||||
|
// Determine protocol: prefer X-Forwarded-Proto, fall back to original or https
|
||||||
|
const protocol = headers['x-forwarded-proto'] || url.protocol.replace(':', '');
|
||||||
|
// Build the correct public-facing origin
|
||||||
|
origin = `${protocol}://${headers['x-forwarded-host']}`;
|
||||||
|
|
||||||
|
// Handle non-standard ports if specified in X-Forwarded-Port
|
||||||
|
if (headers['x-forwarded-port']) {
|
||||||
|
const port = headers['x-forwarded-port'];
|
||||||
|
// Don't append standard ports (80 for HTTP, 443 for HTTPS)
|
||||||
|
if (!((protocol === 'http' && port === '80') || (protocol === 'https' && port === '443'))) {
|
||||||
|
origin = `${origin}:${port}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate and return the OpenSearch response
|
||||||
|
return generateOpenSearchResponse(origin);
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ interface Config {
|
|||||||
GENERAL: {
|
GENERAL: {
|
||||||
SIMILARITY_MEASURE: string;
|
SIMILARITY_MEASURE: string;
|
||||||
KEEP_ALIVE: string;
|
KEEP_ALIVE: string;
|
||||||
|
BASE_URL?: string;
|
||||||
};
|
};
|
||||||
MODELS: {
|
MODELS: {
|
||||||
OPENAI: {
|
OPENAI: {
|
||||||
@ -70,6 +71,8 @@ export const getSimilarityMeasure = () =>
|
|||||||
|
|
||||||
export const getKeepAlive = () => loadConfig().GENERAL.KEEP_ALIVE;
|
export const getKeepAlive = () => loadConfig().GENERAL.KEEP_ALIVE;
|
||||||
|
|
||||||
|
export const getBaseUrl = () => loadConfig().GENERAL.BASE_URL;
|
||||||
|
|
||||||
export const getOpenaiApiKey = () => loadConfig().MODELS.OPENAI.API_KEY;
|
export const getOpenaiApiKey = () => loadConfig().MODELS.OPENAI.API_KEY;
|
||||||
|
|
||||||
export const getGroqApiKey = () => loadConfig().MODELS.GROQ.API_KEY;
|
export const getGroqApiKey = () => loadConfig().MODELS.GROQ.API_KEY;
|
||||||
|
Reference in New Issue
Block a user