11/* eslint-disable node/no-deprecated-api */
22
33import { join , dirname } from 'path'
4- import { createRequire } from 'module'
5- const require = createRequire ( import . meta. url )
6- const intoStream = require ( 'into-stream' )
4+ import intoStream from 'into-stream'
75import url from 'url'
86import fs from 'fs'
97import $rdf from 'rdflib'
10- const { mkdirp } = require ( 'fs-extra' )
8+ import { mkdirp } from 'fs-extra'
119import { v4 as uuid } from 'uuid' // there seem to be an esm module
1210import debug from './debug.mjs'
1311import error from './http-error.mjs'
1412import { stringToStream , serialize , overQuota , getContentType , parse } from './utils.mjs'
15- const extend = require ( 'extend' )
16- const rimraf = require ( 'rimraf' )
13+ import extend from 'extend'
14+ import rimraf from 'rimraf'
15+ import { exec } from 'child_process'
1716import * as ldpContainer from './ldp-container.mjs'
1817import fetch from 'node-fetch'
1918import { promisify } from 'util'
20- import URL from 'url'
2119import withLock from './lock.mjs'
2220import utilPath from 'path'
2321import { clearAclCache } from './acl-checker.mjs'
@@ -413,7 +411,6 @@ class LDP {
413411 } else {
414412 // If original is a folder, copy recursively
415413 copyPromise = new Promise ( ( resolve , reject ) => {
416- const { exec } = require ( 'child_process' )
417414 exec ( `cp -r "${ fromPath } " "${ toPath } "` , function ( err ) {
418415 if ( err ) {
419416 debug . handlers ( 'Error copying directory: ' + err )
@@ -598,9 +595,214 @@ class LDP {
598595 }
599596 }
600597
598+ async graph ( url , baseUri , contentType ) {
599+ const body = await this . readResource ( url )
600+ if ( ! contentType ) {
601+ ( { contentType } = await this . resourceMapper . mapUrlToFile ( { url } ) )
602+ }
603+ return new Promise ( ( resolve , reject ) => {
604+ const graph = $rdf . graph ( )
605+ $rdf . parse ( body , graph , baseUri , contentType ,
606+ err => err ? reject ( err ) : resolve ( graph ) )
607+ } )
608+ }
609+
601610 static getRDFMimeTypes ( ) {
602611 return Array . from ( RDF_MIME_TYPES )
603612 }
613+
614+ getTrustedOrigins ( req ) {
615+ const trustedOrigins = [ this . resourceMapper . resolveUrl ( req . hostname ) ] . concat ( this . trustedOrigins )
616+ if ( this . multiuser ) {
617+ trustedOrigins . push ( this . serverUri )
618+ }
619+ return trustedOrigins
620+ }
621+
622+ async getAvailableUrl ( hostname , containerURI , { slug = uuid ( ) , extension, container } = { } ) {
623+ let requestUrl = this . resourceMapper . resolveUrl ( hostname , containerURI )
624+ requestUrl = requestUrl . replace ( / \/ * $ / , '/' )
625+
626+ let itemName = slug . endsWith ( extension ) || slug . endsWith ( this . suffixAcl ) || slug . endsWith ( this . suffixMeta ) ? slug : slug + extension
627+ try {
628+ // check whether resource exists
629+ const context = container ? '/' : ''
630+ await this . resourceMapper . mapUrlToFile ( { url : ( requestUrl + itemName + context ) } )
631+ itemName = `${ uuid ( ) } -${ itemName } `
632+ } catch ( e ) {
633+ try {
634+ // check whether resource with same name exists
635+ const context = ! container ? '/' : ''
636+ await this . resourceMapper . mapUrlToFile ( { url : ( requestUrl + itemName + context ) } )
637+ itemName = `${ uuid ( ) } -${ itemName } `
638+ } catch ( e ) { }
639+ }
640+ if ( container ) itemName += '/'
641+ return requestUrl + itemName
642+ }
643+
644+ async exists ( hostname , path , searchIndex = true ) {
645+ const options = { hostname, path, includeBody : false , searchIndex }
646+ return await this . get ( options , searchIndex )
647+ }
648+
649+ async fetchGraph ( uri , options = { } ) {
650+ const response = await fetch ( uri )
651+ if ( ! response . ok ) {
652+ const err = new Error (
653+ `Error fetching ${ uri } : ${ response . status } ${ response . statusText } `
654+ )
655+ err . statusCode = response . status || 400
656+ throw err
657+ }
658+ const body = await response . text ( )
659+
660+ return parse ( body , uri , getContentType ( response . headers ) )
661+ }
662+
663+ async checkItemName ( url ) {
664+ let testName , testPath
665+ const { hostname, pathname } = this . resourceMapper . _parseUrl ( url )
666+ let itemUrl = this . resourceMapper . resolveUrl ( hostname , pathname )
667+ if ( this . _containsInvalidSuffixes ( itemUrl ) ) {
668+ throw error ( 400 , `${ itemUrl } contained reserved suffixes in path` )
669+ }
670+ const container = itemUrl . endsWith ( '/' )
671+ try {
672+ const testUrl = container ? itemUrl . slice ( 0 , - 1 ) : itemUrl + '/'
673+ const { path : testPathLocal } = await this . resourceMapper . mapUrlToFile ( { url : testUrl } )
674+ testPath = testPathLocal
675+ testName = container ? fs . lstatSync ( testPath ) . isFile ( ) : fs . lstatSync ( testPath ) . isDirectory ( )
676+ } catch ( err ) {
677+ testName = false
678+
679+ if ( itemUrl . endsWith ( '/' ) ) itemUrl = itemUrl . substring ( 0 , itemUrl . length - 1 )
680+ itemUrl = itemUrl . substring ( 0 , itemUrl . lastIndexOf ( '/' ) + 1 )
681+ const { pathname : parentPathname } = this . resourceMapper . _parseUrl ( itemUrl )
682+ if ( parentPathname !== '/' ) {
683+ return await this . checkItemName ( itemUrl )
684+ }
685+ }
686+ if ( testName ) {
687+ throw error ( 409 , `${ testPath } : Container and resource cannot have the same name in URI` )
688+ }
689+ }
690+
691+ async createDirectory ( pathArg , hostname , nonContainer = true ) {
692+ try {
693+ const dirName = dirname ( pathArg )
694+ if ( ! fs . existsSync ( dirName ) ) {
695+ await promisify ( mkdirp ) ( dirName )
696+ if ( this . live && nonContainer ) {
697+ const parentDirectoryPath = utilPath . dirname ( dirName ) + utilPath . sep
698+ const parentDirectoryUrl = ( await this . resourceMapper . mapFileToUrl ( { path : parentDirectoryPath , hostname } ) ) . url
699+ this . live ( url . parse ( parentDirectoryUrl ) . pathname )
700+ }
701+ }
702+ } catch ( err ) {
703+ debug . handlers ( 'PUT -- Error creating directory: ' + err )
704+ throw error ( err , 'Failed to create the path to the new resource' )
705+ }
706+ }
707+
708+ async checkFileExtension ( urlArg , pathArg ) {
709+ try {
710+ const { path : existingPath } = await this . resourceMapper . mapUrlToFile ( { url : urlArg } )
711+ if ( pathArg !== existingPath ) {
712+ try {
713+ await withLock ( existingPath , ( ) => promisify ( fs . unlink ) ( existingPath ) )
714+ } catch ( err ) { throw error ( err , 'Failed to delete resource' ) }
715+ }
716+ } catch ( err ) { }
717+ }
718+
719+ async deleteContainer ( directory ) {
720+ if ( directory [ directory . length - 1 ] !== '/' ) directory += '/'
721+ let list
722+ try {
723+ list = await promisify ( fs . readdir ) ( directory )
724+ } catch ( err ) {
725+ throw error ( 404 , 'The container does not exist' )
726+ }
727+ if ( list . some ( file => ! file . endsWith ( this . suffixMeta ) && ! file . endsWith ( this . suffixAcl ) ) ) {
728+ throw error ( 409 , 'Container is not empty' )
729+ }
730+ try {
731+ await promisify ( rimraf ) ( directory )
732+ } catch ( err ) {
733+ throw error ( err , 'Failed to delete the container' )
734+ }
735+ }
736+
737+ async deleteDocument ( filePath ) {
738+ const linkPath = this . resourceMapper . _removeDollarExtension ( filePath )
739+ try {
740+ await withLock ( filePath , ( ) => promisify ( fs . unlink ) ( filePath ) )
741+ const aclPath = `${ linkPath } ${ this . suffixAcl } `
742+ if ( await promisify ( fs . exists ) ( aclPath ) ) {
743+ await withLock ( aclPath , ( ) => promisify ( fs . unlink ) ( aclPath ) )
744+ }
745+ const metaPath = `${ linkPath } ${ this . suffixMeta } `
746+ if ( await promisify ( fs . exists ) ( metaPath ) ) {
747+ await withLock ( metaPath , ( ) => promisify ( fs . unlink ) ( metaPath ) )
748+ }
749+ } catch ( err ) {
750+ debug . container ( 'DELETE -- unlink() error: ' + err )
751+ throw error ( err , 'Failed to delete resource' )
752+ }
753+ }
754+
755+ async get ( options , searchIndex = true ) {
756+ let pathLocal , contentType , stats
757+ try {
758+ ( { path : pathLocal , contentType } = await this . resourceMapper . mapUrlToFile ( { url : options , searchIndex } ) )
759+ stats = await this . stat ( pathLocal )
760+ } catch ( err ) {
761+ throw error ( err . status || 500 , err . message )
762+ }
763+
764+ if ( ! options . includeBody ) {
765+ return { stream : stats , contentType, container : stats . isDirectory ( ) }
766+ }
767+
768+ if ( stats . isDirectory ( ) ) {
769+ const { url : absContainerUri } = await this . resourceMapper . mapFileToUrl ( { path : pathLocal , hostname : options . hostname } )
770+ const metaFile = await this . readContainerMeta ( absContainerUri ) . catch ( ( ) => '' )
771+ let data
772+ try {
773+ data = await this . listContainer ( pathLocal , absContainerUri , metaFile , options . hostname )
774+ } catch ( err ) {
775+ debug . handlers ( 'GET container -- Read error:' + err . message )
776+ throw err
777+ }
778+ const stream = stringToStream ( data )
779+ return { stream, contentType, container : true }
780+ } else {
781+ let chunksize , contentRange , start , end
782+ if ( options . range ) {
783+ const total = fs . statSync ( pathLocal ) . size
784+ const parts = options . range . replace ( / b y t e s = / , '' ) . split ( '-' )
785+ const partialstart = parts [ 0 ]
786+ const partialend = parts [ 1 ]
787+ start = parseInt ( partialstart , 10 )
788+ end = partialend ? parseInt ( partialend , 10 ) : total - 1
789+ chunksize = ( end - start ) + 1
790+ contentRange = 'bytes ' + start + '-' + end + '/' + total
791+ }
792+ return withLock ( pathLocal , ( ) => new Promise ( ( resolve , reject ) => {
793+ const stream = fs . createReadStream ( pathLocal , start && end ? { start, end } : { } )
794+ stream
795+ . on ( 'error' , function ( err ) {
796+ debug . handlers ( `GET -- error reading ${ pathLocal } : ${ err . message } ` )
797+ return reject ( error ( err , "Can't read file " + err ) )
798+ } )
799+ . on ( 'open' , function ( ) {
800+ debug . handlers ( `GET -- Reading ${ pathLocal } ` )
801+ return resolve ( { stream, contentType, container : false , contentRange, chunksize } )
802+ } )
803+ } ) )
804+ }
805+ }
604806}
605807
606808export default LDP
0 commit comments