mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 06:00:15 -07:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3a9a6b0273 | |||
| 3b51c39fc3 | |||
| 05293ba557 | |||
| d18d5323aa |
@@ -19,3 +19,15 @@ GOOGLE_CLOUD_API_KEY=
|
||||
# [Optional, Search] Google Custom/Programmable Search Engine ID
|
||||
# https://programmablesearchengine.google.com/
|
||||
GOOGLE_CSE_ID=
|
||||
|
||||
|
||||
# see docs/authentication.md to configure this section
|
||||
AUTH_TYPE=
|
||||
# [At least one required if AUTH_TYPE == credential] You may declare credentials for users from 0 to 99.
|
||||
AUTH_USER_0=
|
||||
AUTH_PASSWORD_0=
|
||||
|
||||
# [Required if AUTH_TYPE == basic and not in development mode] See: https://next-auth.js.org/configuration/options#nextauth_url
|
||||
NEXTAUTH_URL=
|
||||
# [Required if AUTH_TYPE == basic] See: https://next-auth.js.org/configuration/options#secret
|
||||
NEXTAUTH_SECRET=
|
||||
|
||||
@@ -42,7 +42,11 @@ Or fork & run on Vercel
|
||||
|
||||
## Latest Drops 🚀
|
||||
|
||||
#### 🚨 April: more #big-agi-energy
|
||||
#### 🚨 May: mature #big-agi-energy
|
||||
|
||||
- 🎉 **Authentication** basic user authentication framework
|
||||
|
||||
#### April: #big-agi-energy grows
|
||||
|
||||
- 🎉 **[Google Search](docs/pixels/feature_react_google.png)** active in ReAct - add your keys to Settings > Google Search
|
||||
- 🎉 **[Reason+Act](docs/pixels/feature_react_turn_on.png)** preview feature - activate with 2-taps on the 'Chat' button
|
||||
@@ -79,6 +83,10 @@ Or fork & run on Vercel
|
||||
|
||||
<br/>
|
||||
|
||||
### Basic Authentication for public deployments 🔐
|
||||
|
||||
To protect the web app owner from incurring unauthorized costs when deploying the app with a backend API key (`OPENAI_API_KEY`), you can [set up basic authentication.](/docs/authentication.md).
|
||||
|
||||
## Why this? 💡
|
||||
|
||||
Because the official Chat ___lacks important features___, is ___more limited than the api___, at times
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
### Authentication with NextAuth.js 🔐
|
||||
|
||||
To protect the web app owner from incurring unauthorized costs when deploying the app with a backend API
|
||||
key (`OPENAI_API_KEY`), you can set up basic authentication using [NextAuth.js](https://next-auth.js.org/).
|
||||
|
||||
#### Configuration
|
||||
|
||||
Update your `.env` file or Environment Variables with the following variables:
|
||||
|
||||
```
|
||||
# [Optional] Set the authentication type to "credential" to enable basic username/password authentication
|
||||
AUTH_TYPE=credential
|
||||
|
||||
# [Required if AUTH_TYPE == credential] Define credentials for users - you can declare up to 100 users
|
||||
AUTH_USER_0=your_username
|
||||
AUTH_PASSWORD_0=your_password
|
||||
AUTH_USER_1=...
|
||||
AUTH_PASSWORD_1=...
|
||||
...
|
||||
|
||||
# [Required if AUTH_TYPE == credential and *not in development mode*] See: https://next-auth.js.org/configuration/options#nextauth_url
|
||||
NEXTAUTH_URL=https://example.com
|
||||
|
||||
# [Required if AUTH_TYPE == credential] See: https://next-auth.js.org/configuration/options#secret
|
||||
NEXTAUTH_SECRET=your_nextauth_secret
|
||||
```
|
||||
|
||||
You can add multiple users by incrementing the index, e.g., `AUTH_USER_1`, `AUTH_PASSWORD_1`, and so on. They do not
|
||||
need to be contiguous.
|
||||
|
||||
#### Usage
|
||||
|
||||
Once you have set up basic authentication, users will be prompted to enter their credentials when accessing the app.
|
||||
Only users with valid credentials will be able to use the app and make requests to the OpenAI API.
|
||||
|
||||
For more information on configuring and using NextAuth.js, refer to
|
||||
the [official documentation](https://next-auth.js.org/).
|
||||
@@ -0,0 +1,16 @@
|
||||
import { withAuth } from 'next-auth/middleware';
|
||||
|
||||
import { authType } from '@/modules/authentication/auth.server';
|
||||
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export const middleware = !authType ? () => null : withAuth({
|
||||
callbacks: {
|
||||
authorized({ req, token }) {
|
||||
// console.log('authorized', req, token);
|
||||
return !!token;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const config = { matcher: ['/:path*'] };
|
||||
@@ -7,6 +7,8 @@ const nextConfig = {
|
||||
HAS_SERVER_KEY_ELEVENLABS: !!process.env.ELEVENLABS_API_KEY,
|
||||
HAS_SERVER_KEY_PRODIA: !!process.env.PRODIA_API_KEY,
|
||||
HAS_SERVER_KEYS_GOOGLE_CSE: !!process.env.GOOGLE_CLOUD_API_KEY && !!process.env.GOOGLE_CSE_ID,
|
||||
// for auth only
|
||||
SERVER_AUTH_TYPE: process.env.AUTH_TYPE,
|
||||
},
|
||||
webpack(config, { isServer, dev }) {
|
||||
// @mui/joy: anything material gets redirected to Joy
|
||||
|
||||
Generated
+121
-3
@@ -18,6 +18,7 @@
|
||||
"@vercel/analytics": "^1.0.0",
|
||||
"eventsource-parser": "^1.0.0",
|
||||
"next": "^13.3.2",
|
||||
"next-auth": "^4.21.1",
|
||||
"pdfjs-dist": "^3.5.141",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -915,6 +916,14 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@panva/hkdf": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz",
|
||||
"integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/utils": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz",
|
||||
@@ -1714,6 +1723,14 @@
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
@@ -3520,6 +3537,14 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/jose": {
|
||||
"version": "4.14.4",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz",
|
||||
"integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz",
|
||||
@@ -3675,7 +3700,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
@@ -4670,6 +4694,41 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-auth": {
|
||||
"version": "4.22.1",
|
||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.22.1.tgz",
|
||||
"integrity": "sha512-NTR3f6W7/AWXKw8GSsgSyQcDW6jkslZLH8AiZa5PQ09w1kR8uHtR9rez/E9gAq/o17+p0JYHE8QjF3RoniiObA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.13",
|
||||
"@panva/hkdf": "^1.0.2",
|
||||
"cookie": "^0.5.0",
|
||||
"jose": "^4.11.4",
|
||||
"oauth": "^0.9.15",
|
||||
"openid-client": "^5.4.0",
|
||||
"preact": "^10.6.3",
|
||||
"preact-render-to-string": "^5.1.19",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "^12.2.5 || ^13",
|
||||
"nodemailer": "^6.6.5",
|
||||
"react": "^17.0.2 || ^18",
|
||||
"react-dom": "^17.0.2 || ^18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"nodemailer": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/next-auth/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||
@@ -4717,6 +4776,11 @@
|
||||
"set-blocking": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@@ -4725,6 +4789,14 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||
@@ -4838,6 +4910,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/oidc-token-hash": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
|
||||
"integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
|
||||
"engines": {
|
||||
"node": "^10.13.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -4864,6 +4944,20 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/openid-client": {
|
||||
"version": "5.4.2",
|
||||
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.2.tgz",
|
||||
"integrity": "sha512-lIhsdPvJ2RneBm3nGBBhQchpe3Uka//xf7WPHTIglery8gnckvW7Bd9IaQzekzXJvWthCMyi/xVEyGW0RFPytw==",
|
||||
"dependencies": {
|
||||
"jose": "^4.14.1",
|
||||
"lru-cache": "^6.0.0",
|
||||
"object-hash": "^2.2.0",
|
||||
"oidc-token-hash": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/optionator": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
|
||||
@@ -5042,6 +5136,26 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.13.2",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.13.2.tgz",
|
||||
"integrity": "sha512-q44QFLhOhty2Bd0Y46fnYW0gD/cbVM9dUVtNTDKPcdXSMA7jfY+Jpd6rk3GB0lcQss0z5s/6CmVP0Z/hV+g6pw==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
}
|
||||
},
|
||||
"node_modules/preact-render-to-string": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
|
||||
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
|
||||
"dependencies": {
|
||||
"pretty-format": "^3.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"preact": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||
@@ -5066,6 +5180,11 @@
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew=="
|
||||
},
|
||||
"node_modules/prismjs": {
|
||||
"version": "1.29.0",
|
||||
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
|
||||
@@ -6258,8 +6377,7 @@
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"eventsource-parser": "^1.0.0",
|
||||
"next": "^13.3.2",
|
||||
"pdfjs-dist": "^3.5.141",
|
||||
"next-auth": "^4.21.1",
|
||||
"prismjs": "^1.29.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
||||
+16
-9
@@ -5,6 +5,8 @@ import { AppProps } from 'next/app';
|
||||
import { CacheProvider, EmotionCache } from '@emotion/react';
|
||||
import { CssBaseline, CssVarsProvider } from '@mui/joy';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { Session as NextAuthSession } from 'next-auth';
|
||||
import { SessionProvider } from 'next-auth/react';
|
||||
|
||||
import '@/common/styles/GithubMarkdown.css';
|
||||
import { Brand } from '@/common/brand';
|
||||
@@ -16,9 +18,10 @@ const clientSideEmotionCache = createEmotionCache();
|
||||
|
||||
export interface MyAppProps extends AppProps {
|
||||
emotionCache?: EmotionCache;
|
||||
session?: NextAuthSession;
|
||||
}
|
||||
|
||||
export default function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }: MyAppProps) {
|
||||
export default function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps: { session, ...pageProps } }: MyAppProps) {
|
||||
const [queryClient] = React.useState(() => new QueryClient());
|
||||
return <>
|
||||
<CacheProvider value={emotionCache}>
|
||||
@@ -26,14 +29,18 @@ export default function MyApp({ Component, emotionCache = clientSideEmotionCache
|
||||
<title>{Brand.Title.Common}</title>
|
||||
<meta name='viewport' content='minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no' />
|
||||
</Head>
|
||||
{/* Rect-query provider */}
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CssVarsProvider defaultMode='light' theme={theme}>
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
<CssBaseline />
|
||||
<Component {...pageProps} />
|
||||
</CssVarsProvider>
|
||||
</QueryClientProvider>
|
||||
{/* Next-Auth provider */}
|
||||
<SessionProvider session={session}>
|
||||
{/* Rect-query provider */}
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{/* JoyUI/Emotion */}
|
||||
<CssVarsProvider defaultMode='light' theme={theme}>
|
||||
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
|
||||
<CssBaseline />
|
||||
<Component {...pageProps} />
|
||||
</CssVarsProvider>
|
||||
</QueryClientProvider>
|
||||
</SessionProvider>
|
||||
</CacheProvider>
|
||||
<VercelAnalytics debug={false} />
|
||||
</>;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { default as NextAuth } from 'next-auth';
|
||||
|
||||
import { authBasicUsers, authCreateProviders, authType } from '@/modules/authentication/auth.server';
|
||||
|
||||
|
||||
const authOptions = {
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
providers: authCreateProviders(),
|
||||
};
|
||||
|
||||
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!authType)
|
||||
return res.status(200).send('Auth not enabled');
|
||||
|
||||
if (Object.keys(authBasicUsers).length <= 0)
|
||||
res.status(200).send('Auth enabled but no users have been set up');
|
||||
|
||||
return NextAuth(req, res, authOptions);
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { signIn, signOut, useSession } from 'next-auth/react';
|
||||
|
||||
import { Badge, Box, Button, IconButton, ListDivider, ListItem, ListItemDecorator, Menu, MenuItem, Sheet, Stack, SvgIcon, Switch, useColorScheme, useTheme } from '@mui/joy';
|
||||
import { Badge, Box, Button, IconButton, ListDivider, ListItem, ListItemDecorator, Menu, MenuItem, Sheet, Stack, SvgIcon, Switch, Typography, useColorScheme, useTheme } from '@mui/joy';
|
||||
import { SxProps } from '@mui/joy/styles/types';
|
||||
import CheckBoxOutlineBlankOutlinedIcon from '@mui/icons-material/CheckBoxOutlineBlankOutlined';
|
||||
import CheckBoxOutlinedIcon from '@mui/icons-material/CheckBoxOutlined';
|
||||
@@ -10,11 +11,15 @@ import DarkModeIcon from '@mui/icons-material/DarkMode';
|
||||
import ExitToAppIcon from '@mui/icons-material/ExitToApp';
|
||||
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import LoginIcon from '@mui/icons-material/Login';
|
||||
import LogoutIcon from '@mui/icons-material/Logout';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
|
||||
import SettingsSuggestIcon from '@mui/icons-material/SettingsSuggest';
|
||||
|
||||
import { buildTimeAuthEnabled } from '@/modules/authentication/auth.client';
|
||||
|
||||
import { Brand } from '@/common/brand';
|
||||
import { ChatModelId, ChatModels, SystemPurposeId, SystemPurposes } from '../../../../data';
|
||||
import { ConfirmationModal } from '@/common/components/ConfirmationModal';
|
||||
@@ -104,6 +109,8 @@ export function ApplicationBar(props: {
|
||||
|
||||
// center buttons
|
||||
|
||||
const { data: authSession } = useSession();
|
||||
|
||||
const handleChatModelChange = (event: any, value: ChatModelId | null) =>
|
||||
value && props.conversationId && setChatModelId(props.conversationId, value);
|
||||
|
||||
@@ -246,10 +253,24 @@ export function ApplicationBar(props: {
|
||||
)}
|
||||
|
||||
</Stack>
|
||||
|
||||
<IconButton variant='plain' onClick={event => setActionsMenuAnchor(event.currentTarget)}>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
<Stack direction='row'>
|
||||
{buildTimeAuthEnabled && (
|
||||
authSession?.user ? (
|
||||
<IconButton onClick={() => signOut()}>
|
||||
<LogoutIcon style={{ marginRight: '0.33em' }} />
|
||||
<Typography level='body3'>Sign out {authSession.user?.name ?? ''}</Typography>
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton onClick={() => signIn()}>
|
||||
<LoginIcon style={{ marginRight: '0.33em' }} />
|
||||
<Typography>Sign in </Typography>
|
||||
</IconButton>
|
||||
)
|
||||
)}
|
||||
<IconButton variant='plain' onClick={event => setActionsMenuAnchor(event.currentTarget)}>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Sheet>
|
||||
|
||||
|
||||
|
||||
Vendored
+1
@@ -31,6 +31,7 @@ declare namespace NodeJS {
|
||||
HAS_SERVER_KEY_ELEVENLABS: boolean;
|
||||
HAS_SERVER_KEY_PRODIA: boolean;
|
||||
HAS_SERVER_KEYS_GOOGLE_CSE: boolean;
|
||||
SERVER_AUTH_TYPE: string | undefined;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { validateAuthenticationType } from './auth.server';
|
||||
|
||||
export const buildTimeAuthEnabled = !!validateAuthenticationType(process.env.SERVER_AUTH_TYPE);
|
||||
@@ -0,0 +1,84 @@
|
||||
import { AuthenticationBasicUser, AuthenticationType } from './auth.types';
|
||||
import { default as CredentialsProvider } from 'next-auth/providers/credentials';
|
||||
|
||||
|
||||
// Env functions
|
||||
|
||||
export function validateAuthenticationType(authType?: string): AuthenticationType | null {
|
||||
if (!authType)
|
||||
return null;
|
||||
if (authType === 'credential')
|
||||
return authType;
|
||||
throw new Error(`Invalid authentication type: ${authType}`);
|
||||
}
|
||||
|
||||
function getBasicUsersFromEnvironment(): Record<string, AuthenticationBasicUser> {
|
||||
const users: Record<string, AuthenticationBasicUser> = {};
|
||||
for (const i of [...Array(99).keys()]) {
|
||||
const username = (process.env[`AUTH_USER_${i}`] ?? '').trim();
|
||||
const password = (process.env[`AUTH_PASSWORD_${i}`] ?? '').trim();
|
||||
if (username.length > 0 && password.length > 0) {
|
||||
users[username] = {
|
||||
username,
|
||||
password,
|
||||
};
|
||||
}
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
function authProductionPrintNotices() {
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
let message = process.env.OPENAI_API_KEY ? 'OPENAI_API_KEY has been provided. ' : '';
|
||||
message +=
|
||||
authType
|
||||
? Object.keys(authBasicUsers).length > 0
|
||||
? 'Info: AUTH_TYPE has been provided and users have been set up. '
|
||||
: 'Warning: AUTH_TYPE has been provided but no users have been set up. '
|
||||
: 'However, an AUTH_TYPE has not been provided. This means that anyone can use your OpenAI API and incur costs. ';
|
||||
console.warn(message);
|
||||
}
|
||||
}
|
||||
|
||||
export const authType: AuthenticationType | null = validateAuthenticationType(process.env.SERVER_AUTH_TYPE);
|
||||
export const authBasicUsers: Record<string, AuthenticationBasicUser> = authType === 'credential' ? getBasicUsersFromEnvironment() : {};
|
||||
|
||||
authProductionPrintNotices();
|
||||
|
||||
|
||||
// Next Auth functions
|
||||
|
||||
export function authCreateProviders() {
|
||||
const providers: any[] = [];
|
||||
|
||||
if (authType === 'credential') {
|
||||
providers.push(
|
||||
CredentialsProvider({
|
||||
credentials: {
|
||||
username: { label: 'Username', type: 'text' },
|
||||
password: { label: 'Password', type: 'password' },
|
||||
},
|
||||
|
||||
async authorize(credentials, req) {
|
||||
const username = credentials?.username;
|
||||
const password = credentials?.password;
|
||||
|
||||
// Check if credentials are valid
|
||||
if (username && password) {
|
||||
const user = authBasicUsers[username] ?? null;
|
||||
if (user?.password === password) {
|
||||
return {
|
||||
id: user.username,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If credentials are invalid, return null
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return providers;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export type AuthenticationType = 'credential';
|
||||
|
||||
export interface AuthenticationBasicUser {
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
Reference in New Issue
Block a user