feat: Show exec conditions in the UI, and allow right clicking buttons for action details
This commit is contained in:
parent
437255e247
commit
cbed6d68c2
31
config.yaml
31
config.yaml
|
|
@ -14,6 +14,10 @@ logLevel: "INFO"
|
|||
#
|
||||
# Docs: https://docs.olivetin.app/action_execution/create_your_first.html
|
||||
actions:
|
||||
# Every action can still be run on demand from the web UI or API. The keys
|
||||
# below are optional *additional* triggers (see each action and
|
||||
# https://docs.olivetin.app/action_execution/ ).
|
||||
#
|
||||
# This is the most simple action, it just runs the command and flashes the
|
||||
# button to indicate status.
|
||||
#
|
||||
|
|
@ -23,6 +27,8 @@ actions:
|
|||
shell: ping -c 3 1.1.1.1
|
||||
icon: ping
|
||||
popupOnStart: execution-dialog-stdout-only
|
||||
# https://docs.olivetin.app/action_execution/onstartup.html
|
||||
execOnStartup: true
|
||||
|
||||
# This uses `popupOnStart: execution-dialog-stdout-only` to simply show just
|
||||
# the command output.
|
||||
|
|
@ -30,6 +36,10 @@ actions:
|
|||
icon: disk
|
||||
shell: df -h /media
|
||||
popupOnStart: execution-dialog-stdout-only
|
||||
# https://docs.olivetin.app/action_execution/onfilechanged.html
|
||||
# Create the directory first, e.g. mkdir -p /tmp/olivetin-demo-file-changed
|
||||
execOnFileChangedInDir:
|
||||
- /tmp/olivetin-demo-file-changed
|
||||
|
||||
# This uses `popupOnStart: execution-dialog` to show a dialog with more
|
||||
# information about the command that was run.
|
||||
|
|
@ -37,6 +47,10 @@ actions:
|
|||
shell: dmesg | tail
|
||||
icon: logs
|
||||
popupOnStart: execution-dialog
|
||||
# https://docs.olivetin.app/action_execution/oncron.html — second example;
|
||||
# the "date" action uses @hourly elsewhere in this file.
|
||||
execOnCron:
|
||||
- "0 3 * * 0"
|
||||
|
||||
# This uses `popupOnStart: execution-button` to display a mini button that
|
||||
# links to the logs.
|
||||
|
|
@ -51,6 +65,8 @@ actions:
|
|||
maxRate:
|
||||
- limit: 3
|
||||
duration: 1m
|
||||
execOnCron:
|
||||
- "@hourly"
|
||||
|
||||
# You are not limited to operating system commands, and of course you can run
|
||||
# your own scripts. Here `maxConcurrent` stops the script running multiple
|
||||
|
|
@ -63,6 +79,8 @@ actions:
|
|||
timeout: 10
|
||||
icon: backup
|
||||
popupOnStart: execution-dialog
|
||||
# https://docs.olivetin.app/action_execution/oncalendar.html
|
||||
execOnCalendarFile: examples/demo-olivetin-calendar.yaml
|
||||
|
||||
# When you want to prompt users for input, that is when you should use
|
||||
# `arguments` - this presents a popup dialog and asks for argument values.
|
||||
|
|
@ -74,6 +92,11 @@ actions:
|
|||
icon: ping
|
||||
timeout: 100
|
||||
popupOnStart: execution-dialog-stdout-only
|
||||
# https://docs.olivetin.app/action_execution/onwebhook.html — POST to /webhooks
|
||||
# with header X-OliveTin-Demo: ping-host (path and payload rules are documented).
|
||||
execOnWebhook:
|
||||
- matchHeaders:
|
||||
X-OliveTin-Demo: ping-host
|
||||
arguments:
|
||||
- name: host
|
||||
title: Host
|
||||
|
|
@ -149,6 +172,10 @@ actions:
|
|||
icon: ssh
|
||||
shell: olivetin-setup-easy-ssh
|
||||
popupOnStart: execution-dialog
|
||||
# Second webhook example: POST /webhooks?demo=setup-ssh
|
||||
execOnWebhook:
|
||||
- matchQuery:
|
||||
demo: setup-ssh
|
||||
|
||||
# Here's how to use SSH with the "easy" config, to restart a service on
|
||||
# another server.
|
||||
|
|
@ -215,6 +242,10 @@ actions:
|
|||
- title: Ping All Servers
|
||||
shell: "echo 'Ping all servers'"
|
||||
icon: ping
|
||||
# https://docs.olivetin.app/action_execution/onfilecreated.html
|
||||
# mkdir -p /tmp/olivetin-demo-file-created
|
||||
execOnFileCreatedInDir:
|
||||
- /tmp/olivetin-demo-file-created
|
||||
|
||||
- title: Start {{ .CurrentEntity.Names }}
|
||||
icon: box
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// @generated by protoc-gen-es v2.11.0
|
||||
// @generated by protoc-gen-es v2.12.0
|
||||
// @generated from file olivetin/api/v1/olivetin.proto (package olivetin.api.v1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
|
||||
|
|
@ -60,6 +60,36 @@ export declare type Action = Message<"olivetin.api.v1.Action"> & {
|
|||
* @generated from field: string datetime_rate_limit_expires = 9;
|
||||
*/
|
||||
datetimeRateLimitExpires: string;
|
||||
|
||||
/**
|
||||
* @generated from field: bool exec_on_startup = 10;
|
||||
*/
|
||||
execOnStartup: boolean;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated string exec_on_cron = 11;
|
||||
*/
|
||||
execOnCron: string[];
|
||||
|
||||
/**
|
||||
* @generated from field: repeated string exec_on_file_created_in_dir = 12;
|
||||
*/
|
||||
execOnFileCreatedInDir: string[];
|
||||
|
||||
/**
|
||||
* @generated from field: repeated string exec_on_file_changed_in_dir = 13;
|
||||
*/
|
||||
execOnFileChangedInDir: string[];
|
||||
|
||||
/**
|
||||
* @generated from field: string exec_on_calendar_file = 14;
|
||||
*/
|
||||
execOnCalendarFile: string;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated olivetin.api.v1.ActionWebhookExecHint exec_on_webhooks = 15;
|
||||
*/
|
||||
execOnWebhooks: ActionWebhookExecHint[];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -68,6 +98,27 @@ export declare type Action = Message<"olivetin.api.v1.Action"> & {
|
|||
*/
|
||||
export declare const ActionSchema: GenMessage<Action>;
|
||||
|
||||
/**
|
||||
* @generated from message olivetin.api.v1.ActionWebhookExecHint
|
||||
*/
|
||||
export declare type ActionWebhookExecHint = Message<"olivetin.api.v1.ActionWebhookExecHint"> & {
|
||||
/**
|
||||
* @generated from field: string template = 1;
|
||||
*/
|
||||
template: string;
|
||||
|
||||
/**
|
||||
* @generated from field: string match_path = 2;
|
||||
*/
|
||||
matchPath: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the message olivetin.api.v1.ActionWebhookExecHint.
|
||||
* Use `create(ActionWebhookExecHintSchema)` to create a new message.
|
||||
*/
|
||||
export declare const ActionWebhookExecHintSchema: GenMessage<ActionWebhookExecHint>;
|
||||
|
||||
/**
|
||||
* @generated from message olivetin.api.v1.ActionArgument
|
||||
*/
|
||||
|
|
@ -188,7 +239,7 @@ export declare type GetDashboardResponse = Message<"olivetin.api.v1.GetDashboard
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.Dashboard dashboard = 4;
|
||||
*/
|
||||
dashboard?: Dashboard;
|
||||
dashboard?: Dashboard | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -302,7 +353,7 @@ export declare type DashboardComponent = Message<"olivetin.api.v1.DashboardCompo
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.Action action = 6;
|
||||
*/
|
||||
action?: Action;
|
||||
action?: Action | undefined;
|
||||
|
||||
/**
|
||||
* @generated from field: string entity_type = 7;
|
||||
|
|
@ -412,7 +463,7 @@ export declare type StartActionAndWaitResponse = Message<"olivetin.api.v1.StartA
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.LogEntry log_entry = 1;
|
||||
*/
|
||||
logEntry?: LogEntry;
|
||||
logEntry?: LogEntry | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -476,7 +527,7 @@ export declare type StartActionByGetAndWaitResponse = Message<"olivetin.api.v1.S
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.LogEntry log_entry = 1;
|
||||
*/
|
||||
logEntry?: LogEntry;
|
||||
logEntry?: LogEntry | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -825,7 +876,7 @@ export declare type ExecutionStatusResponse = Message<"olivetin.api.v1.Execution
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.LogEntry log_entry = 1;
|
||||
*/
|
||||
logEntry?: LogEntry;
|
||||
logEntry?: LogEntry | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1135,7 +1186,7 @@ export declare type EventExecutionFinished = Message<"olivetin.api.v1.EventExecu
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.LogEntry log_entry = 1;
|
||||
*/
|
||||
logEntry?: LogEntry;
|
||||
logEntry?: LogEntry | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1151,7 +1202,7 @@ export declare type EventExecutionStarted = Message<"olivetin.api.v1.EventExecut
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.LogEntry log_entry = 1;
|
||||
*/
|
||||
logEntry?: LogEntry;
|
||||
logEntry?: LogEntry | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1437,7 +1488,7 @@ export declare type InitResponse = Message<"olivetin.api.v1.InitResponse"> & {
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.EffectivePolicy effective_policy = 18;
|
||||
*/
|
||||
effectivePolicy?: EffectivePolicy;
|
||||
effectivePolicy?: EffectivePolicy | undefined;
|
||||
|
||||
/**
|
||||
* @generated from field: string banner_message = 19;
|
||||
|
|
@ -1553,7 +1604,7 @@ export declare type GetActionBindingResponse = Message<"olivetin.api.v1.GetActio
|
|||
/**
|
||||
* @generated from field: olivetin.api.v1.Action action = 1;
|
||||
*/
|
||||
action?: Action;
|
||||
action?: Action | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1858,3 +1909,4 @@ export declare const OliveTinApiService: GenService<{
|
|||
output: typeof EntitySchema;
|
||||
},
|
||||
}>;
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :id="`actionButton-${bindingId}`" role="none" class="action-button">
|
||||
<div :id="`actionButton-${bindingId}`" role="none" class="action-button" @contextmenu.prevent="openActionDetails">
|
||||
<button :id="`actionButtonInner-${bindingId}`" :title="title" :disabled="!canExec || isDisabled"
|
||||
:class="combinedClasses" @click="handleClick">
|
||||
|
||||
|
|
@ -191,7 +191,19 @@ function updateRateLimitStatus() {
|
|||
}
|
||||
}
|
||||
|
||||
function openActionDetails() {
|
||||
const id = props.actionData?.bindingId
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
router.push(`/action/${id}`)
|
||||
}
|
||||
|
||||
async function handleClick() {
|
||||
if (popupOnStart.value === 'history') {
|
||||
router.push(`/action/${props.actionData.bindingId}`)
|
||||
return
|
||||
}
|
||||
if (props.actionData.arguments && props.actionData.arguments.length > 0) {
|
||||
router.push(`/actionBinding/${props.actionData.bindingId}/argumentForm`)
|
||||
} else {
|
||||
|
|
@ -249,8 +261,6 @@ function onLogEntryChanged(logEntry) {
|
|||
function onExecutionStarted(logEntry) {
|
||||
if (popupOnStart.value && popupOnStart.value.includes('execution-dialog')) {
|
||||
router.push(`/logs/${logEntry.executionTrackingId}`)
|
||||
} else if (popupOnStart.value === 'history') {
|
||||
router.push(`/action/${bindingId.value}`)
|
||||
}
|
||||
|
||||
isDisabled.value = true
|
||||
|
|
|
|||
|
|
@ -95,6 +95,19 @@ const routes = [
|
|||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/action/:actionId/actionexecconditions',
|
||||
name: 'ActionExecConditions',
|
||||
component: () => import('./views/ActionExecConditionsView.vue'),
|
||||
props: true,
|
||||
meta: {
|
||||
title: 'Execution conditions',
|
||||
breadcrumb: [
|
||||
{ name: "Actions", href: "/" },
|
||||
{ name: "Execution conditions" },
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/diagnostics',
|
||||
name: 'Diagnostics',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
<template>
|
||||
<Section :title="'Action Details: ' + actionTitle" :padding="false">
|
||||
<template #toolbar>
|
||||
<div class="action-details-toolbar">
|
||||
<button v-if="action" @click="startAction" title="Start this action" class="button neutral">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M8 6v12l8-6z" />
|
||||
</svg>
|
||||
Start
|
||||
</button>
|
||||
<router-link
|
||||
v-if="action"
|
||||
:to="{ name: 'ActionExecConditions', params: { actionId: route.params.actionId } }"
|
||||
class="button neutral"
|
||||
title="View configured automatic triggers and on-demand execution"
|
||||
>
|
||||
Execution conditions
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class = "flex-row padding" v-if="action">
|
||||
|
|
@ -428,5 +438,12 @@ watch(
|
|||
.padding {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.action-details-toolbar {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,209 @@
|
|||
<template>
|
||||
<Section :title="'Execution conditions: ' + actionTitle" :padding="false">
|
||||
<template #toolbar>
|
||||
<router-link :to="{ name: 'ActionDetails', params: { actionId: route.params.actionId } }" class="button neutral">
|
||||
Back to action details
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<div v-if="action" class="padding content">
|
||||
<p>
|
||||
These entries mirror the automatic triggers from your OliveTin configuration for this action.
|
||||
You can always run the action manually as well.
|
||||
</p>
|
||||
|
||||
<h3 class="exec-type-heading">
|
||||
On demand
|
||||
<a class="doc-link" :href="execConditionDocs.onDemand" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<p>
|
||||
Manual execution from the web UI (dashboard or action details), or via the API (for example StartAction),
|
||||
is always available when your user is allowed to execute the action.
|
||||
</p>
|
||||
|
||||
<template v-if="action.execOnStartup">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnStartup</code>
|
||||
<a class="doc-link" :href="execConditionDocs.startup" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<p>Runs once when OliveTin starts.</p>
|
||||
</template>
|
||||
|
||||
<template v-if="nonEmptyList(action.execOnCron)">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnCron</code>
|
||||
<a class="doc-link" :href="execConditionDocs.cron" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<ul>
|
||||
<li v-for="(line, idx) in action.execOnCron" :key="'cron-' + idx"><code>{{ line }}</code></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template v-if="nonEmptyList(action.execOnFileCreatedInDir)">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnFileCreatedInDir</code>
|
||||
<a class="doc-link" :href="execConditionDocs.fileCreated" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<ul>
|
||||
<li v-for="(dir, idx) in action.execOnFileCreatedInDir" :key="'created-' + idx"><code>{{ dir }}</code></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template v-if="nonEmptyList(action.execOnFileChangedInDir)">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnFileChangedInDir</code>
|
||||
<a class="doc-link" :href="execConditionDocs.fileChanged" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<ul>
|
||||
<li v-for="(dir, idx) in action.execOnFileChangedInDir" :key="'changed-' + idx"><code>{{ dir }}</code></li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template v-if="action.execOnCalendarFile">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnCalendarFile</code>
|
||||
<a class="doc-link" :href="execConditionDocs.calendar" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<p><code>{{ action.execOnCalendarFile }}</code></p>
|
||||
</template>
|
||||
|
||||
<template v-if="nonEmptyList(action.execOnWebhooks)">
|
||||
<h3 class="exec-type-heading">
|
||||
<code>execOnWebhook</code>
|
||||
<a class="doc-link" :href="execConditionDocs.webhook" target="_blank" rel="noopener noreferrer">Documentation</a>
|
||||
</h3>
|
||||
<ul class="webhook-list">
|
||||
<li v-for="(wh, idx) in action.execOnWebhooks" :key="'wh-' + idx">
|
||||
<span v-if="wh.template">template: <code>{{ wh.template }}</code></span>
|
||||
<span v-if="wh.matchPath"> · matchPath: <code>{{ wh.matchPath }}</code></span>
|
||||
<span v-if="!wh.template && !wh.matchPath">Webhook trigger (no template or match path in response)</span>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<p v-if="!hasConfiguredTriggers" class="muted">
|
||||
This action has no automatic triggers in configuration besides on-demand execution.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!loading" class="padding empty-state">
|
||||
<p>Could not load this action.</p>
|
||||
<router-link :to="{ name: 'Actions' }">Return to index</router-link>
|
||||
</div>
|
||||
</Section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import Section from 'picocrank/vue/components/Section.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const action = ref(null)
|
||||
const actionTitle = ref('Action')
|
||||
const loading = ref(true)
|
||||
|
||||
const execConditionDocs = {
|
||||
onDemand: 'https://docs.olivetin.app/action_execution/ondemand.html',
|
||||
startup: 'https://docs.olivetin.app/action_execution/onstartup.html',
|
||||
cron: 'https://docs.olivetin.app/action_execution/oncron.html',
|
||||
fileCreated: 'https://docs.olivetin.app/action_execution/onfilecreated.html',
|
||||
fileChanged: 'https://docs.olivetin.app/action_execution/onfilechanged.html',
|
||||
calendar: 'https://docs.olivetin.app/action_execution/oncalendar.html',
|
||||
webhook: 'https://docs.olivetin.app/action_execution/onwebhook.html',
|
||||
}
|
||||
|
||||
function nonEmptyList(list) {
|
||||
return Array.isArray(list) && list.length > 0
|
||||
}
|
||||
|
||||
const hasConfiguredTriggers = computed(() => {
|
||||
const a = action.value
|
||||
if (!a) {
|
||||
return false
|
||||
}
|
||||
if (a.execOnStartup) {
|
||||
return true
|
||||
}
|
||||
if (nonEmptyList(a.execOnCron) || nonEmptyList(a.execOnFileCreatedInDir) || nonEmptyList(a.execOnFileChangedInDir)) {
|
||||
return true
|
||||
}
|
||||
if (a.execOnCalendarFile) {
|
||||
return true
|
||||
}
|
||||
if (nonEmptyList(a.execOnWebhooks)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
async function fetchAction() {
|
||||
loading.value = true
|
||||
try {
|
||||
const actionId = route.params.actionId
|
||||
const response = await window.client.getActionBinding({ bindingId: actionId })
|
||||
action.value = response.action
|
||||
actionTitle.value = response.action?.title || 'Action'
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch action:', err)
|
||||
window.showBigError('fetch-action-exec-conditions', 'getting action', err, false)
|
||||
action.value = null
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(fetchAction)
|
||||
|
||||
watch(
|
||||
() => route.params.actionId,
|
||||
() => {
|
||||
action.value = null
|
||||
actionTitle.value = 'Action'
|
||||
fetchAction()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content h3 {
|
||||
margin-top: 1.25rem;
|
||||
margin-bottom: 0.35rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.exec-type-heading {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 0.35rem 0.75rem;
|
||||
}
|
||||
|
||||
.exec-type-heading .doc-link {
|
||||
font-size: 0.85rem;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.content p,
|
||||
.content ul {
|
||||
margin: 0.35rem 0 0;
|
||||
}
|
||||
|
||||
.webhook-list li {
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--text-secondary);
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -392,6 +392,11 @@ async function startAction(actionArgs) {
|
|||
async function handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
|
||||
if (popupOnStart.value === 'history') {
|
||||
router.push(`/action/${props.bindingId}`)
|
||||
return
|
||||
}
|
||||
|
||||
// Set custom validity for required fields
|
||||
for (const arg of actionArguments.value) {
|
||||
const value = argValues.value[arg.name]
|
||||
|
|
@ -422,8 +427,6 @@ async function handleSubmit(event) {
|
|||
const response = await startAction(argvs)
|
||||
if (popupOnStart.value && popupOnStart.value.includes('execution-dialog')) {
|
||||
router.push(`/logs/${response.executionTrackingId}`)
|
||||
} else if (popupOnStart.value === 'history') {
|
||||
router.push(`/action/${props.bindingId}`)
|
||||
} else {
|
||||
router.back()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,17 @@ message Action {
|
|||
int32 order = 7;
|
||||
int32 timeout = 8;
|
||||
string datetime_rate_limit_expires = 9; // Datetime when rate limit expires (empty string if not rate limited), format: "2006-01-02 15:04:05"
|
||||
bool exec_on_startup = 10;
|
||||
repeated string exec_on_cron = 11;
|
||||
repeated string exec_on_file_created_in_dir = 12;
|
||||
repeated string exec_on_file_changed_in_dir = 13;
|
||||
string exec_on_calendar_file = 14;
|
||||
repeated ActionWebhookExecHint exec_on_webhooks = 15;
|
||||
}
|
||||
|
||||
message ActionWebhookExecHint {
|
||||
string template = 1;
|
||||
string match_path = 2;
|
||||
}
|
||||
|
||||
message ActionArgument {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,25 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
apiv1 "github.com/OliveTin/OliveTin/gen/olivetin/api/v1"
|
||||
config "github.com/OliveTin/OliveTin/internal/config"
|
||||
)
|
||||
|
||||
func applyActionExecTriggers(pb *apiv1.Action, cfg *config.Action) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pb.ExecOnStartup = cfg.ExecOnStartup
|
||||
pb.ExecOnCron = append([]string(nil), cfg.ExecOnCron...)
|
||||
pb.ExecOnFileCreatedInDir = append([]string(nil), cfg.ExecOnFileCreatedInDir...)
|
||||
pb.ExecOnFileChangedInDir = append([]string(nil), cfg.ExecOnFileChangedInDir...)
|
||||
pb.ExecOnCalendarFile = cfg.ExecOnCalendarFile
|
||||
|
||||
for _, wh := range cfg.ExecOnWebhook {
|
||||
pb.ExecOnWebhooks = append(pb.ExecOnWebhooks, &apiv1.ActionWebhookExecHint{
|
||||
Template: wh.Template,
|
||||
MatchPath: wh.MatchPath,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +156,8 @@ func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderReque
|
|||
DatetimeRateLimitExpires: datetimeRateLimitExpires,
|
||||
}
|
||||
|
||||
applyActionExecTriggers(&btn, action)
|
||||
|
||||
for _, cfgArg := range action.Arguments {
|
||||
pbArg := apiv1.ActionArgument{
|
||||
Name: cfgArg.Name,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ import (
|
|||
|
||||
func WatchFilesInDirectory(cfg *config.Config, ex *executor.Executor) {
|
||||
for _, action := range cfg.Actions {
|
||||
for _, dirname := range action.ExecOnFileCreatedInDir {
|
||||
go func(act *config.Action, dir string) {
|
||||
filehelper.WatchDirectoryCreate(dir, func(filename string) {
|
||||
scheduleExec(act, cfg, ex, filename)
|
||||
})
|
||||
}(action, dirname)
|
||||
}
|
||||
for _, dirname := range action.ExecOnFileChangedInDir {
|
||||
// Pass values into anonymous function because of this issue
|
||||
// https://github.com/OliveTin/OliveTin/issues/503
|
||||
|
|
|
|||
Loading…
Reference in New Issue