diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index d249552b1..85ee280c1 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -982,6 +982,15 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout }); }; + Client.prototype.ackAppChecklistItem = function (appId, key, acknowledged, callback) { + put('/api/v1/apps/' + appId + '/checklist/' + key, { done: acknowledged }, null, function (error, data, status) { + if (error) return callback(error); + if (status !== 202) return callback(new ClientError(status, data)); + + callback(null); + }); + }; + Client.prototype.updateApp = function (id, manifest, options, callback) { var data = { appStoreId: manifest.id + '@' + manifest.version, diff --git a/dashboard/src/views/app.html b/dashboard/src/views/app.html index feea398b5..6a24028b3 100644 --- a/dashboard/src/views/app.html +++ b/dashboard/src/views/app.html @@ -742,14 +742,14 @@ Hide Checklist

-
+
{{ item.message }} - +
-
+
{{ item.message }}
diff --git a/dashboard/src/views/app.js b/dashboard/src/views/app.js index 703bbc924..a7e7f1fcd 100644 --- a/dashboard/src/views/app.js +++ b/dashboard/src/views/app.js @@ -182,11 +182,12 @@ angular.module('Application').controller('AppController', ['$scope', '$location' $scope.info.notes.busy = false; }, - checklistAck(item) { + checklistAck(item, key) { item.acknowledged = true; + // item.acknowledged = !item.acknowledged; - Client.configureApp($scope.app.id, 'checklist', { checklist: $scope.app.checklist }, function (error) { - if (error) return console.error('Failed to save checklist.', error); + Client.ackAppChecklistItem($scope.app.id, key, item.acknowledged, function (error) { + if (error) return console.error('Failed to ack checklist item.', error); $scope.info.hasOldChecklist = true; }); } diff --git a/src/apps.js b/src/apps.js index 3e641e969..2b62d4384 100644 --- a/src/apps.js +++ b/src/apps.js @@ -32,7 +32,7 @@ exports = module.exports = { setIcon, setTags, setNotes, - setChecklist, + setChecklistItem, setMemoryLimit, setCpuQuota, setMounts, @@ -1550,11 +1550,22 @@ async function setNotes(app, notes, auditSource) { await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: app.id, app, notes }); } -async function setChecklist(app, checklist, auditSource) { +async function setChecklistItem(app, checklistItemKey, acknowledged, auditSource) { assert.strictEqual(typeof app, 'object'); - assert.strictEqual(typeof checklist, 'object'); + assert.strictEqual(typeof checklistItemKey, 'string'); + assert.strictEqual(typeof acknowledged, 'boolean'); assert.strictEqual(typeof auditSource, 'object'); + if (!app.checklist[checklistItemKey]) throw new BoxError(BoxError.NOT_FOUND, 'no such checklist item'); + + // nothing changed + if (app.checklist[checklistItemKey].acknowledged === acknowledged) return; + + const checklist = app.checklist; + checklist[checklistItemKey].acknowledged = acknowledged; + checklist[checklistItemKey].changedAt = Date.now(); + checklist[checklistItemKey].changedBy = auditSource.username; + await update(app.id, { checklist }); await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId: app.id, app, checklist }); } diff --git a/src/apptask.js b/src/apptask.js index 4f3edee65..f040d0fef 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -255,7 +255,10 @@ async function updateChecklist(app, newChecks) { const item = { acknowledged: false, sso: newChecks[k].sso, - message: newChecks[k].message + appVersion: app.version, + message: newChecks[k].message, + changedAt: 0, + changedBy: null, // username from audit log }; if (typeof item.sso === 'undefined') checklist[k] = item; diff --git a/src/routes/apps.js b/src/routes/apps.js index d002f2102..c44359594 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -24,7 +24,6 @@ exports = module.exports = { setLabel, setTags, setNotes, - setChecklist, setIcon, setTurn, setRedis, @@ -43,6 +42,8 @@ exports = module.exports = { setMounts, setUpstreamUri, + setChecklistItem, + stop, start, restart, @@ -271,16 +272,16 @@ async function setNotes(req, res, next) { next(new HttpSuccess(200, {})); } -async function setChecklist(req, res, next) { +async function setChecklistItem(req, res, next) { assert.strictEqual(typeof req.body, 'object'); assert.strictEqual(typeof req.app, 'object'); - if (typeof req.body.checklist !== 'object') return next(new HttpError(400, 'checklist must be an object')); + if (typeof req.body.done !== 'boolean') return next(new HttpError(400, 'done must be a boolean')); - const [error] = await safe(apps.setChecklist(req.app, req.body.checklist, AuditSource.fromRequest(req))); + const [error] = await safe(apps.setChecklistItem(req.app, req.params.key, req.body.done, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); - next(new HttpSuccess(200, {})); + next(new HttpSuccess(202, {})); } async function setIcon(req, res, next) { diff --git a/src/server.js b/src/server.js index 34838b30a..be0998f1a 100644 --- a/src/server.js +++ b/src/server.js @@ -247,7 +247,6 @@ async function initializeExpressSync() { router.post('/api/v1/apps/:id/configure/tags', json, token, routes.apps.load, authorizeOperator, routes.apps.setTags); router.post('/api/v1/apps/:id/configure/icon', json, token, routes.apps.load, authorizeOperator, routes.apps.setIcon); router.post('/api/v1/apps/:id/configure/notes', json, token, routes.apps.load, authorizeOperator, routes.apps.setNotes); - router.post('/api/v1/apps/:id/configure/checklist', json, token, routes.apps.load, authorizeOperator, routes.apps.setChecklist); router.post('/api/v1/apps/:id/configure/memory_limit', json, token, routes.apps.load, authorizeOperator, routes.apps.setMemoryLimit); router.post('/api/v1/apps/:id/configure/cpu_quota', json, token, routes.apps.load, authorizeOperator, routes.apps.setCpuQuota); router.post('/api/v1/apps/:id/configure/automatic_backup', json, token, routes.apps.load, authorizeOperator, routes.apps.setAutomaticBackup); @@ -275,6 +274,7 @@ async function initializeExpressSync() { router.get ('/api/v1/apps/:id/backups', token, routes.apps.load, authorizeOperator, routes.apps.listBackups); router.post('/api/v1/apps/:id/backups/:backupId', json, token, routes.apps.load, authorizeOperator, routes.apps.updateBackup); router.get ('/api/v1/apps/:id/backups/:backupId/download', token, routes.apps.load, authorizeOperator, routes.apps.downloadBackup); + router.put ('/api/v1/apps/:id/checklist/:key', json, token, routes.apps.load, authorizeOperator, routes.apps.setChecklistItem); router.post('/api/v1/apps/:id/start', json, token, routes.apps.load, authorizeOperator, routes.apps.start); router.post('/api/v1/apps/:id/stop', json, token, routes.apps.load, authorizeOperator, routes.apps.stop); router.post('/api/v1/apps/:id/restart', json, token, routes.apps.load, authorizeOperator, routes.apps.restart);