Преглед изворни кода

add per-user compilation in server

when no user is defined, fallback to per-project compilation
Brian Gough пре 10 година
родитељ
комит
9d7f129b61

+ 20 - 18
services/web/app/coffee/Features/Compile/ClsiManager.coffee

@@ -11,11 +11,11 @@ ClsiCookieManager = require("./ClsiCookieManager")
 
 module.exports = ClsiManager =
 
-	sendRequest: (project_id, options = {}, callback = (error, success) ->) ->
+	sendRequest: (project_id, user_id, options = {}, callback = (error, success) ->) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
 			return callback(error) if error?
 			logger.log project_id: project_id, "sending compile to CLSI"
-			ClsiManager._postToClsi project_id, req, options.compileGroup, (error, response) ->
+			ClsiManager._postToClsi project_id, user_id, req, options.compileGroup, (error, response) ->
 				if error?
 					logger.err err:error, project_id:project_id, "error sending request to clsi"
 					return callback(error)
@@ -27,14 +27,13 @@ module.exports = ClsiManager =
 					outputFiles = ClsiManager._parseOutputFiles(project_id, response?.compile?.outputFiles)
 					callback(null, response?.compile?.status, outputFiles, clsiServerId)
 
-	deleteAuxFiles: (project_id, options, callback = (error) ->) ->
-		compilerUrl = @_getCompilerUrl(options?.compileGroup)
+	deleteAuxFiles: (project_id, user_id, options, callback = (error) ->) ->
+		compilerUrl = @_getCompilerUrl(options?.compileGroup, project_id, user_id)
 		opts =
-			url:"#{compilerUrl}/project/#{project_id}"
+			url:compilerUrl
 			method:"DELETE"
 		ClsiManager._makeRequest project_id, opts, callback
 
-
 	_makeRequest: (project_id, opts, callback)->
 		ClsiCookieManager.getCookieJar project_id, (err, jar)->
 			if err?
@@ -51,14 +50,17 @@ module.exports = ClsiManager =
 						
 					return callback err, response, body
 
+	_getCompilerUrl: (compileGroup, project_id, user_id, action) ->
+		host = Settings.apis.clsi.url
+		path = "/project/#{project_id}"
+		path += "/user/#{user_id}" if user_id?
+		path += "/#{action}" if action?
+		return "#{host}#{path}"
 
-	_getCompilerUrl: (compileGroup) ->
-		return Settings.apis.clsi.url
-
-	_postToClsi: (project_id, req, compileGroup, callback = (error, response) ->) ->
-		compilerUrl = Settings.apis.clsi.url
-		opts = 
-			url:  "#{compilerUrl}/project/#{project_id}/compile"
+	_postToClsi: (project_id, user_id, req, compileGroup, callback = (error, response) ->) ->
+		compileUrl = @_getCompilerUrl(compileGroup, project_id, user_id, "compile")
+		opts =
+			url:  compileUrl
 			json: req
 			method: "POST"
 		ClsiManager._makeRequest project_id, opts, (error, response, body) ->
@@ -133,15 +135,15 @@ module.exports = ClsiManager =
 							resources: resources
 					}
 
-	wordCount: (project_id, file, options, callback = (error, response) ->) ->
+	wordCount: (project_id, user_id, file, options, callback = (error, response) ->) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
-			compilerUrl = ClsiManager._getCompilerUrl(options?.compileGroup)
 			filename = file || req?.compile?.rootResourcePath
-			wordcount_url = "#{compilerUrl}/project/#{project_id}/wordcount?file=#{encodeURIComponent(filename)}"
-			if req.compile.options.imageName?
-				wordcount_url += "&image=#{encodeURIComponent(req.compile.options.imageName)}"
+			wordcount_url = ClsiManager._getCompilerUrl(options?.compileGroup, project_id, user_id, "wordcount")
 			opts =
 				url: wordcount_url
+				qs:
+					file: filename
+					image: req.compile.options.imageName
 				method: "GET"
 			ClsiManager._makeRequest project_id, opts, (error, response, body) ->
 				return callback(error) if error?

+ 47 - 17
services/web/app/coffee/Features/Compile/CompileController.coffee

@@ -29,7 +29,9 @@ module.exports = CompileController =
 				options.compiler = req.body.compiler
 			if req.body?.draft
 				options.draft = req.body.draft
-			logger.log {options, project_id}, "got compile request"
+			if req.query?.isolated is "true"
+				options.isolated = true
+			logger.log {options:options, project_id:project_id, user_id:user_id}, "got compile request"
 			CompileManager.compile project_id, user_id, options, (error, status, outputFiles, clsiServerId, limits) ->
 				return next(error) if error?
 				res.contentType("application/json")
@@ -40,6 +42,13 @@ module.exports = CompileController =
 					clsiServerId:clsiServerId
 				}
 
+	_compileAsUser: (req, callback) ->
+		isolated = req.query?.isolated is "true"
+		if isolated
+			AuthenticationController.getLoggedInUserId req, callback
+		else
+			callback()
+
 	downloadPdf: (req, res, next = (error) ->)->
 		Metrics.inc "pdf-downloads"
 		project_id = req.params.Project_id
@@ -76,28 +85,40 @@ module.exports = CompileController =
 
 	deleteAuxFiles: (req, res, next) ->
 		project_id = req.params.Project_id
-		CompileManager.deleteAuxFiles project_id, (error) ->
+		CompileController._compileAsUser req, (error, user_id) ->
 			return next(error) if error?
-			res.sendStatus(200)
+			CompileManager.deleteAuxFiles project_id, user_id, (error) ->
+				return next(error) if error?
+				res.sendStatus(200)
 
 	compileAndDownloadPdf: (req, res, next)->
 		project_id = req.params.project_id
-		CompileManager.compile project_id, null, {}, (err)->
-			if err?
-				logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
-				res.sendStatus 500
-			url = "/project/#{project_id}/output/output.pdf"
-			CompileController.proxyToClsi project_id, url, req, res, next
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileManager.compile project_id, user_id, {}, (err)->
+				if err?
+					logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
+					res.sendStatus 500
+				url = "/project/#{project_id}/output/output.pdf"
+				CompileController.proxyToClsi project_id, user_id, url, req, res, next
 
 	getFileFromClsi: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		build = req.params.build
-		if build?
+		user = req.params.user
+		if user? and build?
+			url = "/project/#{project_id}/user/#{user}/build/#{build}/output/#{req.params.file}"
+		else if build?
 			url = "/project/#{project_id}/build/#{build}/output/#{req.params.file}"
 		else
 			url = "/project/#{project_id}/output/#{req.params.file}"
 		CompileController.proxyToClsi(project_id, url, req, res, next)
 
+	_getUrl: (project_id, user_id, action) ->
+		path = "/project/#{project_id}"
+		path += "/user/#{user_id}" if user_id?
+		return "#{path}/#{action}"
+
 	proxySyncPdf: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		{page, h, v} = req.query
@@ -107,8 +128,12 @@ module.exports = CompileController =
 			return next(new Error("invalid h parameter"))
 		if not v?.match(/^\d+\.\d+$/)
 			return next(new Error("invalid v parameter"))
-		destination = {url: "/project/#{project_id}/sync/pdf", qs: {page, h, v}}
-		CompileController.proxyToClsi(project_id, destination, req, res, next)
+		url = CompileController._getUrl(project_id, user_id, "sync/pdf")
+		destination = {url: url, qs: {page, h, v}}
+		# whether this request is going to a per-user container
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileController.proxyToClsi(project_id, destination, req, res, next)
 
 	proxySyncCode: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
@@ -119,8 +144,11 @@ module.exports = CompileController =
 			return next(new Error("invalid line parameter"))
 		if not column?.match(/^\d+$/)
 			return next(new Error("invalid column parameter"))
-		destination = {url:"/project/#{project_id}/sync/code", qs: {file, line, column}}
-		CompileController.proxyToClsi(project_id, destination, req, res, next)
+		url = CompileController._getUrl(project_id, user_id, "sync/code")
+		destination = {url:url, qs: {file, line, column}}
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileController.proxyToClsi(project_id, destination, req, res, next)
 
 	proxyToClsi: (project_id, url, req, res, next = (error) ->) ->
 		if req.query?.compileGroup
@@ -168,7 +196,9 @@ module.exports = CompileController =
 	wordCount: (req, res, next) ->
 		project_id = req.params.Project_id
 		file   = req.query.file || false
-		CompileManager.wordCount project_id, file, (error, body) ->
+		CompileController._compileAsUser req, (error, user_id) ->
 			return next(error) if error?
-			res.contentType("application/json")
-			res.send body
+			CompileManager.wordCount project_id, user_id, file, (error, body) ->
+				return next(error) if error?
+				res.contentType("application/json")
+				res.send body

+ 6 - 5
services/web/app/coffee/Features/Compile/CompileManager.coffee

@@ -37,15 +37,16 @@ module.exports = CompileManager =
 							return callback(error) if error?
 							for key, value of limits
 								options[key] = value
-							ClsiManager.sendRequest project_id, options, (error, status, outputFiles, clsiServerId) ->
+							user_id = undefined if not options.isolated
+							ClsiManager.sendRequest project_id, user_id, options, (error, status, outputFiles, clsiServerId) ->
 								return callback(error) if error?
 								logger.log files: outputFiles, "output files"
 								callback(null, status, outputFiles, clsiServerId, limits)
 								
-	deleteAuxFiles: (project_id, callback = (error) ->) ->
+	deleteAuxFiles: (project_id, user_id, callback = (error) ->) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 			return callback(error) if error?
-			ClsiManager.deleteAuxFiles project_id, limits, callback
+			ClsiManager.deleteAuxFiles project_id, user_id, limits, callback
 
 	getProjectCompileLimits: (project_id, callback = (error, limits) ->) ->
 		Project.findById project_id, {owner_ref: 1}, (error, project) ->
@@ -92,7 +93,7 @@ module.exports = CompileManager =
 			else
 				ProjectRootDocManager.setRootDocAutomatically project_id, callback
 		
-	wordCount: (project_id, file, callback = (error) ->) ->
+	wordCount: (project_id, user_id, file, callback = (error) ->) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 			return callback(error) if error?
-			ClsiManager.wordCount project_id, file, limits, callback
+			ClsiManager.wordCount project_id, user_id, file, limits, callback

+ 13 - 0
services/web/app/coffee/router.coffee

@@ -124,6 +124,19 @@ module.exports = class Router
 				next()
 			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
 
+		# direct url access to output files for a specific user and build (query string not required)
+		webRouter.get  /^\/project\/([^\/]*)\/user\/([0-9a-f]+)\/build\/([0-9a-f-]+)\/output\/(.*)$/,
+			((req, res, next) ->
+				params =
+					"Project_id": req.params[0]
+					"user":       req.params[1]
+					"build":      req.params[2]
+					"file":       req.params[3]
+				req.params = params
+				next()
+			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
+
+
 		webRouter.delete "/project/:Project_id/output", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.deleteAuxFiles
 		webRouter.get "/project/:Project_id/sync/code", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncCode
 		webRouter.get "/project/:Project_id/sync/pdf", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncPdf