1+ const xml2js = require ( 'xml2js' ) ;
12const { errors, errorInstances, jsutil } = require ( 'arsenal' ) ;
23const { Readable } = require ( 'stream' ) ;
34const collectResponseHeaders = require ( '../../utilities/collectResponseHeaders' ) ;
45const collectCorsHeaders = require ( '../../utilities/collectCorsHeaders' ) ;
56const crypto = require ( 'crypto' ) ;
67const { prepareStream } = require ( 'arsenal/build/lib/s3middleware/prepareStream' ) ;
78const UtilizationService = require ( '../../utilization/instance' ) ;
9+ const metadata = require ( '../../metadata/wrapper' ) ;
810
911/**
1012 * Decodes an URI and return the result.
@@ -195,7 +197,6 @@ function getFileToBuild(request, data, inlineLastModified = false) {
195197 return {
196198 value : {
197199 [ fieldName ] : fileToBuild ,
198- LastModified : modified ,
199200 } ,
200201 fieldName,
201202 } ;
@@ -204,14 +205,16 @@ function getFileToBuild(request, data, inlineLastModified = false) {
204205
205206/**
206207 * Fetches capacity metrics from UtilizationService for a bucket.
207- * Handles 404 gracefully (no metrics available yet, e.g. post-install).
208+ * Handles 404 gracefully (no metrics available yet, e.g. post-install),
209+ * returning a default bucketMetrics with the current date so callers always
210+ * receive a usable object.
208211 *
209212 * @param {object } bucketMd - bucket metadata
210213 * @param {object } request - incoming request
211214 * @param {object } log - logger object
212215 * @param {string } method - calling method name for log context
213- * @param {function } callback - (err, bucketMetrics) where bucketMetrics
214- * is undefined when metrics are not available ( 404)
216+ * @param {function } callback - (err, bucketMetrics) where bucketMetrics always
217+ * has at least a `date` field; on a real 404 the date defaults to new Date( )
215218 * @returns {undefined }
216219 */
217220function fetchCapacityMetrics ( bucketMd , request , log , method , callback ) {
@@ -225,7 +228,7 @@ function fetchCapacityMetrics(bucketMd, request, log, method, callback) {
225228 bucket : request . bucketName ,
226229 error : err . message || err . code ,
227230 } ) ;
228- return callback ( null ) ;
231+ return callback ( null , { date : new Date ( ) } ) ;
229232 }
230233 log . error ( 'error fetching capacity metrics from UtilizationService' , {
231234 method,
@@ -239,6 +242,61 @@ function fetchCapacityMetrics(bucketMd, request, log, method, callback) {
239242 } ) ;
240243}
241244
245+ /**
246+ * Builds Veeam file data (XML content + response metadata) for a given request.
247+ *
248+ * @param {object } request - incoming request
249+ * @param {object } bucketMd - bucket metadata from the router
250+ * @param {object } log - logger object
251+ * @param {string } name - calling method name (for log context)
252+ * @param {function } callback - (err, result) where result is { xmlContent, dataBuffer, modified, bucketData }
253+ * @returns {undefined }
254+ */
255+ function buildVeeamFileData ( request , bucketMd , log , name , callback ) {
256+ return metadata . getBucket ( request . bucketName , log , ( err , data ) => {
257+ if ( err ) {
258+ return callback ( errors . InternalError ) ;
259+ }
260+
261+ const fileToBuild = getFileToBuild ( request , data . _capabilities ?. VeeamSOSApi ) ;
262+
263+ if ( fileToBuild . error ) {
264+ return callback ( fileToBuild . error ) ;
265+ }
266+
267+ const finalize = bucketMetrics => {
268+ const modified = bucketMetrics . date ;
269+ if (
270+ bucketMetrics . bytesTotal !== undefined
271+ && fileToBuild . value . CapacityInfo
272+ && ! fileToBuild . value . CapacityInfo . Used
273+ ) {
274+ fileToBuild . value . CapacityInfo . Used = Number ( bucketMetrics . bytesTotal ) ;
275+ fileToBuild . value . CapacityInfo . Available =
276+ Number ( fileToBuild . value . CapacityInfo . Capacity ) - Number ( bucketMetrics . bytesTotal ) ;
277+ // TODO CLDSRV-633 when SUR backend supports realtime metrics: it will
278+ // report the real last cseq/date processed by SUR, instead of the current date,
279+ // ensuring no issue in a SOSAPI context. We should use this information.
280+ }
281+
282+ const builder = new xml2js . Builder ( { headless : true } ) ;
283+ const xmlContent = buildHeadXML ( builder . buildObject ( fileToBuild . value ) ) ;
284+ const dataBuffer = Buffer . from ( xmlContent ) ;
285+ return callback ( null , { xmlContent, dataBuffer, modified, bucketData : data } ) ;
286+ } ;
287+
288+ if ( ! isSystemXML ( request . objectKey ) ) {
289+ return fetchCapacityMetrics ( bucketMd , request , log , name , ( fetchErr , bucketMetrics ) => {
290+ if ( fetchErr ) {
291+ return callback ( errors . InternalError ) ;
292+ }
293+ return finalize ( bucketMetrics ) ;
294+ } ) ;
295+ }
296+ return finalize ( { date : new Date ( ) } ) ;
297+ } ) ;
298+ }
299+
242300module . exports = {
243301 _decodeURI,
244302 receiveData,
@@ -249,4 +307,5 @@ module.exports = {
249307 isSystemXML,
250308 getFileToBuild,
251309 fetchCapacityMetrics,
310+ buildVeeamFileData,
252311} ;
0 commit comments