ADD: added inital scripts and password recovery scripts

This commit is contained in:
hwinkel
2026-03-13 12:06:09 +01:00
parent 3a2a94ec19
commit 71ff97f302
11 changed files with 522 additions and 40 deletions
+65
View File
@@ -0,0 +1,65 @@
/**
* reset-password.ts
*
* Emergency recovery script resets the password for any user.
* Run directly inside the container via docker exec.
*
* Usage:
* docker exec -it annas_app node scripts/reset-password.js --username admin --password newpassword
*
* During development:
* npx ts-node --compiler-options '{"module":"CommonJS"}' scripts/reset-password.ts --username admin --password newpassword
*/
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcryptjs";
const prisma = new PrismaClient();
function parseArgs(): { username: string; password: string } {
const args = process.argv.slice(2);
const get = (flag: string) => {
const i = args.indexOf(flag);
return i !== -1 ? args[i + 1] : undefined;
};
const username = get("--username");
const password = get("--password");
if (!username || !password) {
console.error("Usage: reset-password --username <username> --password <newpassword>");
process.exit(1);
}
return { username, password };
}
async function main() {
const { username, password } = parseArgs();
if (password.length < 8) {
console.error("ERROR: Password must be at least 8 characters.");
process.exit(1);
}
const user = await prisma.user.findFirst({ where: { username } });
if (!user) {
console.error(`ERROR: No user found with username "${username}".`);
const all = await prisma.user.findMany({ select: { username: true, email: true, role: true } });
console.error("Available users:");
all.forEach((u) => console.error(` - ${u.username} (${u.email}) [${u.role}]`));
process.exit(1);
}
const passwordHash = await bcrypt.hash(password, 12);
await prisma.user.update({ where: { id: user.id }, data: { passwordHash } });
console.log(`✅ Password reset for user "${username}" (${user.email}).`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
+92
View File
@@ -0,0 +1,92 @@
/**
* setup-admin.ts
*
* Ensures the initial admin user (username: "admin") exists.
* Called automatically on every container start.
*
* Behaviour:
* - ADMIN_PASSWORD set → create or update admin with that password
* - ADMIN_PASSWORD not set, admin exists → nothing to do, skip silently
* - ADMIN_PASSWORD not set, admin missing → generate a random password,
* create the user, print to logs
*
* Manual usage:
* ADMIN_PASSWORD=secret npx ts-node --compiler-options '{"module":"CommonJS"}' scripts/setup-admin.ts
* docker exec -e ADMIN_PASSWORD=secret annas_app node scripts/setup-admin.js
*/
import { PrismaClient } from "@prisma/client";
import bcrypt from "bcryptjs";
import { randomBytes } from "crypto";
const prisma = new PrismaClient();
function generatePassword(length = 16): string {
// URL-safe characters only so the password is easy to copy from logs
return randomBytes(Math.ceil((length * 3) / 4))
.toString("base64url")
.slice(0, length);
}
async function main() {
const explicitPassword = process.env.ADMIN_PASSWORD ?? process.argv[2];
const existing = await prisma.user.findUnique({ where: { username: "admin" } });
// Admin exists and no explicit password override → nothing to do
if (existing && !explicitPassword) {
console.log("[setup-admin] Admin user already exists skipping.");
return;
}
let password: string;
let generated = false;
if (explicitPassword) {
if (explicitPassword.length < 8) {
console.error("[setup-admin] ERROR: ADMIN_PASSWORD must be at least 8 characters.");
process.exit(1);
}
password = explicitPassword;
} else {
// No admin user yet and no password given → auto-generate
password = generatePassword(16);
generated = true;
}
const passwordHash = await bcrypt.hash(password, 12);
await prisma.user.upsert({
where: { username: "admin" },
update: { passwordHash, role: "ADMIN" },
create: {
username: "admin",
email: "admin@localhost",
name: "Administrator",
passwordHash,
role: "ADMIN",
},
});
if (generated) {
console.log("");
console.log("╔══════════════════════════════════════════════════╗");
console.log("║ ADMIN-ZUGANGSDATEN (einmalig) ║");
console.log("╠══════════════════════════════════════════════════╣");
console.log(`║ Benutzername : admin ║`);
console.log(`║ Passwort : ${password.padEnd(32)}`);
console.log("╠══════════════════════════════════════════════════╣");
console.log("║ Bitte sofort nach dem ersten Login ändern! ║");
console.log("╚══════════════════════════════════════════════════╝");
console.log("");
} else {
console.log(`[setup-admin] ✅ Admin user ${existing ? "updated" : "created"} (username: admin).`);
}
}
main()
.catch((e) => {
console.error("[setup-admin] FATAL:", e);
process.exit(1);
})
.finally(() => prisma.$disconnect());