Ich habe gestern vergessen zu pushen

Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-29 19:21:38 +02:00
Ursprung e275b8fcd6
Commit 6466b36ddf
12 geänderte Dateien mit 125 neuen und 25 gelöschten Zeilen

Datei anzeigen

@ -83,13 +83,13 @@ export const encryptedDb = {
return db.prepare(`
INSERT INTO employees (
id, first_name, last_name, employee_number, photo, position,
id, first_name, last_name, employee_number, photo, position, official_title,
department, email, email_hash, phone, phone_hash, mobile, office, availability,
clearance_level, clearance_valid_until, clearance_issued_date,
primary_unit_id,
created_at, updated_at, created_by
) VALUES (
@id, @first_name, @last_name, @employee_number, @photo, @position,
@id, @first_name, @last_name, @employee_number, @photo, @position, @official_title,
@department, @email, @email_hash, @phone, @phone_hash, @mobile, @office, @availability,
@clearance_level, @clearance_valid_until, @clearance_issued_date,
@primary_unit_id,
@ -109,6 +109,7 @@ export const encryptedDb = {
// Decrypt sensitive fields
return {
...employee,
official_title: employee.official_title,
email: FieldEncryption.decrypt(employee.email),
phone: FieldEncryption.decrypt(employee.phone),
mobile: FieldEncryption.decrypt(employee.mobile),
@ -136,6 +137,7 @@ export const encryptedDb = {
return {
...emp,
official_title: emp.official_title,
email: safeDecrypt(emp.email),
phone: safeDecrypt(emp.phone),
mobile: safeDecrypt(emp.mobile),
@ -164,6 +166,7 @@ export function initializeSecureDatabase() {
employee_number TEXT UNIQUE NOT NULL,
photo TEXT,
position TEXT NOT NULL,
official_title TEXT,
department TEXT NOT NULL,
email TEXT NOT NULL,
email_hash TEXT,
@ -190,6 +193,10 @@ export function initializeSecureDatabase() {
if (!hasPrimaryUnitId) {
db.exec(`ALTER TABLE employees ADD COLUMN primary_unit_id TEXT`)
}
const hasOfficialTitle = cols.some(c => c.name === 'official_title')
if (!hasOfficialTitle) {
db.exec(`ALTER TABLE employees ADD COLUMN official_title TEXT`)
}
} catch (e) {
// ignore
}

Datei anzeigen

@ -8,6 +8,7 @@ export interface EmployeeInput {
employeeNumber?: string | null
photo?: string | null
position?: string
officialTitle?: string | null
department: string
email: string
phone?: string | null
@ -76,6 +77,7 @@ export function getAllWithDetails(): Employee[] {
employeeNumber: (emp.employee_number && !String(emp.employee_number).startsWith('EMP')) ? emp.employee_number : undefined,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -136,6 +138,7 @@ function buildEmployeeWithDetails(emp: any): Employee {
mobile: emp.mobile,
office: emp.office,
availability: emp.availability,
officialTitle: emp.official_title || undefined,
skills: skills.map((s: any) => ({
id: s.id,
name: s.name,
@ -166,6 +169,7 @@ export function createEmployee(input: EmployeeInput, actorUserId: string): { id:
const now = new Date().toISOString()
const employeeId = uuidv4()
const position = input.position || 'Teammitglied'
const officialTitle = input.officialTitle || null
const phone = input.phone || 'Nicht angegeben'
const availability = input.availability || 'available'
const employeeNumber = input.employeeNumber || `EMP${Date.now()}`
@ -186,6 +190,7 @@ export function createEmployee(input: EmployeeInput, actorUserId: string): { id:
employee_number: employeeNumber,
photo: input.photo || null,
position,
official_title: officialTitle,
department: input.department,
email: input.email,
phone,
@ -249,14 +254,14 @@ export function updateEmployee(id: string, input: EmployeeInput, actorUserId: st
db.prepare(`
UPDATE employees SET
first_name = ?, last_name = ?, position = ?, department = ?,
first_name = ?, last_name = ?, position = ?, official_title = ?, department = ?,
email = ?, email_hash = ?, phone = ?, phone_hash = ?,
mobile = ?, office = ?, availability = ?,
clearance_level = ?, clearance_valid_until = ?, clearance_issued_date = ?,
updated_at = ?, updated_by = ?
WHERE id = ?
`).run(
input.firstName, input.lastName, input.position || 'Teammitglied', input.department,
input.firstName, input.lastName, input.position || 'Teammitglied', input.officialTitle || null, input.department,
// encryption handled in secure db layer caller; here store encrypted values already in input? Route prepares with FieldEncryption
input.email, // already encrypted by route layer
null, // email_hash set by route if needed

Datei anzeigen

@ -34,7 +34,7 @@ function mapProficiencyToLevel(proficiency: string): 'basic' | 'fluent' | 'nativ
router.get('/', authenticate, requirePermission('employees:read'), async (req: AuthRequest, res, next) => {
try {
const employees = db.prepare(`
SELECT id, first_name, last_name, employee_number, photo, position,
SELECT id, first_name, last_name, employee_number, photo, position, official_title,
department, email, phone, mobile, office, availability,
clearance_level, clearance_valid_until, clearance_issued_date,
created_at, updated_at, created_by, updated_by
@ -70,6 +70,7 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
employeeNumber: emp.employee_number,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -116,7 +117,7 @@ router.get('/:id', authenticate, requirePermission('employees:read'), async (req
const { id } = req.params
const emp = db.prepare(`
SELECT id, first_name, last_name, employee_number, photo, position,
SELECT id, first_name, last_name, employee_number, photo, position, official_title,
department, email, phone, mobile, office, availability,
clearance_level, clearance_valid_until, clearance_issued_date,
created_at, updated_at, created_by, updated_by
@ -158,6 +159,7 @@ router.get('/:id', authenticate, requirePermission('employees:read'), async (req
employeeNumber: emp.employee_number,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -202,6 +204,7 @@ router.post('/',
[
body('firstName').notEmpty().trim(),
body('lastName').notEmpty().trim(),
body('officialTitle').optional().trim(),
body('email').isEmail(),
body('department').notEmpty().trim(),
body('organizationUnitId').optional({ checkFalsy: true }).isUUID(),
@ -221,7 +224,7 @@ router.post('/',
const now = new Date().toISOString()
const {
firstName, lastName, employeeNumber, photo, position,
firstName, lastName, employeeNumber, photo, position, officialTitle,
department, email, phone, mobile, office, availability,
clearance, skills, languages, specializations,
userRole, createUser, organizationUnitId, organizationRole
@ -253,11 +256,11 @@ router.post('/',
// Insert employee with default values for missing fields
db.prepare(`
INSERT INTO employees (
id, first_name, last_name, employee_number, photo, position,
id, first_name, last_name, employee_number, photo, position, official_title,
department, email, phone, mobile, office, availability, primary_unit_id,
clearance_level, clearance_valid_until, clearance_issued_date,
created_at, updated_at, created_by
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
employeeId,
firstName,
@ -265,6 +268,7 @@ router.post('/',
employeeNumber || null,
photo || null,
position || 'Teammitglied', // Default position
officialTitle || null,
resolvedDepartment,
email,
phone || 'Nicht angegeben', // Default phone
@ -430,6 +434,7 @@ router.put('/:id',
body('firstName').notEmpty().trim(),
body('lastName').notEmpty().trim(),
body('position').notEmpty().trim(),
body('officialTitle').optional().trim(),
body('department').notEmpty().trim(),
body('email').isEmail(),
body('phone').notEmpty().trim(),
@ -449,7 +454,7 @@ router.put('/:id',
const now = new Date().toISOString()
const {
firstName, lastName, position, department, email, phone,
firstName, lastName, position, officialTitle, department, email, phone,
mobile, office, availability, clearance, skills, languages, specializations
} = req.body
@ -465,13 +470,13 @@ router.put('/:id',
// Update employee
db.prepare(`
UPDATE employees SET
first_name = ?, last_name = ?, position = ?, department = ?,
first_name = ?, last_name = ?, position = ?, official_title = ?, department = ?,
email = ?, phone = ?, mobile = ?, office = ?, availability = ?,
clearance_level = ?, clearance_valid_until = ?, clearance_issued_date = ?,
updated_at = ?, updated_by = ?
WHERE id = ?
`).run(
firstName, lastName, position, department,
firstName, lastName, position, officialTitle || null, department,
email, phone, mobile || null, office || null, availability,
clearance?.level || null, clearance?.validUntil || null, clearance?.issuedDate || null,
now, req.user!.id, id
@ -508,6 +513,7 @@ router.put('/:id',
firstName,
lastName,
position,
officialTitle: officialTitle || null,
department,
email,
phone,

Datei anzeigen

@ -121,6 +121,7 @@ router.get('/', authenticate, requirePermission('employees:read'), async (req: A
employeeNumber: (emp.employee_number && !String(emp.employee_number).startsWith('EMP')) ? emp.employee_number : undefined,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -203,6 +204,7 @@ router.get('/public', authenticate, async (req: AuthRequest, res, next) => {
employeeNumber: (emp.employee_number && !String(emp.employee_number).startsWith('EMP')) ? emp.employee_number : undefined,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -285,6 +287,7 @@ router.get('/:id', authenticate, requirePermission('employees:read'), async (req
employeeNumber: (emp.employee_number && !String(emp.employee_number).startsWith('EMP')) ? emp.employee_number : undefined,
photo: emp.photo,
position: emp.position,
officialTitle: emp.official_title || undefined,
department: emp.department,
email: emp.email,
phone: emp.phone,
@ -354,7 +357,7 @@ router.post('/',
const now = new Date().toISOString()
const {
firstName, lastName, employeeNumber, photo, position = 'Teammitglied',
firstName, lastName, employeeNumber, photo, position = 'Teammitglied', officialTitle,
department, email, phone = 'Nicht angegeben', mobile, office, availability = 'available',
clearance, skills = [], languages = [], specializations = [], userRole, createUser,
primaryUnitId, assignmentRole
@ -380,6 +383,7 @@ router.post('/',
employee_number: finalEmployeeNumber,
photo: photo || null,
position,
official_title: officialTitle || null,
department,
email,
phone,
@ -513,6 +517,7 @@ router.post('/',
employeeNumber: finalEmployeeNumber,
photo: photo || null,
position,
officialTitle: officialTitle || null,
department,
email,
phone,
@ -591,6 +596,7 @@ router.put('/:id',
body('firstName').notEmpty().trim().escape(),
body('lastName').notEmpty().trim().escape(),
body('position').optional().trim().escape(),
body('officialTitle').optional().trim().escape(),
body('department').notEmpty().trim().escape(),
body('email').isEmail().normalizeEmail(),
body('phone').optional().trim(),
@ -612,7 +618,7 @@ router.put('/:id',
const now = new Date().toISOString()
const {
firstName, lastName, position = 'Teammitglied', department, email, phone = 'Nicht angegeben',
firstName, lastName, position = 'Teammitglied', officialTitle, department, email, phone = 'Nicht angegeben',
mobile, office, availability = 'available', clearance, skills, languages, specializations,
employeeNumber
} = req.body
@ -629,14 +635,14 @@ router.put('/:id',
// Update employee with encrypted fields
db.prepare(`
UPDATE employees SET
first_name = ?, last_name = ?, position = ?, department = ?,
first_name = ?, last_name = ?, position = ?, official_title = ?, department = ?,
email = ?, email_hash = ?, phone = ?, phone_hash = ?,
mobile = ?, office = ?, availability = ?,
clearance_level = ?, clearance_valid_until = ?, clearance_issued_date = ?,
updated_at = ?, updated_by = ?
WHERE id = ?
`).run(
firstName, lastName, position, department,
firstName, lastName, position, officialTitle || null, department,
FieldEncryption.encrypt(email),
FieldEncryption.hash(email),
FieldEncryption.encrypt(phone || ''),
@ -702,6 +708,7 @@ router.put('/:id',
firstName,
lastName,
position,
officialTitle: officialTitle || null,
department,
email,
phone,

Datei anzeigen

@ -18,6 +18,7 @@ export async function createEmployeeUC(req: Request, body: any, actorUserId: str
employeeNumber: body.employeeNumber,
photo: body.photo,
position: body.position,
officialTitle: body.officialTitle,
department: body.department,
email: body.email,
phone: body.phone,
@ -40,6 +41,7 @@ export async function createEmployeeUC(req: Request, body: any, actorUserId: str
employeeNumber: body.employeeNumber || null,
photo: body.photo || null,
position: body.position || 'Teammitglied',
officialTitle: body.officialTitle || null,
department: body.department,
email: body.email,
phone: body.phone || 'Nicht angegeben',
@ -65,6 +67,7 @@ export async function updateEmployeeUC(req: Request, id: string, body: any, acto
firstName: body.firstName,
lastName: body.lastName,
position: body.position,
officialTitle: body.officialTitle,
department: body.department,
email: body.email,
phone: body.phone,
@ -84,6 +87,7 @@ export async function updateEmployeeUC(req: Request, id: string, body: any, acto
firstName: body.firstName,
lastName: body.lastName,
position: body.position,
officialTitle: body.officialTitle,
department: body.department,
email: body.email,
phone: body.phone,