diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93641205b..a8804b63b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -209,6 +209,12 @@ jobs: name: satellite.did path: . + - name: Download satellite_extension.did + uses: actions/download-artifact@v4 + with: + name: satellite_extension.did + path: . + - name: Download orbiter.did uses: actions/download-artifact@v4 with: diff --git a/Cargo.lock b/Cargo.lock index 6f44d89a8..4e20c9a93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -826,7 +826,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "junobuild-collections" -version = "0.0.7" +version = "0.0.8" dependencies = [ "candid", "ic-cdk", @@ -852,7 +852,7 @@ dependencies = [ [[package]] name = "junobuild-satellite" -version = "0.0.20-patch.3" +version = "0.0.20-patch.4" dependencies = [ "candid", "ciborium", @@ -875,7 +875,7 @@ dependencies = [ [[package]] name = "junobuild-shared" -version = "0.0.20" +version = "0.0.21" dependencies = [ "candid", "ciborium", @@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "junobuild-storage" -version = "0.0.10" +version = "0.0.11" dependencies = [ "base64 0.13.1", "candid", diff --git a/package-lock.json b/package-lock.json index 1b4ad1081..1b807d1c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@junobuild/juno", - "version": "0.0.34", + "version": "0.0.35", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@junobuild/juno", - "version": "0.0.34", + "version": "0.0.35", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@dfinity/agent": "^2.0.0", diff --git a/package.json b/package.json index 3b3dd655f..8e423b165 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@junobuild/juno", - "version": "0.0.34", + "version": "0.0.35", "private": true, "author": "David Dal Busco ", "license": "SEE LICENSE IN LICENSE.md", diff --git a/src/frontend/src/lib/api/mission-control.api.ts b/src/frontend/src/lib/api/mission-control.api.ts index 19f005dba..3e44b0daf 100644 --- a/src/frontend/src/lib/api/mission-control.api.ts +++ b/src/frontend/src/lib/api/mission-control.api.ts @@ -344,6 +344,21 @@ export const unsetOrbiter = async ({ return unset_orbiter(orbiterId); }; +export const setSatellite = async ({ + missionControlId, + satelliteId, + satelliteName, + identity +}: { + missionControlId: Principal; + satelliteId: Principal; + satelliteName?: string; + identity: OptionIdentity; +}): Promise => { + const { set_satellite } = await getMissionControlActor({ missionControlId, identity }); + return set_satellite(satelliteId, toNullable(satelliteName)); +}; + export const unsetSatellite = async ({ missionControlId, satelliteId, diff --git a/src/frontend/src/lib/components/analytics/NoAnalytics.svelte b/src/frontend/src/lib/components/analytics/NoAnalytics.svelte index 7452a0110..f061cc7ab 100644 --- a/src/frontend/src/lib/components/analytics/NoAnalytics.svelte +++ b/src/frontend/src/lib/components/analytics/NoAnalytics.svelte @@ -1,12 +1,9 @@

{$i18n.analytics.empty}

- -
diff --git a/src/frontend/src/lib/components/mission-control/MissionControlActions.svelte b/src/frontend/src/lib/components/mission-control/MissionControlActions.svelte index 4c5790f60..e42c0697c 100644 --- a/src/frontend/src/lib/components/mission-control/MissionControlActions.svelte +++ b/src/frontend/src/lib/components/mission-control/MissionControlActions.svelte @@ -5,6 +5,8 @@ import { emit } from '$lib/utils/events.utils'; import type { CanisterIcStatus } from '$lib/types/canister'; import type { Principal } from '@dfinity/principal'; + import MissionControlAttachSatellite from '$lib/components/mission-control/MissionControlAttachSatellite.svelte'; + import MissionControlAttachOrbiter from '$lib/components/mission-control/MissionControlAttachOrbiter.svelte'; export let missionControlId: Principal; @@ -42,4 +44,10 @@ + +
+ + + + diff --git a/src/frontend/src/lib/components/mission-control/MissionControlAttachOrbiter.svelte b/src/frontend/src/lib/components/mission-control/MissionControlAttachOrbiter.svelte new file mode 100644 index 000000000..128d06284 --- /dev/null +++ b/src/frontend/src/lib/components/mission-control/MissionControlAttachOrbiter.svelte @@ -0,0 +1,49 @@ + + + + + + {$i18n.analytics.attach} + {$i18n.analytics.attach_id} + diff --git a/src/frontend/src/lib/components/mission-control/MissionControlAttachSatellite.svelte b/src/frontend/src/lib/components/mission-control/MissionControlAttachSatellite.svelte new file mode 100644 index 000000000..003bad4aa --- /dev/null +++ b/src/frontend/src/lib/components/mission-control/MissionControlAttachSatellite.svelte @@ -0,0 +1,51 @@ + + + + + + {$i18n.satellites.attach} + {$i18n.satellites.id} + diff --git a/src/frontend/src/lib/components/orbiter/OrbiterActions.svelte b/src/frontend/src/lib/components/orbiter/OrbiterActions.svelte index 5bf4359ba..811ad22c0 100644 --- a/src/frontend/src/lib/components/orbiter/OrbiterActions.svelte +++ b/src/frontend/src/lib/components/orbiter/OrbiterActions.svelte @@ -46,6 +46,8 @@ onCanisterAction('transfer_cycles_orbiter')} /> +
+ diff --git a/src/frontend/src/lib/components/satellites/SatelliteActions.svelte b/src/frontend/src/lib/components/satellites/SatelliteActions.svelte index 7f4da4f4a..168b54c06 100644 --- a/src/frontend/src/lib/components/satellites/SatelliteActions.svelte +++ b/src/frontend/src/lib/components/satellites/SatelliteActions.svelte @@ -79,6 +79,8 @@ +
+ diff --git a/src/frontend/src/lib/i18n/en.json b/src/frontend/src/lib/i18n/en.json index 727f8d240..4b245dbfd 100644 --- a/src/frontend/src/lib/i18n/en.json +++ b/src/frontend/src/lib/i18n/en.json @@ -110,6 +110,7 @@ "detach_title": "Detach {0}", "detach_explanation": "Detaching this {0} will remove it from your mission control list. It will continue to function if it's active, or be forgotten if it's inactive and won't appear in the console anymore.", "detach_info": "Do you want to detach your {0}?", + "attach_success": "{0} attached.", "detach_success": "{0} detached.", "delete_success": "{0} deleted.", "transfer_cycles": "Transfer cycles", @@ -181,7 +182,8 @@ "extended_version": "Extended version", "build": "Build", "see_all_satellites": "See all satellites", - "go_launchpad": "Go to launchpad" + "go_launchpad": "Go to launchpad", + "attach": "Attach satellite" }, "mission_control": { "title": "Mission Control", @@ -274,7 +276,7 @@ "desktop": "Desktop", "others": "Others", "browsers": "Browsers", - "attach": "Attach existing Analytics", + "attach": "Attach analytics", "attach_id": "Enter Orbiter ID", "score": "Score", "rating": "Rating", @@ -419,7 +421,8 @@ "orbiter_configuration_missing": "Satellites and configuration must be provided.", "orbiter_configuration_unexpected": "Unexpected error(s) while trying to edit your configuration.", "orbiter_configuration_listing": "Error while fetching the configuration.", - "orbiter_id_missing": "A valid Orbiter ID must be provided.", + "canister_id_missing": "A valid ID must be provided.", + "canister_attach_error": "Unexpected error(s) while trying to attach the module.", "orbiter_attach": "Unexpected error(s) while trying to attach the orbiter to your mission control.", "orbiter_unexpected_error": "Unexpected error(s) while creating the orbiter.", "transactions_next": "Error while fetching the next transactions.", diff --git a/src/frontend/src/lib/i18n/zh-cn.json b/src/frontend/src/lib/i18n/zh-cn.json index 8ef796fd1..cbc2a6ec1 100644 --- a/src/frontend/src/lib/i18n/zh-cn.json +++ b/src/frontend/src/lib/i18n/zh-cn.json @@ -110,6 +110,7 @@ "detach_title": "Detach {0}", "detach_explanation": "解绑这个 {0} 会将它从mission control列表中删除. 如果它是激活状态,那么它将正常工作,否则就会被遗忘,也不会显示在仪表盘里面.", "detach_info": "你想解绑你的 {0}?", + "attach_success": "{0} attached.", "detach_success": "{0} 已解绑的.", "delete_success": "{0} 已删除.", "transfer_cycles": "转移 cycles", @@ -181,7 +182,8 @@ "extended_version": "扩展版本", "build": "Build", "see_all_satellites": "查看所有的 satellites", - "go_launchpad": "前往发射台" + "go_launchpad": "前往发射台", + "attach": "Attach satellite" }, "mission_control": { "title": "Mission Control", @@ -274,7 +276,7 @@ "desktop": "桌面", "others": "其他", "browsers": "浏览器", - "attach": "附加已有的 Analytics", + "attach": "附加 Analytics", "attach_id": "输入 Orbiter ID", "score": "得分", "rating": "打分", @@ -419,7 +421,8 @@ "orbiter_configuration_missing": "请提供 Satellites 和配置.", "orbiter_configuration_unexpected": "编辑配置时出现意外错误.", "orbiter_configuration_listing": "获取配置时出错.", - "orbiter_id_missing": "提供一个有效的 Orbiter ID .", + "canister_id_missing": "A valid ID must be provided.", + "canister_attach_error": "Unexpected error(s) while trying to attach the module.", "orbiter_attach": "试图关联 orbiter 到你的 mission control时候出现异常.", "orbiter_unexpected_error": "创建 orbiter 时出现异常错误.", "transactions_next": "获取下一部分事务时出错.", diff --git a/src/frontend/src/lib/services/mission-control.services.ts b/src/frontend/src/lib/services/mission-control.services.ts index 798f99d6d..e72e61fdd 100644 --- a/src/frontend/src/lib/services/mission-control.services.ts +++ b/src/frontend/src/lib/services/mission-control.services.ts @@ -5,6 +5,7 @@ import { missionControlVersion, setMissionControlController, setOrbiter, + setSatellite, setSatelliteMetadata, setSatellitesController, unsetOrbiter, @@ -155,6 +156,17 @@ export const setSatelliteName = async ({ ]); }; +export const attachSatellite = async (params: { + missionControlId: Principal; + satelliteId: Principal; +}) => { + const identity = get(authStore).identity; + + const satellite = await setSatellite({ ...params, identity }); + + satellitesStore.add(satellite); +}; + export const detachSatellite = async ({ canisterId, missionControlId diff --git a/src/frontend/src/lib/services/satellites.services.ts b/src/frontend/src/lib/services/satellites.services.ts index f65162fb5..e6cddbd6c 100644 --- a/src/frontend/src/lib/services/satellites.services.ts +++ b/src/frontend/src/lib/services/satellites.services.ts @@ -85,6 +85,6 @@ export const loadSatellites = async ({ detail: err }); - satellitesStore.set(null); + satellitesStore.reset(); } }; diff --git a/src/frontend/src/lib/stores/satellite.store.ts b/src/frontend/src/lib/stores/satellite.store.ts index 8ff7712c0..4c252a509 100644 --- a/src/frontend/src/lib/stores/satellite.store.ts +++ b/src/frontend/src/lib/stores/satellite.store.ts @@ -3,7 +3,35 @@ import type { Satellite } from '$declarations/mission_control/mission_control.di import { isNullish } from '@dfinity/utils'; import { derived, writable, type Readable } from 'svelte/store'; -export const satellitesStore = writable(undefined); +type SatellitesStoreData = Satellite[] | undefined | null; + +interface SatellitesStore extends Readable { + set: (satellites: Satellite[]) => void; + add: (satellite: Satellite) => void; + reset: () => void; +} + +const initSatellitesStore = (): SatellitesStore => { + const { subscribe, update, set } = writable(undefined); + + return { + subscribe, + + set(satellites) { + set(satellites); + }, + + add(satellite) { + update((state) => [...(state ?? []), satellite]); + }, + + reset: () => { + set(null); + } + }; +}; + +export const satellitesStore = initSatellitesStore(); export const satelliteStore: Readable = derived( [satellitesStore, page], diff --git a/src/frontend/src/lib/types/i18n.d.ts b/src/frontend/src/lib/types/i18n.d.ts index f64f38e2c..b84587d2d 100644 --- a/src/frontend/src/lib/types/i18n.d.ts +++ b/src/frontend/src/lib/types/i18n.d.ts @@ -114,6 +114,7 @@ interface I18nCanisters { detach_title: string; detach_explanation: string; detach_info: string; + attach_success: string; detach_success: string; delete_success: string; transfer_cycles: string; @@ -188,6 +189,7 @@ interface I18nSatellites { build: string; see_all_satellites: string; go_launchpad: string; + attach: string; } interface I18nMission_control { @@ -435,7 +437,8 @@ interface I18nErrors { orbiter_configuration_missing: string; orbiter_configuration_unexpected: string; orbiter_configuration_listing: string; - orbiter_id_missing: string; + canister_id_missing: string; + canister_attach_error: string; orbiter_attach: string; orbiter_unexpected_error: string; transactions_next: string; diff --git a/src/libs/collections/Cargo.toml b/src/libs/collections/Cargo.toml index bfa2f0bb0..6822cf176 100644 --- a/src/libs/collections/Cargo.toml +++ b/src/libs/collections/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "junobuild-collections" -version = "0.0.7" +version = "0.0.8" authors.workspace = true edition.workspace = true repository.workspace = true @@ -19,4 +19,4 @@ ic-cdk.workspace = true ic-cdk-macros.workspace = true ic-cdk-timers.workspace = true serde.workspace = true -junobuild-shared = "0.0.20" \ No newline at end of file +junobuild-shared = "0.0.21" \ No newline at end of file diff --git a/src/libs/satellite/Cargo.toml b/src/libs/satellite/Cargo.toml index ab727c841..7ed83864c 100644 --- a/src/libs/satellite/Cargo.toml +++ b/src/libs/satellite/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "junobuild-satellite" -version = "0.0.20-patch.3" +version = "0.0.20-patch.4" authors.workspace = true edition.workspace = true repository.workspace = true @@ -41,7 +41,7 @@ regex.workspace = true url = "2.4.0" getrandom = { version = "0.2", features = ["custom"] } rand = { version = "0.8.5", features = ["getrandom"]} -junobuild-shared = "0.0.20" +junobuild-shared = "0.0.21" junobuild-utils = "0.0.4" -junobuild-collections = "0.0.7" -junobuild-storage = "0.0.10" \ No newline at end of file +junobuild-collections = "0.0.8" +junobuild-storage = "0.0.11" \ No newline at end of file diff --git a/src/libs/shared/Cargo.toml b/src/libs/shared/Cargo.toml index bfd4064be..a1591aa99 100644 --- a/src/libs/shared/Cargo.toml +++ b/src/libs/shared/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "junobuild-shared" -version = "0.0.20" +version = "0.0.21" authors.workspace = true edition.workspace = true repository.workspace = true diff --git a/src/libs/shared/src/mgmt/cmc.rs b/src/libs/shared/src/mgmt/cmc.rs index 5b78b2d1d..9d8da3b6c 100644 --- a/src/libs/shared/src/mgmt/cmc.rs +++ b/src/libs/shared/src/mgmt/cmc.rs @@ -1,17 +1,16 @@ -use crate::constants::{ - CREATE_CANISTER_CYCLES, IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_TOP_UP, WASM_MEMORY_LIMIT, -}; +use crate::constants::{IC_TRANSACTION_FEE_ICP, MEMO_CANISTER_TOP_UP}; use crate::env::CMC; use crate::ledger::icp::transfer_payment; use crate::mgmt::ic::install_code; +use crate::mgmt::settings::{create_canister_cycles, create_canister_settings}; use crate::mgmt::types::cmc::{ CreateCanister, CreateCanisterResult, Cycles, NotifyError, SubnetId, SubnetSelection, TopUpCanisterArgs, }; use crate::mgmt::types::ic::WasmArg; -use candid::{Nat, Principal}; +use candid::Principal; use ic_cdk::api::call::{call_with_payment128, CallResult}; -use ic_cdk::api::management_canister::main::{CanisterId, CanisterInstallMode, CanisterSettings}; +use ic_cdk::api::management_canister::main::{CanisterId, CanisterInstallMode}; use ic_cdk::call; use ic_ledger_types::{Subaccount, Tokens}; @@ -81,22 +80,14 @@ pub async fn cmc_create_canister_install_code( let create_canister_arg = CreateCanister { subnet_type: None, subnet_selection: Some(SubnetSelection::Subnet { subnet: *subnet_id }), - settings: Some(CanisterSettings { - controllers: Some(controllers.clone()), - compute_allocation: None, - memory_allocation: None, - freezing_threshold: None, - reserved_cycles_limit: None, - log_visibility: None, - wasm_memory_limit: Some(Nat::from(WASM_MEMORY_LIMIT)), - }), + settings: create_canister_settings(controllers), }; let result: CallResult<(CreateCanisterResult,)> = call_with_payment128( cmc, "create_canister", (create_canister_arg,), - CREATE_CANISTER_CYCLES + cycles, + create_canister_cycles(cycles), ) .await; diff --git a/src/libs/shared/src/mgmt/ic.rs b/src/libs/shared/src/mgmt/ic.rs index 4c7e2d3f0..6feed46f9 100644 --- a/src/libs/shared/src/mgmt/ic.rs +++ b/src/libs/shared/src/mgmt/ic.rs @@ -1,10 +1,10 @@ -use crate::constants::{CREATE_CANISTER_CYCLES, WASM_MEMORY_LIMIT}; +use crate::mgmt::settings::{create_canister_cycles, create_canister_settings}; use crate::mgmt::types::ic::WasmArg; use crate::types::interface::DepositCyclesArgs; use crate::types::state::{ SegmentCanisterSettings, SegmentCanisterStatus, SegmentStatus, SegmentStatusResult, }; -use candid::{Nat, Principal}; +use candid::{Principal}; use ic_cdk::api::call::CallResult; use ic_cdk::api::management_canister::main::{ canister_status as ic_canister_status, create_canister, delete_canister, @@ -31,17 +31,9 @@ pub async fn create_canister_install_code( ) -> Result { let record = create_canister( CreateCanisterArgument { - settings: Some(CanisterSettings { - controllers: Some(controllers.clone()), - compute_allocation: None, - memory_allocation: None, - freezing_threshold: None, - reserved_cycles_limit: None, - log_visibility: None, - wasm_memory_limit: Some(Nat::from(WASM_MEMORY_LIMIT)), - }), + settings: create_canister_settings(controllers), }, - CREATE_CANISTER_CYCLES + cycles, + create_canister_cycles(cycles), ) .await; diff --git a/src/libs/shared/src/mgmt/mod.rs b/src/libs/shared/src/mgmt/mod.rs index 2955fc740..b5b96509d 100644 --- a/src/libs/shared/src/mgmt/mod.rs +++ b/src/libs/shared/src/mgmt/mod.rs @@ -1,4 +1,5 @@ pub mod cmc; pub mod ic; mod impls; +mod settings; pub mod types; diff --git a/src/libs/shared/src/mgmt/settings.rs b/src/libs/shared/src/mgmt/settings.rs new file mode 100644 index 000000000..c5902b51f --- /dev/null +++ b/src/libs/shared/src/mgmt/settings.rs @@ -0,0 +1,19 @@ +use crate::constants::{CREATE_CANISTER_CYCLES, WASM_MEMORY_LIMIT}; +use candid::{Nat, Principal}; +use ic_cdk::api::management_canister::main::CanisterSettings; + +pub fn create_canister_settings(controllers: Vec) -> Option { + Some(CanisterSettings { + controllers: Some(controllers.clone()), + compute_allocation: None, + memory_allocation: None, + freezing_threshold: None, + reserved_cycles_limit: None, + log_visibility: None, + wasm_memory_limit: Some(Nat::from(WASM_MEMORY_LIMIT)), + }) +} + +pub fn create_canister_cycles(cycles: u128) -> u128 { + CREATE_CANISTER_CYCLES + cycles +} diff --git a/src/libs/storage/Cargo.toml b/src/libs/storage/Cargo.toml index ec0ff9b70..314b83392 100644 --- a/src/libs/storage/Cargo.toml +++ b/src/libs/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "junobuild-storage" -version = "0.0.10" +version = "0.0.11" authors.workspace = true edition.workspace = true repository.workspace = true @@ -32,5 +32,5 @@ base64 = "0.13.1" url = "2.4.0" urlencoding = "2.1.3" globset = "0.4.13" -junobuild-shared = "0.0.20" -junobuild-collections = "0.0.7" +junobuild-shared = "0.0.21" +junobuild-collections = "0.0.8" diff --git a/src/mission_control/src/lib.rs b/src/mission_control/src/lib.rs index 1e304d2a8..5f061724f 100644 --- a/src/mission_control/src/lib.rs +++ b/src/mission_control/src/lib.rs @@ -271,25 +271,6 @@ async fn del_orbiter(orbiter_id: OrbiterId, cycles_to_deposit: u128) { .unwrap_or_else(|e| trap(&e)); } -/// Mgmt - -#[update(guard = "caller_is_user_or_admin_controller")] -async fn top_up(canister_id: Principal, amount: Tokens) { - top_up_canister(&canister_id, &amount) - .await - .unwrap_or_else(|e| trap(&e)); -} - -#[query(guard = "caller_is_user_or_admin_controller")] -fn get_user() -> UserId { - get_user_store() -} - -#[update(guard = "caller_is_user_or_admin_controller")] -fn set_metadata(metadata: Metadata) { - set_metadata_store(&metadata) -} - /// /// Controllers /// @@ -348,6 +329,23 @@ fn list_mission_control_controllers() -> Controllers { /// Mgmt /// +#[query(guard = "caller_is_user_or_admin_controller")] +fn get_user() -> UserId { + get_user_store() +} + +#[update(guard = "caller_is_user_or_admin_controller")] +fn set_metadata(metadata: Metadata) { + set_metadata_store(&metadata) +} + +#[update(guard = "caller_is_user_or_admin_controller")] +async fn top_up(canister_id: Principal, amount: Tokens) { + top_up_canister(&canister_id, &amount) + .await + .unwrap_or_else(|e| trap(&e)); +} + #[update(guard = "caller_is_user_or_admin_controller")] async fn deposit_cycles(args: DepositCyclesArgs) { deposit_cycles_shared(args)