Skip to content

Commit 3d21b73

Browse files
authored
Merge branch 'main' into 6649_add-remote-instance-immersive-view-instance-controls
2 parents 12a868d + 7989b5e commit 3d21b73

14 files changed

Lines changed: 672 additions & 608 deletions

File tree

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
#### 2.27.0: Release
2+
3+
- Bump JS-DevTools/npm-publish from 4.1.4 to 4.1.5 (#6614)
4+
- Bump aws-actions/configure-aws-credentials from 5.1.1 to 6.0.0 (#6673)
5+
- Bump cypress-io/github-action from 7.1.1 to 7.1.2 (#6674)
6+
- Bump flowfuse/github-actions-workflows/.github/workflows/build_container_image.yml (#6676)
7+
- Bump flowfuse/github-actions-workflows/.github/workflows/publish_node_package.yml (#6675)
8+
- Fix concurrency group setting in `Create pre-staging environment` workflow (#6656)
9+
- Add X-Forwarded headers to nginx ingress docs (#6630)
10+
- Ensure UI honors TeamType Enable All flag (#6686) @hardillb
11+
- Bump @modelcontextprotocol/sdk from 1.25.3 to 1.26.0 (#6632) @app/dependabot
12+
- Update device editor navigation to use named route configuration (#6690) @cstns
13+
- Add 4.1.5 to Device Agent Node-RED versions (#6688) @hardillb
14+
- Improve clarity of instance assignment in docs (#6554) @sumitshinde-84
15+
- Allow snapshot to be restored to a remote instance in dev mode (#6679) @knolleary
16+
- Hide billing selector when billing is disabled (#6671) @cstns
17+
- fix(ui): wrap DialogBox in Teleport to fix positioning in drawer cont… (#6255) @dimitrieh
18+
- Reenable immersive mode for remote instances (#6684) @cstns
19+
- Use correct favicon and use provided link to package (#6678) @Steve-Mcl
20+
- Ensure consistent Open Editor behavior — always default to immersive experience (#6670) @dimitrieh
21+
- FlowFuse Expert context UI (#6631) @Steve-Mcl
22+
- Remove WebSocket communication handling and associated methods from the immersive device editor (#6668) @cstns
23+
- Add WebSocket disconnection details by logging event details (#6667) @cstns
24+
- Enhance remote instance editor with connection polling, communication… (#6647) @cstns
25+
- Add the drawer sub routes and the FF Expert to the immersive remote instance (#6629) @cstns
26+
- Add the embedded remote instance editor route (#6628) @cstns
27+
- Relocate immersive editor components to a common namespace (#6624) @cstns
28+
- ci: Update NR versions on pre-staging stacks (#6641) @ppawlowski
29+
- Refactor device routes and update references to use consistent naming convention (#6623) @cstns
30+
- Chore: extract common immersive styling (#6622) @cstns
31+
- Refactor drawer: extract reusable functionality into composable (#6621) @cstns
32+
- Chore/use the resizing helper to resize the instance immersive drawer (#6610) @cstns
33+
- Bump @isaacs/brace-expansion from 5.0.0 to 5.0.1 (#6627) @app/dependabot
34+
- Bump dompurify from 3.3.0 to 3.3.1 (#6612) @app/dependabot
35+
136
#### 2.26.2: Release
237

338
- Bump cypress-io/github-action from 7.1.0 to 7.1.1 (#6616)

docs/device-agent/register.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ flowfuse-device-agent
147147
You will see the Device Agent start and perform a 'call-home' where it connects back
148148
to the platform to check what it should be running.
149149

150-
151150
#### Additional Information
152151

153152
If you copy or download a **Device Provisioning Configuration** file to your hardware,
@@ -164,12 +163,12 @@ you've followed [Single Device Registration](#single-remote-instance-registratio
164163
[Bulk Registration](#bulk-registration) to register your device, it will
165164
automatically be assigned to an Application or Instance.
166165

167-
### Applications (Recommended)
168-
169-
#### Assign to Application
166+
### Applications
170167

171168
This step will permit you to push Snapshots to your Remote Instance via [DevOps Pipelines](/docs/user/devops-pipelines.md), or via a [Target Snapshot](/docs/user/snapshots/#application-owned-devices) from the Application.
172169

170+
#### Assign to Application
171+
173172
1. Go to your teams's **Remote Instances** page.
174173
2. Open the dropdown menu to the right of the Remote Instance you want to assign and
175174
select the **Add to Application** option.
@@ -206,14 +205,17 @@ NOTE: If you wish to keep the flows currently running on the Remote Instance, it
206205

207206
### Hosted Instances
208207

209-
This method permits you to set a [Target Snapshot](/docs/user/snapshots/#instance-owned-devices) from the Hosted Instance to the Remote Instance. Note though, you can only push nodes and flows that are supported by _both_ Hosted and Remote Instances. The best use case for Remote Instances are generally to be assigned by an Application instead.
208+
This method establishes a deployment relationship where a Hosted Instance becomes the source for snapshot deployments to your Remote Instance(s).
209+
210+
**Important:** This is a legacy feature; [assigning to Applications](./register.md#applications) is the recommended approach as it provides better fleet management and DevOps Pipeline capabilities. For guidance, see [when to use each approach](/docs/user/concepts.md#when-to-use-instance-assignment-vs-devops-pipelines).
210211

211212
#### Assign to Hosted Instance
212213

213-
1. Go to your teams's **Remote Instances** page.
214-
2. Open the dropdown menu to the right of the Remote Instance you want to assign and
215-
select the **Add to Hosted Instance** option.
216-
3. Select the instance in the dialog and click **Add** to continue.
214+
1. Go to your team's **Remote Instances** page.
215+
2. Open the dropdown menu to the right of the Remote Instance you want to assign and select the **Add to Instance** option.
216+
3. Select the Hosted Instance in the dialog and click **Add** to continue.
217+
218+
**Note:** There are constraints on which instances can be assigned to each other. For detailed information, refer to [Assignment Rules and Constraints](/docs/user/concepts.md#assignment-rules).
217219

218220
### Remove from Hosted Instance
219221

docs/device-agent/running.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Options
5151
-i, --interval secs
5252
-p, --port number
5353
-m, --moduleCache Use local npm module cache rather than install
54+
--node-options Adds NodeJS command line arguments, can be specified multiple times
5455
5556
Web UI Options
5657
@@ -89,6 +90,12 @@ _Start the agent with a different working directory and the Web UI enabled_
8990
flowfuse-device-agent -d /path/to/working/directory -w --ui-user admin --ui-pass password --ui-port 8081
9091
```
9192

93+
_Start the agent with a larger NodeJS memory space and using the openssl CA certs_
94+
95+
```bash
96+
flowfuse-device-agent -d /path/to/working/directory --node-options='--max_old_space_size=1024' --node_options='--use-openssl-ca'
97+
```
98+
9299
## Running behind a HTTP Proxy
93100

94101
If the Remote Instance is behind a HTTP proxy, the agent can be configured to use the proxy by setting the `http_proxy`, `https_proxy` or `all_proxy` environment variables.
@@ -146,4 +153,4 @@ OS and Architecture as your target device, and then copy the modules on to your
146153
2. Place this file in an empty directory on your local device.
147154
3. Run `npm install` to install the modules. This will create a `node_modules` directory.
148155
4. On your target Remote Instance, create a directory called `module_cache` inside the Device Agent Configuration directory.
149-
5. Copy the `node_modules` directory from your local instance to the target instance so that it is under the `module_cache` directory.
156+
5. Copy the `node_modules` directory from your local instance to the target instance so that it is under the `module_cache` directory.

docs/user/concepts.md

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ navOrder: 2
55

66
# FlowFuse Concepts
77

8-
FlowFuse makes it easy to create, manage, and scale Node-RED instances. The platform introduces a few core concepts to help you organize and work with it effectively. Throughout the platform, youll also see ⓘ icons that you can click on to get pop-up explanations for different features and terms.
8+
FlowFuse makes it easy to create, manage, and scale Node-RED instances. The platform introduces a few core concepts to help you organize and work with it effectively. Throughout the platform, you'll also see ⓘ icons that you can click on to get pop-up explanations for different features and terms.
99

1010
## Table of Contents
1111

@@ -25,7 +25,11 @@ FlowFuse makes it easy to create, manage, and scale Node-RED instances. The plat
2525
- [Template](#template)
2626
- [Snapshot](#snapshot)
2727
- [Hosted Instance Snapshot](#hosted-instance-snapshot)
28-
- [Remote Instances (Device Snapshot)](#remote-instance-snapshot)
28+
- [Remote Instance Snapshot](#remote-instance-snapshot)
29+
- [Assigning Remote Instances to Hosted Instances](#assigning-remote-instances-to-hosted-instances)
30+
- [How It Works](#how-it-works)
31+
- [Assignment Rules](#assignment-rules)
32+
- [When to Use Instance Assignment vs DevOps Pipelines](#when-to-use-instance-assignment-vs-devops-pipelines)
2933
- [Device](#device)
3034
- [Device Agent](#device-agent)
3135
- [Fleet Mode vs Developer Mode](#fleet-mode-vs-developer-mode)
@@ -50,7 +54,7 @@ To organize your Node-RED instances, they are grouped within Applications. With
5054

5155
Applications provide logical organization of related instances, support for DevOps pipeline workflows, simplified device group management, application-level audit logging, and clear organizational boundaries for managing multiple instances and devices.
5256

53-
With FlowFuses Granular RBAC, Applications can now also act as an authorization boundary. This means user roles and permissions can be managed at the application level, providing finer control over access to instances, snapshots, and devices within a given application.
57+
With FlowFuse's Granular RBAC, Applications can now also act as an authorization boundary. This means user roles and permissions can be managed at the application level, providing finer control over access to instances, snapshots, and devices within a given application.
5458

5559
### DevOps Pipeline
5660

@@ -125,6 +129,35 @@ A user can create an hosted instance snapshot and then mark it as the *target* s
125129

126130
Similar to instance snapshots, a device snapshot is a point-in-time backup of a Node-RED instance running on a remote device. It captures the flows, credentials, and runtime settings. The difference is that local changes made on a device during developer mode are pulled into the FlowFuse platform and stored as a snapshot. The dashboard also allows you to see and manage these snapshots. With devices assigned to an application, a user can create a device snapshot from the remote device.
127131

132+
### Assigning Remote Instances to Hosted Instances
133+
134+
**Note:** This is a legacy feature that predates DevOps Pipelines. For new deployments, consider using [DevOps Pipelines](#devops-pipeline) instead, which provide more flexible and powerful deployment workflows.
135+
136+
Instance assignment establishes a parent-child deployment relationship between a hosted instance and remote instances. When assigned, the hosted instance becomes the source of truth and automatically pushes snapshots to the remote instance. You can only push nodes and flows that are supported by both Hosted and Remote Instances. For more information on how this works, see [How Instance Assignment Works](/docs/user/concepts.md#how-it-works).
137+
138+
#### How It Works
139+
140+
Instance assignment creates a parent-child deployment relationship with the following behavior:
141+
142+
- **Hosted Instance (Parent)**: Acts as the deployment controller and source of truth for snapshots
143+
- **Remote Instance (Child)**: Receives and applies snapshot updates from the parent
144+
- **Deployment Flow**: When you set a [Target Snapshot](/docs/user/snapshots/#instance-owned-devices) on the hosted instance, all assigned remote instances automatically restart on that snapshot
145+
- **One-Way Relationship**: Changes flow exclusively from the hosted instance to remote instances, ensuring consistent deployments across your fleet
146+
147+
#### Assignment Rules
148+
149+
FlowFuse enforces specific constraints on instance assignment:
150+
151+
- Remote instances can be assigned to hosted instances
152+
- Remote instances cannot be assigned to other remote instances
153+
- Hosted instances cannot be assigned to any other instances
154+
155+
#### When to Use Instance Assignment vs DevOps Pipelines
156+
157+
Instance assignment suits straightforward scenarios where you need to push snapshots from a single hosted instance directly to one or more remote instances. It works well for maintaining existing deployments or when you simply need one hosted instance deploying to multiple remote instances without staging environments.
158+
159+
DevOps Pipelines are recommended for most deployment workflows. They support staged environments for testing changes in development before promoting to production, enable deployment to device groups for easier fleet management, and offer more flexibility aligned with modern development practices. If you're setting up a new deployment process, use DevOps Pipelines.
160+
128161
## Device
129162

130163
The FlowFuse platform can be used to manage Node-RED applications running on remote devices. A Device is essentially a **Remote Instance** that runs a software agent to connect back to the platform and receive updates.
@@ -155,4 +188,4 @@ Device groups allow you to organize your devices into logical groups. These grou
155188

156189
Device groups provide logical organization of devices by location, function, environment, or other criteria. They enable simplified mass deployments through DevOps pipelines, allow you to target specific device groups for staged rollouts, and let you manage subsets of devices independently.
157190

158-
Read more [about Device Groups](./device-groups.md).
191+
Read more [about Device Groups](./device-groups.md).

docs/user/ff-tables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ navTitle: FlowFuse Tables
55
# FlowFuse Tables
66

77
<div class="bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700 px-4 py-4 mb-4 rounded" role="alert">
8-
<span class="block leading-none">This feature is currently in <a href="https://flowfuse.com/handbook/development/releases/#beta-release">the beta state</a></span>
8+
<span class="block leading-none">This feature is currently in <a href="https://flowfuse.com/handbook/engineering/releases/#beta-release">the beta state</a></span>
99
</div>
1010

1111
From FlowFuse v2.20.0 Teams (Enterprise teams only) can create a relational database to use to store data.

frontend/src/pages/account/Security/dialogs/TokenCreated.vue

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<ff-dialog ref="dialog" data-el="add-token-confirmation" header="Token Created">
33
<template #default>
4-
<p>Your token is <code>{{ token.token }}</code></p>
4+
<p>Your token is <code>{{ token?.token }}</code></p>
55
<p>This is the only time it will be shown, so please ensure you make a note</p>
66
</template>
77
<template #actions>
@@ -18,14 +18,6 @@ import Alerts from '../../../../services/alerts.js'
1818
export default {
1919
name: 'TokenCreated',
2020
mixins: [clipboardMixin],
21-
setup () {
22-
return {
23-
showToken (token) {
24-
this.token = token
25-
this.$refs.dialog.show()
26-
}
27-
}
28-
},
2921
data () {
3022
return {
3123
token: null
@@ -34,7 +26,7 @@ export default {
3426
methods: {
3527
close () {
3628
this.$refs.dialog.close()
37-
this.token = undefined
29+
this.token = null
3830
},
3931
copy () {
4032
this.copyToClipboard(this.token.token).then(() => {
@@ -43,6 +35,10 @@ export default {
4335
console.warn('Clipboard write permission denied: ', err)
4436
Alerts.emit('Clipboard write permission denied.', 'warning')
4537
})
38+
},
39+
showToken (token) {
40+
this.token = token
41+
this.$refs.dialog.show()
4642
}
4743
}
4844
}

frontend/src/pages/admin/TeamTypes/dialogs/TeamTypeEditDialog.vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ export default {
252252
this.input.properties.features = teamType.properties?.features || {}
253253
this.input.properties.billing = teamType.properties?.billing || {}
254254
this.input.properties.trial = teamType.properties?.trial || {}
255+
this.input.properties.enableAllFeatures = teamType.properties?.enableAllFeatures || false
255256
this.input.properties.autoStackUpdate = teamType.properties?.autoStackUpdate || { enabled: false, days: [], hours: [] }
256257
if (this.input.properties.trial.active && !this.input.properties.trial.instanceType) {
257258
this.input.properties.trial.instanceType = '_'
@@ -454,7 +455,8 @@ export default {
454455
instances: { ...this.input.properties.instances },
455456
features: { ...this.input.properties.features },
456457
teamBroker: { ...this.input.properties.teamBroker },
457-
autoStackUpdate: { ...this.input.properties.autoStackUpdate }
458+
autoStackUpdate: { ...this.input.properties.autoStackUpdate },
459+
enableAllFeatures: this.input.properties.enableAllFeatures
458460
}
459461
}
460462
// Utility function that ensures the specific property is

frontend/src/pages/device/Settings/General.vue

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -228,36 +228,10 @@ export default {
228228
nodeRedVersion: ''
229229
},
230230
availableNrVersions: [
231-
'3.1.0',
232-
'3.1.1',
233-
'3.1.2',
234-
'3.1.3',
235-
'3.1.4',
236-
'3.1.5',
237-
'3.1.6',
238-
'3.1.7',
239-
'3.1.8',
240-
'3.1.9',
241-
'3.1.10',
242-
'3.1.11',
243-
'3.1.12',
244-
'3.1.13',
245-
'3.1.14',
246231
'3.1.15',
247-
'4.0.0',
248-
'4.0.1',
249-
'4.0.2',
250-
'4.0.3',
251-
'4.0.4',
252-
'4.0.5',
253-
'4.0.6',
254-
'4.0.7',
255-
'4.0.8',
256232
'4.0.9',
257-
'4.1.0',
258-
'4.1.1',
259-
'4.1.2',
260-
'4.1.3'
233+
'4.1.4',
234+
'4.1.5'
261235
262236
]
263237
}

frontend/src/pages/device/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ export default {
468468
if (!immersive) {
469469
this.openInANewTab(this.deviceEditorURL, `device-editor-${this.device.id}`)
470470
} else {
471-
this.navigateTo('editor', event, { target: `device-editor-${this.device.id}` })
471+
this.navigateTo({ name: 'device-editor' }, event, { target: `device-editor-${this.device.id}` })
472472
}
473473
},
474474
async openTunnel ({

frontend/src/pages/instance/Overview.vue

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -234,34 +234,33 @@ export default {
234234
}
235235
},
236236
watch: {
237-
'instance.id': function (old, news) {
238-
this.loadLogs()
239-
this.getUpdateSchedule(this.instance.id)
237+
instance: {
238+
handler: function (instance) {
239+
if (instance) {
240+
this.loadLogs()
241+
this.getUpdateSchedule(instance.id)
242+
}
243+
},
244+
immediate: true
240245
}
241246
},
242-
mounted () {
243-
this.loadLogs()
244-
this.getUpdateSchedule(this.instance.id)
245-
},
246247
methods: {
247248
openUrl () {
248249
this.openInANewTab(this.instance.url, `_${this.instance.id}`)
249250
},
250251
loadLogs () {
251-
if (this.instance && this.instance.id) {
252-
this.loading = true
253-
this.loadItems(this.instance.id)
254-
.then((data) => {
255-
this.auditLog = data.log
256-
})
257-
.catch((error) => {
258-
console.error('Error loading logs:', error)
259-
this.auditLog = []
260-
})
261-
.finally(() => {
262-
this.loading = false
263-
})
264-
}
252+
this.loading = true
253+
this.loadItems(this.instance.id)
254+
.then((data) => {
255+
this.auditLog = data.log
256+
})
257+
.catch((error) => {
258+
console.error('Error loading logs:', error)
259+
this.auditLog = []
260+
})
261+
.finally(() => {
262+
this.loading = false
263+
})
265264
},
266265
loadItems: async function (instanceId, cursor) {
267266
return await InstanceApi.getInstanceAuditLog(instanceId, null, cursor, 4)

0 commit comments

Comments
 (0)