So mit neuen UI Ideen und so

Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-22 20:54:57 +02:00
Ursprung 6b9b6d4f20
Commit 26f95d2e4a
29 geänderte Dateien mit 5321 neuen und 1325 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,35 @@
import { v4 as uuidv4 } from 'uuid'
import { db } from '../config/secureDatabase'
import { logger } from '../utils/logger'
import type { Request } from 'express'
export function logSecurityAudit(
action: 'create' | 'read' | 'update' | 'delete' | 'login' | 'logout' | 'failed_login',
entityType: string,
entityId: string,
userId: string,
req: Request,
riskLevel: 'low' | 'medium' | 'high' | 'critical' = 'low'
) {
try {
db.prepare(`
INSERT INTO security_audit_log (
id, entity_type, entity_id, action, user_id,
timestamp, ip_address, user_agent, risk_level
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
uuidv4(),
entityType,
entityId,
action,
userId,
new Date().toISOString(),
(req as any).ip || (req as any).connection?.remoteAddress,
req.get('user-agent'),
riskLevel
)
} catch (error) {
logger.error('Failed to log security audit:', error)
}
}

Datei anzeigen

@ -0,0 +1,123 @@
import { db } from '../../config/database'
export async function applyChanges(payload: any) {
const { type, action, data } = payload
switch (type) {
case 'employees':
await syncEmployee(action, data)
break
case 'skills':
await syncSkill(action, data)
break
case 'users':
await syncUser(action, data)
break
case 'settings':
await syncSettings(action, data)
break
}
}
async function syncEmployee(action: string, data: any) {
switch (action) {
case 'create':
db.prepare(`
INSERT INTO employees (
id, first_name, last_name, employee_number, photo, position,
department, email, phone, mobile, office, availability,
clearance_level, clearance_valid_until, clearance_issued_date,
created_at, updated_at, created_by
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
data.id, data.firstName, data.lastName, data.employeeNumber,
data.photo, data.position, data.department, data.email,
data.phone, data.mobile, data.office, data.availability,
data.clearance?.level, data.clearance?.validUntil,
data.clearance?.issuedDate, data.createdAt, data.updatedAt,
data.createdBy
)
if (data.skills) {
for (const skill of data.skills) {
db.prepare(`
INSERT INTO employee_skills (employee_id, skill_id, level, verified, verified_by, verified_date)
VALUES (?, ?, ?, ?, ?, ?)
`).run(
data.id, skill.id, skill.level,
skill.verified ? 1 : 0, skill.verifiedBy, skill.verifiedDate
)
}
}
break
case 'update':
db.prepare(`
UPDATE employees SET
first_name = ?, last_name = ?, position = ?, department = ?,
email = ?, phone = ?, mobile = ?, office = ?, availability = ?,
updated_at = ?, updated_by = ?
WHERE id = ?
`).run(
data.firstName, data.lastName, data.position, data.department,
data.email, data.phone, data.mobile, data.office, data.availability,
data.updatedAt, data.updatedBy, data.id
)
break
case 'delete':
db.prepare('DELETE FROM employees WHERE id = ?').run(data.id)
db.prepare('DELETE FROM employee_skills WHERE employee_id = ?').run(data.id)
db.prepare('DELETE FROM language_skills WHERE employee_id = ?').run(data.id)
db.prepare('DELETE FROM specializations WHERE employee_id = ?').run(data.id)
break
}
}
async function syncSkill(action: string, data: any) {
switch (action) {
case 'create':
db.prepare(`
INSERT INTO skills (id, name, category, description, requires_certification, expires_after)
VALUES (?, ?, ?, ?, ?, ?)
`).run(data.id, data.name, data.category, data.description || null, data.requiresCertification ? 1 : 0, data.expiresAfter || null)
break
case 'update':
db.prepare(`
UPDATE skills SET name = COALESCE(?, name), category = COALESCE(?, category), description = COALESCE(?, description)
WHERE id = ?
`).run(data.name || null, data.category || null, data.description || null, data.id)
break
case 'delete':
db.prepare('DELETE FROM employee_skills WHERE skill_id = ?').run(data.id)
db.prepare('DELETE FROM skills WHERE id = ?').run(data.id)
break
}
}
async function syncUser(action: string, data: any) {
switch (action) {
case 'create':
db.prepare(`
INSERT INTO users (id, username, email, password, role, employee_id, is_active, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(data.id, data.username, data.email, data.password, data.role, data.employeeId || null, 1, data.createdAt, data.updatedAt)
break
case 'update':
db.prepare(`
UPDATE users SET username = ?, email = ?, role = ?, employee_id = ?, updated_at = ?
WHERE id = ?
`).run(data.username, data.email, data.role, data.employeeId || null, data.updatedAt, data.id)
break
case 'delete':
db.prepare('DELETE FROM users WHERE id = ?').run(data.id)
break
}
}
async function syncSettings(action: string, data: any) {
if (action === 'update') {
for (const [key, value] of Object.entries(data || {})) {
db.prepare(`INSERT INTO system_settings (key, value, updated_at) VALUES (?, ?, ?)
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`)
.run(key, String(value), new Date().toISOString())
}
}
}

Datei anzeigen

@ -0,0 +1,60 @@
import { db } from '../../config/database'
export const queueStore = {
getPending() {
return db.prepare(`
SELECT * FROM sync_log
WHERE status = 'pending'
ORDER BY created_at ASC
`).all() as any[]
},
markCompleted(id: string) {
db.prepare(`UPDATE sync_log SET status = 'completed', completed_at = ? WHERE id = ?`).run(new Date().toISOString(), id)
},
markFailed(id: string, message: string) {
db.prepare(`UPDATE sync_log SET status = 'failed', error_message = ? WHERE id = ?`).run(message, id)
},
updateMetadata(nodeId: string, result: { success: boolean; syncedItems: number; conflicts: any[]; errors: any[] }) {
const existing = db.prepare('SELECT * FROM sync_metadata WHERE node_id = ?').get(nodeId) as any
if (existing) {
db.prepare(`
UPDATE sync_metadata SET
last_sync_at = ?,
last_successful_sync = ?,
total_synced_items = total_synced_items + ?,
total_conflicts = total_conflicts + ?,
total_errors = total_errors + ?
WHERE node_id = ?
`).run(
new Date().toISOString(),
result.success ? new Date().toISOString() : existing.last_successful_sync,
result.syncedItems,
result.conflicts.length,
result.errors.length,
nodeId
)
} else {
db.prepare(`
INSERT INTO sync_metadata (
node_id, last_sync_at, last_successful_sync,
total_synced_items, total_conflicts, total_errors
) VALUES (?, ?, ?, ?, ?, ?)
`).run(
nodeId,
new Date().toISOString(),
result.success ? new Date().toISOString() : null,
result.syncedItems,
result.conflicts.length,
result.errors.length
)
}
},
getSyncSettings() {
const settings = db.prepare('SELECT * FROM sync_settings WHERE id = ?').get('default') as any
return settings || { autoSyncInterval: 'disabled', conflictResolution: 'admin' }
},
getNodeInfo(nodeId: string) {
return db.prepare('SELECT * FROM network_nodes WHERE id = ?').get(nodeId)
}
}

Datei anzeigen

@ -0,0 +1,17 @@
import axios from 'axios'
export async function sendToNode(targetNode: any, payload: any, nodeId: string) {
const response = await axios.post(
`http://${targetNode.ip_address}:${targetNode.port}/api/sync/receive`,
payload,
{
headers: {
'Authorization': `Bearer ${targetNode.api_key}`,
'X-Node-Id': nodeId
},
timeout: 30000
}
)
return response.data
}