Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f59430d
feat: work on registry integration
albanm May 7, 2026
8c28485
feat: work on registry integration
albanm May 7, 2026
77ae53c
fix: wrong upgrade script directory name
albanm May 7, 2026
4c14eac
chore: missing lib-node-registry package
albanm May 7, 2026
df23bc4
fix: missing packages
albanm May 7, 2026
7b6a033
chore: broken upgrade scripts
albanm May 7, 2026
3e915f9
chore: broken upgrade scripts
albanm May 7, 2026
c8cc4a1
chore: broken upgrade scripts
albanm May 7, 2026
54aa04b
chore: broken upgrade scripts
albanm May 7, 2026
e3509d6
fix: missing category and thumbnails on uploaded processings
albanm May 8, 2026
d62b9ff
chore: upgrade script to reload missing thumbnails
albanm May 8, 2026
79f247b
chore: upgrade script to reload missing thumbnails
albanm May 8, 2026
c0efdf5
chore: upgrade script to reload missing thumbnails
albanm May 11, 2026
b581865
fix: broken upgrade script
albanm May 11, 2026
1e2c052
fix: broken thumbnail urls
albanm May 11, 2026
2daa015
chore: better icon thumbnails
albanm May 12, 2026
66736e6
chore: better icon thumbnails
albanm May 12, 2026
d5ac4f8
docs: design spec for broken-plugin processings
albanm May 13, 2026
305b3d7
docs: implementation plan for broken-plugin processings
albanm May 13, 2026
6871dae
fix(ui): suppress global notif on plugin-fetch error
albanm May 13, 2026
1a209bf
feat(ui): rename 'Deleted' badge to 'Plugin unavailable' with tooltip
albanm May 13, 2026
cc3e650
test: add PATCH /test-env/raw-processing/:id for invalid-state setups
albanm May 13, 2026
3f5b5d2
feat(worker): friendly log when a run's plugin is unavailable
albanm May 13, 2026
c44fa2f
feat(ui): broken-plugin banner + hide form on processing edit page
albanm May 13, 2026
88c2a04
fix(ui): reset pluginBroken on each fetchPlugin call
albanm May 13, 2026
f75d44d
feat(ui): hide exec/duplicate/change-owner when plugin is broken
albanm May 13, 2026
604c773
test(api): pin read-only contract for broken-plugin processings
albanm May 13, 2026
3cd1d9e
test(e2e): list badge, edit banner, delete on broken-plugin processings
albanm May 13, 2026
878a0c8
test(api): assert worker logs friendly message on unavailable plugin
albanm May 13, 2026
e0fc085
chore: upgrade script should not create access grants
albanm May 13, 2026
9a3089b
feat: support branch-format registry artefacts on processings
albanm May 19, 2026
f4abac3
chore: simplify plugins management
albanm May 20, 2026
832b9c5
chore: temporary upgrade script to revert a change in staging
albanm May 20, 2026
ade56a5
fix: broken showing of plugin name
albanm May 20, 2026
f92d533
chore: cleanup superpowers file
albanm May 20, 2026
b04aa2b
chore: remove temporary upgrade sript
albanm May 20, 2026
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
4 changes: 3 additions & 1 deletion api/config/custom-environment-variables.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export default {
port: 'PORT',
privateDirectoryUrl: 'PRIVATE_DIRECTORY_URL',
privateEventsUrl: 'PRIVATE_EVENTS_URL',
privateRegistryUrl: 'PRIVATE_REGISTRY_URL',
secretKeys: {
limits: 'SECRET_LIMITS',
events: 'SECRET_EVENTS',
identities: 'SECRET_IDENTITIES'
identities: 'SECRET_IDENTITIES',
registry: 'SECRET_REGISTRY'
},
observer: {
active: 'OBSERVER_ACTIVE',
Expand Down
15 changes: 12 additions & 3 deletions api/config/default.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
export default {
cipherPassword: undefined,
dataDir: '/app/data',
tmpDir: null, // will be dataDir + '/tmp' if null
// Optional. When set, the legacy plugins volume at <dataDir>/plugins is read
// by the v6.0 boot migration. Drops with v7.0.
dataDir: null,
// Defaults to <dataDir>/tmp when dataDir is set, else <os.tmpdir>/data-fair-processings.
tmpDir: null,
defaultLimits: {
// Maximum time spent running processings
// -1 for unlimited storage
Expand All @@ -10,10 +13,16 @@ export default {
pluginCategories: ['Essentiels', 'Mes plugins', 'Données de références', 'Tests'],
privateDirectoryUrl: 'http://simple-directory:8080',
privateEventsUrl: undefined,
// Internal URL the API uses for server-to-server calls to registry. The UI
// talks to /registry on the same domain (no public URL is configurable).
privateRegistryUrl: 'http://registry:8080',
secretKeys: {
limits: null,
events: undefined,
identities: undefined
identities: undefined,
// x-secret-key shared with registry. Required for save-time validation,
// prepare-hook downloads, and the v6.0 first-boot migration.
registry: undefined
},
mongoUrl: 'mongodb://localhost:27017/data-fair-processings',
port: 8080,
Expand Down
5 changes: 4 additions & 1 deletion api/config/development.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const apiPort = parseInt(process.env.DEV_API_PORT ?? '8082')
const mongoPort = process.env.MONGO_PORT ?? '27017'
const sdPort = process.env.SD_PORT ?? '8080'
const eventsPort = process.env.EVENTS_PORT ?? '8083'
const registryPort = process.env.REGISTRY_PORT ?? '8085'
const observerPort = parseInt(process.env.DEV_API_OBSERVER_PORT ?? '9092')

export default {
Expand All @@ -14,8 +15,10 @@ export default {
port: apiPort,
privateDirectoryUrl: `http://localhost:${sdPort}`,
privateEventsUrl: `http://localhost:${eventsPort}`,
privateRegistryUrl: `http://localhost:${registryPort}/registry`,
secretKeys: {
identities: 'secret-identities',
events: 'secret-events'
events: 'secret-events',
registry: 'secret-registry-internal'
}
}
17 changes: 14 additions & 3 deletions api/config/type/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"additionalProperties": false,
"required": [
"cipherPassword",
"dataDir",
"privateDirectoryUrl",
"privateRegistryUrl",
"mongoUrl",
"pluginCategories",
"port",
Expand All @@ -25,7 +25,8 @@
"type": "string"
},
"dataDir": {
"type": "string"
"type": ["string", "null"],
"description": "Optional. When set, the legacy plugins volume at <dataDir>/plugins is read by the v6.0 boot migration. Drops with v7.0."
},
"tmpDir": {
"type": "string"
Expand Down Expand Up @@ -57,8 +58,14 @@
"privateEventsUrl": {
"type": "string"
},
"privateRegistryUrl": {
"type": "string",
"pattern": "^https?://",
"description": "Internal URL used by the API to call registry server-to-server. The UI hits /registry on the same domain."
},
"secretKeys": {
"type": "object",
"required": ["registry"],
"properties": {
"limits": {
"type": [
Expand All @@ -71,6 +78,10 @@
},
"identities": {
"type": "string"
},
"registry": {
"type": "string",
"description": "x-secret-key shared with registry. Required for save-time validation, prepare-hook downloads, and the v6.0 first-boot migration."
}
}
},
Expand All @@ -97,4 +108,4 @@
"get": {},
"has": {}
}
}
}
1 change: 0 additions & 1 deletion api/doc/plugin/post-req/index.ts

This file was deleted.

21 changes: 0 additions & 21 deletions api/doc/plugin/post-req/schema.js

This file was deleted.

1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dependencies": {
"@data-fair/lib-express": "^1.22.5",
"@data-fair/lib-node": "^2.12.1",
"@data-fair/lib-node-registry": "^0.4.0",
"@data-fair/lib-utils": "^1.10.1",
"@data-fair/processings-shared": "*",
"ajv": "^8.17.1",
Expand Down
12 changes: 7 additions & 5 deletions api/src/admin/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const volumeStatus = async () => {
await fs.writeFile(`${config.dataDir}/check-access.txt`, 'ok')
}

export const getStatus = async (req: Request) =>
runHealthChecks(req, [
{ fn: mongoStatus, name: 'mongodb' },
{ fn: volumeStatus, name: 'data volume' }
])
export const getStatus = async (req: Request) => {
const checks: Array<{ fn: (req: Request) => Promise<void>; name: string }> = [
{ fn: mongoStatus, name: 'mongodb' }
]
if (config.dataDir) checks.push({ fn: volumeStatus, name: 'data volume' })
return runHealthChecks(req, checks)
}

// Helper functions
const getSingleStatus = async (req: Request, fn: (req: Request) => Promise<void>, name: string) => {
Expand Down
4 changes: 0 additions & 4 deletions api/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import express from 'express'
import { session, errorHandler, createSiteMiddleware, createSpaMiddleware, defaultNonceCSPDirectives } from '@data-fair/lib-express/index.js'
import identitiesRouter from './misc/routers/identities.ts'
import limitsRouter from './limits/router.ts'
import pluginsRegistryRouter from './plugins-registry/router.ts'
import pluginsRouter from './plugins/router.ts'
import processingsRouter from './processings/router.ts'
import runsRouter from './runs/router.ts'
import adminRouter from './admin/router.ts'
Expand All @@ -28,8 +26,6 @@ app.get('/api/v1/_ping', (req, res) => {
})

app.use('/api/identities', identitiesRouter)
app.use('/api/v1/plugins-registry', pluginsRegistryRouter)
app.use('/api/v1/plugins', pluginsRouter)
app.use('/api/v1/processings', processingsRouter)
app.use('/api/v1/runs', runsRouter)
app.use('/api/v1/limits', limitsRouter)
Expand Down
18 changes: 16 additions & 2 deletions api/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import path from 'node:path'
import os from 'node:os'
import type { ApiConfig } from '../config/type/index.ts'
import { assertValid } from '../config/type/index.ts'
import config from 'config'

assertValid(config, { lang: 'en', name: 'config', internal: true })

const apiConfig = config as unknown as ApiConfig
const rawConfig = config as unknown as ApiConfig

if (!rawConfig.tmpDir) {
rawConfig.tmpDir = rawConfig.dataDir
? path.join(rawConfig.dataDir, 'tmp')
: path.join(os.tmpdir(), 'data-fair-processings')
}

const apiConfig = rawConfig as ApiConfig & { tmpDir: string }

// cacheDir is always a subdirectory of tmpDir.
export const registryCacheDir = path.join(apiConfig.tmpDir, 'registry-cache')

export default apiConfig

export const uiConfig = {
pluginCategories: apiConfig.pluginCategories,
pluginCategories: apiConfig.pluginCategories
}
export type UiConfig = typeof uiConfig
26 changes: 23 additions & 3 deletions api/src/misc/routers/test-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ router.get('/raw-processing/:id', async (req, res, next) => {
}
})

// Patch a processing document without validation (used by tests to put a
// processing into states that the normal API guards prevent, e.g. setting
// plugin to a value that doesn't resolve in the registry).
router.patch('/raw-processing/:id', async (req, res, next) => {
try {
const result = await mongo.processings.updateOne(
{ _id: req.params.id },
{ $set: req.body }
)
if (result.matchedCount === 0) return res.status(404).json({ error: 'processing not found' })
const processing = await mongo.processings.findOne({ _id: req.params.id })
res.json(processing)
} catch (err) {
next(err)
}
})

// Return the raw MongoDB document for a run
router.get('/raw-run/:id', async (req, res, next) => {
try {
Expand Down Expand Up @@ -93,11 +110,14 @@ router.post('/set-config', (req, res, next) => {
}
})

// Wipe the installed plugins directory (used between test runs)
// Wipe the installed plugins directory (used between test runs).
// No-op when the legacy plugins volume isn't mounted (config.dataDir unset).
router.delete('/plugins', async (req, res, next) => {
try {
const pluginsDir = path.resolve(config.dataDir, 'plugins')
await fs.emptyDir(pluginsDir)
if (config.dataDir) {
const pluginsDir = path.resolve(config.dataDir, 'plugins')
await fs.emptyDir(pluginsDir)
}
res.json({ ok: true })
} catch (err) {
next(err)
Expand Down
Loading
Loading