@flowfuse/flowfuse
Version:
An open source low-code development platform
217 lines (204 loc) • 7.87 kB
JavaScript
const axios = require('axios')
module.exports = async function (app) {
app.addHook('preHandler', async (request, reply) => {
if (request.params.teamId !== undefined || request.params.teamSlug !== undefined) {
if (!request.team) {
// For a :teamId route, we can now lookup the full team object
request.team = await app.db.models.Team.byId(request.params.teamId)
if (!request.team) {
reply.code(404).send({ code: 'not_found', error: 'Not Found' })
return
}
const teamType = await request.team.getTeamType()
if (!teamType.getFeatureProperty('npm', false)) {
reply.code(404).send({ code: 'not_found', error: 'Not Found' })
return // eslint-disable-line no-useless-return
}
}
if (!request.teamMembership && request.session.User) {
request.teamMembership = await request.session.User.getTeamMembership(request.team.id)
}
}
})
/**
* Get Teams npm packages
* @name /api/v1/teams/:teamId/npm/packages
* @static
* @memberof forge.routes.api.team.npm
*/
app.get('/npm/packages', {
preHandler: app.needsPermission('team:packages:read'),
schema: {
summary: 'Gets the private packages owned by this team',
tags: ['NPM Packages']
}
}, async (request, reply) => {
try {
const packageList = await axios.get(`${app.config.npmRegistry?.url}/-/all`, {
// If we can swap this for a teams creds or token then the
// filtering will all be done in the npm repo
auth: {
username: app.config.npmRegistry.admin.username,
password: app.config.npmRegistry.admin.password
}
})
const packages = {}
for (const package in packageList.data) {
if (package === '_updated') {
continue
}
if (package.startsWith(`@flowfuse-${request.params.teamId}/`)) {
packages[package] = packageList.data[package]
}
}
reply.send(packages)
} catch (err) {
reply.status(500).send({ error: 'unknown_error', message: err.toString() })
}
})
/**
* Test is user already has a npm password
*
* @name /api/v1/teams/:teamId/npm/userToken
* @memberof forge.routes.api.team.npm
*/
app.get('/npm/userToken', {
preHandler: app.needsPermission('team:packages:manage'),
schema: {
summary: 'Check if user already has a NPM auth token',
tags: ['NPM packages'],
params: {
type: 'object',
properties: {
teamId: { type: 'string' },
userId: { type: 'string' }
}
},
response: {
200: {
type: 'object'
},
404: {
type: 'object'
},
'4xx': {
$ref: 'APIError'
}
}
}
}, async (request, reply) => {
const token = await app.db.models.AccessToken.findOne({
where: {
ownerType: 'npm',
ownerId: `${request.session.User.username}`
}
})
if (token) {
reply.status(200).send({})
} else {
reply.status(404).send({})
}
})
/**
* Test is user already has a npm password
*
* @name /api/v1/teams/:teamId/npm/userToken
* @memberof forge.routes.api.team.npm
*/
app.post('/npm/userToken', {
preHandler: app.needsPermission('team:packages:manage'),
schema: {
summary: 'Generate a new user password for NPM registry',
tags: ['NPM packages'],
params: {
type: 'object',
properties: {
teamId: { type: 'string' },
userId: { type: 'string' }
}
},
response: {
201: {
type: 'object',
properties: {
username: { type: 'string' },
token: { type: 'string' }
}
},
'4xx': {
$ref: 'APIError'
}
}
}
}, async (request, reply) => {
const token = await app.db.controllers.AccessToken.createTokenForNPM(request.session.User, request.team, ['team:packages:manage'])
reply.status(201).send(token)
})
/**
* Get Team catalogue
* @name /api/v1/teams/:teamId/npm/catalogue
* @static
* @memberof forge.routes.api.team.npm
*/
app.get('/npm/catalogue', {
config: {
allowAnonymous: true
}
}, async (request, reply) => {
if (request.params.teamId && (request.query.device || request.query.instance)) {
const team = await app.db.models.Team.byId(request.params.teamId)
let clientTeam
if (request.query.device) {
const device = await app.db.models.Device.byId(request.query.device)
if (device) {
clientTeam = device.Team.hashid
}
} else if (request.query.instance) {
const instance = await app.db.models.Project.byId(request.query.instance)
if (instance) {
clientTeam = instance.Team.hashid
}
}
// Might not need the first test as undefined !== teamid...
if (!clientTeam || clientTeam !== request.params.teamId) {
reply.status(401).send({ error: 'not_authorized', message: 'Not Authorized' })
return
}
try {
const packageList = await axios.get(`${app.config.npmRegistry?.url}/-/all`, {
// If we can swap this for a teams creds or token then the
// filtering will all be done in the npm repo
auth: {
username: app.config.npmRegistry.admin.username,
password: app.config.npmRegistry.admin.password
}
})
const modules = []
for (const package in packageList.data) {
if (package === '_updated') {
continue
}
if (package.startsWith(`@flowfuse-${request.params.teamId}/`)) {
modules.push({
id: package,
description: packageList.data[package].description,
version: packageList.data[package]['dist-tags'].latest,
updated_at: packageList.data[package].time.modified,
keywords: packageList.data[package].keywords
})
}
}
reply.header('Access-Control-Allow-Origin', '*')
reply.send({
name: `FlowFuse Team ${team.name} catalogue`,
updated_at: '2025-02-23T11:00:00.000Z',
modules
})
} catch (err) {
reply.status(500).send({ error: 'unknown_error', message: err.toString() })
}
} else {
reply.status(404).send({ error: 'not_found', message: 'not found' })
}
})
}