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:
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',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user