From 72f0ccc0fe0e764f49a2fab85b6d42e299d94f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=A6Ltorio?= Date: Mon, 10 Jun 2024 20:38:18 +0200 Subject: [PATCH] answers to /api/software/releases/latest like GitHub --- Cargo.lock | 1 + libs/s3software/Cargo.toml | 1 + libs/s3software/src/lib.rs | 27 +++++++++++++++++ libs/state/src/database.rs | 23 +++++++++++++++ libs/state/src/state.rs | 16 +++++++++++ s3config.toml | 10 +++---- src/extended_request.rs | 45 +++++++++++++++++++++++++++++ src/lib.rs | 59 +++++++++++++++++++++++++++++++++++++- webconsole/package.json | 46 ++++++++++++++--------------- 9 files changed, 199 insertions(+), 29 deletions(-) create mode 100644 src/extended_request.rs diff --git a/Cargo.lock b/Cargo.lock index aec60a4..dcc6d91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2642,6 +2642,7 @@ dependencies = [ "aws-config", "aws-sdk-s3", "log", + "regex", "serde", "tokio", "toml 0.5.11", diff --git a/libs/s3software/Cargo.toml b/libs/s3software/Cargo.toml index 3545580..9d770e2 100644 --- a/libs/s3software/Cargo.toml +++ b/libs/s3software/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" aws-config = "1.3" aws-sdk-s3 = "1.25" log = "0.4" +regex = "1.10" serde = { version = "1.0", features = ["derive"] } toml = "0.5" tokio = { version = "1", features = ["full"] } diff --git a/libs/s3software/src/lib.rs b/libs/s3software/src/lib.rs index 23633ed..bc46a61 100644 --- a/libs/s3software/src/lib.rs +++ b/libs/s3software/src/lib.rs @@ -16,6 +16,7 @@ use aws_config::{BehaviorVersion, ConfigLoader}; use aws_sdk_s3::presigning::PresigningConfig; use aws_sdk_s3::{config::Region, Client}; +use regex::Regex; use serde::Deserialize; use std::error::Error; use std::fs; @@ -169,3 +170,29 @@ mod tests { assert_eq!(status, StatusCode::OK); } } + +pub async fn extract_version() -> Result> { + let config = get_s3_config_file().await?; + + // Create a regex to match the version number + let re = Regex::new(r"\d+\.\d+\.\d+")?; + + // List all keys containing the version number + let keys = [ + &config.s3config.windows64_key, + &config.s3config.windows32_key, + &config.s3config.osxkey, + &config.s3config.osxarm64_key, + &config.s3config.ioskey, + ]; + + // Extract the version number from the keys + for key in &keys { + if let Some(captures) = re.captures(key) { + return Ok(String::from(captures.get(0).unwrap().as_str())); + } + } + + + Err("No version found".into()) +} \ No newline at end of file diff --git a/libs/state/src/database.rs b/libs/state/src/database.rs index 2230f03..8f16bf4 100644 --- a/libs/state/src/database.rs +++ b/libs/state/src/database.rs @@ -1468,4 +1468,27 @@ impl Database { } Some(()) } + + pub async fn add_shared_address_book(&self, name: &str, owner: UserId) -> Option + { + let mut conn = self.pool.acquire().await.unwrap(); + let ab_guid = Uuid::new_v4().as_bytes().to_vec(); + let res = sqlx::query!( + r#" + INSERT OR IGNORE INTO ab(guid, name, owner, personal, info) + VALUES (?, ?, ?, 0, '{}') + "#, + ab_guid, + name, + owner + ) + .execute(&mut conn) + .await; + if res.is_err() { + log::error!("add_shared_address_book error: {:?}", res); + return None; + } + + Some(guid_into_uuid(ab_guid)?) + } } diff --git a/libs/state/src/state.rs b/libs/state/src/state.rs index 2061a30..728565b 100644 --- a/libs/state/src/state.rs +++ b/libs/state/src/state.rs @@ -676,4 +676,20 @@ impl ApiState { pub async fn add_ab_rule(&self, rule: AbRule) -> Option<()> { self.db.add_ab_rule(rule).await } + + /// Add a shared address book given its name and its owner + /// It returns the guid of the shared address book + /// + /// # Arguments + /// + /// - `name` - The name of the shared address book + /// + /// - `owner` - The owner of the shared address book + /// + /// # Returns + /// + /// - `Option` - The guid of the shared address book + pub async fn add_shared_address_book(&self, name: &str, owner: UserId) -> Option { + self.db.add_shared_address_book(name, owner).await + } } diff --git a/s3config.toml b/s3config.toml index 129b859..3967032 100644 --- a/s3config.toml +++ b/s3config.toml @@ -4,8 +4,8 @@ Region = "eu-london-1" AccessKey = "c324ead11faa0d87337c07ddc4a1129fab76188d" SecretKey = "GJurV55f/LD36kjZFpchZMj/uvgTqxHyFkBchUUa8KA=" Bucket = "aezoz24elapn" -Windows64Key = "master/sctgdesk-releases/sctgdesk-1.2.4-x86_64.exe" -Windows32Key = "master/sctgdesk-releases/sctgdesk-1.2.4-i686.exe" -OSXKey = "master/sctgdesk-releases/sctgdesk-1.2.4.dmg" -OSXArm64Key = "master/sctgdesk-releases/sctgdesk-1.2.4.dmg" -IOSKey = "master/sctgdesk-releases/sctgdesk-1.2.4.ipa" \ No newline at end of file +Windows64Key = "master/sctgdesk-releases/sctgdesk-1.2.6-x86_64.exe" +Windows32Key = "master/sctgdesk-releases/sctgdesk-1.2.6-i686.exe" +OSXKey = "master/sctgdesk-releases/sctgdesk-1.2.6.dmg" +OSXArm64Key = "master/sctgdesk-releases/sctgdesk-1.2.6.dmg" +IOSKey = "master/sctgdesk-releases/sctgdesk-1.2.6.ipa" \ No newline at end of file diff --git a/src/extended_request.rs b/src/extended_request.rs new file mode 100644 index 0000000..0132545 --- /dev/null +++ b/src/extended_request.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2024 Ronan LE MEILLAT for SCTG Development +// +// This file is part of the SCTGDesk project. +// +// SCTGDesk is free software: you can redistribute it and/or modify +// it under the terms of the Affero General Public License version 3 as +// published by the Free Software Foundation. +// +// SCTGDesk is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Affero General Public License for more details. +// +// You should have received a copy of the Affero General Public License +// along with SCTGDesk. If not, see . +use rocket::request::{FromRequest, Outcome, Request}; +use std::{collections::HashMap, convert::Infallible}; +use rocket_okapi::request::OpenApiFromRequest; +use rocket_okapi::gen::OpenApiGenerator; +use rocket_okapi::request::RequestHeaderInput; +use rocket_okapi::OpenApiError; + +pub struct ExtendedRequest{ + pub headers: HashMap, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for ExtendedRequest { + type Error = Infallible; + + async fn from_request(request: &'r Request<'_>) -> Outcome { + let headers_src = request.headers().clone(); + let mut headers:HashMap = HashMap::new(); + headers_src.iter().for_each(|k|{ + headers.insert(k.name.to_string().to_lowercase(),k.value.to_string().to_lowercase()); + }); + Outcome::Success(Self { headers }) + } +} + +impl OpenApiFromRequest<'_> for ExtendedRequest { + fn from_request_input(_: &mut OpenApiGenerator, _: std::string::String, _: bool) -> Result { + Ok(RequestHeaderInput::None) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index bbd6b46..7e4eb9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ // along with SCTGDesk. If not, see . mod api; mod extended_json; +mod extended_request; use std::collections::HashMap; use std::env; @@ -24,6 +25,7 @@ use std::sync::Arc; use api::ActionResponse; use extended_json::ExtendedJson; +use extended_request::ExtendedRequest; use oauth2::oauth_provider::OAuthProvider; use oauth2::oauth_provider::OAuthProviderFactory; use rocket::fairing::{Fairing, Info, Kind}; @@ -49,6 +51,7 @@ use utils::{ AbSharedProfilesResponse, AbTag, BearerAuthToken, OidcAuthRequest, OidcAuthUrl, OidcResponse, OidcState, OidcUser, OidcUserInfo, OidcUserStatus, }; +use s3software::extract_version; use base64::prelude::{Engine as _, BASE64_STANDARD}; use rocket::{ @@ -57,7 +60,7 @@ use rocket::{ use state::{ApiState, UserPasswordInfo}; use utils::{ include_png_as_base64, unwrap_or_return, uuid_into_guid, AbTagRenameRequest, AddUserRequest, - AddressBook, EnableUserRequest, Group, GroupsResponse, OidcSettingsResponse, PeersResponse, + AddressBook, EnableUserRequest, GroupsResponse, OidcSettingsResponse, PeersResponse, SoftwareResponse, SoftwareVersionResponse, UpdateUserRequest, UserList, }; use utils::{ @@ -154,6 +157,8 @@ pub async fn build_rocket(figment: Figment) -> Rocket { ab_rule_delete, software, software_version, + software_releases_latest, + software_releases_tag, webconsole_index, webconsole_index_html, // webconsole_assets, @@ -1791,6 +1796,58 @@ async fn software_version() -> Json { Json(response) } +/// # Retrieve the client version +/// +/// This function is an API endpoint that retrieves the version of the client. +/// It copies the GitHub method of retrieving the latest release version. +/// It is tagged with "software" for OpenAPI documentation. +/// +/// It can be used by replacing the check_software_update() from the client. +/// You can find the client code at rustdesk/src/common.rs +/// ## Returns +/// +/// Returns in the location header the URL of the latest release. +/// something like https://api-server/api/releases/tag/1.2.6 +#[openapi(tag = "software")] +#[get("/api/software/releases/latest")] +async fn software_releases_latest( + request: ExtendedRequest, +) -> Redirect { + log::debug!("software_releases_latest"); + let headers = request.headers; + let host = get_host(headers); + let version = extract_version().await.map_err(|e| status::NotFound(Box::new(e))); + if version.is_err() { + return Redirect::to(format!("{}/api/software/releases/0.0.0",host)); + } + let version = version.unwrap(); + let url = format!("{}/api/releases/tag/{}",host, version); + Redirect::to(url) +} + +/// # Simulate GitHub API for releases +/// +/// This function is an API endpoint that simulates the GitHub API for releases. +/// +/// ## Parameters +/// +/// - `version`: The version of the release. +/// +/// ## Returns +/// +/// Returns a `Json` object containing the version of the release. +#[openapi(tag = "software")] +#[get("/api/releases/tag/")] +async fn software_releases_tag( + version: &str, +) -> Result, status::NotFound<()>> { + log::debug!("software_releases_tag"); + let response = SoftwareVersionResponse { + server: None, + client: Some(version.to_string()), + }; + Ok(Json(response)) +} /// # List the rules /// /// This function is an API endpoint that lists the rules attached to a shared address book. diff --git a/webconsole/package.json b/webconsole/package.json index 0b8487a..cd54ed6 100644 --- a/webconsole/package.json +++ b/webconsole/package.json @@ -1,46 +1,46 @@ { "name": "template-vitejs-vue3-tailwindcss-fontawesome", "private": true, - "version": "1.1.99-29", + "version": "0.1.0", "type": "module", "scripts": { - "devserver": "npx nodemon -V -w ./src -e js,vue,ts,css,html --exec 'npm run build && node devserver.js'", "dev": "vite", - "build": "vite build && gulp licenses", + "create-cert": "openssl req -x509 -newkey rsa:4096 -keyout localhost.key -out localhost.pem -sha256 -nodes -days 365", "preview": "vite preview", - "create-cert": "openssl req -x509 -newkey rsa:4096 -keyout localhost.key -out localhost.pem -sha256 -nodes -days 365" + "devserver": "npx nodemon -V -w ./src -e js,vue,ts,css,html --exec 'npm run build && node devserver.js'", + "build": "vite build && gulp licenses" }, "dependencies": { - "vue-router": "^4.3.2", + "@pqina/flip": "^1.8.3", "pinia": "^2.1.7", + "jdenticon": "^3.3.0", "@heroicons/vue": "^2.1.3", "axios": "^1.7.2", - "jdenticon": "^3.3.0", - "@headlessui/vue": "^1.7.22", "vue": "^3.4.27", - "@pqina/flip": "^1.8.3" + "@headlessui/vue": "^1.7.22", + "vue-router": "^4.3.2" }, "devDependencies": { - "nodemon": "^3.1.0", - "@types/glob": "^8.1.0", - "@vitejs/plugin-vue": "^5.0.4", - "typescript": "^5.4.5", - "express": "^4.19.2", + "sass": "^1.77.2", "vite": "^5.2.11", - "vite-plugin-static-copy": "^1.0.5", - "tailwindcss": "^3.4.3", - "gulp": "^5.0.0", - "gulp-if": "^3.0.0", "@fullhuman/postcss-purgecss": "^6.0.0", - "npm-check-updates": "^16.14.20", - "ts-node": "^10.9.2", - "autoprefixer": "^10.4.19", "postcss-purgefonts": "^1.0.2", - "glob": "10.4.1", - "sass": "^1.77.2", + "autoprefixer": "^10.4.19", + "typescript": "^5.4.5", + "npm-check-updates": "^16.14.20", + "gulp-if": "^3.0.0", + "tailwindcss": "^3.4.3", "gulp-append-prepend": "^1.0.9", "@tailwindcss/typography": "^0.5.13", + "glob": "10.4.1", + "postcss": "^8.4.38", + "ts-node": "^10.9.2", + "nodemon": "^3.1.0", + "@types/glob": "^8.1.0", + "express": "^4.19.2", + "vite-plugin-static-copy": "^1.0.5", "@tailwindcss/forms": "^0.5.7", - "postcss": "^8.4.38" + "@vitejs/plugin-vue": "^5.0.4", + "gulp": "^5.0.0" } } \ No newline at end of file