Tofu/app/services/diagnostics/runWebdavCheck.js
2026-09-18 01:41:02 +02:00

99 lines
3.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// End-to-end check of a WebDAV connection, run once when the user connects.
//
// It makes no request of its own: it drives the client and turns the round trip
// into a readable report. Every step is recorded, including the one that fails,
// so the user sees how far it went — "it does not work" is never an answer.
// The run stops at the first failure, because every later step depends on it.
const CHECK_FILE_NAME = 'tofu-check.json'
const PROBE_LABEL = 'Joindre le serveur'
const WRITE_LABEL = 'Écrire un fichier de test'
const READ_LABEL = 'Relire et vérifier le contenu'
const LIST_LABEL = 'Lister le dossier'
const CLEANUP_LABEL = 'Supprimer le fichier de test'
// The failure already carries a French sentence and, when it exists, a piece of
// advice: the report line shows both.
function formatFailureDetail({ message, hint }) {
return hint ? `${message} ${hint}` : message
}
// -> { steps: [{ id, label, ok, detail }], ok }
export async function runWebdavCheck(client, folder) {
const steps = []
const checkPath = `${folder}/${CHECK_FILE_NAME}`
// The timestamp is the witness: reading it back proves we read our own write.
const payload = { tool: 'tofu', purpose: 'connection check', checkedAt: new Date().toISOString() }
function recordSuccess(id, label, detail) {
steps.push({ id, label, ok: true, detail })
}
function recordFailure(id, label, failure) {
steps.push({ id, label, ok: false, detail: formatFailureDetail(failure) })
}
const probe = await client.listFolder('')
if (!probe.ok) {
recordFailure('probe', PROBE_LABEL, probe.failure)
return { steps, ok: false }
}
recordSuccess('probe', PROBE_LABEL, `Serveur joint, ${probe.entries.length} élément(s) à la racine du compte.`)
const folderLabel = `Créer le dossier ${folder}`
const folderResult = await client.ensureFolder(folder)
if (!folderResult.ok) {
recordFailure('folder', folderLabel, folderResult.failure)
return { steps, ok: false }
}
recordSuccess('folder', folderLabel, `Dossier « ${folder} » disponible (créé, ou déjà présent).`)
const write = await client.writeJson(checkPath, payload)
if (!write.ok) {
recordFailure('write', WRITE_LABEL, write.failure)
return { steps, ok: false }
}
recordSuccess('write', WRITE_LABEL, `Fichier « ${checkPath} » écrit.`)
const read = await client.readJson(checkPath)
if (!read.ok) {
recordFailure('read', READ_LABEL, read.failure)
return { steps, ok: false }
}
if (read.data?.checkedAt !== payload.checkedAt) {
recordFailure('read', READ_LABEL, {
message: 'Le fichier relu ne contient pas ce qui vient dêtre écrit.',
hint: 'Un cache, un proxy ou un autre outil écrit peut-être au même endroit.'
})
return { steps, ok: false }
}
recordSuccess('read', READ_LABEL, 'Contenu relu, identique à ce qui venait dêtre écrit.')
const list = await client.listFolder(folder)
if (!list.ok) {
recordFailure('list', LIST_LABEL, list.failure)
return { steps, ok: false }
}
if (!list.entries.some((entry) => entry.name === CHECK_FILE_NAME)) {
recordFailure('list', LIST_LABEL, {
message: `Le dossier répond, mais « ${CHECK_FILE_NAME} » ny figure pas.`,
hint: 'Le serveur renvoie peut-être une liste mise en cache.'
})
return { steps, ok: false }
}
recordSuccess('list', LIST_LABEL, `Dossier listé, ${list.entries.length} entrée(s), dont le fichier de test.`)
// The round trip is already proven at this point: a failed cleanup leaves a
// harmless file behind, it does not make the connection unusable.
const cleanup = await client.remove(checkPath)
if (!cleanup.ok) {
recordFailure('cleanup', CLEANUP_LABEL, {
message: cleanup.failure.message,
hint: `Le fichier « ${CHECK_FILE_NAME} » est resté sur le serveur ; cela ninvalide pas le test.`
})
return { steps, ok: true }
}
recordSuccess('cleanup', CLEANUP_LABEL, 'Fichier de test supprimé.')
return { steps, ok: true }
}