feat: Datenmodell Sprachnotizen (T7 L2 Task 4)

VoiceCapture und VoiceCaptureAudio nach Spec §3 mit CHECKs
(audioBytes 1..2 MiB, durationSec 1..240, CONFIRMED genau mit
inspectionId, audioMime nur bei AUDIO), Recht voice-captures:use für
ADMIN, BEEKEEPER und STAFF, Sperrreihenfolge mit VoiceCapture am Ende,
Ausschluss aus dem Backup mit Begründung, Wächter gegen Suche und
Export.

Refs #27

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-10-16 09:20:04 +02:00
1 parent 257286fbef
commit af9df0afe4
10 files changed
+412

No files matched your search

@@ -415,6 +415,8 @@ export const PERMISSION_CATALOG = [
// Beute in 3D: hive systems and their part-type catalog. Reading the
// catalog and writing a colony's setup use the colonies permissions.
...group('Beutensysteme', 'hive-systems', [['manage', 'Pflegen']], 3900),
// T7 Freihand: voice notes. Every endpoint also needs inspections:create.
...group('Sprache', 'voice-captures', [['use', 'Benutzen']], 4000),
] as const satisfies readonly PermissionCatalogEntry[];
export type PermissionKey = (typeof PERMISSION_CATALOG)[number]['key'];
@@ -1536,6 +1536,20 @@ describe('BackupService', () => {
);
});
it('excludes VoiceCapture and VoiceCaptureAudio with a reason', async () => {
const bundle = await service.exportAll();
for (const [model, key] of [
['VoiceCapture', 'voiceCapture'],
['VoiceCaptureAudio', 'voiceCaptureAudio'],
] as const) {
expect(bundle.data).not.toHaveProperty(key);
expect(MODELS_EXCLUDED_FROM_BACKUP[model]).toMatch(
/ephemeral, personal data, not deliverable after a restore/,
);
}
});
it('restores phenology reports after the apiaries, remapping a user that does not exist', async () => {
await importData({
apiary: [{ id: 'apiary-1' }],
@@ -158,6 +158,10 @@ export const MODELS_EXCLUDED_FROM_BACKUP: Readonly<
NotificationDelivery:
'Delivery queue - restoring it would resend old messages.',
TelegramPollState: 'Update offset and poll lease of this deployment.',
VoiceCapture:
'Voice notes are ephemeral, personal data, not deliverable after a restore (content is deleted after at most 7 days).',
VoiceCaptureAudio:
'Encrypted audio of voice notes: ephemeral, personal data, not deliverable after a restore.',
};
const DOCUMENT_ENTITY_TYPES = new Set<string>(
@@ -259,6 +259,7 @@ describe('lock keys per table', () => {
'AuthorityNotice',
'ApiaryRestriction',
'ComplianceAcknowledgement',
'VoiceCapture',
]);
});
+1
View File
@@ -59,6 +59,7 @@ const LOCKABLE_TABLES = {
AuthorityNotice: lockable('AuthorityNotice'),
ApiaryRestriction: lockable('ApiaryRestriction'),
ComplianceAcknowledgement: lockable('ComplianceAcknowledgement'),
VoiceCapture: lockable('VoiceCapture'),
} as const;
export type LockableTable = keyof typeof LOCKABLE_TABLES;