Tofu/scripts/import-recipes/fetchOdtFromNextcloud.js
2026-09-18 01:41:02 +02:00

74 lines
3 KiB
JavaScript

#!/usr/bin/env node
import { mkdirSync, writeFileSync } from 'node:fs'
import { basename, join } from 'node:path'
import { PROPFIND_BODY } from '#import-recipes/webdav/PROPFIND_BODY.js'
import { listOdtHrefs } from '#import-recipes/webdav/listOdtHrefs.js'
import { parseFetchArguments } from '#import-recipes/webdav/parseFetchArguments.js'
const USAGE = `Usage: NEXTCLOUD_APP_PASSWORD=... node fetchOdtFromNextcloud.js --server <url> --user <name> --folder <path> [--out odt]
Downloads the .odt files of one Nextcloud folder, so importRecipes.js can read
them from disk. Optional: copying the files by hand does the same job.
The password must be an app password, never the account password.`
// Optional companion to the importer, and the only other place this tool talks
// to the network. It downloads, and nothing else: no parsing, no model call.
async function main() {
const { server, user, folder, out, problems } = parseFetchArguments(process.argv.slice(2))
const password = process.env.NEXTCLOUD_APP_PASSWORD ?? ''
if (problems.length > 0 || !server || !user || !folder || !password) {
for (const problem of problems) console.error(problem)
if (!password) console.error('NEXTCLOUD_APP_PASSWORD is not set')
console.error(`\n${USAGE}`)
return 1
}
const origin = server.startsWith('http') ? server : `https://${server}`
const path = folder.split('/').filter(Boolean).map(encodeURIComponent).join('/')
const folderUrl = `${origin.replace(/\/+$/, '')}/remote.php/dav/files/${encodeURIComponent(user)}/${path}/`
const authorization = `Basic ${Buffer.from(`${user}:${password}`, 'utf8').toString('base64')}`
let hrefs = []
try {
const response = await fetch(folderUrl, {
method: 'PROPFIND',
headers: { authorization, depth: '1', 'content-type': 'application/xml' },
body: PROPFIND_BODY
})
if (!response.ok) {
console.error(`PROPFIND ${folderUrl} answered ${response.status}.`)
return 1
}
hrefs = listOdtHrefs(await response.text())
} catch (error) {
console.error(`PROPFIND ${folderUrl} failed: ${error.message}`)
return 1
}
if (hrefs.length === 0) {
console.error(`No .odt file in ${folder}.`)
return 1
}
mkdirSync(out, { recursive: true })
let failed = 0
for (const href of hrefs) {
const fileName = decodeURIComponent(basename(href))
const fileUrl = href.startsWith('http') ? href : `${new URL(folderUrl).origin}${href}`
try {
const response = await fetch(fileUrl, { headers: { authorization } })
if (!response.ok) {
console.error(`${fileName} - FAILED: HTTP ${response.status}`)
failed += 1
continue
}
writeFileSync(join(out, fileName), Buffer.from(await response.arrayBuffer()))
console.log(`${fileName} - downloaded`)
} catch (error) {
console.error(`${fileName} - FAILED: ${error.message}`)
failed += 1
}
}
console.log(`\n${hrefs.length - failed} downloaded, ${failed} failed, in ${out}/`)
return failed === 0 ? 0 : 1
}
process.exitCode = await main()