gavaengine 0.1.2 → 2.0.0

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 (36) hide show
  1. package/dist/DashboardSplashTrigger-CRpueuUi.d.ts +154 -0
  2. package/dist/auth/index.d.ts +42 -0
  3. package/dist/auth/index.js +12 -0
  4. package/dist/auth/index.js.map +1 -0
  5. package/dist/{chunk-D5PTZWTT.js → chunk-4LM22T36.js} +2 -2
  6. package/dist/chunk-MC3FBYWV.js +534 -0
  7. package/dist/chunk-MC3FBYWV.js.map +1 -0
  8. package/dist/chunk-QO42DDRU.js +113 -0
  9. package/dist/chunk-QO42DDRU.js.map +1 -0
  10. package/dist/{chunk-3C3AAR3S.js → chunk-YSVQQBBU.js} +474 -50
  11. package/dist/chunk-YSVQQBBU.js.map +1 -0
  12. package/dist/components/index.d.ts +64 -132
  13. package/dist/components/index.js +22 -2
  14. package/dist/content/index.d.ts +45 -0
  15. package/dist/content/index.js +38 -0
  16. package/dist/content/index.js.map +1 -0
  17. package/dist/handlers/index.d.ts +60 -3
  18. package/dist/handlers/index.js +477 -126
  19. package/dist/handlers/index.js.map +1 -1
  20. package/dist/i18n/index.d.ts +114 -0
  21. package/dist/i18n/index.js +249 -0
  22. package/dist/i18n/index.js.map +1 -0
  23. package/dist/{index-B1ZYC5TP.d.ts → index-CCsSC4nF.d.ts} +119 -2
  24. package/dist/index.d.ts +45 -7
  25. package/dist/index.js +226 -39
  26. package/dist/index.js.map +1 -1
  27. package/dist/providers/index.d.ts +3 -2
  28. package/dist/providers/index.js +2 -2
  29. package/dist/types-X07o_zKf.d.ts +198 -0
  30. package/dist/types-d8-k_4dN.d.ts +19 -0
  31. package/package.json +16 -1
  32. package/dist/chunk-3C3AAR3S.js.map +0 -1
  33. package/dist/chunk-BVLJYZ6T.js +0 -252
  34. package/dist/chunk-BVLJYZ6T.js.map +0 -1
  35. package/dist/types-BZgSeTU8.d.ts +0 -101
  36. /package/dist/{chunk-D5PTZWTT.js.map → chunk-4LM22T36.js.map} +0 -0
@@ -1,4 +1,317 @@
1
+ // src/i18n/it.ts
2
+ var it = {
3
+ content: {
4
+ articles: "Articoli",
5
+ newArticle: "Nuovo articolo",
6
+ titlePlaceholder: "Titolo dell'articolo",
7
+ excerptPlaceholder: "Breve descrizione dell'articolo...",
8
+ slugPlaceholder: "slug-articolo",
9
+ authorPlaceholder: "Nome dell'autore",
10
+ category: "Categoria",
11
+ selectCategory: "Seleziona categoria",
12
+ excerpt: "Estratto",
13
+ author: "Autore",
14
+ slugUrl: "Slug URL",
15
+ noArticles: "Nessun articolo.",
16
+ noArticlesSearch: "Nessun articolo trovato.",
17
+ searchArticles: "Cerca articoli...",
18
+ coverImageLabel: "Immagine di copertina",
19
+ coverImageHint: "Scegli dalla libreria o carica un nuovo file",
20
+ viewArticle: "Vedi articolo pubblicato",
21
+ untitled: "Senza titolo"
22
+ },
23
+ editor: {
24
+ publish: "Pubblica",
25
+ unpublish: "Ritira",
26
+ saveDraft: "Salva bozza",
27
+ revisions: "Cronologia",
28
+ saving: "Salvando...",
29
+ saved: "Salvato",
30
+ saveError: "Errore nel salvataggio",
31
+ noRevisions: "Nessuna revisione salvata.",
32
+ restore: "Ripristina",
33
+ restoreConfirm: "Ripristinare questa revisione? Lo stato attuale verr\xE0 salvato prima del ripristino.",
34
+ beforeRestore: "Prima del ripristino",
35
+ publishedNote: "Pubblicato",
36
+ preview: "Anteprima",
37
+ placeholder: "Inizia a scrivere il tuo articolo..."
38
+ },
39
+ media: {
40
+ media: "Media",
41
+ noMedia: "Nessun file caricato.",
42
+ noMediaSearch: "Nessun file trovato.",
43
+ chooseFile: "Scegli file",
44
+ dragHint: "Trascina un'immagine qui oppure",
45
+ uploadHint: "JPEG, PNG, WebP, GIF (max 5MB)",
46
+ mediaLibrary: "Libreria media",
47
+ uploadNew: "Carica nuovo",
48
+ searchFiles: "Cerca file...",
49
+ selectImage: "Seleziona immagine",
50
+ noImages: "Nessuna immagine nella libreria.",
51
+ noImagesSearch: "Nessuna immagine trovata.",
52
+ copyUrl: "Copia URL",
53
+ editImage: "Modifica immagine",
54
+ restoreOriginal: "Ripristina originale",
55
+ freeAspect: "Libero"
56
+ },
57
+ users: {
58
+ users: "Utenti",
59
+ name: "Nome",
60
+ email: "Email",
61
+ password: "Password",
62
+ passwordEditHint: " (lascia vuoto per non modificarla)",
63
+ role: "Ruolo",
64
+ createdAt: "Creato il",
65
+ actions: "Azioni",
66
+ createUser: "Crea utente",
67
+ unauthorized: "Non autorizzato",
68
+ notAuthenticated: "Non autenticato",
69
+ allFieldsRequired: "Tutti i campi sono obbligatori.",
70
+ passwordMinLength: "La password deve avere almeno 6 caratteri.",
71
+ emailExists: "Esiste gi\xE0 un utente con questa email.",
72
+ invalidRole: "Ruolo non valido.",
73
+ cannotDeleteSelf: "Non puoi eliminare il tuo stesso account."
74
+ },
75
+ common: {
76
+ statistics: "Statistiche",
77
+ settings: "Impostazioni",
78
+ delete: "Elimina",
79
+ edit: "Modifica",
80
+ search: "Cerca",
81
+ draft: "Bozza",
82
+ published: "Pubblicato",
83
+ all: "Tutti",
84
+ drafts: "Bozze",
85
+ loading: "Caricamento...",
86
+ confirm: "Conferma",
87
+ cancel: "Annulla",
88
+ apply: "Applica",
89
+ change: "Cambia",
90
+ logout: "Esci",
91
+ saveChanges: "Salva modifiche",
92
+ updated: "Aggiornato",
93
+ status: "Stato",
94
+ title: "Titolo",
95
+ deleteConfirm: (title) => `Eliminare "${title || "Senza titolo"}"?`,
96
+ unpublishConfirm: (title) => `Ritirare "${title || "Senza titolo"}"? L'articolo torner\xE0 in bozza.`,
97
+ deleteUserConfirm: (name) => `Sei sicuro di voler eliminare l'utente "${name}"?`,
98
+ totalArticles: (count) => `${count} articol${count === 1 ? "o" : "i"} totali`,
99
+ totalFiles: (count) => `${count} file caricati`
100
+ }
101
+ };
102
+
103
+ // src/i18n/en.ts
104
+ var en = {
105
+ content: {
106
+ articles: "Articles",
107
+ newArticle: "New article",
108
+ titlePlaceholder: "Article title",
109
+ excerptPlaceholder: "Brief description of the article...",
110
+ slugPlaceholder: "article-slug",
111
+ authorPlaceholder: "Author name",
112
+ category: "Category",
113
+ selectCategory: "Select category",
114
+ excerpt: "Excerpt",
115
+ author: "Author",
116
+ slugUrl: "URL Slug",
117
+ noArticles: "No articles.",
118
+ noArticlesSearch: "No articles found.",
119
+ searchArticles: "Search articles...",
120
+ coverImageLabel: "Cover image",
121
+ coverImageHint: "Choose from library or upload a new file",
122
+ viewArticle: "View published article",
123
+ untitled: "Untitled"
124
+ },
125
+ editor: {
126
+ publish: "Publish",
127
+ unpublish: "Unpublish",
128
+ saveDraft: "Save draft",
129
+ revisions: "History",
130
+ saving: "Saving...",
131
+ saved: "Saved",
132
+ saveError: "Error saving",
133
+ noRevisions: "No revisions saved.",
134
+ restore: "Restore",
135
+ restoreConfirm: "Restore this revision? The current state will be saved before restoring.",
136
+ beforeRestore: "Before restore",
137
+ publishedNote: "Published",
138
+ preview: "Preview",
139
+ placeholder: "Start writing your article..."
140
+ },
141
+ media: {
142
+ media: "Media",
143
+ noMedia: "No files uploaded.",
144
+ noMediaSearch: "No files found.",
145
+ chooseFile: "Choose file",
146
+ dragHint: "Drag an image here or",
147
+ uploadHint: "JPEG, PNG, WebP, GIF (max 5MB)",
148
+ mediaLibrary: "Media library",
149
+ uploadNew: "Upload new",
150
+ searchFiles: "Search files...",
151
+ selectImage: "Select image",
152
+ noImages: "No images in library.",
153
+ noImagesSearch: "No images found.",
154
+ copyUrl: "Copy URL",
155
+ editImage: "Edit image",
156
+ restoreOriginal: "Restore original",
157
+ freeAspect: "Free"
158
+ },
159
+ users: {
160
+ users: "Users",
161
+ name: "Name",
162
+ email: "Email",
163
+ password: "Password",
164
+ passwordEditHint: " (leave blank to keep current)",
165
+ role: "Role",
166
+ createdAt: "Created at",
167
+ actions: "Actions",
168
+ createUser: "Create user",
169
+ unauthorized: "Unauthorized",
170
+ notAuthenticated: "Not authenticated",
171
+ allFieldsRequired: "All fields are required.",
172
+ passwordMinLength: "Password must be at least 6 characters.",
173
+ emailExists: "A user with this email already exists.",
174
+ invalidRole: "Invalid role.",
175
+ cannotDeleteSelf: "You cannot delete your own account."
176
+ },
177
+ common: {
178
+ statistics: "Statistics",
179
+ settings: "Settings",
180
+ delete: "Delete",
181
+ edit: "Edit",
182
+ search: "Search",
183
+ draft: "Draft",
184
+ published: "Published",
185
+ all: "All",
186
+ drafts: "Drafts",
187
+ loading: "Loading...",
188
+ confirm: "Confirm",
189
+ cancel: "Cancel",
190
+ apply: "Apply",
191
+ change: "Change",
192
+ logout: "Logout",
193
+ saveChanges: "Save changes",
194
+ updated: "Updated",
195
+ status: "Status",
196
+ title: "Title",
197
+ deleteConfirm: (title) => `Delete "${title || "Untitled"}"?`,
198
+ unpublishConfirm: (title) => `Unpublish "${title || "Untitled"}"? The article will go back to draft.`,
199
+ deleteUserConfirm: (name) => `Are you sure you want to delete user "${name}"?`,
200
+ totalArticles: (count) => `${count} total article${count === 1 ? "" : "s"}`,
201
+ totalFiles: (count) => `${count} uploaded file${count === 1 ? "" : "s"}`
202
+ }
203
+ };
204
+
205
+ // src/i18n/index.ts
206
+ var builtinLocales = { it, en };
207
+ function loadLocale(locale) {
208
+ const found = builtinLocales[locale];
209
+ if (!found) {
210
+ throw new Error(
211
+ `Locale "${locale}" not found. Available: ${Object.keys(builtinLocales).join(", ")}`
212
+ );
213
+ }
214
+ return found;
215
+ }
216
+
1
217
  // src/config.ts
218
+ function localeToStrings(locale) {
219
+ return {
220
+ // content
221
+ articles: locale.content.articles,
222
+ newArticle: locale.content.newArticle,
223
+ titlePlaceholder: locale.content.titlePlaceholder,
224
+ excerptPlaceholder: locale.content.excerptPlaceholder,
225
+ slugPlaceholder: locale.content.slugPlaceholder,
226
+ authorPlaceholder: locale.content.authorPlaceholder,
227
+ category: locale.content.category,
228
+ selectCategory: locale.content.selectCategory,
229
+ excerpt: locale.content.excerpt,
230
+ author: locale.content.author,
231
+ slugUrl: locale.content.slugUrl,
232
+ noArticles: locale.content.noArticles,
233
+ noArticlesSearch: locale.content.noArticlesSearch,
234
+ searchArticles: locale.content.searchArticles,
235
+ coverImageLabel: locale.content.coverImageLabel,
236
+ coverImageHint: locale.content.coverImageHint,
237
+ viewArticle: locale.content.viewArticle,
238
+ untitled: locale.content.untitled,
239
+ // editor
240
+ publish: locale.editor.publish,
241
+ unpublish: locale.editor.unpublish,
242
+ saveDraft: locale.editor.saveDraft,
243
+ revisions: locale.editor.revisions,
244
+ saving: locale.editor.saving,
245
+ saved: locale.editor.saved,
246
+ saveError: locale.editor.saveError,
247
+ noRevisions: locale.editor.noRevisions,
248
+ restore: locale.editor.restore,
249
+ restoreConfirm: locale.editor.restoreConfirm,
250
+ beforeRestore: locale.editor.beforeRestore,
251
+ publishedNote: locale.editor.publishedNote,
252
+ preview: locale.editor.preview,
253
+ // media
254
+ media: locale.media.media,
255
+ noMedia: locale.media.noMedia,
256
+ noMediaSearch: locale.media.noMediaSearch,
257
+ chooseFile: locale.media.chooseFile,
258
+ dragHint: locale.media.dragHint,
259
+ uploadHint: locale.media.uploadHint,
260
+ mediaLibrary: locale.media.mediaLibrary,
261
+ uploadNew: locale.media.uploadNew,
262
+ searchFiles: locale.media.searchFiles,
263
+ selectImage: locale.media.selectImage,
264
+ noImages: locale.media.noImages,
265
+ noImagesSearch: locale.media.noImagesSearch,
266
+ copyUrl: locale.media.copyUrl,
267
+ editImage: locale.media.editImage,
268
+ restoreOriginal: locale.media.restoreOriginal,
269
+ freeAspect: locale.media.freeAspect,
270
+ // users
271
+ users: locale.users.users,
272
+ name: locale.users.name,
273
+ email: locale.users.email,
274
+ password: locale.users.password,
275
+ passwordEditHint: locale.users.passwordEditHint,
276
+ role: locale.users.role,
277
+ createdAt: locale.users.createdAt,
278
+ actions: locale.users.actions,
279
+ createUser: locale.users.createUser,
280
+ unauthorized: locale.users.unauthorized,
281
+ notAuthenticated: locale.users.notAuthenticated,
282
+ allFieldsRequired: locale.users.allFieldsRequired,
283
+ passwordMinLength: locale.users.passwordMinLength,
284
+ emailExists: locale.users.emailExists,
285
+ invalidRole: locale.users.invalidRole,
286
+ cannotDeleteSelf: locale.users.cannotDeleteSelf,
287
+ // common
288
+ statistics: locale.common.statistics,
289
+ settings: locale.common.settings,
290
+ delete: locale.common.delete,
291
+ edit: locale.common.edit,
292
+ search: locale.common.search,
293
+ draft: locale.common.draft,
294
+ published: locale.common.published,
295
+ all: locale.common.all,
296
+ drafts: locale.common.drafts,
297
+ loading: locale.common.loading,
298
+ confirm: locale.common.confirm,
299
+ cancel: locale.common.cancel,
300
+ apply: locale.common.apply,
301
+ change: locale.common.change,
302
+ logout: locale.common.logout,
303
+ saveChanges: locale.common.saveChanges,
304
+ updated: locale.common.updated,
305
+ status: locale.common.status,
306
+ title: locale.common.title,
307
+ deleteConfirm: locale.common.deleteConfirm,
308
+ unpublishConfirm: locale.common.unpublishConfirm,
309
+ deleteUserConfirm: locale.common.deleteUserConfirm,
310
+ totalArticles: locale.common.totalArticles,
311
+ totalFiles: locale.common.totalFiles
312
+ };
313
+ }
314
+ var DEFAULT_STRINGS = localeToStrings(loadLocale("it"));
2
315
  var DEFAULT_CONFIG = {
3
316
  branding: {
4
317
  name: "GavaEngine"
@@ -15,6 +328,18 @@ var DEFAULT_CONFIG = {
15
328
  canPublish: (role) => role === "admin" || role === "redattore",
16
329
  adminRole: "admin"
17
330
  },
331
+ statuses: {
332
+ draft: "bozza",
333
+ published: "pubblicato"
334
+ },
335
+ locale: "it",
336
+ models: {
337
+ article: "article",
338
+ articleRevision: "articleRevision",
339
+ articleView: "articleView",
340
+ media: "media",
341
+ user: "user"
342
+ },
18
343
  categories: [
19
344
  "Cultura ed Intercultura",
20
345
  "Fatti ed Eventi",
@@ -49,95 +374,7 @@ var DEFAULT_CONFIG = {
49
374
  login: "/login",
50
375
  home: "/"
51
376
  },
52
- strings: {
53
- articles: "Articoli",
54
- newArticle: "Nuovo articolo",
55
- media: "Media",
56
- users: "Utenti",
57
- statistics: "Statistiche",
58
- settings: "Impostazioni",
59
- revisions: "Cronologia",
60
- publish: "Pubblica",
61
- unpublish: "Ritira",
62
- saveDraft: "Salva bozza",
63
- delete: "Elimina",
64
- edit: "Modifica",
65
- search: "Cerca",
66
- titlePlaceholder: "Titolo dell'articolo",
67
- excerptPlaceholder: "Breve descrizione dell'articolo...",
68
- slugPlaceholder: "slug-articolo",
69
- authorPlaceholder: "Nome dell'autore",
70
- category: "Categoria",
71
- selectCategory: "Seleziona categoria",
72
- excerpt: "Estratto",
73
- author: "Autore",
74
- slugUrl: "Slug URL",
75
- draft: "Bozza",
76
- published: "Pubblicato",
77
- all: "Tutti",
78
- drafts: "Bozze",
79
- saving: "Salvando...",
80
- saved: "Salvato",
81
- saveError: "Errore nel salvataggio",
82
- noArticles: "Nessun articolo.",
83
- noArticlesSearch: "Nessun articolo trovato.",
84
- noMedia: "Nessun file caricato.",
85
- noMediaSearch: "Nessun file trovato.",
86
- noRevisions: "Nessuna revisione salvata.",
87
- loading: "Caricamento...",
88
- confirm: "Conferma",
89
- cancel: "Annulla",
90
- apply: "Applica",
91
- change: "Cambia",
92
- logout: "Esci",
93
- viewArticle: "Vedi articolo pubblicato",
94
- preview: "Anteprima",
95
- restore: "Ripristina",
96
- restoreConfirm: "Ripristinare questa revisione? Lo stato attuale verr\xE0 salvato prima del ripristino.",
97
- beforeRestore: "Prima del ripristino",
98
- publishedNote: "Pubblicato",
99
- deleteConfirm: (title) => `Eliminare "${title || "Senza titolo"}"?`,
100
- unpublishConfirm: (title) => `Ritirare "${title || "Senza titolo"}"? L'articolo torner\xE0 in bozza.`,
101
- deleteUserConfirm: (name) => `Sei sicuro di voler eliminare l'utente "${name}"?`,
102
- totalArticles: (count) => `${count} articol${count === 1 ? "o" : "i"} totali`,
103
- totalFiles: (count) => `${count} file caricati`,
104
- coverImageLabel: "Immagine di copertina",
105
- coverImageHint: "Scegli dalla libreria o carica un nuovo file",
106
- chooseFile: "Scegli file",
107
- dragHint: "Trascina un'immagine qui oppure",
108
- uploadHint: "JPEG, PNG, WebP, GIF (max 5MB)",
109
- mediaLibrary: "Libreria media",
110
- uploadNew: "Carica nuovo",
111
- searchFiles: "Cerca file...",
112
- searchArticles: "Cerca articoli...",
113
- selectImage: "Seleziona immagine",
114
- noImages: "Nessuna immagine nella libreria.",
115
- noImagesSearch: "Nessuna immagine trovata.",
116
- copyUrl: "Copia URL",
117
- editImage: "Modifica immagine",
118
- restoreOriginal: "Ripristina originale",
119
- freeAspect: "Libero",
120
- unauthorized: "Non autorizzato",
121
- notAuthenticated: "Non autenticato",
122
- allFieldsRequired: "Tutti i campi sono obbligatori.",
123
- passwordMinLength: "La password deve avere almeno 6 caratteri.",
124
- emailExists: "Esiste gi\xE0 un utente con questa email.",
125
- invalidRole: "Ruolo non valido.",
126
- cannotDeleteSelf: "Non puoi eliminare il tuo stesso account.",
127
- name: "Nome",
128
- email: "Email",
129
- password: "Password",
130
- passwordEditHint: " (lascia vuoto per non modificarla)",
131
- role: "Ruolo",
132
- createdAt: "Creato il",
133
- actions: "Azioni",
134
- createUser: "Crea utente",
135
- saveChanges: "Salva modifiche",
136
- updated: "Aggiornato",
137
- status: "Stato",
138
- title: "Titolo",
139
- untitled: "Senza titolo"
140
- }
377
+ strings: DEFAULT_STRINGS
141
378
  };
142
379
  function deepMerge(target, source) {
143
380
  const result = { ...target };
@@ -156,32 +393,47 @@ function deepMerge(target, source) {
156
393
  return result;
157
394
  }
158
395
  function defineConfig(overrides = {}) {
159
- return deepMerge(DEFAULT_CONFIG, overrides);
396
+ const locale = overrides.locale ?? DEFAULT_CONFIG.locale;
397
+ const baseStrings = localeToStrings(loadLocale(locale));
398
+ const configWithLocale = {
399
+ ...DEFAULT_CONFIG,
400
+ strings: baseStrings
401
+ };
402
+ if (!overrides.editor?.placeholder) {
403
+ const localeData = loadLocale(locale);
404
+ configWithLocale.editor = {
405
+ ...configWithLocale.editor,
406
+ placeholder: localeData.editor.placeholder
407
+ };
408
+ }
409
+ return deepMerge(configWithLocale, overrides);
160
410
  }
161
411
 
162
412
  // src/handlers/revisions.ts
163
413
  function createRevisionHandlers(prisma, config) {
164
414
  const throttleMs = config.editor.revisionThrottleMinutes * 60 * 1e3;
415
+ const articleModel = config.models.article;
416
+ const revisionModel = config.models.articleRevision;
165
417
  return {
166
418
  async getRevisions(articleId) {
167
- return prisma.articleRevision.findMany({
419
+ return prisma[revisionModel].findMany({
168
420
  where: { articleId },
169
421
  include: { editor: { select: { name: true } } },
170
422
  orderBy: { createdAt: "desc" }
171
423
  });
172
424
  },
173
425
  async restoreRevision(articleId, revisionId, editorId) {
174
- const article = await prisma.article.findUnique({
426
+ const article = await prisma[articleModel].findUnique({
175
427
  where: { id: articleId }
176
428
  });
177
429
  if (!article) throw new Error("Article not found");
178
- const revision = await prisma.articleRevision.findUnique({
430
+ const revision = await prisma[revisionModel].findUnique({
179
431
  where: { id: revisionId }
180
432
  });
181
433
  if (!revision || revision.articleId !== articleId) {
182
434
  throw new Error("Revision not found");
183
435
  }
184
- await prisma.articleRevision.create({
436
+ await prisma[revisionModel].create({
185
437
  data: {
186
438
  articleId,
187
439
  title: article.title,
@@ -193,7 +445,7 @@ function createRevisionHandlers(prisma, config) {
193
445
  note: config.strings.beforeRestore
194
446
  }
195
447
  });
196
- await prisma.article.update({
448
+ await prisma[articleModel].update({
197
449
  where: { id: articleId },
198
450
  data: {
199
451
  title: revision.title,
@@ -207,17 +459,17 @@ function createRevisionHandlers(prisma, config) {
207
459
  return { success: true };
208
460
  },
209
461
  async createRevisionSnapshot(articleId, editorId, note = "") {
210
- const article = await prisma.article.findUnique({
462
+ const article = await prisma[articleModel].findUnique({
211
463
  where: { id: articleId }
212
464
  });
213
465
  if (!article) return;
214
466
  const threshold = new Date(Date.now() - throttleMs);
215
- const recent = await prisma.articleRevision.findFirst({
467
+ const recent = await prisma[revisionModel].findFirst({
216
468
  where: { articleId, createdAt: { gte: threshold } },
217
469
  orderBy: { createdAt: "desc" }
218
470
  });
219
471
  if (recent && !note) return;
220
- await prisma.articleRevision.create({
472
+ await prisma[revisionModel].create({
221
473
  data: {
222
474
  articleId,
223
475
  title: article.title,
@@ -236,19 +488,20 @@ function createRevisionHandlers(prisma, config) {
236
488
  // src/handlers/articles.ts
237
489
  function createArticleHandlers(prisma, config) {
238
490
  const revisionHandlers = createRevisionHandlers(prisma, config);
491
+ const articleModel = config.models.article;
239
492
  return {
240
493
  async getArticles() {
241
- return prisma.article.findMany({
494
+ return prisma[articleModel].findMany({
242
495
  orderBy: { updatedAt: "desc" }
243
496
  });
244
497
  },
245
498
  async getArticleById(id) {
246
- return prisma.article.findUnique({
499
+ return prisma[articleModel].findUnique({
247
500
  where: { id }
248
501
  });
249
502
  },
250
503
  async createArticle(authorName) {
251
- const article = await prisma.article.create({
504
+ const article = await prisma[articleModel].create({
252
505
  data: {
253
506
  authorName
254
507
  }
@@ -256,23 +509,23 @@ function createArticleHandlers(prisma, config) {
256
509
  return article.id;
257
510
  },
258
511
  async updateArticle(userId, id, data) {
259
- const article = await prisma.article.findUnique({ where: { id } });
512
+ const article = await prisma[articleModel].findUnique({ where: { id } });
260
513
  if (!article) throw new Error("Article not found");
261
514
  await revisionHandlers.createRevisionSnapshot(id, userId);
262
- await prisma.article.update({
515
+ await prisma[articleModel].update({
263
516
  where: { id },
264
517
  data: { ...data, updatedAt: /* @__PURE__ */ new Date() }
265
518
  });
266
519
  return { success: true };
267
520
  },
268
521
  async deleteArticle(id) {
269
- const article = await prisma.article.findUnique({ where: { id } });
522
+ const article = await prisma[articleModel].findUnique({ where: { id } });
270
523
  if (!article) throw new Error("Article not found");
271
- await prisma.article.delete({ where: { id } });
524
+ await prisma[articleModel].delete({ where: { id } });
272
525
  return { success: true };
273
526
  },
274
527
  async publishArticle(userId, id) {
275
- const article = await prisma.article.findUnique({ where: { id } });
528
+ const article = await prisma[articleModel].findUnique({ where: { id } });
276
529
  if (!article) throw new Error("Article not found");
277
530
  if (!article.title || !article.slug) {
278
531
  throw new Error(
@@ -284,22 +537,22 @@ function createArticleHandlers(prisma, config) {
284
537
  userId,
285
538
  config.strings.publishedNote
286
539
  );
287
- await prisma.article.update({
540
+ await prisma[articleModel].update({
288
541
  where: { id },
289
542
  data: {
290
- status: "pubblicato",
543
+ status: config.statuses.published,
291
544
  publishedAt: /* @__PURE__ */ new Date()
292
545
  }
293
546
  });
294
547
  return { success: true };
295
548
  },
296
549
  async unpublishArticle(id) {
297
- const article = await prisma.article.findUnique({ where: { id } });
550
+ const article = await prisma[articleModel].findUnique({ where: { id } });
298
551
  if (!article) throw new Error("Article not found");
299
- await prisma.article.update({
552
+ await prisma[articleModel].update({
300
553
  where: { id },
301
554
  data: {
302
- status: "bozza",
555
+ status: config.statuses.draft,
303
556
  publishedAt: null
304
557
  }
305
558
  });
@@ -311,9 +564,10 @@ function createArticleHandlers(prisma, config) {
311
564
  // src/handlers/users.ts
312
565
  import bcrypt from "bcryptjs";
313
566
  function createUserHandlers(prisma, config) {
567
+ const userModel = config.models.user;
314
568
  return {
315
569
  async getUsers() {
316
- return prisma.user.findMany({
570
+ return prisma[userModel].findMany({
317
571
  select: {
318
572
  id: true,
319
573
  name: true,
@@ -325,7 +579,7 @@ function createUserHandlers(prisma, config) {
325
579
  });
326
580
  },
327
581
  async getUserById(id) {
328
- return prisma.user.findUnique({
582
+ return prisma[userModel].findUnique({
329
583
  where: { id },
330
584
  select: {
331
585
  id: true,
@@ -349,12 +603,12 @@ function createUserHandlers(prisma, config) {
349
603
  if (password.length < 6) {
350
604
  return { error: config.strings.passwordMinLength };
351
605
  }
352
- const existing = await prisma.user.findUnique({ where: { email } });
606
+ const existing = await prisma[userModel].findUnique({ where: { email } });
353
607
  if (existing) {
354
608
  return { error: config.strings.emailExists };
355
609
  }
356
610
  const hashedPassword = await bcrypt.hash(password, 10);
357
- await prisma.user.create({
611
+ await prisma[userModel].create({
358
612
  data: {
359
613
  name,
360
614
  email,
@@ -375,7 +629,7 @@ function createUserHandlers(prisma, config) {
375
629
  if (!config.roles.list.includes(role)) {
376
630
  return { error: config.strings.invalidRole };
377
631
  }
378
- const existing = await prisma.user.findUnique({ where: { email } });
632
+ const existing = await prisma[userModel].findUnique({ where: { email } });
379
633
  if (existing && existing.id !== id) {
380
634
  return { error: config.strings.emailExists };
381
635
  }
@@ -390,7 +644,7 @@ function createUserHandlers(prisma, config) {
390
644
  }
391
645
  data.password = await bcrypt.hash(password, 10);
392
646
  }
393
- await prisma.user.update({
647
+ await prisma[userModel].update({
394
648
  where: { id },
395
649
  data
396
650
  });
@@ -400,29 +654,31 @@ function createUserHandlers(prisma, config) {
400
654
  if (currentUserId === id) {
401
655
  return { error: config.strings.cannotDeleteSelf };
402
656
  }
403
- await prisma.user.delete({ where: { id } });
657
+ await prisma[userModel].delete({ where: { id } });
404
658
  return { success: true };
405
659
  }
406
660
  };
407
661
  }
408
662
 
409
663
  // src/handlers/media.ts
410
- function createMediaHandlers(prisma, _config) {
664
+ function createMediaHandlers(prisma, config) {
665
+ const mediaModel = config.models.media;
666
+ const articleModel = config.models.article;
411
667
  return {
412
668
  async getMedia(search) {
413
- return prisma.media.findMany({
669
+ return prisma[mediaModel].findMany({
414
670
  where: search ? { filename: { contains: search } } : void 0,
415
671
  include: { uploader: { select: { name: true } } },
416
672
  orderBy: { createdAt: "desc" }
417
673
  });
418
674
  },
419
675
  async deleteMedia(userId, userRole, id, deleteFileFromDisk) {
420
- const media = await prisma.media.findUnique({ where: { id } });
676
+ const media = await prisma[mediaModel].findUnique({ where: { id } });
421
677
  if (!media) throw new Error("File not found");
422
678
  if (userRole === "scrittore" && media.uploaderId !== userId) {
423
679
  throw new Error("Unauthorized");
424
680
  }
425
- const usedInArticles = await prisma.article.findMany({
681
+ const usedInArticles = await prisma[articleModel].findMany({
426
682
  where: {
427
683
  OR: [
428
684
  { coverImage: media.path },
@@ -443,7 +699,7 @@ function createMediaHandlers(prisma, _config) {
443
699
  } catch {
444
700
  }
445
701
  }
446
- await prisma.media.delete({ where: { id } });
702
+ await prisma[mediaModel].delete({ where: { id } });
447
703
  return { success: true };
448
704
  }
449
705
  };
@@ -455,6 +711,7 @@ function createUploadHandler(prisma, config, storage) {
455
711
  ...config.upload.imageTypes,
456
712
  ...config.upload.videoTypes
457
713
  ];
714
+ const mediaModel = config.models.media;
458
715
  return {
459
716
  async handleUpload(file, uploaderId) {
460
717
  if (!allowedTypes.includes(file.type)) {
@@ -469,7 +726,7 @@ function createUploadHandler(prisma, config, storage) {
469
726
  }
470
727
  const buffer = Buffer.from(await file.arrayBuffer());
471
728
  const url = await storage.save(file.name, buffer, file.type);
472
- await prisma.media.create({
729
+ await prisma[mediaModel].create({
473
730
  data: {
474
731
  filename: file.name,
475
732
  path: url,
@@ -485,7 +742,8 @@ function createUploadHandler(prisma, config, storage) {
485
742
 
486
743
  // src/handlers/auth.ts
487
744
  import bcrypt2 from "bcryptjs";
488
- function buildCredentialsProvider(prisma) {
745
+ function buildCredentialsProvider(prisma, config) {
746
+ const userModel = config?.models?.user ?? "user";
489
747
  return {
490
748
  credentials: {
491
749
  email: {},
@@ -495,7 +753,7 @@ function buildCredentialsProvider(prisma) {
495
753
  const email = credentials.email;
496
754
  const password = credentials.password;
497
755
  if (!email || !password) return null;
498
- const user = await prisma.user.findUnique({
756
+ const user = await prisma[userModel].findUnique({
499
757
  where: { email }
500
758
  });
501
759
  if (!user) return null;
@@ -571,12 +829,105 @@ function createAuthUtils(config) {
571
829
  }
572
830
  };
573
831
  }
832
+
833
+ // src/handlers/content.ts
834
+ function createContentHandlers(prisma, contentType, config) {
835
+ const modelName = contentType.slug.replace(/s$/, "").replace(/(^|\-)(\w)/g, (_m, _p1, p2) => p2.toUpperCase());
836
+ const model = prisma[modelName] ?? prisma[contentType.slug];
837
+ if (!model) {
838
+ throw new Error(
839
+ `Prisma model "${modelName}" not found for content type "${contentType.slug}"`
840
+ );
841
+ }
842
+ const defaultSort = contentType.admin?.defaultSort ? { [contentType.admin.defaultSort.field]: contentType.admin.defaultSort.direction } : { updatedAt: "desc" };
843
+ const searchableFields = contentType.admin?.searchableFields ?? ["title"];
844
+ return {
845
+ async getAll(options) {
846
+ const where = {};
847
+ if (options?.search) {
848
+ where.OR = searchableFields.map((field) => ({
849
+ [field]: { contains: options.search }
850
+ }));
851
+ }
852
+ if (options?.status) {
853
+ where.status = options.status;
854
+ }
855
+ return model.findMany({
856
+ where: Object.keys(where).length > 0 ? where : void 0,
857
+ orderBy: options?.orderBy ?? defaultSort
858
+ });
859
+ },
860
+ async getById(id) {
861
+ return model.findUnique({ where: { id } });
862
+ },
863
+ async create(data) {
864
+ const createData = {};
865
+ for (const field of contentType.fields) {
866
+ if (field.defaultValue !== void 0) {
867
+ createData[field.name] = field.defaultValue;
868
+ }
869
+ }
870
+ if (contentType.defaultStatus) {
871
+ createData.status = contentType.defaultStatus;
872
+ }
873
+ if (data) {
874
+ Object.assign(createData, data);
875
+ }
876
+ const record = await model.create({
877
+ data: createData
878
+ });
879
+ return record.id;
880
+ },
881
+ async update(id, data) {
882
+ const record = await model.findUnique({ where: { id } });
883
+ if (!record) throw new Error(`${contentType.labels.singular} not found`);
884
+ await model.update({
885
+ where: { id },
886
+ data: { ...data, updatedAt: /* @__PURE__ */ new Date() }
887
+ });
888
+ return { success: true };
889
+ },
890
+ async delete(id) {
891
+ const record = await model.findUnique({ where: { id } });
892
+ if (!record) throw new Error(`${contentType.labels.singular} not found`);
893
+ await model.delete({ where: { id } });
894
+ return { success: true };
895
+ },
896
+ async publish(userId, id) {
897
+ if (!contentType.publishedStatus) return { success: false };
898
+ const record = await model.findUnique({ where: { id } });
899
+ if (!record) throw new Error(`${contentType.labels.singular} not found`);
900
+ await model.update({
901
+ where: { id },
902
+ data: {
903
+ status: contentType.publishedStatus,
904
+ publishedAt: /* @__PURE__ */ new Date()
905
+ }
906
+ });
907
+ return { success: true };
908
+ },
909
+ async unpublish(id) {
910
+ if (!contentType.defaultStatus) return { success: false };
911
+ const record = await model.findUnique({ where: { id } });
912
+ if (!record) throw new Error(`${contentType.labels.singular} not found`);
913
+ await model.update({
914
+ where: { id },
915
+ data: {
916
+ status: contentType.defaultStatus,
917
+ publishedAt: null
918
+ }
919
+ });
920
+ return { success: true };
921
+ }
922
+ };
923
+ }
574
924
  export {
575
925
  DEFAULT_CONFIG,
576
926
  buildAuthCallbacks,
577
927
  buildCredentialsProvider,
578
928
  createArticleHandlers,
579
929
  createAuthUtils,
930
+ createContentHandlers,
580
931
  createMediaHandlers,
581
932
  createRevisionHandlers,
582
933
  createUploadHandler,