Compare commits

..

1 Commits

Author SHA1 Message Date
ItzCrazyKns
046daf442a feat(docker): update searxng build script 2025-10-23 19:06:27 +05:30
10 changed files with 138 additions and 132 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@@ -79,19 +79,19 @@ There are mainly 2 ways of installing Perplexica - With Docker, Without Docker.
Perplexica can be easily run using Docker. Simply run the following command:
```bash
docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:latest
docker run -p 3000:3000 --name perplexica itzcrazykns1337/perplexica:latest
```
This will pull and start the Perplexica container with the bundled SearxNG search engine. Once running, open your browser and navigate to http://localhost:3000. You can then configure your settings (API keys, models, etc.) directly in the setup screen.
**Note**: The image includes both Perplexica and SearxNG, so no additional setup is required. The `-v` flags create persistent volumes for your data and uploaded files.
**Note**: The image includes both Perplexica and SearxNG, so no additional setup is required.
#### Using Perplexica with Your Own SearxNG Instance
If you already have SearxNG running, you can use the slim version of Perplexica:
```bash
docker run -d -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:slim-latest
docker run -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 --name perplexica itzcrazykns1337/perplexica:slim-latest
```
**Important**: Make sure your SearxNG instance has:
@@ -118,7 +118,7 @@ If you prefer to build from source or need more control:
```bash
docker build -t perplexica .
docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica perplexica
docker run -p 3000:3000 --name perplexica perplexica
```
5. Access Perplexica at http://localhost:3000 and configure your settings in the setup screen.

37
app.dockerfile Normal file
View File

@@ -0,0 +1,37 @@
FROM node:24.5.0-slim AS builder
RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/*
WORKDIR /home/perplexica
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile --network-timeout 600000
COPY tsconfig.json next.config.mjs next-env.d.ts postcss.config.js drizzle.config.ts tailwind.config.ts ./
COPY src ./src
COPY public ./public
COPY drizzle ./drizzle
RUN mkdir -p /home/perplexica/data
RUN yarn build
FROM node:24.5.0-slim
RUN apt-get update && apt-get install -y python3 python3-pip sqlite3 && rm -rf /var/lib/apt/lists/*
WORKDIR /home/perplexica
COPY --from=builder /home/perplexica/public ./public
COPY --from=builder /home/perplexica/.next/static ./public/_next/static
COPY --from=builder /home/perplexica/.next/standalone ./
COPY --from=builder /home/perplexica/data ./data
COPY drizzle ./drizzle
RUN mkdir /home/perplexica/uploads
COPY entrypoint.sh ./entrypoint.sh
RUN chmod +x ./entrypoint.sh
RUN sed -i 's/\r$//' ./entrypoint.sh || true
CMD ["/home/perplexica/entrypoint.sh"]

View File

@@ -1,15 +0,0 @@
services:
perplexica:
image: itzcrazykns1337/perplexica:latest
ports:
- "3000:3000"
volumes:
- data:/home/perplexica/data
- uploads:/home/perplexica/uploads
restart: unless-stopped
volumes:
data:
name: 'perplexica-data'
uploads:
name: 'perplexica-uploads'

View File

@@ -10,7 +10,7 @@ Simply pull the latest image and restart your container:
docker pull itzcrazykns1337/perplexica:latest
docker stop perplexica
docker rm perplexica
docker run -d -p 3000:3000 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:latest
docker run -p 3000:3000 --name perplexica itzcrazykns1337/perplexica:latest
```
For slim version:
@@ -19,7 +19,7 @@ For slim version:
docker pull itzcrazykns1337/perplexica:slim-latest
docker stop perplexica
docker rm perplexica
docker run -d -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 -v perplexica-data:/home/perplexica/data -v perplexica-uploads:/home/perplexica/uploads --name perplexica itzcrazykns1337/perplexica:slim-latest
docker run -p 3000:3000 -e SEARXNG_API_URL=http://your-searxng-url:8080 --name perplexica itzcrazykns1337/perplexica:slim-latest
```
Once updated, go to http://localhost:3000 and verify the latest changes. Your settings are preserved automatically.

View File

@@ -1,6 +1,6 @@
{
"name": "perplexica-frontend",
"version": "1.11.1",
"version": "1.11.0",
"license": "MIT",
"author": "ItzCrazyKns",
"scripts": {

View File

@@ -112,96 +112,100 @@ const ModelProvider = ({
>
<div className="border-t border-light-200 dark:border-dark-200" />
<div className="flex flex-col gap-y-4 px-5 py-4">
<div className="flex flex-col gap-y-2">
<div className="flex flex-row w-full justify-between items-center">
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70">
Chat models
</p>
<AddModel
providerId={modelProvider.id}
setProviders={setProviders}
type="chat"
/>
</div>
<div className="flex flex-col gap-2">
{modelProvider.chatModels.some((m) => m.key === 'error') ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.chatModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.chatModels.map((model, index) => (
<div
key={`${modelProvider.id}-chat-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('chat', model.key);
}}
{modelProvider.chatModels.length > 0 && (
<div className="flex flex-col gap-y-2">
<div className="flex flex-row w-full justify-between items-center">
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70">
Chat models
</p>
<AddModel
providerId={modelProvider.id}
setProviders={setProviders}
type="chat"
/>
</div>
<div className="flex flex-col gap-2">
{modelProvider.chatModels.some((m) => m.key === 'error') ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.chatModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.chatModels.map((model, index) => (
<div
key={`${modelProvider.id}-chat-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<X size={12} />
</button>
</div>
))}
</div>
)}
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('chat', model.key);
}}
>
<X size={12} />
</button>
</div>
))}
</div>
)}
</div>
</div>
</div>
<div className="flex flex-col gap-y-2">
<div className="flex flex-row w-full justify-between items-center">
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70">
Embedding models
</p>
<AddModel
providerId={modelProvider.id}
setProviders={setProviders}
type="embedding"
/>
</div>
<div className="flex flex-col gap-2">
{modelProvider.embeddingModels.some(
(m) => m.key === 'error',
) ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.embeddingModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.embeddingModels.map((model, index) => (
<div
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('embedding', model.key);
}}
)}
{modelProvider.embeddingModels.length > 0 && (
<div className="flex flex-col gap-y-2">
<div className="flex flex-row w-full justify-between items-center">
<p className="text-[11px] lg:text-xs text-black/70 dark:text-white/70">
Embedding models
</p>
<AddModel
providerId={modelProvider.id}
setProviders={setProviders}
type="embedding"
/>
</div>
<div className="flex flex-col gap-2">
{modelProvider.embeddingModels.some(
(m) => m.key === 'error',
) ? (
<div className="flex flex-row items-center gap-2 text-xs lg:text-sm text-red-500 dark:text-red-400 rounded-lg bg-red-50 dark:bg-red-950/20 px-3 py-2 border border-red-200 dark:border-red-900/30">
<AlertCircle size={16} className="shrink-0" />
<span className="break-words">
{
modelProvider.embeddingModels.find(
(m) => m.key === 'error',
)?.name
}
</span>
</div>
) : (
<div className="flex flex-row flex-wrap gap-2">
{modelProvider.embeddingModels.map((model, index) => (
<div
key={`${modelProvider.id}-embedding-${model.key}-${index}`}
className="flex flex-row items-center space-x-1 text-xs lg:text-sm text-black/70 dark:text-white/70 rounded-lg bg-light-secondary dark:bg-dark-secondary px-3 py-1.5"
>
<X size={12} />
</button>
</div>
))}
</div>
)}
<span>{model.name}</span>
<button
onClick={() => {
handleModelDelete('embedding', model.key);
}}
>
<X size={12} />
</button>
</div>
))}
</div>
)}
</div>
</div>
</div>
)}
</div>
</motion.div>
)}

View File

@@ -11,16 +11,14 @@ const ModelSelect = ({
type: 'chat' | 'embedding';
}) => {
const [selectedModel, setSelectedModel] = useState<string>(
type === 'chat'
? `${localStorage.getItem('chatModelProviderId')}/${localStorage.getItem('chatModelKey')}`
: `${localStorage.getItem('embeddingModelProviderId')}/${localStorage.getItem('embeddingModelKey')}`,
`${providers[0]?.id}/${providers[0].embeddingModels[0]?.key}`,
);
const [loading, setLoading] = useState(false);
const handleSave = async (newValue: string) => {
setLoading(true);
setSelectedModel(newValue);
console.log(newValue);
try {
if (type === 'chat') {
localStorage.setItem('chatModelProviderId', newValue.split('/')[0]);

View File

@@ -63,7 +63,8 @@ const SetupConfig = ({
}
};
const hasProviders = providers.length > 0;
const hasProviders =
providers.filter((p) => p.chatModels.length > 0).length > 0;
return (
<div className="w-[95vw] md:w-[80vw] lg:w-[65vw] mx-auto px-2 sm:px-4 md:px-6 flex flex-col space-y-6">

View File

@@ -50,25 +50,6 @@ class ConfigManager {
'e.g., "Respond in a friendly and concise tone" or "Use British English and format answers as bullet points."',
scope: 'client',
},
{
name: 'Measurement Unit',
key: 'measureUnit',
type: 'select',
options: [
{
name: 'Imperial',
value: 'Imperial',
},
{
name: 'Metric',
value: 'Metric',
},
],
required: false,
description: 'Choose between Metric and Imperial measurement unit.',
default: 'Metric',
scope: 'client',
},
],
modelProviders: [],
search: [