Unverified Commit 9763d885 authored by lainedfles's avatar lainedfles Committed by GitHub
Browse files

Merge Updates & Dockerfile improvements

parent fdef2abd
...@@ -17,7 +17,7 @@ spec: ...@@ -17,7 +17,7 @@ spec:
resources: resources:
requests: requests:
storage: {{ .Values.webui.persistence.size }} storage: {{ .Values.webui.persistence.size }}
storageClass: {{ .Values.webui.persistence.storageClass }} storageClassName: {{ .Values.webui.persistence.storageClass }}
{{- with .Values.webui.persistence.selector }} {{- with .Values.webui.persistence.selector }}
selector: selector:
{{- toYaml . | nindent 4 }} {{- toYaml . | nindent 4 }}
......
...@@ -4,6 +4,9 @@ metadata: ...@@ -4,6 +4,9 @@ metadata:
name: {{ include "open-webui.name" . }} name: {{ include "open-webui.name" . }}
labels: labels:
{{- include "open-webui.labels" . | nindent 4 }} {{- include "open-webui.labels" . | nindent 4 }}
{{- with .Values.webui.service.labels }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- with .Values.webui.service.annotations }} {{- with .Values.webui.service.annotations }}
annotations: annotations:
{{- toYaml . | nindent 4 }} {{- toYaml . | nindent 4 }}
...@@ -11,14 +14,16 @@ metadata: ...@@ -11,14 +14,16 @@ metadata:
spec: spec:
selector: selector:
{{- include "open-webui.selectorLabels" . | nindent 4 }} {{- include "open-webui.selectorLabels" . | nindent 4 }}
{{- with .Values.webui.service }} type: {{ .Values.webui.service.type | default "ClusterIP" }}
type: {{ .type }}
ports: ports:
- protocol: TCP - protocol: TCP
name: http name: http
port: {{ .port }} port: {{ .Values.webui.service.port }}
targetPort: http targetPort: http
{{- if .nodePort }} {{- if .Values.webui.service.nodePort }}
nodePort: {{ .nodePort | int }} nodePort: {{ .Values.webui.service.nodePort | int }}
{{- end }} {{- end }}
{{- end }} {{- if .Values.webui.service.loadBalancerClass }}
loadBalancerClass: {{ .Values.webui.service.loadBalancerClass | quote }}
{{- end }}
...@@ -70,3 +70,5 @@ webui: ...@@ -70,3 +70,5 @@ webui:
port: 80 port: 80
containerPort: 8080 containerPort: 8080
nodePort: "" nodePort: ""
labels: {}
loadBalancerClass: ""
...@@ -35,4 +35,4 @@ spec: ...@@ -35,4 +35,4 @@ spec:
volumes: volumes:
- name: webui-volume - name: webui-volume
persistentVolumeClaim: persistentVolumeClaim:
claimName: ollama-webui-pvc claimName: open-webui-pvc
\ No newline at end of file \ No newline at end of file
...@@ -2,8 +2,8 @@ apiVersion: v1 ...@@ -2,8 +2,8 @@ apiVersion: v1
kind: PersistentVolumeClaim kind: PersistentVolumeClaim
metadata: metadata:
labels: labels:
app: ollama-webui app: open-webui
name: ollama-webui-pvc name: open-webui-pvc
namespace: open-webui namespace: open-webui
spec: spec:
accessModes: ["ReadWriteOnce"] accessModes: ["ReadWriteOnce"]
......
This diff is collapsed.
{ {
"name": "open-webui", "name": "open-webui",
"version": "0.1.111", "version": "0.1.116",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev --host", "dev": "vite dev --host",
...@@ -13,7 +13,8 @@ ...@@ -13,7 +13,8 @@
"lint:types": "npm run check", "lint:types": "npm run check",
"lint:backend": "pylint backend/", "lint:backend": "pylint backend/",
"format": "prettier --plugin-search-dir --write '**/*.{js,ts,svelte,css,md,html,json}'", "format": "prettier --plugin-search-dir --write '**/*.{js,ts,svelte,css,md,html,json}'",
"format:backend": "yapf --recursive backend -p -i" "format:backend": "black . --exclude \"/venv/\"",
"i18n:parse": "i18next --config i18next-parser.config.ts && prettier --write 'src/lib/i18n/**/*.{js,json}'"
}, },
"devDependencies": { "devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-auto": "^2.0.0",
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
"eslint": "^8.56.0", "eslint": "^8.56.0",
"eslint-config-prettier": "^8.5.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0", "eslint-plugin-svelte": "^2.30.0",
"i18next-parser": "^8.13.0",
"postcss": "^8.4.31", "postcss": "^8.4.31",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1", "prettier-plugin-svelte": "^2.10.1",
...@@ -42,9 +44,13 @@ ...@@ -42,9 +44,13 @@
"dependencies": { "dependencies": {
"@sveltejs/adapter-node": "^1.3.1", "@sveltejs/adapter-node": "^1.3.1",
"async": "^3.2.5", "async": "^3.2.5",
"bits-ui": "^0.19.7",
"dayjs": "^1.11.10", "dayjs": "^1.11.10",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"i18next": "^23.10.0",
"i18next-browser-languagedetector": "^7.2.0",
"i18next-resources-to-backend": "^1.2.0",
"idb": "^7.1.1", "idb": "^7.1.1",
"js-sha256": "^0.10.1", "js-sha256": "^0.10.1",
"katex": "^0.16.9", "katex": "^0.16.9",
...@@ -53,4 +59,4 @@ ...@@ -53,4 +59,4 @@
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"uuid": "^9.0.1" "uuid": "^9.0.1"
} }
} }
\ No newline at end of file
...@@ -78,3 +78,7 @@ select { ...@@ -78,3 +78,7 @@ select {
/* for Chrome */ /* for Chrome */
-webkit-appearance: none; -webkit-appearance: none;
} }
.katex-mathml {
display: none;
}
...@@ -8,18 +8,39 @@ ...@@ -8,18 +8,39 @@
<meta name="robots" content="noindex,nofollow" /> <meta name="robots" content="noindex,nofollow" />
<script> <script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC // On page load or when changing themes, best to add inline in `head` to avoid FOUC
if ( (() => {
localStorage.theme === 'light' || if (localStorage?.theme && localStorage?.theme.includes('oled')) {
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches) document.documentElement.style.setProperty('--color-gray-900', '#000000');
) { document.documentElement.style.setProperty('--color-gray-950', '#000000');
document.documentElement.classList.add('light'); document.documentElement.classList.add('dark');
} else if (localStorage.theme) { } else if (
localStorage.theme.split(' ').forEach((e) => { localStorage.theme === 'light' ||
document.documentElement.classList.add(e); (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches)
) {
document.documentElement.classList.add('light');
} else if (localStorage.theme && localStorage.theme !== 'system') {
localStorage.theme.split(' ').forEach((e) => {
document.documentElement.classList.add(e);
});
} else if (localStorage.theme && localStorage.theme === 'system') {
systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
document.documentElement.classList.add(systemTheme ? 'dark' : 'light');
} else {
document.documentElement.classList.add('dark');
}
window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
if (localStorage.theme === 'system') {
if (e.matches) {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
}
}
}); });
} else { })();
document.documentElement.classList.add('dark');
}
</script> </script>
%sveltekit.head% %sveltekit.head%
......
...@@ -139,7 +139,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => { ...@@ -139,7 +139,7 @@ export const updateOpenAIKey = async (token: string = '', key: string) => {
return res.OPENAI_API_KEY; return res.OPENAI_API_KEY;
}; };
export const getAUTOMATIC1111Url = async (token: string = '') => { export const getImageGenerationEngineUrls = async (token: string = '') => {
let error = null; let error = null;
const res = await fetch(`${IMAGES_API_BASE_URL}/url`, { const res = await fetch(`${IMAGES_API_BASE_URL}/url`, {
...@@ -168,10 +168,10 @@ export const getAUTOMATIC1111Url = async (token: string = '') => { ...@@ -168,10 +168,10 @@ export const getAUTOMATIC1111Url = async (token: string = '') => {
throw error; throw error;
} }
return res.AUTOMATIC1111_BASE_URL; return res;
}; };
export const updateAUTOMATIC1111Url = async (token: string = '', url: string) => { export const updateImageGenerationEngineUrls = async (token: string = '', urls: object = {}) => {
let error = null; let error = null;
const res = await fetch(`${IMAGES_API_BASE_URL}/url/update`, { const res = await fetch(`${IMAGES_API_BASE_URL}/url/update`, {
...@@ -182,7 +182,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) => ...@@ -182,7 +182,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
...(token && { authorization: `Bearer ${token}` }) ...(token && { authorization: `Bearer ${token}` })
}, },
body: JSON.stringify({ body: JSON.stringify({
url: url ...urls
}) })
}) })
.then(async (res) => { .then(async (res) => {
...@@ -203,7 +203,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) => ...@@ -203,7 +203,7 @@ export const updateAUTOMATIC1111Url = async (token: string = '', url: string) =>
throw error; throw error;
} }
return res.AUTOMATIC1111_BASE_URL; return res;
}; };
export const getImageSize = async (token: string = '') => { export const getImageSize = async (token: string = '') => {
......
...@@ -139,3 +139,60 @@ export const updateModelFilterConfig = async ( ...@@ -139,3 +139,60 @@ export const updateModelFilterConfig = async (
return res; return res;
}; };
export const getWebhookUrl = async (token: string) => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
}
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err;
return null;
});
if (error) {
throw error;
}
return res.url;
};
export const updateWebhookUrl = async (token: string, url: string) => {
let error = null;
const res = await fetch(`${WEBUI_BASE_URL}/api/webhook`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
url: url
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
error = err;
return null;
});
if (error) {
throw error;
}
return res.url;
};
...@@ -33,7 +33,7 @@ export const getLiteLLMModels = async (token: string = '') => { ...@@ -33,7 +33,7 @@ export const getLiteLLMModels = async (token: string = '') => {
id: model.id, id: model.id,
name: model.name ?? model.id, name: model.name ?? model.id,
external: true, external: true,
source: 'litellm' source: 'LiteLLM'
})) }))
.sort((a, b) => { .sort((a, b) => {
return a.name.localeCompare(b.name); return a.name.localeCompare(b.name);
......
...@@ -271,7 +271,7 @@ export const generateChatCompletion = async (token: string = '', body: object) = ...@@ -271,7 +271,7 @@ export const generateChatCompletion = async (token: string = '', body: object) =
return [res, controller]; return [res, controller];
}; };
export const cancelChatCompletion = async (token: string = '', requestId: string) => { export const cancelOllamaRequest = async (token: string = '', requestId: string) => {
let error = null; let error = null;
const res = await fetch(`${OLLAMA_API_BASE_URL}/cancel/${requestId}`, { const res = await fetch(`${OLLAMA_API_BASE_URL}/cancel/${requestId}`, {
...@@ -390,6 +390,73 @@ export const pullModel = async (token: string, tagName: string, urlIdx: string | ...@@ -390,6 +390,73 @@ export const pullModel = async (token: string, tagName: string, urlIdx: string |
return res; return res;
}; };
export const downloadModel = async (
token: string,
download_url: string,
urlIdx: string | null = null
) => {
let error = null;
const res = await fetch(
`${OLLAMA_API_BASE_URL}/models/download${urlIdx !== null ? `/${urlIdx}` : ''}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
url: download_url
})
}
).catch((err) => {
console.log(err);
error = err;
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
return res;
};
export const uploadModel = async (token: string, file: File, urlIdx: string | null = null) => {
let error = null;
const formData = new FormData();
formData.append('file', file);
const res = await fetch(
`${OLLAMA_API_BASE_URL}/models/upload${urlIdx !== null ? `/${urlIdx}` : ''}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`
},
body: formData
}
).catch((err) => {
console.log(err);
error = err;
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
return res;
};
// export const pullModel = async (token: string, tagName: string) => { // export const pullModel = async (token: string, tagName: string) => {
// return await fetch(`${OLLAMA_API_BASE_URL}/pull`, { // return await fetch(`${OLLAMA_API_BASE_URL}/pull`, {
// method: 'POST', // method: 'POST',
......
...@@ -263,3 +263,53 @@ export const synthesizeOpenAISpeech = async ( ...@@ -263,3 +263,53 @@ export const synthesizeOpenAISpeech = async (
return res; return res;
}; };
export const generateTitle = async (
token: string = '',
template: string,
model: string,
prompt: string,
url: string = OPENAI_API_BASE_URL
) => {
let error = null;
template = template.replace(/{{prompt}}/g, prompt);
console.log(template);
const res = await fetch(`${url}/chat/completions`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
model: model,
messages: [
{
role: 'user',
content: template
}
],
stream: false
})
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
console.log(err);
if ('detail' in err) {
error = err.detail;
}
return null;
});
if (error) {
throw error;
}
return res?.choices[0]?.message?.content ?? 'New Chat';
};
<script>
import { getContext } from 'svelte';
const i18n = getContext('i18n');
</script>
<div class=" text-center text-6xl mb-3">📄</div> <div class=" text-center text-6xl mb-3">📄</div>
<div class="text-center dark:text-white text-2xl font-semibold z-50">Add Files</div> <div class="text-center dark:text-white text-2xl font-semibold z-50">{$i18n.t('Add Files')}</div>
<slot <slot
><div class=" mt-2 text-center text-sm dark:text-gray-200 w-full"> ><div class=" mt-2 text-center text-sm dark:text-gray-200 w-full">
Drop any files here to add to the conversation {$i18n.t('Drop any files here to add to the conversation')}
</div> </div>
</slot> </slot>
<script lang="ts"> <script lang="ts">
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { Confetti } from 'svelte-confetti'; import { Confetti } from 'svelte-confetti';
import { WEBUI_NAME, config } from '$lib/stores'; import { WEBUI_NAME, config } from '$lib/stores';
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
import Modal from './common/Modal.svelte'; import Modal from './common/Modal.svelte';
const i18n = getContext('i18n');
export let show = false; export let show = false;
let changelog = null; let changelog = null;
...@@ -23,7 +25,8 @@ ...@@ -23,7 +25,8 @@
<div class="px-5 py-4 dark:text-gray-300"> <div class="px-5 py-4 dark:text-gray-300">
<div class="flex justify-between items-start"> <div class="flex justify-between items-start">
<div class="text-xl font-bold"> <div class="text-xl font-bold">
What’s New in {$WEBUI_NAME} {$i18n.t('What’s New in')}
{$WEBUI_NAME}
<Confetti x={[-1, -0.25]} y={[0, 0.5]} /> <Confetti x={[-1, -0.25]} y={[0, 0.5]} />
</div> </div>
<button <button
...@@ -45,7 +48,7 @@ ...@@ -45,7 +48,7 @@
</button> </button>
</div> </div>
<div class="flex items-center mt-1"> <div class="flex items-center mt-1">
<div class="text-sm dark:text-gray-200">Release Notes</div> <div class="text-sm dark:text-gray-200">{$i18n.t('Release Notes')}</div>
<div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" /> <div class="flex self-center w-[1px] h-6 mx-2.5 bg-gray-200 dark:bg-gray-700" />
<div class="text-sm dark:text-gray-200"> <div class="text-sm dark:text-gray-200">
v{WEBUI_VERSION} v{WEBUI_VERSION}
...@@ -108,7 +111,7 @@ ...@@ -108,7 +111,7 @@
}} }}
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
> >
<span class="relative">Okay, Let's Go!</span> <span class="relative">{$i18n.t("Okay, Let's Go!")}</span>
</button> </button>
</div> </div>
</div> </div>
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
import { updateUserById } from '$lib/apis/users'; import { updateUserById } from '$lib/apis/users';
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let show = false; export let show = false;
...@@ -42,7 +43,7 @@ ...@@ -42,7 +43,7 @@
<Modal size="sm" bind:show> <Modal size="sm" bind:show>
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 py-4"> <div class=" flex justify-between dark:text-gray-300 px-5 py-4">
<div class=" text-lg font-medium self-center">Edit User</div> <div class=" text-lg font-medium self-center">{$i18n.t('Edit User')}</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -84,7 +85,8 @@ ...@@ -84,7 +85,8 @@
<div class=" self-center capitalize font-semibold">{selectedUser.name}</div> <div class=" self-center capitalize font-semibold">{selectedUser.name}</div>
<div class="text-xs text-gray-500"> <div class="text-xs text-gray-500">
Created at {dayjs(selectedUser.timestamp * 1000).format('MMMM DD, YYYY')} {$i18n.t('Created at')}
{dayjs(selectedUser.timestamp * 1000).format($i18n.t('MMMM DD, YYYY'))}
</div> </div>
</div> </div>
</div> </div>
...@@ -93,7 +95,7 @@ ...@@ -93,7 +95,7 @@
<div class=" flex flex-col space-y-1.5"> <div class=" flex flex-col space-y-1.5">
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">Email</div> <div class=" mb-1 text-xs text-gray-500">{$i18n.t('Email')}</div>
<div class="flex-1"> <div class="flex-1">
<input <input
...@@ -108,7 +110,7 @@ ...@@ -108,7 +110,7 @@
</div> </div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">Name</div> <div class=" mb-1 text-xs text-gray-500">{$i18n.t('Name')}</div>
<div class="flex-1"> <div class="flex-1">
<input <input
...@@ -122,7 +124,7 @@ ...@@ -122,7 +124,7 @@
</div> </div>
<div class="flex flex-col w-full"> <div class="flex flex-col w-full">
<div class=" mb-1 text-xs text-gray-500">New Password</div> <div class=" mb-1 text-xs text-gray-500">{$i18n.t('New Password')}</div>
<div class="flex-1"> <div class="flex-1">
<input <input
...@@ -140,7 +142,7 @@ ...@@ -140,7 +142,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
......
<script lang="ts"> <script lang="ts">
import { downloadDatabase } from '$lib/apis/utils'; import { downloadDatabase } from '$lib/apis/utils';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
export let saveHandler: Function; export let saveHandler: Function;
...@@ -17,10 +19,10 @@ ...@@ -17,10 +19,10 @@
> >
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div> <div>
<div class=" mb-2 text-sm font-medium">Database</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('Database')}</div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<!-- <div class=" self-center text-xs font-medium">Allow Chat Deletion</div> --> <!-- <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div> -->
<button <button
class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition" class=" flex rounded-md py-1.5 px-3 w-full hover:bg-gray-200 dark:hover:bg-gray-800 transition"
...@@ -46,7 +48,7 @@ ...@@ -46,7 +48,7 @@
/> />
</svg> </svg>
</div> </div>
<div class=" self-center text-sm font-medium">Download Database</div> <div class=" self-center text-sm font-medium">{$i18n.t('Download Database')}</div>
</button> </button>
</div> </div>
</div> </div>
......
<script lang="ts"> <script lang="ts">
import { getWebhookUrl, updateWebhookUrl } from '$lib/apis';
import { import {
getDefaultUserRole, getDefaultUserRole,
getJWTExpiresDuration, getJWTExpiresDuration,
...@@ -7,13 +8,17 @@ ...@@ -7,13 +8,17 @@
updateDefaultUserRole, updateDefaultUserRole,
updateJWTExpiresDuration updateJWTExpiresDuration
} from '$lib/apis/auths'; } from '$lib/apis/auths';
import { onMount } from 'svelte'; import { onMount, getContext } from 'svelte';
const i18n = getContext('i18n');
export let saveHandler: Function; export let saveHandler: Function;
let signUpEnabled = true; let signUpEnabled = true;
let defaultUserRole = 'pending'; let defaultUserRole = 'pending';
let JWTExpiresIn = ''; let JWTExpiresIn = '';
let webhookUrl = '';
const toggleSignUpEnabled = async () => { const toggleSignUpEnabled = async () => {
signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token); signUpEnabled = await toggleSignUpEnabledStatus(localStorage.token);
}; };
...@@ -26,27 +31,32 @@ ...@@ -26,27 +31,32 @@
JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration); JWTExpiresIn = await updateJWTExpiresDuration(localStorage.token, duration);
}; };
const updateWebhookUrlHandler = async () => {
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
};
onMount(async () => { onMount(async () => {
signUpEnabled = await getSignUpEnabledStatus(localStorage.token); signUpEnabled = await getSignUpEnabledStatus(localStorage.token);
defaultUserRole = await getDefaultUserRole(localStorage.token); defaultUserRole = await getDefaultUserRole(localStorage.token);
JWTExpiresIn = await getJWTExpiresDuration(localStorage.token); JWTExpiresIn = await getJWTExpiresDuration(localStorage.token);
webhookUrl = await getWebhookUrl(localStorage.token);
}); });
</script> </script>
<form <form
class="flex flex-col h-full justify-between space-y-3 text-sm" class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={() => { on:submit|preventDefault={() => {
// console.log('submit');
updateJWTExpiresDurationHandler(JWTExpiresIn); updateJWTExpiresDurationHandler(JWTExpiresIn);
updateWebhookUrlHandler();
saveHandler(); saveHandler();
}} }}
> >
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div> <div>
<div class=" mb-2 text-sm font-medium">General Settings</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('General Settings')}</div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">Enable New Sign Ups</div> <div class=" self-center text-xs font-medium">{$i18n.t('Enable New Sign Ups')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -66,7 +76,7 @@ ...@@ -66,7 +76,7 @@
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z" d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
/> />
</svg> </svg>
<span class="ml-2 self-center">Enabled</span> <span class="ml-2 self-center">{$i18n.t('Enabled')}</span>
{:else} {:else}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
...@@ -81,25 +91,25 @@ ...@@ -81,25 +91,25 @@
/> />
</svg> </svg>
<span class="ml-2 self-center">Disabled</span> <span class="ml-2 self-center">{$i18n.t('Disabled')}</span>
{/if} {/if}
</button> </button>
</div> </div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">Default User Role</div> <div class=" self-center text-xs font-medium">{$i18n.t('Default User Role')}</div>
<div class="flex items-center relative"> <div class="flex items-center relative">
<select <select
class="w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right" class="dark:bg-gray-900 w-fit pr-8 rounded py-2 px-2 text-xs bg-transparent outline-none text-right"
bind:value={defaultUserRole} bind:value={defaultUserRole}
placeholder="Select a theme" placeholder="Select a theme"
on:change={(e) => { on:change={(e) => {
updateDefaultUserRoleHandler(e.target.value); updateDefaultUserRoleHandler(e.target.value);
}} }}
> >
<option value="pending">Pending</option> <option value="pending">{$i18n.t('pending')}</option>
<option value="user">User</option> <option value="user">{$i18n.t('user')}</option>
<option value="admin">Admin</option> <option value="admin">{$i18n.t('admin')}</option>
</select> </select>
</div> </div>
</div> </div>
...@@ -108,7 +118,24 @@ ...@@ -108,7 +118,24 @@
<div class=" w-full justify-between"> <div class=" w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">JWT Expiration</div> <div class=" self-center text-xs font-medium">{$i18n.t('Webhook URL')}</div>
</div>
<div class="flex mt-2 space-x-2">
<input
class="w-full rounded py-1.5 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none border border-gray-100 dark:border-gray-600"
type="text"
placeholder={`https://example.com/webhook`}
bind:value={webhookUrl}
/>
</div>
</div>
<hr class=" dark:border-gray-700 my-3" />
<div class=" w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
</div> </div>
<div class="flex mt-2 space-x-2"> <div class="flex mt-2 space-x-2">
...@@ -121,8 +148,9 @@ ...@@ -121,8 +148,9 @@
</div> </div>
<div class="mt-2 text-xs text-gray-400 dark:text-gray-500"> <div class="mt-2 text-xs text-gray-400 dark:text-gray-500">
Valid time units: <span class=" text-gray-300 font-medium" {$i18n.t('Valid time units:')}
>'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.</span <span class=" text-gray-300 font-medium"
>{$i18n.t("'s', 'm', 'h', 'd', 'w' or '-1' for no expiration.")}</span
> >
</div> </div>
</div> </div>
...@@ -134,7 +162,7 @@ ...@@ -134,7 +162,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
import { getModelFilterConfig, updateModelFilterConfig } from '$lib/apis'; import { getModelFilterConfig, updateModelFilterConfig } from '$lib/apis';
import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths'; import { getSignUpEnabledStatus, toggleSignUpEnabledStatus } from '$lib/apis/auths';
import { getUserPermissions, updateUserPermissions } from '$lib/apis/users'; import { getUserPermissions, updateUserPermissions } from '$lib/apis/users';
import { onMount, getContext } from 'svelte';
import { models } from '$lib/stores'; import { models } from '$lib/stores';
import { onMount } from 'svelte';
const i18n = getContext('i18n');
export let saveHandler: Function; export let saveHandler: Function;
...@@ -39,10 +42,10 @@ ...@@ -39,10 +42,10 @@
> >
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80"> <div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div> <div>
<div class=" mb-2 text-sm font-medium">User Permissions</div> <div class=" mb-2 text-sm font-medium">{$i18n.t('User Permissions')}</div>
<div class=" flex w-full justify-between"> <div class=" flex w-full justify-between">
<div class=" self-center text-xs font-medium">Allow Chat Deletion</div> <div class=" self-center text-xs font-medium">{$i18n.t('Allow Chat Deletion')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
...@@ -62,7 +65,7 @@ ...@@ -62,7 +65,7 @@
d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z" d="M11.5 1A3.5 3.5 0 0 0 8 4.5V7H2.5A1.5 1.5 0 0 0 1 8.5v5A1.5 1.5 0 0 0 2.5 15h7a1.5 1.5 0 0 0 1.5-1.5v-5A1.5 1.5 0 0 0 9.5 7V4.5a2 2 0 1 1 4 0v1.75a.75.75 0 0 0 1.5 0V4.5A3.5 3.5 0 0 0 11.5 1Z"
/> />
</svg> </svg>
<span class="ml-2 self-center">Allow</span> <span class="ml-2 self-center">{$i18n.t('Allow')}</span>
{:else} {:else}
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
...@@ -77,7 +80,7 @@ ...@@ -77,7 +80,7 @@
/> />
</svg> </svg>
<span class="ml-2 self-center">Don't Allow</span> <span class="ml-2 self-center">{$i18n.t("Don't Allow")}</span>
{/if} {/if}
</button> </button>
</div> </div>
...@@ -89,21 +92,21 @@ ...@@ -89,21 +92,21 @@
<div> <div>
<div class="mb-2"> <div class="mb-2">
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Manage Models</div> <div class=" text-sm font-medium">{$i18n.t('Manage Models')}</div>
</div> </div>
</div> </div>
<div class=" space-y-3"> <div class=" space-y-3">
<div> <div>
<div class="flex justify-between items-center text-xs"> <div class="flex justify-between items-center text-xs">
<div class=" text-xs font-medium">Model Whitelisting</div> <div class=" text-xs font-medium">{$i18n.t('Model Whitelisting')}</div>
<button <button
class=" text-xs font-medium text-gray-500" class=" text-xs font-medium text-gray-500"
type="button" type="button"
on:click={() => { on:click={() => {
whitelistEnabled = !whitelistEnabled; whitelistEnabled = !whitelistEnabled;
}}>{whitelistEnabled ? 'On' : 'Off'}</button }}>{whitelistEnabled ? $i18n.t('On') : $i18n.t('Off')}</button
> >
</div> </div>
</div> </div>
...@@ -119,7 +122,7 @@ ...@@ -119,7 +122,7 @@
bind:value={modelId} bind:value={modelId}
placeholder="Select a model" placeholder="Select a model"
> >
<option value="" disabled selected>Select a model</option> <option value="" disabled selected>{$i18n.t('Select a model')}</option>
{#each $models.filter((model) => model.id) as model} {#each $models.filter((model) => model.id) as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700" <option value={model.id} class="bg-gray-100 dark:bg-gray-700"
>{model.name}</option >{model.name}</option
...@@ -174,7 +177,8 @@ ...@@ -174,7 +177,8 @@
<div class="flex justify-end items-center text-xs mt-1.5 text-right"> <div class="flex justify-end items-center text-xs mt-1.5 text-right">
<div class=" text-xs font-medium"> <div class=" text-xs font-medium">
{whitelistModels.length} Model(s) Whitelisted {whitelistModels.length}
{$i18n.t('Model(s) Whitelisted')}
</div> </div>
</div> </div>
</div> </div>
...@@ -189,7 +193,7 @@ ...@@ -189,7 +193,7 @@
class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded" class=" px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-gray-100 transition rounded"
type="submit" type="submit"
> >
Save {$i18n.t('Save')}
</button> </button>
</div> </div>
</form> </form>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment