diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 13210342f6..2c8dc4c7e9 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -7769,23 +7769,6 @@ paths: items: $ref: "#/components/schemas/ListableRawApp" - /w/{workspace}/raw_apps/exists/{path}: - get: - summary: does an app exisst at path - operationId: existsRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app exists - content: - application/json: - schema: - type: boolean - /w/{workspace}/apps/get_data/v/{secretWithExtension}: get: summary: get raw app data by @@ -8215,88 +8198,6 @@ paths: schema: $ref: "#/components/schemas/AppWithLastVersion" - /w/{workspace}/raw_apps/create: - post: - summary: create raw app - operationId: createRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - requestBody: - description: new raw app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - value: - type: string - summary: - type: string - required: - - path - - value - - summary - responses: - "201": - description: raw app created - content: - text/plain: - schema: - type: string - - /w/{workspace}/raw_apps/update/{path}: - post: - summary: update app - operationId: updateRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/ScriptPath" - requestBody: - description: updateraw app - required: true - content: - application/json: - schema: - type: object - properties: - path: - type: string - summary: - type: string - value: - type: string - responses: - "200": - description: app updated - content: - text/plain: - schema: - type: string - - /w/{workspace}/raw_apps/delete/{path}: - delete: - summary: delete raw app - operationId: deleteRawApp - tags: - - raw_app - parameters: - - $ref: "#/components/parameters/WorkspaceId" - - $ref: "#/components/parameters/Path" - responses: - "200": - description: app deleted - content: - text/plain: - schema: - type: string - /w/{workspace}/apps/delete/{path}: delete: summary: delete app diff --git a/backend/windmill-api/src/raw_apps.rs b/backend/windmill-api/src/raw_apps.rs index 8aa1650034..e331aa1176 100644 --- a/backend/windmill-api/src/raw_apps.rs +++ b/backend/windmill-api/src/raw_apps.rs @@ -5,42 +5,29 @@ * Please see the included NOTICE for copyright information and * LICENSE-AGPL for a copy of the license. */ -use crate::{ - db::{ApiAuthed, DB}, - users::require_owner_of_path, - utils::check_scopes, - webhook_util::{WebhookMessage, WebhookShared}, -}; +use crate::{db::ApiAuthed, utils::check_scopes}; use axum::{ body::Body, extract::{Extension, Json, Path, Query}, response::Response, - routing::{delete, get, post}, + routing::get, Router, }; -use hyper::{header, StatusCode}; +use hyper::header; use serde::{Deserialize, Serialize}; use sql_builder::{bind::Bind, SqlBuilder}; use sqlx::FromRow; -use std::str; -use windmill_audit::audit_oss::audit_log; -use windmill_audit::ActionKind; use windmill_common::{ apps::ListAppQuery, db::UserDB, error::{Error, JsonResult, Result}, utils::{not_found_if_none, paginate, Pagination, StripPath}, - worker::CLOUD_HOSTED, }; pub fn workspaced_service() -> Router { Router::new() .route("/list", get(list_apps)) .route("/get_data/:version/*path", get(get_data)) - .route("/exists/*path", get(exists_app)) - .route("/update/*path", post(update_app)) - .route("/delete/*path", delete(delete_app)) - .route("/create", post(create_app)) } #[derive(FromRow, Deserialize, Serialize)] @@ -54,20 +41,6 @@ pub struct ListableApp { pub version: i32, } -#[derive(Deserialize)] -pub struct CreateApp { - pub path: String, - pub summary: String, - pub value: String, -} - -#[derive(Deserialize)] -pub struct EditApp { - pub path: Option, - pub summary: Option, - pub value: Option, -} - async fn list_apps( authed: ApiAuthed, Extension(user_db): Extension, @@ -147,230 +120,3 @@ async fn get_data( Ok(res.body(Body::from(app)).unwrap()) } - -async fn create_app( - authed: ApiAuthed, - Extension(user_db): Extension, - Extension(webhook): Extension, - Extension(db): Extension, - Path(w_id): Path, - Json(app): Json, -) -> Result<(StatusCode, String)> { - if authed.is_operator { - return Err(Error::NotAuthorized( - "Operators cannot create raw apps for security reasons".to_string(), - )); - } - check_scopes(&authed, || format!("raw_apps:write:{}", app.path))?; - if *CLOUD_HOSTED { - let nb_apps = sqlx::query_scalar!( - "SELECT COUNT(*) FROM raw_app WHERE workspace_id = $1", - &w_id - ) - .fetch_one(&db) - .await?; - if nb_apps.unwrap_or(0) >= 1000 { - return Err(Error::BadRequest( - "You have reached the maximum number of apps (1000) on cloud. Contact support@windmill.dev to increase the limit" - .to_string(), - )); - } - if app.summary.len() > 300 { - return Err(Error::BadRequest( - "Summary must be less than 300 characters on cloud".to_string(), - )); - } - } - let mut tx = user_db.begin(&authed).await?; - if &app.path == "" { - return Err(Error::BadRequest("App path cannot be empty".to_string())); - } - - let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM raw_app WHERE path = $1 AND workspace_id = $2)", - app.path, - w_id - ) - .fetch_one(&mut *tx) - .await? - .unwrap_or(false); - - if exists { - return Err(Error::BadRequest(format!( - "App with path {} already exists", - &app.path - ))); - } - - sqlx::query!( - "INSERT INTO raw_app - (workspace_id, path, summary, extra_perms, data) - VALUES ($1, $2, $3, '{}', $4)", - w_id, - app.path, - app.summary, - app.value, - ) - .execute(&mut *tx) - .await?; - - audit_log( - &mut *tx, - &authed, - "apps.create", - ActionKind::Create, - &w_id, - Some(&app.path), - None, - ) - .await?; - - tx.commit().await?; - webhook.send_message( - w_id.clone(), - WebhookMessage::CreateApp { workspace: w_id, path: app.path.clone() }, - ); - - Ok((StatusCode::CREATED, app.path)) -} - -async fn delete_app( - authed: ApiAuthed, - Extension(user_db): Extension, - Extension(webhook): Extension, - Path((w_id, path)): Path<(String, StripPath)>, -) -> Result { - let path = path.to_path(); - check_scopes(&authed, || format!("raw_apps:write:{}", path))?; - let mut tx = user_db.begin(&authed).await?; - - sqlx::query!( - "DELETE FROM raw_app WHERE path = $1 AND workspace_id = $2", - path, - w_id - ) - .execute(&mut *tx) - .await?; - audit_log( - &mut *tx, - &authed, - "apps.delete", - ActionKind::Delete, - &w_id, - Some(path), - None, - ) - .await?; - tx.commit().await?; - webhook.send_message( - w_id.clone().clone(), - WebhookMessage::DeleteApp { workspace: w_id, path: path.to_owned() }, - ); - - Ok(format!("app {} deleted", path)) -} - -async fn update_app( - authed: ApiAuthed, - Extension(user_db): Extension, - Extension(webhook): Extension, - Path((w_id, path)): Path<(String, StripPath)>, - Json(app): Json, -) -> Result { - if authed.is_operator { - return Err(Error::NotAuthorized( - "Operators cannot update raw apps for security reasons".to_string(), - )); - } - use sql_builder::prelude::*; - - let path = path.to_path(); - check_scopes(&authed, || format!("raw_apps:write:{}", path))?; - - let mut tx = user_db.begin(&authed).await?; - let mut sqlb = SqlBuilder::update_table("raw_app"); - sqlb.and_where_eq("path", "?".bind(&path)); - sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); - - let npath = &app.path; - if npath.is_some() || app.summary.is_some() { - if let Some(npath) = npath { - if npath != path { - require_owner_of_path(&authed, path)?; - - let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM app WHERE path = $1 AND workspace_id = $2)", - npath, - w_id - ) - .fetch_one(&mut *tx) - .await? - .unwrap_or(false); - - if exists { - return Err(Error::BadRequest(format!( - "App with path {} already exists", - npath - ))); - } - } - sqlb.set_str("path", npath); - } - - if let Some(nsummary) = &app.summary { - sqlb.set_str("summary", nsummary); - } - } - - if let Some(value) = &app.value { - sqlb.set_str("data", value); - sqlb.set("version", "version + 1"); - } - - sqlb.returning("path"); - - let sql = sqlb.sql().map_err(|e| Error::internal_err(e.to_string()))?; - let npath_o: Option = sqlx::query_scalar(&sql).fetch_optional(&mut *tx).await?; - not_found_if_none(npath_o, "Raw App", path)?; - - let npath = app.path.clone().unwrap_or_else(|| path.to_owned()); - audit_log( - &mut *tx, - &authed, - "apps.update", - ActionKind::Update, - &w_id, - Some(&path), - None, - ) - .await?; - tx.commit().await?; - webhook.send_message( - w_id.clone(), - WebhookMessage::UpdateApp { - workspace: w_id, - old_path: path.to_owned(), - new_path: npath.clone(), - }, - ); - - Ok(format!("app {} updated (npath: {:?})", path, npath)) -} - -async fn exists_app( - Extension(db): Extension, - Path((w_id, path)): Path<(String, StripPath)>, -) -> JsonResult { - let path = path.to_path(); - // Note: exists_app doesn't require authentication, so no scope check needed - let exists = sqlx::query_scalar!( - "SELECT EXISTS(SELECT 1 FROM raw_app WHERE path = $1 AND workspace_id = $2)", - path, - w_id - ) - .fetch_one(&db) - .await? - .unwrap_or(false); - - Ok(Json(exists)) -} diff --git a/frontend/src/lib/components/CompareWorkspaces.svelte b/frontend/src/lib/components/CompareWorkspaces.svelte index 2546868041..4d36600295 100644 --- a/frontend/src/lib/components/CompareWorkspaces.svelte +++ b/frontend/src/lib/components/CompareWorkspaces.svelte @@ -16,7 +16,6 @@ AppService, FlowService, FolderService, - RawAppService, ResourceService, ScheduleService, ScriptService, @@ -198,21 +197,6 @@ path: path }) return resource.schema - } else if (kind == 'raw_app') { - throw new Error('Raw app deploy not implemented yet') - // const app = await RawAppService.getRawAppData({ - // workspace: workspace, - // path: path - // }) - // if (alreadyExists) { - // } - // await RawAppService.updateRawApp({ - // workspace: workspace, - // path: path, - // requestBody: { - // path: path - // } - // }) } else if (kind == 'folder') { const folder = await FolderService.getFolder({ workspace: workspace, @@ -293,11 +277,6 @@ workspace: workspace, path: path }) - } else if (kind == 'raw_app') { - exists = await RawAppService.existsRawApp({ - workspace: workspace, - path: path - }) } else if (kind == 'variable') { exists = await VariableService.existsVariable({ workspace: workspace, @@ -573,21 +552,6 @@ } }) } - } else if (kind == 'raw_app') { - throw new Error('Raw app deploy not implemented yet') - // const app = await RawAppService.getRawAppData({ - // workspace: workspaceFrom, - // path: path - // }) - // if (alreadyExists) { - // } - // await RawAppService.updateRawApp({ - // workspace: workspaceFrom, - // path: path, - // requestBody: { - // path: path - // } - // }) } else if (kind == 'folder') { await FolderService.createFolder({ workspace: workspaceToDeployTo, diff --git a/frontend/src/lib/components/DeployWorkspace.svelte b/frontend/src/lib/components/DeployWorkspace.svelte index c1d1871604..60165f8c54 100644 --- a/frontend/src/lib/components/DeployWorkspace.svelte +++ b/frontend/src/lib/components/DeployWorkspace.svelte @@ -6,7 +6,6 @@ AppService, FlowService, FolderService, - RawAppService, ResourceService, ScheduleService, ScriptService, @@ -232,11 +231,6 @@ workspace: workspaceToDeployTo!, path: path }) - } else if (kind == 'raw_app') { - exists = await RawAppService.existsRawApp({ - workspace: workspaceToDeployTo!, - path: path - }) } else if (kind == 'variable') { exists = await VariableService.existsVariable({ workspace: workspaceToDeployTo!, @@ -498,21 +492,6 @@ } }) } - } else if (kind == 'raw_app') { - throw new Error('Raw app deploy not implemented yet') - // const app = await RawAppService.getRawAppData({ - // workspace: $workspaceStore!, - // path: path - // }) - // if (alreadyExists) { - // } - // await RawAppService.updateRawApp({ - // workspace: $workspaceStore!, - // path: path, - // requestBody: { - // path: path - // } - // }) } else if (kind == 'folder') { await FolderService.createFolder({ workspace: workspaceToDeployTo!, @@ -616,21 +595,6 @@ path: path }) return resource.schema - } else if (kind == 'raw_app') { - throw new Error('Raw app deploy not implemented yet') - // const app = await RawAppService.getRawAppData({ - // workspace: workspace, - // path: path - // }) - // if (alreadyExists) { - // } - // await RawAppService.updateRawApp({ - // workspace: workspace, - // path: path, - // requestBody: { - // path: path - // } - // }) } else if (kind == 'folder') { const folder = await FolderService.getFolder({ workspace: workspace, diff --git a/frontend/src/lib/components/MoveDrawer.svelte b/frontend/src/lib/components/MoveDrawer.svelte index 2979987629..493cab3cc3 100644 --- a/frontend/src/lib/components/MoveDrawer.svelte +++ b/frontend/src/lib/components/MoveDrawer.svelte @@ -4,12 +4,12 @@ import { Alert, Button, Drawer } from './common' import DrawerContent from './common/drawer/DrawerContent.svelte' import Path from './Path.svelte' - import { AppService, FlowService, RawAppService, ScriptService } from '$lib/gen' + import { AppService, FlowService, ScriptService } from '$lib/gen' import { isOwner } from '$lib/utils' const dispatch = createEventDispatcher() - type Kind = 'script' | 'resource' | 'schedule' | 'variable' | 'flow' | 'app' | 'raw_app' + type Kind = 'script' | 'resource' | 'schedule' | 'variable' | 'flow' | 'app' let kind: Kind let initialPath: string = '' @@ -83,15 +83,6 @@ summary } }) - } else if (kind == 'raw_app') { - await RawAppService.updateRawApp({ - workspace: $workspaceStore!, - path: initialPath, - requestBody: { - path: path != initialPath ? path : undefined, - summary - } - }) } dispatch('update', path) drawer.closeDrawer() diff --git a/frontend/src/lib/components/common/table/RawAppRow.svelte b/frontend/src/lib/components/common/table/RawAppRow.svelte index 7f27176d5c..76019553f1 100644 --- a/frontend/src/lib/components/common/table/RawAppRow.svelte +++ b/frontend/src/lib/components/common/table/RawAppRow.svelte @@ -1,20 +1,13 @@ -{#if menuOpen} - - updateAppDrawer?.toggleDrawer?.()}> - { - await RawAppService.updateRawApp({ - workspace: $workspaceStore ?? '', - path: app.path, - requestBody: { value: detail?.[0] } - }) - goto(`/apps/get_raw/${app.version + 1}/${app.path}`) - }} - /> - - -{/if} {/snippet} {#snippet actions()} - { - let { summary, path, canWrite } = app + let { path, canWrite } = app return [ - { - displayName: 'Move/Rename', - icon: FolderOpen, - action: () => { - moveDrawer.openDrawer(path, summary, 'raw_app') - }, - disabled: !canWrite - }, ...(isDeployable('app', path, await getDeployUiSettings()) ? [ { @@ -126,25 +66,6 @@ action: () => { shareModal.openDrawer && shareModal.openDrawer(path, 'raw_app') } - }, - { - displayName: 'Delete', - icon: Trash, - action: async (event) => { - // TODO - // @ts-ignore - if (event?.shiftKey) { - await RawAppService.deleteRawApp({ workspace: $workspaceStore ?? '', path }) - dispatch('change') - } else { - deleteConfirmedCallback = async () => { - await RawAppService.deleteRawApp({ workspace: $workspaceStore ?? '', path }) - dispatch('change') - } - } - }, - type: 'delete', - disabled: !canWrite } ] }} diff --git a/frontend/src/lib/components/home/Item.svelte b/frontend/src/lib/components/home/Item.svelte index a470b74e1f..5e5c7f00c3 100644 --- a/frontend/src/lib/components/home/Item.svelte +++ b/frontend/src/lib/components/home/Item.svelte @@ -72,11 +72,8 @@ /> {:else if item.type == 'raw_app'} dispatch('rawAppChanged')} app={item} - {moveDrawer} {shareModal} {deploymentDrawer} {depth} diff --git a/frontend/src/lib/components/home/ItemsList.svelte b/frontend/src/lib/components/home/ItemsList.svelte index 787431b300..d4e77a12c7 100644 --- a/frontend/src/lib/components/home/ItemsList.svelte +++ b/frontend/src/lib/components/home/ItemsList.svelte @@ -9,8 +9,7 @@ type Script, ScriptService, type Flow, - type ListableRawApp, - RawAppService + type ListableRawApp } from '$lib/gen' import { userStore, workspaceStore } from '$lib/stores' import type uFuzzy from '@leeoniya/ufuzzy' @@ -134,17 +133,7 @@ } async function loadRawApps(): Promise { - raw_apps = (await RawAppService.listRawApps({ workspace: $workspaceStore! })).map( - (app: ListableRawApp) => { - return { - canWrite: - canWrite(app.path!, app.extra_perms!, $userStore) && - app.workspace_id == $workspaceStore && - !$userStore?.operator, - ...app - } - } - ) + raw_apps = [] loading = false } diff --git a/frontend/src/lib/components/search/GlobalSearchModal.svelte b/frontend/src/lib/components/search/GlobalSearchModal.svelte index 51ddca9217..3fa32d6db9 100644 --- a/frontend/src/lib/components/search/GlobalSearchModal.svelte +++ b/frontend/src/lib/components/search/GlobalSearchModal.svelte @@ -3,7 +3,6 @@ import { AppService, FlowService, - RawAppService, ScriptService, type Flow, type ListableApp, @@ -492,7 +491,6 @@ withoutDescription: true }) const apps = await AppService.listApps({ workspace: $workspaceStore! }) - const raw_apps = await RawAppService.listRawApps({ workspace: $workspaceStore! }) let combinedItems: (TableScript | TableFlow | TableApp | TableRawApp)[] | undefined = [ ...flows.map((x) => ({ @@ -512,12 +510,6 @@ type: 'app' as 'app', time: new Date(x.edited_at).getTime(), search_id: x.path - })), - ...raw_apps.map((x) => ({ - ...x, - type: 'raw_app' as 'raw_app', - time: new Date(x.edited_at).getTime(), - search_id: x.path })) ].sort((a, b) => (a.starred != b.starred ? (a.starred ? -1 : 1) : a.time - b.time > 0 ? -1 : 1)) diff --git a/frontend/src/routes/(root)/(logged)/+layout.svelte b/frontend/src/routes/(root)/(logged)/+layout.svelte index 256b15053d..b534153981 100644 --- a/frontend/src/routes/(root)/(logged)/+layout.svelte +++ b/frontend/src/routes/(root)/(logged)/+layout.svelte @@ -6,7 +6,6 @@ AssetService, FlowService, OpenAPI, - RawAppService, ScriptService, SettingService, UserService, @@ -192,10 +191,6 @@ workspace: $workspaceStore ?? '', starredOnly: true }) - const raw_apps = await RawAppService.listRawApps({ - workspace: $workspaceStore ?? '', - starredOnly: true - }) const assets = await AssetService.listFavoriteAssets({ workspace: $workspaceStore ?? '' }) favoriteManager.current = [ ...scripts.map((s) => ({ @@ -216,12 +211,6 @@ href: getFavoriteHref(f.path, 'app'), kind: 'app' as const })), - ...raw_apps.map((f) => ({ - label: f.summary || getFavoriteLabel(f.path, 'raw_app'), - path: f.path, - href: getFavoriteHref(f.path, 'raw_app'), - kind: 'raw_app' as const - })), ...assets.map((a) => ({ label: getFavoriteLabel(a.path, 'asset'), path: a.path,