feat: add local cache for folder path used + invalidate cache on folder creation

This commit is contained in:
Ruben Fiszel
2023-04-01 19:17:29 +02:00
parent 43c3bfb9ba
commit 2ab13cb1ec
6 changed files with 108 additions and 143 deletions
+6 -2
View File
@@ -6,9 +6,11 @@
* LICENSE-AGPL for a copy of the license.
*/
use std::sync::Arc;
use crate::{
db::{UserDB, DB},
users::Authed,
users::{AuthCache, Authed, Tokened},
webhook_util::{WebhookMessage, WebhookShared},
};
use axum::{
@@ -144,8 +146,10 @@ lazy_static! {
async fn create_folder(
authed: Authed,
Tokened { token }: Tokened,
Extension(user_db): Extension<UserDB>,
Extension(webhook): Extension<WebhookShared>,
Extension(cache): Extension<Arc<AuthCache>>,
Path(w_id): Path<String>,
Json(ng): Json<NewFolder>,
) -> Result<String> {
@@ -157,7 +161,7 @@ async fn create_folder(
)));
}
check_name_conflict(&mut tx, &w_id, &ng.name).await?;
cache.invalidate(&w_id, token).await;
let owner = username_to_permissioned_as(&authed.username);
let owners = &ng.owners.unwrap_or(vec![owner.clone()]);
+2 -1
View File
@@ -12,7 +12,7 @@ use windmill_audit::{audit_log, ActionKind};
use crate::{
db::{UserDB, DB},
schedule::clear_schedule,
users::{require_owner_of_path, Authed},
users::{maybe_refresh_folders, require_owner_of_path, Authed},
webhook_util::{WebhookMessage, WebhookShared},
HTTP_CLIENT,
};
@@ -190,6 +190,7 @@ async fn create_script(
Json(ns): Json<NewScript>,
) -> Result<(StatusCode, String)> {
let hash = ScriptHash(hash_script(&ns));
// let authed = maybe_refresh_folders(&ns.path, &w_id, authed, &db).await;
let mut tx = user_db.begin(&authed).await?;
if sqlx::query_scalar!(
+64 -59
View File
@@ -53,73 +53,78 @@
async function saveFlow(leave: boolean): Promise<void> {
loadingSave = true
const flow = cleanInputs($flowStore)
const { cron, args, enabled } = $scheduleStore
$dirtyStore = false
if (initialPath === '') {
localStorage.removeItem('flow')
await FlowService.createFlow({
workspace: $workspaceStore!,
requestBody: {
path: flow.path,
summary: flow.summary,
description: flow.description ?? '',
value: flow.value,
schema: flow.schema
try {
const flow = cleanInputs($flowStore)
const { cron, args, enabled } = $scheduleStore
$dirtyStore = false
if (initialPath === '') {
localStorage.removeItem('flow')
await FlowService.createFlow({
workspace: $workspaceStore!,
requestBody: {
path: flow.path,
summary: flow.summary,
description: flow.description ?? '',
value: flow.value,
schema: flow.schema
}
})
if (enabled) {
await createSchedule(flow.path)
}
})
if (enabled) {
await createSchedule(flow.path)
}
} else {
localStorage.removeItem(`flow-${initialPath}`)
await FlowService.updateFlow({
workspace: $workspaceStore!,
path: initialPath,
requestBody: {
path: flow.path,
summary: flow.summary,
description: flow.description ?? '',
value: flow.value,
schema: flow.schema
}
})
const scheduleExists = await ScheduleService.existsSchedule({
workspace: $workspaceStore ?? '',
path: flow.path
})
if (scheduleExists) {
const schedule = await ScheduleService.getSchedule({
} else {
localStorage.removeItem(`flow-${initialPath}`)
await FlowService.updateFlow({
workspace: $workspaceStore!,
path: initialPath,
requestBody: {
path: flow.path,
summary: flow.summary,
description: flow.description ?? '',
value: flow.value,
schema: flow.schema
}
})
const scheduleExists = await ScheduleService.existsSchedule({
workspace: $workspaceStore ?? '',
path: flow.path
})
if (JSON.stringify(schedule.args) != JSON.stringify(args) || schedule.schedule != cron) {
await ScheduleService.updateSchedule({
if (scheduleExists) {
const schedule = await ScheduleService.getSchedule({
workspace: $workspaceStore ?? '',
path: flow.path,
requestBody: {
schedule: formatCron(cron),
args
}
path: flow.path
})
if (JSON.stringify(schedule.args) != JSON.stringify(args) || schedule.schedule != cron) {
await ScheduleService.updateSchedule({
workspace: $workspaceStore ?? '',
path: flow.path,
requestBody: {
schedule: formatCron(cron),
args
}
})
}
if (enabled != schedule.enabled) {
await ScheduleService.setScheduleEnabled({
workspace: $workspaceStore ?? '',
path: flow.path,
requestBody: { enabled }
})
}
} else if (enabled) {
await createSchedule(flow.path)
}
if (enabled != schedule.enabled) {
await ScheduleService.setScheduleEnabled({
workspace: $workspaceStore ?? '',
path: flow.path,
requestBody: { enabled }
})
}
} else if (enabled) {
await createSchedule(flow.path)
}
}
loadingSave = false
if (leave) {
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
} else if (initialPath !== $flowStore.path) {
initialPath = $flowStore.path
goto(`/flows/edit/${$flowStore.path}?workspace_id=${$workspaceStore}`)
loadingSave = false
if (leave) {
goto(`/flows/get/${$flowStore.path}?workspace_id=${$workspaceStore}`)
} else if (initialPath !== $flowStore.path) {
initialPath = $flowStore.path
goto(`/flows/edit/${$flowStore.path}?workspace_id=${$workspaceStore}`)
}
} catch (err) {
sendUserToast(`The flow could not be saved: ${err.body}`, true)
loadingSave = false
}
}
+30 -78
View File
@@ -1,3 +1,7 @@
<script lang="ts" context="module">
const lastMetaUsed = writable<Meta | undefined>(undefined)
</script>
<script lang="ts">
import { type Meta, pathToMeta } from '$lib/common'
@@ -24,6 +28,8 @@
import GroupEditor from './GroupEditor.svelte'
import { random_adj } from './random_positive_adjetive'
import Badge from './common/badge/Badge.svelte'
import { writable } from 'svelte/store'
import { canWrite } from '$lib/utils'
type PathKind = 'resource' | 'script' | 'variable' | 'flow' | 'schedule' | 'app'
let meta: Meta | undefined = undefined
@@ -40,7 +46,7 @@
const dispatch = createEventDispatcher()
let folders: string[] = []
let folders: { name: string; write: boolean }[] = []
let groups: string[] = []
$: meta && onMetaChange()
@@ -49,6 +55,10 @@
if (meta) {
path = metaToPath(meta)
validate(meta, path, kind)
$lastMetaUsed = {
...meta,
name: ''
}
}
}
@@ -71,14 +81,17 @@
export async function reset() {
if (path == '' || path == 'u//') {
meta = {
ownerKind: 'user',
name: random_adj() + '_' + namePlaceholder,
owner: ''
if ($lastMetaUsed == undefined) {
meta = {
ownerKind: 'user',
name: random_adj() + '_' + namePlaceholder,
owner: ''
}
meta.owner = $userStore!.username.split('@')[0].replace(/[^a-zA-Z0-9]/g, '')
} else {
meta = { ...$lastMetaUsed, name: random_adj() + '_' + namePlaceholder }
}
meta.owner = $userStore!.username.split('@')[0]
let newMeta = { ...meta }
while (await pathExists(metaToPath(newMeta), kind)) {
disabled = true
@@ -95,18 +108,20 @@
}
async function loadFolders(): Promise<void> {
let initialFolders: string[] = []
let initialFolders: { name: string; write: boolean }[] = []
let initialFolder = ''
if (initialPath?.split('/')?.[0] == 'f') {
initialFolder = initialPath?.split('/')?.[1]
initialFolders.push(initialFolder)
initialFolders.push({ name: initialFolder, write: true })
}
folders = initialFolders.concat(
(
await FolderService.listFolderNames({
workspace: $workspaceStore!
})
).filter((x) => x != initialFolder)
)
.filter((x) => x != initialFolder)
.map((x) => ({ name: x, write: $userStore?.folders?.includes(x) == true }))
)
}
@@ -224,56 +239,14 @@
requestBody: { name: newFolderName }
})
folderCreated = newFolderName
$userStore?.folders?.push(newFolderName)
loadFolders()
if (meta) {
meta.owner = newFolderName
}
loadFolders()
}
let newGroup: Drawer
let viewGroup: Drawer
let newGroupName: string
let groupCreated: string | undefined = undefined
async function addGroup() {
await GroupService.createGroup({
workspace: $workspaceStore ?? '',
requestBody: { name: newGroupName }
})
groupCreated = newGroupName
if (meta) {
meta.owner = newGroupName
}
loadGroups()
}
</script>
<Drawer bind:this={newGroup}>
<DrawerContent
title="New Folder"
on:close={() => {
newGroup.closeDrawer()
groupCreated = undefined
}}
>
<div class="flex flex-row">
<input class="mr-2" placeholder="New group name" bind:value={newGroupName} />
<Button size="md" endIcon={{ icon: faPlus }} disabled={!newGroupName} on:click={addGroup}>
New&nbsp;group
</Button>
</div>
{#if groupCreated}
<div class="mt-8" />
<GroupEditor name={groupCreated} />
{/if}
</DrawerContent>
</Drawer>
<Drawer bind:this={viewGroup}>
<DrawerContent title="Folder {meta?.owner}" on:close={viewGroup.closeDrawer}>
<GroupEditor name={meta?.owner ?? ''} />
</DrawerContent>
</Drawer>
<Drawer bind:this={newFolder}>
<DrawerContent
title="New Folder"
@@ -356,8 +329,8 @@
<div class="flex flex-row gap-1 w-full">
<select class="grow w-full" {disabled} bind:value={meta.owner}>
{#each folders as f}
<option>{f}</option>
{#each folders as { name, write }}
<option disabled={!write}>{name}{write ? '' : ' (read-only)'}</option>
{/each}
</select>
<Button variant="border" size="xs" on:click={viewFolder.openDrawer}>
@@ -368,27 +341,6 @@
></div
>
</label>
{:else if meta.ownerKind === 'group'}
<label class="block grow w-48">
<span class="text-gray-700 text-sm"
>Group <Tooltip>Item will be owned by the group and hence all its member</Tooltip
></span
>
<div class="flex flex-row gap-1">
<select class="grow w-full" {disabled} bind:value={meta.owner}>
{#each groups as g}
<option>{g}</option>
{/each}
</select>
<Button variant="border" size="xs" on:click={viewGroup.openDrawer}>
<Icon scale={0.8} data={faEye} /></Button
>
<Button variant="border" size="xs" on:click={newGroup.openDrawer}>
<Icon scale={0.8} data={faPlus} /></Button
></div
>
</label>
{/if}
</div>
<label class="block grow w-full max-w-md">
@@ -39,6 +39,7 @@
workspace: $workspaceStore ?? '',
requestBody: { name: newFolderName }
})
$userStore?.folders.push(newFolderName)
loadFolders()
editFolderName = newFolderName
folderDrawer.openDrawer()
@@ -131,10 +131,12 @@
</script>
<!-- Enable submit form on enter -->
<CenteredModal title="Login">
<CenteredModal title={isCloudHosted() ? 'Login/Signup' : 'Login'}>
{#if isCloudHosted()}
<div class="text-center -mt-4">
<span class=" text-gray-600 text-sm">Login or sign up with any of the methods below</span>
<span class=" text-gray-600 text-sm"
>Login or sign up (no cc required) with any of the methods below</span
>
</div>
{/if}
<div class="justify-center text-center flex flex-col">
@@ -194,7 +196,7 @@
<span class="text-gray-700 text-sm">Email</span>
<input type="email" bind:value={email} id="email" />
</label>
<label class="block ">
<label class="block">
<span class="text-gray-700 text-sm">Password</span>
<input type="password" on:keyup={handleKeyUp} bind:value={password} id="password" />
</label>