@umituz/react-native-ai-generation-content 1.12.3 → 1.12.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +30 -6
- package/src/domains/content-moderation/domain/entities/moderation.types.ts +84 -0
- package/src/domains/content-moderation/domain/interfaces/content-filter.interface.ts +24 -0
- package/src/domains/content-moderation/index.ts +67 -0
- package/src/domains/content-moderation/infrastructure/rules/default-rules.data.ts +144 -0
- package/src/domains/content-moderation/infrastructure/rules/rules-registry.ts +75 -0
- package/src/domains/content-moderation/infrastructure/services/content-moderation.service.ts +150 -0
- package/src/domains/content-moderation/infrastructure/services/index.ts +8 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/base.moderator.ts +62 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/image.moderator.ts +64 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/index.ts +10 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/text.moderator.ts +144 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/video.moderator.ts +64 -0
- package/src/domains/content-moderation/infrastructure/services/moderators/voice.moderator.ts +74 -0
- package/src/domains/content-moderation/infrastructure/services/pattern-matcher.service.ts +51 -0
- package/src/domains/content-moderation/presentation/exceptions/content-policy-violation.exception.ts +48 -0
- package/src/domains/creations/application/services/CreationsService.ts +71 -0
- package/src/domains/creations/domain/entities/Creation.ts +51 -0
- package/src/domains/creations/domain/entities/index.ts +6 -0
- package/src/domains/creations/domain/repositories/ICreationsRepository.ts +23 -0
- package/src/domains/creations/domain/repositories/index.ts +5 -0
- package/src/domains/creations/domain/services/ICreationsStorageService.ts +13 -0
- package/src/domains/creations/domain/value-objects/CreationsConfig.ts +76 -0
- package/src/domains/creations/domain/value-objects/index.ts +12 -0
- package/src/domains/creations/index.ts +84 -0
- package/src/domains/creations/infrastructure/adapters/createRepository.ts +54 -0
- package/src/domains/creations/infrastructure/adapters/index.ts +5 -0
- package/src/domains/creations/infrastructure/repositories/CreationsRepository.ts +233 -0
- package/src/domains/creations/infrastructure/repositories/index.ts +8 -0
- package/src/domains/creations/infrastructure/services/CreationsStorageService.ts +48 -0
- package/src/domains/creations/presentation/components/CreationCard.tsx +136 -0
- package/src/domains/creations/presentation/components/CreationDetail/DetailActions.tsx +76 -0
- package/src/domains/creations/presentation/components/CreationDetail/DetailHeader.tsx +81 -0
- package/src/domains/creations/presentation/components/CreationDetail/DetailImage.tsx +41 -0
- package/src/domains/creations/presentation/components/CreationDetail/DetailStory.tsx +67 -0
- package/src/domains/creations/presentation/components/CreationDetail/index.ts +4 -0
- package/src/domains/creations/presentation/components/CreationImageViewer.tsx +43 -0
- package/src/domains/creations/presentation/components/CreationThumbnail.tsx +63 -0
- package/src/domains/creations/presentation/components/CreationsGrid.tsx +75 -0
- package/src/domains/creations/presentation/components/CreationsHomeCard.tsx +176 -0
- package/src/domains/creations/presentation/components/EmptyState.tsx +75 -0
- package/src/domains/creations/presentation/components/FilterBottomSheet.tsx +158 -0
- package/src/domains/creations/presentation/components/FilterChips.tsx +105 -0
- package/src/domains/creations/presentation/components/GalleryHeader.tsx +106 -0
- package/src/domains/creations/presentation/components/index.ts +19 -0
- package/src/domains/creations/presentation/hooks/index.ts +7 -0
- package/src/domains/creations/presentation/hooks/useCreations.ts +33 -0
- package/src/domains/creations/presentation/hooks/useCreationsFilter.ts +70 -0
- package/src/domains/creations/presentation/hooks/useDeleteCreation.ts +51 -0
- package/src/domains/creations/presentation/screens/CreationDetailScreen.tsx +71 -0
- package/src/domains/creations/presentation/screens/CreationsGalleryScreen.tsx +217 -0
- package/src/domains/creations/presentation/screens/index.ts +5 -0
- package/src/domains/creations/presentation/utils/filterUtils.ts +52 -0
- package/src/domains/creations/types.d.ts +107 -0
- package/src/domains/face-detection/domain/constants/faceDetectionConstants.ts +16 -0
- package/src/domains/face-detection/domain/entities/FaceDetection.ts +19 -0
- package/src/domains/face-detection/index.ts +26 -0
- package/src/domains/face-detection/infrastructure/analyzers/faceAnalyzer.ts +36 -0
- package/src/domains/face-detection/infrastructure/validators/faceValidator.ts +52 -0
- package/src/domains/face-detection/presentation/components/FaceValidationStatus.tsx +111 -0
- package/src/domains/face-detection/presentation/hooks/useFaceDetection.ts +58 -0
- package/src/domains/feature-background/domain/entities/background.types.ts +77 -0
- package/src/domains/feature-background/domain/entities/component.types.ts +96 -0
- package/src/domains/feature-background/domain/entities/config.types.ts +41 -0
- package/src/domains/feature-background/domain/entities/index.ts +31 -0
- package/src/domains/feature-background/index.ts +72 -0
- package/src/domains/feature-background/infrastructure/constants/index.ts +5 -0
- package/src/domains/feature-background/infrastructure/constants/prompts.constants.ts +15 -0
- package/src/domains/feature-background/presentation/components/BackgroundFeature.tsx +145 -0
- package/src/domains/feature-background/presentation/components/ComparisonSlider.tsx +199 -0
- package/src/domains/feature-background/presentation/components/ErrorDisplay.tsx +58 -0
- package/src/domains/feature-background/presentation/components/FeatureHeader.tsx +80 -0
- package/src/domains/feature-background/presentation/components/GenerateButton.tsx +86 -0
- package/src/domains/feature-background/presentation/components/ImagePicker.tsx +136 -0
- package/src/domains/feature-background/presentation/components/ModeSelector.tsx +78 -0
- package/src/domains/feature-background/presentation/components/ProcessingModal.tsx +113 -0
- package/src/domains/feature-background/presentation/components/PromptInput.tsx +142 -0
- package/src/domains/feature-background/presentation/components/ResultDisplay.tsx +123 -0
- package/src/domains/feature-background/presentation/components/index.ts +16 -0
- package/src/domains/feature-background/presentation/hooks/index.ts +7 -0
- package/src/domains/feature-background/presentation/hooks/useBackgroundFeature.ts +118 -0
- package/src/index.ts +24 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Content Moderator
|
|
3
|
+
* Validates and moderates image URIs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Violation } from "../../../domain/entities/moderation.types";
|
|
7
|
+
import { BaseModerator, type ModerationResult } from "./base.moderator";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PROTOCOLS = ["http:", "https:", "file:", "data:"];
|
|
10
|
+
const DEFAULT_MAX_URI_LENGTH = 2048;
|
|
11
|
+
|
|
12
|
+
class ImageModerator extends BaseModerator {
|
|
13
|
+
private allowedProtocols = DEFAULT_PROTOCOLS;
|
|
14
|
+
private maxUriLength = DEFAULT_MAX_URI_LENGTH;
|
|
15
|
+
|
|
16
|
+
setAllowedProtocols(protocols: string[]): void {
|
|
17
|
+
this.allowedProtocols = protocols;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setMaxUriLength(length: number): void {
|
|
21
|
+
this.maxUriLength = length;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
moderate(imageUri: string): ModerationResult {
|
|
25
|
+
const validationError = this.validate(imageUri);
|
|
26
|
+
if (validationError) {
|
|
27
|
+
return { isAllowed: false, violations: [validationError] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { isAllowed: true, violations: [] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private validate(uri: string): Violation | null {
|
|
34
|
+
if (!uri || typeof uri !== "string") {
|
|
35
|
+
return this.createViolation("empty-uri", "Image Validation", "empty URI");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (uri.length > this.maxUriLength) {
|
|
39
|
+
return this.createViolation(
|
|
40
|
+
"uri-too-long",
|
|
41
|
+
"Image Validation",
|
|
42
|
+
"URI too long"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!this.hasValidProtocol(uri)) {
|
|
47
|
+
return this.createViolation(
|
|
48
|
+
"invalid-protocol",
|
|
49
|
+
"Image Validation",
|
|
50
|
+
"invalid protocol"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private hasValidProtocol(uri: string): boolean {
|
|
58
|
+
return this.allowedProtocols.some((protocol) =>
|
|
59
|
+
uri.toLowerCase().startsWith(protocol)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const imageModerator = new ImageModerator();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Moderators Index
|
|
3
|
+
* Exports all content moderators
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { textModerator } from "./text.moderator";
|
|
7
|
+
export { imageModerator } from "./image.moderator";
|
|
8
|
+
export { videoModerator } from "./video.moderator";
|
|
9
|
+
export { voiceModerator } from "./voice.moderator";
|
|
10
|
+
export { BaseModerator, type ModerationResult } from "./base.moderator";
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text Content Moderator
|
|
3
|
+
* Validates and moderates text content
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Violation } from "../../../domain/entities/moderation.types";
|
|
7
|
+
import { patternMatcherService } from "../pattern-matcher.service";
|
|
8
|
+
import { rulesRegistry } from "../../rules/rules-registry";
|
|
9
|
+
import { BaseModerator, type ModerationResult } from "./base.moderator";
|
|
10
|
+
|
|
11
|
+
declare const __DEV__: boolean;
|
|
12
|
+
|
|
13
|
+
const DEFAULT_MAX_LENGTH = 10000;
|
|
14
|
+
|
|
15
|
+
const MALICIOUS_CODE_PATTERNS = [
|
|
16
|
+
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
|
|
17
|
+
/javascript:/gi,
|
|
18
|
+
/on\w+\s*=/gi,
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
const PROMPT_INJECTION_PATTERNS = [
|
|
22
|
+
/ignore\s+(all\s+)?(previous|prior|above)\s+(instructions?|prompts?|rules?)/gi,
|
|
23
|
+
/disregard\s+(all\s+)?(previous|prior|above)\s+(instructions?|prompts?)/gi,
|
|
24
|
+
/forget\s+(all\s+)?(previous|prior|your)\s+(instructions?|prompts?|rules?)/gi,
|
|
25
|
+
/you\s+are\s+now\s+(a|an)\s+/gi,
|
|
26
|
+
/act\s+as\s+(if|though)\s+you/gi,
|
|
27
|
+
/pretend\s+(you\s+are|to\s+be)/gi,
|
|
28
|
+
/bypass\s+(your\s+)?(safety|content|moderation)/gi,
|
|
29
|
+
/override\s+(your\s+)?(restrictions?|limitations?|rules?)/gi,
|
|
30
|
+
/jailbreak/gi,
|
|
31
|
+
/DAN\s*mode/gi,
|
|
32
|
+
/developer\s+mode\s+(enabled|on|activated)/gi,
|
|
33
|
+
/system\s*:\s*/gi,
|
|
34
|
+
/\[system\]/gi,
|
|
35
|
+
/<<\s*sys\s*>>/gi,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
class TextModerator extends BaseModerator {
|
|
39
|
+
private maxLength = DEFAULT_MAX_LENGTH;
|
|
40
|
+
|
|
41
|
+
setMaxLength(length: number): void {
|
|
42
|
+
this.maxLength = length;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
moderate(content: string): ModerationResult {
|
|
46
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
47
|
+
// eslint-disable-next-line no-console
|
|
48
|
+
console.log("[TextModerator] moderate() called", {
|
|
49
|
+
contentLength: content?.length ?? 0,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const validationError = this.validate(content);
|
|
54
|
+
if (validationError) {
|
|
55
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
56
|
+
// eslint-disable-next-line no-console
|
|
57
|
+
console.log("[TextModerator] validation failed", {
|
|
58
|
+
ruleId: validationError.ruleId,
|
|
59
|
+
violationType: validationError.violationType,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return { isAllowed: false, violations: [validationError] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const violations = this.evaluateRules(content);
|
|
66
|
+
|
|
67
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.log("[TextModerator] moderate() completed", {
|
|
70
|
+
isAllowed: violations.length === 0,
|
|
71
|
+
violationsCount: violations.length,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { isAllowed: violations.length === 0, violations };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private validate(content: string): Violation | null {
|
|
79
|
+
if (!content || typeof content !== "string") {
|
|
80
|
+
return this.createViolation("empty-content", "Validation", "empty");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (content.length > this.maxLength) {
|
|
84
|
+
return this.createViolation("too-long", "Validation", "length exceeded");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.containsMaliciousCode(content)) {
|
|
88
|
+
return this.createViolation(
|
|
89
|
+
"malicious",
|
|
90
|
+
"Security",
|
|
91
|
+
"malicious code",
|
|
92
|
+
"dangerous_content"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (this.containsPromptInjection(content)) {
|
|
97
|
+
return this.createViolation(
|
|
98
|
+
"prompt-injection",
|
|
99
|
+
"Security",
|
|
100
|
+
"prompt injection",
|
|
101
|
+
"dangerous_content"
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private containsMaliciousCode(content: string): boolean {
|
|
109
|
+
return MALICIOUS_CODE_PATTERNS.some((pattern) => pattern.test(content));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private containsPromptInjection(content: string): boolean {
|
|
113
|
+
return PROMPT_INJECTION_PATTERNS.some((pattern) => pattern.test(content));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private evaluateRules(content: string): Violation[] {
|
|
117
|
+
const rules = rulesRegistry.getRulesByContentType("text");
|
|
118
|
+
const violations: Violation[] = [];
|
|
119
|
+
|
|
120
|
+
for (const rule of rules) {
|
|
121
|
+
const matches = patternMatcherService.matchAnyPattern(
|
|
122
|
+
content,
|
|
123
|
+
rule.patterns
|
|
124
|
+
);
|
|
125
|
+
const matched = matches.find((m) => m.matched);
|
|
126
|
+
|
|
127
|
+
if (matched) {
|
|
128
|
+
violations.push({
|
|
129
|
+
ruleId: rule.id,
|
|
130
|
+
ruleName: rule.name,
|
|
131
|
+
violationType: rule.violationType,
|
|
132
|
+
severity: rule.severity,
|
|
133
|
+
matchedPattern: matched.matchedText || "",
|
|
134
|
+
context: content.slice(0, 100),
|
|
135
|
+
suggestion: this.getSuggestion(rule.violationType),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return violations;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const textModerator = new TextModerator();
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Video Content Moderator
|
|
3
|
+
* Validates and moderates video URIs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Violation } from "../../../domain/entities/moderation.types";
|
|
7
|
+
import { BaseModerator, type ModerationResult } from "./base.moderator";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_PROTOCOLS = ["http:", "https:", "file:"];
|
|
10
|
+
const DEFAULT_MAX_URI_LENGTH = 2048;
|
|
11
|
+
|
|
12
|
+
class VideoModerator extends BaseModerator {
|
|
13
|
+
private allowedProtocols = DEFAULT_PROTOCOLS;
|
|
14
|
+
private maxUriLength = DEFAULT_MAX_URI_LENGTH;
|
|
15
|
+
|
|
16
|
+
setAllowedProtocols(protocols: string[]): void {
|
|
17
|
+
this.allowedProtocols = protocols;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
setMaxUriLength(length: number): void {
|
|
21
|
+
this.maxUriLength = length;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
moderate(videoUri: string): ModerationResult {
|
|
25
|
+
const validationError = this.validate(videoUri);
|
|
26
|
+
if (validationError) {
|
|
27
|
+
return { isAllowed: false, violations: [validationError] };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return { isAllowed: true, violations: [] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private validate(uri: string): Violation | null {
|
|
34
|
+
if (!uri || typeof uri !== "string") {
|
|
35
|
+
return this.createViolation("empty-uri", "Video Validation", "empty URI");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (uri.length > this.maxUriLength) {
|
|
39
|
+
return this.createViolation(
|
|
40
|
+
"uri-too-long",
|
|
41
|
+
"Video Validation",
|
|
42
|
+
"URI too long"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!this.hasValidProtocol(uri)) {
|
|
47
|
+
return this.createViolation(
|
|
48
|
+
"invalid-protocol",
|
|
49
|
+
"Video Validation",
|
|
50
|
+
"invalid protocol"
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private hasValidProtocol(uri: string): boolean {
|
|
58
|
+
return this.allowedProtocols.some((protocol) =>
|
|
59
|
+
uri.toLowerCase().startsWith(protocol)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const videoModerator = new VideoModerator();
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice Content Moderator
|
|
3
|
+
* Validates and moderates voice/TTS text content
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Violation } from "../../../domain/entities/moderation.types";
|
|
7
|
+
import { patternMatcherService } from "../pattern-matcher.service";
|
|
8
|
+
import { rulesRegistry } from "../../rules/rules-registry";
|
|
9
|
+
import { BaseModerator, type ModerationResult } from "./base.moderator";
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MAX_LENGTH = 5000;
|
|
12
|
+
|
|
13
|
+
class VoiceModerator extends BaseModerator {
|
|
14
|
+
private maxLength = DEFAULT_MAX_LENGTH;
|
|
15
|
+
|
|
16
|
+
setMaxLength(length: number): void {
|
|
17
|
+
this.maxLength = length;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
moderate(text: string): ModerationResult {
|
|
21
|
+
const validationError = this.validate(text);
|
|
22
|
+
if (validationError) {
|
|
23
|
+
return { isAllowed: false, violations: [validationError] };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const violations = this.evaluateRules(text);
|
|
27
|
+
return { isAllowed: violations.length === 0, violations };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private validate(text: string): Violation | null {
|
|
31
|
+
if (!text || typeof text !== "string") {
|
|
32
|
+
return this.createViolation("empty-text", "Voice Validation", "empty");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (text.length > this.maxLength) {
|
|
36
|
+
return this.createViolation(
|
|
37
|
+
"too-long",
|
|
38
|
+
"Voice Validation",
|
|
39
|
+
"length exceeded"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private evaluateRules(text: string): Violation[] {
|
|
47
|
+
const rules = rulesRegistry.getRulesByContentType("voice");
|
|
48
|
+
const violations: Violation[] = [];
|
|
49
|
+
|
|
50
|
+
for (const rule of rules) {
|
|
51
|
+
const matches = patternMatcherService.matchAnyPattern(
|
|
52
|
+
text,
|
|
53
|
+
rule.patterns
|
|
54
|
+
);
|
|
55
|
+
const matched = matches.find((m) => m.matched);
|
|
56
|
+
|
|
57
|
+
if (matched) {
|
|
58
|
+
violations.push({
|
|
59
|
+
ruleId: rule.id,
|
|
60
|
+
ruleName: rule.name,
|
|
61
|
+
violationType: rule.violationType,
|
|
62
|
+
severity: rule.severity,
|
|
63
|
+
matchedPattern: matched.matchedText || "",
|
|
64
|
+
context: text.slice(0, 100),
|
|
65
|
+
suggestion: this.getSuggestion(rule.violationType),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return violations;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const voiceModerator = new VoiceModerator();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Matcher Service
|
|
3
|
+
* Utility service for regex pattern matching
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface PatternMatch {
|
|
7
|
+
pattern: string;
|
|
8
|
+
matched: boolean;
|
|
9
|
+
matchedText?: string;
|
|
10
|
+
position?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class PatternMatcherService {
|
|
14
|
+
matchPattern(content: string, pattern: string): PatternMatch {
|
|
15
|
+
try {
|
|
16
|
+
const regex = new RegExp(pattern, "gi");
|
|
17
|
+
const match = regex.exec(content);
|
|
18
|
+
|
|
19
|
+
if (match) {
|
|
20
|
+
return {
|
|
21
|
+
pattern,
|
|
22
|
+
matched: true,
|
|
23
|
+
matchedText: match[0],
|
|
24
|
+
position: match.index,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { pattern, matched: false };
|
|
29
|
+
} catch {
|
|
30
|
+
return { pattern, matched: false };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
matchAnyPattern(content: string, patterns: string[]): PatternMatch[] {
|
|
35
|
+
return patterns.map((pattern) => this.matchPattern(content, pattern));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
matchAllPatterns(content: string, patterns: string[]): boolean {
|
|
39
|
+
return patterns.every(
|
|
40
|
+
(pattern) => this.matchPattern(content, pattern).matched
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
hasAnyMatch(content: string, patterns: string[]): boolean {
|
|
45
|
+
return patterns.some(
|
|
46
|
+
(pattern) => this.matchPattern(content, pattern).matched
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const patternMatcherService = new PatternMatcherService();
|
package/src/domains/content-moderation/presentation/exceptions/content-policy-violation.exception.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Policy Violation Exception
|
|
3
|
+
* Custom error class for content policy violations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
Violation,
|
|
8
|
+
ViolationType,
|
|
9
|
+
} from "../../domain/entities/moderation.types";
|
|
10
|
+
|
|
11
|
+
export class ContentPolicyViolationError extends Error {
|
|
12
|
+
public readonly violations: Violation[];
|
|
13
|
+
|
|
14
|
+
constructor(violations: Violation[], message?: string) {
|
|
15
|
+
const defaultMessage = `Content policy violation: ${violations
|
|
16
|
+
.map((v) => v.ruleName)
|
|
17
|
+
.join(", ")}`;
|
|
18
|
+
super(message || defaultMessage);
|
|
19
|
+
this.name = "ContentPolicyViolationError";
|
|
20
|
+
this.violations = violations;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getUserMessage(): string {
|
|
24
|
+
if (this.violations.length === 0) {
|
|
25
|
+
return "Content policy violation detected.";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const firstViolation = this.violations[0];
|
|
29
|
+
return firstViolation?.suggestion || "Please modify your content.";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getViolationTypes(): ViolationType[] {
|
|
33
|
+
return this.violations.map((v) => v.violationType);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hasViolationType(type: ViolationType): boolean {
|
|
37
|
+
return this.violations.some((v) => v.violationType === type);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getSeverityLevel(): "low" | "medium" | "high" | "critical" {
|
|
41
|
+
const severities = this.violations.map((v) => v.severity);
|
|
42
|
+
|
|
43
|
+
if (severities.includes("critical")) return "critical";
|
|
44
|
+
if (severities.includes("high")) return "high";
|
|
45
|
+
if (severities.includes("medium")) return "medium";
|
|
46
|
+
return "low";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { serverTimestamp, addDoc, collection } from "firebase/firestore";
|
|
2
|
+
import { generateUUID } from "@umituz/react-native-uuid";
|
|
3
|
+
import type { ICreationsRepository } from "../../domain/repositories/ICreationsRepository";
|
|
4
|
+
import type { ICreationsStorageService } from "../../domain/services/ICreationsStorageService";
|
|
5
|
+
import type { CreationType } from "../../domain/value-objects";
|
|
6
|
+
import { BaseRepository } from "@umituz/react-native-firebase";
|
|
7
|
+
|
|
8
|
+
export interface CreateCreationDTO {
|
|
9
|
+
userId: string;
|
|
10
|
+
type: CreationType;
|
|
11
|
+
prompt: string;
|
|
12
|
+
metadata?: Record<string, any>;
|
|
13
|
+
imageUri: string; // can be local file uri or base64
|
|
14
|
+
aspectRatio?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class CreationsService extends BaseRepository {
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly repository: ICreationsRepository,
|
|
20
|
+
private readonly storageService: ICreationsStorageService,
|
|
21
|
+
private readonly collectionName: string = "creations" // Default to generic name, app can override via repo
|
|
22
|
+
) {
|
|
23
|
+
super();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async saveCreation(dto: CreateCreationDTO): Promise<string> {
|
|
27
|
+
const db = this.getDb();
|
|
28
|
+
if (!db) throw new Error("Firestore not initialized");
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const creationId = generateUUID();
|
|
32
|
+
|
|
33
|
+
const imageUrl = await this.storageService.uploadCreationImage(
|
|
34
|
+
dto.userId,
|
|
35
|
+
creationId,
|
|
36
|
+
dto.imageUri
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
await this.repository.create(dto.userId, {
|
|
40
|
+
id: creationId,
|
|
41
|
+
uri: imageUrl,
|
|
42
|
+
type: dto.type.id,
|
|
43
|
+
prompt: dto.prompt,
|
|
44
|
+
metadata: dto.metadata,
|
|
45
|
+
createdAt: new Date(),
|
|
46
|
+
isShared: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return creationId;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(error);
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getCreation(userId: string, id: string): Promise<any> {
|
|
57
|
+
return this.repository.getById(userId, id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async updateCreation(
|
|
61
|
+
userId: string,
|
|
62
|
+
id: string,
|
|
63
|
+
updates: { metadata?: Record<string, any> },
|
|
64
|
+
): Promise<boolean> {
|
|
65
|
+
return this.repository.update(userId, id, updates);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async deleteCreation(userId: string, id: string): Promise<boolean> {
|
|
69
|
+
return this.repository.delete(userId, id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creation Entity
|
|
3
|
+
* Represents an AI-generated creation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Creation {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly uri: string;
|
|
9
|
+
readonly type: string;
|
|
10
|
+
readonly prompt?: string;
|
|
11
|
+
readonly metadata?: Record<string, any>;
|
|
12
|
+
readonly originalUri?: string;
|
|
13
|
+
readonly createdAt: Date;
|
|
14
|
+
readonly isShared: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CreationDocument {
|
|
18
|
+
readonly uri?: string;
|
|
19
|
+
readonly prompt?: string;
|
|
20
|
+
readonly metadata?: Record<string, any>;
|
|
21
|
+
readonly originalImage?: string;
|
|
22
|
+
readonly originalImageUrl?: string;
|
|
23
|
+
readonly transformedImage?: string;
|
|
24
|
+
readonly transformedImageUrl?: string;
|
|
25
|
+
readonly transformationType?: string;
|
|
26
|
+
readonly type?: string;
|
|
27
|
+
readonly status?: string;
|
|
28
|
+
readonly isShared: boolean;
|
|
29
|
+
readonly createdAt: FirebaseTimestamp | Date; // Allow Date for writing
|
|
30
|
+
readonly completedAt?: FirebaseTimestamp | Date;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface FirebaseTimestamp {
|
|
34
|
+
toDate: () => Date;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function mapDocumentToCreation(
|
|
38
|
+
id: string,
|
|
39
|
+
data: CreationDocument,
|
|
40
|
+
): Creation {
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
uri: data.transformedImageUrl || data.transformedImage || data.uri || "",
|
|
44
|
+
type: data.transformationType || data.type || "unknown",
|
|
45
|
+
prompt: data.prompt,
|
|
46
|
+
metadata: data.metadata,
|
|
47
|
+
originalUri: data.originalImageUrl || data.originalImage,
|
|
48
|
+
createdAt: (data.createdAt as any)?.toDate?.() || (data.createdAt instanceof Date ? data.createdAt : new Date()),
|
|
49
|
+
isShared: data.isShared ?? false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creations Repository Interface
|
|
3
|
+
* Defines the contract for creations data access
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Creation } from "../entities/Creation";
|
|
7
|
+
|
|
8
|
+
export interface ICreationsRepository {
|
|
9
|
+
getAll(userId: string): Promise<Creation[]>;
|
|
10
|
+
getById(userId: string, id: string): Promise<Creation | null>;
|
|
11
|
+
create(userId: string, creation: Creation): Promise<void>;
|
|
12
|
+
update(
|
|
13
|
+
userId: string,
|
|
14
|
+
id: string,
|
|
15
|
+
updates: Partial<Creation>,
|
|
16
|
+
): Promise<boolean>;
|
|
17
|
+
delete(userId: string, creationId: string): Promise<boolean>;
|
|
18
|
+
updateShared(
|
|
19
|
+
userId: string,
|
|
20
|
+
creationId: string,
|
|
21
|
+
isShared: boolean,
|
|
22
|
+
): Promise<boolean>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ICreationsStorageService {
|
|
2
|
+
uploadCreationImage(
|
|
3
|
+
userId: string,
|
|
4
|
+
creationId: string,
|
|
5
|
+
imageUri: string,
|
|
6
|
+
mimeType?: string
|
|
7
|
+
): Promise<string>;
|
|
8
|
+
|
|
9
|
+
deleteCreationImage(
|
|
10
|
+
userId: string,
|
|
11
|
+
creationId: string
|
|
12
|
+
): Promise<boolean>;
|
|
13
|
+
}
|