@@ -13,12 +13,18 @@ module.exports.debrack = debrack
1313module . exports . stripLineEndings = stripLineEndings
1414module . exports . fullUrlForReq = fullUrlForReq
1515module . exports . routeResolvedFile = routeResolvedFile
16+ module . exports . getQuota = getQuota
17+ module . exports . overQuota = overQuota
1618
1719const fs = require ( 'fs-extra' )
1820const path = require ( 'path' )
21+ const util = require ( 'util' )
1922const $rdf = require ( 'rdflib' )
2023const from = require ( 'from2' )
2124const url = require ( 'url' )
25+ const debug = require ( './debug' ) . fs
26+ const getSize = require ( 'get-folder-size' )
27+ var ns = require ( 'solid-namespace' ) ( $rdf )
2228
2329/**
2430 * Returns a fully qualified URL from an Express.js Request object.
@@ -239,3 +245,60 @@ function routeResolvedFile (router, path, file, appendFileName = true) {
239245 const fullFile = require . resolve ( file )
240246 router . get ( fullPath , ( req , res ) => res . sendFile ( fullFile ) )
241247}
248+
249+ /**
250+ * Returns the number of bytes that the user owning the requested POD
251+ * may store or Infinity if no limit
252+ */
253+
254+ async function getQuota ( root , serverUri ) {
255+ const filename = path . join ( root , 'settings/serverSide.ttl' )
256+ var prefs
257+ try {
258+ prefs = await _asyncReadfile ( filename )
259+ } catch ( error ) {
260+ debug ( 'Setting no quota. While reading serverSide.ttl, got ' + error )
261+ return Infinity
262+ }
263+ var graph = $rdf . graph ( )
264+ const storageUri = serverUri + '/'
265+ try {
266+ $rdf . parse ( prefs , graph , storageUri , 'text/turtle' )
267+ } catch ( error ) {
268+ throw new Error ( 'Failed to parse serverSide.ttl, got ' + error )
269+ }
270+ return Number ( graph . anyValue ( $rdf . sym ( storageUri ) , ns . solid ( 'storageQuota' ) ) ) || Infinity
271+ }
272+
273+ /**
274+ * Returns true of the user has already exceeded their quota, i.e. it
275+ * will check if new requests should be rejected, which means they
276+ * could PUT a large file and get away with it.
277+ */
278+
279+ async function overQuota ( root , serverUri ) {
280+ let quota = await getQuota ( root , serverUri )
281+ if ( quota === Infinity ) {
282+ return false
283+ }
284+ // TODO: cache this value?
285+ var size = await actualSize ( root )
286+ return ( size > quota )
287+ }
288+
289+ /**
290+ * Returns the number of bytes that is occupied by the actual files in
291+ * the file system. IMPORTANT NOTE: Since it traverses the directory
292+ * to find the actual file sizes, this does a costly operation, but
293+ * neglible for the small quotas we currently allow. If the quotas
294+ * grow bigger, this will significantly reduce write performance, and
295+ * so it needs to be rewritten.
296+ */
297+
298+ function actualSize ( root ) {
299+ return util . promisify ( getSize ) ( root )
300+ }
301+
302+ function _asyncReadfile ( filename ) {
303+ return util . promisify ( fs . readFile ) ( filename , 'utf-8' )
304+ }
0 commit comments