@nextsparkjs/core 0.1.0-beta.97 → 0.1.0-beta.99
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/dist/components/media/MediaCard.d.ts +2 -1
- package/dist/components/media/MediaCard.d.ts.map +1 -1
- package/dist/components/media/MediaCard.js +13 -9
- package/dist/components/media/MediaDetailPanel.d.ts +2 -1
- package/dist/components/media/MediaDetailPanel.d.ts.map +1 -1
- package/dist/components/media/MediaDetailPanel.js +22 -10
- package/dist/components/media/MediaGrid.d.ts +3 -2
- package/dist/components/media/MediaGrid.d.ts.map +1 -1
- package/dist/components/media/MediaGrid.js +3 -1
- package/dist/components/media/MediaList.d.ts +3 -2
- package/dist/components/media/MediaList.d.ts.map +1 -1
- package/dist/components/media/MediaList.js +10 -6
- package/dist/contexts/TeamContext.d.ts.map +1 -1
- package/dist/contexts/TeamContext.js +9 -5
- package/dist/hooks/useMedia.d.ts.map +1 -1
- package/dist/hooks/useMedia.js +20 -14
- package/dist/lib/api/api-error.d.ts +41 -0
- package/dist/lib/api/api-error.d.ts.map +1 -0
- package/dist/lib/api/api-error.js +61 -0
- package/dist/lib/api/auth/dual-auth.d.ts +15 -0
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +21 -1
- package/dist/lib/api/index.d.ts +2 -0
- package/dist/lib/api/index.d.ts.map +1 -1
- package/dist/lib/api/index.js +5 -0
- package/dist/lib/api/permission-middleware.d.ts.map +1 -1
- package/dist/lib/api/permission-middleware.js +2 -1
- package/dist/lib/services/media.service.d.ts +30 -56
- package/dist/lib/services/media.service.d.ts.map +1 -1
- package/dist/lib/services/media.service.js +63 -77
- package/dist/lib/teams/schema.d.ts +6 -34
- package/dist/lib/teams/schema.d.ts.map +1 -1
- package/dist/lib/teams/schema.js +14 -7
- package/dist/messages/de/index.d.ts +2 -0
- package/dist/messages/de/index.d.ts.map +1 -1
- package/dist/messages/de/permissions.json +2 -0
- package/dist/messages/en/index.d.ts +3 -0
- package/dist/messages/en/index.d.ts.map +1 -1
- package/dist/messages/en/media.json +1 -0
- package/dist/messages/en/permissions.json +2 -0
- package/dist/messages/es/index.d.ts +3 -0
- package/dist/messages/es/index.d.ts.map +1 -1
- package/dist/messages/es/media.json +1 -0
- package/dist/messages/es/permissions.json +2 -0
- package/dist/messages/fr/index.d.ts +2 -0
- package/dist/messages/fr/index.d.ts.map +1 -1
- package/dist/messages/fr/permissions.json +2 -0
- package/dist/messages/it/index.d.ts +2 -0
- package/dist/messages/it/index.d.ts.map +1 -1
- package/dist/messages/it/permissions.json +2 -0
- package/dist/messages/pt/index.d.ts +2 -0
- package/dist/messages/pt/index.d.ts.map +1 -1
- package/dist/messages/pt/permissions.json +2 -0
- package/dist/migrations/021_media.sql +53 -0
- package/dist/providers/query-provider.d.ts +0 -1
- package/dist/providers/query-provider.d.ts.map +1 -1
- package/dist/providers/query-provider.js +26 -3
- package/dist/styles/classes.json +2 -3
- package/dist/templates/app/api/csp-report/route.ts +1 -0
- package/dist/templates/app/api/v1/media/[id]/route.ts +50 -14
- package/dist/templates/app/api/v1/media/[id]/tags/route.ts +75 -9
- package/dist/templates/app/api/v1/media/check-duplicates/route.ts +14 -2
- package/dist/templates/app/api/v1/media/route.ts +17 -5
- package/dist/templates/app/api/v1/media/upload/route.ts +22 -10
- package/dist/templates/app/api/v1/media-tags/route.ts +27 -7
- package/dist/templates/app/dashboard/(main)/media/page.tsx +35 -23
- package/dist/templates/instrumentation.ts +18 -12
- package/migrations/021_media.sql +53 -0
- package/package.json +15 -15
- package/scripts/build/registry/discovery/permissions.mjs +79 -2
- package/templates/app/api/csp-report/route.ts +1 -0
- package/templates/app/api/v1/media/[id]/route.ts +50 -14
- package/templates/app/api/v1/media/[id]/tags/route.ts +75 -9
- package/templates/app/api/v1/media/check-duplicates/route.ts +14 -2
- package/templates/app/api/v1/media/route.ts +17 -5
- package/templates/app/api/v1/media/upload/route.ts +22 -10
- package/templates/app/api/v1/media-tags/route.ts +27 -7
- package/templates/app/dashboard/(main)/media/page.tsx +35 -23
- package/templates/instrumentation.ts +18 -12
- package/tests/jest/__mocks__/@nextsparkjs/registries/permissions-registry.ts +28 -17
package/dist/lib/api/index.d.ts
CHANGED
|
@@ -11,6 +11,8 @@ export * from './helpers';
|
|
|
11
11
|
export * from './rate-limit';
|
|
12
12
|
export * from './cache';
|
|
13
13
|
export * from './distributed-cache';
|
|
14
|
+
export * from './api-error';
|
|
14
15
|
export type { ApiKeyAuth, ApiKeyValidationResult } from './auth';
|
|
15
16
|
export type { ApiScope } from './keys';
|
|
17
|
+
export { ApiError } from './api-error';
|
|
16
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/api/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,QAAQ,CAAC;AACvB,cAAc,kBAAkB,CAAC;AACjC,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/api/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,QAAQ,CAAC;AACvB,cAAc,kBAAkB,CAAC;AACjC,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAG5B,YAAY,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,QAAQ,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/lib/api/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"permission-middleware.d.ts","sourceRoot":"","sources":["../../../src/lib/api/permission-middleware.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAc1C;;GAEG;AACH,eAAO,MAAM,WAAW,SAAS,CAAA;AAEjC;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAElF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAOjF;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC,CAkCtE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,OAAO,GACpB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAK9B"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkPermission } from "../permissions/check.js";
|
|
2
2
|
import { NextResponse } from "next/server";
|
|
3
|
+
import { API_ERROR_CODES } from "./api-error.js";
|
|
3
4
|
const HTTP_TO_ACTION = {
|
|
4
5
|
"GET": "read",
|
|
5
6
|
// Single entity retrieval
|
|
@@ -31,7 +32,7 @@ async function checkEntityPermission(userId, teamId, entitySlug, action) {
|
|
|
31
32
|
success: false,
|
|
32
33
|
error: {
|
|
33
34
|
message: `Permission denied: You do not have permission to ${action} ${entitySlug}`,
|
|
34
|
-
code:
|
|
35
|
+
code: API_ERROR_CODES.PERMISSION_DENIED,
|
|
35
36
|
details: {
|
|
36
37
|
requiredPermission: permission,
|
|
37
38
|
entity: entitySlug,
|
|
@@ -2,50 +2,40 @@
|
|
|
2
2
|
* Media Service
|
|
3
3
|
*
|
|
4
4
|
* Provides CRUD operations for media library entries.
|
|
5
|
-
*
|
|
5
|
+
* ALL methods require teamId for strict team isolation.
|
|
6
6
|
*
|
|
7
7
|
* @module MediaService
|
|
8
8
|
*/
|
|
9
9
|
import type { Media, CreateMediaInput, UpdateMediaInput, MediaListOptions, MediaListResult, MediaTag } from '../media/types';
|
|
10
10
|
export declare class MediaService {
|
|
11
11
|
/**
|
|
12
|
-
* Get media item by ID
|
|
12
|
+
* Get media item by ID (team-scoped)
|
|
13
13
|
*
|
|
14
14
|
* @param id - Media ID
|
|
15
15
|
* @param userId - User ID for RLS context
|
|
16
|
+
* @param teamId - Team ID for isolation
|
|
16
17
|
* @returns Media or null if not found
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* const media = await MediaService.getById('media-123', 'user-456')
|
|
20
18
|
*/
|
|
21
|
-
static getById(id: string, userId: string): Promise<Media | null>;
|
|
19
|
+
static getById(id: string, userId: string, teamId: string): Promise<Media | null>;
|
|
22
20
|
/**
|
|
23
|
-
* List media with pagination, filtering, and search
|
|
21
|
+
* List media with pagination, filtering, and search (team-scoped)
|
|
24
22
|
*
|
|
25
23
|
* @param userId - User ID for RLS context
|
|
24
|
+
* @param teamId - Team ID for isolation
|
|
26
25
|
* @param options - List options (pagination, filtering, search, sort)
|
|
27
26
|
* @returns Paginated media list result
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* const result = await MediaService.list('user-123', {
|
|
31
|
-
* limit: 20,
|
|
32
|
-
* offset: 0,
|
|
33
|
-
* type: 'image',
|
|
34
|
-
* search: 'logo',
|
|
35
|
-
* orderBy: 'createdAt',
|
|
36
|
-
* orderDir: 'desc'
|
|
37
|
-
* })
|
|
38
27
|
*/
|
|
39
|
-
static list(userId: string, options?: MediaListOptions): Promise<MediaListResult>;
|
|
28
|
+
static list(userId: string, teamId: string, options?: MediaListOptions): Promise<MediaListResult>;
|
|
40
29
|
/**
|
|
41
|
-
* Find existing media by filename and fileSize (duplicate detection)
|
|
30
|
+
* Find existing media by filename and fileSize (duplicate detection, team-scoped)
|
|
42
31
|
*
|
|
43
32
|
* @param userId - User ID for RLS context
|
|
33
|
+
* @param teamId - Team ID for isolation
|
|
44
34
|
* @param filename - Original filename
|
|
45
35
|
* @param fileSize - File size in bytes
|
|
46
36
|
* @returns Matching media items (same name+size = likely duplicate)
|
|
47
37
|
*/
|
|
48
|
-
static findDuplicates(userId: string, filename: string, fileSize: number): Promise<Media[]>;
|
|
38
|
+
static findDuplicates(userId: string, teamId: string, filename: string, fileSize: number): Promise<Media[]>;
|
|
49
39
|
/**
|
|
50
40
|
* Create a new media record
|
|
51
41
|
*
|
|
@@ -53,59 +43,44 @@ export declare class MediaService {
|
|
|
53
43
|
* @param teamId - Team ID for isolation
|
|
54
44
|
* @param data - Media data
|
|
55
45
|
* @returns Created media record
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* const media = await MediaService.create('user-123', 'team-456', {
|
|
59
|
-
* url: 'https://example.com/image.jpg',
|
|
60
|
-
* filename: 'image.jpg',
|
|
61
|
-
* fileSize: 150000,
|
|
62
|
-
* mimeType: 'image/jpeg',
|
|
63
|
-
* width: 1920,
|
|
64
|
-
* height: 1080
|
|
65
|
-
* })
|
|
66
46
|
*/
|
|
67
47
|
static create(userId: string, teamId: string, data: CreateMediaInput): Promise<Media>;
|
|
68
48
|
/**
|
|
69
|
-
* Update media metadata (
|
|
49
|
+
* Update media metadata (team-scoped)
|
|
70
50
|
*
|
|
71
51
|
* @param id - Media ID
|
|
72
52
|
* @param userId - User ID for RLS context
|
|
73
|
-
* @param data - Update data (alt, caption)
|
|
74
|
-
* @
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* const media = await MediaService.update('media-123', 'user-456', {
|
|
78
|
-
* alt: 'Company logo',
|
|
79
|
-
* caption: 'Our brand logo in high resolution'
|
|
80
|
-
* })
|
|
53
|
+
* @param data - Update data (title, alt, caption)
|
|
54
|
+
* @param teamId - Team ID for isolation
|
|
55
|
+
* @returns Updated media record or null if not found
|
|
81
56
|
*/
|
|
82
|
-
static update(id: string, userId: string, data: UpdateMediaInput): Promise<Media>;
|
|
57
|
+
static update(id: string, userId: string, data: UpdateMediaInput, teamId: string): Promise<Media | null>;
|
|
83
58
|
/**
|
|
84
|
-
* Soft delete a media record (
|
|
59
|
+
* Soft delete a media record (team-scoped)
|
|
85
60
|
*
|
|
86
61
|
* @param id - Media ID
|
|
87
62
|
* @param userId - User ID for RLS context
|
|
63
|
+
* @param teamId - Team ID for isolation
|
|
88
64
|
* @returns True if deleted successfully
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* const deleted = await MediaService.softDelete('media-123', 'user-456')
|
|
92
65
|
*/
|
|
93
|
-
static softDelete(id: string, userId: string): Promise<boolean>;
|
|
66
|
+
static softDelete(id: string, userId: string, teamId: string): Promise<boolean>;
|
|
94
67
|
/**
|
|
95
|
-
* Get all media tags
|
|
68
|
+
* Get all media tags for a team
|
|
96
69
|
*
|
|
97
70
|
* @param userId - User ID for RLS context
|
|
98
|
-
* @
|
|
71
|
+
* @param teamId - Team ID for isolation
|
|
72
|
+
* @returns Array of media tags scoped to the team
|
|
99
73
|
*/
|
|
100
|
-
static getTags(userId: string): Promise<MediaTag[]>;
|
|
74
|
+
static getTags(userId: string, teamId: string): Promise<MediaTag[]>;
|
|
101
75
|
/**
|
|
102
76
|
* Get tags for a specific media item
|
|
103
77
|
*
|
|
104
78
|
* @param mediaId - Media item ID
|
|
105
79
|
* @param userId - User ID for RLS context
|
|
80
|
+
* @param teamId - Team ID for isolation (media ownership verified before calling)
|
|
106
81
|
* @returns Array of tags assigned to the media
|
|
107
82
|
*/
|
|
108
|
-
static getMediaTags(mediaId: string, userId: string): Promise<MediaTag[]>;
|
|
83
|
+
static getMediaTags(mediaId: string, userId: string, teamId: string): Promise<MediaTag[]>;
|
|
109
84
|
/**
|
|
110
85
|
* Add a tag to a media item
|
|
111
86
|
*
|
|
@@ -133,24 +108,23 @@ export declare class MediaService {
|
|
|
133
108
|
*/
|
|
134
109
|
static setTags(mediaId: string, tagIds: string[], userId: string): Promise<void>;
|
|
135
110
|
/**
|
|
136
|
-
* Create a new media tag (
|
|
111
|
+
* Create a new media tag (team-scoped)
|
|
137
112
|
*
|
|
138
113
|
* @param name - Tag display name
|
|
139
114
|
* @param userId - User ID for RLS context
|
|
115
|
+
* @param teamId - Team ID for isolation
|
|
140
116
|
* @returns The created tag
|
|
141
117
|
*/
|
|
142
|
-
static createTag(name: string, userId: string): Promise<MediaTag>;
|
|
118
|
+
static createTag(name: string, userId: string, teamId: string): Promise<MediaTag>;
|
|
143
119
|
/**
|
|
144
|
-
* Count media items
|
|
120
|
+
* Count media items (team-scoped)
|
|
145
121
|
*
|
|
146
122
|
* @param userId - User ID for RLS context
|
|
123
|
+
* @param teamId - Team ID for isolation
|
|
147
124
|
* @param options - Count options (type filter, status filter)
|
|
148
125
|
* @returns Total count
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* const imageCount = await MediaService.count('user-123', { type: 'image' })
|
|
152
126
|
*/
|
|
153
|
-
static count(userId: string, options?: {
|
|
127
|
+
static count(userId: string, teamId: string, options?: {
|
|
154
128
|
type?: 'image' | 'video' | 'all';
|
|
155
129
|
status?: string;
|
|
156
130
|
}): Promise<number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/media.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,KAAK,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACT,MAAM,gBAAgB,CAAA;AAEvB,qBAAa,YAAY;IAKvB
|
|
1
|
+
{"version":3,"file":"media.service.d.ts","sourceRoot":"","sources":["../../../src/lib/services/media.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,KAAK,EACL,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,QAAQ,EACT,MAAM,gBAAgB,CAAA;AAEvB,qBAAa,YAAY;IAKvB;;;;;;;OAOG;WACU,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAYvF;;;;;;;OAOG;WACU,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC;IA+F3G;;;;;;;;OAQG;WACU,cAAc,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,KAAK,EAAE,CAAC;IAkBnB;;;;;;;OAOG;WACU,MAAM,CACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,KAAK,CAAC;IAsBjB;;;;;;;;OAQG;WACU,MAAM,CACjB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,gBAAgB,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;IAyCxB;;;;;;;OAOG;WACU,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBrF;;;;;;OAMG;WACU,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAczE;;;;;;;OAOG;WACU,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAkB/F;;;;;;;OAOG;WACU,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBrF;;;;;;;OAOG;WACU,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAexF;;;;;;OAMG;WACU,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBtF;;;;;;;OAOG;WACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAoBvF;;;;;;;OAOG;WACU,KAAK,CAChB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAClE,OAAO,CAAC,MAAM,CAAC;CAmBnB"}
|
|
@@ -4,44 +4,35 @@ class MediaService {
|
|
|
4
4
|
// QUERIES
|
|
5
5
|
// ============================================
|
|
6
6
|
/**
|
|
7
|
-
* Get media item by ID
|
|
7
|
+
* Get media item by ID (team-scoped)
|
|
8
8
|
*
|
|
9
9
|
* @param id - Media ID
|
|
10
10
|
* @param userId - User ID for RLS context
|
|
11
|
+
* @param teamId - Team ID for isolation
|
|
11
12
|
* @returns Media or null if not found
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const media = await MediaService.getById('media-123', 'user-456')
|
|
15
13
|
*/
|
|
16
|
-
static async getById(id, userId) {
|
|
14
|
+
static async getById(id, userId, teamId) {
|
|
17
15
|
if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
|
|
18
16
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
17
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
19
18
|
return queryOneWithRLS(
|
|
20
|
-
`SELECT * FROM "media" WHERE id = $1 AND status = 'active'`,
|
|
21
|
-
[id],
|
|
19
|
+
`SELECT * FROM "media" WHERE id = $1 AND "teamId" = $2 AND status = 'active'`,
|
|
20
|
+
[id, teamId],
|
|
22
21
|
userId
|
|
23
22
|
);
|
|
24
23
|
}
|
|
25
24
|
/**
|
|
26
|
-
* List media with pagination, filtering, and search
|
|
25
|
+
* List media with pagination, filtering, and search (team-scoped)
|
|
27
26
|
*
|
|
28
27
|
* @param userId - User ID for RLS context
|
|
28
|
+
* @param teamId - Team ID for isolation
|
|
29
29
|
* @param options - List options (pagination, filtering, search, sort)
|
|
30
30
|
* @returns Paginated media list result
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* const result = await MediaService.list('user-123', {
|
|
34
|
-
* limit: 20,
|
|
35
|
-
* offset: 0,
|
|
36
|
-
* type: 'image',
|
|
37
|
-
* search: 'logo',
|
|
38
|
-
* orderBy: 'createdAt',
|
|
39
|
-
* orderDir: 'desc'
|
|
40
|
-
* })
|
|
41
31
|
*/
|
|
42
|
-
static async list(userId, options = {}) {
|
|
32
|
+
static async list(userId, teamId, options = {}) {
|
|
43
33
|
var _a;
|
|
44
34
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
35
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
45
36
|
const {
|
|
46
37
|
limit = 20,
|
|
47
38
|
offset = 0,
|
|
@@ -53,9 +44,9 @@ class MediaService {
|
|
|
53
44
|
tagIds,
|
|
54
45
|
tagSlugs
|
|
55
46
|
} = options;
|
|
56
|
-
const conditions = ["m.status = $1"];
|
|
57
|
-
const params = [status];
|
|
58
|
-
let paramIndex =
|
|
47
|
+
const conditions = ["m.status = $1", 'm."teamId" = $2'];
|
|
48
|
+
const params = [status, teamId];
|
|
49
|
+
let paramIndex = 3;
|
|
59
50
|
if (type === "image") {
|
|
60
51
|
conditions.push(`m."mimeType" LIKE 'image/%'`);
|
|
61
52
|
} else if (type === "video") {
|
|
@@ -116,21 +107,23 @@ class MediaService {
|
|
|
116
107
|
return { data, total, limit, offset };
|
|
117
108
|
}
|
|
118
109
|
/**
|
|
119
|
-
* Find existing media by filename and fileSize (duplicate detection)
|
|
110
|
+
* Find existing media by filename and fileSize (duplicate detection, team-scoped)
|
|
120
111
|
*
|
|
121
112
|
* @param userId - User ID for RLS context
|
|
113
|
+
* @param teamId - Team ID for isolation
|
|
122
114
|
* @param filename - Original filename
|
|
123
115
|
* @param fileSize - File size in bytes
|
|
124
116
|
* @returns Matching media items (same name+size = likely duplicate)
|
|
125
117
|
*/
|
|
126
|
-
static async findDuplicates(userId, filename, fileSize) {
|
|
118
|
+
static async findDuplicates(userId, teamId, filename, fileSize) {
|
|
127
119
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
120
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
128
121
|
return queryWithRLS(
|
|
129
122
|
`SELECT * FROM "media"
|
|
130
|
-
WHERE filename = $1 AND "fileSize" = $2 AND status = 'active'
|
|
123
|
+
WHERE filename = $1 AND "fileSize" = $2 AND "teamId" = $3 AND status = 'active'
|
|
131
124
|
ORDER BY "createdAt" DESC
|
|
132
125
|
LIMIT 5`,
|
|
133
|
-
[filename, fileSize],
|
|
126
|
+
[filename, fileSize, teamId],
|
|
134
127
|
userId
|
|
135
128
|
);
|
|
136
129
|
}
|
|
@@ -144,16 +137,6 @@ class MediaService {
|
|
|
144
137
|
* @param teamId - Team ID for isolation
|
|
145
138
|
* @param data - Media data
|
|
146
139
|
* @returns Created media record
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* const media = await MediaService.create('user-123', 'team-456', {
|
|
150
|
-
* url: 'https://example.com/image.jpg',
|
|
151
|
-
* filename: 'image.jpg',
|
|
152
|
-
* fileSize: 150000,
|
|
153
|
-
* mimeType: 'image/jpeg',
|
|
154
|
-
* width: 1920,
|
|
155
|
-
* height: 1080
|
|
156
|
-
* })
|
|
157
140
|
*/
|
|
158
141
|
static async create(userId, teamId, data) {
|
|
159
142
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
@@ -183,22 +166,18 @@ class MediaService {
|
|
|
183
166
|
return result.rows[0];
|
|
184
167
|
}
|
|
185
168
|
/**
|
|
186
|
-
* Update media metadata (
|
|
169
|
+
* Update media metadata (team-scoped)
|
|
187
170
|
*
|
|
188
171
|
* @param id - Media ID
|
|
189
172
|
* @param userId - User ID for RLS context
|
|
190
|
-
* @param data - Update data (alt, caption)
|
|
191
|
-
* @
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* const media = await MediaService.update('media-123', 'user-456', {
|
|
195
|
-
* alt: 'Company logo',
|
|
196
|
-
* caption: 'Our brand logo in high resolution'
|
|
197
|
-
* })
|
|
173
|
+
* @param data - Update data (title, alt, caption)
|
|
174
|
+
* @param teamId - Team ID for isolation
|
|
175
|
+
* @returns Updated media record or null if not found
|
|
198
176
|
*/
|
|
199
|
-
static async update(id, userId, data) {
|
|
177
|
+
static async update(id, userId, data, teamId) {
|
|
200
178
|
if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
|
|
201
179
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
180
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
202
181
|
const setClauses = [];
|
|
203
182
|
const params = [];
|
|
204
183
|
let paramIndex = 1;
|
|
@@ -217,55 +196,57 @@ class MediaService {
|
|
|
217
196
|
if (setClauses.length === 0) throw new Error("No fields to update");
|
|
218
197
|
setClauses.push(`"updatedAt" = NOW()`);
|
|
219
198
|
params.push(id);
|
|
199
|
+
paramIndex++;
|
|
200
|
+
params.push(teamId);
|
|
220
201
|
const result = await mutateWithRLS(
|
|
221
202
|
`UPDATE "media"
|
|
222
203
|
SET ${setClauses.join(", ")}
|
|
223
|
-
WHERE id = $${paramIndex} AND status = 'active'
|
|
204
|
+
WHERE id = $${paramIndex - 1} AND status = 'active' AND "teamId" = $${paramIndex}
|
|
224
205
|
RETURNING *`,
|
|
225
206
|
params,
|
|
226
207
|
userId
|
|
227
208
|
);
|
|
228
|
-
|
|
229
|
-
return result.rows[0];
|
|
209
|
+
return result.rows[0] || null;
|
|
230
210
|
}
|
|
231
211
|
/**
|
|
232
|
-
* Soft delete a media record (
|
|
212
|
+
* Soft delete a media record (team-scoped)
|
|
233
213
|
*
|
|
234
214
|
* @param id - Media ID
|
|
235
215
|
* @param userId - User ID for RLS context
|
|
216
|
+
* @param teamId - Team ID for isolation
|
|
236
217
|
* @returns True if deleted successfully
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* const deleted = await MediaService.softDelete('media-123', 'user-456')
|
|
240
218
|
*/
|
|
241
|
-
static async softDelete(id, userId) {
|
|
219
|
+
static async softDelete(id, userId, teamId) {
|
|
242
220
|
if (!(id == null ? void 0 : id.trim())) throw new Error("Media ID is required");
|
|
243
221
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
222
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
244
223
|
const result = await mutateWithRLS(
|
|
245
224
|
`UPDATE "media" SET status = 'deleted', "updatedAt" = NOW()
|
|
246
|
-
WHERE id = $1 AND status = 'active'`,
|
|
247
|
-
[id],
|
|
225
|
+
WHERE id = $1 AND "teamId" = $2 AND status = 'active'`,
|
|
226
|
+
[id, teamId],
|
|
248
227
|
userId
|
|
249
228
|
);
|
|
250
229
|
return result.rowCount > 0;
|
|
251
230
|
}
|
|
252
231
|
// ============================================
|
|
253
|
-
// TAG OPERATIONS
|
|
232
|
+
// TAG OPERATIONS (team-scoped)
|
|
254
233
|
// ============================================
|
|
255
234
|
/**
|
|
256
|
-
* Get all media tags
|
|
235
|
+
* Get all media tags for a team
|
|
257
236
|
*
|
|
258
237
|
* @param userId - User ID for RLS context
|
|
259
|
-
* @
|
|
238
|
+
* @param teamId - Team ID for isolation
|
|
239
|
+
* @returns Array of media tags scoped to the team
|
|
260
240
|
*/
|
|
261
|
-
static async getTags(userId) {
|
|
241
|
+
static async getTags(userId, teamId) {
|
|
262
242
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
243
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
263
244
|
return queryWithRLS(
|
|
264
245
|
`SELECT id, type, slug, name, description, icon, color, "order", "isActive", "createdAt", "updatedAt"
|
|
265
246
|
FROM "taxonomies"
|
|
266
|
-
WHERE type = 'media_tag' AND "isActive" = true AND "deletedAt" IS NULL
|
|
247
|
+
WHERE type = 'media_tag' AND "teamId" = $1 AND "isActive" = true AND "deletedAt" IS NULL
|
|
267
248
|
ORDER BY "order" ASC, name ASC`,
|
|
268
|
-
[],
|
|
249
|
+
[teamId],
|
|
269
250
|
userId
|
|
270
251
|
);
|
|
271
252
|
}
|
|
@@ -274,19 +255,22 @@ class MediaService {
|
|
|
274
255
|
*
|
|
275
256
|
* @param mediaId - Media item ID
|
|
276
257
|
* @param userId - User ID for RLS context
|
|
258
|
+
* @param teamId - Team ID for isolation (media ownership verified before calling)
|
|
277
259
|
* @returns Array of tags assigned to the media
|
|
278
260
|
*/
|
|
279
|
-
static async getMediaTags(mediaId, userId) {
|
|
261
|
+
static async getMediaTags(mediaId, userId, teamId) {
|
|
280
262
|
if (!(mediaId == null ? void 0 : mediaId.trim())) throw new Error("Media ID is required");
|
|
281
263
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
264
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
282
265
|
return queryWithRLS(
|
|
283
266
|
`SELECT t.id, t.type, t.slug, t.name, t.description, t.icon, t.color, t."order", t."isActive", t."createdAt", t."updatedAt"
|
|
284
267
|
FROM "taxonomies" t
|
|
285
268
|
JOIN "entity_taxonomy_relations" etr ON etr."taxonomyId" = t.id
|
|
286
269
|
WHERE etr."entityType" = 'media' AND etr."entityId" = $1
|
|
270
|
+
AND t."teamId" = $2
|
|
287
271
|
AND t."isActive" = true AND t."deletedAt" IS NULL
|
|
288
272
|
ORDER BY t."order" ASC, t.name ASC`,
|
|
289
|
-
[mediaId],
|
|
273
|
+
[mediaId, teamId],
|
|
290
274
|
userId
|
|
291
275
|
);
|
|
292
276
|
}
|
|
@@ -359,42 +343,44 @@ class MediaService {
|
|
|
359
343
|
}
|
|
360
344
|
}
|
|
361
345
|
/**
|
|
362
|
-
* Create a new media tag (
|
|
346
|
+
* Create a new media tag (team-scoped)
|
|
363
347
|
*
|
|
364
348
|
* @param name - Tag display name
|
|
365
349
|
* @param userId - User ID for RLS context
|
|
350
|
+
* @param teamId - Team ID for isolation
|
|
366
351
|
* @returns The created tag
|
|
367
352
|
*/
|
|
368
|
-
static async createTag(name, userId) {
|
|
353
|
+
static async createTag(name, userId, teamId) {
|
|
369
354
|
if (!(name == null ? void 0 : name.trim())) throw new Error("Tag name is required");
|
|
370
355
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
356
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
371
357
|
const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
372
358
|
const result = await mutateWithRLS(
|
|
373
|
-
`INSERT INTO "taxonomies" (type, slug, name, "isActive")
|
|
374
|
-
VALUES ('media_tag', $1, $2, true)
|
|
375
|
-
ON CONFLICT (type, slug)
|
|
359
|
+
`INSERT INTO "taxonomies" (type, slug, name, "teamId", "isActive")
|
|
360
|
+
VALUES ('media_tag', $1, $2, $3, true)
|
|
361
|
+
ON CONFLICT (type, slug, "teamId") WHERE "teamId" IS NOT NULL
|
|
362
|
+
DO UPDATE SET name = EXCLUDED.name
|
|
376
363
|
RETURNING id, slug, name, color, icon, "order", "isActive"`,
|
|
377
|
-
[slug, name.trim()],
|
|
364
|
+
[slug, name.trim(), teamId],
|
|
378
365
|
userId
|
|
379
366
|
);
|
|
380
367
|
return result.rows[0];
|
|
381
368
|
}
|
|
382
369
|
/**
|
|
383
|
-
* Count media items
|
|
370
|
+
* Count media items (team-scoped)
|
|
384
371
|
*
|
|
385
372
|
* @param userId - User ID for RLS context
|
|
373
|
+
* @param teamId - Team ID for isolation
|
|
386
374
|
* @param options - Count options (type filter, status filter)
|
|
387
375
|
* @returns Total count
|
|
388
|
-
*
|
|
389
|
-
* @example
|
|
390
|
-
* const imageCount = await MediaService.count('user-123', { type: 'image' })
|
|
391
376
|
*/
|
|
392
|
-
static async count(userId, options = {}) {
|
|
377
|
+
static async count(userId, teamId, options = {}) {
|
|
393
378
|
var _a;
|
|
394
379
|
if (!(userId == null ? void 0 : userId.trim())) throw new Error("User ID is required");
|
|
380
|
+
if (!(teamId == null ? void 0 : teamId.trim())) throw new Error("Team ID is required");
|
|
395
381
|
const { type = "all", status = "active" } = options;
|
|
396
|
-
const conditions = ["status = $1"];
|
|
397
|
-
const params = [status];
|
|
382
|
+
const conditions = ["status = $1", '"teamId" = $2'];
|
|
383
|
+
const params = [status, teamId];
|
|
398
384
|
if (type === "image") conditions.push(`"mimeType" LIKE 'image/%'`);
|
|
399
385
|
else if (type === "video") conditions.push(`"mimeType" LIKE 'video/%'`);
|
|
400
386
|
const result = await queryWithRLS(
|
|
@@ -7,12 +7,7 @@
|
|
|
7
7
|
* @module core/lib/teams/schema
|
|
8
8
|
*/
|
|
9
9
|
import { z } from 'zod';
|
|
10
|
-
export declare const teamRoleSchema: z.
|
|
11
|
-
member: "member";
|
|
12
|
-
owner: "owner";
|
|
13
|
-
admin: "admin";
|
|
14
|
-
viewer: "viewer";
|
|
15
|
-
}>;
|
|
10
|
+
export declare const teamRoleSchema: z.ZodString;
|
|
16
11
|
export declare const invitationStatusSchema: z.ZodEnum<{
|
|
17
12
|
expired: "expired";
|
|
18
13
|
pending: "pending";
|
|
@@ -34,12 +29,7 @@ export declare const teamMemberSchema: z.ZodObject<{
|
|
|
34
29
|
id: z.ZodString;
|
|
35
30
|
teamId: z.ZodString;
|
|
36
31
|
userId: z.ZodString;
|
|
37
|
-
role: z.
|
|
38
|
-
member: "member";
|
|
39
|
-
owner: "owner";
|
|
40
|
-
admin: "admin";
|
|
41
|
-
viewer: "viewer";
|
|
42
|
-
}>;
|
|
32
|
+
role: z.ZodString;
|
|
43
33
|
invitedBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
44
34
|
joinedAt: z.ZodString;
|
|
45
35
|
createdAt: z.ZodString;
|
|
@@ -49,12 +39,7 @@ export declare const teamInvitationSchema: z.ZodObject<{
|
|
|
49
39
|
id: z.ZodString;
|
|
50
40
|
teamId: z.ZodString;
|
|
51
41
|
email: z.ZodString;
|
|
52
|
-
role: z.
|
|
53
|
-
member: "member";
|
|
54
|
-
owner: "owner";
|
|
55
|
-
admin: "admin";
|
|
56
|
-
viewer: "viewer";
|
|
57
|
-
}>;
|
|
42
|
+
role: z.ZodString;
|
|
58
43
|
status: z.ZodEnum<{
|
|
59
44
|
expired: "expired";
|
|
60
45
|
pending: "pending";
|
|
@@ -116,18 +101,10 @@ export declare const updateTeamSchema: z.ZodObject<{
|
|
|
116
101
|
}, z.core.$strip>;
|
|
117
102
|
export declare const inviteMemberSchema: z.ZodObject<{
|
|
118
103
|
email: z.ZodString;
|
|
119
|
-
role: z.
|
|
120
|
-
member: "member";
|
|
121
|
-
admin: "admin";
|
|
122
|
-
viewer: "viewer";
|
|
123
|
-
}>;
|
|
104
|
+
role: z.ZodString;
|
|
124
105
|
}, z.core.$strip>;
|
|
125
106
|
export declare const updateMemberRoleSchema: z.ZodObject<{
|
|
126
|
-
role: z.
|
|
127
|
-
member: "member";
|
|
128
|
-
admin: "admin";
|
|
129
|
-
viewer: "viewer";
|
|
130
|
-
}>;
|
|
107
|
+
role: z.ZodString;
|
|
131
108
|
}, z.core.$strip>;
|
|
132
109
|
export declare const paginationSchema: z.ZodObject<{
|
|
133
110
|
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -154,12 +131,7 @@ export declare const teamListQuerySchema: z.ZodObject<{
|
|
|
154
131
|
export declare const memberListQuerySchema: z.ZodObject<{
|
|
155
132
|
page: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
156
133
|
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
157
|
-
role: z.ZodOptional<z.
|
|
158
|
-
member: "member";
|
|
159
|
-
owner: "owner";
|
|
160
|
-
admin: "admin";
|
|
161
|
-
viewer: "viewer";
|
|
162
|
-
}>>;
|
|
134
|
+
role: z.ZodOptional<z.ZodString>;
|
|
163
135
|
search: z.ZodOptional<z.ZodString>;
|
|
164
136
|
}, z.core.$strip>;
|
|
165
137
|
export declare const invitationListQuerySchema: z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/teams/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/lib/teams/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,cAAc,aAG1B,CAAA;AACD,eAAO,MAAM,sBAAsB;;;;;EAAyD,CAAA;AAG5F,eAAO,MAAM,UAAU;;;;;;;;;;iBAerB,CAAA;AAGF,eAAO,MAAM,gBAAgB;;;;;;;;;iBAS3B,CAAA;AAGF,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;iBAa/B,CAAA;AAGF,eAAO,MAAM,gBAAgB;;;;iBAI3B,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;iBAOhC,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;;iBAIhC,CAAA;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;;;;iBAAqD,CAAA;AAElF,eAAO,MAAM,kBAAkB;;;iBAM7B,CAAA;AAEF,eAAO,MAAM,sBAAsB;;iBAKjC,CAAA;AAGF,eAAO,MAAM,gBAAgB;;;iBAG3B,CAAA;AAGF,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;iBAK9B,CAAA;AAEF,eAAO,MAAM,qBAAqB;;;;;iBAGhC,CAAA;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;iBAEpC,CAAA;AAGF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAA;AACnD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACvE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAC/D,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACzE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACzE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAA;AACnE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAC3E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA;AACrE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACzE,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA"}
|
package/dist/lib/teams/schema.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
2
|
+
import { AVAILABLE_ROLES } from "@nextsparkjs/registries/permissions-registry";
|
|
3
|
+
import { getInvitableRoles } from "./permissions.js";
|
|
4
|
+
const teamRoleSchema = z.string().refine(
|
|
5
|
+
(role) => AVAILABLE_ROLES.includes(role),
|
|
6
|
+
(role) => ({ message: `Invalid team role: ${role}. Must be one of: ${[...AVAILABLE_ROLES].join(", ")}` })
|
|
7
|
+
);
|
|
3
8
|
const invitationStatusSchema = z.enum(["pending", "accepted", "declined", "expired"]);
|
|
4
9
|
const teamSchema = z.object({
|
|
5
10
|
id: z.string().uuid(),
|
|
@@ -58,15 +63,17 @@ const adminUpdateTeamSchema = z.object({
|
|
|
58
63
|
const updateTeamSchema = ownerUpdateTeamSchema.merge(adminUpdateTeamSchema);
|
|
59
64
|
const inviteMemberSchema = z.object({
|
|
60
65
|
email: z.string().email("Invalid email address"),
|
|
61
|
-
role: z.
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
role: z.string().refine(
|
|
67
|
+
(role) => getInvitableRoles().includes(role),
|
|
68
|
+
(role) => ({ message: `Role must be one of: ${getInvitableRoles().join(", ")}` })
|
|
69
|
+
)
|
|
64
70
|
// Cannot invite as owner
|
|
65
71
|
});
|
|
66
72
|
const updateMemberRoleSchema = z.object({
|
|
67
|
-
role: z.
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
role: z.string().refine(
|
|
74
|
+
(role) => getInvitableRoles().includes(role),
|
|
75
|
+
(role) => ({ message: `Role must be one of: ${getInvitableRoles().join(", ")}` })
|
|
76
|
+
)
|
|
70
77
|
// Cannot promote to owner
|
|
71
78
|
});
|
|
72
79
|
const paginationSchema = z.object({
|