Files
TicketingSystem/server/src/services/userService.ts
T
josh d8785a964d
Build & Push / Test (client) (push) Successful in 31s
Build & Push / Test (server) (push) Successful in 38s
Build & Push / Build Client (push) Successful in 1m17s
Build & Push / Build Server (push) Successful in 1m18s
Merge SERVICE role into AGENT
Every AGENT now gets an auto-generated API key on creation, shown once
in a modal. AGENTs log in with password and authenticate to the API
with X-Api-Key. pre-push.sql defensively migrates any residual SERVICE
rows to AGENT before Prisma rewrites the enum. Goddard is no longer
baked into the seed — create agents via Admin → Users.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-18 22:44:32 -04:00

91 lines
2.4 KiB
TypeScript

import bcrypt from 'bcryptjs';
import crypto from 'crypto';
import { Prisma } from '@prisma/client';
import prisma from '../lib/prisma';
import { HttpError } from '../lib/httpError';
import type { CreateUserInput, UpdateUserInput } from '../../../shared/schemas/user';
const userSelect = {
id: true,
username: true,
displayName: true,
email: true,
role: true,
apiKey: true,
createdAt: true,
} as const;
const userListSelect = {
id: true,
username: true,
displayName: true,
email: true,
role: true,
createdAt: true,
} as const;
export function listUsers() {
return prisma.user.findMany({
select: userListSelect,
orderBy: { displayName: 'asc' },
});
}
export async function getCurrentUser(id: string) {
const user = await prisma.user.findUnique({
where: { id },
select: userListSelect,
});
if (!user) throw new HttpError(404, 'User not found');
return user;
}
export async function createUser(data: CreateUserInput) {
const passwordHash = await bcrypt.hash(data.password, 12);
const apiKey =
data.role === 'AGENT' ? `sk_${crypto.randomBytes(32).toString('hex')}` : undefined;
return prisma.user.create({
data: {
username: data.username,
email: data.email,
displayName: data.displayName,
passwordHash,
role: data.role,
apiKey,
},
select: userSelect,
});
}
export async function updateUser(id: string, data: UpdateUserInput) {
const update: Prisma.UserUpdateInput = {};
if (data.displayName) update.displayName = data.displayName;
if (data.email) update.email = data.email;
if (data.password) update.passwordHash = await bcrypt.hash(data.password, 12);
if (data.role) {
update.role = data.role;
if (data.role === 'AGENT') {
const existing = await prisma.user.findUnique({ where: { id }, select: { apiKey: true } });
if (!existing?.apiKey) {
update.apiKey = `sk_${crypto.randomBytes(32).toString('hex')}`;
}
} else {
update.apiKey = null;
}
}
if (data.regenerateApiKey) {
update.apiKey = `sk_${crypto.randomBytes(32).toString('hex')}`;
}
return prisma.user.update({ where: { id }, data: update, select: userSelect });
}
export async function deleteUser(id: string, actorId: string) {
if (id === actorId) {
throw new HttpError(400, 'Cannot delete your own account');
}
await prisma.user.delete({ where: { id } });
}