Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
ae347bb5
Unverified
Commit
ae347bb5
authored
Mar 20, 2024
by
Timothy Jaeryang Baek
Committed by
GitHub
Mar 20, 2024
Browse files
Merge pull request #1239 from open-webui/webhook
feat: webhook
parents
16fe0ee1
fa65be2a
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
153 additions
and
3 deletions
+153
-3
backend/apps/web/main.py
backend/apps/web/main.py
+2
-0
backend/apps/web/routers/auths.py
backend/apps/web/routers/auths.py
+13
-1
backend/config.py
backend/config.py
+1
-0
backend/constants.py
backend/constants.py
+7
-0
backend/main.py
backend/main.py
+27
-1
backend/utils/webhook.py
backend/utils/webhook.py
+20
-0
src/lib/apis/index.ts
src/lib/apis/index.ts
+57
-0
src/lib/components/admin/Settings/General.svelte
src/lib/components/admin/Settings/General.svelte
+26
-1
No files found.
backend/apps/web/main.py
View file @
ae347bb5
...
@@ -19,6 +19,7 @@ from config import (
...
@@ -19,6 +19,7 @@ from config import (
DEFAULT_USER_ROLE
,
DEFAULT_USER_ROLE
,
ENABLE_SIGNUP
,
ENABLE_SIGNUP
,
USER_PERMISSIONS
,
USER_PERMISSIONS
,
WEBHOOK_URL
,
)
)
app
=
FastAPI
()
app
=
FastAPI
()
...
@@ -32,6 +33,7 @@ app.state.DEFAULT_MODELS = DEFAULT_MODELS
...
@@ -32,6 +33,7 @@ app.state.DEFAULT_MODELS = DEFAULT_MODELS
app
.
state
.
DEFAULT_PROMPT_SUGGESTIONS
=
DEFAULT_PROMPT_SUGGESTIONS
app
.
state
.
DEFAULT_PROMPT_SUGGESTIONS
=
DEFAULT_PROMPT_SUGGESTIONS
app
.
state
.
DEFAULT_USER_ROLE
=
DEFAULT_USER_ROLE
app
.
state
.
DEFAULT_USER_ROLE
=
DEFAULT_USER_ROLE
app
.
state
.
USER_PERMISSIONS
=
USER_PERMISSIONS
app
.
state
.
USER_PERMISSIONS
=
USER_PERMISSIONS
app
.
state
.
WEBHOOK_URL
=
WEBHOOK_URL
app
.
add_middleware
(
app
.
add_middleware
(
...
...
backend/apps/web/routers/auths.py
View file @
ae347bb5
...
@@ -27,7 +27,8 @@ from utils.utils import (
...
@@ -27,7 +27,8 @@ from utils.utils import (
create_token
,
create_token
,
)
)
from
utils.misc
import
parse_duration
,
validate_email_format
from
utils.misc
import
parse_duration
,
validate_email_format
from
constants
import
ERROR_MESSAGES
from
utils.webhook
import
post_webhook
from
constants
import
ERROR_MESSAGES
,
WEBHOOK_MESSAGES
router
=
APIRouter
()
router
=
APIRouter
()
...
@@ -155,6 +156,17 @@ async def signup(request: Request, form_data: SignupForm):
...
@@ -155,6 +156,17 @@ async def signup(request: Request, form_data: SignupForm):
)
)
# response.set_cookie(key='token', value=token, httponly=True)
# response.set_cookie(key='token', value=token, httponly=True)
if
request
.
app
.
state
.
WEBHOOK_URL
:
post_webhook
(
request
.
app
.
state
.
WEBHOOK_URL
,
WEBHOOK_MESSAGES
.
USER_SIGNUP
(
user
.
name
),
{
"action"
:
"signup"
,
"message"
:
WEBHOOK_MESSAGES
.
USER_SIGNUP
(
user
.
name
),
"user"
:
user
.
model_dump_json
(
exclude_none
=
True
),
},
)
return
{
return
{
"token"
:
token
,
"token"
:
token
,
"token_type"
:
"Bearer"
,
"token_type"
:
"Bearer"
,
...
...
backend/config.py
View file @
ae347bb5
...
@@ -302,6 +302,7 @@ MODEL_FILTER_ENABLED = os.environ.get("MODEL_FILTER_ENABLED", False)
...
@@ -302,6 +302,7 @@ MODEL_FILTER_ENABLED = os.environ.get("MODEL_FILTER_ENABLED", False)
MODEL_FILTER_LIST
=
os
.
environ
.
get
(
"MODEL_FILTER_LIST"
,
""
)
MODEL_FILTER_LIST
=
os
.
environ
.
get
(
"MODEL_FILTER_LIST"
,
""
)
MODEL_FILTER_LIST
=
[
model
.
strip
()
for
model
in
MODEL_FILTER_LIST
.
split
(
";"
)]
MODEL_FILTER_LIST
=
[
model
.
strip
()
for
model
in
MODEL_FILTER_LIST
.
split
(
";"
)]
WEBHOOK_URL
=
os
.
environ
.
get
(
"WEBHOOK_URL"
,
""
)
####################################
####################################
# WEBUI_VERSION
# WEBUI_VERSION
...
...
backend/constants.py
View file @
ae347bb5
...
@@ -5,6 +5,13 @@ class MESSAGES(str, Enum):
...
@@ -5,6 +5,13 @@ class MESSAGES(str, Enum):
DEFAULT
=
lambda
msg
=
""
:
f
"
{
msg
if
msg
else
''
}
"
DEFAULT
=
lambda
msg
=
""
:
f
"
{
msg
if
msg
else
''
}
"
class
WEBHOOK_MESSAGES
(
str
,
Enum
):
DEFAULT
=
lambda
msg
=
""
:
f
"
{
msg
if
msg
else
''
}
"
USER_SIGNUP
=
lambda
username
=
""
:
(
f
"New user signed up:
{
username
}
"
if
username
else
"New user signed up"
)
class
ERROR_MESSAGES
(
str
,
Enum
):
class
ERROR_MESSAGES
(
str
,
Enum
):
def
__str__
(
self
)
->
str
:
def
__str__
(
self
)
->
str
:
return
super
().
__str__
()
return
super
().
__str__
()
...
...
backend/main.py
View file @
ae347bb5
...
@@ -38,6 +38,7 @@ from config import (
...
@@ -38,6 +38,7 @@ from config import (
FRONTEND_BUILD_DIR
,
FRONTEND_BUILD_DIR
,
MODEL_FILTER_ENABLED
,
MODEL_FILTER_ENABLED
,
MODEL_FILTER_LIST
,
MODEL_FILTER_LIST
,
WEBHOOK_URL
,
)
)
from
constants
import
ERROR_MESSAGES
from
constants
import
ERROR_MESSAGES
...
@@ -58,6 +59,9 @@ app = FastAPI(docs_url="/docs" if ENV == "dev" else None, redoc_url=None)
...
@@ -58,6 +59,9 @@ app = FastAPI(docs_url="/docs" if ENV == "dev" else None, redoc_url=None)
app
.
state
.
MODEL_FILTER_ENABLED
=
MODEL_FILTER_ENABLED
app
.
state
.
MODEL_FILTER_ENABLED
=
MODEL_FILTER_ENABLED
app
.
state
.
MODEL_FILTER_LIST
=
MODEL_FILTER_LIST
app
.
state
.
MODEL_FILTER_LIST
=
MODEL_FILTER_LIST
app
.
state
.
WEBHOOK_URL
=
WEBHOOK_URL
origins
=
[
"*"
]
origins
=
[
"*"
]
...
@@ -178,7 +182,7 @@ class ModelFilterConfigForm(BaseModel):
...
@@ -178,7 +182,7 @@ class ModelFilterConfigForm(BaseModel):
@
app
.
post
(
"/api/config/model/filter"
)
@
app
.
post
(
"/api/config/model/filter"
)
async
def
get
_model_filter_config
(
async
def
update
_model_filter_config
(
form_data
:
ModelFilterConfigForm
,
user
=
Depends
(
get_admin_user
)
form_data
:
ModelFilterConfigForm
,
user
=
Depends
(
get_admin_user
)
):
):
...
@@ -197,6 +201,28 @@ async def get_model_filter_config(
...
@@ -197,6 +201,28 @@ async def get_model_filter_config(
}
}
@
app
.
get
(
"/api/webhook"
)
async
def
get_webhook_url
(
user
=
Depends
(
get_admin_user
)):
return
{
"url"
:
app
.
state
.
WEBHOOK_URL
,
}
class
UrlForm
(
BaseModel
):
url
:
str
@
app
.
post
(
"/api/webhook"
)
async
def
update_webhook_url
(
form_data
:
UrlForm
,
user
=
Depends
(
get_admin_user
)):
app
.
state
.
WEBHOOK_URL
=
form_data
.
url
webui_app
.
state
.
WEBHOOK_URL
=
app
.
state
.
WEBHOOK_URL
return
{
"url"
:
app
.
state
.
WEBHOOK_URL
,
}
@
app
.
get
(
"/api/version"
)
@
app
.
get
(
"/api/version"
)
async
def
get_app_config
():
async
def
get_app_config
():
...
...
backend/utils/webhook.py
0 → 100644
View file @
ae347bb5
import
requests
def
post_webhook
(
url
:
str
,
message
:
str
,
event_data
:
dict
)
->
bool
:
try
:
payload
=
{}
if
"https://hooks.slack.com"
in
url
:
payload
[
"text"
]
=
message
elif
"https://discord.com/api/webhooks"
in
url
:
payload
[
"content"
]
=
message
else
:
payload
=
{
**
event_data
}
r
=
requests
.
post
(
url
,
json
=
payload
)
r
.
raise_for_status
()
return
True
except
Exception
as
e
:
print
(
e
)
return
False
src/lib/apis/index.ts
View file @
ae347bb5
...
@@ -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
;
};
src/lib/components/admin/Settings/General.svelte
View file @
ae347bb5
<script lang="ts">
<script lang="ts">
import { getWebhookUrl, updateWebhookUrl } from '$lib/apis';
import {
import {
getDefaultUserRole,
getDefaultUserRole,
getJWTExpiresDuration,
getJWTExpiresDuration,
...
@@ -16,6 +17,8 @@
...
@@ -16,6 +17,8 @@
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);
};
};
...
@@ -28,18 +31,23 @@
...
@@ -28,18 +31,23 @@
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();
}}
}}
>
>
...
@@ -108,6 +116,23 @@
...
@@ -108,6 +116,23 @@
<hr class=" dark:border-gray-700 my-3" />
<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('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=" 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">{$i18n.t('JWT Expiration')}</div>
<div class=" self-center text-xs font-medium">{$i18n.t('JWT Expiration')}</div>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment