diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 0490becd21..81bb04dc93 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -41,6 +41,20 @@ paths: schema: type: string + /uptodate: + get: + summary: is backend up to date + operationId: backendUptodate + tags: + - settings + responses: + "200": + description: is backend up to date + content: + text/plain: + schema: + type: string + /ee_license: get: summary: get license id diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index ccd3dd2c79..3c530d53ea 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -30,6 +30,8 @@ use tower_http::{ }; use windmill_common::utils::rd_string; +use windmill_common::error::AppError; + mod apps; mod audit; mod capture; @@ -189,6 +191,7 @@ pub async fn run_server( ) .nest("/oauth", oauth2::global_service()) .route("/version", get(git_v)) + .route("/uptodate", get(is_up_to_date)) .route("/ee_license", get(ee_license)) .route("/openapi.yaml", get(openapi)), ) @@ -211,6 +214,31 @@ pub async fn run_server( Ok(()) } +async fn is_up_to_date() -> Result { + let error_reading_version = || anyhow::anyhow!("Error reading latest released version"); + let version = HTTP_CLIENT + .get("https://api.github.com/repos/windmill-labs/windmill/releases/latest") + .send() + .await? + .json::() + .await? + .get("tag_name") + .ok_or_else(error_reading_version)? + .as_str() + .ok_or_else(error_reading_version)? + .to_string(); + let release = GIT_VERSION + .split('-') + .next() + .ok_or_else(error_reading_version)? + .to_string(); + if version == release { + Ok("yes".to_string()) + } else { + Ok(format!("Update: {GIT_VERSION} -> {version}")) + } +} + #[cfg(feature = "enterprise")] async fn git_v() -> String { format!("EE {GIT_VERSION}") diff --git a/backend/windmill-common/src/error.rs b/backend/windmill-common/src/error.rs index 54ac50c5e5..7fa890f5be 100644 --- a/backend/windmill-common/src/error.rs +++ b/backend/windmill-common/src/error.rs @@ -6,6 +6,7 @@ * LICENSE-AGPL for a copy of the license. */ +use axum::response::Response; #[cfg(feature = "axum")] use axum::{ body::{self, BoxBody}, @@ -107,3 +108,28 @@ impl OrElseNotFound for Option { self.ok_or_else(|| Error::NotFound(s.to_string())) } } + +// Make our own error that wraps `anyhow::Error`. +pub struct AppError(anyhow::Error); + +// Tell axum how to convert `AppError` into a response. +impl IntoResponse for AppError { + fn into_response(self) -> Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} diff --git a/frontend/src/lib/components/CenteredModal.svelte b/frontend/src/lib/components/CenteredModal.svelte index 63c474d402..6fbc317036 100644 --- a/frontend/src/lib/components/CenteredModal.svelte +++ b/frontend/src/lib/components/CenteredModal.svelte @@ -1,16 +1,11 @@
@@ -42,7 +37,9 @@
-
- {version} +
+ +
Windmill
+
diff --git a/frontend/src/lib/components/SuperadminSettings.svelte b/frontend/src/lib/components/SuperadminSettings.svelte index d564e16041..9f2a0d28d8 100644 --- a/frontend/src/lib/components/SuperadminSettings.svelte +++ b/frontend/src/lib/components/SuperadminSettings.svelte @@ -1,5 +1,5 @@ + +{#if uptodate} + {uptodate}   + How to update?
+ - docker: `docker compose up`
- + helm +
+{/if} diff --git a/frontend/src/lib/components/UserSettings.svelte b/frontend/src/lib/components/UserSettings.svelte index 4736a37687..dde9d9aa89 100644 --- a/frontend/src/lib/components/UserSettings.svelte +++ b/frontend/src/lib/components/UserSettings.svelte @@ -1,7 +1,7 @@ + +{version} diff --git a/frontend/src/routes/(root)/(logged)/user/(user)/login/+page.svelte b/frontend/src/routes/(root)/(logged)/user/(user)/login/+page.svelte index 13bb97ec5c..8a6278e3bb 100644 --- a/frontend/src/routes/(root)/(logged)/user/(user)/login/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/user/(user)/login/+page.svelte @@ -3,7 +3,7 @@ import { page } from '$app/stores' import { faGithub, faGitlab, faGoogle, faMicrosoft } from '@fortawesome/free-brands-svg-icons' import { onMount } from 'svelte' - import { OauthService, SettingsService, UserService, WorkspaceService } from '$lib/gen' + import { OauthService, UserService, WorkspaceService } from '$lib/gen' import { clearStores, usersWorkspaceStore, workspaceStore, userStore } from '$lib/stores' import { classNames, parseQueryParams } from '$lib/utils' import { getUserExt } from '$lib/user' @@ -12,6 +12,8 @@ import { sendUserToast } from '$lib/toast' import { isCloudHosted } from '$lib/cloud' import { refreshSuperadmin } from '$lib/refreshUser' + import Version from '$lib/components/Version.svelte' + import Uptodate from '$lib/components/Uptodate.svelte' let email = $page.url.searchParams.get('email') ?? '' let password = $page.url.searchParams.get('password') ?? '' @@ -151,17 +153,11 @@ $: if (error) { sendUserToast(error, true) } - - let version = '' - - onMount(async () => { - version = await SettingsService.backendVersion() - })
- {version} +