Files
beauty-bookings/src/server/rpc/auth.ts
elpatron af0502baa6 fix: Load admin credentials from .env file
- Add dotenv dependency and load .env configuration in auth.ts
- Admin credentials now read from environment variables:
  - ADMIN_USERNAME (default: owner)
  - ADMIN_PASSWORD_HASH (default: admin123 hash)
  - ADMIN_EMAIL (default: owner@stargirlnails.de)
- Remove hardcoded admin credentials
- Add fallback values for backward compatibility
- Clear existing user storage to force recreation with new credentials
2025-09-30 11:03:28 +02:00

180 lines
4.5 KiB
TypeScript

import { call, os } from "@orpc/server";
import { z } from "zod";
import { randomUUID } from "crypto";
import { createKV } from "@/server/lib/create-kv";
import { config } from "dotenv";
// Load environment variables from .env file
config();
const UserSchema = z.object({
id: z.string(),
username: z.string(),
email: z.string(),
passwordHash: z.string(),
role: z.enum(["customer", "owner"]),
createdAt: z.string(),
});
const SessionSchema = z.object({
id: z.string(),
userId: z.string(),
expiresAt: z.string(),
createdAt: z.string(),
});
type User = z.output<typeof UserSchema>;
type Session = z.output<typeof SessionSchema>;
const usersKV = createKV<User>("users");
const sessionsKV = createKV<Session>("sessions");
// Simple password hashing (in production, use bcrypt or similar)
const hashPassword = (password: string): string => {
return Buffer.from(password).toString('base64');
};
const verifyPassword = (password: string, hash: string): boolean => {
return hashPassword(password) === hash;
};
// Export hashPassword for external use (e.g., generating hashes for .env)
export const generatePasswordHash = hashPassword;
// Initialize default owner account
const initializeOwner = async () => {
const existingUsers = await usersKV.getAllItems();
if (existingUsers.length === 0) {
const ownerId = randomUUID();
// Get admin credentials from environment variables
const adminUsername = process.env.ADMIN_USERNAME || "owner";
const adminPasswordHash = process.env.ADMIN_PASSWORD_HASH || hashPassword("admin123");
const adminEmail = process.env.ADMIN_EMAIL || "owner@stargirlnails.de";
const owner: User = {
id: ownerId,
username: adminUsername,
email: adminEmail,
passwordHash: adminPasswordHash,
role: "owner",
createdAt: new Date().toISOString(),
};
await usersKV.setItem(ownerId, owner);
console.log(`✅ Admin account created: username="${adminUsername}", email="${adminEmail}"`);
}
};
// Initialize on module load
initializeOwner();
const login = os
.input(z.object({
username: z.string(),
password: z.string(),
}))
.handler(async ({ input }) => {
const users = await usersKV.getAllItems();
const user = users.find(u => u.username === input.username);
if (!user || !verifyPassword(input.password, user.passwordHash)) {
throw new Error("Invalid credentials");
}
// Create session
const sessionId = randomUUID();
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24); // 24 hours
const session: Session = {
id: sessionId,
userId: user.id,
expiresAt: expiresAt.toISOString(),
createdAt: new Date().toISOString(),
};
await sessionsKV.setItem(sessionId, session);
return {
sessionId,
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
},
};
});
const logout = os
.input(z.string()) // sessionId
.handler(async ({ input }) => {
await sessionsKV.removeItem(input);
return { success: true };
});
const verifySession = os
.input(z.string()) // sessionId
.handler(async ({ input }) => {
const session = await sessionsKV.getItem(input);
if (!session) {
throw new Error("Invalid session");
}
if (new Date(session.expiresAt) < new Date()) {
await sessionsKV.removeItem(input);
throw new Error("Session expired");
}
const user = await usersKV.getItem(session.userId);
if (!user) {
throw new Error("User not found");
}
return {
user: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
},
};
});
const changePassword = os
.input(z.object({
sessionId: z.string(),
currentPassword: z.string(),
newPassword: z.string(),
}))
.handler(async ({ input }) => {
const session = await sessionsKV.getItem(input.sessionId);
if (!session) {
throw new Error("Invalid session");
}
const user = await usersKV.getItem(session.userId);
if (!user) {
throw new Error("User not found");
}
if (!verifyPassword(input.currentPassword, user.passwordHash)) {
throw new Error("Current password is incorrect");
}
const updatedUser = {
...user,
passwordHash: hashPassword(input.newPassword),
};
await usersKV.setItem(user.id, updatedUser);
return { success: true };
});
export const router = {
login,
logout,
verifySession,
changePassword,
};