Skip to content
Open
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: {}
}
}
}
8 changes: 8 additions & 0 deletions forge/db/models/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,14 @@ module.exports = {
app.log.error(`Error removing MCPRegistrations for deleted instance ${project.id}: ${err.message}`)
}
}
if (app.db.models.NodeREDNodeVersions?.destory) {
await app.db.models.destory({
Comment on lines +263 to +264

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt the guard is necessary as (unlike MCP) this model is in the core set and will always be available. But we do need to fix the reference...

Suggested change
if (app.db.models.NodeREDNodeVersions?.destory) {
await app.db.models.destory({
if (app.db.models.NodeREDNodeVersions?.destory) {
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) {
// console.log(err)
}
}
}
}
}
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
Loading