Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions forge/db/migrations/20260717-01-add-node-red-node-version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { DataTypes } = require('sequelize')

module.exports = {
/**
* upgrade database
* @param {QueryInterface} context Sequelize.QueryInterface
*/
up: async (context, Sequelize) => {
await context.createTable('NodeREDNodeVersions', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
ownerId: {
type: DataTypes.STRING,
allowNull: false
},
ownerType: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
currentVersion: {
type: DataTypes.STRING,
allowNull: false
},
latestVersion: {
type: DataTypes.STRING,
allowNull: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false
}
})
},
down: async (context, Sequelize) => {}
}
66 changes: 66 additions & 0 deletions forge/db/models/NodeREDNodeVersions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Which nodes are installed in which Instances
* @namespace forge.db.models.NodeREDNodeVersion
* // type helpers for design time help and error checking
* @typedef {import('sequelize').Model} Model
* @typedef {import('sequelize').ModelAttributes} ModelAttributes
* @typedef {import('sequelize').SchemaOptions} SchemaOptions
* @typedef {import('sequelize').ModelIndexesOptions} ModelIndexesOptions
* @typedef {import('sequelize').InitOptions} InitOptions
* @typedef {import('sequelize').ModelScopeOptions} ModelScopeOptions
* @typedef {{name: string, schema: ModelAttributes, model: Model, indexes?: ModelIndexesOptions[], scopes?: ModelScopeOptions, options?: InitOptions}} FFModel
*/

const { DataTypes } = require('sequelize')

/** @type {FFModel} */
module.exports = {
name: 'NodeREDNodeVersions',
schema: {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
ownerId: {
type: DataTypes.STRING,
allowNull: false
},
ownerType: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING,
allowNull: false
},
currentVersion: {
type: DataTypes.STRING,
allowNull: false
},
latestVersion: {
type: DataTypes.STRING,
allowNull: true
}
},
associations: function (M) {
this.belongsTo(M.Project, { foreignKey: 'ownerId', constraints: false })
this.belongsTo(M.Device, { foreignKey: 'ownerId', constraints: false })
},
finders: function (M) {
return {
static: {
updateAllLatest: async (name, version) => {
await this.update({
latestVersion: version
}, {
where: {
name
}
})
}
},
instance: {}
}
}
}
7 changes: 7 additions & 0 deletions forge/db/models/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,13 @@ module.exports = {
app.log.error(`Error removing MCPRegistrations for deleted instance ${project.id}: ${err.message}`)
}
}
// Remove version info when Project removed
await app.db.models.NodeREDNodeVersions.destory({
where: {
ownerType: 'instance',
ownerId: project.id
}
})
}
}
},
Expand Down
30 changes: 30 additions & 0 deletions forge/db/models/StorageSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
const { DataTypes } = require('sequelize')

const NODE_VERSION_CACHE = 'nodes-latestVersion'

module.exports = {
name: 'StorageSettings',
schema: {
Expand All @@ -23,5 +25,33 @@ module.exports = {
}
}
}
},
hooks: function (M, app) {
return {
afterUpdate: async (storageSettings, options) => {
try {
const cache = app.caches.getCache(NODE_VERSION_CACHE)
await storageSettings.reload({
attributes: ['id', 'settings', 'ProjectId']
})
const settingsObj = JSON.parse(storageSettings.settings)
const nodes = settingsObj.nodes
for (const n of Object.keys(nodes)) {
if (n !== 'node-red') {
const latest = await cache.get(n)
await app.db.models.NodeREDNodeVersions.upsert({
ownerId: storageSettings.ProjectId,
ownerType: 'instance',
name: n,
currentVersion: nodes[n].version,
latestVersion: latest
})
}
}
} catch (err) {
// swallowing error as this is an async hook triggered by an update to the underlying table
}
}
}
}
}
3 changes: 2 additions & 1 deletion forge/db/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ const modelTypes = [
'TeamBrokerClient',
'BrokerCredentials',
'MQTTTopicSchema',
'TeamBrokerAgent'
'TeamBrokerAgent',
'NodeREDNodeVersions'
]

// A local map of the known models.
Expand Down
4 changes: 4 additions & 0 deletions forge/ee/lib/bom/tasks/cache-catalogues.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ module.exports = {
for (const mod of modules) {
const name = mod.id
const version = mod.version
const current = await cache.get(name)
if (current && current !== version) {
await app.models.NodeREDNodeVersions.updateAllLatest(name, version)
}
cache.set(name, version)
}
}
Expand Down
2 changes: 1 addition & 1 deletion forge/ee/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
(Object.prototype.hasOwnProperty.call(app.config?.expert ?? {}, 'insights') ? !!app.config?.expert?.insights?.enabled : true)

app.config.features.register('expertInsights', isInsightsEnabled ?? false, true)

Check failure on line 64 in forge/ee/lib/index.js

View workflow job for this annotation

GitHub Actions / Postgres tests

Trailing spaces not allowed
require('./bom').init(app)
}

Expand Down
Loading