Files
wondif_vue/scripts/find-sharepoint-drive.js
2026-06-16 10:33:35 +02:00

193 lines
5.3 KiB
JavaScript

import fs from "node:fs"
import path from "node:path"
import process from "node:process"
const ROOT_DIR = process.cwd()
function loadDotEnv(filePath = path.join(ROOT_DIR, ".env")) {
if (!fs.existsSync(filePath)) {
return
}
const content = fs.readFileSync(filePath, "utf8")
for (const line of content.split(/\r?\n/)) {
const trimmed = line.trim()
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) {
continue
}
const index = trimmed.indexOf("=")
const key = trimmed.slice(0, index).trim()
let value = trimmed.slice(index + 1).trim()
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1)
}
if (!process.env[key]) {
process.env[key] = value
}
}
}
function requireEnv(name) {
const value = process.env[name]
if (!value) {
throw new Error(`Variable d'environnement manquante : ${name}`)
}
return value
}
async function getAccessToken() {
const tenantId = requireEnv("MICROSOFT_TENANT_ID")
const clientId = requireEnv("MICROSOFT_CLIENT_ID")
const clientSecret = requireEnv("MICROSOFT_CLIENT_SECRET")
const body = new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
scope: "https://graph.microsoft.com/.default",
grant_type: "client_credentials",
})
const response = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body,
})
if (!response.ok) {
throw new Error(`Erreur token Microsoft Graph (${response.status}) : ${await response.text()}`)
}
const data = await response.json()
return data.access_token
}
function decodeJwtPayload(token) {
const [, payload] = token.split(".")
if (!payload) {
return {}
}
const normalizedPayload = payload.replaceAll("-", "+").replaceAll("_", "/")
const paddedPayload = normalizedPayload.padEnd(normalizedPayload.length + ((4 - normalizedPayload.length % 4) % 4), "=")
return JSON.parse(Buffer.from(paddedPayload, "base64").toString("utf8"))
}
function parseSharePointSiteUrl(rawUrl) {
const url = new URL(rawUrl)
const normalizedPath = url.pathname.replace(/\/+$/, "")
if (!normalizedPath) {
throw new Error("L'URL SharePoint doit contenir le chemin du site, par exemple https://tenant.sharepoint.com/sites/nom-du-site")
}
return {
hostname: url.hostname,
sitePath: normalizedPath,
}
}
async function graphGet(token, url) {
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
})
if (!response.ok) {
const details = await response.text()
const hint = response.status === 401
? "\nVérifie que l'application Entra ID a des permissions Microsoft Graph de type Application, par exemple Sites.Read.All ou Sites.ReadWrite.All, avec Grant admin consent. Vérifie aussi que le tenant correspond bien au SharePoint."
: ""
const error = new Error(`Erreur Microsoft Graph (${response.status}) : ${details}${hint}`)
error.status = response.status
error.details = details
throw error
}
return response.json()
}
async function main() {
loadDotEnv()
const sharePointUrl = process.argv[2] || process.env.SHAREPOINT_SITE_URL
if (!sharePointUrl) {
throw new Error("Usage : npm run graph:find-drive -- https://tenant.sharepoint.com/sites/nom-du-site")
}
const token = await getAccessToken()
const claims = decodeJwtPayload(token)
console.log(`Token Microsoft Graph obtenu pour tenant=${claims.tid || "inconnu"} app=${claims.appid || claims.azp || "inconnue"}`)
console.log(`Permissions applicatives dans le token : ${(claims.roles || []).join(", ") || "aucune"}`)
console.log("")
const { hostname, sitePath } = parseSharePointSiteUrl(sharePointUrl)
let site
try {
site = await graphGet(
token,
`https://graph.microsoft.com/v1.0/sites/${hostname}:${sitePath}`
)
} catch (error) {
if (error.status !== 404) {
throw error
}
const searchTerm = sitePath.split("/").filter(Boolean).at(-1)
console.log(`Site introuvable avec le chemin ${sitePath}. Recherche des sites contenant "${searchTerm}"...`)
console.log("")
const searchResults = await graphGet(
token,
`https://graph.microsoft.com/v1.0/sites?search=${encodeURIComponent(searchTerm)}`
)
if (!searchResults.value?.length) {
throw error
}
console.log("Sites trouvés :")
for (const result of searchResults.value) {
console.log(`- ${result.displayName || result.name}`)
console.log(` SITE_ID=${result.id}`)
console.log(` webUrl=${result.webUrl}`)
}
console.log("")
console.log("Relance ensuite la commande avec l'URL webUrl exacte du bon site.")
return
}
const drives = await graphGet(
token,
`https://graph.microsoft.com/v1.0/sites/${encodeURIComponent(site.id)}/drives`
)
console.log(`SITE_ID=${site.id}`)
console.log("")
console.log("Bibliothèques SharePoint trouvées :")
for (const drive of drives.value || []) {
console.log(`- ${drive.name}`)
console.log(` GRAPH_DRIVE_ID=${drive.id}`)
console.log(` webUrl=${drive.webUrl}`)
}
}
main().catch((error) => {
console.error(error)
process.exit(1)
})