die heldin script add

This commit is contained in:
2026-02-24 07:04:01 +01:00
parent a1b35fecce
commit 420391f48a
1847 changed files with 597777 additions and 0 deletions

156
export-customers.ts Normal file
View File

@@ -0,0 +1,156 @@
import { writeFileSync } from "node:fs"
import { Openmagicline } from "openmagicline"
// ---------------------------------------------------------------------------
// Config set via environment variables or edit directly below
// ---------------------------------------------------------------------------
const GYM = process.env.MAGICLINE_GYM ?? "dieheldin"
const USERNAME = process.env.MAGICLINE_USER ?? "tanja.wild"
const PASSWORD = process.env.MAGICLINE_PASS ?? "Luna22111993"
if (!GYM || !USERNAME || !PASSWORD) {
console.error(
"Missing credentials. Set MAGICLINE_GYM, MAGICLINE_USER, and MAGICLINE_PASS.",
)
process.exit(1)
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Escape a value for use in a CSV cell. */
function csvCell(value: unknown): string {
if (value === null || value === undefined) return ""
const str = String(value)
if (str.includes(",") || str.includes('"') || str.includes("\n")) {
return `"${str.replace(/"/g, '""')}"`
}
return str
}
/** Escape a value for use in HTML table cell content. */
function htmlEscape(value: unknown): string {
if (value === null || value === undefined) return ""
return String(value)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
const mgl = new Openmagicline({ gym: GYM, username: USERNAME, password: PASSWORD })
await mgl.login()
console.log("Logged in.")
// Fetch the real facility/organization unit ID — searching with facility:0 returns nothing.
const facilityId = await mgl.util.getDefaultUnitID()
console.log(`Using facility ID: ${facilityId}`)
const SEARCH_CAP = 50
const seen = new Set<number>()
const customers: Record<string, unknown>[] = []
async function sweep(prefix: string): Promise<void> {
const results = await mgl.customer.search(prefix, {
facility: facilityId,
showAllFacilities: false,
searchInName: false,
searchInCustomerNumber: true,
})
if (results.length === SEARCH_CAP) {
for (const d of "0123456789") {
await sweep(prefix + d)
}
return
}
for (const c of results) {
if (!seen.has(c.databaseId) && c.customerStatus === 1) {
seen.add(c.databaseId)
customers.push(c as unknown as Record<string, unknown>)
}
}
}
for (const d of "0123456789") {
const prefix = `1-${d}`
process.stdout.write(`Sweeping '${prefix}'… `)
const before = customers.length
await sweep(prefix)
console.log(`+${customers.length - before} (total: ${customers.length})`)
}
if (customers.length === 0) {
console.error("No customers found.")
process.exit(1)
}
// Collect the union of all keys across all customer records
const allKeys = Array.from(
customers.reduce((keys, c) => {
for (const k of Object.keys(c)) keys.add(k)
return keys
}, new Set<string>()),
)
// ---------------------------------------------------------------------------
// CSV export
// ---------------------------------------------------------------------------
const csvLines = [
allKeys.map(csvCell).join(","),
...customers.map((c) => allKeys.map((k) => csvCell(c[k])).join(",")),
]
const csvPath = "customers.csv"
writeFileSync(csvPath, csvLines.join("\n"), "utf-8")
console.log(`CSV written to ${csvPath} (${customers.length} rows)`)
// ---------------------------------------------------------------------------
// HTML export
// ---------------------------------------------------------------------------
const headerCells = allKeys.map((k) => `<th>${htmlEscape(k)}</th>`).join("")
const bodyRows = customers
.map(
(c) =>
`<tr>${allKeys.map((k) => `<td>${htmlEscape(c[k])}</td>`).join("")}</tr>`,
)
.join("\n")
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Customers ${new Date().toLocaleDateString()}</title>
<style>
body { font-family: sans-serif; font-size: 13px; padding: 1rem; }
h1 { margin-bottom: 0.5rem; }
p.meta { color: #666; margin-bottom: 1rem; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ccc; padding: 4px 8px; text-align: left; white-space: nowrap; }
th { background: #f0f0f0; position: sticky; top: 0; }
tr:nth-child(even) { background: #fafafa; }
</style>
</head>
<body>
<h1>Customer Export</h1>
<p class="meta">Generated: ${new Date().toISOString()} &nbsp;|&nbsp; Total: ${customers.length}</p>
<table>
<thead><tr>${headerCells}</tr></thead>
<tbody>
${bodyRows}
</tbody>
</table>
</body>
</html>`
const htmlPath = "customers.html"
writeFileSync(htmlPath, html, "utf-8")
console.log(`HTML written to ${htmlPath}`)