feat: add financial transactions management for companies
- Implemented a new route for managing financial transactions (money) for companies, including creating, editing, and deleting transactions. - Added a new model `Buchung` to represent transactions with fields for date, account type, transaction type, amount, and description. - Updated the `companies` model to include a relation to the new `Buchung` model. - Enhanced the company overview page to link to the new financial transactions page. - Added migration scripts to create the necessary database tables and fields for the new functionality. - Created utility scripts for resetting the admin password and setting up the initial admin user.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* 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.cjs
|
||||
*/
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { randomBytes } from "crypto";
|
||||
const prisma = new PrismaClient();
|
||||
function generatePassword(length = 16) {
|
||||
// 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;
|
||||
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());
|
||||
Reference in New Issue
Block a user