Looped sync works, but is slow. No retry or resiliency built in.
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
[dev]
|
||||
port = 3000
|
||||
port = 5000
|
||||
static = "./public"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
console.log("Hello via Bun!");
|
||||
@@ -1,6 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Auth Response</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Impersonation</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; margin: 2rem; }
|
||||
#banner { display:none; background:#ffcc00; padding:1rem; margin-bottom:1rem; font-weight:bold; }
|
||||
#form { display:flex; flex-direction:column; max-width:400px; }
|
||||
label { margin:0.5rem 0 0.2rem; }
|
||||
input { padding:0.5rem; font-size:1rem; }
|
||||
button { margin-top:1rem; padding:0.5rem; font-size:1rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="banner">⚠️ Impersonation Mode Active</div>
|
||||
|
||||
<h1>Test Impersonation</h1>
|
||||
<div id="form">
|
||||
<label for="targetInput">User ID or Email:</label>
|
||||
<input type="text" id="targetInput" placeholder="Enter user ID or email">
|
||||
<button id="impersonateBtn">Impersonate</button>
|
||||
<button id="returnBtn" style="display:none;">Return to Admin</button>
|
||||
<button id="goMainBtn" style="display:none;">Go to Main App</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const banner = document.getElementById('banner');
|
||||
const targetInput = document.getElementById('targetInput');
|
||||
const impersonateBtn = document.getElementById('impersonateBtn');
|
||||
const returnBtn = document.getElementById('returnBtn');
|
||||
const goMainBtn = document.getElementById('goMainBtn');
|
||||
|
||||
let originalToken = localStorage.getItem('pb_original_token') || localStorage.getItem('pb_token') || '';
|
||||
|
||||
impersonateBtn.addEventListener('click', async () => {
|
||||
const identifier = targetInput.value.trim();
|
||||
if (!identifier) return alert('Enter a user ID or email');
|
||||
if (!originalToken) return alert('You must be logged in as Admin/Superuser');
|
||||
|
||||
try {
|
||||
const res = await fetch('http://localhost:8081/api/impersonate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + originalToken,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ targetUserIdentifier: identifier })
|
||||
});
|
||||
|
||||
if (!res.ok) return alert('Failed: ' + await res.text());
|
||||
|
||||
const data = await res.json();
|
||||
localStorage.setItem('pb_token', data.token);
|
||||
if (!localStorage.getItem('pb_original_token')) {
|
||||
localStorage.setItem('pb_original_token', originalToken);
|
||||
}
|
||||
|
||||
banner.style.display = 'block';
|
||||
returnBtn.style.display = 'inline-block';
|
||||
goMainBtn.style.display = 'inline-block';
|
||||
impersonateBtn.disabled = true;
|
||||
alert('Now impersonating ' + identifier);
|
||||
|
||||
} catch (err) { alert('Error: ' + err.message); }
|
||||
});
|
||||
|
||||
returnBtn.addEventListener('click', () => {
|
||||
const orig = localStorage.getItem('pb_original_token');
|
||||
if (!orig) return alert('Original token not found');
|
||||
localStorage.setItem('pb_token', orig);
|
||||
localStorage.removeItem('pb_original_token');
|
||||
banner.style.display = 'none';
|
||||
returnBtn.style.display = 'none';
|
||||
goMainBtn.style.display = 'none';
|
||||
impersonateBtn.disabled = false;
|
||||
alert('Returned to Admin session');
|
||||
});
|
||||
|
||||
goMainBtn.addEventListener('click', () => window.location.href = '/index.html');
|
||||
|
||||
if (localStorage.getItem('pb_original_token') && localStorage.getItem('pb_token') !== localStorage.getItem('pb_original_token')) {
|
||||
banner.style.display = 'block';
|
||||
returnBtn.style.display = 'inline-block';
|
||||
goMainBtn.style.display = 'inline-block';
|
||||
impersonateBtn.disabled = true;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,70 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import axios from "axios";
|
||||
import qs from "qs";
|
||||
import dotenv from "dotenv";
|
||||
import PocketBase from "pocketbase";
|
||||
import XLSX from "xlsx";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
// -----------------------------
|
||||
// CONFIG
|
||||
// -----------------------------
|
||||
const CONFIG = JSON.parse(fs.readFileSync("config.json", "utf8"));
|
||||
const tenantId = CONFIG.tenantId;
|
||||
const clientId = CONFIG.clientId;
|
||||
const clientSecret = process.env.CLIENT_SECRET;
|
||||
|
||||
|
||||
// -----------------------------
|
||||
// EXCEL HEADER AND DATA ROW PARAMETERS
|
||||
// -----------------------------
|
||||
const HEADER_ROW_INDEX = 1; // headers on row 2 (index 1)
|
||||
const DATA_START_ROW_INDEX = 2; // data starts on row 3 (index 2)
|
||||
|
||||
|
||||
// -----------------------------
|
||||
// GRAPH TOKEN ACQUISITION
|
||||
// -----------------------------
|
||||
async function getGraphToken() {
|
||||
const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
||||
const body = qs.stringify({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
scope: "https://graph.microsoft.com/.default",
|
||||
grant_type: "client_credentials",
|
||||
});
|
||||
const res = await axios.post(tokenUrl, body, {
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
});
|
||||
log("✅ Graph token acquired");
|
||||
return res.data.access_token;
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// GRAPH EXCEL DOWNLOAD HELPER
|
||||
// -----------------------------
|
||||
async function downloadExcelFromGraph(graphToken, driveId, itemId) {
|
||||
const url = `https://graph.microsoft.com/v1.0/drives/${driveId}/items/${itemId}/content`;
|
||||
const res = await axios.get(url, {
|
||||
headers: { Authorization: `Bearer ${graphToken}` },
|
||||
responseType: "arraybuffer",
|
||||
});
|
||||
return Buffer.from(res.data);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// MAIN SYNC LOGIC
|
||||
// -----------------------------
|
||||
async function runSync() {
|
||||
try {
|
||||
const graphToken = await getGraphToken();
|
||||
|
||||
const driveId = "b!9YOqqr2xM0G2DFBwMEJYY4UzQgocFddEtpLYWuS9_AtkCKl2q8yjQJteQ7Ti4QSx";
|
||||
const itemId = "01SPNXLDSQTMG4F6BDANCLJU2FZKEDQLVU";
|
||||
|
||||
const excelBuffer = await downloadExcelFromGraph(graphToken, driveId, itemId);
|
||||
} catch (error) {
|
||||
console.error("❌ Error during sync:", error);
|
||||
}}
|
||||
@@ -3,7 +3,7 @@
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"module": "ESnext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
@@ -17,6 +17,7 @@
|
||||
// Best practices
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
|
||||
Reference in New Issue
Block a user