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: Perplexica can be easily run using Docker. Simply run the following command:
```bash ```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. 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 #### Using Perplexica with Your Own SearxNG Instance
If you already have SearxNG running, you can use the slim version of Perplexica: If you already have SearxNG running, you can use the slim version of Perplexica:
```bash ```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: **Important**: Make sure your SearxNG instance has:
@@ -118,7 +118,7 @@ If you prefer to build from source or need more control:
```bash ```bash
docker build -t perplexica . 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. 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 pull itzcrazykns1337/perplexica:latest
docker stop perplexica docker stop perplexica
docker rm 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: For slim version:
@@ -19,7 +19,7 @@ For slim version:
docker pull itzcrazykns1337/perplexica:slim-latest docker pull itzcrazykns1337/perplexica:slim-latest
docker stop perplexica docker stop perplexica
docker rm 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. 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", "name": "perplexica-frontend",
"version": "1.11.1", "version": "1.11.0",
"license": "MIT", "license": "MIT",
"author": "ItzCrazyKns", "author": "ItzCrazyKns",
"scripts": { "scripts": {

View File

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

View File

@@ -11,16 +11,14 @@ const ModelSelect = ({
type: 'chat' | 'embedding'; type: 'chat' | 'embedding';
}) => { }) => {
const [selectedModel, setSelectedModel] = useState<string>( const [selectedModel, setSelectedModel] = useState<string>(
type === 'chat' `${providers[0]?.id}/${providers[0].embeddingModels[0]?.key}`,
? `${localStorage.getItem('chatModelProviderId')}/${localStorage.getItem('chatModelKey')}`
: `${localStorage.getItem('embeddingModelProviderId')}/${localStorage.getItem('embeddingModelKey')}`,
); );
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const handleSave = async (newValue: string) => { const handleSave = async (newValue: string) => {
setLoading(true); setLoading(true);
setSelectedModel(newValue); setSelectedModel(newValue);
console.log(newValue);
try { try {
if (type === 'chat') { if (type === 'chat') {
localStorage.setItem('chatModelProviderId', newValue.split('/')[0]); 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 ( 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"> <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."', 'e.g., "Respond in a friendly and concise tone" or "Use British English and format answers as bullet points."',
scope: 'client', 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: [], modelProviders: [],
search: [ search: [