scriptremote
Version:
Message server providing secure remote access to scripted applications from a browser UI
777 lines (737 loc) • 34.2 kB
JavaScript
;
// Comments are structured for apidoc
/**
* @api /:base
* @apiGroup Overview
*
* @apiDescription
* Following are endpoints to access project data for both
* browser and script clients. The browser client is
* used mainly to read messages and respond to reply
* requests from script clients. Script clients create
* projects, jobs, and send messages to them.
* <p>
* This does not include endpoints that are used only by browser
* client to controll the UI.
* <p>
* All endpoints have a <tt>:base</tt> route parameter that depends
* on the type of client, corresponding to the fact that browser
* and script clients use different authentication methods..
* Browser clients should use <tt>'/brsapi/projects/...'</tt> url's,
* which require login/session authentication.
* <p>
* Script clients should use <tt>'/api/projects/...'</tt> url's, which
* require a Basic Auth header. The name and password fields of the header
* are the user id and api token, respectively, obtained from the
* <tt>Settings</tt> page of the website. Each time a token is generated
* on the <tt>Settings</tt> page it becomes the current token value. The
* token value used in the request that creates a job is stored with the job.
* Later requests
* that address a specific job can use either the job's stored token value
* or the current user value in the auth header. Requests not addressing
* a job need to use the current user token value. This permits a new token
* to be generated anytime without affecting existing jobs.
* <p>
* Endpoints that are project-specific use a <tt>:project</tt> route
* parameter that depends on the project ownership.
* A project is owned by the user who created it.
* For a project owned by the authenticated user the project route
* parameter is just the project name. For a project owned by a
* different user the project route parameter is the project name
* followed by the owner's email address, separated by a '~'. The
* project owner controls the list of other users permitted
* to access the project.
* <p>
* Route params defined by the client (project,
* location, file_key) should use url encoding when needed.
* <p>
* For <tt>/brsapi</tt> routes, requests that include data must use json.
* For <tt>/api</tt> routes requests with data can use json or form encoding.
* Requests that upload files use multipart/form-data encoding.
* <p>
* Responses are json objects except for file downloads. The json includes a
* field 'SR_status' with value 'OK' if successful or a
* message if not. For file downloads the response body is
* the file contents.
* <p>
* Response result codes:
* <ul>
* <li>200 - success
* <li>400 - input error other than "url not found"
* <li>401 - auth error
* <li>404 - resource url not found
* <li>500 - server error
* </ul>
* <p>
* Encryption
* <p>
* For an encrypted project the url's will contain encrypted items.
* The server is mostly unaffected by the encryption, except for some
* additional flags and hashes added to the data. Script clients
* that implement encryption need to manage the conversion between
* encrypted and unencrypted url's. The script client also needs
* to be consistent with the browser client, which expects all
* the user-defined data items to be encrypted:
* <ul>
* <li>project names
* <li>job names
* <li>location names
* <li>message content name
* <li>message content values
* <li>reply message content name
* <li>reply message content values
* <li>file tags
* <li>file contents
* </ul>
*
* The user-defined items that can occur in url's are project names, location
* names, and file tags. Jobs and messages are identified by server-generated
* id's and these do not need to be encrypted.
*
* The encryption method is aes-256 ctr and the formatting
* must be either:
* <p>
* 1) openssl enc
* <p>
* This is the format produced by the openssl command with
* an 8-byte salt and a passphrase, using base64 output.
* Any slash ('/') characters in the base64 output
* are then replaced by dash ('-') characters for url safety.
* <p>
* 2) 'normal' format
* <p>
* This is a format that can be produced using common crypto
* libraries: 12-byte-salt + ciphertext, in modified base64 with '+-'
* as the additional characters instead of '+/'. The encryption
* iv is the 12-byte salt + 4-byte counter, initially zero.
* The salt is longer than the usual 8 bytes to reduce the
* possibility of a repeat within the lifetime of a project
* since the passphrase remains constant. A 4 byte counter
* still allows encrypting items up to 64GB, much larger than
* expected in practice.
* <p>
* The encryption key is generated from the passphrase and an
* 8-byte salt using pbkdf2 with a sha256 hash and 10000 rounds.
* Like the passphrase, the 8-byte salt and key for a project
* are fixed at the time of creation. The 8-byte salt is stored
* with the project in the server.
* <p>
* For either encryption format, reply content is also expected to be
* authenticated in both directions using a sha256 digest in hex format,
* with the passphrase used as the key. The string to be authenticated
* is formed by concatenating the encrypted key-value pairs, in order.
* The computed digest is added as an extra key-value pair with a
* key of 'hmac'.
*
* For encrypted shared projects the <tt>:project</tt> route parameter
* is the encrypted project name followed by the plaintext owner's
* email, separated by a tilde character.
*/
var bodyParser = require('body-parser'),
csrf = require('csurf'),
middleModule = require('../../../app/controllers/lib/middle.server.js'),
projectsController = require('../../../app/controllers/api/projects.server.controller.js'),
jobsController = require('../../../app/controllers/api/jobs.server.controller.js'),
locationsController = require('../../../app/controllers/api/locations.server.controller.js'),
msgsController = require('../../../app/controllers/api/messages.server.controller.js'),
filesController = require('../../../app/controllers/api/files.server.controller.js');
module.exports = function(app) {
app.use('/api', bodyParser.urlencoded({extended: true, limit:'50mb'}));
// Add csrf token for routes handled by Angular
var csrfProtect = csrf(app.locals.csurf);
app.use('/brsapi', csrfProtect, function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken(), {'secure': app.locals.secureCookies});
next();
});
/**
* Projects
*/
/**
* @api {get} /:base/projects Projects owned by user
* @apiName GetProjects
* @apiGroup Projects
*
* @apiDescription Returns array of projects owned by the user. Array elements are
* tuples containing name, flag indicating if project is encrypted, and
* AES key generation salt (empty string for openssl format).
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'projects': [[(string),(boolean),(string)],...] }
*/
app.get('/:base/projects',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'projects', [])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.get, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project Project properties
* @apiName GetProject
* @apiGroup Projects
*
* @apiDescription Returns parameters of a project
*
* @apiSuccess (string) owner Email of project owner
* @apiSuccess (string) timestamp Project creation time
* @apiSuccess (string) description Project description
* @apiSuccess (boolean) encrypted Flag indicating if project is encrypted
* @apiSuccess (string) salt Project aes key generation salt or empty string
* @apiSuccess (string) authUsers Array of other users authorized for project and their access level
* @apiSuccess (string) notifyTo Array of users to receive notifications of project messages
*
* @apiSuccessExample {json} Success-Response - non-shared project
* { 'SR_status': 'OK', 'owner': (string), 'timestamp': (string), 'description': (string), 'encrypted': (boolean), 'salt': (string) }
*
* @apiSuccessExample {json} Success-Response - shared project
* { 'SR_status': 'OK', 'owner': (string), 'authUsers': [{email:(string), access:(string)},...], 'notifyTo': [(string), ...], 'timestamp': (string), 'description': (string), 'encrypted': (boolean), 'salt': (string) }
*/
app.get('/:base/projects/:project',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'project', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.get, middleModule.errHandler);
/**
* @api {post} /:base/projects Create project
* @apiName CreateProject
* @apiGroup Projects
*
* @apiDescription Starts a new project with user as owner.
*
* @apiParam project_name (string) Unique new name - fails if name already exists
* @apiParam is_encrypted (boolean) Flag indicating project data (including project_name) is encrypted. Values are strings 'true', 'false', or case variations
* @apiParam timestamp (string) Project creation time - any format
* @apiParam [salt] (string) AES key generation salt, if required
*
* is_encrypted values are 'true', 'false' or case variations.
*
* @apiSuccessExample {json} Success-Response
* {'SR_status': 'OK'}
*/
app.post('/:base/projects',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'owner', 'project-post', [])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.post, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/share Project sharing
* @apiName ShareProject
* @apiGroup Projects
*
* @apiDescription Updates the authorized users of a project.
* When adding a user the access level determines the actions
* permitted for the user. 'read' does not allow creating jobs
* in the project or replying to messages. 'reply' adds
* permission to reply to messages for any job in the project.
* 'write' adds permission to create or delete jobs.
*
* @apiParam email (string)
* @apiParam action (string = 'add', 'remove')
* @apiParam [access] (string ='read', 'reply', 'write') Permission when adding a user
*
* @apiSuccessExample {json} Success-Response
* {'SR_status': 'OK'}
*/
app.put('/:base/projects/:project/share',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'owner', 'share', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.patch, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/description Project description
* @apiName ProjectDescription
* @apiGroup Projects
*
* @apiDescription Updates the description of a project.
*
* @apiParam description (string)
*
* @apiSuccessExample {json} Success-Response
* {'SR_status': 'OK'}
*/
app.put('/:base/projects/:project/description',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', 'description', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.patch, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/notify Project notifications
* @apiName ProjectNotifications
* @apiGroup Projects
*
* @apiDescription Updates the notification subscribers of a project
*
* @apiParam nickname (string) Project nickname displayed in notifications
* @apiParam action (string='on','off') Turn notification on or off
*
* @apiSuccessExample {json} Success-Response
* {'SR_status': 'OK'}
*/
app.put('/:base/projects/:project/notify',
function(req, res, next) {
// Project permission required is only 'read' even though it's
// a 'put' so that notification is possible for readers
middleModule.copyParams(req, res, next, 'read', 'notify', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.patch, middleModule.errHandler);
/**
* @api {delete} /:base/projects/:project Delete project
* @apiName DeleteProject
* @apiGroup Projects
*
* @apiDescription Deletes a project and all its related data
*
* @apiSuccessExample {json} Success-Response
* {'SR_status': 'OK'}
*/
app.delete('/:base/projects/:project',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'owner', 'delete', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.delete, middleModule.errHandler);
/**
* @api {get} /:base/projects-share/:user Projects shared by another user
* @apiName GetProjectsShare
* @apiGroup Projects
*
* @apiDescription Returns array of projects owned by another user that are shared to
* authenticating user. The :owner parameter is the email of owner. Array elements are
* tuples containing name, flag indicating if project is encrypted, and AES key generation
* salt (empty string if none). This is mainly used to determine the encrypted project
* name corresponding to a plaintext name.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'projects': [[(string),(boolean),(string)],...] }
*/
app.get('/:base/projects-share/:owner',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'projects-share', ['owner'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, projectsController.get, middleModule.errHandler);
/**
* Jobs
*/
/**
* @api {get} /:base/projects/:project/jobs Jobs belonging to a project
* @apiName ProjectJobs
* @apiGroup Jobs
*
* @apiDescription Returns array of jobs of a project, including
^ id, name, and timestamp
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'jobs': [{'id':(string), 'name':(string), 'timestamp':(string)},...] }
*/
app.get('/:base/projects/:project/jobs',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'jobs', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.get, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid Job properties
* @apiName GetJob
* @apiGroup Jobs
*
* @apiDescription Returns parameters for a job
*
* @apiSuccess (string) job_name User-assigned job name
* @apiSuccess (string) max_msgs Max messages retained per location
* @apiSuccess (string) description Project description
* @apiSuccess (string) timestamp Job creation time
* @apiSuccess (string) end Flag indicating if job has ended
* @apiSuccess (string) timestamp_end Job end time
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK',
* 'job_name': (string),
* 'description': (string),
* 'max_msgs': (number),
* 'timestamp': (string),
* 'end': (boolean),
* 'timestamp_end': (string) }
*/
app.get('/:base/projects/:project/jobs/:job',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'job', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.get, middleModule.errHandler);
/**
* @api {post} /:base/projects/:project/jobs Create job
* @apiName CreateJob
* @apiGroup Jobs
*
* @apiDescription Starts a new job in a project, returns job id.
*
* @apiParam job_name (string) Job name - need not be unique
* @apiParam max_msgs (string) Max messages to retain per location
* @apiParam timestamp (string) Job creation time - any format
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'job': (string) }
*/
app.post('/:base/projects/:project/jobs',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.post, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/jobs/:job/description Job description
* @apiName JobDescription
* @apiGroup Jobs
*
* @apiDescription Updates the description of a job
*
* @apiParam description (string)
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.put('/:base/projects/:project/jobs/:job/description',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', 'description', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.patch, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/jobs/:jobid End job
* @apiName EndJob
* @apiGroup Jobs
*
* @apiDescription Ends a job - no more messages can be added to it
*
* @apiParam timestamp (string)
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.put('/:base/projects/:project/jobs/:job/end',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', 'end', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.patch, middleModule.errHandler);
/**
* @api {delete} /:base/projects/:project/jobs/:jobid Delete job
* @apiName DeleteJob
* @apiGroup Jobs
*
* @apiDescription Deletes a job and all its related data
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.delete('/:base/projects/:project/jobs/:job',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, jobsController.delete, middleModule.errHandler);
/**
* Locations
*/
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations Locations of a job
* @apiName GetLocations
* @apiGroup Locations
*
* @apiDescription Returns array of location names for a job
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'locations': [(string),...] }
*/
app.get('/:base/projects/:project/jobs/:job/locations',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'locations', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, locationsController.get, middleModule.errHandler);
/**
* @api {post} /:base/projects/:project/jobs/:jobid/locations Create location
* @apiName CreateLocation
* @apiGroup Locations
*
* @apiDescription Starts a new location in a job
*
* @apiParam location_name (string) Location name - must be unique
* @apiParam timestamp (string) Location creation time - any format
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK'}
*/
app.post('/:base/projects/:project/jobs/:job/locations',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project', 'job'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, locationsController.post, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location Location properties
* @apiName GetLocation
* @apiGroup Locations
*
* @apiDescription Returns parameters of a location
*
* @apiSuccess (string) timestamp Location creation time
* @apiSuccess (integer) msgcnt Messages at the location
* @apiSuccess (string) description Location description
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'timestamp': (string), 'msgcnt': (integer), 'description': (string) }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'location', ['project', 'job', 'location'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, locationsController.get, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/jobs/:jobid/locations/:location/description Location description
* @apiName LocationDescription
* @apiGroup Locations
*
* @apiDescription Updates the description of a location
*
* @apiParam description (string)
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK'}
*/
app.put('/:base/projects/:project/jobs/:job/locations/:location/description',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project', 'job', 'location'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, locationsController.patch, middleModule.errHandler);
/**
* @api {delete} /:base/projects/:project/jobs/:jobid/locations/:location Delete location
* @apiName DeleteLocation
* @apiGroup Locations
*
* @apiDescription Deletes a location and all its related data
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK'}
*/
app.delete('/:base/projects/:project/jobs/:job/locations/:location',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project', 'job', 'location'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, locationsController.delete, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs Messages at location
* @apiName GetMessages
* @apiGroup Messages
*
* @apiDescription Returns array of message id's and timestamps for a location
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'messages': [{'id': (string), 'timestamp': (string)},...] }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'msgs', ['project', 'job', 'location'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.get, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid Message content
* @apiName GetMessage
* @apiGroup Messages
*
* @apiDescription Returns the contents of a message:
*
* @apiSuccess (json) content Sent message data
* @apiSuccess (boolean) is_reply Flag indicating if reply requested
* @apiSuccess (json) [reply_content] Initial reply data from script or final from browser
* @apiSuccess (boolean) [reply_done] Flag indicating if reply sent from browser
* @apiSuccess (boolean) [reply_ack] Flag indicating if reply received by script
* @apiSuccess (string) timestamp Message creation time
*
* @apiSuccessExample {json} Success-Response - no reply requested
* { ''SR_status': 'OK', content': (json), 'is_reply': (false), 'timestamp': (string) }
*
* @apiSuccessExample {json} Success-Response - reply requested
* { 'SR_status': 'OK', 'content': (json), 'is_reply': (true), 'reply_content': (json),
* 'reply_done': (boolean), 'reply_ack': (boolean), 'timestamp': (string) }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'msg', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.get, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/shorts Message short url's
* @apiName Shorts
* @apiGroup Messages
*
* @apiDescription Returns json array of shortened url keys for a message.
* This is mainly for testing.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'keys': [(string),...] }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/shorts',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'shorts', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.get, middleModule.errHandler);
/**
* @api {post} /:base/projects/:project/jobs/:jobid/locations/:location/msgs Create message
* @apiName CreateMessage
* @apiGroup Messages
*
* @apiDescription Adds a new message to given job/location. Location may already
* exist or be new. Returns new message id.
*
* @apiParam [content] (json) Message content
* @apiParam is_reply (boolean) Flag indicating if browser reply is requested
* @apiParam [reply_content] (json) Initial reply content
* @apiParam timestamp (string) Message creation time - any format
*
* is_reply values are strings 'true', 'false' or case variations.
*
* The browser client expects content and reply_content to be
* empty strings or arrays of objects of the form
* {'name': (string), 'value': (string)}.
*
* For reply messages (is_reply = 'true') browser and script
* clients can use a handshake sequence to confirm reply
* is received by script:
* <ul>
* <li>script sends POST message with reply request
* <li>script sends long-polling GET to wait for reply
* <li>browser eventually sends GET to retrieve new message
* <li>browser eventually sends PUT to update message reply
* <li>script receives GET response and sends PUT to acknowledge
* <li>browser eventually sends GET to check for acknowledge
* </ul>
* The browser displays the status of the message for feedback,
* but the browser actions only occur if the user initiates them.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'message': (string) }
*
*/
app.post('/:base/projects/:project/jobs/:job/locations/:location/msgs',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', 'msg', ['project', 'job', 'location'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.post, middleModule.errHandler);
/**
* @api {post} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/files Add file
* @apiName AddFile
* @apiGroup Messages
*
* @apiDescription Uploads a file to attach to a message,
* using multipart/form-data encoding. The first part is the file
* key which is an identifer that can be referenced in other message
* content or later request url's. The remaining parts contain the file
* contents.
*
* @apiHeader (string) Content-Type: multipart/form-data; boundary=(boundary)
*
* --(boundary)--
*
* @apiHeader (string) Content-Disposition: form-data; name="file_key"
* </br>(string)
* </br>--(boundary)--
*
* @apiHeader (string) content-disposition: form-data; name="file"; filename="(string)"
* @apiHeader (string) content-type: text/plain | image/png | image/svg+xml
* </br>... contents of file ...
* </br>--(boundary)--
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.post('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/files',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', '', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, filesController.upload, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/reply Put reply
* @apiName PutReply
* @apiGroup Messages
*
* @apiDescription Replaces the original reply content of a message. Fails if a reply PUT
* was done previously. Normally used by browser clients.
*
* @apiParam reply_content (json)
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.put('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/reply',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'reply', 'reply', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.patch, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/reply Get reply
* @apiName GetReply
* @apiGroup Messages
*
* @apiDescription Waits until a PUT reply to a message has occurred then returns the
* new reply content. Normally used by script clients. The request may timeout or be
* killed during very long waits. Client should allow for this and also implement
* any desired time limits.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'reply_content': (json) }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/reply',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'reply', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.get, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/reply_ack Get acknowledge
* @apiName GetAck
* @apiGroup Messages
*
* @apiDescription Returns reply_ack value of the message. A true value indicates successful
* GET occurred following a reply PUT. Normally used by browser clients.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'reply_ack': (boolean) }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/reply_ack',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'reply_ack', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.get, middleModule.errHandler);
/**
* @api {put} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/reply_ack Put acknowledge
* @apiName PutAck
* @apiGroup Messages
*
* @apiDescription Acknowledges a successful GET of reply following a reply PUT. Normally used by script clients.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK' }
*/
app.put('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/reply_ack',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'write', 'reply_ack', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, msgsController.patch, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/files File keys
* @apiName FileKeys
* @apiGroup Messages
*
* @apiDescription Returns array of the file keys for the attachments of a message. The keys are indentifiers that were defined in the requests that uploaded the files.
*
* @apiSuccessExample {json} Success-Response
* { 'SR_status': 'OK', 'file_keys': [(string),...] }
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/files',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'list', ['project', 'job', 'location', 'msg'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, filesController.download, middleModule.errHandler);
/**
* @api {get} /:base/projects/:project/jobs/:jobid/locations/:location/msgs/:msgid/files/:file_key Get file
* @apiName GetFile
* @apiGroup Messages
*
* @apiDescription Downloads a file attachment of a message using its key. Returns file type in the
* Content-Type header and file content as the response body.
*/
app.get('/:base/projects/:project/jobs/:job/locations/:location/msgs/:msg/files/:file_key',
function(req, res, next) {
middleModule.copyParams(req, res, next, 'read', 'file', ['project', 'job', 'location', 'msg', 'file_key'])
},
middleModule.authRest, middleModule.validateParams, middleModule.parse, filesController.download, middleModule.errHandler);
}