feat: configurable languages and orders

This commit is contained in:
Ruben Fiszel
2024-03-12 21:50:24 +01:00
parent 853bb844d9
commit 16c615e038
18 changed files with 413 additions and 364 deletions
+55
View File
@@ -1698,6 +1698,43 @@ paths:
schema:
type: string
/w/{workspace}/workspaces/default_scripts:
post:
summary: edit default scripts for workspace
operationId: editDefaultScripts
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
requestBody:
description: Workspace default app
content:
application/json:
schema:
$ref: "#/components/schemas/WorkspaceDefaultScripts"
responses:
"200":
description: status
content:
text/plain:
schema:
type: string
get:
summary: get default scripts for workspace
operationId: get default scripts
tags:
- workspace
parameters:
- $ref: "#/components/parameters/WorkspaceId"
responses:
"200":
description: status
content:
application/json:
schema:
$ref: "#/components/schemas/WorkspaceDefaultScripts"
/w/{workspace}/workspaces/encryption_key:
get:
summary: retrieves the encryption key for this workspace
@@ -9834,6 +9871,24 @@ components:
items:
$ref: "#/components/schemas/GitRepositorySettings"
WorkspaceDefaultScripts:
type: object
properties:
order:
type: array
items:
type: string
hidden:
type: array
items:
type: string
default_script_content:
additionalProperties:
type: string
GitRepositorySettings:
type: object
properties:
+66 -1
View File
@@ -100,6 +100,10 @@ pub fn workspaced_service() -> Router {
.route("/edit_git_sync_config", post(edit_git_sync_config))
.route("/edit_default_app", post(edit_default_app))
.route("/default_app", get(get_default_app))
.route(
"/default_scripts",
post(edit_default_scripts).get(get_default_scripts),
)
.route(
"/encryption_key",
get(get_encryption_key).post(set_encryption_key),
@@ -164,6 +168,7 @@ pub struct WorkspaceSettings {
pub git_sync: Option<serde_json::Value>, // effectively: WorkspaceGitSyncSettings
pub default_app: Option<String>,
pub automatic_billing: bool,
pub default_scripts: Option<serde_json::Value>,
}
#[derive(FromRow, Serialize, Debug)]
@@ -984,6 +989,66 @@ async fn edit_default_app(
));
}
async fn edit_default_scripts(
authed: ApiAuthed,
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
ApiAuthed { is_admin, username, .. }: ApiAuthed,
Json(new_config): Json<Option<serde_json::Value>>,
) -> Result<String> {
require_admin(is_admin, &username)?;
let mut tx = db.begin().await?;
audit_log(
&mut *tx,
&authed.username,
"workspaces.edit_default_scripts",
ActionKind::Update,
&w_id,
Some(&authed.email),
None,
)
.await?;
if let Some(config) = new_config {
sqlx::query!(
"UPDATE workspace_settings SET default_scripts = $1 WHERE workspace_id = $2",
config,
&w_id
)
.execute(&mut *tx)
.await?;
} else {
sqlx::query!(
"UPDATE workspace_settings SET default_scripts = NULL WHERE workspace_id = $1",
&w_id,
)
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
Ok(format!("Edit default scripts for workspace {}", &w_id))
}
async fn get_default_scripts(
Extension(db): Extension<DB>,
Path(w_id): Path<String>,
) -> JsonResult<Option<serde_json::Value>> {
let mut tx = db.begin().await?;
let default_scripts = sqlx::query_scalar!(
"SELECT default_scripts FROM workspace_settings WHERE workspace_id = $1",
&w_id
)
.fetch_optional(&mut *tx)
.await
.map_err(|err| Error::InternalErr(format!("getting default_app: {err}")))?;
tx.commit().await?;
Ok(Json(default_scripts.flatten()))
}
#[cfg(feature = "enterprise")]
async fn edit_default_app(
authed: ApiAuthed,
@@ -1012,7 +1077,7 @@ async fn edit_default_app(
ActionKind::Update,
&w_id,
Some(&authed.email),
Some([("args_for_audit", args_for_audit.as_str())].into()),
Some([("default_app", args_for_audit.as_str())].into()),
)
.await?;
@@ -0,0 +1,28 @@
<script lang="ts">
import { userStore } from '$lib/stores'
import { SettingsIcon } from 'lucide-svelte'
import { Button } from './common'
import Drawer from './common/drawer/Drawer.svelte'
import DrawerContent from './common/drawer/DrawerContent.svelte'
import DefaultScriptsInner from './DefaultScriptsInner.svelte'
import Portal from 'svelte-portal'
let drawer: Drawer
</script>
{#if $userStore?.is_admin || $userStore?.is_super_admin}
<Portal>
<Drawer bind:this={drawer} placement="left">
<DrawerContent title="Edit Default Scripts" on:close={drawer.closeDrawer}>
<DefaultScriptsInner />
</DrawerContent>
</Drawer>
</Portal>
<Button
on:click={drawer?.openDrawer}
startIcon={{ icon: SettingsIcon }}
color="light"
size="xs2"
variant="contained">defaults</Button
>
{/if}
@@ -0,0 +1,75 @@
<script lang="ts">
import { WorkspaceService, type WorkspaceDefaultScripts } from '$lib/gen'
import { defaultScripts, workspaceStore } from '$lib/stores'
import { flip } from 'svelte/animate'
import Toggle from './Toggle.svelte'
import { defaultScriptLanguages } from '$lib/scripts'
import Alert from './common/alert/Alert.svelte'
$: langs = computeLangs($defaultScripts)
function computeLangs(defaultScripts: WorkspaceDefaultScripts | undefined) {
const allLangs = Object.keys(defaultScriptLanguages)
if (!defaultScripts || defaultScripts.order == undefined) return allLangs
return defaultScripts.order?.concat(allLangs.filter((l) => !defaultScripts.order?.includes(l)))
}
async function changePosition(i: number, up: boolean) {
let norder = langs
if (up) {
;[norder[i], norder[i - 1]] = [norder[i - 1], norder[i]]
} else {
;[norder[i], norder[i + 1]] = [norder[i + 1], norder[i]]
}
defaultScripts.update((s) => ({ ...s, order: norder }))
await WorkspaceService.editDefaultScripts({
workspace: $workspaceStore!,
requestBody: $defaultScripts
})
}
</script>
<Alert title="Global to workspace" type="info" class="mb-4">
This setting is only available to admins and will affect all users in the workspace.
</Alert>
<div class="h-full w-full flex-col gap-2 flex">
{#each langs as lang, i (lang)}
<div
animate:flip={{ duration: 300 }}
class="w-full p-2 rounded border border-seconadry grid grid-cols-3"
><h3>{lang}</h3>
<div>
{#if i > 0}
<button on:click={() => changePosition(i ?? 0, true)} class="text-lg mr-2">
&uparrow;</button
>
{/if}
{#if i < langs.length - 1}
<button on:click={() => changePosition(i ?? 0, false)} class="text-lg mr-2"
>&downarrow;</button
>
{/if}</div
>
<!-- <Toggle options={{ right: 'custom default' }} size="xs" /> -->
<div class="flex justify-end">
<Toggle
options={{ right: 'hide' }}
size="xs"
color="red"
checked={$defaultScripts?.hidden?.includes(lang)}
on:change={(e) => {
let toggled = e.detail
if (toggled) {
defaultScripts.update((s) => ({ ...(s ?? {}), hidden: [...(s?.hidden ?? []), lang] }))
} else {
defaultScripts.update((s) => ({
...(s ?? {}),
hidden: (s?.hidden ?? []).filter((h) => h != lang)
}))
}
}}
/>
</div>
</div>
{/each}
</div>
@@ -16,7 +16,7 @@
export let simpleTooltip: string | undefined = undefined
</script>
<div class="inline-flex flex-row items-center truncated">
<div class="inline-flex flex-row items-baseline truncated">
<span class={twMerge(disabled ? 'text-tertiary' : '', 'font-semibold', labelClass)}>
{#if prettify}
{label.replace(/_/g, ' ').split(' ').map(capitalize).join(' ')}
@@ -43,10 +43,10 @@
{/if}
{#if !emptyString(simpleTooltip)}
<Tooltip class="ml-2">
<span class="text-xs">
{simpleTooltip}
</span>
</Tooltip>
{/if}
<Tooltip class="ml-2">
<span class="text-xs">
{simpleTooltip}
</span>
</Tooltip>
{/if}
</div>
@@ -11,7 +11,7 @@
import { page } from '$app/stores'
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
import { defaultScripts, enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
import {
cleanValueProperties,
emptySchema,
@@ -43,7 +43,6 @@
Settings,
X
} from 'lucide-svelte'
import { SCRIPT_SHOW_BASH, SCRIPT_SHOW_GO } from '$lib/consts'
import UnsavedConfirmationModal from './common/confirmationModal/UnsavedConfirmationModal.svelte'
import { sendUserToast } from '$lib/toast'
import { isCloudHosted } from '$lib/cloud'
@@ -61,11 +60,12 @@
import MetadataGen from './copilot/MetadataGen.svelte'
import ScriptSchedules from './ScriptSchedules.svelte'
import { writable } from 'svelte/store'
import { type ScriptSchedule, loadScriptSchedule } from '$lib/scripts'
import { type ScriptSchedule, loadScriptSchedule, defaultScriptLanguages } from '$lib/scripts'
import DefaultScripts from './DefaultScripts.svelte'
export let script: NewScript
export let initialPath: string = ''
export let template: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' = 'script'
export let template: 'docker' | 'script' = 'script'
export let initialArgs: Record<string, any> = {}
export let lockedLanguage = false
export let showMeta: boolean = false
@@ -107,25 +107,11 @@
editor?.setCode(code)
}
const langs: [string, SupportedLanguage][] = [
['TypeScript (Bun)', Script.language.BUN],
['Python', Script.language.PYTHON3],
['TypeScript (Deno)', Script.language.DENO]
]
if (SCRIPT_SHOW_BASH) {
langs.push(['Bash', Script.language.BASH])
}
if (SCRIPT_SHOW_GO) {
langs.push(['Go', Script.language.GO])
}
langs.push(['REST', Script.language.NATIVETS])
langs.push(['PostgreSQL', Script.language.POSTGRESQL])
langs.push(['MySQL', Script.language.MYSQL])
langs.push(['BigQuery', Script.language.BIGQUERY])
langs.push(['Snowflake', Script.language.SNOWFLAKE])
langs.push(['MS SQL Server', Script.language.MSSQL])
langs.push(['GraphQL', Script.language.GRAPHQL])
langs.push(['PowerShell', Script.language.POWERSHELL])
$: langs = ($defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) => $defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1])
) as [string, SupportedLanguage | 'docker'][]
const scriptKindOptions: {
value: Script.kind
@@ -551,14 +537,17 @@
</Section>
<Section label="Language">
<svelte:fragment slot="action"><DefaultScripts /></svelte:fragment>
{#if lockedLanguage}
<div class="text-sm text-tertiary italic mb-2">
As a forked script, the language '{script.language}' cannot be modified.
</div>
{/if}
<div class=" grid grid-cols-3 gap-2">
{#each langs as [label, lang]}
{@const isPicked = script.language == lang && template == 'script'}
{#each langs as [label, lang] (lang)}
{@const isPicked =
(lang == script.language && template == 'script') ||
(template == 'docker' && lang == 'docker')}
<Popover
disablePopup={!enterpriseLangs.includes(lang) || !!$enterpriseLicense}
>
@@ -570,9 +559,33 @@
? '!border-2 !bg-blue-50/75 dark:!bg-frost-900/75'
: 'm-[1px]'}
on:click={() => {
template = 'script'
initContent(lang, script.kind, template)
script.language = lang
if (lang == 'docker') {
if (isCloudHosted()) {
sendUserToast(
'You cannot use Docker scripts on the multi-tenant platform. Use a dedicated instance or self-host windmill instead.',
true,
[
{
label: 'Learn more',
callback: () => {
window.open(
'https://www.windmill.dev/docs/advanced/docker',
'_blank'
)
}
}
]
)
return
}
template = 'docker'
} else {
template = 'script'
}
let language = lang == 'docker' ? Script.language.BASH : lang
//
initContent(language, script.kind, template)
script.language = language
}}
disabled={lockedLanguage ||
(enterpriseLangs.includes(lang) && !$enterpriseLicense)}
@@ -585,40 +598,6 @@
>
</Popover>
{/each}
<Button
size="sm"
variant="border"
color={template == 'docker' ? 'blue' : 'light'}
btnClasses={template == 'docker'
? '!border-2 !bg-blue-50/75 dark:!bg-frost-900/75'
: 'm-[1px]'}
disabled={lockedLanguage}
on:click={() => {
if (isCloudHosted()) {
sendUserToast(
'You cannot use Docker scripts on the multi-tenant platform. Use a dedicated instance or self-host windmill instead.',
true,
[
{
label: 'Learn more',
callback: () => {
window.open(
'https://www.windmill.dev/docs/advanced/docker',
'_blank'
)
}
}
]
)
return
}
template = 'docker'
initContent(Script.language.BASH, script.kind, template)
script.language = Script.language.BASH
}}
>
<LanguageIcon lang="docker" /><span class="ml-2 py-2">Docker</span>
</Button>
</div>
</Section>
+1 -1
View File
@@ -69,7 +69,7 @@
minute: '2-digit'
})}`
} else {
return !withDate ? displayDate(dateString) : ''
return !withDate ? displayDate(dateString, false, withDate) : ''
}
}
}
@@ -8,7 +8,7 @@
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { emptySchema } from '$lib/utils'
import { getScriptByPath } from '$lib/scripts'
import { defaultScriptLanguages, getScriptByPath } from '$lib/scripts'
import { Building, GitFork, Globe2 } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
@@ -18,6 +18,8 @@
import InlineScriptList from '../settingsPanel/mainInput/InlineScriptList.svelte'
import WorkspaceScriptList from '../settingsPanel/mainInput/WorkspaceScriptList.svelte'
import RunnableSelector from '../settingsPanel/mainInput/RunnableSelector.svelte'
import { defaultScripts } from '$lib/stores'
import DefaultScripts from '$lib/components/DefaultScripts.svelte'
export let name: string
export let componentType: string | undefined = undefined
@@ -92,21 +94,13 @@
dispatch('new', unusedInlineScript.inlineScript)
}
const langs = [
['bun', 'TypeScript (Bun)'],
['python3', 'Python'],
['deno', 'TypeScript (Deno)'],
['go', 'Go'],
['bash', 'Bash'],
['powershell', 'PowerShell'],
['nativets', 'REST'],
['postgresql', 'PostgreSQL'],
['mysql', 'MySQL'],
['bigquery', 'BigQuery'],
['snowflake', 'Snowflake'],
['mssql', 'MS SQL Server'],
['graphql', 'GraphQL']
] as [Script.language, string][]
$: langs = ($defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) =>
x[1] != 'docker' &&
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
) as [string, Preview.language][]
</script>
<Drawer bind:this={picker} size="1000px">
@@ -192,10 +186,10 @@
<div class="flex flex-row w-full gap-8">
<div id="app-editor-backend-runnables">
<div class="mb-1 text-sm font-semibold">Backend</div>
<div class="mb-1 text-sm font-semibold flex gap-4">Backend <DefaultScripts /> </div>
<div class="flex flex-row flex-wrap gap-2">
{#each langs as [lang, label]}
{#each langs as [label, lang] (lang)}
<FlowScriptPicker
{label}
{lang}
@@ -2,7 +2,7 @@
import { Alert } from '$lib/components/common'
import ToggleHubWorkspace from '$lib/components/ToggleHubWorkspace.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
import { RawScript, Script } from '$lib/gen'
import { Script } from '$lib/gen'
import { createEventDispatcher } from 'svelte'
import FlowScriptPicker from '../pickers/FlowScriptPicker.svelte'
@@ -14,6 +14,10 @@
import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte'
import { Check, Code, Zap } from 'lucide-svelte'
import SuspendDrawer from './SuspendDrawer.svelte'
import { defaultScripts } from '$lib/stores'
import { defaultScriptLanguages } from '$lib/scripts'
import type { SupportedLanguage } from '$lib/common'
import DefaultScripts from '$lib/components/DefaultScripts.svelte'
export let failureModule: boolean
export let shouldDisableTriggerScripts: boolean = false
@@ -30,6 +34,30 @@
: 'script'
let pick_existing: 'workspace' | 'hub' = 'hub'
let filter = ''
$: langs = ($defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) => $defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1])
) as [string, SupportedLanguage | 'docker'][]
function displayLang(lang: SupportedLanguage | 'docker', kind: string) {
if (
lang == Script.language.BUN ||
lang == Script.language.PYTHON3 ||
lang == Script.language.DENO
) {
return true
}
if (lang == Script.language.GO) {
return kind == 'script' || kind == 'trigger' || failureModule
}
if (lang == Script.language.BASH || lang == Script.language.NATIVETS) {
return kind == 'script'
}
return kind == 'script' && !failureModule
}
</script>
<div class="p-4 h-full flex flex-col" id="flow-editor-flow-inputs">
@@ -136,21 +164,24 @@
>
{/if}
{/if}
<h3 class="pb-2 pt-4">
Inline new <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script
<Tooltip
documentationLink={kind === 'script'
? 'https://www.windmill.dev/docs/flows/editor_components#flow-actions'
: kind === 'trigger'
? 'https://www.windmill.dev/docs/flows/flow_trigger'
: kind === 'approval'
? 'https://www.windmill.dev/docs/flows/flow_approval'
: 'https://www.windmill.dev/docs/getting_started/flows_quickstart#flow-editor'}
>
Embed <span>{kind == 'script' ? 'action' : kind}</span> script directly inside a flow instead
of saving the script into your workspace for reuse. You can always save an inline script to your
workspace later.
</Tooltip>
<h3 class="pb-2 pt-4 flex gap-x-8 flex-wrap">
<div>
Inline new <span class="text-blue-500">{kind == 'script' ? 'action' : kind}</span> script
<Tooltip
documentationLink={kind === 'script'
? 'https://www.windmill.dev/docs/flows/editor_components#flow-actions'
: kind === 'trigger'
? 'https://www.windmill.dev/docs/flows/flow_trigger'
: kind === 'approval'
? 'https://www.windmill.dev/docs/flows/flow_approval'
: 'https://www.windmill.dev/docs/getting_started/flows_quickstart#flow-editor'}
>
Embed <span>{kind == 'script' ? 'action' : kind}</span> script directly inside a flow instead
of saving the script into your workspace for reuse. You can always save an inline script to
your workspace later.
</Tooltip>
</div>
<DefaultScripts />
</h3>
{#if noEditor}
<div
@@ -165,226 +196,41 @@
{/if}
<div class="flex flex-row">
<div class="flex flex-row flex-wrap gap-2" id="flow-editor-action-script">
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="TypeScript (Bun)"
lang={Script.language.BUN}
on:click={() => {
dispatch('new', {
language: RawScript.language.BUN,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="Python"
lang={Script.language.PYTHON3}
on:click={() => {
dispatch('new', {
language: RawScript.language.PYTHON3,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="TypeScript (Deno)"
lang={Script.language.DENO}
on:click={() => {
dispatch('new', {
language: RawScript.language.DENO,
kind,
subkind: 'flow',
summary
})
}}
/>
{#if kind != 'approval'}
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="Go"
lang={Script.language.GO}
on:click={() => {
dispatch('new', {
language: RawScript.language.GO,
kind,
subkind: 'flow',
summary
})
}}
/>
{/if}
{#if kind == 'script'}
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="Bash"
lang={Script.language.BASH}
on:click={() => {
dispatch('new', {
language: RawScript.language.BASH,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="REST"
lang={Script.language.NATIVETS}
on:click={() => {
dispatch('new', {
language: RawScript.language.NATIVETS,
kind,
subkind: 'flow',
summary
})
}}
/>
{#if !failureModule}
{#each langs as [label, lang] (lang)}
{#if displayLang(lang, kind)}
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="PostgreSQL"
lang={Script.language.POSTGRESQL}
{label}
lang={lang == 'docker' ? Script.language.BASH : lang}
on:click={() => {
dispatch('new', {
language: RawScript.language.POSTGRESQL,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="MySQL"
lang={Script.language.MYSQL}
on:click={() => {
dispatch('new', {
language: RawScript.language.MYSQL,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="BigQuery"
lang={Script.language.BIGQUERY}
on:click={() => {
dispatch('new', {
language: RawScript.language.BIGQUERY,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="Snowflake"
lang={Script.language.SNOWFLAKE}
on:click={() => {
dispatch('new', {
language: RawScript.language.SNOWFLAKE,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="MS SQL Server"
lang={Script.language.MSSQL}
on:click={() => {
dispatch('new', {
language: RawScript.language.MSSQL,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="GraphQL"
lang={Script.language.GRAPHQL}
on:click={() => {
dispatch('new', {
language: RawScript.language.GRAPHQL,
kind,
subkind: 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label={`Docker`}
lang="docker"
on:click={() => {
if (isCloudHosted()) {
sendUserToast(
'You cannot use Docker scripts on the multi-tenant platform. Use a dedicated instance or self-host windmill instead.',
true,
[
{
label: 'Learn more',
callback: () => {
window.open('https://www.windmill.dev/docs/advanced/docker', '_blank')
if (lang == 'docker') {
if (isCloudHosted()) {
sendUserToast(
'You cannot use Docker scripts on the multi-tenant platform. Use a dedicated instance or self-host windmill instead.',
true,
[
{
label: 'Learn more',
callback: () => {
window.open('https://www.windmill.dev/docs/advanced/docker', '_blank')
}
}
}
]
)
return
]
)
return
}
}
console.log(lang, kind)
dispatch('new', {
language: RawScript.language.BASH,
language: lang == 'docker' ? Script.language.BASH : lang,
kind,
subkind: 'docker',
subkind: lang == 'docker' ? 'docker' : 'flow',
summary
})
}}
/>
<FlowScriptPicker
disabled={noEditor && (summary == undefined || summary == '')}
label="PowerShell"
lang={Script.language.POWERSHELL}
on:click={() => {
dispatch('new', {
language: RawScript.language.POWERSHELL,
kind,
subkind: 'flow',
summary
})
}}
/>
<!-- <FlowScriptPicker
label={`MySQL`}
lang="mysql"
on:click={() =>
dispatch('new', { language: RawScript.language.DENO, kind, subkind: 'mysql' })}
/> -->
{/if}
{/if}
{/each}
</div>
</div>
@@ -7,15 +7,7 @@
export let disabled: boolean = false
export let label: string
export let lang:
| SupportedLanguage
| 'pgsql'
| 'mysql'
| 'javascript'
| 'fetch'
| 'docker'
| 'powershell'
| undefined = undefined
export let lang: SupportedLanguage | 'docker' | 'javascript' | undefined = undefined
export let id: string | undefined = undefined
+5 -20
View File
@@ -30,16 +30,6 @@
export let containerWidth: number = 0
let scheduleEditor: ScheduleEditor
function endedDate(started_at: string, duration_ms: number): string {
const started = new Date(started_at)
started.setMilliseconds(started.getMilliseconds() + duration_ms)
return `${started.toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})}`
}
</script>
<Portal>
@@ -93,16 +83,11 @@
<div class="flex flex-row items-center gap-1 text-gray-500 dark:text-gray-300 text-2xs">
{#if job}
{#if 'started_at' in job && job.started_at}
{#if job?.['duration_ms']}
Ended {endedDate(job.started_at, job?.['duration_ms'])}
{#if job && 'duration_ms' in job && job.duration_ms != undefined}
(Ran in {msToSec(job.duration_ms)}s)
{/if}
{:else}
<div>
Started
<TimeAgo date={job.started_at ?? ''} />
</div>
Started <TimeAgo date={job.started_at ?? ''} />
{#if job && 'duration_ms' in job && job.duration_ms != undefined}
(Ran in {msToSec(
job.duration_ms
)}s{#if job.job_kind == 'flow' || job.job_kind == 'flowpreview'}&nbsp;total{/if})
{/if}
{:else if `scheduled_for` in job && job.scheduled_for && forLater(job.scheduled_for)}
Scheduled for {displayDate(job.scheduled_for)}
@@ -23,7 +23,7 @@
const field: string | undefined = getTime(job)
if (field) {
const date = new Date(field)
date.setMilliseconds(date.getMilliseconds() + (job['duration_ms'] ?? 0))
date.setMilliseconds(date.getMilliseconds())
const day = date.toLocaleDateString('en-US', {
year: 'numeric',
-4
View File
@@ -11,10 +11,6 @@ export const HOME_SEARCH_PLACEHOLDER = 'Search Scripts, Flows & Apps'
export const SIDEBAR_SHOW_SCHEDULES = true
export const SCRIPT_SHOW_PSQL = true
export const SCRIPT_SHOW_GO = true
export const SCRIPT_SHOW_BASH = true
export const WORKSPACE_SHOW_SLACK_CMD = true
export const WORKSPACE_SHOW_WEBHOOK_CLI_SYNC = true
+1
View File
@@ -340,6 +340,7 @@ export function initialCode(
kind: Script.kind | undefined,
subkind: 'pgsql' | 'mysql' | 'flow' | 'script' | 'fetch' | 'docker' | 'powershell' | undefined
): string {
console.log(language, kind, subkind)
if (!kind) {
kind = Script.kind.SCRIPT
}
+17
View File
@@ -87,6 +87,23 @@ export function scriptPathToHref(path: string): string {
}
}
export const defaultScriptLanguages = Object.fromEntries([
[Script.language.BUN, 'TypeScript (Bun)'],
[Script.language.PYTHON3, 'Python'],
[Script.language.DENO, 'TypeScript (Deno)'],
[Script.language.BASH, 'Bash'],
[Script.language.GO, 'Go'],
[Script.language.NATIVETS, 'REST'],
[Script.language.POSTGRESQL, 'PostgreSQL'],
[Script.language.MYSQL, 'MySQL'],
[Script.language.BIGQUERY, 'BigQuery'],
[Script.language.SNOWFLAKE, 'Snowflake'],
[Script.language.MSSQL, 'MS SQL Server'],
[Script.language.GRAPHQL, 'GraphQL'],
[Script.language.POWERSHELL, 'PowerShell'],
['docker', 'Docker']
])
export async function getScriptByPath(path: string): Promise<{
content: string
language: SupportedLanguage
+2 -1
View File
@@ -1,7 +1,7 @@
import { BROWSER } from 'esm-env'
import { derived, type Readable, writable } from 'svelte/store'
import type { UserWorkspaceList } from '$lib/gen/models/UserWorkspaceList.js'
import type { TokenResponse } from './gen'
import { type WorkspaceDefaultScripts, type TokenResponse } from './gen'
import type { IntrospectionQuery } from 'graphql'
export interface UserExt {
@@ -39,6 +39,7 @@ export const userStore = writable<UserExt | undefined>(undefined)
export const workspaceStore = writable<string | undefined>(
persistedWorkspace ? String(persistedWorkspace) : undefined
)
export const defaultScripts = writable<WorkspaceDefaultScripts | undefined>(undefined)
export const dbClockDrift = writable<number | undefined>(undefined)
export const isPremiumStore = writable<boolean>(false)
export const starStore = writable(1)
+6 -2
View File
@@ -36,7 +36,11 @@ export function parseQueryParams(url: string | undefined) {
return params
}
export function displayDate(dateString: string | Date | undefined, displaySecond = false): string {
export function displayDate(
dateString: string | Date | undefined,
displaySecond = false,
displayDate = true
): string {
const date = new Date(dateString ?? '')
if (date.toString() === 'Invalid Date') {
return ''
@@ -45,7 +49,7 @@ export function displayDate(dateString: string | Date | undefined, displaySecond
hour: '2-digit',
minute: '2-digit',
second: displaySecond ? '2-digit' : undefined
})} ${date.getDate()}/${date.getMonth() + 1}`
})}${displayDate ? ` ${date.getDate()}/${date.getMonth() + 1}` : ''}`
}
}
@@ -21,7 +21,8 @@
usageStore,
userStore,
workspaceStore,
type UserExt
type UserExt,
defaultScripts
} from '$lib/stores'
import CenteredModal from '$lib/components/CenteredModal.svelte'
import { afterNavigate, beforeNavigate, goto } from '$app/navigation'
@@ -186,22 +187,32 @@
let devOnly = $page.url.pathname.startsWith('/scripts/dev')
workspaceStore.subscribe(async (value) => {
if (value) {
workspacedOpenai.init(value)
try {
copilotInfo.set(await WorkspaceService.getCopilotInfo({ workspace: value }))
} catch (err) {
copilotInfo.set({
exists_openai_resource_path: false,
code_completion_enabled: false
})
console.error('Could not get copilot info')
}
async function loadCopilot(workspace: string) {
workspacedOpenai.init(workspace)
try {
copilotInfo.set(await WorkspaceService.getCopilotInfo({ workspace }))
} catch (err) {
copilotInfo.set({
exists_openai_resource_path: false,
code_completion_enabled: false
})
console.error('Could not get copilot info')
}
}
workspaceStore.subscribe(async (workspace) => {
if (workspace) {
loadCopilot(workspace)
}
})
$: onUserStore($userStore)
$: onUserStore($userStore)
$: $workspaceStore && $userStore && loadDefaultScripts($workspaceStore, $userStore)
async function loadDefaultScripts(workspace: string, user: UserExt | undefined) {
if (!user?.operator) {
$defaultScripts = await WorkspaceService.getDefaultScripts({ workspace })
}
}
let timeout: NodeJS.Timeout | undefined
async function onUserStore(u: UserExt | undefined) {
if (u && timeout) {