mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 08:02:26 +00:00
improve schedule section of run page
This commit is contained in:
+138
-134
@@ -1230,6 +1230,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun"
|
||||
]
|
||||
},
|
||||
@@ -2116,6 +2117,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun"
|
||||
]
|
||||
},
|
||||
@@ -2792,6 +2794,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun"
|
||||
]
|
||||
},
|
||||
@@ -3902,6 +3905,7 @@
|
||||
"bash",
|
||||
"postgresql",
|
||||
"nativets",
|
||||
"Nativets",
|
||||
"bun"
|
||||
]
|
||||
},
|
||||
@@ -4019,106 +4023,6 @@
|
||||
},
|
||||
"query": "SELECT EXISTS(SELECT 1 FROM usr WHERE workspace_id = $1 AND email = $2)"
|
||||
},
|
||||
"8caa01546506f42740b7973a3a45e40093a06714b8524570c5143b77af4a8e19": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "workspace_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 1,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "edited_by",
|
||||
"ordinal": 2,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "edited_at",
|
||||
"ordinal": 3,
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"name": "schedule",
|
||||
"ordinal": 4,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"ordinal": 5,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "script_path",
|
||||
"ordinal": 6,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"ordinal": 7,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "extra_perms",
|
||||
"ordinal": 8,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "is_flow",
|
||||
"ordinal": 9,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"ordinal": 10,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"ordinal": 12,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "on_failure",
|
||||
"ordinal": 13,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Int8",
|
||||
"Int8"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT * FROM schedule WHERE workspace_id = $1 ORDER BY edited_at desc LIMIT $2 OFFSET $3"
|
||||
},
|
||||
"8cc4fa257cb8053d27eb7a555d6e1b993e4a74b063eac632df8da24f6b87130e": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@@ -5544,7 +5448,39 @@
|
||||
},
|
||||
"query": "DELETE FROM resource WHERE path = $1 AND workspace_id = $2"
|
||||
},
|
||||
"bfc760c2f3f1477b177a7c570ddadda818dd3f57142165f381c1db01c26cf1bf": {
|
||||
"c0635f65d561c1a6b183b8fea45320482375d0bb5e617a9dd236a631a7b9ff05": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE workspace_settings SET error_handler = NULL WHERE workspace_id = $1"
|
||||
},
|
||||
"c07c9276945663d062cf0ff5b3323be681a0e2cb07a457ea9aede2daeff551cc": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "customer_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT customer_id FROM workspace_settings WHERE workspace_id = $1"
|
||||
},
|
||||
"c10348d26e3e3dc9e345d9044db35db9906617eb9c98aaf58a55d9681ffb85e3": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -5642,39 +5578,7 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *"
|
||||
},
|
||||
"c0635f65d561c1a6b183b8fea45320482375d0bb5e617a9dd236a631a7b9ff05": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE workspace_settings SET error_handler = NULL WHERE workspace_id = $1"
|
||||
},
|
||||
"c07c9276945663d062cf0ff5b3323be681a0e2cb07a457ea9aede2daeff551cc": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "customer_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "SELECT customer_id FROM workspace_settings WHERE workspace_id = $1"
|
||||
"query": "UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS false RETURNING *"
|
||||
},
|
||||
"c1149f74ef0cca4a48be80f523d339d78e8e20e2bfacac9ad1b6b75c5ae370fb": {
|
||||
"describe": {
|
||||
@@ -6460,6 +6364,106 @@
|
||||
},
|
||||
"query": "UPDATE app SET versions = array_append(versions, $1) WHERE path = $2 AND workspace_id = $3"
|
||||
},
|
||||
"dd74fa9468b5fe8c8ad657ded06076c11a78d0206af2af20685c691d4d9520bb": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "workspace_id",
|
||||
"ordinal": 0,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"ordinal": 1,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "edited_by",
|
||||
"ordinal": 2,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "edited_at",
|
||||
"ordinal": 3,
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"name": "schedule",
|
||||
"ordinal": 4,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "enabled",
|
||||
"ordinal": 5,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "script_path",
|
||||
"ordinal": 6,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"ordinal": 7,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "extra_perms",
|
||||
"ordinal": 8,
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"name": "is_flow",
|
||||
"ordinal": 9,
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"name": "email",
|
||||
"ordinal": 10,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "error",
|
||||
"ordinal": 11,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "timezone",
|
||||
"ordinal": 12,
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"name": "on_failure",
|
||||
"ordinal": 13,
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Varchar",
|
||||
"Text",
|
||||
"Text"
|
||||
]
|
||||
}
|
||||
},
|
||||
"query": "UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *"
|
||||
},
|
||||
"dd7940ec390357b268d616e1880516ecc08d506db2109efcce840f096d7a594e": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
|
||||
@@ -4439,6 +4439,15 @@ paths:
|
||||
- $ref: "#/components/parameters/WorkspaceId"
|
||||
- $ref: "#/components/parameters/Page"
|
||||
- $ref: "#/components/parameters/PerPage"
|
||||
- name: path
|
||||
description: filter by path
|
||||
in: query
|
||||
schema:
|
||||
type: string
|
||||
- name: is_flow
|
||||
in: query
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
description: schedule list
|
||||
|
||||
@@ -17,6 +17,7 @@ use axum::{
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sql_builder::{prelude::Bind, SqlBuilder};
|
||||
use sqlx::{Postgres, Transaction};
|
||||
use std::str::FromStr;
|
||||
use windmill_audit::{audit_log, ActionKind};
|
||||
@@ -211,6 +212,14 @@ async fn edit_schedule(
|
||||
Ok(path.to_string())
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ListScheduleQuery {
|
||||
pub page: Option<usize>,
|
||||
pub per_page: Option<usize>,
|
||||
pub path: Option<String>,
|
||||
pub is_flow: Option<bool>,
|
||||
}
|
||||
|
||||
async fn list_schedule(
|
||||
authed: Authed,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
@@ -218,16 +227,19 @@ async fn list_schedule(
|
||||
Query(pagination): Query<Pagination>,
|
||||
) -> JsonResult<Vec<Schedule>> {
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
let (per_page, offset) = paginate(pagination);
|
||||
let rows = sqlx::query_as!(
|
||||
Schedule,
|
||||
"SELECT * FROM schedule WHERE workspace_id = $1 ORDER BY edited_at desc LIMIT $2 OFFSET $3",
|
||||
w_id,
|
||||
per_page as i64,
|
||||
offset as i64
|
||||
)
|
||||
.fetch_all(&mut tx)
|
||||
.await?;
|
||||
let (per_page, offset) =
|
||||
paginate(Pagination { per_page: pagination.per_page, page: pagination.page });
|
||||
let sqlb = SqlBuilder::select_from("schedule")
|
||||
.field("*")
|
||||
.order_by("edited_at", true)
|
||||
.and_where("workspace_id = ?".bind(&w_id))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
.clone();
|
||||
let sql = sqlb.sql().map_err(|e| Error::InternalErr(e.to_string()))?;
|
||||
let rows = sqlx::query_as::<_, Schedule>(&sql)
|
||||
.fetch_all(&mut tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok(Json(rows))
|
||||
}
|
||||
|
||||
@@ -399,9 +399,9 @@ async fn create_script(
|
||||
.execute(&mut tx)
|
||||
.await?;
|
||||
|
||||
let schedulables = sqlx::query_as!(
|
||||
let mut schedulables = sqlx::query_as!(
|
||||
Schedule,
|
||||
"UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *",
|
||||
"UPDATE schedule SET script_path = $1 WHERE script_path = $2 AND path != $2 AND workspace_id = $3 AND is_flow IS false RETURNING *",
|
||||
ns.path,
|
||||
p_path,
|
||||
w_id,
|
||||
@@ -409,6 +409,19 @@ async fn create_script(
|
||||
.fetch_all(&mut tx)
|
||||
.await?;
|
||||
|
||||
let schedule = sqlx::query_as!(Schedule,
|
||||
"UPDATE schedule SET path = $1, script_path = $1 WHERE path = $2 AND workspace_id = $3 AND is_flow IS false RETURNING *",
|
||||
ns.path,
|
||||
p_path,
|
||||
w_id,
|
||||
)
|
||||
.fetch_optional(&mut tx)
|
||||
.await?;
|
||||
|
||||
if let Some(schedule) = schedule {
|
||||
schedulables.push(schedule);
|
||||
}
|
||||
|
||||
for schedule in schedulables {
|
||||
clear_schedule(tx.transaction_mut(), &schedule.path, false).await?;
|
||||
|
||||
|
||||
@@ -210,6 +210,7 @@
|
||||
function initPath() {
|
||||
if (path != undefined && path != '') {
|
||||
meta = pathToMeta(path)
|
||||
onMetaChange()
|
||||
return
|
||||
}
|
||||
if (initialPath == undefined || initialPath == '') {
|
||||
@@ -287,7 +288,6 @@
|
||||
const kind = e.detail
|
||||
if (meta) {
|
||||
if (kind === 'folder') {
|
||||
console.log($userStore?.folders)
|
||||
meta.owner = folders?.[0]?.name ?? ''
|
||||
} else if (kind === 'group') {
|
||||
meta.owner = 'all'
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
{#if schedulable}
|
||||
<div class="mt-10" />
|
||||
<div class="flex gap-2 items-start flex-wrap justify-between mt-2 md:mt-6 mb-6">
|
||||
<div class="flex-row-reverse flex w-full gap-4">
|
||||
<div class="flex-row-reverse flex-wrap flex w-full gap-4">
|
||||
<Button
|
||||
{loading}
|
||||
color="dark"
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
<script lang="ts">
|
||||
import { faCalendar } from '@fortawesome/free-solid-svg-icons'
|
||||
import ScheduleEditor from './ScheduleEditor.svelte'
|
||||
import { Badge, Button } from './common'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { ScheduleService, type Schedule } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { ListOrdered, PenBox } from 'lucide-svelte'
|
||||
import JobArgs from './JobArgs.svelte'
|
||||
import Skeleton from './common/skeleton/Skeleton.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
|
||||
export let isFlow: boolean
|
||||
export let path: string
|
||||
export let can_write: boolean
|
||||
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
let schedule: Schedule | undefined = undefined
|
||||
let schedules: Schedule[] | undefined = undefined
|
||||
|
||||
$: path && loadSchedule()
|
||||
$: path && loadSchedules()
|
||||
|
||||
async function loadSchedule() {
|
||||
try {
|
||||
let exists = await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
if (exists) {
|
||||
schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('no primary schedule')
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSchedules() {
|
||||
try {
|
||||
schedules = (
|
||||
await ScheduleService.listSchedules({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path,
|
||||
isFlow
|
||||
})
|
||||
).filter((s) => s.path != path)
|
||||
} catch (e) {
|
||||
console.error('impossible to load schedules')
|
||||
}
|
||||
}
|
||||
|
||||
async function setScheduleEnabled(path: string, enabled: boolean): Promise<void> {
|
||||
try {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
loadSchedule()
|
||||
|
||||
sendUserToast(`Schedule ${enabled ? 'enabled' : 'disabled'}`)
|
||||
} catch (err) {
|
||||
sendUserToast(`Cannot ` + (enabled ? 'disable' : 'enable') + ` schedule: ${err}`, true)
|
||||
loadSchedule()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ScheduleEditor
|
||||
on:update={() => {
|
||||
loadSchedule()
|
||||
loadSchedules()
|
||||
}}
|
||||
bind:this={scheduleEditor}
|
||||
/>
|
||||
|
||||
<div class="p-2 flex flex-col">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(isFlow, path)}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: faCalendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if schedule}
|
||||
<div class="p-2 flex flex-col gap-2">
|
||||
<div class="flex flex-row justify-between h-8">
|
||||
<div class="flex flex-row gap-2">
|
||||
<input
|
||||
class="inline-block !w-32"
|
||||
type="text"
|
||||
id="cron-schedule"
|
||||
name="cron-schedule"
|
||||
placeholder="*/30 * * * *"
|
||||
value={schedule.schedule}
|
||||
disabled={true}
|
||||
/>
|
||||
<Badge color="indigo" small
|
||||
>Primary schedule <Tooltip
|
||||
>Share the same path as the script or flow it is attached to and its path get renamed
|
||||
whenever the source path is renamed</Tooltip
|
||||
></Badge
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<Toggle
|
||||
checked={schedule.enabled}
|
||||
on:change={(e) => {
|
||||
if (can_write) {
|
||||
setScheduleEnabled(path, e.detail)
|
||||
} else {
|
||||
sendUserToast('not enough permission', true)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'On'
|
||||
}}
|
||||
size="xs"
|
||||
/>
|
||||
<Button size="xs" variant="border" color="light" href={`/runs/${path}`}>
|
||||
<div class="flex flex-row gap-2">
|
||||
<ListOrdered size={14} />
|
||||
Runs
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => scheduleEditor?.openEdit(path ?? '', isFlow)}
|
||||
>
|
||||
<PenBox size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#if Object.keys(schedule?.args ?? {}).length > 0}
|
||||
<div class="">
|
||||
<JobArgs args={schedule.args ?? {}} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-xs texg-gray-700"> No arguments </div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else}
|
||||
<Skeleton layout={[[8, 2]]} />
|
||||
{/if}
|
||||
|
||||
<h3 class="px-2 pt-4">Other schedules</h3>
|
||||
|
||||
{#if schedules}
|
||||
{#if schedules.length == 0}
|
||||
<div class="text-xs text-gray-700 px-2"> No other schedules </div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y px-2 pt-2">
|
||||
{#each schedules as schedule}
|
||||
<div class="grid grid-cols-6 text-2xs items-center py-2"
|
||||
><div class="col-span-3 truncate">{schedule.path}</div><div>{schedule.schedule}</div>
|
||||
<div>{schedule.enabled ? 'on' : 'off'}</div>
|
||||
<button on:click={() => scheduleEditor?.openEdit(path, isFlow)}>edit</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<Skeleton layout={[[8, 2]]} />
|
||||
{/if}
|
||||
@@ -251,8 +251,6 @@
|
||||
/>
|
||||
{/if}
|
||||
<div class="mt-6">
|
||||
<span class="font-semibold text-gray-700">Arguments</span>
|
||||
|
||||
{#if runnable}
|
||||
{#if runnable?.schema && runnable.schema.properties && Object.keys(runnable.schema.properties).length > 0}
|
||||
<SchemaForm disabled={!can_write} schema={runnable.schema} bind:isValid bind:args />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="border-b p-2 shadow-md">
|
||||
<div class="mx-auto">
|
||||
<div class="flex w-full flex-wrap md:flex-nowrap justify-end gap-x-2 gap-y-4 h-8 items-center">
|
||||
<div class="grow text-lg font-bold truncate">
|
||||
<div class="grow text-lg font-bold truncate px-2">
|
||||
{title}
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
<Tab value="schedule">
|
||||
<span class="flex flex-row gap-2 items-center">
|
||||
<CalendarCheck2 size={14} />
|
||||
Schedule
|
||||
Schedules
|
||||
</span>
|
||||
</Tab>
|
||||
<Tab value="cli">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores'
|
||||
import { FlowService, JobService, ScheduleService, type Flow, type Schedule } from '$lib/gen'
|
||||
import { FlowService, JobService, type Flow } from '$lib/gen'
|
||||
import {
|
||||
canWrite,
|
||||
defaultIfEmptyString,
|
||||
@@ -8,36 +8,32 @@
|
||||
emptyString,
|
||||
encodeState
|
||||
} from '$lib/utils'
|
||||
import { faCalendar, faCodeFork, faEdit } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCodeFork, faEdit } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
|
||||
import DetailPageLayout from '$lib/components/details/DetailPageLayout.svelte'
|
||||
import { goto } from '$app/navigation'
|
||||
import { Alert, Button, Skeleton } from '$lib/components/common'
|
||||
import JobArgs from '$lib/components/JobArgs.svelte'
|
||||
import { Alert, Skeleton } from '$lib/components/common'
|
||||
import MoveDrawer from '$lib/components/MoveDrawer.svelte'
|
||||
import RunForm from '$lib/components/RunForm.svelte'
|
||||
import ScheduleEditor from '$lib/components/ScheduleEditor.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import Urlize from '$lib/components/Urlize.svelte'
|
||||
import DeployWorkspaceDrawer from '$lib/components/DeployWorkspaceDrawer.svelte'
|
||||
import SavedInputs from '$lib/components/SavedInputs.svelte'
|
||||
import { FolderOpen, Archive, Trash, Server, Share, PenBox, ListOrdered } from 'lucide-svelte'
|
||||
import { FolderOpen, Archive, Trash, Server, Share } from 'lucide-svelte'
|
||||
|
||||
import DetailPageHeader from '$lib/components/details/DetailPageHeader.svelte'
|
||||
import WebhooksPanel from '$lib/components/details/WebhooksPanel.svelte'
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import CliHelpBox from '$lib/components/CliHelpBox.svelte'
|
||||
import InlineCodeCopy from '$lib/components/InlineCodeCopy.svelte'
|
||||
import FlowGraphViewer from '$lib/components/FlowGraphViewer.svelte'
|
||||
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
|
||||
import SchemaViewer from '$lib/components/SchemaViewer.svelte'
|
||||
import RunPageSchedules from '$lib/components/RunPageSchedules.svelte'
|
||||
|
||||
let flow: Flow | undefined
|
||||
let schedule: Schedule | undefined
|
||||
let can_write = false
|
||||
let path = $page.params.path
|
||||
let shareModal: ShareModal
|
||||
@@ -47,24 +43,6 @@
|
||||
$: {
|
||||
if ($workspaceStore && $userStore) {
|
||||
loadFlow()
|
||||
loadSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSchedule() {
|
||||
try {
|
||||
let exists = await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
if (exists) {
|
||||
schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('no primary schedule')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,22 +61,6 @@
|
||||
goto('/')
|
||||
}
|
||||
|
||||
async function setScheduleEnabled(path: string, enabled: boolean): Promise<void> {
|
||||
try {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
path,
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
loadSchedule()
|
||||
|
||||
sendUserToast(`Schedule ${enabled ? 'enabled' : 'disabled'}`)
|
||||
} catch (err) {
|
||||
sendUserToast(`Cannot ` + (enabled ? 'disable' : 'enable') + ` schedule: ${err}`, true)
|
||||
loadSchedule()
|
||||
}
|
||||
}
|
||||
|
||||
async function loadFlow(): Promise<void> {
|
||||
flow = await FlowService.getFlowByPath({ workspace: $workspaceStore!, path })
|
||||
can_write = canWrite(flow.path, flow.extra_perms!, $userStore)
|
||||
@@ -126,7 +88,6 @@
|
||||
})
|
||||
await goto('/run/' + run + '?workspace=' + $workspaceStore)
|
||||
}
|
||||
let scheduleEditor: ScheduleEditor
|
||||
|
||||
let args = undefined
|
||||
|
||||
@@ -229,7 +190,6 @@
|
||||
layout={[0.75, [2, 0, 2], 2.25, [{ h: 1.5, w: 40 }], 0.2, [{ h: 1, w: 30 }]]}
|
||||
/>
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
<ScheduleEditor on:update={() => loadSchedule()} bind:this={scheduleEditor} />
|
||||
<DeployWorkspaceDrawer bind:this={deploymentDrawer} />
|
||||
<ShareModal bind:this={shareModal} />
|
||||
<MoveDrawer
|
||||
@@ -237,7 +197,6 @@
|
||||
on:update={async (e) => {
|
||||
await goto('/flows/get/' + e.detail + `?workspace=${$workspaceStore}`)
|
||||
loadFlow()
|
||||
loadSchedule()
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -326,71 +285,7 @@
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="schedule">
|
||||
<div class="flex flex-row justify-end p-2">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(true, flow?.path ?? '')}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: faCalendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
{#if schedule}
|
||||
<div class="p-2 flex flex-col gap-2">
|
||||
<div class="flex flex-row justify-between h-8">
|
||||
<div class="flex flex-row gap-2">
|
||||
<input
|
||||
class="inline-block !w-32"
|
||||
type="text"
|
||||
id="cron-schedule"
|
||||
name="cron-schedule"
|
||||
placeholder="*/30 * * * *"
|
||||
value={schedule.schedule}
|
||||
disabled={true}
|
||||
/>
|
||||
<Badge color="indigo" small>Primary schedule</Badge>
|
||||
</div>
|
||||
<div class="flex flex-row gap-2">
|
||||
<Toggle
|
||||
checked={schedule.enabled}
|
||||
on:change={(e) => {
|
||||
if (can_write) {
|
||||
setScheduleEnabled(path, e.detail)
|
||||
} else {
|
||||
sendUserToast('not enough permission', true)
|
||||
}
|
||||
}}
|
||||
options={{
|
||||
right: 'On'
|
||||
}}
|
||||
size="xs"
|
||||
/>
|
||||
<Button size="xs" variant="border" color="light" href={`/runs/${flow?.path}`}>
|
||||
<div class="flex flex-row gap-2">
|
||||
<ListOrdered size={14} />
|
||||
Runs
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
color="dark"
|
||||
on:click={() => scheduleEditor?.openEdit(flow?.path ?? '', true)}
|
||||
>
|
||||
<PenBox size={14} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{#if Object.keys(schedule?.args ?? {}).length > 0}
|
||||
<div class="">
|
||||
<JobArgs args={schedule.args ?? {}} />
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-xs texg-gray-700"> This flow takes no argument </div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
<RunPageSchedules isFlow={true} path={flow.path ?? ''} {can_write} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="cli">
|
||||
<div class="p-2">
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
displayDaysAgo,
|
||||
truncateHash
|
||||
} from '$lib/utils'
|
||||
import { faEdit, faCalendar, faCodeFork, faHistory } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faEdit, faCodeFork, faHistory } from '@fortawesome/free-solid-svg-icons'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import ShareModal from '$lib/components/ShareModal.svelte'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
@@ -20,7 +20,6 @@
|
||||
Tabs,
|
||||
Tab,
|
||||
TabContent,
|
||||
Button,
|
||||
Badge,
|
||||
Alert,
|
||||
DrawerContent,
|
||||
@@ -56,6 +55,7 @@
|
||||
import { scriptToHubUrl } from '$lib/hub'
|
||||
import SharedBadge from '$lib/components/SharedBadge.svelte'
|
||||
import ScriptVersionHistory from '$lib/components/ScriptVersionHistory.svelte'
|
||||
import RunPageSchedules from '$lib/components/RunPageSchedules.svelte'
|
||||
|
||||
let script: Script | undefined
|
||||
let topHash: string | undefined
|
||||
@@ -352,6 +352,7 @@
|
||||
/>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
<DeployWorkspaceDrawer bind:this={deploymentDrawer} />
|
||||
<ScheduleEditor bind:this={scheduleEditor} />
|
||||
<ShareModal bind:this={shareModal} />
|
||||
@@ -482,17 +483,7 @@
|
||||
<WebhooksPanel scopes={[`run:script/${script?.path}`]} {webhooks} {args} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="schedule">
|
||||
<div class="p-2 flex flex-col">
|
||||
<Button
|
||||
on:click={() => scheduleEditor?.openNew(false, script?.path ?? '')}
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
startIcon={{ icon: faCalendar }}
|
||||
>
|
||||
New Schedule
|
||||
</Button>
|
||||
</div>
|
||||
<RunPageSchedules isFlow={false} path={script.path ?? ''} {can_write} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="details">
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user