diff --git a/README.md b/README.md index 939da3290..685e0c281 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,8 @@ the [past releases changelog](docs/changelog.md). ![React](https://img.shields.io/badge/React-61DAFB?style=&logo=react&logoColor=black) ![Next.js](https://img.shields.io/badge/Next.js-000000?style=&logo=vercel&logoColor=white) -Clone this repo, install the dependencies, and run the development server: +Clone this repo, install the dependencies (all locally), and run the development server (which auto-watches the +files for changes): ```bash git clone https://github.com/enricoros/big-agi.git @@ -91,15 +92,21 @@ npm install npm run dev ``` -The app will be running on `http://localhost:3000` +The development app will be running on `http://localhost:3000`. Development builds have the advantage of not requiring +a build step, but can be slower than production builds. Also, development builds won't have timeout on edge functions. -Integrations: +## 🌐 Deploy manually -* Local models: Ollama, Oobabooga, LocalAi, etc. -* [ElevenLabs](https://elevenlabs.io/) Voice Synthesis (bring your own voice too) - Settings > Text To Speech -* [Helicone](https://www.helicone.ai/) LLM Observability Platform - Models > OpenAI > Advanced > API Host: 'oai.hconeai.com' -* [Paste.gg](https://paste.gg/) Paste Sharing - Chat Menu > Share via paste.gg -* [Prodia](https://prodia.com/) Image Generation - Settings > Image Generation > Api Key & Model +The _production_ build of the application is optimized for performance and is performed by the `npm run build` command, +after installing the required dependencies. + +```bash +# .. repeat the steps above up to `npm install`, then: +npm run build +npm run start --port 3000 +``` + +The app will be running on the specified port, e.g. `http://localhost:3000` ## 🐳 Deploy with Docker @@ -115,7 +122,7 @@ docker run -d -p 3000:3000 big-agi Or run the official container: - manually: `docker run -d -p 3000:3000 ghcr.io/enricoros/big-agi` -- or, with docker-compose: `docker-compose up` +- or, with docker-compose: `docker-compose up` or see [the documentation](docs/deploy-docker.md) for a composer file with integrated browsing ## ☁️ Deploy on Cloudflare Pages @@ -127,7 +134,13 @@ Create your GitHub fork, create a Vercel project over that fork, and deploy it. [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fenricoros%2Fbig-agi&env=OPENAI_API_KEY,OPENAI_API_HOST&envDescription=OpenAI%20KEY%20for%20your%20deployment.%20Set%20HOST%20only%20if%20non-default.) +## Integrations: +* Local models: Ollama, Oobabooga, LocalAi, etc. +* [ElevenLabs](https://elevenlabs.io/) Voice Synthesis (bring your own voice too) - Settings > Text To Speech +* [Helicone](https://www.helicone.ai/) LLM Observability Platform - Models > OpenAI > Advanced > API Host: 'oai.hconeai.com' +* [Paste.gg](https://paste.gg/) Paste Sharing - Chat Menu > Share via paste.gg +* [Prodia](https://prodia.com/) Image Generation - Settings > Image Generation > Api Key & Model
diff --git a/docs/deploy-authentication.md b/docs/deploy-authentication.md new file mode 100644 index 000000000..f4a5f9d4b --- /dev/null +++ b/docs/deploy-authentication.md @@ -0,0 +1,39 @@ +# Authentication + +`big-AGI` does not come with built-in authentication. To secure your deployment, you can implement authentication +in one of the following ways: + +1. Rebuild `big-AGI` with support for [HTTP Basic Authentication](#http-authentication) +2. Utilize user authentication features provided by your [cloud deployment platform](#cloud-deployments-authentication) +3. Develop a custom authentication solution + +## HTTP Authentication + +[HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) is a simple method +to secure your application. To enable it in `big-AGI`, you **must manually build the application**: + +- Build `big-AGI` with HTTP authentication enabled: + - Clone the repository + - Rename `middleware_BASIC_AUTH.ts` to `middleware_BASIC_AUTH.ts` + - Build: follow the build instructions in [Deploy manually](../README.md#-deploy-manually) or [Deploying with Docker](deploy-docker.md) + +- Configure the following [environment variables](environment-variables.md) before launching `big-AGI`: +```dotenv +HTTP_BASIC_AUTH_USERNAME= +HTTP_BASIC_AUTH_PASSWORD= +``` + +- Start the application + +## Cloud Deployments Authentication + +> This approach allows you to enable authentication without rebuilding the application by using the features +> provided by your cloud platform to manage user accounts and access. + +Many cloud deployment platforms offer built-in authentication mechanisms. Refer to the platform's documentation +for setup instructions: + +1. [CloudFlare Access / Zero Trust](https://www.cloudflare.com/zero-trust/products/access/) +2. [Vercel Authentication](https://vercel.com/docs/security/deployment-protection/methods-to-protect-deployments/vercel-authentication) +3. [Vercel Password Protection](https://vercel.com/docs/security/deployment-protection/methods-to-protect-deployments/password-protection) +4. Let us know when you test more solutions (Heroku, AWS IAM, Google IAP, etc.) diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 785a96e1b..1272e5724 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -44,6 +44,10 @@ PUPPETEER_WSS_ENDPOINT= # Backend Analytics BACKEND_ANALYTICS= + +# Backend HTTP Basic Authentication +HTTP_BASIC_AUTH_USERNAME= +HTTP_BASIC_AUTH_PASSWORD= ``` ## Variables Documentation @@ -94,21 +98,23 @@ It is currently supported for: Enable the app to Talk, Draw, and Google things up. -| Variable | Description | -|:-------------------------|:------------------------------------------------------------------------------------------------------------------------| -| **Text-To-Speech** | [ElevenLabs](https://elevenlabs.io/) is a high quality speech synthesis service | -| `ELEVENLABS_API_KEY` | ElevenLabs API Key - used for calls, etc. | -| `ELEVENLABS_API_HOST` | Custom host for ElevenLabs | -| `ELEVENLABS_VOICE_ID` | Default voice ID for ElevenLabs | -| **Google Custom Search** | [Google Programmable Search Engine](https://programmablesearchengine.google.com/about/) produces links to pages | -| `GOOGLE_CLOUD_API_KEY` | Google Cloud API Key, used with the '/react' command - [Link to GCP](https://console.cloud.google.com/apis/credentials) | -| `GOOGLE_CSE_ID` | Google Custom/Programmable Search Engine ID - [Link to PSE](https://programmablesearchengine.google.com/) | -| **Text-To-Image** | [Prodia](https://prodia.com/) is a reliable image generation service | -| `PRODIA_API_KEY` | Prodia API Key - used with '/imagine ...' | -| **Browse** | | -| `PUPPETEER_WSS_ENDPOINT` | Puppeteer WebSocket endpoint - used for browsing, etc. | -| **Backend** | | -| `BACKEND_ANALYTICS` | Semicolon-separated list of analytics flags (see backend.analytics.ts). Flags: `domain` logs the responding domain. | +| Variable | Description | +|:---------------------------|:------------------------------------------------------------------------------------------------------------------------| +| **Text-To-Speech** | [ElevenLabs](https://elevenlabs.io/) is a high quality speech synthesis service | +| `ELEVENLABS_API_KEY` | ElevenLabs API Key - used for calls, etc. | +| `ELEVENLABS_API_HOST` | Custom host for ElevenLabs | +| `ELEVENLABS_VOICE_ID` | Default voice ID for ElevenLabs | +| **Google Custom Search** | [Google Programmable Search Engine](https://programmablesearchengine.google.com/about/) produces links to pages | +| `GOOGLE_CLOUD_API_KEY` | Google Cloud API Key, used with the '/react' command - [Link to GCP](https://console.cloud.google.com/apis/credentials) | +| `GOOGLE_CSE_ID` | Google Custom/Programmable Search Engine ID - [Link to PSE](https://programmablesearchengine.google.com/) | +| **Text-To-Image** | [Prodia](https://prodia.com/) is a reliable image generation service | +| `PRODIA_API_KEY` | Prodia API Key - used with '/imagine ...' | +| **Browse** | | +| `PUPPETEER_WSS_ENDPOINT` | Puppeteer WebSocket endpoint - used for browsing, etc. | +| **Backend** | | +| `BACKEND_ANALYTICS` | Semicolon-separated list of analytics flags (see backend.analytics.ts). Flags: `domain` logs the responding domain. | +| `HTTP_BASIC_AUTH_USERNAME` | Username for HTTP Basic Authentication. See the [Authentication](deploy-authentication.md) guide. | +| `HTTP_BASIC_AUTH_PASSWORD` | Password for HTTP Basic Authentication. | --- diff --git a/middleware_BASIC_AUTH.ts b/middleware_BASIC_AUTH.ts new file mode 100644 index 000000000..1bb820a14 --- /dev/null +++ b/middleware_BASIC_AUTH.ts @@ -0,0 +1,55 @@ +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; + + +// noinspection JSUnusedGlobalSymbols +/** + * Middleware to protect with HTTP Basic Authentication. + */ +export function middleware(request: NextRequest) { + + // Validate http basic auth configuration + if (!process.env.HTTP_BASIC_AUTH_USERNAME || !process.env.HTTP_BASIC_AUTH_PASSWORD) { + console.warn('HTTP Basic Authentication is enabled but not configured'); + return new Response('Unauthorized/Unconfigured', unauthResponse); + } + + // Request client authentication if no credentials are provided + const authHeader = request.headers.get('authorization'); + if (!authHeader?.startsWith('Basic ')) + return new Response('Unauthorized', unauthResponse); + + // Request authentication if credentials are invalid + const base64Credentials = authHeader.split(' ')[1]; + const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); + const [username, password] = credentials.split(':'); + if ( + !username || !password || + username !== process.env.HTTP_BASIC_AUTH_USERNAME || + password !== process.env.HTTP_BASIC_AUTH_PASSWORD + ) + return new Response('Unauthorized', unauthResponse); + + return NextResponse.next(); +} + + +// Response to send when authentication is required +const unauthResponse: ResponseInit = { + status: 401, + headers: { + 'WWW-Authenticate': 'Basic realm="Secure big-AGI"', + }, +}; + +export const config = { + matcher: [ + // Include root + '/', + // Include pages + '/(call|index|news|personas|link)(.*)', + // Include API routes (the most important part to block) + '/api(.*)', + // Note: this excludes _next, /images etc.. + ], +}; \ No newline at end of file diff --git a/pages/api/auth/auth.ts b/pages/api/auth/auth.ts new file mode 100644 index 000000000..9bf5eef42 --- /dev/null +++ b/pages/api/auth/auth.ts @@ -0,0 +1,7 @@ +import type { NextApiRequest, NextApiResponse } from 'next' + +export default function handler(_: NextApiRequest, res: NextApiResponse) { + res.setHeader('WWW-authenticate', 'Basic realm="Private Area"') + res.statusCode = 401 + res.end(`Auth Required.`) +} \ No newline at end of file diff --git a/src/server/env.mjs b/src/server/env.mjs index 88be83455..40e218947 100644 --- a/src/server/env.mjs +++ b/src/server/env.mjs @@ -48,6 +48,10 @@ export const env = createEnv({ // Backend: Analytics flags (e.g. which hostname responds) for managed installs BACKEND_ANALYTICS: z.string().optional().transform(list => (list || '').split(';').filter(flag => !!flag)), + // Backend: HTTP Basic Authentication + HTTP_BASIC_AUTH_USERNAME: z.string().optional(), + HTTP_BASIC_AUTH_PASSWORD: z.string().optional(), + }, onValidationError: error => {