Files.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /**
  2. * @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com>
  3. *
  4. * @author John Molakvoæ <skjnldsv@protonmail.com>
  5. *
  6. * @license AGPL-3.0-or-later
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU Affero General Public License as
  10. * published by the Free Software Foundation, either version 3 of the
  11. * License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. import type { ContentsWithRoot } from '@nextcloud/files'
  23. import type { FileStat, ResponseDataDetailed, DAVResultResponseProps } from 'webdav'
  24. import { CancelablePromise } from 'cancelable-promise'
  25. import { File, Folder, davParsePermissions, davGetDefaultPropfind } from '@nextcloud/files'
  26. import { generateRemoteUrl } from '@nextcloud/router'
  27. import { getCurrentUser } from '@nextcloud/auth'
  28. import { getClient, rootPath } from './WebdavClient'
  29. import { hashCode } from '../utils/hashUtils'
  30. import logger from '../logger'
  31. const client = getClient()
  32. interface ResponseProps extends DAVResultResponseProps {
  33. permissions: string,
  34. fileid: number,
  35. size: number,
  36. }
  37. export const resultToNode = function(node: FileStat): File | Folder {
  38. const props = node.props as ResponseProps
  39. const permissions = davParsePermissions(props?.permissions)
  40. const owner = getCurrentUser()?.uid as string
  41. const source = generateRemoteUrl('dav' + rootPath + node.filename)
  42. const id = props?.fileid < 0
  43. ? hashCode(source)
  44. : props?.fileid as number || 0
  45. const nodeData = {
  46. id,
  47. source,
  48. mtime: new Date(node.lastmod),
  49. mime: node.mime as string,
  50. size: props?.size as number || 0,
  51. permissions,
  52. owner,
  53. root: rootPath,
  54. attributes: {
  55. ...node,
  56. ...props,
  57. hasPreview: props?.['has-preview'],
  58. failed: props?.fileid < 0,
  59. },
  60. }
  61. delete nodeData.attributes.props
  62. return node.type === 'file'
  63. ? new File(nodeData)
  64. : new Folder(nodeData)
  65. }
  66. export const getContents = (path = '/'): Promise<ContentsWithRoot> => {
  67. const controller = new AbortController()
  68. const propfindPayload = davGetDefaultPropfind()
  69. return new CancelablePromise(async (resolve, reject, onCancel) => {
  70. onCancel(() => controller.abort())
  71. try {
  72. const contentsResponse = await client.getDirectoryContents(path, {
  73. details: true,
  74. data: propfindPayload,
  75. includeSelf: true,
  76. signal: controller.signal,
  77. }) as ResponseDataDetailed<FileStat[]>
  78. const root = contentsResponse.data[0]
  79. const contents = contentsResponse.data.slice(1)
  80. if (root.filename !== path) {
  81. throw new Error('Root node does not match requested path')
  82. }
  83. resolve({
  84. folder: resultToNode(root) as Folder,
  85. contents: contents.map(result => {
  86. try {
  87. return resultToNode(result)
  88. } catch (error) {
  89. logger.error(`Invalid node detected '${result.basename}'`, { error })
  90. return null
  91. }
  92. }).filter(Boolean) as File[],
  93. })
  94. } catch (error) {
  95. reject(error)
  96. }
  97. })
  98. }