This commit is contained in:
Timothy J. Baek 2024-10-01 21:32:59 -07:00
parent c2732a0990
commit 1b7d363d32
11 changed files with 182 additions and 28 deletions

View File

@ -2,6 +2,7 @@ import json
import logging
import time
from typing import Optional
import uuid
from open_webui.apps.webui.internal.db import Base, get_db
from open_webui.env import SRC_LOG_LEVELS
@ -65,7 +66,6 @@ class ProjectResponse(BaseModel):
class ProjectForm(BaseModel):
id: str
name: str
description: str
data: Optional[dict] = None
@ -79,6 +79,7 @@ class ProjectTable:
project = ProjectModel(
**{
**form_data.model_dump(),
"id": str(uuid.uuid4()),
"user_id": user_id,
"created_at": int(time.time()),
"updated_at": int(time.time()),

View File

@ -46,21 +46,32 @@ async def get_projects(id: Optional[str] = None, user=Depends(get_verified_user)
@router.post("/create", response_model=Optional[ProjectResponse])
async def create_new_project(form_data: ProjectForm, user=Depends(get_admin_user)):
project = Projects.get_project_by_id(form_data.id)
if project is None:
project = Projects.insert_new_project(user.id, form_data)
project = Projects.insert_new_project(user.id, form_data)
if project:
return project
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.FILE_EXISTS,
)
if project:
return project
else:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=ERROR_MESSAGES.ID_TAKEN,
detail=ERROR_MESSAGES.FILE_EXISTS,
)
############################
# GetProjectById
############################
@router.get("/{id}", response_model=Optional[ProjectResponse])
async def get_projects(id: str, user=Depends(get_verified_user)):
project = Projects.get_project_by_id(id=id)
if project:
return project
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.NOT_FOUND,
)
@ -69,8 +80,9 @@ async def create_new_project(form_data: ProjectForm, user=Depends(get_admin_user
############################
@router.post("/update", response_model=Optional[ProjectResponse])
@router.post("/{id}/update", response_model=Optional[ProjectResponse])
async def update_project_by_id(
id: str,
form_data: ProjectForm,
user=Depends(get_admin_user),
):
@ -89,7 +101,7 @@ async def update_project_by_id(
############################
@router.delete("/delete", response_model=bool)
@router.delete("/{id}/delete", response_model=bool)
async def delete_project_by_id(id: str, user=Depends(get_admin_user)):
result = Projects.delete_project_by_id(id=id)
return result

View File

@ -1,6 +1,6 @@
import { WEBUI_API_BASE_URL } from '$lib/constants';
export const createNewProject = async (token: string, id: string, name: string) => {
export const createNewProject = async (token: string, name: string, description: string) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/projects/create`, {
@ -11,8 +11,8 @@ export const createNewProject = async (token: string, id: string, name: string)
authorization: `Bearer ${token}`
},
body: JSON.stringify({
id: id,
name: name
name: name,
description: description
})
})
.then(async (res) => {

View File

@ -2,7 +2,7 @@
import { getRAGConfig, updateRAGConfig } from '$lib/apis/retrieval';
import Switch from '$lib/components/common/Switch.svelte';
import { documents, models } from '$lib/stores';
import { models } from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import SensitiveInput from '$lib/components/common/SensitiveInput.svelte';

View File

@ -114,7 +114,7 @@
files = [
...files,
{
type: e?.detail?.type ?? 'file',
type: e?.detail?.meta?.legacy ? 'file' : 'project',
...e.detail,
status: 'processed'
}

View File

@ -111,12 +111,13 @@
</div>
</div>
<hr class=" dark:border-gray-850 my-2.5" />
<hr class=" border-gray-50 dark:border-gray-850 my-2.5" />
<div class="my-3 mb-5 grid md:grid-cols-2 gap-2">
{#each filteredProjects as project}
{JSON.stringify(project)}
<button
class=" flex space-x-4 cursor-pointer text-left w-full px-4 py-3 border dark:border-gray-850 dark:hover:bg-gray-850 rounded-xl"
class=" flex space-x-4 cursor-pointer text-left w-full px-4 py-3 border border-gray-50 dark:border-gray-850 hover:bg-gray-50 dark:hover:bg-gray-850 rounded-xl"
>
<div class=" w-full">
<div class="flex items-center justify-between -mt-1">
@ -147,7 +148,7 @@
</div>
{:else}
<div
class="bg-green-500/20 text-green-700 dark:text-green-200 rounded uppercase text-xs px-1"
class="bg-green-500/20 text-green-700 dark:text-green-200 rounded uppercase text-xs font-bold px-1"
>
{$i18n.t('Project')}
</div>

View File

@ -0,0 +1,138 @@
<script>
import { goto } from '$app/navigation';
import { getContext } from 'svelte';
const i18n = getContext('i18n');
import { createNewProject, getProjects } from '$lib/apis/projects';
import { toast } from 'svelte-sonner';
import { projects } from '$lib/stores';
let loading = false;
let name = '';
let description = '';
const submitHandler = async () => {
loading = true;
const res = await createNewProject(localStorage.token, name, description).catch((e) => {
toast.error(e);
});
if (res) {
toast.success($i18n.t('Project created successfully.'));
projects.set(await getProjects(localStorage.token));
goto(`/workspace/projects/${res.id}`);
}
loading = false;
};
</script>
<div class="w-full max-h-full">
<button
class="flex space-x-1"
on:click={() => {
goto('/workspace/projects');
}}
>
<div class=" self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center font-medium text-sm">{$i18n.t('Back')}</div>
</button>
<form
class="flex flex-col max-w-lg mx-auto mt-10 mb-10"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div class=" w-full flex flex-col justify-center">
<div class=" text-2xl font-medium font-primary mb-2.5">Create a project</div>
<div class="w-full flex flex-col gap-2.5">
<div class="w-full">
<div class=" text-sm mb-2">What are you working on?</div>
<div class="w-full mt-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
bind:value={name}
placeholder={`Name your project`}
required
/>
</div>
</div>
<div>
<div class="text-sm mb-2">What are you trying to achieve?</div>
<div class=" w-full mt-1">
<textarea
class="w-full resize-none rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-none"
rows="4"
bind:value={description}
placeholder={`Describe your project, its goals, and objectives`}
required
/>
</div>
</div>
</div>
</div>
<div class="flex justify-end mt-2">
<div>
<button
class=" text-sm px-4 py-2 transition rounded-lg {loading
? ' cursor-not-allowed bg-gray-100 dark:bg-gray-800'
: ' bg-gray-50 hover:bg-gray-100 dark:bg-gray-850 dark:hover:bg-gray-800'} flex"
type="submit"
disabled={loading}
>
<div class=" self-center font-medium">{$i18n.t('Create Project')}</div>
{#if loading}
<div class="ml-1.5 self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{/if}
</button>
</div>
</div>
</form>
</div>

View File

@ -99,7 +99,7 @@
</div>
</div>
<hr class=" my-2 dark:border-gray-850" />
<hr class=" my-2 border-gray-100 dark:border-gray-850" />
<div class=" py-1 px-5 flex-1 max-h-full overflow-y-auto">
<slot />

View File

@ -0,0 +1,7 @@
<script>
import { page } from '$app/stores';
import Project from '$lib/components/workspace/Projects/Project.svelte';
</script>
<Project />

View File

@ -1,5 +0,0 @@
<script>
import EditProject from '$lib/components/workspace/Projects/EditProject.svelte';
</script>
<EditProject />