This commit is contained in:
Timothy Jaeryang Baek
2026-02-10 15:57:08 -06:00
parent 4aedfdc547
commit 30f72672fa
3 changed files with 115 additions and 25 deletions
+52
View File
@@ -300,6 +300,58 @@ async def update_note_by_id(
)
############################
# UpdateNoteAccessById
############################
class NoteAccessGrantsForm(BaseModel):
access_grants: list[dict]
@router.post("/{id}/access/update", response_model=Optional[NoteModel])
async def update_note_access_by_id(
request: Request,
id: str,
form_data: NoteAccessGrantsForm,
user=Depends(get_verified_user),
db: Session = Depends(get_session),
):
if user.role != "admin" and not has_permission(
user.id, "features.notes", request.app.state.config.USER_PERMISSIONS, db=db
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=ERROR_MESSAGES.UNAUTHORIZED,
)
note = Notes.get_note_by_id(id, db=db)
if not note:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
)
if user.role != "admin" and (
user.id != note.user_id
and not AccessGrants.has_access(
user_id=user.id,
resource_type="note",
resource_id=note.id,
permission="write",
db=db,
)
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT()
)
AccessGrants.set_access_grants(
"note", id, form_data.access_grants, db=db
)
return Notes.get_note_by_id(id, db=db)
############################
# DeleteNoteById
############################
+33
View File
@@ -253,6 +253,39 @@ export const updateNoteById = async (token: string, id: string, note: NoteItem)
return res;
};
export const updateNoteAccessGrants = async (
token: string,
id: string,
accessGrants: any[]
) => {
let error = null;
const res = await fetch(`${WEBUI_API_BASE_URL}/notes/${id}/access/update`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
authorization: `Bearer ${token}`
},
body: JSON.stringify({ access_grants: accessGrants })
})
.then(async (res) => {
if (!res.ok) throw await res.json();
return res.json();
})
.catch((err) => {
error = err.detail;
console.error(err);
return null;
});
if (error) {
throw error;
}
return res;
};
export const deleteNoteById = async (token: string, id: string) => {
let error = null;
+30 -25
View File
@@ -60,7 +60,7 @@
// Assuming $i18n.languages is an array of language codes
$: loadLocale($i18n.languages);
import { deleteNoteById, getNoteById, updateNoteById } from '$lib/apis/notes';
import { deleteNoteById, getNoteById, updateNoteById, updateNoteAccessGrants } from '$lib/apis/notes';
import RichTextInput from '../common/RichTextInput.svelte';
import Spinner from '../common/Spinner.svelte';
@@ -71,6 +71,7 @@
import Calendar from '../icons/Calendar.svelte';
import Users from '../icons/Users.svelte';
import LockClosed from '../icons/LockClosed.svelte';
import Image from '../common/Image.svelte';
import FileItem from '../common/FileItem.svelte';
@@ -865,8 +866,15 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
bind:show={showAccessControlModal}
bind:accessGrants={note.access_grants}
accessRoles={['read', 'write']}
onChange={() => {
changeDebounceHandler();
onChange={async () => {
if (id) {
try {
await updateNoteAccessGrants(localStorage.token, id, note.access_grants ?? []);
toast.success($i18n.t('Saved'));
} catch (error) {
toast.error(`${error}`);
}
}
}}
/>
{/if}
@@ -898,7 +906,7 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
{:else}
<div class=" w-full flex flex-col {loading ? 'opacity-20' : ''}">
<div class="shrink-0 w-full flex justify-between items-center px-3.5 mb-1.5">
<div class="w-full flex items-center">
<div class="w-full min-w-0 flex items-center">
{#if $mobile}
<div
class="{$showSidebar
@@ -974,7 +982,7 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
</div>
{/if}
<div class="flex items-center gap-0.5 translate-x-1">
<div class="flex items-center gap-0.5 shrink-0 translate-x-1">
{#if note?.write_access}
{#if editor}
<div>
@@ -1077,6 +1085,23 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
<EllipsisHorizontal className="size-5" />
</div>
</NoteMenu>
{#if note?.write_access}
<button
class="shrink-0 bg-gray-50 hover:bg-gray-100 text-black dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition px-2.5 py-1 rounded-full flex gap-1.5 items-center text-sm"
on:click={() => {
showAccessControlModal = true;
}}
disabled={note?.user_id !== $user?.id && $user?.role !== 'admin'}
>
<LockClosed strokeWidth="2.5" className="size-3.5" />
{$i18n.t('Access')}
</button>
{:else}
<div class="shrink-0 text-xs text-gray-500 px-2 py-1">
{$i18n.t('Read-Only Access')}
</div>
{/if}
</div>
</div>
</div>
@@ -1118,26 +1143,6 @@ Provide the enhanced notes in markdown format. Use markdown syntax for headings,
{/if}
</button>
{#if note?.write_access}
<button
class=" flex items-center gap-1 w-fit py-1 px-1.5 rounded-lg min-w-fit"
on:click={() => {
showAccessControlModal = true;
}}
disabled={note?.user_id !== $user?.id && $user?.role !== 'admin'}
>
<span>
{hasPublicReadGrant(note?.access_grants)
? $i18n.t('Everyone')
: $i18n.t('Private')}
</span>
</button>
{:else}
<div>
{$i18n.t('Read-Only Access')}
</div>
{/if}
{#if editor}
<div class="flex items-center gap-1 px-1 min-w-fit">
<div>