@aphexcms/cms-core 0.1.17 → 0.2.1
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/api/client.d.ts.map +1 -1
- package/dist/api/client.js +7 -1
- package/dist/api/types.d.ts +2 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +59 -16
- package/dist/cli/index.js +1 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +0 -1
- package/dist/components/AdminApp.svelte +278 -45
- package/dist/components/AdminApp.svelte.d.ts +2 -0
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +60 -13
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +15 -4
- package/dist/components/admin/ObjectModal.svelte.d.ts +1 -0
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/SchemaField.svelte +64 -5
- package/dist/components/admin/SchemaField.svelte.d.ts +1 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +72 -17
- package/dist/components/admin/fields/ArrayField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/DateField.svelte +145 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/DateTimeField.svelte +225 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/ImageField.svelte +20 -7
- package/dist/components/admin/fields/ImageField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +1 -1
- package/dist/components/admin/fields/SlugField.svelte +1 -3
- package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/StringField.svelte +156 -12
- package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
- package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/URLField.svelte +41 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +0 -1
- package/dist/db/interfaces/document.d.ts +0 -2
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +2 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/user.d.ts +2 -0
- package/dist/db/interfaces/user.d.ts.map +1 -1
- package/dist/db/utils/reference-resolver.js +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/field-validation/date-utils.d.ts +30 -0
- package/dist/field-validation/date-utils.d.ts.map +1 -0
- package/dist/field-validation/date-utils.js +147 -0
- package/dist/field-validation/rule.d.ts +4 -0
- package/dist/field-validation/rule.d.ts.map +1 -1
- package/dist/field-validation/rule.js +170 -4
- package/dist/field-validation/utils.d.ts +7 -3
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +129 -35
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +38 -21
- package/dist/lib/field-validation/date-utils.js +147 -0
- package/dist/lib/field-validation/rule.js +170 -4
- package/dist/lib/field-validation/utils.js +129 -35
- package/dist/local-api/collection-api.d.ts +16 -4
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +51 -17
- package/dist/local-api/index.d.ts +1 -1
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/routes/assets-cdn.d.ts.map +1 -1
- package/dist/routes/assets-cdn.js +14 -7
- package/dist/routes/assets.d.ts.map +1 -1
- package/dist/routes/assets.js +6 -1
- package/dist/routes/documents-by-id.d.ts.map +1 -1
- package/dist/routes/documents-by-id.js +18 -7
- package/dist/routes/documents-publish.js +2 -2
- package/dist/routes/documents-query.d.ts +3 -1
- package/dist/routes/documents-query.d.ts.map +1 -1
- package/dist/routes/documents-query.js +6 -2
- package/dist/routes/documents.d.ts.map +1 -1
- package/dist/routes/documents.js +20 -4
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/user-preferences.d.ts +4 -0
- package/dist/routes/user-preferences.d.ts.map +1 -0
- package/dist/routes/user-preferences.js +77 -0
- package/dist/schema-utils/utils.d.ts +4 -0
- package/dist/schema-utils/utils.d.ts.map +1 -1
- package/dist/schema-utils/utils.js +23 -2
- package/dist/schema-utils/validator.d.ts +4 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +120 -0
- package/dist/types/filters.d.ts +13 -0
- package/dist/types/filters.d.ts.map +1 -1
- package/dist/types/organization.d.ts +3 -0
- package/dist/types/organization.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +67 -7
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/default-orderings.d.ts +10 -0
- package/dist/utils/default-orderings.d.ts.map +1 -0
- package/dist/utils/default-orderings.js +63 -0
- package/dist/utils/field-defaults.d.ts +8 -0
- package/dist/utils/field-defaults.d.ts.map +1 -0
- package/dist/utils/field-defaults.js +20 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/initial-value-helpers.d.ts +50 -0
- package/dist/utils/initial-value-helpers.d.ts.map +1 -0
- package/dist/utils/initial-value-helpers.js +70 -0
- package/package.json +6 -4
- package/dist/components/admin/DocumentTypesList.svelte +0 -97
- package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
- package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Rule } from './rule.js';
|
|
2
|
+
import { normalizeDateFields } from './date-utils.js';
|
|
2
3
|
/**
|
|
3
4
|
* Check if a field is required based on its validation rules
|
|
4
5
|
*/
|
|
@@ -20,39 +21,106 @@ export function isFieldRequired(field) {
|
|
|
20
21
|
* Validate a field value against its validation rules
|
|
21
22
|
*/
|
|
22
23
|
export async function validateField(field, value, context = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
console.log(`[validateField] Validating field "${field.name}"`, {
|
|
25
|
+
type: field.type,
|
|
26
|
+
value,
|
|
27
|
+
hasValidation: !!field.validation
|
|
28
|
+
});
|
|
29
|
+
const allErrors = [];
|
|
30
|
+
// Add automatic validation for date/datetime/url fields based on type
|
|
31
|
+
if (field.type === 'date') {
|
|
32
|
+
const dateField = field;
|
|
33
|
+
const dateFormat = dateField.options?.dateFormat || 'YYYY-MM-DD';
|
|
34
|
+
console.log(`[validateField] Adding automatic DATE validation for "${field.name}"`, {
|
|
35
|
+
dateFormat
|
|
36
|
+
});
|
|
37
|
+
const autoRule = new Rule().date(dateFormat);
|
|
38
|
+
const markers = await autoRule.validate(value, {
|
|
39
|
+
path: [field.name],
|
|
40
|
+
...context
|
|
41
|
+
});
|
|
42
|
+
allErrors.push(...markers.map((marker) => ({
|
|
43
|
+
level: marker.level,
|
|
44
|
+
message: marker.message
|
|
45
|
+
})));
|
|
25
46
|
}
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
else if (field.type === 'datetime') {
|
|
48
|
+
const dateTimeField = field;
|
|
49
|
+
const dateFormat = dateTimeField.options?.dateFormat || 'YYYY-MM-DD';
|
|
50
|
+
const timeFormat = dateTimeField.options?.timeFormat || 'HH:mm';
|
|
51
|
+
console.log(`[validateField] Adding automatic DATETIME validation for "${field.name}"`, {
|
|
52
|
+
dateFormat,
|
|
53
|
+
timeFormat
|
|
54
|
+
});
|
|
55
|
+
const autoRule = new Rule().datetime(dateFormat, timeFormat);
|
|
56
|
+
const markers = await autoRule.validate(value, {
|
|
57
|
+
path: [field.name],
|
|
58
|
+
...context
|
|
59
|
+
});
|
|
60
|
+
allErrors.push(...markers.map((marker) => ({
|
|
61
|
+
level: marker.level,
|
|
62
|
+
message: marker.message
|
|
63
|
+
})));
|
|
64
|
+
}
|
|
65
|
+
else if (field.type === 'url') {
|
|
66
|
+
// Only add automatic URL validation if there's no custom validation
|
|
67
|
+
// This allows custom validation to specify different options (scheme, allowRelative, relativeOnly)
|
|
68
|
+
if (!field.validation) {
|
|
69
|
+
console.log(`[validateField] Adding automatic URL validation for "${field.name}"`);
|
|
70
|
+
// Automatic URL validation - only validate if there's a value
|
|
71
|
+
if (value && value !== '') {
|
|
72
|
+
const autoRule = new Rule().uri();
|
|
73
|
+
const markers = await autoRule.validate(value, {
|
|
74
|
+
path: [field.name],
|
|
75
|
+
...context
|
|
76
|
+
});
|
|
77
|
+
allErrors.push(...markers.map((marker) => ({
|
|
78
|
+
level: marker.level,
|
|
79
|
+
message: marker.message
|
|
80
|
+
})));
|
|
36
81
|
}
|
|
37
|
-
const markers = await rule.validate(value, {
|
|
38
|
-
path: [field.name],
|
|
39
|
-
...context
|
|
40
|
-
});
|
|
41
|
-
allErrors.push(...markers.map((marker) => ({
|
|
42
|
-
level: marker.level,
|
|
43
|
-
message: marker.message
|
|
44
|
-
})));
|
|
45
82
|
}
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
else {
|
|
84
|
+
console.log(`[validateField] Skipping automatic URL validation for "${field.name}" (has custom validation)`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Run user-defined validation rules if present
|
|
88
|
+
if (!field.validation) {
|
|
89
|
+
console.log(`[validateField] No custom validation rules for "${field.name}"`);
|
|
48
90
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
91
|
+
else {
|
|
92
|
+
try {
|
|
93
|
+
const validationFunctions = Array.isArray(field.validation)
|
|
94
|
+
? field.validation
|
|
95
|
+
: [field.validation];
|
|
96
|
+
console.log(`[validateField] Field "${field.name}" has ${validationFunctions.length} custom validation function(s)`);
|
|
97
|
+
for (const validationFn of validationFunctions) {
|
|
98
|
+
const rule = validationFn(new Rule());
|
|
99
|
+
if (!(rule instanceof Rule)) {
|
|
100
|
+
console.error(`Validation function for field "${field.name}" did not return a Rule object. Make sure you are chaining validation methods and returning the result.`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const markers = await rule.validate(value, {
|
|
104
|
+
path: [field.name],
|
|
105
|
+
...context
|
|
106
|
+
});
|
|
107
|
+
allErrors.push(...markers.map((marker) => ({
|
|
108
|
+
level: marker.level,
|
|
109
|
+
message: marker.message
|
|
110
|
+
})));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
console.error(`[validateField] Validation error for "${field.name}":`, error);
|
|
115
|
+
allErrors.push({ level: 'error', message: 'Validation failed' });
|
|
116
|
+
}
|
|
55
117
|
}
|
|
118
|
+
const isValid = allErrors.filter((e) => e.level === 'error').length === 0;
|
|
119
|
+
console.log(`[validateField] Field "${field.name}" validation complete`, {
|
|
120
|
+
isValid,
|
|
121
|
+
errors: allErrors
|
|
122
|
+
});
|
|
123
|
+
return { isValid, errors: allErrors };
|
|
56
124
|
}
|
|
57
125
|
/**
|
|
58
126
|
* Get validation CSS classes for input styling
|
|
@@ -66,20 +134,41 @@ export function getValidationClasses(hasErrors) {
|
|
|
66
134
|
}
|
|
67
135
|
/**
|
|
68
136
|
* Validate an entire document's data against a schema
|
|
69
|
-
* This function
|
|
70
|
-
*
|
|
137
|
+
* This function:
|
|
138
|
+
* 1. Normalizes date fields (converts user format to ISO for storage)
|
|
139
|
+
* 2. Converts ISO dates to user format for validation
|
|
140
|
+
* 3. Validates all fields and returns errors
|
|
141
|
+
* 4. Returns normalized data (with ISO dates) for storage
|
|
71
142
|
*
|
|
72
143
|
* @param schema - The schema type containing field definitions
|
|
73
144
|
* @param data - The document data to validate
|
|
74
145
|
* @param context - Optional context to pass to field validators
|
|
75
|
-
* @returns Validation result with isValid flag and
|
|
146
|
+
* @returns Validation result with isValid flag, errors, and normalized data
|
|
76
147
|
*/
|
|
77
148
|
export async function validateDocumentData(schema, data, context = {}) {
|
|
149
|
+
console.log('[validateDocumentData] Starting validation', {
|
|
150
|
+
schemaName: schema.name,
|
|
151
|
+
data
|
|
152
|
+
});
|
|
78
153
|
const validationErrors = [];
|
|
79
|
-
//
|
|
154
|
+
// Normalize date fields: convert to ISO for storage, user format for validation
|
|
155
|
+
const { normalizedData, dataForValidation } = normalizeDateFields(data, schema);
|
|
156
|
+
console.log('[validateDocumentData] After normalization', {
|
|
157
|
+
normalizedData,
|
|
158
|
+
dataForValidation
|
|
159
|
+
});
|
|
160
|
+
// Validate each field using the user-formatted data
|
|
80
161
|
for (const field of schema.fields) {
|
|
81
|
-
const value =
|
|
82
|
-
|
|
162
|
+
const value = dataForValidation[field.name];
|
|
163
|
+
console.log(`[validateDocumentData] Validating field "${field.name}"`, {
|
|
164
|
+
type: field.type,
|
|
165
|
+
value
|
|
166
|
+
});
|
|
167
|
+
const result = await validateField(field, value, { ...context, ...dataForValidation });
|
|
168
|
+
console.log(`[validateDocumentData] Field "${field.name}" validation result`, {
|
|
169
|
+
isValid: result.isValid,
|
|
170
|
+
errors: result.errors
|
|
171
|
+
});
|
|
83
172
|
if (!result.isValid) {
|
|
84
173
|
const errorMessages = result.errors.filter((e) => e.level === 'error').map((e) => e.message);
|
|
85
174
|
if (errorMessages.length > 0) {
|
|
@@ -90,8 +179,13 @@ export async function validateDocumentData(schema, data, context = {}) {
|
|
|
90
179
|
}
|
|
91
180
|
}
|
|
92
181
|
}
|
|
93
|
-
|
|
182
|
+
console.log('[validateDocumentData] Final result', {
|
|
94
183
|
isValid: validationErrors.length === 0,
|
|
95
184
|
errors: validationErrors
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
isValid: validationErrors.length === 0,
|
|
188
|
+
errors: validationErrors,
|
|
189
|
+
normalizedData
|
|
96
190
|
};
|
|
97
191
|
}
|
|
@@ -4,6 +4,14 @@ import type { Document } from '../types/document.js';
|
|
|
4
4
|
import type { LocalAPIContext } from './types.js';
|
|
5
5
|
import type { SchemaType } from '../types/schemas.js';
|
|
6
6
|
import { PermissionChecker } from './permissions.js';
|
|
7
|
+
import { type DocumentValidationResult } from '../field-validation/utils.js';
|
|
8
|
+
/**
|
|
9
|
+
* Result from create/update operations that includes validation
|
|
10
|
+
*/
|
|
11
|
+
export interface DocumentResult<T> {
|
|
12
|
+
document: T;
|
|
13
|
+
validation: DocumentValidationResult;
|
|
14
|
+
}
|
|
7
15
|
/**
|
|
8
16
|
* Collection API - provides type-safe operations for a single collection
|
|
9
17
|
* Generic type T represents the document type for this collection
|
|
@@ -69,7 +77,7 @@ export declare class CollectionAPI<T = Document> {
|
|
|
69
77
|
*
|
|
70
78
|
* @example
|
|
71
79
|
* ```typescript
|
|
72
|
-
* const
|
|
80
|
+
* const result = await api.collections.pages.create(
|
|
73
81
|
* { organizationId: 'org_123', user },
|
|
74
82
|
* {
|
|
75
83
|
* title: 'New Page',
|
|
@@ -77,27 +85,31 @@ export declare class CollectionAPI<T = Document> {
|
|
|
77
85
|
* content: []
|
|
78
86
|
* }
|
|
79
87
|
* );
|
|
88
|
+
* // result.document - the created document
|
|
89
|
+
* // result.validation - validation results
|
|
80
90
|
* ```
|
|
81
91
|
*/
|
|
82
92
|
create(context: LocalAPIContext, data: Omit<T, 'id' | '_meta'>, options?: {
|
|
83
93
|
publish?: boolean;
|
|
84
|
-
}): Promise<T
|
|
94
|
+
}): Promise<DocumentResult<T>>;
|
|
85
95
|
/**
|
|
86
96
|
* Update an existing document
|
|
87
97
|
*
|
|
88
98
|
* @example
|
|
89
99
|
* ```typescript
|
|
90
|
-
* const
|
|
100
|
+
* const result = await api.collections.pages.update(
|
|
91
101
|
* { organizationId: 'org_123', user },
|
|
92
102
|
* 'doc_123',
|
|
93
103
|
* { title: 'Updated Title' },
|
|
94
104
|
* { publish: true }
|
|
95
105
|
* );
|
|
106
|
+
* // result.document - the updated document
|
|
107
|
+
* // result.validation - validation results
|
|
96
108
|
* ```
|
|
97
109
|
*/
|
|
98
110
|
update(context: LocalAPIContext, id: string, data: Partial<Omit<T, 'id' | '_meta'>>, options?: {
|
|
99
111
|
publish?: boolean;
|
|
100
|
-
}): Promise<T | null>;
|
|
112
|
+
}): Promise<DocumentResult<T> | null>;
|
|
101
113
|
/**
|
|
102
114
|
* Delete a document
|
|
103
115
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"collection-api.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/collection-api.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAwB,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAEhG;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,CAAC;IACZ,UAAU,EAAE,wBAAwB,CAAC;CACrC;AA4BD;;;GAGG;AACH,qBAAa,aAAa,CAAC,CAAC,GAAG,QAAQ;IAErC,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;gBAHX,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,UAAU,EACnB,WAAW,EAAE,iBAAiB;IAMvC;;OAEG;IACH,IAAI,MAAM,IAAI,UAAU,CAEvB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,WAAW,CAAC,CAAC,CAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAqB1F;;;;;;;;;;;OAWG;IACG,QAAQ,CACb,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAC/B,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAmBpB;;;;;;;;;;OAUG;IACG,KAAK,CACV,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;KAAE,GACzC,OAAO,CAAC,MAAM,CAAC;IAWlB;;;;;;;;;;;;;;;;OAgBG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IA4C7B;;;;;;;;;;;;;;OAcG;IACG,MAAM,CACX,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,EACtC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAC7B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAkEpC;;;;;;;;;;OAUG;IACG,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOpE;;;;;;;;;;OAUG;IACG,OAAO,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAgCtE;;;;;;;;;;OAUG;IACG,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;CAUxE"}
|
|
@@ -124,7 +124,7 @@ export class CollectionAPI {
|
|
|
124
124
|
*
|
|
125
125
|
* @example
|
|
126
126
|
* ```typescript
|
|
127
|
-
* const
|
|
127
|
+
* const result = await api.collections.pages.create(
|
|
128
128
|
* { organizationId: 'org_123', user },
|
|
129
129
|
* {
|
|
130
130
|
* title: 'New Page',
|
|
@@ -132,15 +132,18 @@ export class CollectionAPI {
|
|
|
132
132
|
* content: []
|
|
133
133
|
* }
|
|
134
134
|
* );
|
|
135
|
+
* // result.document - the created document
|
|
136
|
+
* // result.validation - validation results
|
|
135
137
|
* ```
|
|
136
138
|
*/
|
|
137
139
|
async create(context, data, options) {
|
|
138
140
|
// Permission check (unless overrideAccess)
|
|
139
141
|
await this.permissions.canWrite(context, this.collectionName);
|
|
140
|
-
// Validate data
|
|
142
|
+
// Validate and normalize data (dates converted to ISO)
|
|
143
|
+
const validationResult = await validateDocumentData(this._schema, data, data);
|
|
141
144
|
if (options?.publish) {
|
|
142
145
|
await this.permissions.canPublish(context, this.collectionName);
|
|
143
|
-
|
|
146
|
+
// Block publish if validation fails
|
|
144
147
|
if (!validationResult.isValid) {
|
|
145
148
|
const errorMessage = validationResult.errors
|
|
146
149
|
.map((e) => `${e.field}: ${e.errors.join(', ')}`)
|
|
@@ -148,48 +151,73 @@ export class CollectionAPI {
|
|
|
148
151
|
throw new Error(`Cannot publish: validation errors - ${errorMessage}`);
|
|
149
152
|
}
|
|
150
153
|
}
|
|
151
|
-
// Create document with
|
|
154
|
+
// Create document with normalized data (dates in ISO format)
|
|
152
155
|
const document = await this.databaseAdapter.createDocument({
|
|
153
156
|
organizationId: context.organizationId,
|
|
154
157
|
type: this.collectionName,
|
|
155
|
-
draftData:
|
|
158
|
+
draftData: validationResult.normalizedData,
|
|
156
159
|
createdBy: context.user?.id
|
|
157
160
|
});
|
|
158
161
|
// Publish immediately if requested (validation already done above)
|
|
159
162
|
if (options?.publish) {
|
|
160
163
|
const published = await this.databaseAdapter.publishDoc(context.organizationId, document.id);
|
|
161
164
|
if (published) {
|
|
162
|
-
return
|
|
165
|
+
return {
|
|
166
|
+
document: transformDocument(published, 'published'),
|
|
167
|
+
validation: validationResult
|
|
168
|
+
};
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
|
-
return
|
|
171
|
+
return {
|
|
172
|
+
document: transformDocument(document, 'draft'),
|
|
173
|
+
validation: validationResult
|
|
174
|
+
};
|
|
166
175
|
}
|
|
167
176
|
/**
|
|
168
177
|
* Update an existing document
|
|
169
178
|
*
|
|
170
179
|
* @example
|
|
171
180
|
* ```typescript
|
|
172
|
-
* const
|
|
181
|
+
* const result = await api.collections.pages.update(
|
|
173
182
|
* { organizationId: 'org_123', user },
|
|
174
183
|
* 'doc_123',
|
|
175
184
|
* { title: 'Updated Title' },
|
|
176
185
|
* { publish: true }
|
|
177
186
|
* );
|
|
187
|
+
* // result.document - the updated document
|
|
188
|
+
* // result.validation - validation results
|
|
178
189
|
* ```
|
|
179
190
|
*/
|
|
180
191
|
async update(context, id, data, options) {
|
|
181
192
|
// Permission check (unless overrideAccess)
|
|
182
193
|
await this.permissions.canWrite(context, this.collectionName);
|
|
183
|
-
//
|
|
184
|
-
const
|
|
194
|
+
// Get existing document to merge with updates
|
|
195
|
+
const existingDoc = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
196
|
+
if (!existingDoc) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
// Merge existing data with updates, but only keep fields defined in schema
|
|
200
|
+
// This prevents orphaned fields from persisting when schema changes
|
|
201
|
+
const schemaFieldNames = new Set(this._schema.fields.map((f) => f.name));
|
|
202
|
+
const cleanedExisting = {};
|
|
203
|
+
// Only copy fields from existing doc that are still in the schema
|
|
204
|
+
for (const [key, value] of Object.entries(existingDoc.draftData || {})) {
|
|
205
|
+
if (schemaFieldNames.has(key)) {
|
|
206
|
+
cleanedExisting[key] = value;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Merge cleaned existing data with new updates
|
|
210
|
+
const mergedData = { ...cleanedExisting, ...data };
|
|
211
|
+
// Validate and normalize the merged data
|
|
212
|
+
const validationResult = await validateDocumentData(this._schema, mergedData, mergedData);
|
|
213
|
+
// Update draft with normalized data (dates in ISO format)
|
|
214
|
+
const document = await this.databaseAdapter.updateDocDraft(context.organizationId, id, validationResult.normalizedData, context.user?.id);
|
|
185
215
|
if (!document) {
|
|
186
216
|
return null;
|
|
187
217
|
}
|
|
188
|
-
// Validate and publish immediately if requested
|
|
189
218
|
if (options?.publish) {
|
|
190
219
|
await this.permissions.canPublish(context, this.collectionName);
|
|
191
|
-
//
|
|
192
|
-
const validationResult = await validateDocumentData(this._schema, document.draftData, document.draftData);
|
|
220
|
+
// Block publish if validation fails
|
|
193
221
|
if (!validationResult.isValid) {
|
|
194
222
|
const errorMessage = validationResult.errors
|
|
195
223
|
.map((e) => `${e.field}: ${e.errors.join(', ')}`)
|
|
@@ -198,10 +226,16 @@ export class CollectionAPI {
|
|
|
198
226
|
}
|
|
199
227
|
const published = await this.databaseAdapter.publishDoc(context.organizationId, document.id);
|
|
200
228
|
if (published) {
|
|
201
|
-
return
|
|
229
|
+
return {
|
|
230
|
+
document: transformDocument(published, 'published'),
|
|
231
|
+
validation: validationResult
|
|
232
|
+
};
|
|
202
233
|
}
|
|
203
234
|
}
|
|
204
|
-
return
|
|
235
|
+
return {
|
|
236
|
+
document: transformDocument(document, 'draft'),
|
|
237
|
+
validation: validationResult
|
|
238
|
+
};
|
|
205
239
|
}
|
|
206
240
|
/**
|
|
207
241
|
* Delete a document
|
|
@@ -234,11 +268,11 @@ export class CollectionAPI {
|
|
|
234
268
|
// Permission check (unless overrideAccess)
|
|
235
269
|
await this.permissions.canPublish(context, this.collectionName);
|
|
236
270
|
// Get the document to access draft data for validation
|
|
237
|
-
const document = await this.databaseAdapter.
|
|
271
|
+
const document = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
238
272
|
if (!document || !document.draftData) {
|
|
239
273
|
throw new Error('Document not found or has no draft content to publish');
|
|
240
274
|
}
|
|
241
|
-
// Validate draft data
|
|
275
|
+
// Validate draft data (dates already in ISO, will be converted for validation)
|
|
242
276
|
const validationResult = await validateDocumentData(this._schema, document.draftData, document.draftData);
|
|
243
277
|
if (!validationResult.isValid) {
|
|
244
278
|
const errorMessage = validationResult.errors
|
|
@@ -101,7 +101,7 @@ export declare function createLocalAPI(config: CMSConfig, userAdapter: DatabaseA
|
|
|
101
101
|
* ```
|
|
102
102
|
*/
|
|
103
103
|
export declare function getLocalAPI(): LocalAPI;
|
|
104
|
-
export { CollectionAPI } from './collection-api.js';
|
|
104
|
+
export { CollectionAPI, type DocumentResult } from './collection-api.js';
|
|
105
105
|
export { PermissionChecker, PermissionError } from './permissions.js';
|
|
106
106
|
export type { LocalAPIContext, CreateOptions, UpdateOptions } from './types.js';
|
|
107
107
|
export { authToContext, requireAuth, systemContext } from './auth-helpers.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAQnB,OAAO,CAAC,MAAM;IAPR,WAAW,EAAE,WAAW,CAAM;IACrC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAmBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAGzD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAQnB,OAAO,CAAC,MAAM;IAPR,WAAW,EAAE,WAAW,CAAM;IACrC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAmBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAGzD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets-cdn.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets-cdn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"assets-cdn.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets-cdn.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,GAAG,EAAE,cAqMjB,CAAC"}
|
|
@@ -97,14 +97,21 @@ export const GET = async ({ params, locals, setHeaders, request }) => {
|
|
|
97
97
|
console.warn('[Asset CDN] Private asset accessed without auth - DENIED');
|
|
98
98
|
return new Response('Unauthorized - This asset is private', { status: 401 });
|
|
99
99
|
}
|
|
100
|
-
// If asset is private, verify org
|
|
101
|
-
|
|
102
|
-
console.warn('[Asset CDN] Org mismatch for private asset - FORBIDDEN');
|
|
103
|
-
return new Response('Forbidden', { status: 403 });
|
|
104
|
-
}
|
|
105
|
-
// Log the decision
|
|
100
|
+
// If asset is private, verify user has access to the asset's org
|
|
101
|
+
// This includes exact match OR parent org accessing child org asset (hierarchy)
|
|
106
102
|
if (isPrivate && organizationId) {
|
|
107
|
-
|
|
103
|
+
let hasAccess = organizationId === asset.organizationId; // Same org
|
|
104
|
+
// If not same org, check if asset's org is a child of user's org (hierarchy)
|
|
105
|
+
if (!hasAccess && databaseAdapter.getChildOrganizations) {
|
|
106
|
+
const childOrgs = await databaseAdapter.getChildOrganizations(organizationId);
|
|
107
|
+
hasAccess = childOrgs.includes(asset.organizationId);
|
|
108
|
+
console.log(`[Asset CDN] Checking hierarchy: user org ${organizationId} has ${childOrgs.length} children, asset org ${asset.organizationId} is child? ${hasAccess}`);
|
|
109
|
+
}
|
|
110
|
+
if (!hasAccess) {
|
|
111
|
+
console.warn(`[Asset CDN] Org ${organizationId} cannot access asset from org ${asset.organizationId} - FORBIDDEN`);
|
|
112
|
+
return new Response('Forbidden', { status: 403 });
|
|
113
|
+
}
|
|
114
|
+
console.log(`[Asset CDN] Private asset access ALLOWED - user org ${organizationId} has access to asset org ${asset.organizationId}`);
|
|
108
115
|
}
|
|
109
116
|
else if (!isPrivate) {
|
|
110
117
|
console.log('[Asset CDN] Public asset access ALLOWED');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,eAAO,MAAM,IAAI,EAAE,cAuElB,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,cA4CjB,CAAC"}
|
package/dist/routes/assets.js
CHANGED
|
@@ -23,8 +23,12 @@ export const POST = async ({ request, locals }) => {
|
|
|
23
23
|
// Get field metadata for privacy checking
|
|
24
24
|
const schemaType = formData.get('schemaType') || undefined;
|
|
25
25
|
const fieldPath = formData.get('fieldPath') || undefined;
|
|
26
|
+
// Get target organization (document's org takes precedence)
|
|
27
|
+
// This allows assets to belong to the document's org, not the uploader's org
|
|
28
|
+
const targetOrganizationId = formData.get('organizationId') || auth.organizationId;
|
|
26
29
|
// Create asset upload data
|
|
27
30
|
const uploadData = {
|
|
31
|
+
organizationId: targetOrganizationId, // Asset belongs to document's org
|
|
28
32
|
buffer,
|
|
29
33
|
originalFilename: file.name,
|
|
30
34
|
mimeType: file.type,
|
|
@@ -40,7 +44,8 @@ export const POST = async ({ request, locals }) => {
|
|
|
40
44
|
}
|
|
41
45
|
};
|
|
42
46
|
// Upload asset using the service
|
|
43
|
-
|
|
47
|
+
// The adapter will validate hierarchy access via withOrgContext
|
|
48
|
+
const asset = await assetService.uploadAsset(targetOrganizationId, uploadData);
|
|
44
49
|
return json({
|
|
45
50
|
success: true,
|
|
46
51
|
data: asset
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-by-id.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"documents-by-id.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-by-id.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,eAAO,MAAM,GAAG,EAAE,cAuEjB,CAAC;AAIF,eAAO,MAAM,GAAG,EAAE,cAkFjB,CAAC;AAGF,eAAO,MAAM,MAAM,EAAE,cA+DpB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { json } from '@sveltejs/kit';
|
|
|
3
3
|
import { authToContext } from '../local-api/auth-helpers.js';
|
|
4
4
|
import { PermissionError } from '../local-api/permissions.js';
|
|
5
5
|
// GET /api/documents/[id] - Get document by ID
|
|
6
|
+
// TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
|
|
6
7
|
export const GET = async ({ params, url, locals }) => {
|
|
7
8
|
try {
|
|
8
9
|
const { localAPI, databaseAdapter } = locals.aphexCMS;
|
|
@@ -16,7 +17,7 @@ export const GET = async ({ params, url, locals }) => {
|
|
|
16
17
|
const depth = depthParam ? Math.max(0, Math.min(parseInt(depthParam), 5)) : 0;
|
|
17
18
|
const perspective = url.searchParams.get('perspective') || 'draft';
|
|
18
19
|
// First, fetch document to get its type (need this for collection-specific API)
|
|
19
|
-
const rawDoc = await databaseAdapter.
|
|
20
|
+
const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
20
21
|
if (!rawDoc) {
|
|
21
22
|
return json({ success: false, error: 'Document not found' }, { status: 404 });
|
|
22
23
|
}
|
|
@@ -59,6 +60,7 @@ export const GET = async ({ params, url, locals }) => {
|
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
62
|
// PUT /api/documents/[id] - Update document
|
|
63
|
+
// TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
|
|
62
64
|
export const PUT = async ({ params, request, locals }) => {
|
|
63
65
|
try {
|
|
64
66
|
const { localAPI, databaseAdapter } = locals.aphexCMS;
|
|
@@ -71,7 +73,7 @@ export const PUT = async ({ params, request, locals }) => {
|
|
|
71
73
|
const documentData = body.draftData || body.data;
|
|
72
74
|
const shouldPublish = body.publish || false;
|
|
73
75
|
// Fetch document to get its type
|
|
74
|
-
const rawDoc = await databaseAdapter.
|
|
76
|
+
const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
75
77
|
if (!rawDoc) {
|
|
76
78
|
return json({ success: false, error: 'Document not found' }, { status: 404 });
|
|
77
79
|
}
|
|
@@ -84,16 +86,17 @@ export const PUT = async ({ params, request, locals }) => {
|
|
|
84
86
|
message: `Collection '${rawDoc.type}' not found`
|
|
85
87
|
}, { status: 400 });
|
|
86
88
|
}
|
|
87
|
-
// Update via LocalAPI (permission checks happen inside)
|
|
88
|
-
const
|
|
89
|
+
// Update via LocalAPI (permission checks and validation happen inside)
|
|
90
|
+
const result = await collection.update(context, id, documentData, {
|
|
89
91
|
publish: shouldPublish
|
|
90
92
|
});
|
|
91
|
-
if (!
|
|
93
|
+
if (!result) {
|
|
92
94
|
return json({ success: false, error: 'Document not found' }, { status: 404 });
|
|
93
95
|
}
|
|
94
96
|
return json({
|
|
95
97
|
success: true,
|
|
96
|
-
data:
|
|
98
|
+
data: result.document,
|
|
99
|
+
validation: result.validation
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
102
|
catch (error) {
|
|
@@ -105,6 +108,14 @@ export const PUT = async ({ params, request, locals }) => {
|
|
|
105
108
|
message: error.message
|
|
106
109
|
}, { status: 403 });
|
|
107
110
|
}
|
|
111
|
+
// Validation errors from publish attempts
|
|
112
|
+
if (error instanceof Error && error.message.includes('validation errors')) {
|
|
113
|
+
return json({
|
|
114
|
+
success: false,
|
|
115
|
+
error: 'Validation failed',
|
|
116
|
+
message: error.message
|
|
117
|
+
}, { status: 400 });
|
|
118
|
+
}
|
|
108
119
|
return json({
|
|
109
120
|
success: false,
|
|
110
121
|
error: 'Failed to update document',
|
|
@@ -122,7 +133,7 @@ export const DELETE = async ({ params, locals }) => {
|
|
|
122
133
|
return json({ success: false, error: 'Document ID is required' }, { status: 400 });
|
|
123
134
|
}
|
|
124
135
|
// Fetch document to get its type
|
|
125
|
-
const rawDoc = await databaseAdapter.
|
|
136
|
+
const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
126
137
|
if (!rawDoc) {
|
|
127
138
|
return json({ success: false, error: 'Document not found' }, { status: 404 });
|
|
128
139
|
}
|
|
@@ -16,7 +16,7 @@ export const POST = async ({ params, locals }) => {
|
|
|
16
16
|
}, { status: 400 });
|
|
17
17
|
}
|
|
18
18
|
// Fetch document to get its type
|
|
19
|
-
const rawDoc = await databaseAdapter.
|
|
19
|
+
const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
20
20
|
if (!rawDoc) {
|
|
21
21
|
return json({
|
|
22
22
|
success: false,
|
|
@@ -86,7 +86,7 @@ export const DELETE = async ({ params, locals }) => {
|
|
|
86
86
|
}, { status: 400 });
|
|
87
87
|
}
|
|
88
88
|
// Fetch document to get its type
|
|
89
|
-
const rawDoc = await databaseAdapter.
|
|
89
|
+
const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
|
|
90
90
|
if (!rawDoc) {
|
|
91
91
|
return json({
|
|
92
92
|
success: false,
|
|
@@ -17,7 +17,9 @@ import type { RequestHandler } from '@sveltejs/kit';
|
|
|
17
17
|
* "page": 1,
|
|
18
18
|
* "sort": ["-publishedAt", "title"],
|
|
19
19
|
* "depth": 1,
|
|
20
|
-
* "perspective": "published"
|
|
20
|
+
* "perspective": "published",
|
|
21
|
+
* "filterOrganizationIds": ["org_child1", "org_child2"],
|
|
22
|
+
* "includeChildOrganizations": false
|
|
21
23
|
* }
|
|
22
24
|
*/
|
|
23
25
|
export declare const POST: RequestHandler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"documents-query.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AASpD
|
|
1
|
+
{"version":3,"file":"documents-query.d.ts","sourceRoot":"","sources":["../../src/lib/routes/documents-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AASpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,IAAI,EAAE,cAuFlB,CAAC"}
|
|
@@ -23,7 +23,9 @@ const DEFAULT_PAGE = 1;
|
|
|
23
23
|
* "page": 1,
|
|
24
24
|
* "sort": ["-publishedAt", "title"],
|
|
25
25
|
* "depth": 1,
|
|
26
|
-
* "perspective": "published"
|
|
26
|
+
* "perspective": "published",
|
|
27
|
+
* "filterOrganizationIds": ["org_child1", "org_child2"],
|
|
28
|
+
* "includeChildOrganizations": false
|
|
27
29
|
* }
|
|
28
30
|
*/
|
|
29
31
|
export const POST = async ({ request, locals }) => {
|
|
@@ -60,7 +62,9 @@ export const POST = async ({ request, locals }) => {
|
|
|
60
62
|
sort: body.sort,
|
|
61
63
|
depth: body.depth !== undefined ? Math.max(0, Math.min(body.depth, 5)) : 0,
|
|
62
64
|
select: body.select,
|
|
63
|
-
perspective: body.perspective || 'draft'
|
|
65
|
+
perspective: body.perspective || 'draft',
|
|
66
|
+
includeChildOrganizations: body.includeChildOrganizations,
|
|
67
|
+
filterOrganizationIds: body.filterOrganizationIds
|
|
64
68
|
};
|
|
65
69
|
// Query via LocalAPI
|
|
66
70
|
const result = await localAPI.collections[documentType].find(context, findOptions);
|