From d562466fc4e24369f151046145ebb0ce280f68e4 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 18 Feb 2026 18:18:53 -0700 Subject: [PATCH] feat: split row_actions into remove_action and overflow_actions for URL plugins --- Makefile | 2 +- core/src/net/service_interface.rs | 13 +++-- core/src/service/effects/net/plugin.rs | 34 ++++++++---- sdk/base/lib/Effects.ts | 3 +- sdk/base/lib/osBindings/HostnameMetadata.ts | 3 +- .../osBindings/UrlPluginExportUrlParams.ts | 3 +- sdk/package/lib/StartSdk.ts | 13 ++++- .../interfaces/addresses/plugin.component.ts | 52 ++++++++++++------- .../ui/src/app/services/api/mock-patch.ts | 44 +++++++++++++--- 9 files changed, 119 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 6153a4f0d..2894b4d15 100644 --- a/Makefile +++ b/Makefile @@ -278,7 +278,7 @@ core/bindings/index.ts: $(call ls-files, core) $(ENVIRONMENT_FILE) rm -rf core/bindings ./core/build/build-ts.sh ls core/bindings/*.ts | sed 's/core\/bindings\/\([^.]*\)\.ts/export { \1 } from ".\/\1";/g' | grep -v '"./index"' | tee core/bindings/index.ts - npm --prefix sdk exec -- prettier --config ./sdk/base/package.json -w ./core/bindings/*.ts + npm --prefix sdk/base exec -- prettier --config=./sdk/base/package.json -w ./core/bindings/*.ts touch core/bindings/index.ts sdk/dist/package.json sdk/baseDist/package.json: $(call ls-files, sdk) sdk/base/lib/osBindings/index.ts diff --git a/core/src/net/service_interface.rs b/core/src/net/service_interface.rs index ba2feaf1d..7c4b294aa 100644 --- a/core/src/net/service_interface.rs +++ b/core/src/net/service_interface.rs @@ -43,7 +43,8 @@ pub enum HostnameMetadata { }, Plugin { package_id: PackageId, - row_actions: Vec, + remove_action: Option, + overflow_actions: Vec, #[ts(type = "unknown")] #[serde(default)] info: Value, @@ -99,7 +100,8 @@ impl PluginHostnameInfo { pub fn to_hostname_info( &self, plugin_package: &PackageId, - row_actions: Vec, + remove_action: Option, + overflow_actions: Vec, ) -> HostnameInfo { HostnameInfo { ssl: self.ssl, @@ -109,7 +111,8 @@ impl PluginHostnameInfo { metadata: HostnameMetadata::Plugin { package_id: plugin_package.clone(), info: self.info.clone(), - row_actions, + remove_action, + overflow_actions, }, } } @@ -118,7 +121,9 @@ impl PluginHostnameInfo { /// (comparing address fields only, not row_actions). pub fn matches_hostname_info(&self, h: &HostnameInfo, plugin_package: &PackageId) -> bool { match &h.metadata { - HostnameMetadata::Plugin { package_id, info, .. } => { + HostnameMetadata::Plugin { + package_id, info, .. + } => { package_id == plugin_package && h.ssl == self.ssl && h.public == self.public diff --git a/core/src/service/effects/net/plugin.rs b/core/src/service/effects/net/plugin.rs index 713fbf705..7d98bb30c 100644 --- a/core/src/service/effects/net/plugin.rs +++ b/core/src/service/effects/net/plugin.rs @@ -19,7 +19,10 @@ fn require_url_plugin(context: &Arc) -> Result<(), Error> { .contains(&PluginId::UrlV0) { return Err(Error::new( - eyre!("{}", t!("net.plugin.manifest-missing-plugin", plugin = "url-v0")), + eyre!( + "{}", + t!("net.plugin.manifest-missing-plugin", plugin = "url-v0") + ), ErrorKind::InvalidRequest, )); } @@ -68,36 +71,45 @@ pub async fn register( #[ts(export)] pub struct UrlPluginExportUrlParams { pub hostname_info: PluginHostnameInfo, - pub row_actions: Vec, + pub remove_action: Option, + pub overflow_actions: Vec, } pub async fn export_url( context: EffectContext, UrlPluginExportUrlParams { hostname_info, - row_actions, + remove_action, + overflow_actions, }: UrlPluginExportUrlParams, ) -> Result<(), Error> { let context = context.deref()?; require_url_plugin(&context)?; let plugin_id = context.seed.id.clone(); - let entry = hostname_info.to_hostname_info(&plugin_id, row_actions); + let entry = hostname_info.to_hostname_info(&plugin_id, remove_action, overflow_actions); context .seed .ctx .db .mutate(|db| { - let host = host_for(db, hostname_info.package_id.as_ref(), &hostname_info.host_id)?; + let host = host_for( + db, + hostname_info.package_id.as_ref(), + &hostname_info.host_id, + )?; host.as_bindings_mut() .as_idx_mut(&hostname_info.internal_port) - .or_not_found(t!("net.plugin.binding-not-found", binding = format!( - "{}:{}:{}", - hostname_info.package_id.as_deref().unwrap_or("STARTOS"), - hostname_info.host_id, - hostname_info.internal_port - )))? + .or_not_found(t!( + "net.plugin.binding-not-found", + binding = format!( + "{}:{}:{}", + hostname_info.package_id.as_deref().unwrap_or("STARTOS"), + hostname_info.host_id, + hostname_info.internal_port + ) + ))? .as_addresses_mut() .as_available_mut() .mutate(|available: &mut BTreeSet<_>| { diff --git a/sdk/base/lib/Effects.ts b/sdk/base/lib/Effects.ts index baa03c082..596fffe92 100644 --- a/sdk/base/lib/Effects.ts +++ b/sdk/base/lib/Effects.ts @@ -159,7 +159,8 @@ export type Effects = { register(options: { tableAction: ActionId }): Promise exportUrl(options: { hostnameInfo: PluginHostnameInfo - rowActions: ActionId[] + removeAction: ActionId | null + overflowActions: ActionId[] }): Promise clearUrls(options: { except: PluginHostnameInfo[] }): Promise } diff --git a/sdk/base/lib/osBindings/HostnameMetadata.ts b/sdk/base/lib/osBindings/HostnameMetadata.ts index fbd3cdd73..3fb9e7aa7 100644 --- a/sdk/base/lib/osBindings/HostnameMetadata.ts +++ b/sdk/base/lib/osBindings/HostnameMetadata.ts @@ -12,6 +12,7 @@ export type HostnameMetadata = | { kind: 'plugin' packageId: PackageId - rowActions: Array + removeAction: ActionId | null + overflowActions: Array info: unknown } diff --git a/sdk/base/lib/osBindings/UrlPluginExportUrlParams.ts b/sdk/base/lib/osBindings/UrlPluginExportUrlParams.ts index 94a69db38..51171357c 100644 --- a/sdk/base/lib/osBindings/UrlPluginExportUrlParams.ts +++ b/sdk/base/lib/osBindings/UrlPluginExportUrlParams.ts @@ -4,5 +4,6 @@ import type { PluginHostnameInfo } from './PluginHostnameInfo' export type UrlPluginExportUrlParams = { hostnameInfo: PluginHostnameInfo - rowActions: Array + removeAction: ActionId | null + overflowActions: Array } diff --git a/sdk/package/lib/StartSdk.ts b/sdk/package/lib/StartSdk.ts index d1f5532f8..d8d255af2 100644 --- a/sdk/package/lib/StartSdk.ts +++ b/sdk/package/lib/StartSdk.ts @@ -765,7 +765,15 @@ export class StartSdk { effects: T.Effects, options: { hostnameInfo: T.PluginHostnameInfo - rowActions: ActionInfo< + removeAction: ActionInfo< + T.ActionId, + { + urlPluginMetadata: T.PluginHostnameInfo & { + interfaceId: T.ServiceInterfaceId + } + } + > | null + overflowActions: ActionInfo< T.ActionId, { urlPluginMetadata: T.PluginHostnameInfo & { @@ -777,7 +785,8 @@ export class StartSdk { ) => effects.plugin.url.exportUrl({ hostnameInfo: options.hostnameInfo, - rowActions: options.rowActions.map((a) => a.id), + removeAction: options.removeAction?.id ?? null, + overflowActions: options.overflowActions.map((a) => a.id), }), setupExportedUrls, // similar to setupInterfaces }), diff --git a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/plugin.component.ts b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/plugin.component.ts index c1ccd0f8e..5a1566324 100644 --- a/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/plugin.component.ts +++ b/web/projects/ui/src/app/routes/portal/components/interfaces/addresses/plugin.component.ts @@ -5,11 +5,7 @@ import { input, signal, } from '@angular/core' -import { - CopyService, - DialogService, - i18nPipe, -} from '@start9labs/shared' +import { CopyService, DialogService, i18nPipe } from '@start9labs/shared' import { T } from '@start9labs/start-sdk' import { TUI_IS_MOBILE } from '@taiga-ui/cdk' import { @@ -72,21 +68,30 @@ import { {{ 'Copy URL' | i18n }} @if (address.hostnameInfo.metadata.kind === 'plugin') { - @for ( - actionId of address.hostnameInfo.metadata.rowActions; - track actionId - ) { - @if (pluginGroup().pluginActions[actionId]; as meta) { + @if (address.hostnameInfo.metadata.removeAction) { + @if ( + pluginGroup().pluginActions[ + address.hostnameInfo.metadata.removeAction + ]; + as meta + ) { } } + }
@@ -117,21 +122,30 @@ import { {{ 'Copy URL' | i18n }} @if (address.hostnameInfo.metadata.kind === 'plugin') { - @for ( - actionId of address.hostnameInfo.metadata.rowActions; - track actionId - ) { - @if (pluginGroup().pluginActions[actionId]; as meta) { + @if (address.hostnameInfo.metadata.removeAction) { + @if ( + pluginGroup().pluginActions[ + address.hostnameInfo.metadata.removeAction + ]; + as meta + ) { } } + } diff --git a/web/projects/ui/src/app/services/api/mock-patch.ts b/web/projects/ui/src/app/services/api/mock-patch.ts index a8d79dc0c..3d0247341 100644 --- a/web/projects/ui/src/app/services/api/mock-patch.ts +++ b/web/projects/ui/src/app/services/api/mock-patch.ts @@ -94,16 +94,30 @@ export const mockPatchData: DataModel = { { ssl: false, public: false, - hostname: 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567abc.onion', + hostname: + 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567abc.onion', port: 80, - metadata: { kind: 'plugin', packageId: 'tor', rowActions: [], info: null }, + metadata: { + kind: 'plugin', + packageId: 'tor', + removeAction: 'delete-onion-service', + overflowActions: [], + info: null, + }, }, { ssl: true, public: false, - hostname: 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567abc.onion', + hostname: + 'abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567abc.onion', port: 443, - metadata: { kind: 'plugin', packageId: 'tor', rowActions: [], info: null }, + metadata: { + kind: 'plugin', + packageId: 'tor', + removeAction: 'delete-onion-service', + overflowActions: [], + info: null, + }, }, ], }, @@ -595,16 +609,30 @@ export const mockPatchData: DataModel = { { ssl: false, public: false, - hostname: 'xyz789abc123def456ghi789jkl012mno345pqr678stu901vwx234.onion', + hostname: + 'xyz789abc123def456ghi789jkl012mno345pqr678stu901vwx234.onion', port: 42080, - metadata: { kind: 'plugin', packageId: 'tor', rowActions: [], info: null }, + metadata: { + kind: 'plugin', + packageId: 'tor', + removeAction: 'delete-onion-service', + overflowActions: [], + info: null, + }, }, { ssl: true, public: false, - hostname: 'xyz789abc123def456ghi789jkl012mno345pqr678stu901vwx234.onion', + hostname: + 'xyz789abc123def456ghi789jkl012mno345pqr678stu901vwx234.onion', port: 42443, - metadata: { kind: 'plugin', packageId: 'tor', rowActions: [], info: null }, + metadata: { + kind: 'plugin', + packageId: 'tor', + removeAction: 'delete-onion-service', + overflowActions: [], + info: null, + }, }, ], },