Skip to content

Commit

Permalink
Merge branch 'main' into docs-device-agent
Browse files Browse the repository at this point in the history
  • Loading branch information
joepavitt authored Nov 1, 2024
2 parents 10ba20b + 53b25a8 commit 0cf9564
Show file tree
Hide file tree
Showing 28 changed files with 1,478 additions and 302 deletions.
2 changes: 1 addition & 1 deletion .github/scripts/initial-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ curl -ks -w "\n" -XPOST \
"contextLimit": null,
"customHostnames":false,
"staticAssets":false,
"teamBroker":true
"teamBroker":false
},
"instances": {
"'"$projectTypeId"'": {
Expand Down
2 changes: 2 additions & 0 deletions ci/ci-values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ forge:
localPostgresql: true
broker:
enabled: true
teamBroker:
uiOnly: true
cloudProvider: aws
email:
ses:
Expand Down
Binary file modified docs/cloud/images/create-broker-client.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/cloud/images/node-red-mqtt-connection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/cloud/images/node-red-mqtt-security.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 10 additions & 4 deletions docs/cloud/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,27 +188,33 @@ platform.

#### Enterprise Team Broker

Enterprise level teams come with their own MQTT broker. You can provision clients from the broker tab in the left hand menu.
Both Team and Enterprise level teams come with their own MQTT broker. You can provision clients from the broker tab in the left hand menu.

Teams can register up to 20 clients as part of their plan. The ability to purchase additional packs of clients will come in a future release.
Enterprise level Teams can register up to 20 and Teams level Teams can register up to 5 clients as part of their plan. The ability to purchase additional packs of clients will come in a near future release.

The broker is available on `broker.flowfuse.cloud` and supports the following connection types:

- MQTT on port `1883`
- MQTT over TLS on port `8883`
- MQTT over secure WebSockets on port `443`

When creating clients you can specify a username, but it will prepended to the the Team's id e.g. `alice` will become `alice@abcd1234`.
When creating clients you can specify a username, but it will prepended to the the Team's id e.g. `alice` will become `alice@32E4NEO5pY`.
Clients must also use the username as the MQTT Client ID in order to connect.

![Create Broker Client](./images/create-broker-client.png)

e.g.

```
mosquitto_sub -u "alice@abcd1234" -i "alice@abcd1234" -P "password" -h broker.flowfuse.cloud -t "#"
mosquitto_sub -u "alice@32E4NEO5pY" -i "alice@32E4NEO5pY" -P "password" -h broker.flowfuse.cloud -t "#"
```

Or in Node-RED as follows

![Node-RED MQTT Client Connection](./images/node-red-mqtt-connection.png)

![Node-RED MQTT Client Security](./images/node-red-mqtt-security.png)

### IP Addresses

Outbound connections from FlowFuse will always come from the IP address `63.33.85.112`.
Expand Down
2 changes: 0 additions & 2 deletions docs/install/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ meta:
This guide walks you through detailed set up of FlowFuse Platform on a Docker container envoronment using Docker Compose. Typically suited for small/medium on premise deployments.
By the end, you will have a fully functioning FlowFuse instance running in a Docker container.

For a FlowFuse platform evaluation purposes, check out our [Quick Start Guide](../../quick-start/README.md).

The following guide walks through a full production-ready deployment. If you want to install FlowFuse for evaluation purposes, please refer to the [Quick Start Guide](../../quick-start/README.md).

## Checklist
Expand Down
2 changes: 1 addition & 1 deletion forge/comms/v2AuthRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ module.exports = async function (app) {
const topic = request.body.topic
const action = request.body.action
if ((username.startsWith('device:') ||
username.startsWith('platform:') ||
username.startsWith('project:') ||
username.startsWith('frontend:') ||
username === 'forge_platform') && !username.includes('@')) {
const acc = action === 'subscribe' ? 1 : 2
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Accordion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</slot>
<div class="toggle">
<slot name="meta" />
<ChevronLeftIcon v-if="!disabled" class="ff-icon" />
<ChevronLeftIcon v-if="!disabled" class="ff-icon chevron" />
</div>
</button>
<div ref="content" class="ff-accordion--content">
Expand Down
28 changes: 22 additions & 6 deletions frontend/src/components/TextCopier.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,41 @@
<script>
import { DuplicateIcon } from '@heroicons/vue/outline'
import Alert from '../services/alerts.js'
export default {
name: 'TextCopier',
components: { DuplicateIcon },
props: {
text: {
required: true,
type: String
},
confirmationType: {
type: String,
required: false,
default: 'prompt',
validator: (value) => {
return ['prompt', 'alert'].includes(value)
}
}
},
emits: ['copied'],
methods: {
copyPath () {
navigator.clipboard.writeText(this.text)
// show "Copied" notification
this.$refs.copied.style.display = 'inline'
// hide after 500ms
setTimeout(() => {
this.$refs.copied.style.display = 'none'
}, 500)
if (this.confirmationType === 'alert') {
Alert.emit('Copied to Clipboard', 'confirmation')
} else {
// show "Copied" notification
this.$refs.copied.style.display = 'inline'
// hide after 500ms
setTimeout(() => {
this.$refs.copied.style.display = 'none'
}, 500)
this.$emit('copied')
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/composables/String.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,14 @@ export const removeSlashes = (str, leading = true, trailing = true) => {
}
return str
}

/**
*
* @param length
* @returns {string}
*/
export const generateUuid = (length = 6) => {
return Array.from(crypto.getRandomValues(new Uint8Array(6)), (byte) =>
byte.toString(36).padStart(2, '0')
).join('').substring(0, length)
}
Binary file added frontend/src/images/empty-states/mqtt-empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
67 changes: 67 additions & 0 deletions frontend/src/pages/team/Broker/components/BrokerAclRule.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<div class="acl-rule grid grid-cols-6 gap-4">
<div class="action col-start-2 flex gap-2.5">
<span :class="pubClass" data-el="pub">
<CheckIcon v-if="canPublish" class="ff-icon-sm" />
<XIcon v-else class="ff-icon-sm" />
pub
</span>
<span :class="subClass" data-el="sub">
<CheckIcon v-if="canSubscribe" class="ff-icon-sm" />
<XIcon v-else class="ff-icon-sm" />
sub
</span>
</div>
<div class="pattern">
{{ acl.pattern }}
</div>
</div>
</template>

<script>
import { CheckIcon, XIcon } from '@heroicons/vue/outline'
export default {
name: 'BrokerAclRule',
components: {
CheckIcon,
XIcon
},
props: {
acl: {
required: true,
type: Object
}
},
computed: {
canPublish () {
return ['publish', 'both'].includes(this.acl.action)
},
canOnlyPublish () {
return ['publish'].includes(this.acl.action)
},
canSubscribe () {
return ['subscribe', 'both'].includes(this.acl.action)
},
canOnlySubscribe () {
return ['subscribe'].includes(this.acl.action)
},
pubClass () {
return {
'text-green-500': this.canPublish,
'text-red-500': this.canOnlySubscribe
}
},
subClass () {
return {
'text-green-500': this.canSubscribe,
'text-red-500': this.canOnlyPublish
}
}
}
}
</script>

<style scoped lang="scss">
</style>
177 changes: 177 additions & 0 deletions frontend/src/pages/team/Broker/components/BrokerClient.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
<template>
<ff-accordion class="max-w-full w-full broker-client">
<template #label>
<div class="username text-left flex">
<text-copier :text="client.username + '@' + team.id" confirmation-type="alert" @click.prevent.stop>
<span :title="client.username + '@' + team.id" class="title-wrapper">
<span class="mt-1 font-bold">{{ client.username }}</span>
<span class="italic mt-1">@{{ team.id }}</span>
</span>
</text-copier>
</div>
<div class="rules text-left">
<span>{{ client.acls.length }} Rule{{ client.acls.length > 1 ? 's' : '' }}</span>
</div>
</template>
<template #meta>
<span
class="edit hover:cursor-pointer"
data-action="edit-client"
@click.prevent.stop="$emit('edit-client', client)"
>
<PencilIcon
v-if="hasAMinimumTeamRoleOf(Roles.Owner)"
class="ff-icon-sm"
/>
</span>
<span
class="delete hover:cursor-pointer "
data-action="delete-client"
@click.prevent.stop="$emit('delete-client',client)"
>
<TrashIcon
v-if="hasAMinimumTeamRoleOf(Roles.Owner)"
class="ff-icon-sm text-red-500"
/>
</span>
</template>
<template #content>
<ul class="acl-list">
<li v-for="(acl, $key) in client.acls" :key="$key" class="acl-wrapper" data-el="acl">
<BrokerAclRule :acl="acl" />
</li>
</ul>
</template>
</ff-accordion>
</template>

<script>
import { PencilIcon, TrashIcon } from '@heroicons/vue/outline'
import FfAccordion from '../../../../components/Accordion.vue'
import TextCopier from '../../../../components/TextCopier.vue'
import permissionsMixin from '../../../../mixins/Permissions.js'
import { Roles } from '../../../../utils/roles.js'
import BrokerAclRule from './BrokerAclRule.vue'
export default {
name: 'BrokerClient',
components: {
BrokerAclRule,
TextCopier,
PencilIcon,
FfAccordion,
TrashIcon
},
mixins: [permissionsMixin],
props: {
client: {
required: true,
type: Object
}
},
emits: ['edit-client', 'delete-client'],
computed: {
Roles () {
return Roles
}
}
}
</script>

<style lang="scss">
.ff-accordion.broker-client {
margin-bottom: 0;
button {
border: none;
background: none;
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 15px;
padding: 0;
.username {
padding: 15px 10px;
grid-column: span 2;
overflow: hidden;
.ff-text-copier {
@include truncate;
& > span {
@include truncate;
}
.title-wrapper {
@include truncate;
}
}
.ff-icon {
margin-left: 0;
min-width: 20px;
}
}
.rules {
padding: 15px 10px;
}
.toggle {
grid-column: span 3;
text-align: right;
padding-right: 10px;
display: flex;
align-items: center;
justify-content: flex-end;
.edit, .delete {
padding: 24px 15px;
display: inline-block;
position: relative;
align-self: stretch;
.ff-icon-sm {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition: ease-in-out .3s;
}
&:hover {
.ff-icon-sm {
width: 20px;
height: 20px;
}
}
}
.edit:hover {
color: $ff-grey-700;
}
.delete:hover {
color: $ff-red-700;
}
}
}
.ff-accordion--content {
background: $ff-grey-100;
.acl-list {
.acl-wrapper {
border-bottom: 1px solid $ff-grey-200;
padding: 15px 10px;
gap: 10px;
font-size: 80%;
&:last-of-type {
border: none;
}
}
}
}
}
</style>
Loading

0 comments on commit 0cf9564

Please sign in to comment.