generated from gitea_admin/default
API Microsoft
This commit is contained in:
192
scripts/find-sharepoint-drive.js
Normal file
192
scripts/find-sharepoint-drive.js
Normal file
@@ -0,0 +1,192 @@
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user