@sequent-org/moodboard 1.0.17 → 1.0.18

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequent-org/moodboard",
3
- "version": "1.0.17",
3
+ "version": "1.0.18",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
package/src/core/index.js CHANGED
@@ -52,8 +52,14 @@ export class CoreMoodBoard {
52
52
  this.saveManager = new SaveManager(this.eventBus, this.options);
53
53
  this.history = new HistoryManager(this.eventBus);
54
54
  this.apiClient = new ApiClient();
55
- this.imageUploadService = new ImageUploadService(this.apiClient);
56
- this.fileUploadService = new FileUploadService(this.apiClient);
55
+ this.imageUploadService = new ImageUploadService(this.apiClient, {
56
+ requireCsrf: this.options.requireCsrf !== false, // По умолчанию требуем CSRF
57
+ csrfToken: this.options.csrfToken
58
+ });
59
+ this.fileUploadService = new FileUploadService(this.apiClient, {
60
+ requireCsrf: this.options.requireCsrf !== false, // По умолчанию требуем CSRF
61
+ csrfToken: this.options.csrfToken
62
+ });
57
63
 
58
64
  // Связываем SaveManager с ApiClient для правильной обработки изображений
59
65
  this.saveManager.setApiClient(this.apiClient);
@@ -2,17 +2,54 @@
2
2
  * Сервис для загрузки и управления файлами на сервере
3
3
  */
4
4
  export class FileUploadService {
5
- constructor(apiClient) {
5
+ constructor(apiClient, options = {}) {
6
6
  this.apiClient = apiClient;
7
7
  this.uploadEndpoint = '/api/files/upload';
8
8
  this.deleteEndpoint = '/api/files';
9
+ this.options = {
10
+ csrfToken: null, // Можно передать токен напрямую
11
+ csrfTokenSelector: 'meta[name="csrf-token"]', // Селектор для поиска токена в DOM
12
+ requireCsrf: true, // Требовать ли CSRF токен
13
+ ...options
14
+ };
15
+ }
16
+
17
+ /**
18
+ * Получает CSRF токен из различных источников
19
+ * @private
20
+ */
21
+ _getCsrfToken() {
22
+ // 1. Сначала проверяем токен, переданный в опциях
23
+ if (this.options.csrfToken) {
24
+ return this.options.csrfToken;
25
+ }
26
+
27
+ // 2. Ищем токен в DOM
28
+ if (typeof document !== 'undefined') {
29
+ const tokenElement = document.querySelector(this.options.csrfTokenSelector);
30
+ if (tokenElement) {
31
+ return tokenElement.getAttribute('content');
32
+ }
33
+ }
34
+
35
+ // 3. Проверяем глобальную переменную (для тестирования)
36
+ if (typeof window !== 'undefined' && window.csrfToken) {
37
+ return window.csrfToken;
38
+ }
39
+
40
+ // 4. Если CSRF не требуется, возвращаем null
41
+ if (!this.options.requireCsrf) {
42
+ return null;
43
+ }
44
+
45
+ return null;
9
46
  }
10
47
 
11
48
  /**
12
49
  * Загружает файл на сервер
13
- * @param {File|Blob} file - файл
50
+ * @param {File|Blob} file - файл для загрузки
14
51
  * @param {string} name - имя файла
15
- * @returns {Promise<{id: string, url: string, size: number, mimeType: string, formattedSize: string}>}
52
+ * @returns {Promise<{id: string, url: string, size: number, name: string}>}
16
53
  */
17
54
  async uploadFile(file, name = null) {
18
55
  try {
@@ -22,18 +59,24 @@ export class FileUploadService {
22
59
  formData.append('name', name || file.name || 'file');
23
60
 
24
61
  // Получаем CSRF токен
25
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
62
+ const csrfToken = this._getCsrfToken();
26
63
 
27
- if (!csrfToken) {
28
- throw new Error('CSRF токен не найден');
64
+ if (this.options.requireCsrf && !csrfToken) {
65
+ throw new Error('CSRF токен не найден. Добавьте <meta name="csrf-token" content="{{ csrf_token() }}"> в HTML или передайте токен в опциях.');
66
+ }
67
+
68
+ const headers = {
69
+ 'X-Requested-With': 'XMLHttpRequest'
70
+ };
71
+
72
+ // Добавляем CSRF токен только если он есть
73
+ if (csrfToken) {
74
+ headers['X-CSRF-TOKEN'] = csrfToken;
29
75
  }
30
76
 
31
77
  const response = await fetch(this.uploadEndpoint, {
32
78
  method: 'POST',
33
- headers: {
34
- 'X-CSRF-TOKEN': csrfToken,
35
- 'X-Requested-With': 'XMLHttpRequest'
36
- },
79
+ headers,
37
80
  credentials: 'same-origin',
38
81
  body: formData
39
82
  });
@@ -54,9 +97,8 @@ export class FileUploadService {
54
97
  fileId: result.data.fileId || result.data.id, // Добавляем fileId для явного доступа
55
98
  url: result.data.url,
56
99
  size: result.data.size,
57
- mimeType: result.data.mime_type,
58
- formattedSize: result.data.formatted_size,
59
- name: result.data.name
100
+ name: result.data.name,
101
+ type: result.data.type
60
102
  };
61
103
 
62
104
  } catch (error) {
@@ -73,20 +115,26 @@ export class FileUploadService {
73
115
  */
74
116
  async updateFileMetadata(fileId, metadata) {
75
117
  try {
76
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
118
+ const csrfToken = this._getCsrfToken();
77
119
 
78
- if (!csrfToken) {
79
- throw new Error('CSRF токен не найден');
120
+ if (this.options.requireCsrf && !csrfToken) {
121
+ throw new Error('CSRF токен не найден. Добавьте <meta name="csrf-token" content="{{ csrf_token() }}"> в HTML или передайте токен в опциях.');
122
+ }
123
+
124
+ const headers = {
125
+ 'Content-Type': 'application/json',
126
+ 'X-Requested-With': 'XMLHttpRequest',
127
+ 'Accept': 'application/json'
128
+ };
129
+
130
+ // Добавляем CSRF токен только если он есть
131
+ if (csrfToken) {
132
+ headers['X-CSRF-TOKEN'] = csrfToken;
80
133
  }
81
134
 
82
135
  const response = await fetch(`${this.deleteEndpoint}/${fileId}`, {
83
136
  method: 'PUT',
84
- headers: {
85
- 'Content-Type': 'application/json',
86
- 'X-CSRF-TOKEN': csrfToken,
87
- 'X-Requested-With': 'XMLHttpRequest',
88
- 'Accept': 'application/json'
89
- },
137
+ headers,
90
138
  credentials: 'same-origin',
91
139
  body: JSON.stringify(metadata)
92
140
  });
@@ -116,15 +164,21 @@ export class FileUploadService {
116
164
  */
117
165
  async deleteFile(fileId) {
118
166
  try {
119
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
167
+ const csrfToken = this._getCsrfToken();
120
168
 
169
+ const headers = {
170
+ 'X-Requested-With': 'XMLHttpRequest',
171
+ 'Accept': 'application/json'
172
+ };
173
+
174
+ // Добавляем CSRF токен только если он есть
175
+ if (csrfToken) {
176
+ headers['X-CSRF-TOKEN'] = csrfToken;
177
+ }
178
+
121
179
  const response = await fetch(`${this.deleteEndpoint}/${fileId}`, {
122
180
  method: 'DELETE',
123
- headers: {
124
- 'X-CSRF-TOKEN': csrfToken,
125
- 'X-Requested-With': 'XMLHttpRequest',
126
- 'Accept': 'application/json'
127
- },
181
+ headers,
128
182
  credentials: 'same-origin'
129
183
  });
130
184
 
@@ -318,15 +372,21 @@ export class FileUploadService {
318
372
  */
319
373
  async cleanupUnusedFiles() {
320
374
  try {
321
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
375
+ const csrfToken = this._getCsrfToken();
322
376
 
377
+ const headers = {
378
+ 'X-Requested-With': 'XMLHttpRequest',
379
+ 'Accept': 'application/json'
380
+ };
381
+
382
+ // Добавляем CSRF токен только если он есть
383
+ if (csrfToken) {
384
+ headers['X-CSRF-TOKEN'] = csrfToken;
385
+ }
386
+
323
387
  const response = await fetch(`${this.deleteEndpoint}/cleanup`, {
324
388
  method: 'POST',
325
- headers: {
326
- 'X-CSRF-TOKEN': csrfToken,
327
- 'X-Requested-With': 'XMLHttpRequest',
328
- 'Accept': 'application/json'
329
- },
389
+ headers,
330
390
  credentials: 'same-origin'
331
391
  });
332
392
 
@@ -2,10 +2,47 @@
2
2
  * Сервис для загрузки и управления изображениями на сервере
3
3
  */
4
4
  export class ImageUploadService {
5
- constructor(apiClient) {
5
+ constructor(apiClient, options = {}) {
6
6
  this.apiClient = apiClient;
7
7
  this.uploadEndpoint = '/api/images/upload';
8
8
  this.deleteEndpoint = '/api/images';
9
+ this.options = {
10
+ csrfToken: null, // Можно передать токен напрямую
11
+ csrfTokenSelector: 'meta[name="csrf-token"]', // Селектор для поиска токена в DOM
12
+ requireCsrf: true, // Требовать ли CSRF токен
13
+ ...options
14
+ };
15
+ }
16
+
17
+ /**
18
+ * Получает CSRF токен из различных источников
19
+ * @private
20
+ */
21
+ _getCsrfToken() {
22
+ // 1. Сначала проверяем токен, переданный в опциях
23
+ if (this.options.csrfToken) {
24
+ return this.options.csrfToken;
25
+ }
26
+
27
+ // 2. Ищем токен в DOM
28
+ if (typeof document !== 'undefined') {
29
+ const tokenElement = document.querySelector(this.options.csrfTokenSelector);
30
+ if (tokenElement) {
31
+ return tokenElement.getAttribute('content');
32
+ }
33
+ }
34
+
35
+ // 3. Проверяем глобальную переменную (для тестирования)
36
+ if (typeof window !== 'undefined' && window.csrfToken) {
37
+ return window.csrfToken;
38
+ }
39
+
40
+ // 4. Если CSRF не требуется, возвращаем null
41
+ if (!this.options.requireCsrf) {
42
+ return null;
43
+ }
44
+
45
+ return null;
9
46
  }
10
47
 
11
48
  /**
@@ -27,18 +64,24 @@ export class ImageUploadService {
27
64
  formData.append('height', dimensions.height.toString());
28
65
 
29
66
  // Получаем CSRF токен
30
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
67
+ const csrfToken = this._getCsrfToken();
31
68
 
32
- if (!csrfToken) {
33
- throw new Error('CSRF токен не найден');
69
+ if (this.options.requireCsrf && !csrfToken) {
70
+ throw new Error('CSRF токен не найден. Добавьте <meta name="csrf-token" content="{{ csrf_token() }}"> в HTML или передайте токен в опциях.');
71
+ }
72
+
73
+ const headers = {
74
+ 'X-Requested-With': 'XMLHttpRequest'
75
+ };
76
+
77
+ // Добавляем CSRF токен только если он есть
78
+ if (csrfToken) {
79
+ headers['X-CSRF-TOKEN'] = csrfToken;
34
80
  }
35
81
 
36
82
  const response = await fetch(this.uploadEndpoint, {
37
83
  method: 'POST',
38
- headers: {
39
- 'X-CSRF-TOKEN': csrfToken,
40
- 'X-Requested-With': 'XMLHttpRequest'
41
- },
84
+ headers,
42
85
  credentials: 'same-origin',
43
86
  body: formData
44
87
  });
@@ -87,15 +130,21 @@ export class ImageUploadService {
87
130
  */
88
131
  async deleteImage(imageId) {
89
132
  try {
90
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
133
+ const csrfToken = this._getCsrfToken();
91
134
 
135
+ const headers = {
136
+ 'X-Requested-With': 'XMLHttpRequest',
137
+ 'Accept': 'application/json'
138
+ };
139
+
140
+ // Добавляем CSRF токен только если он есть
141
+ if (csrfToken) {
142
+ headers['X-CSRF-TOKEN'] = csrfToken;
143
+ }
144
+
92
145
  const response = await fetch(`${this.deleteEndpoint}/${imageId}`, {
93
146
  method: 'DELETE',
94
- headers: {
95
- 'X-CSRF-TOKEN': csrfToken,
96
- 'X-Requested-With': 'XMLHttpRequest',
97
- 'Accept': 'application/json'
98
- },
147
+ headers,
99
148
  credentials: 'same-origin'
100
149
  });
101
150
 
@@ -119,15 +168,21 @@ export class ImageUploadService {
119
168
  */
120
169
  async cleanupUnusedImages() {
121
170
  try {
122
- const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
171
+ const csrfToken = this._getCsrfToken();
123
172
 
173
+ const headers = {
174
+ 'X-Requested-With': 'XMLHttpRequest',
175
+ 'Accept': 'application/json'
176
+ };
177
+
178
+ // Добавляем CSRF токен только если он есть
179
+ if (csrfToken) {
180
+ headers['X-CSRF-TOKEN'] = csrfToken;
181
+ }
182
+
124
183
  const response = await fetch(`${this.deleteEndpoint}/cleanup`, {
125
184
  method: 'POST',
126
- headers: {
127
- 'X-CSRF-TOKEN': csrfToken,
128
- 'X-Requested-With': 'XMLHttpRequest',
129
- 'Accept': 'application/json'
130
- },
185
+ headers,
131
186
  credentials: 'same-origin'
132
187
  });
133
188