webspresso 0.0.99 → 0.0.100

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.
Files changed (60) hide show
  1. package/.agents/AGENTS.md +2 -2
  2. package/.agents/AUTH.md +246 -0
  3. package/.agents/CLI.md +47 -0
  4. package/.agents/ERRORS.md +59 -0
  5. package/.agents/ORM.md +90 -0
  6. package/.agents/PLUGINS.md +44 -7
  7. package/.agents/QUEUE.md +18 -10
  8. package/.agents/ROUTING.md +33 -0
  9. package/.agents/STREAMING.md +35 -0
  10. package/AGENTS.md +1 -0
  11. package/README.md +145 -93
  12. package/bin/commands/add-docker.js +467 -0
  13. package/bin/commands/deploy-check.js +339 -0
  14. package/bin/webspresso.js +4 -0
  15. package/core/auth/index.js +63 -1
  16. package/core/auth/magic-link.js +170 -0
  17. package/core/auth/manager.js +343 -3
  18. package/core/auth/middleware.js +30 -5
  19. package/core/auth/session-manager.js +255 -0
  20. package/core/auth/sessions.js +351 -0
  21. package/core/auth/tokens.js +1 -0
  22. package/core/auth/totp.js +4 -1
  23. package/core/auth/user-agent.js +161 -0
  24. package/core/orm/index.js +2 -1
  25. package/core/orm/model.js +51 -1
  26. package/core/orm/repository.js +283 -13
  27. package/core/orm/scopes.js +3 -2
  28. package/core/orm/types.js +6 -1
  29. package/core/queue/cron.js +252 -39
  30. package/core/queue/manager.js +20 -2
  31. package/core/queue/scheduler.js +115 -59
  32. package/core/ssr/stream.js +62 -2
  33. package/index.d.ts +274 -14
  34. package/index.js +4 -0
  35. package/package.json +7 -5
  36. package/plugins/admin-panel/modules/admin-users.js +1 -1
  37. package/plugins/audit-log/admin-component.js +54 -54
  38. package/plugins/audit-log/index.js +2 -2
  39. package/plugins/content/client/inline-edit.js +2 -46
  40. package/plugins/cors.js +13 -2
  41. package/plugins/email/auth-bridge.js +66 -0
  42. package/plugins/email/bundled-templates.js +15 -0
  43. package/plugins/email/templates/auth-magic-link.mjml +13 -0
  44. package/plugins/file-manager/admin-component.js +49 -49
  45. package/plugins/file-manager/index.js +4 -4
  46. package/plugins/index.js +2 -0
  47. package/plugins/webhooks/admin-component.js +608 -0
  48. package/plugins/webhooks/api-handlers.js +237 -0
  49. package/plugins/webhooks/crypto.js +144 -0
  50. package/plugins/webhooks/dispatcher.js +708 -0
  51. package/plugins/webhooks/index.js +263 -0
  52. package/plugins/webhooks/matcher.js +113 -0
  53. package/plugins/webhooks/migration-template.js +75 -0
  54. package/plugins/webhooks/models.js +98 -0
  55. package/plugins/webhooks/sender.js +176 -0
  56. package/src/bundle/index.js +2 -1
  57. package/src/file-router.js +173 -4
  58. package/src/helpers.js +7 -1
  59. package/src/njk-frontmatter.js +3 -0
  60. package/src/routing/cascading.js +112 -0
@@ -133,7 +133,7 @@ function generateFileManagerComponent(options = {}) {
133
133
  inputFolderValue = '';
134
134
  loadDirectory();
135
135
  }).catch(function(err) {
136
- alert('Klasör oluşturulamadı: ' + (err.message || String(err)));
136
+ alert('Failed to create folder: ' + (err.message || String(err)));
137
137
  });
138
138
  }
139
139
 
@@ -145,7 +145,7 @@ function generateFileManagerComponent(options = {}) {
145
145
  selectedItem = null;
146
146
  loadDirectory();
147
147
  }).catch(function(err) {
148
- alert('Yeniden adlandırılamadı: ' + (err.message || String(err)));
148
+ alert('Failed to rename: ' + (err.message || String(err)));
149
149
  });
150
150
  }
151
151
 
@@ -163,7 +163,7 @@ function generateFileManagerComponent(options = {}) {
163
163
  function handleFileUpload(fileList) {
164
164
  if (!fileList || fileList.length === 0) return;
165
165
  uploading = true;
166
- uploadStatus = fileList.length + ' dosya yükleniyor…';
166
+ uploadStatus = 'Uploading ' + fileList.length + ' file(s)…';
167
167
  if (typeof m !== 'undefined' && m.redraw) m.redraw();
168
168
 
169
169
  var fd = new FormData();
@@ -183,13 +183,13 @@ function generateFileManagerComponent(options = {}) {
183
183
  if (res.success) {
184
184
  loadDirectory();
185
185
  } else {
186
- alert('Yükleme hatası: ' + (res.error || 'Bilinmeyen hata'));
186
+ alert('Upload error: ' + (res.error || 'Unknown error'));
187
187
  if (typeof m !== 'undefined' && m.redraw) m.redraw();
188
188
  }
189
189
  }).catch(function(err) {
190
190
  uploading = false;
191
191
  uploadStatus = '';
192
- alert('Yükleme hatası: ' + (err.message || String(err)));
192
+ alert('Upload error: ' + (err.message || String(err)));
193
193
  if (typeof m !== 'undefined' && m.redraw) m.redraw();
194
194
  });
195
195
  }
@@ -231,7 +231,7 @@ function generateFileManagerComponent(options = {}) {
231
231
  m('div.relative', [
232
232
  m('input.text-xs.pl-8.pr-3.py-1.5.border.border-gray-200.dark:border-slate-700.rounded-lg.bg-gray-50.dark:bg-slate-900.text-gray-900.dark:text-slate-100.placeholder-gray-400', {
233
233
  type: 'text',
234
- placeholder: 'Dosyalarda ara…',
234
+ placeholder: 'Search files…',
235
235
  value: searchQuery,
236
236
  oninput: function(e) { searchQuery = e.target.value; },
237
237
  onkeydown: function(e) { if (e.key === 'Enter') loadDirectory(); }
@@ -244,13 +244,13 @@ function generateFileManagerComponent(options = {}) {
244
244
  value: typeFilter,
245
245
  onchange: function(e) { typeFilter = e.target.value; loadDirectory(); }
246
246
  }, [
247
- m('option', { value: 'all' }, 'Tüm Dosyalar'),
248
- m('option', { value: 'image' }, 'Resimler'),
249
- m('option', { value: 'document' }, 'Belgeler'),
250
- m('option', { value: 'video' }, 'Videolar'),
251
- m('option', { value: 'audio' }, 'Sesler'),
252
- m('option', { value: 'archive' }, 'Arşivler'),
253
- m('option', { value: 'code' }, 'Kod & Metin'),
247
+ m('option', { value: 'all' }, 'All Files'),
248
+ m('option', { value: 'image' }, 'Images'),
249
+ m('option', { value: 'document' }, 'Documents'),
250
+ m('option', { value: 'video' }, 'Videos'),
251
+ m('option', { value: 'audio' }, 'Audio'),
252
+ m('option', { value: 'archive' }, 'Archives'),
253
+ m('option', { value: 'code' }, 'Code & Text'),
254
254
  ]),
255
255
 
256
256
  // Grid / List View switch
@@ -262,7 +262,7 @@ function generateFileManagerComponent(options = {}) {
262
262
  m('button.px-2.5.py-1.text-xs.font-medium', {
263
263
  class: viewMode === 'list' ? 'bg-indigo-600 text-white' : 'bg-gray-50 dark:bg-slate-900 text-gray-700 dark:text-slate-300',
264
264
  onclick: function() { viewMode = 'list'; }
265
- }, 'Liste'),
265
+ }, 'List'),
266
266
  ]),
267
267
 
268
268
  // New Folder Button
@@ -270,13 +270,13 @@ function generateFileManagerComponent(options = {}) {
270
270
  onclick: function() { inputFolderValue = ''; modalState = 'newFolder'; }
271
271
  }, [
272
272
  m('span', '+'),
273
- m('span', 'Yeni Klasör')
273
+ m('span', 'New Folder')
274
274
  ]),
275
275
 
276
276
  // Upload Button
277
277
  m('label.px-3.py-1.5.text-xs.font-medium.bg-indigo-600.hover:bg-indigo-700.text-white.rounded-lg.cursor-pointer.flex.items-center.gap-1.transition', [
278
278
  m('span', '↑'),
279
- m('span', 'Yükle'),
279
+ m('span', 'Upload'),
280
280
  m('input[type=file].hidden', {
281
281
  multiple: true,
282
282
  onchange: function(e) { handleFileUpload(e.target.files); e.target.value = ''; }
@@ -285,7 +285,7 @@ function generateFileManagerComponent(options = {}) {
285
285
 
286
286
  // Refresh
287
287
  m('button.p-1.5.text-xs.text-gray-500.hover:text-gray-900.dark:hover:text-slate-100.rounded-lg', {
288
- title: 'Yenile',
288
+ title: 'Refresh',
289
289
  onclick: function() { loadDirectory(); }
290
290
  }, '🔄')
291
291
  ])
@@ -310,15 +310,15 @@ function generateFileManagerComponent(options = {}) {
310
310
  }, [
311
311
  loading ? m('div.flex.flex-col.items-center.justify-center.py-20.text-gray-400', [
312
312
  m('div.w-8.h-8.border-2.border-indigo-600.border-t-transparent.rounded-full.animate-spin.mb-2'),
313
- m('p.text-xs', 'Dosyalar yükleniyor…')
313
+ m('p.text-xs', 'Loading files…')
314
314
  ]) : null,
315
315
 
316
316
  !loading && error ? m('div.p-6.text-center.text-red-600.dark:text-red-400.text-sm', error) : null,
317
317
 
318
318
  !loading && !error && items.length === 0 ? m('div.flex.flex-col.items-center.justify-center.py-20.text-gray-400.border-2.border-dashed.border-gray-200.dark:border-slate-800.rounded-xl', [
319
319
  m('span.text-4xl.mb-2', '📂'),
320
- m('p.text-sm.font-medium.text-gray-600.dark:text-slate-400', 'Bu klasör boş'),
321
- m('p.text-xs.text-gray-400.mt-1', 'Dosyaları buraya sürükleyip bırakabilir veya "Yükle" butonunu kullanabilirsiniz.')
320
+ m('p.text-sm.font-medium.text-gray-600.dark:text-slate-400', 'This folder is empty'),
321
+ m('p.text-xs.text-gray-400.mt-1', 'You can drag and drop files here or use the "Upload" button.')
322
322
  ]) : null,
323
323
 
324
324
  // GRID VIEW
@@ -347,7 +347,7 @@ function generateFileManagerComponent(options = {}) {
347
347
  // Info
348
348
  m('p.text-xs.font-medium.text-gray-900.dark:text-slate-100.truncate', { title: item.name }, item.name),
349
349
  m('div.flex.items-center.justify-between.text-[11px].text-gray-400.dark:text-slate-500.mt-1', [
350
- m('span', item.isDir ? 'Klasör' : formatBytes(item.size)),
350
+ m('span', item.isDir ? 'Folder' : formatBytes(item.size)),
351
351
  // Menu dots
352
352
  m('div.relative.opacity-0.group-hover:opacity-100.transition', [
353
353
  m('button.hover:text-gray-700.dark:hover:text-slate-200.px-1', {
@@ -375,11 +375,11 @@ function generateFileManagerComponent(options = {}) {
375
375
  m('table.min-w-full.text-left.text-xs', [
376
376
  m('thead.bg-gray-50.dark:bg-slate-900.border-b.border-gray-200.dark:border-slate-700.text-gray-500', [
377
377
  m('tr', [
378
- m('th.p-3', 'Ad'),
379
- m('th.p-3', 'Tür'),
380
- m('th.p-3', 'Boyut'),
381
- m('th.p-3', 'Tarih'),
382
- m('th.p-3.text-right', 'İşlemler'),
378
+ m('th.p-3', 'Name'),
379
+ m('th.p-3', 'Type'),
380
+ m('th.p-3', 'Size'),
381
+ m('th.p-3', 'Date'),
382
+ m('th.p-3.text-right', 'Actions'),
383
383
  ])
384
384
  ]),
385
385
  m('tbody.divide-y.divide-gray-100.dark:divide-slate-700/50', items.map(function(item) {
@@ -399,7 +399,7 @@ function generateFileManagerComponent(options = {}) {
399
399
  m('span', renderFileIcon(item)),
400
400
  m('span.truncate.max-w-xs.md:max-w-md', item.name)
401
401
  ]),
402
- m('td.p-3.text-gray-500.dark:text-slate-400', item.isDir ? 'Klasör' : (item.ext || item.type).toUpperCase()),
402
+ m('td.p-3.text-gray-500.dark:text-slate-400', item.isDir ? 'Folder' : (item.ext || item.type).toUpperCase()),
403
403
  m('td.p-3.text-gray-500.dark:text-slate-400', item.isDir ? '—' : formatBytes(item.size)),
404
404
  m('td.p-3.text-gray-500.dark:text-slate-400', formatDate(item.mtime)),
405
405
  m('td.p-3.text-right.space-x-2', [
@@ -407,7 +407,7 @@ function generateFileManagerComponent(options = {}) {
407
407
  onclick: function(e) {
408
408
  e.stopPropagation();
409
409
  copyToClipboard(item.publicUrl);
410
- alert('URL kopyalandı: ' + item.publicUrl);
410
+ alert('URL copied: ' + item.publicUrl);
411
411
  }
412
412
  }, 'URL') : null,
413
413
  m('button.text-gray-600.dark:text-slate-400.hover:text-gray-900', {
@@ -417,14 +417,14 @@ function generateFileManagerComponent(options = {}) {
417
417
  inputRenameValue = item.name;
418
418
  modalState = 'rename';
419
419
  }
420
- }, 'Ad Değiştir'),
420
+ }, 'Rename'),
421
421
  m('button.text-red-600.dark:text-red-400.hover:underline', {
422
422
  onclick: function(e) {
423
423
  e.stopPropagation();
424
424
  selectedItem = item;
425
425
  modalState = 'delete';
426
426
  }
427
- }, 'Sil'),
427
+ }, 'Delete'),
428
428
  ])
429
429
  ]);
430
430
  }))
@@ -436,10 +436,10 @@ function generateFileManagerComponent(options = {}) {
436
436
  // 1. New Folder Modal
437
437
  modalState === 'newFolder' ? m('div.fixed.inset-0.bg-black/50.flex.items-center.justify-center.z-50.p-4', [
438
438
  m('div.bg-white.dark:bg-slate-800.rounded-xl.shadow-xl.max-w-md.w-full.p-6.space-y-4', [
439
- m('h3.text-base.font-bold.text-gray-900.dark:text-slate-100', 'Yeni Klasör Oluştur'),
439
+ m('h3.text-base.font-bold.text-gray-900.dark:text-slate-100', 'Create New Folder'),
440
440
  m('input.w-full.px-3.py-2.border.border-gray-300.dark:border-slate-600.rounded-lg.bg-white.dark:bg-slate-900.text-sm', {
441
441
  type: 'text',
442
- placeholder: 'Klasör adı…',
442
+ placeholder: 'Folder name…',
443
443
  value: inputFolderValue,
444
444
  oninput: function(e) { inputFolderValue = e.target.value; },
445
445
  onkeydown: function(e) { if (e.key === 'Enter') createFolder(); }
@@ -447,10 +447,10 @@ function generateFileManagerComponent(options = {}) {
447
447
  m('div.flex.justify-end.gap-2', [
448
448
  m('button.px-4.py-2.text-xs.rounded-lg.border.border-gray-300.dark:border-slate-600.text-gray-700.dark:text-slate-300', {
449
449
  onclick: function() { modalState = null; }
450
- }, 'İptal'),
450
+ }, 'Cancel'),
451
451
  m('button.px-4.py-2.text-xs.rounded-lg.bg-indigo-600.text-white.font-medium', {
452
452
  onclick: createFolder
453
- }, 'Oluştur')
453
+ }, 'Create')
454
454
  ])
455
455
  ])
456
456
  ]) : null,
@@ -458,7 +458,7 @@ function generateFileManagerComponent(options = {}) {
458
458
  // 2. Rename Modal
459
459
  modalState === 'rename' && selectedItem ? m('div.fixed.inset-0.bg-black/50.flex.items-center.justify-center.z-50.p-4', [
460
460
  m('div.bg-white.dark:bg-slate-800.rounded-xl.shadow-xl.max-w-md.w-full.p-6.space-y-4', [
461
- m('h3.text-base.font-bold.text-gray-900.dark:text-slate-100', 'Yeniden Adlandır'),
461
+ m('h3.text-base.font-bold.text-gray-900.dark:text-slate-100', 'Rename'),
462
462
  m('input.w-full.px-3.py-2.border.border-gray-300.dark:border-slate-600.rounded-lg.bg-white.dark:bg-slate-900.text-sm', {
463
463
  type: 'text',
464
464
  value: inputRenameValue,
@@ -468,10 +468,10 @@ function generateFileManagerComponent(options = {}) {
468
468
  m('div.flex.justify-end.gap-2', [
469
469
  m('button.px-4.py-2.text-xs.rounded-lg.border.border-gray-300.dark:border-slate-600.text-gray-700.dark:text-slate-300', {
470
470
  onclick: function() { modalState = null; }
471
- }, 'İptal'),
471
+ }, 'Cancel'),
472
472
  m('button.px-4.py-2.text-xs.rounded-lg.bg-indigo-600.text-white.font-medium', {
473
473
  onclick: renameSelected
474
- }, 'Kaydet')
474
+ }, 'Save')
475
475
  ])
476
476
  ])
477
477
  ]) : null,
@@ -479,18 +479,18 @@ function generateFileManagerComponent(options = {}) {
479
479
  // 3. Delete Confirmation Modal
480
480
  modalState === 'delete' && selectedItem ? m('div.fixed.inset-0.bg-black/50.flex.items-center.justify-center.z-50.p-4', [
481
481
  m('div.bg-white.dark:bg-slate-800.rounded-xl.shadow-xl.max-w-md.w-full.p-6.space-y-4', [
482
- m('h3.text-base.font-bold.text-red-600.dark:text-red-400', 'Silmeyi Onayla'),
482
+ m('h3.text-base.font-bold.text-red-600.dark:text-red-400', 'Confirm Delete'),
483
483
  m('p.text-sm.text-gray-600.dark:text-slate-300', [
484
484
  m('strong', selectedItem.name),
485
- ' kalıcı olarak silinecek. Emin misiniz?'
485
+ ' will be permanently deleted. Are you sure?'
486
486
  ]),
487
487
  m('div.flex.justify-end.gap-2', [
488
488
  m('button.px-4.py-2.text-xs.rounded-lg.border.border-gray-300.dark:border-slate-600.text-gray-700.dark:text-slate-300', {
489
489
  onclick: function() { modalState = null; }
490
- }, 'Vazgeç'),
490
+ }, 'Cancel'),
491
491
  m('button.px-4.py-2.text-xs.rounded-lg.bg-red-600.text-white.font-medium', {
492
492
  onclick: deleteSelected
493
- }, 'Sil')
493
+ }, 'Delete')
494
494
  ])
495
495
  ])
496
496
  ]) : null,
@@ -519,16 +519,16 @@ function generateFileManagerComponent(options = {}) {
519
519
  ? m('audio.w-full', { src: selectedItem.publicUrl, controls: true })
520
520
  : m('div.text-center.p-8', [
521
521
  renderFileIcon(selectedItem),
522
- m('p.text-xs.text-gray-500.mt-2', 'Önizleme mevcut değil')
522
+ m('p.text-xs.text-gray-500.mt-2', 'Preview not available')
523
523
  ])))
524
524
  ]),
525
525
  // File Info Pane
526
526
  m('div.p-4.space-y-2.text-xs.text-gray-600.dark:text-slate-300.border-t.border-gray-200.dark:border-slate-700', [
527
527
  m('div.grid.grid-cols-2.gap-2', [
528
- m('p', [m('span.text-gray-400', 'Boyut: '), formatBytes(selectedItem.size)]),
529
- m('p', [m('span.text-gray-400', 'Tür: '), selectedItem.mimeType || selectedItem.ext]),
530
- m('p', [m('span.text-gray-400', 'Tarih: '), formatDate(selectedItem.mtime)]),
531
- m('p.truncate', [m('span.text-gray-400', 'Yol: '), selectedItem.path]),
528
+ m('p', [m('span.text-gray-400', 'Size: '), formatBytes(selectedItem.size)]),
529
+ m('p', [m('span.text-gray-400', 'Type: '), selectedItem.mimeType || selectedItem.ext]),
530
+ m('p', [m('span.text-gray-400', 'Date: '), formatDate(selectedItem.mtime)]),
531
+ m('p.truncate', [m('span.text-gray-400', 'Path: '), selectedItem.path]),
532
532
  ]),
533
533
  selectedItem.publicUrl ? m('div.flex.items-center.gap-2.pt-2', [
534
534
  m('input.flex-1.px-2.py-1.text-xs.border.rounded.bg-gray-50.dark:bg-slate-900.text-gray-700.dark:text-slate-300', {
@@ -538,15 +538,15 @@ function generateFileManagerComponent(options = {}) {
538
538
  m('button.px-3.py-1.bg-indigo-600.text-white.rounded.font-medium.hover:bg-indigo-700', {
539
539
  onclick: function() {
540
540
  copyToClipboard(selectedItem.publicUrl);
541
- alert('URL kopyalandı!');
541
+ alert('URL copied!');
542
542
  }
543
- }, 'Kopyala'),
543
+ }, 'Copy'),
544
544
  isPickerMode && onSelectCallback ? m('button.px-3.py-1.bg-emerald-600.text-white.rounded.font-medium.hover:bg-emerald-700', {
545
545
  onclick: function() {
546
546
  onSelectCallback(selectedItem.publicUrl);
547
547
  modalState = null;
548
548
  }
549
- }, 'Bu Dosyayı Seç') : null
549
+ }, 'Select This File') : null
550
550
  ]) : null
551
551
  ])
552
552
  ])
@@ -16,8 +16,8 @@ const security = require('./security');
16
16
  * @param {string} [options.baseDir='./public/uploads'] - Filesystem directory to manage
17
17
  * @param {string} [options.publicBasePath='/uploads'] - URL prefix for stored files
18
18
  * @param {string} [options.apiPrefix='/files'] - API subpath under Admin API
19
- * @param {string} [options.pageTitle='Dosya Yöneticisi'] - Title of admin page
20
- * @param {string} [options.menuLabel='Dosya Yöneticisi'] - Sidebar menu label
19
+ * @param {string} [options.pageTitle='File Manager'] - Title of admin page
20
+ * @param {string} [options.menuLabel='File Manager'] - Sidebar menu label
21
21
  * @param {number} [options.menuOrder=50] - Sidebar menu ordering
22
22
  * @param {number} [options.maxUploadBytes=10485760] - Max upload size in bytes (10MB default)
23
23
  * @param {number|null} [options.maxTotalStorageBytes] - Storage quota limit in bytes (optional)
@@ -34,8 +34,8 @@ function fileManagerPlugin(options = {}) {
34
34
  baseDir = path.join(process.cwd(), 'public', 'uploads'),
35
35
  publicBasePath = '/uploads',
36
36
  apiPrefix = '/files',
37
- pageTitle = 'Dosya Yöneticisi',
38
- menuLabel = 'Dosya Yöneticisi',
37
+ pageTitle = 'File Manager',
38
+ menuLabel = 'File Manager',
39
39
  menuOrder = 50,
40
40
  maxUploadBytes = 10 * 1024 * 1024,
41
41
  maxTotalStorageBytes = null,
package/plugins/index.js CHANGED
@@ -59,6 +59,8 @@ module.exports = {
59
59
  get createOAuthClient() { return require('./oauth').createOAuthClient; },
60
60
  get apiKeyPlugin() { return require('./api-key').apiKeyPlugin; },
61
61
  get createApiKeyMiddleware() { return require('./api-key').createApiKeyMiddleware; },
62
+ get webhooksPlugin() { return require('./webhooks').webhooksPlugin; },
63
+ get WebhookDispatcher() { return require('./webhooks').WebhookDispatcher; },
62
64
  };
63
65
 
64
66